lightgallery.bundle.js 143 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312
  1. /*!
  2. * justifiedGallery - v3.7.0
  3. * http://miromannino.github.io/Justified-Gallery/
  4. * Copyright (c) 2018 Miro Mannino
  5. * Licensed under the MIT license.
  6. */
  7. (function (factory) {
  8. if (typeof define === 'function' && define.amd) {
  9. // AMD. Register as an anonymous module.
  10. define(['jquery'], factory);
  11. } else if (typeof module === 'object' && module.exports) {
  12. // Node/CommonJS
  13. module.exports = function( root, jQuery ) {
  14. if ( jQuery === undefined ) {
  15. // require('jQuery') returns a factory that requires window to
  16. // build a jQuery instance, we normalize how we use modules
  17. // that require this pattern but the window provided is a noop
  18. // if it's defined (how jquery works)
  19. if ( typeof window !== 'undefined' ) {
  20. jQuery = require('jquery');
  21. }
  22. else {
  23. jQuery = require('jquery')(root);
  24. }
  25. }
  26. factory(jQuery);
  27. return jQuery;
  28. };
  29. } else {
  30. // Browser globals
  31. factory(jQuery);
  32. }
  33. }(function ($) {
  34. /**
  35. * Justified Gallery controller constructor
  36. *
  37. * @param $gallery the gallery to build
  38. * @param settings the settings (the defaults are in JustifiedGallery.defaults)
  39. * @constructor
  40. */
  41. var JustifiedGallery = function ($gallery, settings) {
  42. this.settings = settings;
  43. this.checkSettings();
  44. this.imgAnalyzerTimeout = null;
  45. this.entries = null;
  46. this.buildingRow = {
  47. entriesBuff : [],
  48. width : 0,
  49. height : 0,
  50. aspectRatio : 0
  51. };
  52. this.lastFetchedEntry = null;
  53. this.lastAnalyzedIndex = -1;
  54. this.yield = {
  55. every : 2, // do a flush every n flushes (must be greater than 1)
  56. flushed : 0 // flushed rows without a yield
  57. };
  58. this.border = settings.border >= 0 ? settings.border : settings.margins;
  59. this.maxRowHeight = this.retrieveMaxRowHeight();
  60. this.suffixRanges = this.retrieveSuffixRanges();
  61. this.offY = this.border;
  62. this.rows = 0;
  63. this.spinner = {
  64. phase : 0,
  65. timeSlot : 150,
  66. $el : $('<div class="spinner"><span></span><span></span><span></span></div>'),
  67. intervalId : null
  68. };
  69. this.scrollBarOn = false;
  70. this.checkWidthIntervalId = null;
  71. this.galleryWidth = $gallery.width();
  72. this.$gallery = $gallery;
  73. };
  74. /** @returns {String} the best suffix given the width and the height */
  75. JustifiedGallery.prototype.getSuffix = function (width, height) {
  76. var longestSide, i;
  77. longestSide = (width > height) ? width : height;
  78. for (i = 0; i < this.suffixRanges.length; i++) {
  79. if (longestSide <= this.suffixRanges[i]) {
  80. return this.settings.sizeRangeSuffixes[this.suffixRanges[i]];
  81. }
  82. }
  83. return this.settings.sizeRangeSuffixes[this.suffixRanges[i - 1]];
  84. };
  85. /**
  86. * Remove the suffix from the string
  87. *
  88. * @returns {string} a new string without the suffix
  89. */
  90. JustifiedGallery.prototype.removeSuffix = function (str, suffix) {
  91. return str.substring(0, str.length - suffix.length);
  92. };
  93. /**
  94. * @returns {boolean} a boolean to say if the suffix is contained in the str or not
  95. */
  96. JustifiedGallery.prototype.endsWith = function (str, suffix) {
  97. return str.indexOf(suffix, str.length - suffix.length) !== -1;
  98. };
  99. /**
  100. * Get the used suffix of a particular url
  101. *
  102. * @param str
  103. * @returns {String} return the used suffix
  104. */
  105. JustifiedGallery.prototype.getUsedSuffix = function (str) {
  106. for (var si in this.settings.sizeRangeSuffixes) {
  107. if (this.settings.sizeRangeSuffixes.hasOwnProperty(si)) {
  108. if (this.settings.sizeRangeSuffixes[si].length === 0) continue;
  109. if (this.endsWith(str, this.settings.sizeRangeSuffixes[si])) return this.settings.sizeRangeSuffixes[si];
  110. }
  111. }
  112. return '';
  113. };
  114. /**
  115. * Given an image src, with the width and the height, returns the new image src with the
  116. * best suffix to show the best quality thumbnail.
  117. *
  118. * @returns {String} the suffix to use
  119. */
  120. JustifiedGallery.prototype.newSrc = function (imageSrc, imgWidth, imgHeight, image) {
  121. var newImageSrc;
  122. if (this.settings.thumbnailPath) {
  123. newImageSrc = this.settings.thumbnailPath(imageSrc, imgWidth, imgHeight, image);
  124. } else {
  125. var matchRes = imageSrc.match(this.settings.extension);
  126. var ext = (matchRes !== null) ? matchRes[0] : '';
  127. newImageSrc = imageSrc.replace(this.settings.extension, '');
  128. newImageSrc = this.removeSuffix(newImageSrc, this.getUsedSuffix(newImageSrc));
  129. newImageSrc += this.getSuffix(imgWidth, imgHeight) + ext;
  130. }
  131. return newImageSrc;
  132. };
  133. /**
  134. * Shows the images that is in the given entry
  135. *
  136. * @param $entry the entry
  137. * @param callback the callback that is called when the show animation is finished
  138. */
  139. JustifiedGallery.prototype.showImg = function ($entry, callback) {
  140. if (this.settings.cssAnimation) {
  141. $entry.addClass('entry-visible');
  142. if (callback) callback();
  143. } else {
  144. $entry.stop().fadeTo(this.settings.imagesAnimationDuration, 1.0, callback);
  145. $entry.find(this.settings.imgSelector).stop().fadeTo(this.settings.imagesAnimationDuration, 1.0, callback);
  146. }
  147. };
  148. /**
  149. * Extract the image src form the image, looking from the 'safe-src', and if it can't be found, from the
  150. * 'src' attribute. It saves in the image data the 'jg.originalSrc' field, with the extracted src.
  151. *
  152. * @param $image the image to analyze
  153. * @returns {String} the extracted src
  154. */
  155. JustifiedGallery.prototype.extractImgSrcFromImage = function ($image) {
  156. var imageSrc = (typeof $image.data('safe-src') !== 'undefined') ? $image.data('safe-src') : $image.attr('src');
  157. $image.data('jg.originalSrc', imageSrc);
  158. return imageSrc;
  159. };
  160. /** @returns {jQuery} the image in the given entry */
  161. JustifiedGallery.prototype.imgFromEntry = function ($entry) {
  162. var $img = $entry.find(this.settings.imgSelector);
  163. return $img.length === 0 ? null : $img;
  164. };
  165. /** @returns {jQuery} the caption in the given entry */
  166. JustifiedGallery.prototype.captionFromEntry = function ($entry) {
  167. var $caption = $entry.find('> .caption');
  168. return $caption.length === 0 ? null : $caption;
  169. };
  170. /**
  171. * Display the entry
  172. *
  173. * @param {jQuery} $entry the entry to display
  174. * @param {int} x the x position where the entry must be positioned
  175. * @param y the y position where the entry must be positioned
  176. * @param imgWidth the image width
  177. * @param imgHeight the image height
  178. * @param rowHeight the row height of the row that owns the entry
  179. */
  180. JustifiedGallery.prototype.displayEntry = function ($entry, x, y, imgWidth, imgHeight, rowHeight) {
  181. $entry.width(imgWidth);
  182. $entry.height(rowHeight);
  183. $entry.css('top', y);
  184. $entry.css('left', x);
  185. var $image = this.imgFromEntry($entry);
  186. if ($image !== null) {
  187. $image.css('width', imgWidth);
  188. $image.css('height', imgHeight);
  189. $image.css('margin-left', - imgWidth / 2);
  190. $image.css('margin-top', - imgHeight / 2);
  191. // Image reloading for an high quality of thumbnails
  192. var imageSrc = $image.attr('src');
  193. var newImageSrc = this.newSrc(imageSrc, imgWidth, imgHeight, $image[0]);
  194. $image.one('error', function () {
  195. $image.attr('src', $image.data('jg.originalSrc')); //revert to the original thumbnail, we got it.
  196. });
  197. var loadNewImage = function () {
  198. if (imageSrc !== newImageSrc) { //load the new image after the fadeIn
  199. $image.attr('src', newImageSrc);
  200. }
  201. };
  202. if ($entry.data('jg.loaded') === 'skipped') {
  203. this.onImageEvent(imageSrc, $.proxy(function() {
  204. this.showImg($entry, loadNewImage);
  205. $entry.data('jg.loaded', true);
  206. }, this));
  207. } else {
  208. this.showImg($entry, loadNewImage);
  209. }
  210. } else {
  211. this.showImg($entry);
  212. }
  213. this.displayEntryCaption($entry);
  214. };
  215. /**
  216. * Display the entry caption. If the caption element doesn't exists, it creates the caption using the 'alt'
  217. * or the 'title' attributes.
  218. *
  219. * @param {jQuery} $entry the entry to process
  220. */
  221. JustifiedGallery.prototype.displayEntryCaption = function ($entry) {
  222. var $image = this.imgFromEntry($entry);
  223. if ($image !== null && this.settings.captions) {
  224. var $imgCaption = this.captionFromEntry($entry);
  225. // Create it if it doesn't exists
  226. if ($imgCaption === null) {
  227. var caption = $image.attr('alt');
  228. if (!this.isValidCaption(caption)) caption = $entry.attr('title');
  229. if (this.isValidCaption(caption)) { // Create only we found something
  230. $imgCaption = $('<div class="caption">' + caption + '</div>');
  231. $entry.append($imgCaption);
  232. $entry.data('jg.createdCaption', true);
  233. }
  234. }
  235. // Create events (we check again the $imgCaption because it can be still inexistent)
  236. if ($imgCaption !== null) {
  237. if (!this.settings.cssAnimation) $imgCaption.stop().fadeTo(0, this.settings.captionSettings.nonVisibleOpacity);
  238. this.addCaptionEventsHandlers($entry);
  239. }
  240. } else {
  241. this.removeCaptionEventsHandlers($entry);
  242. }
  243. };
  244. /**
  245. * Validates the caption
  246. *
  247. * @param caption The caption that should be validated
  248. * @return {boolean} Validation result
  249. */
  250. JustifiedGallery.prototype.isValidCaption = function (caption) {
  251. return (typeof caption !== 'undefined' && caption.length > 0);
  252. };
  253. /**
  254. * The callback for the event 'mouseenter'. It assumes that the event currentTarget is an entry.
  255. * It shows the caption using jQuery (or using CSS if it is configured so)
  256. *
  257. * @param {Event} eventObject the event object
  258. */
  259. JustifiedGallery.prototype.onEntryMouseEnterForCaption = function (eventObject) {
  260. var $caption = this.captionFromEntry($(eventObject.currentTarget));
  261. if (this.settings.cssAnimation) {
  262. $caption.addClass('caption-visible').removeClass('caption-hidden');
  263. } else {
  264. $caption.stop().fadeTo(this.settings.captionSettings.animationDuration,
  265. this.settings.captionSettings.visibleOpacity);
  266. }
  267. };
  268. /**
  269. * The callback for the event 'mouseleave'. It assumes that the event currentTarget is an entry.
  270. * It hides the caption using jQuery (or using CSS if it is configured so)
  271. *
  272. * @param {Event} eventObject the event object
  273. */
  274. JustifiedGallery.prototype.onEntryMouseLeaveForCaption = function (eventObject) {
  275. var $caption = this.captionFromEntry($(eventObject.currentTarget));
  276. if (this.settings.cssAnimation) {
  277. $caption.removeClass('caption-visible').removeClass('caption-hidden');
  278. } else {
  279. $caption.stop().fadeTo(this.settings.captionSettings.animationDuration,
  280. this.settings.captionSettings.nonVisibleOpacity);
  281. }
  282. };
  283. /**
  284. * Add the handlers of the entry for the caption
  285. *
  286. * @param $entry the entry to modify
  287. */
  288. JustifiedGallery.prototype.addCaptionEventsHandlers = function ($entry) {
  289. var captionMouseEvents = $entry.data('jg.captionMouseEvents');
  290. if (typeof captionMouseEvents === 'undefined') {
  291. captionMouseEvents = {
  292. mouseenter: $.proxy(this.onEntryMouseEnterForCaption, this),
  293. mouseleave: $.proxy(this.onEntryMouseLeaveForCaption, this)
  294. };
  295. $entry.on('mouseenter', undefined, undefined, captionMouseEvents.mouseenter);
  296. $entry.on('mouseleave', undefined, undefined, captionMouseEvents.mouseleave);
  297. $entry.data('jg.captionMouseEvents', captionMouseEvents);
  298. }
  299. };
  300. /**
  301. * Remove the handlers of the entry for the caption
  302. *
  303. * @param $entry the entry to modify
  304. */
  305. JustifiedGallery.prototype.removeCaptionEventsHandlers = function ($entry) {
  306. var captionMouseEvents = $entry.data('jg.captionMouseEvents');
  307. if (typeof captionMouseEvents !== 'undefined') {
  308. $entry.off('mouseenter', undefined, captionMouseEvents.mouseenter);
  309. $entry.off('mouseleave', undefined, captionMouseEvents.mouseleave);
  310. $entry.removeData('jg.captionMouseEvents');
  311. }
  312. };
  313. /**
  314. * Clear the building row data to be used for a new row
  315. */
  316. JustifiedGallery.prototype.clearBuildingRow = function () {
  317. this.buildingRow.entriesBuff = [];
  318. this.buildingRow.aspectRatio = 0;
  319. this.buildingRow.width = 0;
  320. };
  321. /**
  322. * Justify the building row, preparing it to
  323. *
  324. * @param isLastRow
  325. * @returns a boolean to know if the row has been justified or not
  326. */
  327. JustifiedGallery.prototype.prepareBuildingRow = function (isLastRow) {
  328. var i, $entry, imgAspectRatio, newImgW, newImgH, justify = true;
  329. var minHeight = 0;
  330. var availableWidth = this.galleryWidth - 2 * this.border - (
  331. (this.buildingRow.entriesBuff.length - 1) * this.settings.margins);
  332. var rowHeight = availableWidth / this.buildingRow.aspectRatio;
  333. var defaultRowHeight = this.settings.rowHeight;
  334. var justifiable = this.buildingRow.width / availableWidth > this.settings.justifyThreshold;
  335. //Skip the last row if we can't justify it and the lastRow == 'hide'
  336. if (isLastRow && this.settings.lastRow === 'hide' && !justifiable) {
  337. for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
  338. $entry = this.buildingRow.entriesBuff[i];
  339. if (this.settings.cssAnimation)
  340. $entry.removeClass('entry-visible');
  341. else {
  342. $entry.stop().fadeTo(0, 0.1);
  343. $entry.find('> img, > a > img').fadeTo(0, 0);
  344. }
  345. }
  346. return -1;
  347. }
  348. // With lastRow = nojustify, justify if is justificable (the images will not become too big)
  349. if (isLastRow && !justifiable && this.settings.lastRow !== 'justify' && this.settings.lastRow !== 'hide') {
  350. justify = false;
  351. if (this.rows > 0) {
  352. defaultRowHeight = (this.offY - this.border - this.settings.margins * this.rows) / this.rows;
  353. justify = defaultRowHeight * this.buildingRow.aspectRatio / availableWidth > this.settings.justifyThreshold;
  354. }
  355. }
  356. for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
  357. $entry = this.buildingRow.entriesBuff[i];
  358. imgAspectRatio = $entry.data('jg.width') / $entry.data('jg.height');
  359. if (justify) {
  360. newImgW = (i === this.buildingRow.entriesBuff.length - 1) ? availableWidth : rowHeight * imgAspectRatio;
  361. newImgH = rowHeight;
  362. } else {
  363. newImgW = defaultRowHeight * imgAspectRatio;
  364. newImgH = defaultRowHeight;
  365. }
  366. availableWidth -= Math.round(newImgW);
  367. $entry.data('jg.jwidth', Math.round(newImgW));
  368. $entry.data('jg.jheight', Math.ceil(newImgH));
  369. if (i === 0 || minHeight > newImgH) minHeight = newImgH;
  370. }
  371. this.buildingRow.height = minHeight;
  372. return justify;
  373. };
  374. /**
  375. * Flush a row: justify it, modify the gallery height accordingly to the row height
  376. *
  377. * @param isLastRow
  378. */
  379. JustifiedGallery.prototype.flushRow = function (isLastRow) {
  380. var settings = this.settings;
  381. var $entry, buildingRowRes, offX = this.border, i;
  382. buildingRowRes = this.prepareBuildingRow(isLastRow);
  383. if (isLastRow && settings.lastRow === 'hide' && buildingRowRes === -1) {
  384. this.clearBuildingRow();
  385. return;
  386. }
  387. if(this.maxRowHeight) {
  388. if(this.maxRowHeight < this.buildingRow.height) this.buildingRow.height = this.maxRowHeight;
  389. }
  390. //Align last (unjustified) row
  391. if (isLastRow && (settings.lastRow === 'center' || settings.lastRow === 'right')) {
  392. var availableWidth = this.galleryWidth - 2 * this.border - (this.buildingRow.entriesBuff.length - 1) * settings.margins;
  393. for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
  394. $entry = this.buildingRow.entriesBuff[i];
  395. availableWidth -= $entry.data('jg.jwidth');
  396. }
  397. if (settings.lastRow === 'center')
  398. offX += availableWidth / 2;
  399. else if (settings.lastRow === 'right')
  400. offX += availableWidth;
  401. }
  402. var lastEntryIdx = this.buildingRow.entriesBuff.length - 1;
  403. for (i = 0; i <= lastEntryIdx; i++) {
  404. $entry = this.buildingRow.entriesBuff[ this.settings.rtl ? lastEntryIdx - i : i ];
  405. this.displayEntry($entry, offX, this.offY, $entry.data('jg.jwidth'), $entry.data('jg.jheight'), this.buildingRow.height);
  406. offX += $entry.data('jg.jwidth') + settings.margins;
  407. }
  408. //Gallery Height
  409. this.galleryHeightToSet = this.offY + this.buildingRow.height + this.border;
  410. this.setGalleryTempHeight(this.galleryHeightToSet + this.getSpinnerHeight());
  411. if (!isLastRow || (this.buildingRow.height <= settings.rowHeight && buildingRowRes)) {
  412. //Ready for a new row
  413. this.offY += this.buildingRow.height + settings.margins;
  414. this.rows += 1;
  415. this.clearBuildingRow();
  416. this.settings.triggerEvent.call(this, 'jg.rowflush');
  417. }
  418. };
  419. // Scroll position not restoring: https://github.com/miromannino/Justified-Gallery/issues/221
  420. var galleryPrevStaticHeight = 0;
  421. JustifiedGallery.prototype.rememberGalleryHeight = function () {
  422. galleryPrevStaticHeight = this.$gallery.height();
  423. this.$gallery.height(galleryPrevStaticHeight);
  424. };
  425. // grow only
  426. JustifiedGallery.prototype.setGalleryTempHeight = function (height) {
  427. galleryPrevStaticHeight = Math.max(height, galleryPrevStaticHeight);
  428. this.$gallery.height(galleryPrevStaticHeight);
  429. };
  430. JustifiedGallery.prototype.setGalleryFinalHeight = function (height) {
  431. galleryPrevStaticHeight = height;
  432. this.$gallery.height(height);
  433. };
  434. /**
  435. * @returns {boolean} a boolean saying if the scrollbar is active or not
  436. */
  437. function hasScrollBar() {
  438. return $("body").height() > $(window).height();
  439. }
  440. /**
  441. * Checks the width of the gallery container, to know if a new justification is needed
  442. */
  443. JustifiedGallery.prototype.checkWidth = function () {
  444. this.checkWidthIntervalId = setInterval($.proxy(function () {
  445. // if the gallery is not currently visible, abort.
  446. if (!this.$gallery.is(":visible")) return;
  447. var galleryWidth = parseFloat(this.$gallery.width());
  448. if (hasScrollBar() === this.scrollBarOn) {
  449. if (Math.abs(galleryWidth - this.galleryWidth) > this.settings.refreshSensitivity) {
  450. this.galleryWidth = galleryWidth;
  451. this.rewind();
  452. this.rememberGalleryHeight();
  453. // Restart to analyze
  454. this.startImgAnalyzer(true);
  455. }
  456. } else {
  457. this.scrollBarOn = hasScrollBar();
  458. this.galleryWidth = galleryWidth;
  459. }
  460. }, this), this.settings.refreshTime);
  461. };
  462. /**
  463. * @returns {boolean} a boolean saying if the spinner is active or not
  464. */
  465. JustifiedGallery.prototype.isSpinnerActive = function () {
  466. return this.spinner.intervalId !== null;
  467. };
  468. /**
  469. * @returns {int} the spinner height
  470. */
  471. JustifiedGallery.prototype.getSpinnerHeight = function () {
  472. return this.spinner.$el.innerHeight();
  473. };
  474. /**
  475. * Stops the spinner animation and modify the gallery height to exclude the spinner
  476. */
  477. JustifiedGallery.prototype.stopLoadingSpinnerAnimation = function () {
  478. clearInterval(this.spinner.intervalId);
  479. this.spinner.intervalId = null;
  480. this.setGalleryTempHeight(this.$gallery.height() - this.getSpinnerHeight());
  481. this.spinner.$el.detach();
  482. };
  483. /**
  484. * Starts the spinner animation
  485. */
  486. JustifiedGallery.prototype.startLoadingSpinnerAnimation = function () {
  487. var spinnerContext = this.spinner;
  488. var $spinnerPoints = spinnerContext.$el.find('span');
  489. clearInterval(spinnerContext.intervalId);
  490. this.$gallery.append(spinnerContext.$el);
  491. this.setGalleryTempHeight(this.offY + this.buildingRow.height + this.getSpinnerHeight());
  492. spinnerContext.intervalId = setInterval(function () {
  493. if (spinnerContext.phase < $spinnerPoints.length) {
  494. $spinnerPoints.eq(spinnerContext.phase).fadeTo(spinnerContext.timeSlot, 1);
  495. } else {
  496. $spinnerPoints.eq(spinnerContext.phase - $spinnerPoints.length).fadeTo(spinnerContext.timeSlot, 0);
  497. }
  498. spinnerContext.phase = (spinnerContext.phase + 1) % ($spinnerPoints.length * 2);
  499. }, spinnerContext.timeSlot);
  500. };
  501. /**
  502. * Rewind the image analysis to start from the first entry.
  503. */
  504. JustifiedGallery.prototype.rewind = function () {
  505. this.lastFetchedEntry = null;
  506. this.lastAnalyzedIndex = -1;
  507. this.offY = this.border;
  508. this.rows = 0;
  509. this.clearBuildingRow();
  510. };
  511. /**
  512. * Update the entries searching it from the justified gallery HTML element
  513. *
  514. * @param norewind if norewind only the new entries will be changed (i.e. randomized, sorted or filtered)
  515. * @returns {boolean} true if some entries has been founded
  516. */
  517. JustifiedGallery.prototype.updateEntries = function (norewind) {
  518. var newEntries;
  519. if (norewind && this.lastFetchedEntry != null) {
  520. newEntries = $(this.lastFetchedEntry).nextAll(this.settings.selector).toArray();
  521. } else {
  522. this.entries = [];
  523. newEntries = this.$gallery.children(this.settings.selector).toArray();
  524. }
  525. if (newEntries.length > 0) {
  526. // Sort or randomize
  527. if ($.isFunction(this.settings.sort)) {
  528. newEntries = this.sortArray(newEntries);
  529. } else if (this.settings.randomize) {
  530. newEntries = this.shuffleArray(newEntries);
  531. }
  532. this.lastFetchedEntry = newEntries[newEntries.length - 1];
  533. // Filter
  534. if (this.settings.filter) {
  535. newEntries = this.filterArray(newEntries);
  536. } else {
  537. this.resetFilters(newEntries);
  538. }
  539. }
  540. this.entries = this.entries.concat(newEntries);
  541. return true;
  542. };
  543. /**
  544. * Apply the entries order to the DOM, iterating the entries and appending the images
  545. *
  546. * @param entries the entries that has been modified and that must be re-ordered in the DOM
  547. */
  548. JustifiedGallery.prototype.insertToGallery = function (entries) {
  549. var that = this;
  550. $.each(entries, function () {
  551. $(this).appendTo(that.$gallery);
  552. });
  553. };
  554. /**
  555. * Shuffle the array using the Fisher-Yates shuffle algorithm
  556. *
  557. * @param a the array to shuffle
  558. * @return the shuffled array
  559. */
  560. JustifiedGallery.prototype.shuffleArray = function (a) {
  561. var i, j, temp;
  562. for (i = a.length - 1; i > 0; i--) {
  563. j = Math.floor(Math.random() * (i + 1));
  564. temp = a[i];
  565. a[i] = a[j];
  566. a[j] = temp;
  567. }
  568. this.insertToGallery(a);
  569. return a;
  570. };
  571. /**
  572. * Sort the array using settings.comparator as comparator
  573. *
  574. * @param a the array to sort (it is sorted)
  575. * @return the sorted array
  576. */
  577. JustifiedGallery.prototype.sortArray = function (a) {
  578. a.sort(this.settings.sort);
  579. this.insertToGallery(a);
  580. return a;
  581. };
  582. /**
  583. * Reset the filters removing the 'jg-filtered' class from all the entries
  584. *
  585. * @param a the array to reset
  586. */
  587. JustifiedGallery.prototype.resetFilters = function (a) {
  588. for (var i = 0; i < a.length; i++) $(a[i]).removeClass('jg-filtered');
  589. };
  590. /**
  591. * Filter the entries considering theirs classes (if a string has been passed) or using a function for filtering.
  592. *
  593. * @param a the array to filter
  594. * @return the filtered array
  595. */
  596. JustifiedGallery.prototype.filterArray = function (a) {
  597. var settings = this.settings;
  598. if ($.type(settings.filter) === 'string') {
  599. // Filter only keeping the entries passed in the string
  600. return a.filter(function (el) {
  601. var $el = $(el);
  602. if ($el.is(settings.filter)) {
  603. $el.removeClass('jg-filtered');
  604. return true;
  605. } else {
  606. $el.addClass('jg-filtered').removeClass('jg-visible');
  607. return false;
  608. }
  609. });
  610. } else if ($.isFunction(settings.filter)) {
  611. // Filter using the passed function
  612. var filteredArr = a.filter(settings.filter);
  613. for (var i = 0; i < a.length; i++) {
  614. if (filteredArr.indexOf(a[i]) === -1) {
  615. $(a[i]).addClass('jg-filtered').removeClass('jg-visible');
  616. } else {
  617. $(a[i]).removeClass('jg-filtered');
  618. }
  619. }
  620. return filteredArr;
  621. }
  622. };
  623. /**
  624. * Destroy the Justified Gallery instance.
  625. *
  626. * It clears all the css properties added in the style attributes. We doesn't backup the original
  627. * values for those css attributes, because it costs (performance) and because in general one
  628. * shouldn't use the style attribute for an uniform set of images (where we suppose the use of
  629. * classes). Creating a backup is also difficult because JG could be called multiple times and
  630. * with different style attributes.
  631. */
  632. JustifiedGallery.prototype.destroy = function () {
  633. clearInterval(this.checkWidthIntervalId);
  634. $.each(this.entries, $.proxy(function(_, entry) {
  635. var $entry = $(entry);
  636. // Reset entry style
  637. $entry.css('width', '');
  638. $entry.css('height', '');
  639. $entry.css('top', '');
  640. $entry.css('left', '');
  641. $entry.data('jg.loaded', undefined);
  642. $entry.removeClass('jg-entry');
  643. // Reset image style
  644. var $img = this.imgFromEntry($entry);
  645. $img.css('width', '');
  646. $img.css('height', '');
  647. $img.css('margin-left', '');
  648. $img.css('margin-top', '');
  649. $img.attr('src', $img.data('jg.originalSrc'));
  650. $img.data('jg.originalSrc', undefined);
  651. // Remove caption
  652. this.removeCaptionEventsHandlers($entry);
  653. var $caption = this.captionFromEntry($entry);
  654. if ($entry.data('jg.createdCaption')) {
  655. // remove also the caption element (if created by jg)
  656. $entry.data('jg.createdCaption', undefined);
  657. if ($caption !== null) $caption.remove();
  658. } else {
  659. if ($caption !== null) $caption.fadeTo(0, 1);
  660. }
  661. }, this));
  662. this.$gallery.css('height', '');
  663. this.$gallery.removeClass('justified-gallery');
  664. this.$gallery.data('jg.controller', undefined);
  665. };
  666. /**
  667. * Analyze the images and builds the rows. It returns if it found an image that is not loaded.
  668. *
  669. * @param isForResize if the image analyzer is called for resizing or not, to call a different callback at the end
  670. */
  671. JustifiedGallery.prototype.analyzeImages = function (isForResize) {
  672. for (var i = this.lastAnalyzedIndex + 1; i < this.entries.length; i++) {
  673. var $entry = $(this.entries[i]);
  674. if ($entry.data('jg.loaded') === true || $entry.data('jg.loaded') === 'skipped') {
  675. var availableWidth = this.galleryWidth - 2 * this.border - (
  676. (this.buildingRow.entriesBuff.length - 1) * this.settings.margins);
  677. var imgAspectRatio = $entry.data('jg.width') / $entry.data('jg.height');
  678. if (availableWidth / (this.buildingRow.aspectRatio + imgAspectRatio) < this.settings.rowHeight) {
  679. this.flushRow(false);
  680. if(++this.yield.flushed >= this.yield.every) {
  681. this.startImgAnalyzer(isForResize);
  682. return;
  683. }
  684. }
  685. this.buildingRow.entriesBuff.push($entry);
  686. this.buildingRow.aspectRatio += imgAspectRatio;
  687. this.buildingRow.width += imgAspectRatio * this.settings.rowHeight;
  688. this.lastAnalyzedIndex = i;
  689. } else if ($entry.data('jg.loaded') !== 'error') {
  690. return;
  691. }
  692. }
  693. // Last row flush (the row is not full)
  694. if (this.buildingRow.entriesBuff.length > 0) this.flushRow(true);
  695. if (this.isSpinnerActive()) {
  696. this.stopLoadingSpinnerAnimation();
  697. }
  698. /* Stop, if there is, the timeout to start the analyzeImages.
  699. This is because an image can be set loaded, and the timeout can be set,
  700. but this image can be analyzed yet.
  701. */
  702. this.stopImgAnalyzerStarter();
  703. //On complete callback
  704. this.settings.triggerEvent.call(this, isForResize ? 'jg.resize' : 'jg.complete');
  705. this.setGalleryFinalHeight(this.galleryHeightToSet);
  706. };
  707. /**
  708. * Stops any ImgAnalyzer starter (that has an assigned timeout)
  709. */
  710. JustifiedGallery.prototype.stopImgAnalyzerStarter = function () {
  711. this.yield.flushed = 0;
  712. if (this.imgAnalyzerTimeout !== null) {
  713. clearTimeout(this.imgAnalyzerTimeout);
  714. this.imgAnalyzerTimeout = null;
  715. }
  716. };
  717. /**
  718. * Starts the image analyzer. It is not immediately called to let the browser to update the view
  719. *
  720. * @param isForResize specifies if the image analyzer must be called for resizing or not
  721. */
  722. JustifiedGallery.prototype.startImgAnalyzer = function (isForResize) {
  723. var that = this;
  724. this.stopImgAnalyzerStarter();
  725. this.imgAnalyzerTimeout = setTimeout(function () {
  726. that.analyzeImages(isForResize);
  727. }, 0.001); // we can't start it immediately due to a IE different behaviour
  728. };
  729. /**
  730. * Checks if the image is loaded or not using another image object. We cannot use the 'complete' image property,
  731. * because some browsers, with a 404 set complete = true.
  732. *
  733. * @param imageSrc the image src to load
  734. * @param onLoad callback that is called when the image has been loaded
  735. * @param onError callback that is called in case of an error
  736. */
  737. JustifiedGallery.prototype.onImageEvent = function (imageSrc, onLoad, onError) {
  738. if (!onLoad && !onError) return;
  739. var memImage = new Image();
  740. var $memImage = $(memImage);
  741. if (onLoad) {
  742. $memImage.one('load', function () {
  743. $memImage.off('load error');
  744. onLoad(memImage);
  745. });
  746. }
  747. if (onError) {
  748. $memImage.one('error', function() {
  749. $memImage.off('load error');
  750. onError(memImage);
  751. });
  752. }
  753. memImage.src = imageSrc;
  754. };
  755. /**
  756. * Init of Justified Gallery controlled
  757. * It analyzes all the entries starting theirs loading and calling the image analyzer (that works with loaded images)
  758. */
  759. JustifiedGallery.prototype.init = function () {
  760. var imagesToLoad = false, skippedImages = false, that = this;
  761. $.each(this.entries, function (index, entry) {
  762. var $entry = $(entry);
  763. var $image = that.imgFromEntry($entry);
  764. $entry.addClass('jg-entry');
  765. if ($entry.data('jg.loaded') !== true && $entry.data('jg.loaded') !== 'skipped') {
  766. // Link Rel global overwrite
  767. if (that.settings.rel !== null) $entry.attr('rel', that.settings.rel);
  768. // Link Target global overwrite
  769. if (that.settings.target !== null) $entry.attr('target', that.settings.target);
  770. if ($image !== null) {
  771. // Image src
  772. var imageSrc = that.extractImgSrcFromImage($image);
  773. $image.attr('src', imageSrc);
  774. /* If we have the height and the width, we don't wait that the image is loaded, but we start directly
  775. * with the justification */
  776. if (that.settings.waitThumbnailsLoad === false) {
  777. var width = parseFloat($image.prop('width'));
  778. var height = parseFloat($image.prop('height'));
  779. if (!isNaN(width) && !isNaN(height)) {
  780. $entry.data('jg.width', width);
  781. $entry.data('jg.height', height);
  782. $entry.data('jg.loaded', 'skipped');
  783. skippedImages = true;
  784. that.startImgAnalyzer(false);
  785. return true; // continue
  786. }
  787. }
  788. $entry.data('jg.loaded', false);
  789. imagesToLoad = true;
  790. // Spinner start
  791. if (!that.isSpinnerActive()) that.startLoadingSpinnerAnimation();
  792. that.onImageEvent(imageSrc, function (loadImg) { // image loaded
  793. $entry.data('jg.width', loadImg.width);
  794. $entry.data('jg.height', loadImg.height);
  795. $entry.data('jg.loaded', true);
  796. that.startImgAnalyzer(false);
  797. }, function () { // image load error
  798. $entry.data('jg.loaded', 'error');
  799. that.startImgAnalyzer(false);
  800. });
  801. } else {
  802. $entry.data('jg.loaded', true);
  803. $entry.data('jg.width', $entry.width() | parseFloat($entry.css('width')) | 1);
  804. $entry.data('jg.height', $entry.height() | parseFloat($entry.css('height')) | 1);
  805. }
  806. }
  807. });
  808. if (!imagesToLoad && !skippedImages) this.startImgAnalyzer(false);
  809. this.checkWidth();
  810. };
  811. /**
  812. * Checks that it is a valid number. If a string is passed it is converted to a number
  813. *
  814. * @param settingContainer the object that contains the setting (to allow the conversion)
  815. * @param settingName the setting name
  816. */
  817. JustifiedGallery.prototype.checkOrConvertNumber = function (settingContainer, settingName) {
  818. if ($.type(settingContainer[settingName]) === 'string') {
  819. settingContainer[settingName] = parseFloat(settingContainer[settingName]);
  820. }
  821. if ($.type(settingContainer[settingName]) === 'number') {
  822. if (isNaN(settingContainer[settingName])) throw 'invalid number for ' + settingName;
  823. } else {
  824. throw settingName + ' must be a number';
  825. }
  826. };
  827. /**
  828. * Checks the sizeRangeSuffixes and, if necessary, converts
  829. * its keys from string (e.g. old settings with 'lt100') to int.
  830. */
  831. JustifiedGallery.prototype.checkSizeRangesSuffixes = function () {
  832. if ($.type(this.settings.sizeRangeSuffixes) !== 'object') {
  833. throw 'sizeRangeSuffixes must be defined and must be an object';
  834. }
  835. var suffixRanges = [];
  836. for (var rangeIdx in this.settings.sizeRangeSuffixes) {
  837. if (this.settings.sizeRangeSuffixes.hasOwnProperty(rangeIdx)) suffixRanges.push(rangeIdx);
  838. }
  839. var newSizeRngSuffixes = {0: ''};
  840. for (var i = 0; i < suffixRanges.length; i++) {
  841. if ($.type(suffixRanges[i]) === 'string') {
  842. try {
  843. var numIdx = parseInt(suffixRanges[i].replace(/^[a-z]+/, ''), 10);
  844. newSizeRngSuffixes[numIdx] = this.settings.sizeRangeSuffixes[suffixRanges[i]];
  845. } catch (e) {
  846. throw 'sizeRangeSuffixes keys must contains correct numbers (' + e + ')';
  847. }
  848. } else {
  849. newSizeRngSuffixes[suffixRanges[i]] = this.settings.sizeRangeSuffixes[suffixRanges[i]];
  850. }
  851. }
  852. this.settings.sizeRangeSuffixes = newSizeRngSuffixes;
  853. };
  854. /**
  855. * check and convert the maxRowHeight setting
  856. * requires rowHeight to be already set
  857. * TODO: should be always called when only rowHeight is changed
  858. * @return number or null
  859. */
  860. JustifiedGallery.prototype.retrieveMaxRowHeight = function () {
  861. var newMaxRowHeight = null;
  862. var rowHeight = this.settings.rowHeight;
  863. if ($.type(this.settings.maxRowHeight) === 'string') {
  864. if (this.settings.maxRowHeight.match(/^[0-9]+%$/)) {
  865. newMaxRowHeight = rowHeight * parseFloat(this.settings.maxRowHeight.match(/^([0-9]+)%$/)[1]) / 100;
  866. } else {
  867. newMaxRowHeight = parseFloat(this.settings.maxRowHeight);
  868. }
  869. } else if ($.type(this.settings.maxRowHeight) === 'number') {
  870. newMaxRowHeight = this.settings.maxRowHeight;
  871. } else if (this.settings.maxRowHeight === false || this.settings.maxRowHeight == null) {
  872. return null;
  873. } else {
  874. throw 'maxRowHeight must be a number or a percentage';
  875. }
  876. // check if the converted value is not a number
  877. if (isNaN(newMaxRowHeight)) throw 'invalid number for maxRowHeight';
  878. // check values, maxRowHeight must be >= rowHeight
  879. if (newMaxRowHeight < rowHeight) newMaxRowHeight = rowHeight;
  880. return newMaxRowHeight;
  881. };
  882. /**
  883. * Checks the settings
  884. */
  885. JustifiedGallery.prototype.checkSettings = function () {
  886. this.checkSizeRangesSuffixes();
  887. this.checkOrConvertNumber(this.settings, 'rowHeight');
  888. this.checkOrConvertNumber(this.settings, 'margins');
  889. this.checkOrConvertNumber(this.settings, 'border');
  890. var lastRowModes = [
  891. 'justify',
  892. 'nojustify',
  893. 'left',
  894. 'center',
  895. 'right',
  896. 'hide'
  897. ];
  898. if (lastRowModes.indexOf(this.settings.lastRow) === -1) {
  899. throw 'lastRow must be one of: ' + lastRowModes.join(', ');
  900. }
  901. this.checkOrConvertNumber(this.settings, 'justifyThreshold');
  902. if (this.settings.justifyThreshold < 0 || this.settings.justifyThreshold > 1) {
  903. throw 'justifyThreshold must be in the interval [0,1]';
  904. }
  905. if ($.type(this.settings.cssAnimation) !== 'boolean') {
  906. throw 'cssAnimation must be a boolean';
  907. }
  908. if ($.type(this.settings.captions) !== 'boolean') throw 'captions must be a boolean';
  909. this.checkOrConvertNumber(this.settings.captionSettings, 'animationDuration');
  910. this.checkOrConvertNumber(this.settings.captionSettings, 'visibleOpacity');
  911. if (this.settings.captionSettings.visibleOpacity < 0 ||
  912. this.settings.captionSettings.visibleOpacity > 1) {
  913. throw 'captionSettings.visibleOpacity must be in the interval [0, 1]';
  914. }
  915. this.checkOrConvertNumber(this.settings.captionSettings, 'nonVisibleOpacity');
  916. if (this.settings.captionSettings.nonVisibleOpacity < 0 ||
  917. this.settings.captionSettings.nonVisibleOpacity > 1) {
  918. throw 'captionSettings.nonVisibleOpacity must be in the interval [0, 1]';
  919. }
  920. this.checkOrConvertNumber(this.settings, 'imagesAnimationDuration');
  921. this.checkOrConvertNumber(this.settings, 'refreshTime');
  922. this.checkOrConvertNumber(this.settings, 'refreshSensitivity');
  923. if ($.type(this.settings.randomize) !== 'boolean') throw 'randomize must be a boolean';
  924. if ($.type(this.settings.selector) !== 'string') throw 'selector must be a string';
  925. if (this.settings.sort !== false && !$.isFunction(this.settings.sort)) {
  926. throw 'sort must be false or a comparison function';
  927. }
  928. if (this.settings.filter !== false && !$.isFunction(this.settings.filter) &&
  929. $.type(this.settings.filter) !== 'string') {
  930. throw 'filter must be false, a string or a filter function';
  931. }
  932. };
  933. /**
  934. * It brings all the indexes from the sizeRangeSuffixes and it orders them. They are then sorted and returned.
  935. * @returns {Array} sorted suffix ranges
  936. */
  937. JustifiedGallery.prototype.retrieveSuffixRanges = function () {
  938. var suffixRanges = [];
  939. for (var rangeIdx in this.settings.sizeRangeSuffixes) {
  940. if (this.settings.sizeRangeSuffixes.hasOwnProperty(rangeIdx)) suffixRanges.push(parseInt(rangeIdx, 10));
  941. }
  942. suffixRanges.sort(function (a, b) { return a > b ? 1 : a < b ? -1 : 0; });
  943. return suffixRanges;
  944. };
  945. /**
  946. * Update the existing settings only changing some of them
  947. *
  948. * @param newSettings the new settings (or a subgroup of them)
  949. */
  950. JustifiedGallery.prototype.updateSettings = function (newSettings) {
  951. // In this case Justified Gallery has been called again changing only some options
  952. this.settings = $.extend({}, this.settings, newSettings);
  953. this.checkSettings();
  954. // As reported in the settings: negative value = same as margins, 0 = disabled
  955. this.border = this.settings.border >= 0 ? this.settings.border : this.settings.margins;
  956. this.maxRowHeight = this.retrieveMaxRowHeight();
  957. this.suffixRanges = this.retrieveSuffixRanges();
  958. };
  959. JustifiedGallery.prototype.defaults = {
  960. sizeRangeSuffixes: { }, /* e.g. Flickr configuration
  961. {
  962. 100: '_t', // used when longest is less than 100px
  963. 240: '_m', // used when longest is between 101px and 240px
  964. 320: '_n', // ...
  965. 500: '',
  966. 640: '_z',
  967. 1024: '_b' // used as else case because it is the last
  968. }
  969. */
  970. thumbnailPath: undefined, /* If defined, sizeRangeSuffixes is not used, and this function is used to determine the
  971. path relative to a specific thumbnail size. The function should accept respectively three arguments:
  972. current path, width and height */
  973. rowHeight: 120, // required? required to be > 0?
  974. maxRowHeight: false, // false or negative value to deactivate. Positive number to express the value in pixels,
  975. // A string '[0-9]+%' to express in percentage (e.g. 300% means that the row height
  976. // can't exceed 3 * rowHeight)
  977. margins: 1,
  978. border: -1, // negative value = same as margins, 0 = disabled, any other value to set the border
  979. lastRow: 'nojustify', // … which is the same as 'left', or can be 'justify', 'center', 'right' or 'hide'
  980. justifyThreshold: 0.90, /* if row width / available space > 0.90 it will be always justified
  981. * (i.e. lastRow setting is not considered) */
  982. waitThumbnailsLoad: true,
  983. captions: true,
  984. cssAnimation: true,
  985. imagesAnimationDuration: 500, // ignored with css animations
  986. captionSettings: { // ignored with css animations
  987. animationDuration: 500,
  988. visibleOpacity: 0.7,
  989. nonVisibleOpacity: 0.0
  990. },
  991. rel: null, // rewrite the rel of each analyzed links
  992. target: null, // rewrite the target of all links
  993. extension: /\.[^.\\/]+$/, // regexp to capture the extension of an image
  994. refreshTime: 200, // time interval (in ms) to check if the page changes its width
  995. refreshSensitivity: 0, // change in width allowed (in px) without re-building the gallery
  996. randomize: false,
  997. rtl: false, // right-to-left mode
  998. sort: false, /*
  999. - false: to do not sort
  1000. - function: to sort them using the function as comparator (see Array.prototype.sort())
  1001. */
  1002. filter: false, /*
  1003. - false, null or undefined: for a disabled filter
  1004. - a string: an entry is kept if entry.is(filter string) returns true
  1005. see jQuery's .is() function for further information
  1006. - a function: invoked with arguments (entry, index, array). Return true to keep the entry, false otherwise.
  1007. It follows the specifications of the Array.prototype.filter() function of JavaScript.
  1008. */
  1009. selector: 'a, div:not(.spinner)', // The selector that is used to know what are the entries of the gallery
  1010. imgSelector: '> img, > a > img', // The selector that is used to know what are the images of each entry
  1011. triggerEvent: function (event) { // This is called to trigger events, the default behavior is to call $.trigger
  1012. this.$gallery.trigger(event); // Consider that 'this' is this set to the JustifiedGallery object, so it can
  1013. } // access to fields such as $gallery, useful to trigger events with jQuery.
  1014. };
  1015. /**
  1016. * Justified Gallery plugin for jQuery
  1017. *
  1018. * Events
  1019. * - jg.complete : called when all the gallery has been created
  1020. * - jg.resize : called when the gallery has been resized
  1021. * - jg.rowflush : when a new row appears
  1022. *
  1023. * @param arg the action (or the settings) passed when the plugin is called
  1024. * @returns {*} the object itself
  1025. */
  1026. $.fn.justifiedGallery = function (arg) {
  1027. return this.each(function (index, gallery) {
  1028. var $gallery = $(gallery);
  1029. $gallery.addClass('justified-gallery');
  1030. var controller = $gallery.data('jg.controller');
  1031. if (typeof controller === 'undefined') {
  1032. // Create controller and assign it to the object data
  1033. if (typeof arg !== 'undefined' && arg !== null && $.type(arg) !== 'object') {
  1034. if (arg === 'destroy') return; // Just a call to an unexisting object
  1035. throw 'The argument must be an object';
  1036. }
  1037. controller = new JustifiedGallery($gallery, $.extend({}, JustifiedGallery.prototype.defaults, arg));
  1038. $gallery.data('jg.controller', controller);
  1039. } else if (arg === 'norewind') {
  1040. // In this case we don't rewind: we analyze only the latest images (e.g. to complete the last unfinished row
  1041. // ... left to be more readable
  1042. } else if (arg === 'destroy') {
  1043. controller.destroy();
  1044. return;
  1045. } else {
  1046. // In this case Justified Gallery has been called again changing only some options
  1047. controller.updateSettings(arg);
  1048. controller.rewind();
  1049. }
  1050. // Update the entries list
  1051. if (!controller.updateEntries(arg === 'norewind')) return;
  1052. // Init justified gallery
  1053. controller.init();
  1054. });
  1055. };
  1056. }));
  1057. /*!
  1058. * jQuery Mousewheel 3.1.13
  1059. *
  1060. * Copyright jQuery Foundation and other contributors
  1061. * Released under the MIT license
  1062. * http://jquery.org/license
  1063. */
  1064. (function (factory) {
  1065. if ( typeof define === 'function' && define.amd ) {
  1066. // AMD. Register as an anonymous module.
  1067. define(['jquery'], factory);
  1068. } else if (typeof exports === 'object') {
  1069. // Node/CommonJS style for Browserify
  1070. module.exports = factory;
  1071. } else {
  1072. // Browser globals
  1073. factory(jQuery);
  1074. }
  1075. }(function ($) {
  1076. var toFix = ['wheel', 'mousewheel', 'DOMMouseScroll', 'MozMousePixelScroll'],
  1077. toBind = ( 'onwheel' in document || document.documentMode >= 9 ) ?
  1078. ['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'],
  1079. slice = Array.prototype.slice,
  1080. nullLowestDeltaTimeout, lowestDelta;
  1081. if ( $.event.fixHooks ) {
  1082. for ( var i = toFix.length; i; ) {
  1083. $.event.fixHooks[ toFix[--i] ] = $.event.mouseHooks;
  1084. }
  1085. }
  1086. var special = $.event.special.mousewheel = {
  1087. version: '3.1.12',
  1088. setup: function() {
  1089. if ( this.addEventListener ) {
  1090. for ( var i = toBind.length; i; ) {
  1091. this.addEventListener( toBind[--i], handler, false );
  1092. }
  1093. } else {
  1094. this.onmousewheel = handler;
  1095. }
  1096. // Store the line height and page height for this particular element
  1097. $.data(this, 'mousewheel-line-height', special.getLineHeight(this));
  1098. $.data(this, 'mousewheel-page-height', special.getPageHeight(this));
  1099. },
  1100. teardown: function() {
  1101. if ( this.removeEventListener ) {
  1102. for ( var i = toBind.length; i; ) {
  1103. this.removeEventListener( toBind[--i], handler, false );
  1104. }
  1105. } else {
  1106. this.onmousewheel = null;
  1107. }
  1108. // Clean up the data we added to the element
  1109. $.removeData(this, 'mousewheel-line-height');
  1110. $.removeData(this, 'mousewheel-page-height');
  1111. },
  1112. getLineHeight: function(elem) {
  1113. var $elem = $(elem),
  1114. $parent = $elem['offsetParent' in $.fn ? 'offsetParent' : 'parent']();
  1115. if (!$parent.length) {
  1116. $parent = $('body');
  1117. }
  1118. return parseInt($parent.css('fontSize'), 10) || parseInt($elem.css('fontSize'), 10) || 16;
  1119. },
  1120. getPageHeight: function(elem) {
  1121. return $(elem).height();
  1122. },
  1123. settings: {
  1124. adjustOldDeltas: true, // see shouldAdjustOldDeltas() below
  1125. normalizeOffset: true // calls getBoundingClientRect for each event
  1126. }
  1127. };
  1128. $.fn.extend({
  1129. mousewheel: function(fn) {
  1130. return fn ? this.bind('mousewheel', fn) : this.trigger('mousewheel');
  1131. },
  1132. unmousewheel: function(fn) {
  1133. return this.unbind('mousewheel', fn);
  1134. }
  1135. });
  1136. function handler(event) {
  1137. var orgEvent = event || window.event,
  1138. args = slice.call(arguments, 1),
  1139. delta = 0,
  1140. deltaX = 0,
  1141. deltaY = 0,
  1142. absDelta = 0,
  1143. offsetX = 0,
  1144. offsetY = 0;
  1145. event = $.event.fix(orgEvent);
  1146. event.type = 'mousewheel';
  1147. // Old school scrollwheel delta
  1148. if ( 'detail' in orgEvent ) { deltaY = orgEvent.detail * -1; }
  1149. if ( 'wheelDelta' in orgEvent ) { deltaY = orgEvent.wheelDelta; }
  1150. if ( 'wheelDeltaY' in orgEvent ) { deltaY = orgEvent.wheelDeltaY; }
  1151. if ( 'wheelDeltaX' in orgEvent ) { deltaX = orgEvent.wheelDeltaX * -1; }
  1152. // Firefox < 17 horizontal scrolling related to DOMMouseScroll event
  1153. if ( 'axis' in orgEvent && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
  1154. deltaX = deltaY * -1;
  1155. deltaY = 0;
  1156. }
  1157. // Set delta to be deltaY or deltaX if deltaY is 0 for backwards compatabilitiy
  1158. delta = deltaY === 0 ? deltaX : deltaY;
  1159. // New school wheel delta (wheel event)
  1160. if ( 'deltaY' in orgEvent ) {
  1161. deltaY = orgEvent.deltaY * -1;
  1162. delta = deltaY;
  1163. }
  1164. if ( 'deltaX' in orgEvent ) {
  1165. deltaX = orgEvent.deltaX;
  1166. if ( deltaY === 0 ) { delta = deltaX * -1; }
  1167. }
  1168. // No change actually happened, no reason to go any further
  1169. if ( deltaY === 0 && deltaX === 0 ) { return; }
  1170. // Need to convert lines and pages to pixels if we aren't already in pixels
  1171. // There are three delta modes:
  1172. // * deltaMode 0 is by pixels, nothing to do
  1173. // * deltaMode 1 is by lines
  1174. // * deltaMode 2 is by pages
  1175. if ( orgEvent.deltaMode === 1 ) {
  1176. var lineHeight = $.data(this, 'mousewheel-line-height');
  1177. delta *= lineHeight;
  1178. deltaY *= lineHeight;
  1179. deltaX *= lineHeight;
  1180. } else if ( orgEvent.deltaMode === 2 ) {
  1181. var pageHeight = $.data(this, 'mousewheel-page-height');
  1182. delta *= pageHeight;
  1183. deltaY *= pageHeight;
  1184. deltaX *= pageHeight;
  1185. }
  1186. // Store lowest absolute delta to normalize the delta values
  1187. absDelta = Math.max( Math.abs(deltaY), Math.abs(deltaX) );
  1188. if ( !lowestDelta || absDelta < lowestDelta ) {
  1189. lowestDelta = absDelta;
  1190. // Adjust older deltas if necessary
  1191. if ( shouldAdjustOldDeltas(orgEvent, absDelta) ) {
  1192. lowestDelta /= 40;
  1193. }
  1194. }
  1195. // Adjust older deltas if necessary
  1196. if ( shouldAdjustOldDeltas(orgEvent, absDelta) ) {
  1197. // Divide all the things by 40!
  1198. delta /= 40;
  1199. deltaX /= 40;
  1200. deltaY /= 40;
  1201. }
  1202. // Get a whole, normalized value for the deltas
  1203. delta = Math[ delta >= 1 ? 'floor' : 'ceil' ](delta / lowestDelta);
  1204. deltaX = Math[ deltaX >= 1 ? 'floor' : 'ceil' ](deltaX / lowestDelta);
  1205. deltaY = Math[ deltaY >= 1 ? 'floor' : 'ceil' ](deltaY / lowestDelta);
  1206. // Normalise offsetX and offsetY properties
  1207. if ( special.settings.normalizeOffset && this.getBoundingClientRect ) {
  1208. var boundingRect = this.getBoundingClientRect();
  1209. offsetX = event.clientX - boundingRect.left;
  1210. offsetY = event.clientY - boundingRect.top;
  1211. }
  1212. // Add information to the event object
  1213. event.deltaX = deltaX;
  1214. event.deltaY = deltaY;
  1215. event.deltaFactor = lowestDelta;
  1216. event.offsetX = offsetX;
  1217. event.offsetY = offsetY;
  1218. // Go ahead and set deltaMode to 0 since we converted to pixels
  1219. // Although this is a little odd since we overwrite the deltaX/Y
  1220. // properties with normalized deltas.
  1221. event.deltaMode = 0;
  1222. // Add event and delta to the front of the arguments
  1223. args.unshift(event, delta, deltaX, deltaY);
  1224. // Clearout lowestDelta after sometime to better
  1225. // handle multiple device types that give different
  1226. // a different lowestDelta
  1227. // Ex: trackpad = 3 and mouse wheel = 120
  1228. if (nullLowestDeltaTimeout) { clearTimeout(nullLowestDeltaTimeout); }
  1229. nullLowestDeltaTimeout = setTimeout(nullLowestDelta, 200);
  1230. return ($.event.dispatch || $.event.handle).apply(this, args);
  1231. }
  1232. function nullLowestDelta() {
  1233. lowestDelta = null;
  1234. }
  1235. function shouldAdjustOldDeltas(orgEvent, absDelta) {
  1236. // If this is an older event and the delta is divisable by 120,
  1237. // then we are assuming that the browser is treating this as an
  1238. // older mouse wheel event and that we should divide the deltas
  1239. // by 40 to try and get a more usable deltaFactor.
  1240. // Side note, this actually impacts the reported scroll distance
  1241. // in older browsers and can cause scrolling to be slower than native.
  1242. // Turn this off by setting $.event.special.mousewheel.settings.adjustOldDeltas to false.
  1243. return special.settings.adjustOldDeltas && orgEvent.type === 'mousewheel' && absDelta % 120 === 0;
  1244. }
  1245. }));
  1246. (function() {
  1247. 'use strict';
  1248. var defaults = {
  1249. mode: 'lg-slide',
  1250. // Ex : 'ease'
  1251. cssEasing: 'ease',
  1252. //'for jquery animation'
  1253. easing: 'linear',
  1254. speed: 600,
  1255. height: '100%',
  1256. width: '100%',
  1257. addClass: '',
  1258. startClass: 'lg-start-zoom',
  1259. backdropDuration: 150,
  1260. hideBarsDelay: 6000,
  1261. useLeft: false,
  1262. closable: true,
  1263. loop: true,
  1264. escKey: true,
  1265. keyPress: true,
  1266. controls: true,
  1267. slideEndAnimatoin: true,
  1268. hideControlOnEnd: false,
  1269. mousewheel: true,
  1270. getCaptionFromTitleOrAlt: true,
  1271. // .lg-item || '.lg-sub-html'
  1272. appendSubHtmlTo: '.lg-sub-html',
  1273. subHtmlSelectorRelative: false,
  1274. /**
  1275. * @desc number of preload slides
  1276. * will exicute only after the current slide is fully loaded.
  1277. *
  1278. * @ex you clicked on 4th image and if preload = 1 then 3rd slide and 5th
  1279. * slide will be loaded in the background after the 4th slide is fully loaded..
  1280. * if preload is 2 then 2nd 3rd 5th 6th slides will be preloaded.. ... ...
  1281. *
  1282. */
  1283. preload: 1,
  1284. showAfterLoad: true,
  1285. selector: '',
  1286. selectWithin: '',
  1287. nextHtml: '',
  1288. prevHtml: '',
  1289. // 0, 1
  1290. index: false,
  1291. iframeMaxWidth: '100%',
  1292. download: true,
  1293. counter: true,
  1294. appendCounterTo: '.lg-toolbar',
  1295. swipeThreshold: 50,
  1296. enableSwipe: true,
  1297. enableDrag: true,
  1298. dynamic: false,
  1299. dynamicEl: [],
  1300. galleryId: 1
  1301. };
  1302. function Plugin(element, options) {
  1303. // Current lightGallery element
  1304. this.el = element;
  1305. // Current jquery element
  1306. this.$el = $(element);
  1307. // lightGallery settings
  1308. this.s = $.extend({}, defaults, options);
  1309. // When using dynamic mode, ensure dynamicEl is an array
  1310. if (this.s.dynamic && this.s.dynamicEl !== 'undefined' && this.s.dynamicEl.constructor === Array && !this.s.dynamicEl.length) {
  1311. throw ('When using dynamic mode, you must also define dynamicEl as an Array.');
  1312. }
  1313. // lightGallery modules
  1314. this.modules = {};
  1315. // false when lightgallery complete first slide;
  1316. this.lGalleryOn = false;
  1317. this.lgBusy = false;
  1318. // Timeout function for hiding controls;
  1319. this.hideBartimeout = false;
  1320. // To determine browser supports for touch events;
  1321. this.isTouch = ('ontouchstart' in document.documentElement);
  1322. // Disable hideControlOnEnd if sildeEndAnimation is true
  1323. if (this.s.slideEndAnimatoin) {
  1324. this.s.hideControlOnEnd = false;
  1325. }
  1326. // Gallery items
  1327. if (this.s.dynamic) {
  1328. this.$items = this.s.dynamicEl;
  1329. } else {
  1330. if (this.s.selector === 'this') {
  1331. this.$items = this.$el;
  1332. } else if (this.s.selector !== '') {
  1333. if (this.s.selectWithin) {
  1334. this.$items = $(this.s.selectWithin).find(this.s.selector);
  1335. } else {
  1336. this.$items = this.$el.find($(this.s.selector));
  1337. }
  1338. } else {
  1339. this.$items = this.$el.children();
  1340. }
  1341. }
  1342. // .lg-item
  1343. this.$slide = '';
  1344. // .lg-outer
  1345. this.$outer = '';
  1346. this.init();
  1347. return this;
  1348. }
  1349. Plugin.prototype.init = function() {
  1350. var _this = this;
  1351. // s.preload should not be more than $item.length
  1352. if (_this.s.preload > _this.$items.length) {
  1353. _this.s.preload = _this.$items.length;
  1354. }
  1355. // if dynamic option is enabled execute immediately
  1356. var _hash = window.location.hash;
  1357. if (_hash.indexOf('lg=' + this.s.galleryId) > 0) {
  1358. _this.index = parseInt(_hash.split('&slide=')[1], 10);
  1359. $('body').addClass('lg-from-hash');
  1360. if (!$('body').hasClass('lg-on')) {
  1361. setTimeout(function() {
  1362. _this.build(_this.index);
  1363. });
  1364. $('body').addClass('lg-on');
  1365. }
  1366. }
  1367. if (_this.s.dynamic) {
  1368. _this.$el.trigger('onBeforeOpen.lg');
  1369. _this.index = _this.s.index || 0;
  1370. // prevent accidental double execution
  1371. if (!$('body').hasClass('lg-on')) {
  1372. setTimeout(function() {
  1373. _this.build(_this.index);
  1374. $('body').addClass('lg-on');
  1375. });
  1376. }
  1377. } else {
  1378. // Using different namespace for click because click event should not unbind if selector is same object('this')
  1379. _this.$items.on('click.lgcustom', function(event) {
  1380. // For IE8
  1381. try {
  1382. event.preventDefault();
  1383. event.preventDefault();
  1384. } catch (er) {
  1385. event.returnValue = false;
  1386. }
  1387. _this.$el.trigger('onBeforeOpen.lg');
  1388. _this.index = _this.s.index || _this.$items.index(this);
  1389. // prevent accidental double execution
  1390. if (!$('body').hasClass('lg-on')) {
  1391. _this.build(_this.index);
  1392. $('body').addClass('lg-on');
  1393. }
  1394. });
  1395. }
  1396. };
  1397. Plugin.prototype.build = function(index) {
  1398. var _this = this;
  1399. _this.structure();
  1400. // module constructor
  1401. $.each($.fn.lightGallery.modules, function(key) {
  1402. _this.modules[key] = new $.fn.lightGallery.modules[key](_this.el);
  1403. });
  1404. // initiate slide function
  1405. _this.slide(index, false, false, false);
  1406. if (_this.s.keyPress) {
  1407. _this.keyPress();
  1408. }
  1409. if (_this.$items.length > 1) {
  1410. _this.arrow();
  1411. setTimeout(function() {
  1412. _this.enableDrag();
  1413. _this.enableSwipe();
  1414. }, 50);
  1415. if (_this.s.mousewheel) {
  1416. _this.mousewheel();
  1417. }
  1418. } else {
  1419. _this.$slide.on('click.lg', function() {
  1420. _this.$el.trigger('onSlideClick.lg');
  1421. });
  1422. }
  1423. _this.counter();
  1424. _this.closeGallery();
  1425. _this.$el.trigger('onAfterOpen.lg');
  1426. // Hide controllers if mouse doesn't move for some period
  1427. _this.$outer.on('mousemove.lg click.lg touchstart.lg', function() {
  1428. _this.$outer.removeClass('lg-hide-items');
  1429. clearTimeout(_this.hideBartimeout);
  1430. // Timeout will be cleared on each slide movement also
  1431. _this.hideBartimeout = setTimeout(function() {
  1432. _this.$outer.addClass('lg-hide-items');
  1433. }, _this.s.hideBarsDelay);
  1434. });
  1435. _this.$outer.trigger('mousemove.lg');
  1436. };
  1437. Plugin.prototype.structure = function() {
  1438. var list = '';
  1439. var controls = '';
  1440. var i = 0;
  1441. var subHtmlCont = '';
  1442. var template;
  1443. var _this = this;
  1444. $('body').append('<div class="lg-backdrop"></div>');
  1445. $('.lg-backdrop').css('transition-duration', this.s.backdropDuration + 'ms');
  1446. // Create gallery items
  1447. for (i = 0; i < this.$items.length; i++) {
  1448. list += '<div class="lg-item"></div>';
  1449. }
  1450. // Create controlls
  1451. if (this.s.controls && this.$items.length > 1) {
  1452. controls = '<div class="lg-actions">' +
  1453. '<button class="lg-prev lg-icon">' + this.s.prevHtml + '</button>' +
  1454. '<button class="lg-next lg-icon">' + this.s.nextHtml + '</button>' +
  1455. '</div>';
  1456. }
  1457. if (this.s.appendSubHtmlTo === '.lg-sub-html') {
  1458. subHtmlCont = '<div class="lg-sub-html"></div>';
  1459. }
  1460. template = '<div class="lg-outer ' + this.s.addClass + ' ' + this.s.startClass + '">' +
  1461. '<div class="lg" style="width:' + this.s.width + '; height:' + this.s.height + '">' +
  1462. '<div class="lg-inner">' + list + '</div>' +
  1463. '<div class="lg-toolbar lg-group">' +
  1464. '<span class="lg-close lg-icon"></span>' +
  1465. '</div>' +
  1466. controls +
  1467. subHtmlCont +
  1468. '</div>' +
  1469. '</div>';
  1470. $('body').append(template);
  1471. this.$outer = $('.lg-outer');
  1472. this.$slide = this.$outer.find('.lg-item');
  1473. if (this.s.useLeft) {
  1474. this.$outer.addClass('lg-use-left');
  1475. // Set mode lg-slide if use left is true;
  1476. this.s.mode = 'lg-slide';
  1477. } else {
  1478. this.$outer.addClass('lg-use-css3');
  1479. }
  1480. // For fixed height gallery
  1481. _this.setTop();
  1482. $(window).on('resize.lg orientationchange.lg', function() {
  1483. setTimeout(function() {
  1484. _this.setTop();
  1485. }, 100);
  1486. });
  1487. // add class lg-current to remove initial transition
  1488. this.$slide.eq(this.index).addClass('lg-current');
  1489. // add Class for css support and transition mode
  1490. if (this.doCss()) {
  1491. this.$outer.addClass('lg-css3');
  1492. } else {
  1493. this.$outer.addClass('lg-css');
  1494. // Set speed 0 because no animation will happen if browser doesn't support css3
  1495. this.s.speed = 0;
  1496. }
  1497. this.$outer.addClass(this.s.mode);
  1498. if (this.s.enableDrag && this.$items.length > 1) {
  1499. this.$outer.addClass('lg-grab');
  1500. }
  1501. if (this.s.showAfterLoad) {
  1502. this.$outer.addClass('lg-show-after-load');
  1503. }
  1504. if (this.doCss()) {
  1505. var $inner = this.$outer.find('.lg-inner');
  1506. $inner.css('transition-timing-function', this.s.cssEasing);
  1507. $inner.css('transition-duration', this.s.speed + 'ms');
  1508. }
  1509. setTimeout(function() {
  1510. $('.lg-backdrop').addClass('in');
  1511. });
  1512. setTimeout(function() {
  1513. _this.$outer.addClass('lg-visible');
  1514. }, this.s.backdropDuration);
  1515. if (this.s.download) {
  1516. this.$outer.find('.lg-toolbar').append('<a id="lg-download" target="_blank" download class="lg-download lg-icon"></a>');
  1517. }
  1518. // Store the current scroll top value to scroll back after closing the gallery..
  1519. this.prevScrollTop = $(window).scrollTop();
  1520. };
  1521. // For fixed height gallery
  1522. Plugin.prototype.setTop = function() {
  1523. if (this.s.height !== '100%') {
  1524. var wH = $(window).height();
  1525. var top = (wH - parseInt(this.s.height, 10)) / 2;
  1526. var $lGallery = this.$outer.find('.lg');
  1527. if (wH >= parseInt(this.s.height, 10)) {
  1528. $lGallery.css('top', top + 'px');
  1529. } else {
  1530. $lGallery.css('top', '0px');
  1531. }
  1532. }
  1533. };
  1534. // Find css3 support
  1535. Plugin.prototype.doCss = function() {
  1536. // check for css animation support
  1537. var support = function() {
  1538. var transition = ['transition', 'MozTransition', 'WebkitTransition', 'OTransition', 'msTransition', 'KhtmlTransition'];
  1539. var root = document.documentElement;
  1540. var i = 0;
  1541. for (i = 0; i < transition.length; i++) {
  1542. if (transition[i] in root.style) {
  1543. return true;
  1544. }
  1545. }
  1546. };
  1547. if (support()) {
  1548. return true;
  1549. }
  1550. return false;
  1551. };
  1552. /**
  1553. * @desc Check the given src is video
  1554. * @param {String} src
  1555. * @return {Object} video type
  1556. * Ex:{ youtube : ["//www.youtube.com/watch?v=c0asJgSyxcY", "c0asJgSyxcY"] }
  1557. */
  1558. Plugin.prototype.isVideo = function(src, index) {
  1559. var html;
  1560. if (this.s.dynamic) {
  1561. html = this.s.dynamicEl[index].html;
  1562. } else {
  1563. html = this.$items.eq(index).attr('data-html');
  1564. }
  1565. if (!src) {
  1566. if(html) {
  1567. return {
  1568. html5: true
  1569. };
  1570. } else {
  1571. console.error('lightGallery :- data-src is not pvovided on slide item ' + (index + 1) + '. Please make sure the selector property is properly configured. More info - http://sachinchoolur.github.io/lightGallery/demos/html-markup.html');
  1572. return false;
  1573. }
  1574. }
  1575. var youtube = src.match(/\/\/(?:www\.)?youtu(?:\.be|be\.com|be-nocookie\.com)\/(?:watch\?v=|embed\/)?([a-z0-9\-\_\%]+)/i);
  1576. var vimeo = src.match(/\/\/(?:www\.)?vimeo.com\/([0-9a-z\-_]+)/i);
  1577. var dailymotion = src.match(/\/\/(?:www\.)?dai.ly\/([0-9a-z\-_]+)/i);
  1578. var vk = src.match(/\/\/(?:www\.)?(?:vk\.com|vkontakte\.ru)\/(?:video_ext\.php\?)(.*)/i);
  1579. if (youtube) {
  1580. return {
  1581. youtube: youtube
  1582. };
  1583. } else if (vimeo) {
  1584. return {
  1585. vimeo: vimeo
  1586. };
  1587. } else if (dailymotion) {
  1588. return {
  1589. dailymotion: dailymotion
  1590. };
  1591. } else if (vk) {
  1592. return {
  1593. vk: vk
  1594. };
  1595. }
  1596. };
  1597. /**
  1598. * @desc Create image counter
  1599. * Ex: 1/10
  1600. */
  1601. Plugin.prototype.counter = function() {
  1602. if (this.s.counter) {
  1603. $(this.s.appendCounterTo).append('<div id="lg-counter"><span id="lg-counter-current">' + (parseInt(this.index, 10) + 1) + '</span> / <span id="lg-counter-all">' + this.$items.length + '</span></div>');
  1604. }
  1605. };
  1606. /**
  1607. * @desc add sub-html into the slide
  1608. * @param {Number} index - index of the slide
  1609. */
  1610. Plugin.prototype.addHtml = function(index) {
  1611. var subHtml = null;
  1612. var subHtmlUrl;
  1613. var $currentEle;
  1614. if (this.s.dynamic) {
  1615. if (this.s.dynamicEl[index].subHtmlUrl) {
  1616. subHtmlUrl = this.s.dynamicEl[index].subHtmlUrl;
  1617. } else {
  1618. subHtml = this.s.dynamicEl[index].subHtml;
  1619. }
  1620. } else {
  1621. $currentEle = this.$items.eq(index);
  1622. if ($currentEle.attr('data-sub-html-url')) {
  1623. subHtmlUrl = $currentEle.attr('data-sub-html-url');
  1624. } else {
  1625. subHtml = $currentEle.attr('data-sub-html');
  1626. if (this.s.getCaptionFromTitleOrAlt && !subHtml) {
  1627. subHtml = $currentEle.attr('title') || $currentEle.find('img').first().attr('alt');
  1628. }
  1629. }
  1630. }
  1631. if (!subHtmlUrl) {
  1632. if (typeof subHtml !== 'undefined' && subHtml !== null) {
  1633. // get first letter of subhtml
  1634. // if first letter starts with . or # get the html form the jQuery object
  1635. var fL = subHtml.substring(0, 1);
  1636. if (fL === '.' || fL === '#') {
  1637. if (this.s.subHtmlSelectorRelative && !this.s.dynamic) {
  1638. subHtml = $currentEle.find(subHtml).html();
  1639. } else {
  1640. subHtml = $(subHtml).html();
  1641. }
  1642. }
  1643. } else {
  1644. subHtml = '';
  1645. }
  1646. }
  1647. if (this.s.appendSubHtmlTo === '.lg-sub-html') {
  1648. if (subHtmlUrl) {
  1649. this.$outer.find(this.s.appendSubHtmlTo).load(subHtmlUrl);
  1650. } else {
  1651. this.$outer.find(this.s.appendSubHtmlTo).html(subHtml);
  1652. }
  1653. } else {
  1654. if (subHtmlUrl) {
  1655. this.$slide.eq(index).load(subHtmlUrl);
  1656. } else {
  1657. this.$slide.eq(index).append(subHtml);
  1658. }
  1659. }
  1660. // Add lg-empty-html class if title doesn't exist
  1661. if (typeof subHtml !== 'undefined' && subHtml !== null) {
  1662. if (subHtml === '') {
  1663. this.$outer.find(this.s.appendSubHtmlTo).addClass('lg-empty-html');
  1664. } else {
  1665. this.$outer.find(this.s.appendSubHtmlTo).removeClass('lg-empty-html');
  1666. }
  1667. }
  1668. this.$el.trigger('onAfterAppendSubHtml.lg', [index]);
  1669. };
  1670. /**
  1671. * @desc Preload slides
  1672. * @param {Number} index - index of the slide
  1673. */
  1674. Plugin.prototype.preload = function(index) {
  1675. var i = 1;
  1676. var j = 1;
  1677. for (i = 1; i <= this.s.preload; i++) {
  1678. if (i >= this.$items.length - index) {
  1679. break;
  1680. }
  1681. this.loadContent(index + i, false, 0);
  1682. }
  1683. for (j = 1; j <= this.s.preload; j++) {
  1684. if (index - j < 0) {
  1685. break;
  1686. }
  1687. this.loadContent(index - j, false, 0);
  1688. }
  1689. };
  1690. /**
  1691. * @desc Load slide content into slide.
  1692. * @param {Number} index - index of the slide.
  1693. * @param {Boolean} rec - if true call loadcontent() function again.
  1694. * @param {Boolean} delay - delay for adding complete class. it is 0 except first time.
  1695. */
  1696. Plugin.prototype.loadContent = function(index, rec, delay) {
  1697. var _this = this;
  1698. var _hasPoster = false;
  1699. var _$img;
  1700. var _src;
  1701. var _poster;
  1702. var _srcset;
  1703. var _sizes;
  1704. var _html;
  1705. var getResponsiveSrc = function(srcItms) {
  1706. var rsWidth = [];
  1707. var rsSrc = [];
  1708. for (var i = 0; i < srcItms.length; i++) {
  1709. var __src = srcItms[i].split(' ');
  1710. // Manage empty space
  1711. if (__src[0] === '') {
  1712. __src.splice(0, 1);
  1713. }
  1714. rsSrc.push(__src[0]);
  1715. rsWidth.push(__src[1]);
  1716. }
  1717. var wWidth = $(window).width();
  1718. for (var j = 0; j < rsWidth.length; j++) {
  1719. if (parseInt(rsWidth[j], 10) > wWidth) {
  1720. _src = rsSrc[j];
  1721. break;
  1722. }
  1723. }
  1724. };
  1725. if (_this.s.dynamic) {
  1726. if (_this.s.dynamicEl[index].poster) {
  1727. _hasPoster = true;
  1728. _poster = _this.s.dynamicEl[index].poster;
  1729. }
  1730. _html = _this.s.dynamicEl[index].html;
  1731. _src = _this.s.dynamicEl[index].src;
  1732. if (_this.s.dynamicEl[index].responsive) {
  1733. var srcDyItms = _this.s.dynamicEl[index].responsive.split(',');
  1734. getResponsiveSrc(srcDyItms);
  1735. }
  1736. _srcset = _this.s.dynamicEl[index].srcset;
  1737. _sizes = _this.s.dynamicEl[index].sizes;
  1738. } else {
  1739. if (_this.$items.eq(index).attr('data-poster')) {
  1740. _hasPoster = true;
  1741. _poster = _this.$items.eq(index).attr('data-poster');
  1742. }
  1743. _html = _this.$items.eq(index).attr('data-html');
  1744. _src = _this.$items.eq(index).attr('href') || _this.$items.eq(index).attr('data-src');
  1745. if (_this.$items.eq(index).attr('data-responsive')) {
  1746. var srcItms = _this.$items.eq(index).attr('data-responsive').split(',');
  1747. getResponsiveSrc(srcItms);
  1748. }
  1749. _srcset = _this.$items.eq(index).attr('data-srcset');
  1750. _sizes = _this.$items.eq(index).attr('data-sizes');
  1751. }
  1752. //if (_src || _srcset || _sizes || _poster) {
  1753. var iframe = false;
  1754. if (_this.s.dynamic) {
  1755. if (_this.s.dynamicEl[index].iframe) {
  1756. iframe = true;
  1757. }
  1758. } else {
  1759. if (_this.$items.eq(index).attr('data-iframe') === 'true') {
  1760. iframe = true;
  1761. }
  1762. }
  1763. var _isVideo = _this.isVideo(_src, index);
  1764. if (!_this.$slide.eq(index).hasClass('lg-loaded')) {
  1765. if (iframe) {
  1766. _this.$slide.eq(index).prepend('<div class="lg-video-cont lg-has-iframe" style="max-width:' + _this.s.iframeMaxWidth + '"><div class="lg-video"><iframe class="lg-object" frameborder="0" src="' + _src + '" allowfullscreen="true"></iframe></div></div>');
  1767. } else if (_hasPoster) {
  1768. var videoClass = '';
  1769. if (_isVideo && _isVideo.youtube) {
  1770. videoClass = 'lg-has-youtube';
  1771. } else if (_isVideo && _isVideo.vimeo) {
  1772. videoClass = 'lg-has-vimeo';
  1773. } else {
  1774. videoClass = 'lg-has-html5';
  1775. }
  1776. _this.$slide.eq(index).prepend('<div class="lg-video-cont ' + videoClass + ' "><div class="lg-video"><span class="lg-video-play"></span><img class="lg-object lg-has-poster" src="' + _poster + '" /></div></div>');
  1777. } else if (_isVideo) {
  1778. _this.$slide.eq(index).prepend('<div class="lg-video-cont "><div class="lg-video"></div></div>');
  1779. _this.$el.trigger('hasVideo.lg', [index, _src, _html]);
  1780. } else {
  1781. _this.$slide.eq(index).prepend('<div class="lg-img-wrap"><img class="lg-object lg-image" src="' + _src + '" /></div>');
  1782. }
  1783. _this.$el.trigger('onAferAppendSlide.lg', [index]);
  1784. _$img = _this.$slide.eq(index).find('.lg-object');
  1785. if (_sizes) {
  1786. _$img.attr('sizes', _sizes);
  1787. }
  1788. if (_srcset) {
  1789. _$img.attr('srcset', _srcset);
  1790. try {
  1791. picturefill({
  1792. elements: [_$img[0]]
  1793. });
  1794. } catch (e) {
  1795. console.warn('lightGallery :- If you want srcset to be supported for older browser please include picturefil version 2 javascript library in your document.');
  1796. }
  1797. }
  1798. if (this.s.appendSubHtmlTo !== '.lg-sub-html') {
  1799. _this.addHtml(index);
  1800. }
  1801. _this.$slide.eq(index).addClass('lg-loaded');
  1802. }
  1803. _this.$slide.eq(index).find('.lg-object').on('load.lg error.lg', function() {
  1804. // For first time add some delay for displaying the start animation.
  1805. var _speed = 0;
  1806. // Do not change the delay value because it is required for zoom plugin.
  1807. // If gallery opened from direct url (hash) speed value should be 0
  1808. if (delay && !$('body').hasClass('lg-from-hash')) {
  1809. _speed = delay;
  1810. }
  1811. setTimeout(function() {
  1812. _this.$slide.eq(index).addClass('lg-complete');
  1813. _this.$el.trigger('onSlideItemLoad.lg', [index, delay || 0]);
  1814. }, _speed);
  1815. });
  1816. // @todo check load state for html5 videos
  1817. if (_isVideo && _isVideo.html5 && !_hasPoster) {
  1818. _this.$slide.eq(index).addClass('lg-complete');
  1819. }
  1820. if (rec === true) {
  1821. if (!_this.$slide.eq(index).hasClass('lg-complete')) {
  1822. _this.$slide.eq(index).find('.lg-object').on('load.lg error.lg', function() {
  1823. _this.preload(index);
  1824. });
  1825. } else {
  1826. _this.preload(index);
  1827. }
  1828. }
  1829. //}
  1830. };
  1831. /**
  1832. * @desc slide function for lightgallery
  1833. ** Slide() gets call on start
  1834. ** ** Set lg.on true once slide() function gets called.
  1835. ** Call loadContent() on slide() function inside setTimeout
  1836. ** ** On first slide we do not want any animation like slide of fade
  1837. ** ** So on first slide( if lg.on if false that is first slide) loadContent() should start loading immediately
  1838. ** ** Else loadContent() should wait for the transition to complete.
  1839. ** ** So set timeout s.speed + 50
  1840. <=> ** loadContent() will load slide content in to the particular slide
  1841. ** ** It has recursion (rec) parameter. if rec === true loadContent() will call preload() function.
  1842. ** ** preload will execute only when the previous slide is fully loaded (images iframe)
  1843. ** ** avoid simultaneous image load
  1844. <=> ** Preload() will check for s.preload value and call loadContent() again accoring to preload value
  1845. ** loadContent() <====> Preload();
  1846. * @param {Number} index - index of the slide
  1847. * @param {Boolean} fromTouch - true if slide function called via touch event or mouse drag
  1848. * @param {Boolean} fromThumb - true if slide function called via thumbnail click
  1849. * @param {String} direction - Direction of the slide(next/prev)
  1850. */
  1851. Plugin.prototype.slide = function(index, fromTouch, fromThumb, direction) {
  1852. var _prevIndex = this.$outer.find('.lg-current').index();
  1853. var _this = this;
  1854. // Prevent if multiple call
  1855. // Required for hsh plugin
  1856. if (_this.lGalleryOn && (_prevIndex === index)) {
  1857. return;
  1858. }
  1859. var _length = this.$slide.length;
  1860. var _time = _this.lGalleryOn ? this.s.speed : 0;
  1861. if (!_this.lgBusy) {
  1862. if (this.s.download) {
  1863. var _src;
  1864. if (_this.s.dynamic) {
  1865. _src = _this.s.dynamicEl[index].downloadUrl !== false && (_this.s.dynamicEl[index].downloadUrl || _this.s.dynamicEl[index].src);
  1866. } else {
  1867. _src = _this.$items.eq(index).attr('data-download-url') !== 'false' && (_this.$items.eq(index).attr('data-download-url') || _this.$items.eq(index).attr('href') || _this.$items.eq(index).attr('data-src'));
  1868. }
  1869. if (_src) {
  1870. $('#lg-download').attr('href', _src);
  1871. _this.$outer.removeClass('lg-hide-download');
  1872. } else {
  1873. _this.$outer.addClass('lg-hide-download');
  1874. }
  1875. }
  1876. this.$el.trigger('onBeforeSlide.lg', [_prevIndex, index, fromTouch, fromThumb]);
  1877. _this.lgBusy = true;
  1878. clearTimeout(_this.hideBartimeout);
  1879. // Add title if this.s.appendSubHtmlTo === lg-sub-html
  1880. if (this.s.appendSubHtmlTo === '.lg-sub-html') {
  1881. // wait for slide animation to complete
  1882. setTimeout(function() {
  1883. _this.addHtml(index);
  1884. }, _time);
  1885. }
  1886. this.arrowDisable(index);
  1887. if (!direction) {
  1888. if (index < _prevIndex) {
  1889. direction = 'prev';
  1890. } else if (index > _prevIndex) {
  1891. direction = 'next';
  1892. }
  1893. }
  1894. if (!fromTouch) {
  1895. // remove all transitions
  1896. _this.$outer.addClass('lg-no-trans');
  1897. this.$slide.removeClass('lg-prev-slide lg-next-slide');
  1898. if (direction === 'prev') {
  1899. //prevslide
  1900. this.$slide.eq(index).addClass('lg-prev-slide');
  1901. this.$slide.eq(_prevIndex).addClass('lg-next-slide');
  1902. } else {
  1903. // next slide
  1904. this.$slide.eq(index).addClass('lg-next-slide');
  1905. this.$slide.eq(_prevIndex).addClass('lg-prev-slide');
  1906. }
  1907. // give 50 ms for browser to add/remove class
  1908. setTimeout(function() {
  1909. _this.$slide.removeClass('lg-current');
  1910. //_this.$slide.eq(_prevIndex).removeClass('lg-current');
  1911. _this.$slide.eq(index).addClass('lg-current');
  1912. // reset all transitions
  1913. _this.$outer.removeClass('lg-no-trans');
  1914. }, 50);
  1915. } else {
  1916. this.$slide.removeClass('lg-prev-slide lg-current lg-next-slide');
  1917. var touchPrev;
  1918. var touchNext;
  1919. if (_length > 2) {
  1920. touchPrev = index - 1;
  1921. touchNext = index + 1;
  1922. if ((index === 0) && (_prevIndex === _length - 1)) {
  1923. // next slide
  1924. touchNext = 0;
  1925. touchPrev = _length - 1;
  1926. } else if ((index === _length - 1) && (_prevIndex === 0)) {
  1927. // prev slide
  1928. touchNext = 0;
  1929. touchPrev = _length - 1;
  1930. }
  1931. } else {
  1932. touchPrev = 0;
  1933. touchNext = 1;
  1934. }
  1935. if (direction === 'prev') {
  1936. _this.$slide.eq(touchNext).addClass('lg-next-slide');
  1937. } else {
  1938. _this.$slide.eq(touchPrev).addClass('lg-prev-slide');
  1939. }
  1940. _this.$slide.eq(index).addClass('lg-current');
  1941. }
  1942. if (_this.lGalleryOn) {
  1943. setTimeout(function() {
  1944. _this.loadContent(index, true, 0);
  1945. }, this.s.speed + 50);
  1946. setTimeout(function() {
  1947. _this.lgBusy = false;
  1948. _this.$el.trigger('onAfterSlide.lg', [_prevIndex, index, fromTouch, fromThumb]);
  1949. }, this.s.speed);
  1950. } else {
  1951. _this.loadContent(index, true, _this.s.backdropDuration);
  1952. _this.lgBusy = false;
  1953. _this.$el.trigger('onAfterSlide.lg', [_prevIndex, index, fromTouch, fromThumb]);
  1954. }
  1955. _this.lGalleryOn = true;
  1956. if (this.s.counter) {
  1957. $('#lg-counter-current').text(index + 1);
  1958. }
  1959. }
  1960. _this.index = index;
  1961. };
  1962. /**
  1963. * @desc Go to next slide
  1964. * @param {Boolean} fromTouch - true if slide function called via touch event
  1965. */
  1966. Plugin.prototype.goToNextSlide = function(fromTouch) {
  1967. var _this = this;
  1968. var _loop = _this.s.loop;
  1969. if (fromTouch && _this.$slide.length < 3) {
  1970. _loop = false;
  1971. }
  1972. if (!_this.lgBusy) {
  1973. if ((_this.index + 1) < _this.$slide.length) {
  1974. _this.index++;
  1975. _this.$el.trigger('onBeforeNextSlide.lg', [_this.index]);
  1976. _this.slide(_this.index, fromTouch, false, 'next');
  1977. } else {
  1978. if (_loop) {
  1979. _this.index = 0;
  1980. _this.$el.trigger('onBeforeNextSlide.lg', [_this.index]);
  1981. _this.slide(_this.index, fromTouch, false, 'next');
  1982. } else if (_this.s.slideEndAnimatoin && !fromTouch) {
  1983. _this.$outer.addClass('lg-right-end');
  1984. setTimeout(function() {
  1985. _this.$outer.removeClass('lg-right-end');
  1986. }, 400);
  1987. }
  1988. }
  1989. }
  1990. };
  1991. /**
  1992. * @desc Go to previous slide
  1993. * @param {Boolean} fromTouch - true if slide function called via touch event
  1994. */
  1995. Plugin.prototype.goToPrevSlide = function(fromTouch) {
  1996. var _this = this;
  1997. var _loop = _this.s.loop;
  1998. if (fromTouch && _this.$slide.length < 3) {
  1999. _loop = false;
  2000. }
  2001. if (!_this.lgBusy) {
  2002. if (_this.index > 0) {
  2003. _this.index--;
  2004. _this.$el.trigger('onBeforePrevSlide.lg', [_this.index, fromTouch]);
  2005. _this.slide(_this.index, fromTouch, false, 'prev');
  2006. } else {
  2007. if (_loop) {
  2008. _this.index = _this.$items.length - 1;
  2009. _this.$el.trigger('onBeforePrevSlide.lg', [_this.index, fromTouch]);
  2010. _this.slide(_this.index, fromTouch, false, 'prev');
  2011. } else if (_this.s.slideEndAnimatoin && !fromTouch) {
  2012. _this.$outer.addClass('lg-left-end');
  2013. setTimeout(function() {
  2014. _this.$outer.removeClass('lg-left-end');
  2015. }, 400);
  2016. }
  2017. }
  2018. }
  2019. };
  2020. Plugin.prototype.keyPress = function() {
  2021. var _this = this;
  2022. if (this.$items.length > 1) {
  2023. $(window).on('keyup.lg', function(e) {
  2024. if (_this.$items.length > 1) {
  2025. if (e.keyCode === 37) {
  2026. e.preventDefault();
  2027. _this.goToPrevSlide();
  2028. }
  2029. if (e.keyCode === 39) {
  2030. e.preventDefault();
  2031. _this.goToNextSlide();
  2032. }
  2033. }
  2034. });
  2035. }
  2036. $(window).on('keydown.lg', function(e) {
  2037. if (_this.s.escKey === true && e.keyCode === 27) {
  2038. e.preventDefault();
  2039. if (!_this.$outer.hasClass('lg-thumb-open')) {
  2040. _this.destroy();
  2041. } else {
  2042. _this.$outer.removeClass('lg-thumb-open');
  2043. }
  2044. }
  2045. });
  2046. };
  2047. Plugin.prototype.arrow = function() {
  2048. var _this = this;
  2049. this.$outer.find('.lg-prev').on('click.lg', function() {
  2050. _this.goToPrevSlide();
  2051. });
  2052. this.$outer.find('.lg-next').on('click.lg', function() {
  2053. _this.goToNextSlide();
  2054. });
  2055. };
  2056. Plugin.prototype.arrowDisable = function(index) {
  2057. // Disable arrows if s.hideControlOnEnd is true
  2058. if (!this.s.loop && this.s.hideControlOnEnd) {
  2059. if ((index + 1) < this.$slide.length) {
  2060. this.$outer.find('.lg-next').removeAttr('disabled').removeClass('disabled');
  2061. } else {
  2062. this.$outer.find('.lg-next').attr('disabled', 'disabled').addClass('disabled');
  2063. }
  2064. if (index > 0) {
  2065. this.$outer.find('.lg-prev').removeAttr('disabled').removeClass('disabled');
  2066. } else {
  2067. this.$outer.find('.lg-prev').attr('disabled', 'disabled').addClass('disabled');
  2068. }
  2069. }
  2070. };
  2071. Plugin.prototype.setTranslate = function($el, xValue, yValue) {
  2072. // jQuery supports Automatic CSS prefixing since jQuery 1.8.0
  2073. if (this.s.useLeft) {
  2074. $el.css('left', xValue);
  2075. } else {
  2076. $el.css({
  2077. transform: 'translate3d(' + (xValue) + 'px, ' + yValue + 'px, 0px)'
  2078. });
  2079. }
  2080. };
  2081. Plugin.prototype.touchMove = function(startCoords, endCoords) {
  2082. var distance = endCoords - startCoords;
  2083. if (Math.abs(distance) > 15) {
  2084. // reset opacity and transition duration
  2085. this.$outer.addClass('lg-dragging');
  2086. // move current slide
  2087. this.setTranslate(this.$slide.eq(this.index), distance, 0);
  2088. // move next and prev slide with current slide
  2089. this.setTranslate($('.lg-prev-slide'), -this.$slide.eq(this.index).width() + distance, 0);
  2090. this.setTranslate($('.lg-next-slide'), this.$slide.eq(this.index).width() + distance, 0);
  2091. }
  2092. };
  2093. Plugin.prototype.touchEnd = function(distance) {
  2094. var _this = this;
  2095. // keep slide animation for any mode while dragg/swipe
  2096. if (_this.s.mode !== 'lg-slide') {
  2097. _this.$outer.addClass('lg-slide');
  2098. }
  2099. this.$slide.not('.lg-current, .lg-prev-slide, .lg-next-slide').css('opacity', '0');
  2100. // set transition duration
  2101. setTimeout(function() {
  2102. _this.$outer.removeClass('lg-dragging');
  2103. if ((distance < 0) && (Math.abs(distance) > _this.s.swipeThreshold)) {
  2104. _this.goToNextSlide(true);
  2105. } else if ((distance > 0) && (Math.abs(distance) > _this.s.swipeThreshold)) {
  2106. _this.goToPrevSlide(true);
  2107. } else if (Math.abs(distance) < 5) {
  2108. // Trigger click if distance is less than 5 pix
  2109. _this.$el.trigger('onSlideClick.lg');
  2110. }
  2111. _this.$slide.removeAttr('style');
  2112. });
  2113. // remove slide class once drag/swipe is completed if mode is not slide
  2114. setTimeout(function() {
  2115. if (!_this.$outer.hasClass('lg-dragging') && _this.s.mode !== 'lg-slide') {
  2116. _this.$outer.removeClass('lg-slide');
  2117. }
  2118. }, _this.s.speed + 100);
  2119. };
  2120. Plugin.prototype.enableSwipe = function() {
  2121. var _this = this;
  2122. var startCoords = 0;
  2123. var endCoords = 0;
  2124. var isMoved = false;
  2125. if (_this.s.enableSwipe && _this.doCss()) {
  2126. _this.$slide.on('touchstart.lg', function(e) {
  2127. if (!_this.$outer.hasClass('lg-zoomed') && !_this.lgBusy) {
  2128. e.preventDefault();
  2129. _this.manageSwipeClass();
  2130. startCoords = e.originalEvent.targetTouches[0].pageX;
  2131. }
  2132. });
  2133. _this.$slide.on('touchmove.lg', function(e) {
  2134. if (!_this.$outer.hasClass('lg-zoomed')) {
  2135. e.preventDefault();
  2136. endCoords = e.originalEvent.targetTouches[0].pageX;
  2137. _this.touchMove(startCoords, endCoords);
  2138. isMoved = true;
  2139. }
  2140. });
  2141. _this.$slide.on('touchend.lg', function() {
  2142. if (!_this.$outer.hasClass('lg-zoomed')) {
  2143. if (isMoved) {
  2144. isMoved = false;
  2145. _this.touchEnd(endCoords - startCoords);
  2146. } else {
  2147. _this.$el.trigger('onSlideClick.lg');
  2148. }
  2149. }
  2150. });
  2151. }
  2152. };
  2153. Plugin.prototype.enableDrag = function() {
  2154. var _this = this;
  2155. var startCoords = 0;
  2156. var endCoords = 0;
  2157. var isDraging = false;
  2158. var isMoved = false;
  2159. if (_this.s.enableDrag && _this.doCss()) {
  2160. _this.$slide.on('mousedown.lg', function(e) {
  2161. if (!_this.$outer.hasClass('lg-zoomed') && !_this.lgBusy && !$(e.target).text().trim()) {
  2162. e.preventDefault();
  2163. _this.manageSwipeClass();
  2164. startCoords = e.pageX;
  2165. isDraging = true;
  2166. // ** Fix for webkit cursor issue https://code.google.com/p/chromium/issues/detail?id=26723
  2167. _this.$outer.scrollLeft += 1;
  2168. _this.$outer.scrollLeft -= 1;
  2169. // *
  2170. _this.$outer.removeClass('lg-grab').addClass('lg-grabbing');
  2171. _this.$el.trigger('onDragstart.lg');
  2172. }
  2173. });
  2174. $(window).on('mousemove.lg', function(e) {
  2175. if (isDraging) {
  2176. isMoved = true;
  2177. endCoords = e.pageX;
  2178. _this.touchMove(startCoords, endCoords);
  2179. _this.$el.trigger('onDragmove.lg');
  2180. }
  2181. });
  2182. $(window).on('mouseup.lg', function(e) {
  2183. if (isMoved) {
  2184. isMoved = false;
  2185. _this.touchEnd(endCoords - startCoords);
  2186. _this.$el.trigger('onDragend.lg');
  2187. } else if ($(e.target).hasClass('lg-object') || $(e.target).hasClass('lg-video-play')) {
  2188. _this.$el.trigger('onSlideClick.lg');
  2189. }
  2190. // Prevent execution on click
  2191. if (isDraging) {
  2192. isDraging = false;
  2193. _this.$outer.removeClass('lg-grabbing').addClass('lg-grab');
  2194. }
  2195. });
  2196. }
  2197. };
  2198. Plugin.prototype.manageSwipeClass = function() {
  2199. var _touchNext = this.index + 1;
  2200. var _touchPrev = this.index - 1;
  2201. if (this.s.loop && this.$slide.length > 2) {
  2202. if (this.index === 0) {
  2203. _touchPrev = this.$slide.length - 1;
  2204. } else if (this.index === this.$slide.length - 1) {
  2205. _touchNext = 0;
  2206. }
  2207. }
  2208. this.$slide.removeClass('lg-next-slide lg-prev-slide');
  2209. if (_touchPrev > -1) {
  2210. this.$slide.eq(_touchPrev).addClass('lg-prev-slide');
  2211. }
  2212. this.$slide.eq(_touchNext).addClass('lg-next-slide');
  2213. };
  2214. Plugin.prototype.mousewheel = function() {
  2215. var _this = this;
  2216. _this.$outer.on('mousewheel.lg', function(e) {
  2217. if (!e.deltaY) {
  2218. return;
  2219. }
  2220. if (e.deltaY > 0) {
  2221. _this.goToPrevSlide();
  2222. } else {
  2223. _this.goToNextSlide();
  2224. }
  2225. e.preventDefault();
  2226. });
  2227. };
  2228. Plugin.prototype.closeGallery = function() {
  2229. var _this = this;
  2230. var mousedown = false;
  2231. this.$outer.find('.lg-close').on('click.lg', function() {
  2232. _this.destroy();
  2233. });
  2234. if (_this.s.closable) {
  2235. // If you drag the slide and release outside gallery gets close on chrome
  2236. // for preventing this check mousedown and mouseup happened on .lg-item or lg-outer
  2237. _this.$outer.on('mousedown.lg', function(e) {
  2238. if ($(e.target).is('.lg-outer') || $(e.target).is('.lg-item ') || $(e.target).is('.lg-img-wrap')) {
  2239. mousedown = true;
  2240. } else {
  2241. mousedown = false;
  2242. }
  2243. });
  2244. _this.$outer.on('mousemove.lg', function() {
  2245. mousedown = false;
  2246. });
  2247. _this.$outer.on('mouseup.lg', function(e) {
  2248. if ($(e.target).is('.lg-outer') || $(e.target).is('.lg-item ') || $(e.target).is('.lg-img-wrap') && mousedown) {
  2249. if (!_this.$outer.hasClass('lg-dragging')) {
  2250. _this.destroy();
  2251. }
  2252. }
  2253. });
  2254. }
  2255. };
  2256. Plugin.prototype.destroy = function(d) {
  2257. var _this = this;
  2258. if (!d) {
  2259. _this.$el.trigger('onBeforeClose.lg');
  2260. $(window).scrollTop(_this.prevScrollTop);
  2261. }
  2262. /**
  2263. * if d is false or undefined destroy will only close the gallery
  2264. * plugins instance remains with the element
  2265. *
  2266. * if d is true destroy will completely remove the plugin
  2267. */
  2268. if (d) {
  2269. if (!_this.s.dynamic) {
  2270. // only when not using dynamic mode is $items a jquery collection
  2271. this.$items.off('click.lg click.lgcustom');
  2272. }
  2273. $.removeData(_this.el, 'lightGallery');
  2274. }
  2275. // Unbind all events added by lightGallery
  2276. this.$el.off('.lg.tm');
  2277. // Distroy all lightGallery modules
  2278. $.each($.fn.lightGallery.modules, function(key) {
  2279. if (_this.modules[key]) {
  2280. _this.modules[key].destroy();
  2281. }
  2282. });
  2283. this.lGalleryOn = false;
  2284. clearTimeout(_this.hideBartimeout);
  2285. this.hideBartimeout = false;
  2286. $(window).off('.lg');
  2287. $('body').removeClass('lg-on lg-from-hash');
  2288. if (_this.$outer) {
  2289. _this.$outer.removeClass('lg-visible');
  2290. }
  2291. $('.lg-backdrop').removeClass('in');
  2292. setTimeout(function() {
  2293. if (_this.$outer) {
  2294. _this.$outer.remove();
  2295. }
  2296. $('.lg-backdrop').remove();
  2297. if (!d) {
  2298. _this.$el.trigger('onCloseAfter.lg');
  2299. }
  2300. }, _this.s.backdropDuration + 50);
  2301. };
  2302. $.fn.lightGallery = function(options) {
  2303. return this.each(function() {
  2304. if (!$.data(this, 'lightGallery')) {
  2305. $.data(this, 'lightGallery', new Plugin(this, options));
  2306. } else {
  2307. try {
  2308. $(this).data('lightGallery').init();
  2309. } catch (err) {
  2310. console.error('lightGallery has not initiated properly');
  2311. }
  2312. }
  2313. });
  2314. };
  2315. $.fn.lightGallery.modules = {};
  2316. })();
  2317. /*! lg-autoplay - v1.0.4 - 2017-03-28
  2318. * http://sachinchoolur.github.io/lightGallery
  2319. * Copyright (c) 2017 Sachin N; Licensed GPLv3 */
  2320. (function (root, factory) {
  2321. if (typeof define === 'function' && define.amd) {
  2322. // AMD. Register as an anonymous module unless amdModuleId is set
  2323. define(['jquery'], function (a0) {
  2324. return (factory(a0));
  2325. });
  2326. } else if (typeof exports === 'object') {
  2327. // Node. Does not work with strict CommonJS, but
  2328. // only CommonJS-like environments that support module.exports,
  2329. // like Node.
  2330. module.exports = factory(require('jquery'));
  2331. } else {
  2332. factory(jQuery);
  2333. }
  2334. }(this, function ($) {
  2335. (function() {
  2336. 'use strict';
  2337. var defaults = {
  2338. autoplay: false,
  2339. pause: 5000,
  2340. progressBar: true,
  2341. fourceAutoplay: false,
  2342. autoplayControls: true,
  2343. appendAutoplayControlsTo: '.lg-toolbar'
  2344. };
  2345. /**
  2346. * Creates the autoplay plugin.
  2347. * @param {object} element - lightGallery element
  2348. */
  2349. var Autoplay = function(element) {
  2350. this.core = $(element).data('lightGallery');
  2351. this.$el = $(element);
  2352. // Execute only if items are above 1
  2353. if (this.core.$items.length < 2) {
  2354. return false;
  2355. }
  2356. this.core.s = $.extend({}, defaults, this.core.s);
  2357. this.interval = false;
  2358. // Identify if slide happened from autoplay
  2359. this.fromAuto = true;
  2360. // Identify if autoplay canceled from touch/drag
  2361. this.canceledOnTouch = false;
  2362. // save fourceautoplay value
  2363. this.fourceAutoplayTemp = this.core.s.fourceAutoplay;
  2364. // do not allow progress bar if browser does not support css3 transitions
  2365. if (!this.core.doCss()) {
  2366. this.core.s.progressBar = false;
  2367. }
  2368. this.init();
  2369. return this;
  2370. };
  2371. Autoplay.prototype.init = function() {
  2372. var _this = this;
  2373. // append autoplay controls
  2374. if (_this.core.s.autoplayControls) {
  2375. _this.controls();
  2376. }
  2377. // Create progress bar
  2378. if (_this.core.s.progressBar) {
  2379. _this.core.$outer.find('.lg').append('<div class="lg-progress-bar"><div class="lg-progress"></div></div>');
  2380. }
  2381. // set progress
  2382. _this.progress();
  2383. // Start autoplay
  2384. if (_this.core.s.autoplay) {
  2385. _this.$el.one('onSlideItemLoad.lg.tm', function() {
  2386. _this.startlAuto();
  2387. });
  2388. }
  2389. // cancel interval on touchstart and dragstart
  2390. _this.$el.on('onDragstart.lg.tm touchstart.lg.tm', function() {
  2391. if (_this.interval) {
  2392. _this.cancelAuto();
  2393. _this.canceledOnTouch = true;
  2394. }
  2395. });
  2396. // restore autoplay if autoplay canceled from touchstart / dragstart
  2397. _this.$el.on('onDragend.lg.tm touchend.lg.tm onSlideClick.lg.tm', function() {
  2398. if (!_this.interval && _this.canceledOnTouch) {
  2399. _this.startlAuto();
  2400. _this.canceledOnTouch = false;
  2401. }
  2402. });
  2403. };
  2404. Autoplay.prototype.progress = function() {
  2405. var _this = this;
  2406. var _$progressBar;
  2407. var _$progress;
  2408. _this.$el.on('onBeforeSlide.lg.tm', function() {
  2409. // start progress bar animation
  2410. if (_this.core.s.progressBar && _this.fromAuto) {
  2411. _$progressBar = _this.core.$outer.find('.lg-progress-bar');
  2412. _$progress = _this.core.$outer.find('.lg-progress');
  2413. if (_this.interval) {
  2414. _$progress.removeAttr('style');
  2415. _$progressBar.removeClass('lg-start');
  2416. setTimeout(function() {
  2417. _$progress.css('transition', 'width ' + (_this.core.s.speed + _this.core.s.pause) + 'ms ease 0s');
  2418. _$progressBar.addClass('lg-start');
  2419. }, 20);
  2420. }
  2421. }
  2422. // Remove setinterval if slide is triggered manually and fourceautoplay is false
  2423. if (!_this.fromAuto && !_this.core.s.fourceAutoplay) {
  2424. _this.cancelAuto();
  2425. }
  2426. _this.fromAuto = false;
  2427. });
  2428. };
  2429. // Manage autoplay via play/stop buttons
  2430. Autoplay.prototype.controls = function() {
  2431. var _this = this;
  2432. var _html = '<span class="lg-autoplay-button lg-icon"></span>';
  2433. // Append autoplay controls
  2434. $(this.core.s.appendAutoplayControlsTo).append(_html);
  2435. _this.core.$outer.find('.lg-autoplay-button').on('click.lg', function() {
  2436. if ($(_this.core.$outer).hasClass('lg-show-autoplay')) {
  2437. _this.cancelAuto();
  2438. _this.core.s.fourceAutoplay = false;
  2439. } else {
  2440. if (!_this.interval) {
  2441. _this.startlAuto();
  2442. _this.core.s.fourceAutoplay = _this.fourceAutoplayTemp;
  2443. }
  2444. }
  2445. });
  2446. };
  2447. // Autostart gallery
  2448. Autoplay.prototype.startlAuto = function() {
  2449. var _this = this;
  2450. _this.core.$outer.find('.lg-progress').css('transition', 'width ' + (_this.core.s.speed + _this.core.s.pause) + 'ms ease 0s');
  2451. _this.core.$outer.addClass('lg-show-autoplay');
  2452. _this.core.$outer.find('.lg-progress-bar').addClass('lg-start');
  2453. _this.interval = setInterval(function() {
  2454. if (_this.core.index + 1 < _this.core.$items.length) {
  2455. _this.core.index++;
  2456. } else {
  2457. _this.core.index = 0;
  2458. }
  2459. _this.fromAuto = true;
  2460. _this.core.slide(_this.core.index, false, false, 'next');
  2461. }, _this.core.s.speed + _this.core.s.pause);
  2462. };
  2463. // cancel Autostart
  2464. Autoplay.prototype.cancelAuto = function() {
  2465. clearInterval(this.interval);
  2466. this.interval = false;
  2467. this.core.$outer.find('.lg-progress').removeAttr('style');
  2468. this.core.$outer.removeClass('lg-show-autoplay');
  2469. this.core.$outer.find('.lg-progress-bar').removeClass('lg-start');
  2470. };
  2471. Autoplay.prototype.destroy = function() {
  2472. this.cancelAuto();
  2473. this.core.$outer.find('.lg-progress-bar').remove();
  2474. };
  2475. $.fn.lightGallery.modules.autoplay = Autoplay;
  2476. })();
  2477. }));
  2478. /*! lg-fullscreen - v1.1.0 - 2019-02-19
  2479. * http://sachinchoolur.github.io/lightGallery
  2480. * Copyright (c) 2019 Sachin N; Licensed GPLv3 */
  2481. (function (root, factory) {
  2482. if (typeof define === 'function' && define.amd) {
  2483. // AMD. Register as an anonymous module unless amdModuleId is set
  2484. define(['jquery'], function (a0) {
  2485. return (factory(a0));
  2486. });
  2487. } else if (typeof module === 'object' && module.exports) {
  2488. // Node. Does not work with strict CommonJS, but
  2489. // only CommonJS-like environments that support module.exports,
  2490. // like Node.
  2491. module.exports = factory(require('jquery'));
  2492. } else {
  2493. factory(root["jQuery"]);
  2494. }
  2495. }(this, function ($) {
  2496. (function() {
  2497. 'use strict';
  2498. var defaults = {
  2499. fullScreen: true
  2500. };
  2501. function isFullScreen() {
  2502. return (
  2503. document.fullscreenElement ||
  2504. document.mozFullScreenElement ||
  2505. document.webkitFullscreenElement ||
  2506. document.msFullscreenElement
  2507. );
  2508. }
  2509. var Fullscreen = function(element) {
  2510. // get lightGallery core plugin data
  2511. this.core = $(element).data('lightGallery');
  2512. this.$el = $(element);
  2513. // extend module defalut settings with lightGallery core settings
  2514. this.core.s = $.extend({}, defaults, this.core.s);
  2515. this.init();
  2516. return this;
  2517. };
  2518. Fullscreen.prototype.init = function() {
  2519. var fullScreen = '';
  2520. if (this.core.s.fullScreen) {
  2521. // check for fullscreen browser support
  2522. if (!document.fullscreenEnabled && !document.webkitFullscreenEnabled &&
  2523. !document.mozFullScreenEnabled && !document.msFullscreenEnabled) {
  2524. return;
  2525. } else {
  2526. fullScreen = '<span class="lg-fullscreen lg-icon"></span>';
  2527. this.core.$outer.find('.lg-toolbar').append(fullScreen);
  2528. this.fullScreen();
  2529. }
  2530. }
  2531. };
  2532. Fullscreen.prototype.requestFullscreen = function() {
  2533. var el = document.documentElement;
  2534. if (el.requestFullscreen) {
  2535. el.requestFullscreen();
  2536. } else if (el.msRequestFullscreen) {
  2537. el.msRequestFullscreen();
  2538. } else if (el.mozRequestFullScreen) {
  2539. el.mozRequestFullScreen();
  2540. } else if (el.webkitRequestFullscreen) {
  2541. el.webkitRequestFullscreen();
  2542. }
  2543. };
  2544. Fullscreen.prototype.exitFullscreen = function() {
  2545. if (document.exitFullscreen) {
  2546. document.exitFullscreen();
  2547. } else if (document.msExitFullscreen) {
  2548. document.msExitFullscreen();
  2549. } else if (document.mozCancelFullScreen) {
  2550. document.mozCancelFullScreen();
  2551. } else if (document.webkitExitFullscreen) {
  2552. document.webkitExitFullscreen();
  2553. }
  2554. };
  2555. // https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Using_full_screen_mode
  2556. Fullscreen.prototype.fullScreen = function() {
  2557. var _this = this;
  2558. $(document).on('fullscreenchange.lg webkitfullscreenchange.lg mozfullscreenchange.lg MSFullscreenChange.lg', function() {
  2559. _this.core.$outer.toggleClass('lg-fullscreen-on');
  2560. });
  2561. this.core.$outer.find('.lg-fullscreen').on('click.lg', function() {
  2562. if (isFullScreen()) {
  2563. _this.exitFullscreen();
  2564. } else {
  2565. _this.requestFullscreen();
  2566. }
  2567. });
  2568. };
  2569. Fullscreen.prototype.destroy = function() {
  2570. // exit from fullscreen if activated
  2571. if(isFullScreen()) {
  2572. this.exitFullscreen();
  2573. }
  2574. $(document).off('fullscreenchange.lg webkitfullscreenchange.lg mozfullscreenchange.lg MSFullscreenChange.lg');
  2575. };
  2576. $.fn.lightGallery.modules.fullscreen = Fullscreen;
  2577. })();
  2578. }));
  2579. /*! lg-hash - v1.0.4 - 2017-12-20
  2580. * http://sachinchoolur.github.io/lightGallery
  2581. * Copyright (c) 2017 Sachin N; Licensed GPLv3 */
  2582. (function (root, factory) {
  2583. if (typeof define === 'function' && define.amd) {
  2584. // AMD. Register as an anonymous module unless amdModuleId is set
  2585. define(['jquery'], function (a0) {
  2586. return (factory(a0));
  2587. });
  2588. } else if (typeof exports === 'object') {
  2589. // Node. Does not work with strict CommonJS, but
  2590. // only CommonJS-like environments that support module.exports,
  2591. // like Node.
  2592. module.exports = factory(require('jquery'));
  2593. } else {
  2594. factory(jQuery);
  2595. }
  2596. }(this, function ($) {
  2597. (function() {
  2598. 'use strict';
  2599. var defaults = {
  2600. hash: true
  2601. };
  2602. var Hash = function(element) {
  2603. this.core = $(element).data('lightGallery');
  2604. this.core.s = $.extend({}, defaults, this.core.s);
  2605. if (this.core.s.hash) {
  2606. this.oldHash = window.location.hash;
  2607. this.init();
  2608. }
  2609. return this;
  2610. };
  2611. Hash.prototype.init = function() {
  2612. var _this = this;
  2613. var _hash;
  2614. // Change hash value on after each slide transition
  2615. _this.core.$el.on('onAfterSlide.lg.tm', function(event, prevIndex, index) {
  2616. if (history.replaceState) {
  2617. history.replaceState(null, null, window.location.pathname + window.location.search + '#lg=' + _this.core.s.galleryId + '&slide=' + index);
  2618. } else {
  2619. window.location.hash = 'lg=' + _this.core.s.galleryId + '&slide=' + index;
  2620. }
  2621. });
  2622. // Listen hash change and change the slide according to slide value
  2623. $(window).on('hashchange.lg.hash', function() {
  2624. _hash = window.location.hash;
  2625. var _idx = parseInt(_hash.split('&slide=')[1], 10);
  2626. // it galleryId doesn't exist in the url close the gallery
  2627. if ((_hash.indexOf('lg=' + _this.core.s.galleryId) > -1)) {
  2628. _this.core.slide(_idx, false, false);
  2629. } else if (_this.core.lGalleryOn) {
  2630. _this.core.destroy();
  2631. }
  2632. });
  2633. };
  2634. Hash.prototype.destroy = function() {
  2635. if (!this.core.s.hash) {
  2636. return;
  2637. }
  2638. // Reset to old hash value
  2639. if (this.oldHash && this.oldHash.indexOf('lg=' + this.core.s.galleryId) < 0) {
  2640. if (history.replaceState) {
  2641. history.replaceState(null, null, this.oldHash);
  2642. } else {
  2643. window.location.hash = this.oldHash;
  2644. }
  2645. } else {
  2646. if (history.replaceState) {
  2647. history.replaceState(null, document.title, window.location.pathname + window.location.search);
  2648. } else {
  2649. window.location.hash = '';
  2650. }
  2651. }
  2652. this.core.$el.off('.lg.hash');
  2653. };
  2654. $.fn.lightGallery.modules.hash = Hash;
  2655. })();
  2656. }));
  2657. /*! lg-pager - v1.0.2 - 2017-01-22
  2658. * http://sachinchoolur.github.io/lightGallery
  2659. * Copyright (c) 2017 Sachin N; Licensed GPLv3 */
  2660. (function (root, factory) {
  2661. if (typeof define === 'function' && define.amd) {
  2662. // AMD. Register as an anonymous module unless amdModuleId is set
  2663. define(['jquery'], function (a0) {
  2664. return (factory(a0));
  2665. });
  2666. } else if (typeof exports === 'object') {
  2667. // Node. Does not work with strict CommonJS, but
  2668. // only CommonJS-like environments that support module.exports,
  2669. // like Node.
  2670. module.exports = factory(require('jquery'));
  2671. } else {
  2672. factory(jQuery);
  2673. }
  2674. }(this, function ($) {
  2675. (function() {
  2676. 'use strict';
  2677. var defaults = {
  2678. pager: false
  2679. };
  2680. var Pager = function(element) {
  2681. this.core = $(element).data('lightGallery');
  2682. this.$el = $(element);
  2683. this.core.s = $.extend({}, defaults, this.core.s);
  2684. if (this.core.s.pager && this.core.$items.length > 1) {
  2685. this.init();
  2686. }
  2687. return this;
  2688. };
  2689. Pager.prototype.init = function() {
  2690. var _this = this;
  2691. var pagerList = '';
  2692. var $pagerCont;
  2693. var $pagerOuter;
  2694. var timeout;
  2695. _this.core.$outer.find('.lg').append('<div class="lg-pager-outer"></div>');
  2696. if (_this.core.s.dynamic) {
  2697. for (var i = 0; i < _this.core.s.dynamicEl.length; i++) {
  2698. pagerList += '<span class="lg-pager-cont"> <span class="lg-pager"></span><div class="lg-pager-thumb-cont"><span class="lg-caret"></span> <img src="' + _this.core.s.dynamicEl[i].thumb + '" /></div></span>';
  2699. }
  2700. } else {
  2701. _this.core.$items.each(function() {
  2702. if (!_this.core.s.exThumbImage) {
  2703. pagerList += '<span class="lg-pager-cont"> <span class="lg-pager"></span><div class="lg-pager-thumb-cont"><span class="lg-caret"></span> <img src="' + $(this).find('img').attr('src') + '" /></div></span>';
  2704. } else {
  2705. pagerList += '<span class="lg-pager-cont"> <span class="lg-pager"></span><div class="lg-pager-thumb-cont"><span class="lg-caret"></span> <img src="' + $(this).attr(_this.core.s.exThumbImage) + '" /></div></span>';
  2706. }
  2707. });
  2708. }
  2709. $pagerOuter = _this.core.$outer.find('.lg-pager-outer');
  2710. $pagerOuter.html(pagerList);
  2711. $pagerCont = _this.core.$outer.find('.lg-pager-cont');
  2712. $pagerCont.on('click.lg touchend.lg', function() {
  2713. var _$this = $(this);
  2714. _this.core.index = _$this.index();
  2715. _this.core.slide(_this.core.index, false, true, false);
  2716. });
  2717. $pagerOuter.on('mouseover.lg', function() {
  2718. clearTimeout(timeout);
  2719. $pagerOuter.addClass('lg-pager-hover');
  2720. });
  2721. $pagerOuter.on('mouseout.lg', function() {
  2722. timeout = setTimeout(function() {
  2723. $pagerOuter.removeClass('lg-pager-hover');
  2724. });
  2725. });
  2726. _this.core.$el.on('onBeforeSlide.lg.tm', function(e, prevIndex, index) {
  2727. $pagerCont.removeClass('lg-pager-active');
  2728. $pagerCont.eq(index).addClass('lg-pager-active');
  2729. });
  2730. };
  2731. Pager.prototype.destroy = function() {
  2732. };
  2733. $.fn.lightGallery.modules.pager = Pager;
  2734. })();
  2735. }));
  2736. /*! lg-thumbnail - v1.1.0 - 2017-08-08
  2737. * http://sachinchoolur.github.io/lightGallery
  2738. * Copyright (c) 2017 Sachin N; Licensed GPLv3 */
  2739. (function (root, factory) {
  2740. if (typeof define === 'function' && define.amd) {
  2741. // AMD. Register as an anonymous module unless amdModuleId is set
  2742. define(['jquery'], function (a0) {
  2743. return (factory(a0));
  2744. });
  2745. } else if (typeof exports === 'object') {
  2746. // Node. Does not work with strict CommonJS, but
  2747. // only CommonJS-like environments that support module.exports,
  2748. // like Node.
  2749. module.exports = factory(require('jquery'));
  2750. } else {
  2751. factory(jQuery);
  2752. }
  2753. }(this, function ($) {
  2754. (function() {
  2755. 'use strict';
  2756. var defaults = {
  2757. thumbnail: true,
  2758. animateThumb: true,
  2759. currentPagerPosition: 'middle',
  2760. thumbWidth: 100,
  2761. thumbHeight: '80px',
  2762. thumbContHeight: 100,
  2763. thumbMargin: 5,
  2764. exThumbImage: false,
  2765. showThumbByDefault: true,
  2766. toogleThumb: true,
  2767. pullCaptionUp: true,
  2768. enableThumbDrag: true,
  2769. enableThumbSwipe: true,
  2770. swipeThreshold: 50,
  2771. loadYoutubeThumbnail: true,
  2772. youtubeThumbSize: 1,
  2773. loadVimeoThumbnail: true,
  2774. vimeoThumbSize: 'thumbnail_small',
  2775. loadDailymotionThumbnail: true
  2776. };
  2777. var Thumbnail = function(element) {
  2778. // get lightGallery core plugin data
  2779. this.core = $(element).data('lightGallery');
  2780. // extend module default settings with lightGallery core settings
  2781. this.core.s = $.extend({}, defaults, this.core.s);
  2782. this.$el = $(element);
  2783. this.$thumbOuter = null;
  2784. this.thumbOuterWidth = 0;
  2785. this.thumbTotalWidth = (this.core.$items.length * (this.core.s.thumbWidth + this.core.s.thumbMargin));
  2786. this.thumbIndex = this.core.index;
  2787. if (this.core.s.animateThumb) {
  2788. this.core.s.thumbHeight = '100%';
  2789. }
  2790. // Thumbnail animation value
  2791. this.left = 0;
  2792. this.init();
  2793. return this;
  2794. };
  2795. Thumbnail.prototype.init = function() {
  2796. var _this = this;
  2797. if (this.core.s.thumbnail && this.core.$items.length > 1) {
  2798. if (this.core.s.showThumbByDefault) {
  2799. setTimeout(function(){
  2800. _this.core.$outer.addClass('lg-thumb-open');
  2801. }, 700);
  2802. }
  2803. if (this.core.s.pullCaptionUp) {
  2804. this.core.$outer.addClass('lg-pull-caption-up');
  2805. }
  2806. this.build();
  2807. if (this.core.s.animateThumb && this.core.doCss()) {
  2808. if (this.core.s.enableThumbDrag) {
  2809. this.enableThumbDrag();
  2810. }
  2811. if (this.core.s.enableThumbSwipe) {
  2812. this.enableThumbSwipe();
  2813. }
  2814. this.thumbClickable = false;
  2815. } else {
  2816. this.thumbClickable = true;
  2817. }
  2818. this.toogle();
  2819. this.thumbkeyPress();
  2820. }
  2821. };
  2822. Thumbnail.prototype.build = function() {
  2823. var _this = this;
  2824. var thumbList = '';
  2825. var vimeoErrorThumbSize = '';
  2826. var $thumb;
  2827. var html = '<div class="lg-thumb-outer">' +
  2828. '<div class="lg-thumb lg-group">' +
  2829. '</div>' +
  2830. '</div>';
  2831. switch (this.core.s.vimeoThumbSize) {
  2832. case 'thumbnail_large':
  2833. vimeoErrorThumbSize = '640';
  2834. break;
  2835. case 'thumbnail_medium':
  2836. vimeoErrorThumbSize = '200x150';
  2837. break;
  2838. case 'thumbnail_small':
  2839. vimeoErrorThumbSize = '100x75';
  2840. }
  2841. _this.core.$outer.addClass('lg-has-thumb');
  2842. _this.core.$outer.find('.lg').append(html);
  2843. _this.$thumbOuter = _this.core.$outer.find('.lg-thumb-outer');
  2844. _this.thumbOuterWidth = _this.$thumbOuter.width();
  2845. if (_this.core.s.animateThumb) {
  2846. _this.core.$outer.find('.lg-thumb').css({
  2847. width: _this.thumbTotalWidth + 'px',
  2848. position: 'relative'
  2849. });
  2850. }
  2851. if (this.core.s.animateThumb) {
  2852. _this.$thumbOuter.css('height', _this.core.s.thumbContHeight + 'px');
  2853. }
  2854. function getThumb(src, thumb, index) {
  2855. var isVideo = _this.core.isVideo(src, index) || {};
  2856. var thumbImg;
  2857. var vimeoId = '';
  2858. if (isVideo.youtube || isVideo.vimeo || isVideo.dailymotion) {
  2859. if (isVideo.youtube) {
  2860. if (_this.core.s.loadYoutubeThumbnail) {
  2861. thumbImg = '//img.youtube.com/vi/' + isVideo.youtube[1] + '/' + _this.core.s.youtubeThumbSize + '.jpg';
  2862. } else {
  2863. thumbImg = thumb;
  2864. }
  2865. } else if (isVideo.vimeo) {
  2866. if (_this.core.s.loadVimeoThumbnail) {
  2867. thumbImg = '//i.vimeocdn.com/video/error_' + vimeoErrorThumbSize + '.jpg';
  2868. vimeoId = isVideo.vimeo[1];
  2869. } else {
  2870. thumbImg = thumb;
  2871. }
  2872. } else if (isVideo.dailymotion) {
  2873. if (_this.core.s.loadDailymotionThumbnail) {
  2874. thumbImg = '//www.dailymotion.com/thumbnail/video/' + isVideo.dailymotion[1];
  2875. } else {
  2876. thumbImg = thumb;
  2877. }
  2878. }
  2879. } else {
  2880. thumbImg = thumb;
  2881. }
  2882. thumbList += '<div data-vimeo-id="' + vimeoId + '" class="lg-thumb-item" style="width:' + _this.core.s.thumbWidth + 'px; height: ' + _this.core.s.thumbHeight + '; margin-right: ' + _this.core.s.thumbMargin + 'px"><img src="' + thumbImg + '" /></div>';
  2883. vimeoId = '';
  2884. }
  2885. if (_this.core.s.dynamic) {
  2886. for (var i = 0; i < _this.core.s.dynamicEl.length; i++) {
  2887. getThumb(_this.core.s.dynamicEl[i].src, _this.core.s.dynamicEl[i].thumb, i);
  2888. }
  2889. } else {
  2890. _this.core.$items.each(function(i) {
  2891. if (!_this.core.s.exThumbImage) {
  2892. getThumb($(this).attr('href') || $(this).attr('data-src'), $(this).find('img').attr('src'), i);
  2893. } else {
  2894. getThumb($(this).attr('href') || $(this).attr('data-src'), $(this).attr(_this.core.s.exThumbImage), i);
  2895. }
  2896. });
  2897. }
  2898. _this.core.$outer.find('.lg-thumb').html(thumbList);
  2899. $thumb = _this.core.$outer.find('.lg-thumb-item');
  2900. // Load vimeo thumbnails
  2901. $thumb.each(function() {
  2902. var $this = $(this);
  2903. var vimeoVideoId = $this.attr('data-vimeo-id');
  2904. if (vimeoVideoId) {
  2905. $.getJSON('//www.vimeo.com/api/v2/video/' + vimeoVideoId + '.json?callback=?', {
  2906. format: 'json'
  2907. }, function(data) {
  2908. $this.find('img').attr('src', data[0][_this.core.s.vimeoThumbSize]);
  2909. });
  2910. }
  2911. });
  2912. // manage active class for thumbnail
  2913. $thumb.eq(_this.core.index).addClass('active');
  2914. _this.core.$el.on('onBeforeSlide.lg.tm', function() {
  2915. $thumb.removeClass('active');
  2916. $thumb.eq(_this.core.index).addClass('active');
  2917. });
  2918. $thumb.on('click.lg touchend.lg', function() {
  2919. var _$this = $(this);
  2920. setTimeout(function() {
  2921. // In IE9 and bellow touch does not support
  2922. // Go to slide if browser does not support css transitions
  2923. if ((_this.thumbClickable && !_this.core.lgBusy) || !_this.core.doCss()) {
  2924. _this.core.index = _$this.index();
  2925. _this.core.slide(_this.core.index, false, true, false);
  2926. }
  2927. }, 50);
  2928. });
  2929. _this.core.$el.on('onBeforeSlide.lg.tm', function() {
  2930. _this.animateThumb(_this.core.index);
  2931. });
  2932. $(window).on('resize.lg.thumb orientationchange.lg.thumb', function() {
  2933. setTimeout(function() {
  2934. _this.animateThumb(_this.core.index);
  2935. _this.thumbOuterWidth = _this.$thumbOuter.width();
  2936. }, 200);
  2937. });
  2938. };
  2939. Thumbnail.prototype.setTranslate = function(value) {
  2940. // jQuery supports Automatic CSS prefixing since jQuery 1.8.0
  2941. this.core.$outer.find('.lg-thumb').css({
  2942. transform: 'translate3d(-' + (value) + 'px, 0px, 0px)'
  2943. });
  2944. };
  2945. Thumbnail.prototype.animateThumb = function(index) {
  2946. var $thumb = this.core.$outer.find('.lg-thumb');
  2947. if (this.core.s.animateThumb) {
  2948. var position;
  2949. switch (this.core.s.currentPagerPosition) {
  2950. case 'left':
  2951. position = 0;
  2952. break;
  2953. case 'middle':
  2954. position = (this.thumbOuterWidth / 2) - (this.core.s.thumbWidth / 2);
  2955. break;
  2956. case 'right':
  2957. position = this.thumbOuterWidth - this.core.s.thumbWidth;
  2958. }
  2959. this.left = ((this.core.s.thumbWidth + this.core.s.thumbMargin) * index - 1) - position;
  2960. if (this.left > (this.thumbTotalWidth - this.thumbOuterWidth)) {
  2961. this.left = this.thumbTotalWidth - this.thumbOuterWidth;
  2962. }
  2963. if (this.left < 0) {
  2964. this.left = 0;
  2965. }
  2966. if (this.core.lGalleryOn) {
  2967. if (!$thumb.hasClass('on')) {
  2968. this.core.$outer.find('.lg-thumb').css('transition-duration', this.core.s.speed + 'ms');
  2969. }
  2970. if (!this.core.doCss()) {
  2971. $thumb.animate({
  2972. left: -this.left + 'px'
  2973. }, this.core.s.speed);
  2974. }
  2975. } else {
  2976. if (!this.core.doCss()) {
  2977. $thumb.css('left', -this.left + 'px');
  2978. }
  2979. }
  2980. this.setTranslate(this.left);
  2981. }
  2982. };
  2983. // Enable thumbnail dragging and swiping
  2984. Thumbnail.prototype.enableThumbDrag = function() {
  2985. var _this = this;
  2986. var startCoords = 0;
  2987. var endCoords = 0;
  2988. var isDraging = false;
  2989. var isMoved = false;
  2990. var tempLeft = 0;
  2991. _this.$thumbOuter.addClass('lg-grab');
  2992. _this.core.$outer.find('.lg-thumb').on('mousedown.lg.thumb', function(e) {
  2993. if (_this.thumbTotalWidth > _this.thumbOuterWidth) {
  2994. // execute only on .lg-object
  2995. e.preventDefault();
  2996. startCoords = e.pageX;
  2997. isDraging = true;
  2998. // ** Fix for webkit cursor issue https://code.google.com/p/chromium/issues/detail?id=26723
  2999. _this.core.$outer.scrollLeft += 1;
  3000. _this.core.$outer.scrollLeft -= 1;
  3001. // *
  3002. _this.thumbClickable = false;
  3003. _this.$thumbOuter.removeClass('lg-grab').addClass('lg-grabbing');
  3004. }
  3005. });
  3006. $(window).on('mousemove.lg.thumb', function(e) {
  3007. if (isDraging) {
  3008. tempLeft = _this.left;
  3009. isMoved = true;
  3010. endCoords = e.pageX;
  3011. _this.$thumbOuter.addClass('lg-dragging');
  3012. tempLeft = tempLeft - (endCoords - startCoords);
  3013. if (tempLeft > (_this.thumbTotalWidth - _this.thumbOuterWidth)) {
  3014. tempLeft = _this.thumbTotalWidth - _this.thumbOuterWidth;
  3015. }
  3016. if (tempLeft < 0) {
  3017. tempLeft = 0;
  3018. }
  3019. // move current slide
  3020. _this.setTranslate(tempLeft);
  3021. }
  3022. });
  3023. $(window).on('mouseup.lg.thumb', function() {
  3024. if (isMoved) {
  3025. isMoved = false;
  3026. _this.$thumbOuter.removeClass('lg-dragging');
  3027. _this.left = tempLeft;
  3028. if (Math.abs(endCoords - startCoords) < _this.core.s.swipeThreshold) {
  3029. _this.thumbClickable = true;
  3030. }
  3031. } else {
  3032. _this.thumbClickable = true;
  3033. }
  3034. if (isDraging) {
  3035. isDraging = false;
  3036. _this.$thumbOuter.removeClass('lg-grabbing').addClass('lg-grab');
  3037. }
  3038. });
  3039. };
  3040. Thumbnail.prototype.enableThumbSwipe = function() {
  3041. var _this = this;
  3042. var startCoords = 0;
  3043. var endCoords = 0;
  3044. var isMoved = false;
  3045. var tempLeft = 0;
  3046. _this.core.$outer.find('.lg-thumb').on('touchstart.lg', function(e) {
  3047. if (_this.thumbTotalWidth > _this.thumbOuterWidth) {
  3048. e.preventDefault();
  3049. startCoords = e.originalEvent.targetTouches[0].pageX;
  3050. _this.thumbClickable = false;
  3051. }
  3052. });
  3053. _this.core.$outer.find('.lg-thumb').on('touchmove.lg', function(e) {
  3054. if (_this.thumbTotalWidth > _this.thumbOuterWidth) {
  3055. e.preventDefault();
  3056. endCoords = e.originalEvent.targetTouches[0].pageX;
  3057. isMoved = true;
  3058. _this.$thumbOuter.addClass('lg-dragging');
  3059. tempLeft = _this.left;
  3060. tempLeft = tempLeft - (endCoords - startCoords);
  3061. if (tempLeft > (_this.thumbTotalWidth - _this.thumbOuterWidth)) {
  3062. tempLeft = _this.thumbTotalWidth - _this.thumbOuterWidth;
  3063. }
  3064. if (tempLeft < 0) {
  3065. tempLeft = 0;
  3066. }
  3067. // move current slide
  3068. _this.setTranslate(tempLeft);
  3069. }
  3070. });
  3071. _this.core.$outer.find('.lg-thumb').on('touchend.lg', function() {
  3072. if (_this.thumbTotalWidth > _this.thumbOuterWidth) {
  3073. if (isMoved) {
  3074. isMoved = false;
  3075. _this.$thumbOuter.removeClass('lg-dragging');
  3076. if (Math.abs(endCoords - startCoords) < _this.core.s.swipeThreshold) {
  3077. _this.thumbClickable = true;
  3078. }
  3079. _this.left = tempLeft;
  3080. } else {
  3081. _this.thumbClickable = true;
  3082. }
  3083. } else {
  3084. _this.thumbClickable = true;
  3085. }
  3086. });
  3087. };
  3088. Thumbnail.prototype.toogle = function() {
  3089. var _this = this;
  3090. if (_this.core.s.toogleThumb) {
  3091. _this.core.$outer.addClass('lg-can-toggle');
  3092. _this.$thumbOuter.append('<span class="lg-toogle-thumb lg-icon"></span>');
  3093. _this.core.$outer.find('.lg-toogle-thumb').on('click.lg', function() {
  3094. _this.core.$outer.toggleClass('lg-thumb-open');
  3095. });
  3096. }
  3097. };
  3098. Thumbnail.prototype.thumbkeyPress = function() {
  3099. var _this = this;
  3100. $(window).on('keydown.lg.thumb', function(e) {
  3101. if (e.keyCode === 38) {
  3102. e.preventDefault();
  3103. _this.core.$outer.addClass('lg-thumb-open');
  3104. } else if (e.keyCode === 40) {
  3105. e.preventDefault();
  3106. _this.core.$outer.removeClass('lg-thumb-open');
  3107. }
  3108. });
  3109. };
  3110. Thumbnail.prototype.destroy = function() {
  3111. if (this.core.s.thumbnail && this.core.$items.length > 1) {
  3112. $(window).off('resize.lg.thumb orientationchange.lg.thumb keydown.lg.thumb');
  3113. this.$thumbOuter.remove();
  3114. this.core.$outer.removeClass('lg-has-thumb');
  3115. }
  3116. };
  3117. $.fn.lightGallery.modules.Thumbnail = Thumbnail;
  3118. })();
  3119. }));
  3120. /*! lg-zoom - v1.1.0 - 2017-08-08
  3121. * http://sachinchoolur.github.io/lightGallery
  3122. * Copyright (c) 2017 Sachin N; Licensed GPLv3 */
  3123. (function (root, factory) {
  3124. if (typeof define === 'function' && define.amd) {
  3125. // AMD. Register as an anonymous module unless amdModuleId is set
  3126. define(['jquery'], function (a0) {
  3127. return (factory(a0));
  3128. });
  3129. } else if (typeof exports === 'object') {
  3130. // Node. Does not work with strict CommonJS, but
  3131. // only CommonJS-like environments that support module.exports,
  3132. // like Node.
  3133. module.exports = factory(require('jquery'));
  3134. } else {
  3135. factory(jQuery);
  3136. }
  3137. }(this, function ($) {
  3138. (function() {
  3139. 'use strict';
  3140. var getUseLeft = function() {
  3141. var useLeft = false;
  3142. var isChrome = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);
  3143. if (isChrome && parseInt(isChrome[2], 10) < 54) {
  3144. useLeft = true;
  3145. }
  3146. return useLeft;
  3147. };
  3148. var defaults = {
  3149. scale: 1,
  3150. zoom: true,
  3151. actualSize: true,
  3152. enableZoomAfter: 300,
  3153. useLeftForZoom: getUseLeft()
  3154. };
  3155. var Zoom = function(element) {
  3156. this.core = $(element).data('lightGallery');
  3157. this.core.s = $.extend({}, defaults, this.core.s);
  3158. if (this.core.s.zoom && this.core.doCss()) {
  3159. this.init();
  3160. // Store the zoomable timeout value just to clear it while closing
  3161. this.zoomabletimeout = false;
  3162. // Set the initial value center
  3163. this.pageX = $(window).width() / 2;
  3164. this.pageY = ($(window).height() / 2) + $(window).scrollTop();
  3165. }
  3166. return this;
  3167. };
  3168. Zoom.prototype.init = function() {
  3169. var _this = this;
  3170. var zoomIcons = '<span id="lg-zoom-in" class="lg-icon"></span><span id="lg-zoom-out" class="lg-icon"></span>';
  3171. if (_this.core.s.actualSize) {
  3172. zoomIcons += '<span id="lg-actual-size" class="lg-icon"></span>';
  3173. }
  3174. if (_this.core.s.useLeftForZoom) {
  3175. _this.core.$outer.addClass('lg-use-left-for-zoom');
  3176. } else {
  3177. _this.core.$outer.addClass('lg-use-transition-for-zoom');
  3178. }
  3179. this.core.$outer.find('.lg-toolbar').append(zoomIcons);
  3180. // Add zoomable class
  3181. _this.core.$el.on('onSlideItemLoad.lg.tm.zoom', function(event, index, delay) {
  3182. // delay will be 0 except first time
  3183. var _speed = _this.core.s.enableZoomAfter + delay;
  3184. // set _speed value 0 if gallery opened from direct url and if it is first slide
  3185. if ($('body').hasClass('lg-from-hash') && delay) {
  3186. // will execute only once
  3187. _speed = 0;
  3188. } else {
  3189. // Remove lg-from-hash to enable starting animation.
  3190. $('body').removeClass('lg-from-hash');
  3191. }
  3192. _this.zoomabletimeout = setTimeout(function() {
  3193. _this.core.$slide.eq(index).addClass('lg-zoomable');
  3194. }, _speed + 30);
  3195. });
  3196. var scale = 1;
  3197. /**
  3198. * @desc Image zoom
  3199. * Translate the wrap and scale the image to get better user experience
  3200. *
  3201. * @param {String} scaleVal - Zoom decrement/increment value
  3202. */
  3203. var zoom = function(scaleVal) {
  3204. var $image = _this.core.$outer.find('.lg-current .lg-image');
  3205. var _x;
  3206. var _y;
  3207. // Find offset manually to avoid issue after zoom
  3208. var offsetX = ($(window).width() - $image.prop('offsetWidth')) / 2;
  3209. var offsetY = (($(window).height() - $image.prop('offsetHeight')) / 2) + $(window).scrollTop();
  3210. _x = _this.pageX - offsetX;
  3211. _y = _this.pageY - offsetY;
  3212. var x = (scaleVal - 1) * (_x);
  3213. var y = (scaleVal - 1) * (_y);
  3214. $image.css('transform', 'scale3d(' + scaleVal + ', ' + scaleVal + ', 1)').attr('data-scale', scaleVal);
  3215. if (_this.core.s.useLeftForZoom) {
  3216. $image.parent().css({
  3217. left: -x + 'px',
  3218. top: -y + 'px'
  3219. }).attr('data-x', x).attr('data-y', y);
  3220. } else {
  3221. $image.parent().css('transform', 'translate3d(-' + x + 'px, -' + y + 'px, 0)').attr('data-x', x).attr('data-y', y);
  3222. }
  3223. };
  3224. var callScale = function() {
  3225. if (scale > 1) {
  3226. _this.core.$outer.addClass('lg-zoomed');
  3227. } else {
  3228. _this.resetZoom();
  3229. }
  3230. if (scale < 1) {
  3231. scale = 1;
  3232. }
  3233. zoom(scale);
  3234. };
  3235. var actualSize = function(event, $image, index, fromIcon) {
  3236. var w = $image.prop('offsetWidth');
  3237. var nw;
  3238. if (_this.core.s.dynamic) {
  3239. nw = _this.core.s.dynamicEl[index].width || $image[0].naturalWidth || w;
  3240. } else {
  3241. nw = _this.core.$items.eq(index).attr('data-width') || $image[0].naturalWidth || w;
  3242. }
  3243. var _scale;
  3244. if (_this.core.$outer.hasClass('lg-zoomed')) {
  3245. scale = 1;
  3246. } else {
  3247. if (nw > w) {
  3248. _scale = nw / w;
  3249. scale = _scale || 2;
  3250. }
  3251. }
  3252. if (fromIcon) {
  3253. _this.pageX = $(window).width() / 2;
  3254. _this.pageY = ($(window).height() / 2) + $(window).scrollTop();
  3255. } else {
  3256. _this.pageX = event.pageX || event.originalEvent.targetTouches[0].pageX;
  3257. _this.pageY = event.pageY || event.originalEvent.targetTouches[0].pageY;
  3258. }
  3259. callScale();
  3260. setTimeout(function() {
  3261. _this.core.$outer.removeClass('lg-grabbing').addClass('lg-grab');
  3262. }, 10);
  3263. };
  3264. var tapped = false;
  3265. // event triggered after appending slide content
  3266. _this.core.$el.on('onAferAppendSlide.lg.tm.zoom', function(event, index) {
  3267. // Get the current element
  3268. var $image = _this.core.$slide.eq(index).find('.lg-image');
  3269. $image.on('dblclick', function(event) {
  3270. actualSize(event, $image, index);
  3271. });
  3272. $image.on('touchstart', function(event) {
  3273. if (!tapped) {
  3274. tapped = setTimeout(function() {
  3275. tapped = null;
  3276. }, 300);
  3277. } else {
  3278. clearTimeout(tapped);
  3279. tapped = null;
  3280. actualSize(event, $image, index);
  3281. }
  3282. event.preventDefault();
  3283. });
  3284. });
  3285. // Update zoom on resize and orientationchange
  3286. $(window).on('resize.lg.zoom scroll.lg.zoom orientationchange.lg.zoom', function() {
  3287. _this.pageX = $(window).width() / 2;
  3288. _this.pageY = ($(window).height() / 2) + $(window).scrollTop();
  3289. zoom(scale);
  3290. });
  3291. $('#lg-zoom-out').on('click.lg', function() {
  3292. if (_this.core.$outer.find('.lg-current .lg-image').length) {
  3293. scale -= _this.core.s.scale;
  3294. callScale();
  3295. }
  3296. });
  3297. $('#lg-zoom-in').on('click.lg', function() {
  3298. if (_this.core.$outer.find('.lg-current .lg-image').length) {
  3299. scale += _this.core.s.scale;
  3300. callScale();
  3301. }
  3302. });
  3303. $('#lg-actual-size').on('click.lg', function(event) {
  3304. actualSize(event, _this.core.$slide.eq(_this.core.index).find('.lg-image'), _this.core.index, true);
  3305. });
  3306. // Reset zoom on slide change
  3307. _this.core.$el.on('onBeforeSlide.lg.tm', function() {
  3308. scale = 1;
  3309. _this.resetZoom();
  3310. });
  3311. // Drag option after zoom
  3312. _this.zoomDrag();
  3313. _this.zoomSwipe();
  3314. };
  3315. // Reset zoom effect
  3316. Zoom.prototype.resetZoom = function() {
  3317. this.core.$outer.removeClass('lg-zoomed');
  3318. this.core.$slide.find('.lg-img-wrap').removeAttr('style data-x data-y');
  3319. this.core.$slide.find('.lg-image').removeAttr('style data-scale');
  3320. // Reset pagx pagy values to center
  3321. this.pageX = $(window).width() / 2;
  3322. this.pageY = ($(window).height() / 2) + $(window).scrollTop();
  3323. };
  3324. Zoom.prototype.zoomSwipe = function() {
  3325. var _this = this;
  3326. var startCoords = {};
  3327. var endCoords = {};
  3328. var isMoved = false;
  3329. // Allow x direction drag
  3330. var allowX = false;
  3331. // Allow Y direction drag
  3332. var allowY = false;
  3333. _this.core.$slide.on('touchstart.lg', function(e) {
  3334. if (_this.core.$outer.hasClass('lg-zoomed')) {
  3335. var $image = _this.core.$slide.eq(_this.core.index).find('.lg-object');
  3336. allowY = $image.prop('offsetHeight') * $image.attr('data-scale') > _this.core.$outer.find('.lg').height();
  3337. allowX = $image.prop('offsetWidth') * $image.attr('data-scale') > _this.core.$outer.find('.lg').width();
  3338. if ((allowX || allowY)) {
  3339. e.preventDefault();
  3340. startCoords = {
  3341. x: e.originalEvent.targetTouches[0].pageX,
  3342. y: e.originalEvent.targetTouches[0].pageY
  3343. };
  3344. }
  3345. }
  3346. });
  3347. _this.core.$slide.on('touchmove.lg', function(e) {
  3348. if (_this.core.$outer.hasClass('lg-zoomed')) {
  3349. var _$el = _this.core.$slide.eq(_this.core.index).find('.lg-img-wrap');
  3350. var distanceX;
  3351. var distanceY;
  3352. e.preventDefault();
  3353. isMoved = true;
  3354. endCoords = {
  3355. x: e.originalEvent.targetTouches[0].pageX,
  3356. y: e.originalEvent.targetTouches[0].pageY
  3357. };
  3358. // reset opacity and transition duration
  3359. _this.core.$outer.addClass('lg-zoom-dragging');
  3360. if (allowY) {
  3361. distanceY = (-Math.abs(_$el.attr('data-y'))) + (endCoords.y - startCoords.y);
  3362. } else {
  3363. distanceY = -Math.abs(_$el.attr('data-y'));
  3364. }
  3365. if (allowX) {
  3366. distanceX = (-Math.abs(_$el.attr('data-x'))) + (endCoords.x - startCoords.x);
  3367. } else {
  3368. distanceX = -Math.abs(_$el.attr('data-x'));
  3369. }
  3370. if ((Math.abs(endCoords.x - startCoords.x) > 15) || (Math.abs(endCoords.y - startCoords.y) > 15)) {
  3371. if (_this.core.s.useLeftForZoom) {
  3372. _$el.css({
  3373. left: distanceX + 'px',
  3374. top: distanceY + 'px'
  3375. });
  3376. } else {
  3377. _$el.css('transform', 'translate3d(' + distanceX + 'px, ' + distanceY + 'px, 0)');
  3378. }
  3379. }
  3380. }
  3381. });
  3382. _this.core.$slide.on('touchend.lg', function() {
  3383. if (_this.core.$outer.hasClass('lg-zoomed')) {
  3384. if (isMoved) {
  3385. isMoved = false;
  3386. _this.core.$outer.removeClass('lg-zoom-dragging');
  3387. _this.touchendZoom(startCoords, endCoords, allowX, allowY);
  3388. }
  3389. }
  3390. });
  3391. };
  3392. Zoom.prototype.zoomDrag = function() {
  3393. var _this = this;
  3394. var startCoords = {};
  3395. var endCoords = {};
  3396. var isDraging = false;
  3397. var isMoved = false;
  3398. // Allow x direction drag
  3399. var allowX = false;
  3400. // Allow Y direction drag
  3401. var allowY = false;
  3402. _this.core.$slide.on('mousedown.lg.zoom', function(e) {
  3403. // execute only on .lg-object
  3404. var $image = _this.core.$slide.eq(_this.core.index).find('.lg-object');
  3405. allowY = $image.prop('offsetHeight') * $image.attr('data-scale') > _this.core.$outer.find('.lg').height();
  3406. allowX = $image.prop('offsetWidth') * $image.attr('data-scale') > _this.core.$outer.find('.lg').width();
  3407. if (_this.core.$outer.hasClass('lg-zoomed')) {
  3408. if ($(e.target).hasClass('lg-object') && (allowX || allowY)) {
  3409. e.preventDefault();
  3410. startCoords = {
  3411. x: e.pageX,
  3412. y: e.pageY
  3413. };
  3414. isDraging = true;
  3415. // ** Fix for webkit cursor issue https://code.google.com/p/chromium/issues/detail?id=26723
  3416. _this.core.$outer.scrollLeft += 1;
  3417. _this.core.$outer.scrollLeft -= 1;
  3418. _this.core.$outer.removeClass('lg-grab').addClass('lg-grabbing');
  3419. }
  3420. }
  3421. });
  3422. $(window).on('mousemove.lg.zoom', function(e) {
  3423. if (isDraging) {
  3424. var _$el = _this.core.$slide.eq(_this.core.index).find('.lg-img-wrap');
  3425. var distanceX;
  3426. var distanceY;
  3427. isMoved = true;
  3428. endCoords = {
  3429. x: e.pageX,
  3430. y: e.pageY
  3431. };
  3432. // reset opacity and transition duration
  3433. _this.core.$outer.addClass('lg-zoom-dragging');
  3434. if (allowY) {
  3435. distanceY = (-Math.abs(_$el.attr('data-y'))) + (endCoords.y - startCoords.y);
  3436. } else {
  3437. distanceY = -Math.abs(_$el.attr('data-y'));
  3438. }
  3439. if (allowX) {
  3440. distanceX = (-Math.abs(_$el.attr('data-x'))) + (endCoords.x - startCoords.x);
  3441. } else {
  3442. distanceX = -Math.abs(_$el.attr('data-x'));
  3443. }
  3444. if (_this.core.s.useLeftForZoom) {
  3445. _$el.css({
  3446. left: distanceX + 'px',
  3447. top: distanceY + 'px'
  3448. });
  3449. } else {
  3450. _$el.css('transform', 'translate3d(' + distanceX + 'px, ' + distanceY + 'px, 0)');
  3451. }
  3452. }
  3453. });
  3454. $(window).on('mouseup.lg.zoom', function(e) {
  3455. if (isDraging) {
  3456. isDraging = false;
  3457. _this.core.$outer.removeClass('lg-zoom-dragging');
  3458. // Fix for chrome mouse move on click
  3459. if (isMoved && ((startCoords.x !== endCoords.x) || (startCoords.y !== endCoords.y))) {
  3460. endCoords = {
  3461. x: e.pageX,
  3462. y: e.pageY
  3463. };
  3464. _this.touchendZoom(startCoords, endCoords, allowX, allowY);
  3465. }
  3466. isMoved = false;
  3467. }
  3468. _this.core.$outer.removeClass('lg-grabbing').addClass('lg-grab');
  3469. });
  3470. };
  3471. Zoom.prototype.touchendZoom = function(startCoords, endCoords, allowX, allowY) {
  3472. var _this = this;
  3473. var _$el = _this.core.$slide.eq(_this.core.index).find('.lg-img-wrap');
  3474. var $image = _this.core.$slide.eq(_this.core.index).find('.lg-object');
  3475. var distanceX = (-Math.abs(_$el.attr('data-x'))) + (endCoords.x - startCoords.x);
  3476. var distanceY = (-Math.abs(_$el.attr('data-y'))) + (endCoords.y - startCoords.y);
  3477. var minY = (_this.core.$outer.find('.lg').height() - $image.prop('offsetHeight')) / 2;
  3478. var maxY = Math.abs(($image.prop('offsetHeight') * Math.abs($image.attr('data-scale'))) - _this.core.$outer.find('.lg').height() + minY);
  3479. var minX = (_this.core.$outer.find('.lg').width() - $image.prop('offsetWidth')) / 2;
  3480. var maxX = Math.abs(($image.prop('offsetWidth') * Math.abs($image.attr('data-scale'))) - _this.core.$outer.find('.lg').width() + minX);
  3481. if ((Math.abs(endCoords.x - startCoords.x) > 15) || (Math.abs(endCoords.y - startCoords.y) > 15)) {
  3482. if (allowY) {
  3483. if (distanceY <= -maxY) {
  3484. distanceY = -maxY;
  3485. } else if (distanceY >= -minY) {
  3486. distanceY = -minY;
  3487. }
  3488. }
  3489. if (allowX) {
  3490. if (distanceX <= -maxX) {
  3491. distanceX = -maxX;
  3492. } else if (distanceX >= -minX) {
  3493. distanceX = -minX;
  3494. }
  3495. }
  3496. if (allowY) {
  3497. _$el.attr('data-y', Math.abs(distanceY));
  3498. } else {
  3499. distanceY = -Math.abs(_$el.attr('data-y'));
  3500. }
  3501. if (allowX) {
  3502. _$el.attr('data-x', Math.abs(distanceX));
  3503. } else {
  3504. distanceX = -Math.abs(_$el.attr('data-x'));
  3505. }
  3506. if (_this.core.s.useLeftForZoom) {
  3507. _$el.css({
  3508. left: distanceX + 'px',
  3509. top: distanceY + 'px'
  3510. });
  3511. } else {
  3512. _$el.css('transform', 'translate3d(' + distanceX + 'px, ' + distanceY + 'px, 0)');
  3513. }
  3514. }
  3515. };
  3516. Zoom.prototype.destroy = function() {
  3517. var _this = this;
  3518. // Unbind all events added by lightGallery zoom plugin
  3519. _this.core.$el.off('.lg.zoom');
  3520. $(window).off('.lg.zoom');
  3521. _this.core.$slide.off('.lg.zoom');
  3522. _this.core.$el.off('.lg.tm.zoom');
  3523. _this.resetZoom();
  3524. clearTimeout(_this.zoomabletimeout);
  3525. _this.zoomabletimeout = false;
  3526. };
  3527. $.fn.lightGallery.modules.zoom = Zoom;
  3528. })();
  3529. }));