fotorama.js 129 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890
  1. /*!
  2. * Fotorama 4.6.4 | http://fotorama.io/license/
  3. */
  4. fotoramaVersion = '4.6.4';
  5. (function (window, document, location, $, undefined) {
  6. "use strict";
  7. var _fotoramaClass = 'fotorama',
  8. _fullscreenClass = 'fotorama__fullscreen',
  9. wrapClass = _fotoramaClass + '__wrap',
  10. wrapCss2Class = wrapClass + '--css2',
  11. wrapCss3Class = wrapClass + '--css3',
  12. wrapVideoClass = wrapClass + '--video',
  13. wrapFadeClass = wrapClass + '--fade',
  14. wrapSlideClass = wrapClass + '--slide',
  15. wrapNoControlsClass = wrapClass + '--no-controls',
  16. wrapNoShadowsClass = wrapClass + '--no-shadows',
  17. wrapPanYClass = wrapClass + '--pan-y',
  18. wrapRtlClass = wrapClass + '--rtl',
  19. wrapOnlyActiveClass = wrapClass + '--only-active',
  20. wrapNoCaptionsClass = wrapClass + '--no-captions',
  21. wrapToggleArrowsClass = wrapClass + '--toggle-arrows',
  22. stageClass = _fotoramaClass + '__stage',
  23. stageFrameClass = stageClass + '__frame',
  24. stageFrameVideoClass = stageFrameClass + '--video',
  25. stageShaftClass = stageClass + '__shaft',
  26. grabClass = _fotoramaClass + '__grab',
  27. pointerClass = _fotoramaClass + '__pointer',
  28. arrClass = _fotoramaClass + '__arr',
  29. arrDisabledClass = arrClass + '--disabled',
  30. arrPrevClass = arrClass + '--prev',
  31. arrNextClass = arrClass + '--next',
  32. navClass = _fotoramaClass + '__nav',
  33. navWrapClass = navClass + '-wrap',
  34. navShaftClass = navClass + '__shaft',
  35. navShaftVerticalClass = navWrapClass + '--vertical',
  36. navShaftListClass = navWrapClass + '--list',
  37. navShafthorizontalClass = navWrapClass + '--horizontal',
  38. navDotsClass = navClass + '--dots',
  39. navThumbsClass = navClass + '--thumbs',
  40. navFrameClass = navClass + '__frame',
  41. fadeClass = _fotoramaClass + '__fade',
  42. fadeFrontClass = fadeClass + '-front',
  43. fadeRearClass = fadeClass + '-rear',
  44. shadowClass = _fotoramaClass + '__shadow',
  45. shadowsClass = shadowClass + 's',
  46. shadowsLeftClass = shadowsClass + '--left',
  47. shadowsRightClass = shadowsClass + '--right',
  48. shadowsTopClass = shadowsClass + '--top',
  49. shadowsBottomClass = shadowsClass + '--bottom',
  50. activeClass = _fotoramaClass + '__active',
  51. selectClass = _fotoramaClass + '__select',
  52. hiddenClass = _fotoramaClass + '--hidden',
  53. fullscreenClass = _fotoramaClass + '--fullscreen',
  54. fullscreenIconClass = _fotoramaClass + '__fullscreen-icon',
  55. errorClass = _fotoramaClass + '__error',
  56. loadingClass = _fotoramaClass + '__loading',
  57. loadedClass = _fotoramaClass + '__loaded',
  58. loadedFullClass = loadedClass + '--full',
  59. loadedImgClass = loadedClass + '--img',
  60. grabbingClass = _fotoramaClass + '__grabbing',
  61. imgClass = _fotoramaClass + '__img',
  62. imgFullClass = imgClass + '--full',
  63. thumbClass = _fotoramaClass + '__thumb',
  64. thumbArrLeft = thumbClass + '__arr--left',
  65. thumbArrRight = thumbClass + '__arr--right',
  66. thumbBorderClass = thumbClass + '-border',
  67. htmlClass = _fotoramaClass + '__html',
  68. videoContainerClass = _fotoramaClass + '-video-container',
  69. videoClass = _fotoramaClass + '__video',
  70. videoPlayClass = videoClass + '-play',
  71. videoCloseClass = videoClass + '-close',
  72. horizontalImageClass = _fotoramaClass + '_horizontal_ratio',
  73. verticalImageClass = _fotoramaClass + '_vertical_ratio',
  74. fotoramaSpinnerClass = _fotoramaClass + '__spinner',
  75. spinnerShowClass = fotoramaSpinnerClass + '--show';
  76. var JQUERY_VERSION = $ && $.fn.jquery.split('.');
  77. if (!JQUERY_VERSION
  78. || JQUERY_VERSION[0] < 1
  79. || (JQUERY_VERSION[0] == 1 && JQUERY_VERSION[1] < 8)) {
  80. throw 'Fotorama requires jQuery 1.8 or later and will not run without it.';
  81. }
  82. var _ = {};
  83. /* Modernizr 2.8.3 (Custom Build) | MIT & BSD
  84. * Build: http://modernizr.com/download/#-csstransforms3d-csstransitions-touch-prefixed
  85. */
  86. var Modernizr = (function (window, document, undefined) {
  87. var version = '2.8.3',
  88. Modernizr = {},
  89. docElement = document.documentElement,
  90. mod = 'modernizr',
  91. modElem = document.createElement(mod),
  92. mStyle = modElem.style,
  93. inputElem,
  94. toString = {}.toString,
  95. prefixes = ' -webkit- -moz- -o- -ms- '.split(' '),
  96. omPrefixes = 'Webkit Moz O ms',
  97. cssomPrefixes = omPrefixes.split(' '),
  98. domPrefixes = omPrefixes.toLowerCase().split(' '),
  99. tests = {},
  100. inputs = {},
  101. attrs = {},
  102. classes = [],
  103. slice = classes.slice,
  104. featureName,
  105. injectElementWithStyles = function (rule, callback, nodes, testnames) {
  106. var style, ret, node, docOverflow,
  107. div = document.createElement('div'),
  108. body = document.body,
  109. fakeBody = body || document.createElement('body');
  110. if (parseInt(nodes, 10)) {
  111. while (nodes--) {
  112. node = document.createElement('div');
  113. node.id = testnames ? testnames[nodes] : mod + (nodes + 1);
  114. div.appendChild(node);
  115. }
  116. }
  117. style = ['&#173;', '<style id="s', mod, '">', rule, '</style>'].join('');
  118. div.id = mod;
  119. (body ? div : fakeBody).innerHTML += style;
  120. fakeBody.appendChild(div);
  121. if (!body) {
  122. fakeBody.style.background = '';
  123. fakeBody.style.overflow = 'hidden';
  124. docOverflow = docElement.style.overflow;
  125. docElement.style.overflow = 'hidden';
  126. docElement.appendChild(fakeBody);
  127. }
  128. ret = callback(div, rule);
  129. if (!body) {
  130. fakeBody.parentNode.removeChild(fakeBody);
  131. docElement.style.overflow = docOverflow;
  132. } else {
  133. div.parentNode.removeChild(div);
  134. }
  135. return !!ret;
  136. },
  137. _hasOwnProperty = ({}).hasOwnProperty, hasOwnProp;
  138. if (!is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined')) {
  139. hasOwnProp = function (object, property) {
  140. return _hasOwnProperty.call(object, property);
  141. };
  142. }
  143. else {
  144. hasOwnProp = function (object, property) {
  145. return ((property in object) && is(object.constructor.prototype[property], 'undefined'));
  146. };
  147. }
  148. if (!Function.prototype.bind) {
  149. Function.prototype.bind = function bind(that) {
  150. var target = this;
  151. if (typeof target != "function") {
  152. throw new TypeError();
  153. }
  154. var args = slice.call(arguments, 1),
  155. bound = function () {
  156. if (this instanceof bound) {
  157. var F = function () {
  158. };
  159. F.prototype = target.prototype;
  160. var self = new F();
  161. var result = target.apply(
  162. self,
  163. args.concat(slice.call(arguments))
  164. );
  165. if (Object(result) === result) {
  166. return result;
  167. }
  168. return self;
  169. } else {
  170. return target.apply(
  171. that,
  172. args.concat(slice.call(arguments))
  173. );
  174. }
  175. };
  176. return bound;
  177. };
  178. }
  179. function setCss(str) {
  180. mStyle.cssText = str;
  181. }
  182. function setCssAll(str1, str2) {
  183. return setCss(prefixes.join(str1 + ';') + ( str2 || '' ));
  184. }
  185. function is(obj, type) {
  186. return typeof obj === type;
  187. }
  188. function contains(str, substr) {
  189. return !!~('' + str).indexOf(substr);
  190. }
  191. function testProps(props, prefixed) {
  192. for (var i in props) {
  193. var prop = props[i];
  194. if (!contains(prop, "-") && mStyle[prop] !== undefined) {
  195. return prefixed == 'pfx' ? prop : true;
  196. }
  197. }
  198. return false;
  199. }
  200. function testDOMProps(props, obj, elem) {
  201. for (var i in props) {
  202. var item = obj[props[i]];
  203. if (item !== undefined) {
  204. if (elem === false) return props[i];
  205. if (is(item, 'function')) {
  206. return item.bind(elem || obj);
  207. }
  208. return item;
  209. }
  210. }
  211. return false;
  212. }
  213. function testPropsAll(prop, prefixed, elem) {
  214. var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1),
  215. props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' ');
  216. if (is(prefixed, "string") || is(prefixed, "undefined")) {
  217. return testProps(props, prefixed);
  218. } else {
  219. props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' ');
  220. return testDOMProps(props, prefixed, elem);
  221. }
  222. }
  223. tests['touch'] = function () {
  224. var bool;
  225. if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
  226. bool = true;
  227. } else {
  228. injectElementWithStyles(['@media (', prefixes.join('touch-enabled),('), mod, ')', '{#modernizr{top:9px;position:absolute}}'].join(''), function (node) {
  229. bool = node.offsetTop === 9;
  230. });
  231. }
  232. return bool;
  233. };
  234. tests['csstransforms3d'] = function () {
  235. var ret = !!testPropsAll('perspective');
  236. if (ret && 'webkitPerspective' in docElement.style) {
  237. injectElementWithStyles('@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}', function (node, rule) {
  238. ret = node.offsetLeft === 9 && node.offsetHeight === 3;
  239. });
  240. }
  241. return ret;
  242. };
  243. tests['csstransitions'] = function () {
  244. return testPropsAll('transition');
  245. };
  246. for (var feature in tests) {
  247. if (hasOwnProp(tests, feature)) {
  248. featureName = feature.toLowerCase();
  249. Modernizr[featureName] = tests[feature]();
  250. classes.push((Modernizr[featureName] ? '' : 'no-') + featureName);
  251. }
  252. }
  253. Modernizr.addTest = function (feature, test) {
  254. if (typeof feature == 'object') {
  255. for (var key in feature) {
  256. if (hasOwnProp(feature, key)) {
  257. Modernizr.addTest(key, feature[key]);
  258. }
  259. }
  260. } else {
  261. feature = feature.toLowerCase();
  262. if (Modernizr[feature] !== undefined) {
  263. return Modernizr;
  264. }
  265. test = typeof test == 'function' ? test() : test;
  266. if (typeof enableClasses !== "undefined" && enableClasses) {
  267. docElement.className += ' ' + (test ? '' : 'no-') + feature;
  268. }
  269. Modernizr[feature] = test;
  270. }
  271. return Modernizr;
  272. };
  273. setCss('');
  274. modElem = inputElem = null;
  275. Modernizr._version = version;
  276. Modernizr._prefixes = prefixes;
  277. Modernizr._domPrefixes = domPrefixes;
  278. Modernizr._cssomPrefixes = cssomPrefixes;
  279. Modernizr.testProp = function (prop) {
  280. return testProps([prop]);
  281. };
  282. Modernizr.testAllProps = testPropsAll;
  283. Modernizr.testStyles = injectElementWithStyles;
  284. Modernizr.prefixed = function (prop, obj, elem) {
  285. if (!obj) {
  286. return testPropsAll(prop, 'pfx');
  287. } else {
  288. return testPropsAll(prop, obj, elem);
  289. }
  290. };
  291. return Modernizr;
  292. })(window, document);
  293. var fullScreenApi = {
  294. ok: false,
  295. is: function () {
  296. return false;
  297. },
  298. request: function () {
  299. },
  300. cancel: function () {
  301. },
  302. event: '',
  303. prefix: ''
  304. },
  305. browserPrefixes = 'webkit moz o ms khtml'.split(' ');
  306. // check for native support
  307. if (typeof document.cancelFullScreen != 'undefined') {
  308. fullScreenApi.ok = true;
  309. } else {
  310. // check for fullscreen support by vendor prefix
  311. for (var i = 0, il = browserPrefixes.length; i < il; i++) {
  312. fullScreenApi.prefix = browserPrefixes[i];
  313. if (typeof document[fullScreenApi.prefix + 'CancelFullScreen'] != 'undefined') {
  314. fullScreenApi.ok = true;
  315. break;
  316. }
  317. }
  318. }
  319. // update methods to do something useful
  320. if (fullScreenApi.ok) {
  321. fullScreenApi.event = fullScreenApi.prefix + 'fullscreenchange';
  322. fullScreenApi.is = function () {
  323. switch (this.prefix) {
  324. case '':
  325. return document.fullScreen;
  326. case 'webkit':
  327. return document.webkitIsFullScreen;
  328. default:
  329. return document[this.prefix + 'FullScreen'];
  330. }
  331. };
  332. fullScreenApi.request = function (el) {
  333. return (this.prefix === '') ? el.requestFullScreen() : el[this.prefix + 'RequestFullScreen']();
  334. };
  335. fullScreenApi.cancel = function (el) {
  336. return (this.prefix === '') ? document.cancelFullScreen() : document[this.prefix + 'CancelFullScreen']();
  337. };
  338. }
  339. /* Bez v1.0.10-g5ae0136
  340. * http://github.com/rdallasgray/bez
  341. *
  342. * A plugin to convert CSS3 cubic-bezier co-ordinates to jQuery-compatible easing functions
  343. *
  344. * With thanks to Nikolay Nemshilov for clarification on the cubic-bezier maths
  345. * See http://st-on-it.blogspot.com/2011/05/calculating-cubic-bezier-function.html
  346. *
  347. * Copyright 2011 Robert Dallas Gray. All rights reserved.
  348. * Provided under the FreeBSD license: https://github.com/rdallasgray/bez/blob/master/LICENSE.txt
  349. */
  350. function bez(coOrdArray) {
  351. var encodedFuncName = "bez_" + $.makeArray(arguments).join("_").replace(".", "p");
  352. if (typeof $['easing'][encodedFuncName] !== "function") {
  353. var polyBez = function (p1, p2) {
  354. var A = [null, null],
  355. B = [null, null],
  356. C = [null, null],
  357. bezCoOrd = function (t, ax) {
  358. C[ax] = 3 * p1[ax];
  359. B[ax] = 3 * (p2[ax] - p1[ax]) - C[ax];
  360. A[ax] = 1 - C[ax] - B[ax];
  361. return t * (C[ax] + t * (B[ax] + t * A[ax]));
  362. },
  363. xDeriv = function (t) {
  364. return C[0] + t * (2 * B[0] + 3 * A[0] * t);
  365. },
  366. xForT = function (t) {
  367. var x = t, i = 0, z;
  368. while (++i < 14) {
  369. z = bezCoOrd(x, 0) - t;
  370. if (Math.abs(z) < 1e-3) break;
  371. x -= z / xDeriv(x);
  372. }
  373. return x;
  374. };
  375. return function (t) {
  376. return bezCoOrd(xForT(t), 1);
  377. }
  378. };
  379. $['easing'][encodedFuncName] = function (x, t, b, c, d) {
  380. return c * polyBez([coOrdArray[0], coOrdArray[1]], [coOrdArray[2], coOrdArray[3]])(t / d) + b;
  381. }
  382. }
  383. return encodedFuncName;
  384. }
  385. var $WINDOW = $(window),
  386. $DOCUMENT = $(document),
  387. $HTML,
  388. $BODY,
  389. QUIRKS_FORCE = location.hash.replace('#', '') === 'quirks',
  390. TRANSFORMS3D = Modernizr.csstransforms3d,
  391. CSS3 = TRANSFORMS3D && !QUIRKS_FORCE,
  392. COMPAT = TRANSFORMS3D || document.compatMode === 'CSS1Compat',
  393. FULLSCREEN = fullScreenApi.ok,
  394. MOBILE = navigator.userAgent.match(/Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i),
  395. SLOW = !CSS3 || MOBILE,
  396. MS_POINTER = navigator.msPointerEnabled,
  397. WHEEL = "onwheel" in document.createElement("div") ? "wheel" : document.onmousewheel !== undefined ? "mousewheel" : "DOMMouseScroll",
  398. TOUCH_TIMEOUT = 250,
  399. TRANSITION_DURATION = 300,
  400. SCROLL_LOCK_TIMEOUT = 1400,
  401. AUTOPLAY_INTERVAL = 5000,
  402. MARGIN = 2,
  403. THUMB_SIZE = 64,
  404. WIDTH = 500,
  405. HEIGHT = 333,
  406. STAGE_FRAME_KEY = '$stageFrame',
  407. NAV_DOT_FRAME_KEY = '$navDotFrame',
  408. NAV_THUMB_FRAME_KEY = '$navThumbFrame',
  409. AUTO = 'auto',
  410. BEZIER = bez([.1, 0, .25, 1]),
  411. MAX_WIDTH = 1200,
  412. /**
  413. * Number of thumbnails in slide. Calculated only on setOptions and resize.
  414. * @type {number}
  415. */
  416. thumbsPerSlide = 1,
  417. OPTIONS = {
  418. /**
  419. * Set width for gallery.
  420. * Default value - width of first image
  421. * Number - set value in px
  422. * String - set value in quotes
  423. *
  424. */
  425. width: null,
  426. /**
  427. * Set min-width for gallery
  428. *
  429. */
  430. minwidth: null,
  431. /**
  432. * Set max-width for gallery
  433. *
  434. */
  435. maxwidth: '100%',
  436. /**
  437. * Set height for gallery
  438. * Default value - height of first image
  439. * Number - set value in px
  440. * String - set value in quotes
  441. *
  442. */
  443. height: null,
  444. /**
  445. * Set min-height for gallery
  446. *
  447. */
  448. minheight: null,
  449. /**
  450. * Set max-height for gallery
  451. *
  452. */
  453. maxheight: null,
  454. /**
  455. * Set proportion ratio for gallery depends of image
  456. *
  457. */
  458. ratio: null, // '16/9' || 500/333 || 1.5
  459. margin: MARGIN,
  460. nav: 'dots', // 'thumbs' || false
  461. navposition: 'bottom', // 'top'
  462. navwidth: null,
  463. thumbwidth: THUMB_SIZE,
  464. thumbheight: THUMB_SIZE,
  465. thumbmargin: MARGIN,
  466. thumbborderwidth: MARGIN,
  467. allowfullscreen: false, // true || 'native'
  468. transition: 'slide', // 'crossfade' || 'dissolve'
  469. clicktransition: null,
  470. transitionduration: TRANSITION_DURATION,
  471. captions: true,
  472. startindex: 0,
  473. loop: false,
  474. autoplay: false,
  475. stopautoplayontouch: true,
  476. keyboard: false,
  477. arrows: true,
  478. click: true,
  479. swipe: false,
  480. trackpad: false,
  481. shuffle: false,
  482. direction: 'ltr', // 'rtl'
  483. shadows: true,
  484. showcaption: true,
  485. /**
  486. * Set type of thumbnail navigation
  487. */
  488. navdir: 'horizontal',
  489. /**
  490. * Set configuration to show or hide arrows in thumb navigation
  491. */
  492. navarrows: true,
  493. /**
  494. * Set type of navigation. Can be thumbs or slides
  495. */
  496. navtype: 'thumbs'
  497. },
  498. KEYBOARD_OPTIONS = {
  499. left: true,
  500. right: true,
  501. down: true,
  502. up: true,
  503. space: false,
  504. home: false,
  505. end: false
  506. };
  507. function noop() {
  508. }
  509. function minMaxLimit(value, min, max) {
  510. return Math.max(isNaN(min) ? -Infinity : min, Math.min(isNaN(max) ? Infinity : max, value));
  511. }
  512. function readTransform(css, dir) {
  513. return css.match(/ma/) && css.match(/-?\d+(?!d)/g)[css.match(/3d/) ?
  514. (dir === 'vertical' ? 13 : 12) : (dir === 'vertical' ? 5 : 4)
  515. ]
  516. }
  517. function readPosition($el, dir) {
  518. if (CSS3) {
  519. return +readTransform($el.css('transform'), dir);
  520. } else {
  521. return +$el.css(dir === 'vertical' ? 'top' : 'left').replace('px', '');
  522. }
  523. }
  524. function getTranslate(pos, direction) {
  525. var obj = {};
  526. if (CSS3) {
  527. switch (direction) {
  528. case 'vertical':
  529. obj.transform = 'translate3d(0, ' + (pos) + 'px,0)';
  530. break;
  531. case 'list':
  532. break;
  533. default :
  534. obj.transform = 'translate3d(' + (pos) + 'px,0,0)';
  535. break;
  536. }
  537. } else {
  538. direction === 'vertical' ?
  539. obj.top = pos :
  540. obj.left = pos;
  541. }
  542. return obj;
  543. }
  544. function getDuration(time) {
  545. return {'transition-duration': time + 'ms'};
  546. }
  547. function unlessNaN(value, alternative) {
  548. return isNaN(value) ? alternative : value;
  549. }
  550. function numberFromMeasure(value, measure) {
  551. return unlessNaN(+String(value).replace(measure || 'px', ''));
  552. }
  553. function numberFromPercent(value) {
  554. return /%$/.test(value) ? numberFromMeasure(value, '%') : undefined;
  555. }
  556. function numberFromWhatever(value, whole) {
  557. return unlessNaN(numberFromPercent(value) / 100 * whole, numberFromMeasure(value));
  558. }
  559. function measureIsValid(value) {
  560. return (!isNaN(numberFromMeasure(value)) || !isNaN(numberFromMeasure(value, '%'))) && value;
  561. }
  562. function getPosByIndex(index, side, margin, baseIndex) {
  563. return (index - (baseIndex || 0)) * (side + (margin || 0));
  564. }
  565. function getIndexByPos(pos, side, margin, baseIndex) {
  566. return -Math.round(pos / (side + (margin || 0)) - (baseIndex || 0));
  567. }
  568. function bindTransitionEnd($el) {
  569. var elData = $el.data();
  570. if (elData.tEnd) return;
  571. var el = $el[0],
  572. transitionEndEvent = {
  573. WebkitTransition: 'webkitTransitionEnd',
  574. MozTransition: 'transitionend',
  575. OTransition: 'oTransitionEnd otransitionend',
  576. msTransition: 'MSTransitionEnd',
  577. transition: 'transitionend'
  578. };
  579. addEvent(el, transitionEndEvent[Modernizr.prefixed('transition')], function (e) {
  580. elData.tProp && e.propertyName.match(elData.tProp) && elData.onEndFn();
  581. });
  582. elData.tEnd = true;
  583. }
  584. function afterTransition($el, property, fn, time) {
  585. var ok,
  586. elData = $el.data();
  587. if (elData) {
  588. elData.onEndFn = function () {
  589. if (ok) return;
  590. ok = true;
  591. clearTimeout(elData.tT);
  592. fn();
  593. };
  594. elData.tProp = property;
  595. // Passive call, just in case of fail of native transition-end event
  596. clearTimeout(elData.tT);
  597. elData.tT = setTimeout(function () {
  598. elData.onEndFn();
  599. }, time * 1.5);
  600. bindTransitionEnd($el);
  601. }
  602. }
  603. function stop($el, pos/*, _001*/) {
  604. var dir = $el.navdir || 'horizontal';
  605. if ($el.length) {
  606. var elData = $el.data();
  607. if (CSS3) {
  608. $el.css(getDuration(0));
  609. elData.onEndFn = noop;
  610. clearTimeout(elData.tT);
  611. } else {
  612. $el.stop();
  613. }
  614. var lockedPos = getNumber(pos, function () {
  615. return readPosition($el, dir);
  616. });
  617. $el.css(getTranslate(lockedPos, dir/*, _001*/));//.width(); // `.width()` for reflow
  618. return lockedPos;
  619. }
  620. }
  621. function getNumber() {
  622. var number;
  623. for (var _i = 0, _l = arguments.length; _i < _l; _i++) {
  624. number = _i ? arguments[_i]() : arguments[_i];
  625. if (typeof number === 'number') {
  626. break;
  627. }
  628. }
  629. return number;
  630. }
  631. function edgeResistance(pos, edge) {
  632. return Math.round(pos + ((edge - pos) / 1.5));
  633. }
  634. function getProtocol() {
  635. getProtocol.p = getProtocol.p || (location.protocol === 'https:' ? 'https://' : 'http://');
  636. return getProtocol.p;
  637. }
  638. function parseHref(href) {
  639. var a = document.createElement('a');
  640. a.href = href;
  641. return a;
  642. }
  643. function findVideoId(href, forceVideo) {
  644. if (typeof href !== 'string') return href;
  645. href = parseHref(href);
  646. var id,
  647. type;
  648. if (href.host.match(/youtube\.com/) && href.search) {
  649. //.log();
  650. id = href.search.split('v=')[1];
  651. if (id) {
  652. var ampersandPosition = id.indexOf('&');
  653. if (ampersandPosition !== -1) {
  654. id = id.substring(0, ampersandPosition);
  655. }
  656. type = 'youtube';
  657. }
  658. } else if (href.host.match(/youtube\.com|youtu\.be|youtube-nocookie.com/)) {
  659. id = href.pathname.replace(/^\/(embed\/|v\/)?/, '').replace(/\/.*/, '');
  660. type = 'youtube';
  661. } else if (href.host.match(/vimeo\.com/)) {
  662. type = 'vimeo';
  663. id = href.pathname.replace(/^\/(video\/)?/, '').replace(/\/.*/, '');
  664. }
  665. if ((!id || !type) && forceVideo) {
  666. id = href.href;
  667. type = 'custom';
  668. }
  669. return id ? {id: id, type: type, s: href.search.replace(/^\?/, ''), p: getProtocol()} : false;
  670. }
  671. function getVideoThumbs(dataFrame, data, fotorama) {
  672. var img, thumb, video = dataFrame.video;
  673. if (video.type === 'youtube') {
  674. thumb = getProtocol() + 'img.youtube.com/vi/' + video.id + '/default.jpg';
  675. img = thumb.replace(/\/default.jpg$/, '/hqdefault.jpg');
  676. dataFrame.thumbsReady = true;
  677. } else if (video.type === 'vimeo') {
  678. $.ajax({
  679. url: getProtocol() + 'vimeo.com/api/v2/video/' + video.id + '.json',
  680. dataType: 'jsonp',
  681. success: function (json) {
  682. dataFrame.thumbsReady = true;
  683. updateData(data, {
  684. img: json[0].thumbnail_large,
  685. thumb: json[0].thumbnail_small
  686. }, dataFrame.i, fotorama);
  687. }
  688. });
  689. } else {
  690. dataFrame.thumbsReady = true;
  691. }
  692. return {
  693. img: img,
  694. thumb: thumb
  695. }
  696. }
  697. function updateData(data, _dataFrame, i, fotorama) {
  698. for (var _i = 0, _l = data.length; _i < _l; _i++) {
  699. var dataFrame = data[_i];
  700. if (dataFrame.i === i && dataFrame.thumbsReady) {
  701. var clear = {videoReady: true};
  702. clear[STAGE_FRAME_KEY] = clear[NAV_THUMB_FRAME_KEY] = clear[NAV_DOT_FRAME_KEY] = false;
  703. fotorama.splice(_i, 1, $.extend(
  704. {},
  705. dataFrame,
  706. clear,
  707. _dataFrame
  708. ));
  709. break;
  710. }
  711. }
  712. }
  713. function getDataFromHtml($el) {
  714. var data = [];
  715. function getDataFromImg($img, imgData, checkVideo) {
  716. var $child = $img.children('img').eq(0),
  717. _imgHref = $img.attr('href'),
  718. _imgSrc = $img.attr('src'),
  719. _thumbSrc = $child.attr('src'),
  720. _video = imgData.video,
  721. video = checkVideo ? findVideoId(_imgHref, _video === true) : false;
  722. if (video) {
  723. _imgHref = false;
  724. } else {
  725. video = _video;
  726. }
  727. getDimensions($img, $child, $.extend(imgData, {
  728. video: video,
  729. img: imgData.img || _imgHref || _imgSrc || _thumbSrc,
  730. thumb: imgData.thumb || _thumbSrc || _imgSrc || _imgHref
  731. }));
  732. }
  733. function getDimensions($img, $child, imgData) {
  734. var separateThumbFLAG = imgData.thumb && imgData.img !== imgData.thumb,
  735. width = numberFromMeasure(imgData.width || $img.attr('width')),
  736. height = numberFromMeasure(imgData.height || $img.attr('height'));
  737. $.extend(imgData, {
  738. width: width,
  739. height: height,
  740. thumbratio: getRatio(imgData.thumbratio || (numberFromMeasure(imgData.thumbwidth || ($child && $child.attr('width')) || separateThumbFLAG || width) / numberFromMeasure(imgData.thumbheight || ($child && $child.attr('height')) || separateThumbFLAG || height)))
  741. });
  742. }
  743. $el.children().each(function () {
  744. var $this = $(this),
  745. dataFrame = optionsToLowerCase($.extend($this.data(), {id: $this.attr('id')}));
  746. if ($this.is('a, img')) {
  747. getDataFromImg($this, dataFrame, true);
  748. } else if (!$this.is(':empty')) {
  749. getDimensions($this, null, $.extend(dataFrame, {
  750. html: this,
  751. _html: $this.html() // Because of IE
  752. }));
  753. } else return;
  754. data.push(dataFrame);
  755. });
  756. return data;
  757. }
  758. function isHidden(el) {
  759. return el.offsetWidth === 0 && el.offsetHeight === 0;
  760. }
  761. function isDetached(el) {
  762. return !$.contains(document.documentElement, el);
  763. }
  764. function waitFor(test, fn, timeout, i) {
  765. if (!waitFor.i) {
  766. waitFor.i = 1;
  767. waitFor.ii = [true];
  768. }
  769. i = i || waitFor.i;
  770. if (typeof waitFor.ii[i] === 'undefined') {
  771. waitFor.ii[i] = true;
  772. }
  773. if (test()) {
  774. fn();
  775. } else {
  776. waitFor.ii[i] && setTimeout(function () {
  777. waitFor.ii[i] && waitFor(test, fn, timeout, i);
  778. }, timeout || 100);
  779. }
  780. return waitFor.i++;
  781. }
  782. waitFor.stop = function (i) {
  783. waitFor.ii[i] = false;
  784. };
  785. function fit($el, measuresToFit) {
  786. var elData = $el.data(),
  787. measures = elData.measures;
  788. if (measures && (!elData.l ||
  789. elData.l.W !== measures.width ||
  790. elData.l.H !== measures.height ||
  791. elData.l.r !== measures.ratio ||
  792. elData.l.w !== measuresToFit.w ||
  793. elData.l.h !== measuresToFit.h)) {
  794. var height = minMaxLimit(measuresToFit.h, 0, measures.height),
  795. width = height * measures.ratio;
  796. UTIL.setRatio($el, width, height);
  797. elData.l = {
  798. W: measures.width,
  799. H: measures.height,
  800. r: measures.ratio,
  801. w: measuresToFit.w,
  802. h: measuresToFit.h
  803. };
  804. }
  805. return true;
  806. }
  807. function setStyle($el, style) {
  808. var el = $el[0];
  809. if (el.styleSheet) {
  810. el.styleSheet.cssText = style;
  811. } else {
  812. $el.html(style);
  813. }
  814. }
  815. function findShadowEdge(pos, min, max, dir) {
  816. return min === max ? false :
  817. dir === 'vertical' ?
  818. (pos <= min ? 'top' : pos >= max ? 'bottom' : 'top bottom') :
  819. (pos <= min ? 'left' : pos >= max ? 'right' : 'left right');
  820. }
  821. function smartClick($el, fn, _options) {
  822. _options = _options || {};
  823. $el.each(function () {
  824. var $this = $(this),
  825. thisData = $this.data(),
  826. startEvent;
  827. if (thisData.clickOn) return;
  828. thisData.clickOn = true;
  829. $.extend(touch($this, {
  830. onStart: function (e) {
  831. startEvent = e;
  832. (_options.onStart || noop).call(this, e);
  833. },
  834. onMove: _options.onMove || noop,
  835. onTouchEnd: _options.onTouchEnd || noop,
  836. onEnd: function (result) {
  837. if (result.moved) return;
  838. fn.call(this, startEvent);
  839. }
  840. }), {noMove: true});
  841. });
  842. }
  843. function div(classes, child) {
  844. return '<div class="' + classes + '">' + (child || '') + '</div>';
  845. }
  846. /**
  847. * Function transforming into valid classname
  848. * @param className - name of the class
  849. * @returns {string} - dom format of class name
  850. */
  851. function cls(className) {
  852. return "." + className;
  853. }
  854. /**
  855. *
  856. * @param {json-object} videoItem Parsed object from data.video item or href from link a in input dates
  857. * @returns {string} DOM view of video iframe
  858. */
  859. function createVideoFrame(videoItem) {
  860. var frame = '<iframe src="' + videoItem.p + videoItem.type + '.com/embed/' + videoItem.id + '" frameborder="0" allowfullscreen></iframe>';
  861. return frame;
  862. }
  863. // Fisher–Yates Shuffle
  864. // http://bost.ocks.org/mike/shuffle/
  865. function shuffle(array) {
  866. // While there remain elements to shuffle
  867. var l = array.length;
  868. while (l) {
  869. // Pick a remaining element
  870. var i = Math.floor(Math.random() * l--);
  871. // And swap it with the current element
  872. var t = array[l];
  873. array[l] = array[i];
  874. array[i] = t;
  875. }
  876. return array;
  877. }
  878. function clone(array) {
  879. return Object.prototype.toString.call(array) == '[object Array]'
  880. && $.map(array, function (frame) {
  881. return $.extend({}, frame);
  882. });
  883. }
  884. function lockScroll($el, left, top) {
  885. $el
  886. .scrollLeft(left || 0)
  887. .scrollTop(top || 0);
  888. }
  889. function optionsToLowerCase(options) {
  890. if (options) {
  891. var opts = {};
  892. $.each(options, function (key, value) {
  893. opts[key.toLowerCase()] = value;
  894. });
  895. return opts;
  896. }
  897. }
  898. function getRatio(_ratio) {
  899. if (!_ratio) return;
  900. var ratio = +_ratio;
  901. if (!isNaN(ratio)) {
  902. return ratio;
  903. } else {
  904. ratio = _ratio.split('/');
  905. return +ratio[0] / +ratio[1] || undefined;
  906. }
  907. }
  908. function addEvent(el, e, fn, bool) {
  909. if (!e) return;
  910. el.addEventListener ? el.addEventListener(e, fn, !!bool) : el.attachEvent('on' + e, fn);
  911. }
  912. /**
  913. *
  914. * @param position guess position for navShaft
  915. * @param restriction object contains min and max values for position
  916. * @returns {*} filtered value of position
  917. */
  918. function validateRestrictions(position, restriction) {
  919. if (position > restriction.max) {
  920. position = restriction.max;
  921. } else {
  922. if (position < restriction.min) {
  923. position = restriction.min;
  924. }
  925. }
  926. return position;
  927. }
  928. function validateSlidePos(opt, navShaftTouchTail, guessIndex, offsetNav, $guessNavFrame, $navWrap, dir) {
  929. var position,
  930. size,
  931. wrapSize;
  932. if (dir === 'horizontal') {
  933. size = opt.thumbwidth;
  934. wrapSize = $navWrap.width();
  935. } else {
  936. size = opt.thumbheight;
  937. wrapSize = $navWrap.height();
  938. }
  939. if ( (size + opt.margin) * (guessIndex + 1) >= (wrapSize - offsetNav) ) {
  940. if (dir === 'horizontal') {
  941. position = -$guessNavFrame.position().left;
  942. } else {
  943. position = -$guessNavFrame.position().top;
  944. }
  945. } else {
  946. if ((size + opt.margin) * (guessIndex) <= Math.abs(offsetNav)) {
  947. if (dir === 'horizontal') {
  948. position = -$guessNavFrame.position().left + wrapSize - (size + opt.margin);
  949. } else {
  950. position = -$guessNavFrame.position().top + wrapSize - (size + opt.margin);
  951. }
  952. } else {
  953. position = offsetNav;
  954. }
  955. }
  956. position = validateRestrictions(position, navShaftTouchTail);
  957. return position || 0;
  958. }
  959. function elIsDisabled(el) {
  960. return !!el.getAttribute('disabled');
  961. }
  962. function disableAttr(FLAG, disable) {
  963. if (disable) {
  964. return {disabled: FLAG};
  965. } else {
  966. return {tabindex: FLAG * -1 + '', disabled: FLAG};
  967. }
  968. }
  969. function addEnterUp(el, fn) {
  970. addEvent(el, 'keyup', function (e) {
  971. elIsDisabled(el) || e.keyCode == 13 && fn.call(el, e);
  972. });
  973. }
  974. function addFocus(el, fn) {
  975. addEvent(el, 'focus', el.onfocusin = function (e) {
  976. fn.call(el, e);
  977. }, true);
  978. }
  979. function stopEvent(e, stopPropagation) {
  980. e.preventDefault ? e.preventDefault() : (e.returnValue = false);
  981. stopPropagation && e.stopPropagation && e.stopPropagation();
  982. }
  983. function stubEvent($el, eventType) {
  984. var isIOS = /ip(ad|hone|od)/i.test(window.navigator.userAgent);
  985. if (isIOS && eventType === 'touchend') {
  986. $el.on('touchend', function(e){
  987. $DOCUMENT.trigger('mouseup', e);
  988. })
  989. }
  990. $el.on(eventType, function (e) {
  991. stopEvent(e, true);
  992. return false;
  993. });
  994. }
  995. function getDirectionSign(forward) {
  996. return forward ? '>' : '<';
  997. }
  998. var UTIL = (function () {
  999. function setRatioClass($el, wh, ht) {
  1000. var rateImg = wh / ht;
  1001. if (rateImg <= 1) {
  1002. $el.parent().removeClass(horizontalImageClass);
  1003. $el.parent().addClass(verticalImageClass);
  1004. } else {
  1005. $el.parent().removeClass(verticalImageClass);
  1006. $el.parent().addClass(horizontalImageClass);
  1007. }
  1008. }
  1009. /**
  1010. * Set specific attribute in thumbnail template
  1011. * @param $frame DOM item of specific thumbnail
  1012. * @param value Value which must be setted into specific attribute
  1013. * @param searchAttr Name of attribute where value must be included
  1014. */
  1015. function setThumbAttr($frame, value, searchAttr) {
  1016. var attr = searchAttr;
  1017. if (!$frame.attr(attr) && $frame.attr(attr) !== undefined) {
  1018. $frame.attr(attr, value);
  1019. }
  1020. if ($frame.find("[" + attr + "]").length) {
  1021. $frame.find("[" + attr + "]")
  1022. .each(function () {
  1023. $(this).attr(attr, value);
  1024. });
  1025. }
  1026. }
  1027. /**
  1028. * Method describe behavior need to render caption on preview or not
  1029. * @param frameItem specific item from data
  1030. * @param isExpected {bool} if items with caption need render them or not
  1031. * @returns {boolean} if true then caption should be rendered
  1032. */
  1033. function isExpectedCaption(frameItem, isExpected, undefined) {
  1034. var expected = false,
  1035. frameExpected;
  1036. frameItem.showCaption === undefined || frameItem.showCaption === true ? frameExpected = true : frameExpected = false;
  1037. if (!isExpected) {
  1038. return false;
  1039. }
  1040. if (frameItem.caption && frameExpected) {
  1041. expected = true;
  1042. }
  1043. return expected;
  1044. }
  1045. return {
  1046. setRatio: setRatioClass,
  1047. setThumbAttr: setThumbAttr,
  1048. isExpectedCaption: isExpectedCaption
  1049. };
  1050. }(UTIL || {}, jQuery));
  1051. function slide($el, options) {
  1052. var elData = $el.data(),
  1053. elPos = Math.round(options.pos),
  1054. onEndFn = function () {
  1055. if (elData && elData.sliding) {
  1056. elData.sliding = false;
  1057. }
  1058. (options.onEnd || noop)();
  1059. };
  1060. if (typeof options.overPos !== 'undefined' && options.overPos !== options.pos) {
  1061. elPos = options.overPos;
  1062. }
  1063. var translate = $.extend(getTranslate(elPos, options.direction), options.width && {width: options.width}, options.height && {height: options.height});
  1064. if (elData && elData.sliding) {
  1065. elData.sliding = true;
  1066. }
  1067. if (CSS3) {
  1068. $el.css($.extend(getDuration(options.time), translate));
  1069. if (options.time > 10) {
  1070. afterTransition($el, 'transform', onEndFn, options.time);
  1071. } else {
  1072. onEndFn();
  1073. }
  1074. } else {
  1075. $el.stop().animate(translate, options.time, BEZIER, onEndFn);
  1076. }
  1077. }
  1078. function fade($el1, $el2, $frames, options, fadeStack, chain) {
  1079. var chainedFLAG = typeof chain !== 'undefined';
  1080. if (!chainedFLAG) {
  1081. fadeStack.push(arguments);
  1082. Array.prototype.push.call(arguments, fadeStack.length);
  1083. if (fadeStack.length > 1) return;
  1084. }
  1085. $el1 = $el1 || $($el1);
  1086. $el2 = $el2 || $($el2);
  1087. var _$el1 = $el1[0],
  1088. _$el2 = $el2[0],
  1089. crossfadeFLAG = options.method === 'crossfade',
  1090. onEndFn = function () {
  1091. if (!onEndFn.done) {
  1092. onEndFn.done = true;
  1093. var args = (chainedFLAG || fadeStack.shift()) && fadeStack.shift();
  1094. args && fade.apply(this, args);
  1095. (options.onEnd || noop)(!!args);
  1096. }
  1097. },
  1098. time = options.time / (chain || 1);
  1099. $frames.removeClass(fadeRearClass + ' ' + fadeFrontClass);
  1100. $el1
  1101. .stop()
  1102. .addClass(fadeRearClass);
  1103. $el2
  1104. .stop()
  1105. .addClass(fadeFrontClass);
  1106. crossfadeFLAG && _$el2 && $el1.fadeTo(0, 0);
  1107. $el1.fadeTo(crossfadeFLAG ? time : 0, 1, crossfadeFLAG && onEndFn);
  1108. $el2.fadeTo(time, 0, onEndFn);
  1109. (_$el1 && crossfadeFLAG) || _$el2 || onEndFn();
  1110. }
  1111. var lastEvent,
  1112. moveEventType,
  1113. preventEvent,
  1114. preventEventTimeout,
  1115. dragDomEl;
  1116. function extendEvent(e) {
  1117. var touch = (e.touches || [])[0] || e;
  1118. e._x = touch.pageX || touch.originalEvent.pageX;
  1119. e._y = touch.clientY || touch.originalEvent.clientY;
  1120. e._now = $.now();
  1121. }
  1122. function touch($el, options) {
  1123. var el = $el[0],
  1124. tail = {},
  1125. touchEnabledFLAG,
  1126. startEvent,
  1127. $target,
  1128. controlTouch,
  1129. touchFLAG,
  1130. targetIsSelectFLAG,
  1131. targetIsLinkFlag,
  1132. tolerance,
  1133. moved;
  1134. function onStart(e) {
  1135. $target = $(e.target);
  1136. tail.checked = targetIsSelectFLAG = targetIsLinkFlag = moved = false;
  1137. if (touchEnabledFLAG
  1138. || tail.flow
  1139. || (e.touches && e.touches.length > 1)
  1140. || e.which > 1
  1141. || (lastEvent && lastEvent.type !== e.type && preventEvent)
  1142. || (targetIsSelectFLAG = options.select && $target.is(options.select, el))) return targetIsSelectFLAG;
  1143. touchFLAG = e.type === 'touchstart';
  1144. targetIsLinkFlag = $target.is('a, a *', el);
  1145. controlTouch = tail.control;
  1146. tolerance = (tail.noMove || tail.noSwipe || controlTouch) ? 16 : !tail.snap ? 4 : 0;
  1147. extendEvent(e);
  1148. startEvent = lastEvent = e;
  1149. moveEventType = e.type.replace(/down|start/, 'move').replace(/Down/, 'Move');
  1150. (options.onStart || noop).call(el, e, {control: controlTouch, $target: $target});
  1151. touchEnabledFLAG = tail.flow = true;
  1152. if (!touchFLAG || tail.go) stopEvent(e);
  1153. }
  1154. function onMove(e) {
  1155. if ((e.touches && e.touches.length > 1)
  1156. || (MS_POINTER && !e.isPrimary)
  1157. || moveEventType !== e.type
  1158. || !touchEnabledFLAG) {
  1159. touchEnabledFLAG && onEnd();
  1160. (options.onTouchEnd || noop)();
  1161. return;
  1162. }
  1163. extendEvent(e);
  1164. var xDiff = Math.abs(e._x - startEvent._x), // opt _x → _pageX
  1165. yDiff = Math.abs(e._y - startEvent._y),
  1166. xyDiff = xDiff - yDiff,
  1167. xWin = (tail.go || tail.x || xyDiff >= 0) && !tail.noSwipe,
  1168. yWin = xyDiff < 0;
  1169. if (touchFLAG && !tail.checked) {
  1170. if (touchEnabledFLAG = xWin) {
  1171. stopEvent(e);
  1172. }
  1173. } else {
  1174. stopEvent(e);
  1175. if (movedEnough(xDiff,yDiff)) {
  1176. (options.onMove || noop).call(el, e, {touch: touchFLAG});
  1177. }
  1178. }
  1179. if (!moved && movedEnough(xDiff, yDiff) && Math.sqrt(Math.pow(xDiff, 2) + Math.pow(yDiff, 2)) > tolerance) {
  1180. moved = true;
  1181. }
  1182. tail.checked = tail.checked || xWin || yWin;
  1183. }
  1184. function movedEnough(xDiff, yDiff) {
  1185. return xDiff > yDiff && xDiff > 1.5;
  1186. }
  1187. function onEnd(e) {
  1188. (options.onTouchEnd || noop)();
  1189. var _touchEnabledFLAG = touchEnabledFLAG;
  1190. tail.control = touchEnabledFLAG = false;
  1191. if (_touchEnabledFLAG) {
  1192. tail.flow = false;
  1193. }
  1194. if (!_touchEnabledFLAG || (targetIsLinkFlag && !tail.checked)) return;
  1195. e && stopEvent(e);
  1196. preventEvent = true;
  1197. clearTimeout(preventEventTimeout);
  1198. preventEventTimeout = setTimeout(function () {
  1199. preventEvent = false;
  1200. }, 1000);
  1201. (options.onEnd || noop).call(el, {
  1202. moved: moved,
  1203. $target: $target,
  1204. control: controlTouch,
  1205. touch: touchFLAG,
  1206. startEvent: startEvent,
  1207. aborted: !e || e.type === 'MSPointerCancel'
  1208. });
  1209. }
  1210. function onOtherStart() {
  1211. if (tail.flow) return;
  1212. tail.flow = true;
  1213. }
  1214. function onOtherEnd() {
  1215. if (!tail.flow) return;
  1216. tail.flow = false;
  1217. }
  1218. if (MS_POINTER) {
  1219. addEvent(el, 'MSPointerDown', onStart);
  1220. addEvent(document, 'MSPointerMove', onMove);
  1221. addEvent(document, 'MSPointerCancel', onEnd);
  1222. addEvent(document, 'MSPointerUp', onEnd);
  1223. } else {
  1224. addEvent(el, 'touchstart', onStart);
  1225. addEvent(el, 'touchmove', onMove);
  1226. addEvent(el, 'touchend', onEnd);
  1227. addEvent(document, 'touchstart', onOtherStart);
  1228. addEvent(document, 'touchend', onOtherEnd);
  1229. addEvent(document, 'touchcancel', onOtherEnd);
  1230. $WINDOW.on('scroll', onOtherEnd);
  1231. $el.on('mousedown pointerdown', onStart);
  1232. $DOCUMENT
  1233. .on('mousemove pointermove', onMove)
  1234. .on('mouseup pointerup', onEnd);
  1235. }
  1236. if (Modernizr.touch) {
  1237. dragDomEl = 'a';
  1238. } else {
  1239. dragDomEl = 'div';
  1240. }
  1241. $el.on('click', dragDomEl, function (e) {
  1242. tail.checked && stopEvent(e);
  1243. });
  1244. return tail;
  1245. }
  1246. function moveOnTouch($el, options) {
  1247. var el = $el[0],
  1248. elData = $el.data(),
  1249. tail = {},
  1250. startCoo,
  1251. coo,
  1252. startElPos,
  1253. moveElPos,
  1254. edge,
  1255. moveTrack,
  1256. startTime,
  1257. endTime,
  1258. min,
  1259. max,
  1260. snap,
  1261. dir,
  1262. slowFLAG,
  1263. controlFLAG,
  1264. moved,
  1265. tracked;
  1266. function startTracking(e, noStop) {
  1267. tracked = true;
  1268. startCoo = coo = (dir === 'vertical') ? e._y : e._x;
  1269. startTime = e._now;
  1270. moveTrack = [
  1271. [startTime, startCoo]
  1272. ];
  1273. startElPos = moveElPos = tail.noMove || noStop ? 0 : stop($el, (options.getPos || noop)()/*, options._001*/);
  1274. (options.onStart || noop).call(el, e);
  1275. }
  1276. function onStart(e, result) {
  1277. min = tail.min;
  1278. max = tail.max;
  1279. snap = tail.snap,
  1280. dir = tail.direction || 'horizontal',
  1281. $el.navdir = dir;
  1282. slowFLAG = e.altKey;
  1283. tracked = moved = false;
  1284. controlFLAG = result.control;
  1285. if (!controlFLAG && !elData.sliding) {
  1286. startTracking(e);
  1287. }
  1288. }
  1289. function onMove(e, result) {
  1290. if (!tail.noSwipe) {
  1291. if (!tracked) {
  1292. startTracking(e);
  1293. }
  1294. coo = (dir === 'vertical') ? e._y : e._x;
  1295. moveTrack.push([e._now, coo]);
  1296. moveElPos = startElPos - (startCoo - coo);
  1297. edge = findShadowEdge(moveElPos, min, max, dir);
  1298. if (moveElPos <= min) {
  1299. moveElPos = edgeResistance(moveElPos, min);
  1300. } else if (moveElPos >= max) {
  1301. moveElPos = edgeResistance(moveElPos, max);
  1302. }
  1303. if (!tail.noMove) {
  1304. $el.css(getTranslate(moveElPos, dir));
  1305. if (!moved) {
  1306. moved = true;
  1307. // only for mouse
  1308. result.touch || MS_POINTER || $el.addClass(grabbingClass);
  1309. }
  1310. (options.onMove || noop).call(el, e, {pos: moveElPos, edge: edge});
  1311. }
  1312. }
  1313. }
  1314. function onEnd(result) {
  1315. if (tail.noSwipe && result.moved) return;
  1316. if (!tracked) {
  1317. startTracking(result.startEvent, true);
  1318. }
  1319. result.touch || MS_POINTER || $el.removeClass(grabbingClass);
  1320. endTime = $.now();
  1321. var _backTimeIdeal = endTime - TOUCH_TIMEOUT,
  1322. _backTime,
  1323. _timeDiff,
  1324. _timeDiffLast,
  1325. backTime = null,
  1326. backCoo,
  1327. virtualPos,
  1328. limitPos,
  1329. newPos,
  1330. overPos,
  1331. time = TRANSITION_DURATION,
  1332. speed,
  1333. friction = options.friction;
  1334. for (var _i = moveTrack.length - 1; _i >= 0; _i--) {
  1335. _backTime = moveTrack[_i][0];
  1336. _timeDiff = Math.abs(_backTime - _backTimeIdeal);
  1337. if (backTime === null || _timeDiff < _timeDiffLast) {
  1338. backTime = _backTime;
  1339. backCoo = moveTrack[_i][1];
  1340. } else if (backTime === _backTimeIdeal || _timeDiff > _timeDiffLast) {
  1341. break;
  1342. }
  1343. _timeDiffLast = _timeDiff;
  1344. }
  1345. newPos = minMaxLimit(moveElPos, min, max);
  1346. var cooDiff = backCoo - coo,
  1347. forwardFLAG = cooDiff >= 0,
  1348. timeDiff = endTime - backTime,
  1349. longTouchFLAG = timeDiff > TOUCH_TIMEOUT,
  1350. swipeFLAG = !longTouchFLAG && moveElPos !== startElPos && newPos === moveElPos;
  1351. if (snap) {
  1352. newPos = minMaxLimit(Math[swipeFLAG ? (forwardFLAG ? 'floor' : 'ceil') : 'round'](moveElPos / snap) * snap, min, max);
  1353. min = max = newPos;
  1354. }
  1355. if (swipeFLAG && (snap || newPos === moveElPos)) {
  1356. speed = -(cooDiff / timeDiff);
  1357. time *= minMaxLimit(Math.abs(speed), options.timeLow, options.timeHigh);
  1358. virtualPos = Math.round(moveElPos + speed * time / friction);
  1359. if (!snap) {
  1360. newPos = virtualPos;
  1361. }
  1362. if (!forwardFLAG && virtualPos > max || forwardFLAG && virtualPos < min) {
  1363. limitPos = forwardFLAG ? min : max;
  1364. overPos = virtualPos - limitPos;
  1365. if (!snap) {
  1366. newPos = limitPos;
  1367. }
  1368. overPos = minMaxLimit(newPos + overPos * .03, limitPos - 50, limitPos + 50);
  1369. time = Math.abs((moveElPos - overPos) / (speed / friction));
  1370. }
  1371. }
  1372. time *= slowFLAG ? 10 : 1;
  1373. (options.onEnd || noop).call(el, $.extend(result, {
  1374. moved: result.moved || longTouchFLAG && snap,
  1375. pos: moveElPos,
  1376. newPos: newPos,
  1377. overPos: overPos,
  1378. time: time,
  1379. dir: dir
  1380. }));
  1381. }
  1382. tail = $.extend(touch(options.$wrap, $.extend({}, options, {
  1383. onStart: onStart,
  1384. onMove: onMove,
  1385. onEnd: onEnd
  1386. })), tail);
  1387. return tail;
  1388. }
  1389. function wheel($el, options) {
  1390. var el = $el[0],
  1391. lockFLAG,
  1392. lastDirection,
  1393. lastNow,
  1394. tail = {
  1395. prevent: {}
  1396. };
  1397. addEvent(el, WHEEL, function (e) {
  1398. var yDelta = e.wheelDeltaY || -1 * e.deltaY || 0,
  1399. xDelta = e.wheelDeltaX || -1 * e.deltaX || 0,
  1400. xWin = Math.abs(xDelta) && !Math.abs(yDelta),
  1401. direction = getDirectionSign(xDelta < 0),
  1402. sameDirection = lastDirection === direction,
  1403. now = $.now(),
  1404. tooFast = now - lastNow < TOUCH_TIMEOUT;
  1405. lastDirection = direction;
  1406. lastNow = now;
  1407. if (!xWin || !tail.ok || tail.prevent[direction] && !lockFLAG) {
  1408. return;
  1409. } else {
  1410. stopEvent(e, true);
  1411. if (lockFLAG && sameDirection && tooFast) {
  1412. return;
  1413. }
  1414. }
  1415. if (options.shift) {
  1416. lockFLAG = true;
  1417. clearTimeout(tail.t);
  1418. tail.t = setTimeout(function () {
  1419. lockFLAG = false;
  1420. }, SCROLL_LOCK_TIMEOUT);
  1421. }
  1422. (options.onEnd || noop)(e, options.shift ? direction : xDelta);
  1423. });
  1424. return tail;
  1425. }
  1426. jQuery.Fotorama = function ($fotorama, opts) {
  1427. $HTML = $('html');
  1428. $BODY = $('body');
  1429. var that = this,
  1430. stamp = $.now(),
  1431. stampClass = _fotoramaClass + stamp,
  1432. fotorama = $fotorama[0],
  1433. data,
  1434. dataFrameCount = 1,
  1435. fotoramaData = $fotorama.data(),
  1436. size,
  1437. $style = $('<style></style>'),
  1438. $anchor = $(div(hiddenClass)),
  1439. $wrap = $fotorama.find(cls(wrapClass)),
  1440. $stage = $wrap.find(cls(stageClass)),
  1441. stage = $stage[0],
  1442. $stageShaft = $fotorama.find(cls(stageShaftClass)),
  1443. $stageFrame = $(),
  1444. $arrPrev = $fotorama.find(cls(arrPrevClass)),
  1445. $arrNext = $fotorama.find(cls(arrNextClass)),
  1446. $arrs = $fotorama.find(cls(arrClass)),
  1447. $navWrap = $fotorama.find(cls(navWrapClass)),
  1448. $nav = $navWrap.find(cls(navClass)),
  1449. $navShaft = $nav.find(cls(navShaftClass)),
  1450. $navFrame,
  1451. $navDotFrame = $(),
  1452. $navThumbFrame = $(),
  1453. stageShaftData = $stageShaft.data(),
  1454. navShaftData = $navShaft.data(),
  1455. $thumbBorder = $fotorama.find(cls(thumbBorderClass)),
  1456. $thumbArrLeft = $fotorama.find(cls(thumbArrLeft)),
  1457. $thumbArrRight = $fotorama.find(cls(thumbArrRight)),
  1458. $fullscreenIcon = $fotorama.find(cls(fullscreenIconClass)),
  1459. fullscreenIcon = $fullscreenIcon[0],
  1460. $videoPlay = $(div(videoPlayClass)),
  1461. $videoClose = $fotorama.find(cls(videoCloseClass)),
  1462. videoClose = $videoClose[0],
  1463. $spinner = $fotorama.find(cls(fotoramaSpinnerClass)),
  1464. $videoPlaying,
  1465. activeIndex = false,
  1466. activeFrame,
  1467. activeIndexes,
  1468. repositionIndex,
  1469. dirtyIndex,
  1470. lastActiveIndex,
  1471. prevIndex,
  1472. nextIndex,
  1473. nextAutoplayIndex,
  1474. startIndex,
  1475. o_loop,
  1476. o_nav,
  1477. o_navThumbs,
  1478. o_navTop,
  1479. o_allowFullScreen,
  1480. o_nativeFullScreen,
  1481. o_fade,
  1482. o_thumbSide,
  1483. o_thumbSide2,
  1484. o_transitionDuration,
  1485. o_transition,
  1486. o_shadows,
  1487. o_rtl,
  1488. o_keyboard,
  1489. lastOptions = {},
  1490. measures = {},
  1491. measuresSetFLAG,
  1492. stageShaftTouchTail = {},
  1493. stageWheelTail = {},
  1494. navShaftTouchTail = {},
  1495. navWheelTail = {},
  1496. scrollTop,
  1497. scrollLeft,
  1498. showedFLAG,
  1499. pausedAutoplayFLAG,
  1500. stoppedAutoplayFLAG,
  1501. toDeactivate = {},
  1502. toDetach = {},
  1503. measuresStash,
  1504. touchedFLAG,
  1505. hoverFLAG,
  1506. navFrameKey,
  1507. stageLeft = 0,
  1508. fadeStack = [];
  1509. $wrap[STAGE_FRAME_KEY] = $('<div class="' + stageFrameClass + '"></div>');
  1510. $wrap[NAV_THUMB_FRAME_KEY] = $($.Fotorama.jst.thumb());
  1511. $wrap[NAV_DOT_FRAME_KEY] = $($.Fotorama.jst.dots());
  1512. toDeactivate[STAGE_FRAME_KEY] = [];
  1513. toDeactivate[NAV_THUMB_FRAME_KEY] = [];
  1514. toDeactivate[NAV_DOT_FRAME_KEY] = [];
  1515. toDetach[STAGE_FRAME_KEY] = {};
  1516. $wrap.addClass(CSS3 ? wrapCss3Class : wrapCss2Class);
  1517. fotoramaData.fotorama = this;
  1518. /**
  1519. * Search video items in incoming data and transform object for video layout.
  1520. *
  1521. */
  1522. function checkForVideo() {
  1523. $.each(data, function (i, dataFrame) {
  1524. if (!dataFrame.i) {
  1525. dataFrame.i = dataFrameCount++;
  1526. var video = findVideoId(dataFrame.video, true);
  1527. if (video) {
  1528. var thumbs = {};
  1529. dataFrame.video = video;
  1530. if (!dataFrame.img && !dataFrame.thumb) {
  1531. thumbs = getVideoThumbs(dataFrame, data, that);
  1532. } else {
  1533. dataFrame.thumbsReady = true;
  1534. }
  1535. updateData(data, {img: thumbs.img, thumb: thumbs.thumb}, dataFrame.i, that);
  1536. }
  1537. }
  1538. });
  1539. }
  1540. function allowKey(key) {
  1541. return o_keyboard[key];
  1542. }
  1543. function setStagePosition() {
  1544. if ($stage !== undefined) {
  1545. if (opts.navdir == 'vertical') {
  1546. var padding = opts.thumbwidth + opts.thumbmargin;
  1547. $stage.css('left', padding);
  1548. $arrNext.css('right', padding);
  1549. $fullscreenIcon.css('right', padding);
  1550. $wrap.css('width', $wrap.css('width') + padding);
  1551. $stageShaft.css('max-width', $wrap.width() - padding);
  1552. } else {
  1553. $stage.css('left', '');
  1554. $arrNext.css('right', '');
  1555. $fullscreenIcon.css('right', '');
  1556. $wrap.css('width', $wrap.css('width') + padding);
  1557. $stageShaft.css('max-width', '');
  1558. }
  1559. }
  1560. }
  1561. function bindGlobalEvents(FLAG) {
  1562. var keydownCommon = 'keydown.' + _fotoramaClass,
  1563. localStamp = _fotoramaClass + stamp,
  1564. keydownLocal = 'keydown.' + localStamp,
  1565. keyupLocal = 'keyup.' + localStamp,
  1566. resizeLocal = 'resize.' + localStamp + ' ' + 'orientationchange.' + localStamp,
  1567. showParams;
  1568. if (FLAG) {
  1569. $DOCUMENT
  1570. .on(keydownLocal, function (e) {
  1571. var catched,
  1572. index;
  1573. if ($videoPlaying && e.keyCode === 27) {
  1574. catched = true;
  1575. unloadVideo($videoPlaying, true, true);
  1576. } else if (that.fullScreen || (opts.keyboard && !that.index)) {
  1577. if (e.keyCode === 27) {
  1578. catched = true;
  1579. that.cancelFullScreen();
  1580. } else if ((e.shiftKey && e.keyCode === 32 && allowKey('space')) || (!e.altKey && !e.metaKey && e.keyCode === 37 && allowKey('left')) || (e.keyCode === 38 && allowKey('up') && $(':focus').attr('data-gallery-role'))) {
  1581. that.longPress.progress();
  1582. index = '<';
  1583. } else if ((e.keyCode === 32 && allowKey('space')) || (!e.altKey && !e.metaKey && e.keyCode === 39 && allowKey('right')) || (e.keyCode === 40 && allowKey('down') && $(':focus').attr('data-gallery-role'))) {
  1584. that.longPress.progress();
  1585. index = '>';
  1586. } else if (e.keyCode === 36 && allowKey('home')) {
  1587. that.longPress.progress();
  1588. index = '<<';
  1589. } else if (e.keyCode === 35 && allowKey('end')) {
  1590. that.longPress.progress();
  1591. index = '>>';
  1592. }
  1593. }
  1594. (catched || index) && stopEvent(e);
  1595. showParams = {index: index, slow: e.altKey, user: true};
  1596. index && (that.longPress.inProgress ?
  1597. that.showWhileLongPress(showParams) :
  1598. that.show(showParams));
  1599. });
  1600. if (FLAG) {
  1601. $DOCUMENT
  1602. .on(keyupLocal, function (e) {
  1603. if (that.longPress.inProgress) {
  1604. that.showEndLongPress({user: true});
  1605. }
  1606. that.longPress.reset();
  1607. });
  1608. }
  1609. if (!that.index) {
  1610. $DOCUMENT
  1611. .off(keydownCommon)
  1612. .on(keydownCommon, 'textarea, input, select', function (e) {
  1613. !$BODY.hasClass(_fullscreenClass) && e.stopPropagation();
  1614. });
  1615. }
  1616. $WINDOW.on(resizeLocal, that.resize);
  1617. } else {
  1618. $DOCUMENT.off(keydownLocal);
  1619. $WINDOW.off(resizeLocal);
  1620. }
  1621. }
  1622. function appendElements(FLAG) {
  1623. if (FLAG === appendElements.f) return;
  1624. if (FLAG) {
  1625. $fotorama
  1626. .addClass(_fotoramaClass + ' ' + stampClass)
  1627. .before($anchor)
  1628. .before($style);
  1629. addInstance(that);
  1630. } else {
  1631. $anchor.detach();
  1632. $style.detach();
  1633. $fotorama
  1634. .html(fotoramaData.urtext)
  1635. .removeClass(stampClass);
  1636. hideInstance(that);
  1637. }
  1638. bindGlobalEvents(FLAG);
  1639. appendElements.f = FLAG;
  1640. }
  1641. /**
  1642. * Set and install data from incoming @param {JSON} options or takes data attr from data-"name"=... values.
  1643. */
  1644. function setData() {
  1645. data = that.data = data || clone(opts.data) || getDataFromHtml($fotorama);
  1646. size = that.size = data.length;
  1647. ready.ok && opts.shuffle && shuffle(data);
  1648. checkForVideo();
  1649. activeIndex = limitIndex(activeIndex);
  1650. size && appendElements(true);
  1651. }
  1652. function stageNoMove() {
  1653. var _noMove = size < 2 || $videoPlaying;
  1654. stageShaftTouchTail.noMove = _noMove || o_fade;
  1655. stageShaftTouchTail.noSwipe = _noMove || !opts.swipe;
  1656. !o_transition && $stageShaft.toggleClass(grabClass, !opts.click && !stageShaftTouchTail.noMove && !stageShaftTouchTail.noSwipe);
  1657. MS_POINTER && $wrap.toggleClass(wrapPanYClass, !stageShaftTouchTail.noSwipe);
  1658. }
  1659. function setAutoplayInterval(interval) {
  1660. if (interval === true) interval = '';
  1661. opts.autoplay = Math.max(+interval || AUTOPLAY_INTERVAL, o_transitionDuration * 1.5);
  1662. }
  1663. function updateThumbArrow(opt) {
  1664. if (opt.navarrows && opt.nav === 'thumbs') {
  1665. $thumbArrLeft.show();
  1666. $thumbArrRight.show();
  1667. } else {
  1668. $thumbArrLeft.hide();
  1669. $thumbArrRight.hide();
  1670. }
  1671. }
  1672. function getThumbsInSlide($el, opts) {
  1673. return Math.floor($wrap.width() / (opts.thumbwidth + opts.thumbmargin));
  1674. }
  1675. /**
  1676. * Options on the fly
  1677. * */
  1678. function setOptions() {
  1679. if (!opts.nav || opts.nav === 'dots') {
  1680. opts.navdir = 'horizontal'
  1681. }
  1682. that.options = opts = optionsToLowerCase(opts);
  1683. thumbsPerSlide = getThumbsInSlide($wrap, opts);
  1684. o_fade = (opts.transition === 'crossfade' || opts.transition === 'dissolve');
  1685. o_loop = opts.loop && (size > 2 || (o_fade && (!o_transition || o_transition !== 'slide')));
  1686. o_transitionDuration = +opts.transitionduration || TRANSITION_DURATION;
  1687. o_rtl = opts.direction === 'rtl';
  1688. o_keyboard = $.extend({}, opts.keyboard && KEYBOARD_OPTIONS, opts.keyboard);
  1689. updateThumbArrow(opts);
  1690. var classes = {add: [], remove: []};
  1691. function addOrRemoveClass(FLAG, value) {
  1692. classes[FLAG ? 'add' : 'remove'].push(value);
  1693. }
  1694. if (size > 1) {
  1695. o_nav = opts.nav;
  1696. o_navTop = opts.navposition === 'top';
  1697. classes.remove.push(selectClass);
  1698. $arrs.toggle(!!opts.arrows);
  1699. } else {
  1700. o_nav = false;
  1701. $arrs.hide();
  1702. }
  1703. arrsUpdate();
  1704. stageWheelUpdate();
  1705. thumbArrUpdate();
  1706. if (opts.autoplay) setAutoplayInterval(opts.autoplay);
  1707. o_thumbSide = numberFromMeasure(opts.thumbwidth) || THUMB_SIZE;
  1708. o_thumbSide2 = numberFromMeasure(opts.thumbheight) || THUMB_SIZE;
  1709. stageWheelTail.ok = navWheelTail.ok = opts.trackpad && !SLOW;
  1710. stageNoMove();
  1711. extendMeasures(opts, [measures]);
  1712. o_navThumbs = o_nav === 'thumbs';
  1713. if ($navWrap.filter(':hidden') && !!o_nav) {
  1714. $navWrap.show();
  1715. }
  1716. if (o_navThumbs) {
  1717. frameDraw(size, 'navThumb');
  1718. $navFrame = $navThumbFrame;
  1719. navFrameKey = NAV_THUMB_FRAME_KEY;
  1720. setStyle($style, $.Fotorama.jst.style({
  1721. w: o_thumbSide,
  1722. h: o_thumbSide2,
  1723. b: opts.thumbborderwidth,
  1724. m: opts.thumbmargin,
  1725. s: stamp,
  1726. q: !COMPAT
  1727. }));
  1728. $nav
  1729. .addClass(navThumbsClass)
  1730. .removeClass(navDotsClass);
  1731. } else if (o_nav === 'dots') {
  1732. frameDraw(size, 'navDot');
  1733. $navFrame = $navDotFrame;
  1734. navFrameKey = NAV_DOT_FRAME_KEY;
  1735. $nav
  1736. .addClass(navDotsClass)
  1737. .removeClass(navThumbsClass);
  1738. } else {
  1739. $navWrap.hide();
  1740. o_nav = false;
  1741. $nav.removeClass(navThumbsClass + ' ' + navDotsClass);
  1742. }
  1743. if (o_nav) {
  1744. if (o_navTop) {
  1745. $navWrap.insertBefore($stage);
  1746. } else {
  1747. $navWrap.insertAfter($stage);
  1748. }
  1749. frameAppend.nav = false;
  1750. frameAppend($navFrame, $navShaft, 'nav');
  1751. }
  1752. o_allowFullScreen = opts.allowfullscreen;
  1753. if (o_allowFullScreen) {
  1754. $fullscreenIcon.prependTo($stage);
  1755. o_nativeFullScreen = FULLSCREEN && o_allowFullScreen === 'native';
  1756. // Due 300ms click delay on mobile devices
  1757. // we stub touchend and fallback to click.
  1758. // MAGETWO-69567
  1759. stubEvent($fullscreenIcon, 'touchend');
  1760. } else {
  1761. $fullscreenIcon.detach();
  1762. o_nativeFullScreen = false;
  1763. }
  1764. addOrRemoveClass(o_fade, wrapFadeClass);
  1765. addOrRemoveClass(!o_fade, wrapSlideClass);
  1766. addOrRemoveClass(!opts.captions, wrapNoCaptionsClass);
  1767. addOrRemoveClass(o_rtl, wrapRtlClass);
  1768. addOrRemoveClass(opts.arrows, wrapToggleArrowsClass);
  1769. o_shadows = opts.shadows && !SLOW;
  1770. addOrRemoveClass(!o_shadows, wrapNoShadowsClass);
  1771. $wrap
  1772. .addClass(classes.add.join(' '))
  1773. .removeClass(classes.remove.join(' '));
  1774. lastOptions = $.extend({}, opts);
  1775. setStagePosition();
  1776. }
  1777. function normalizeIndex(index) {
  1778. return index < 0 ? (size + (index % size)) % size : index >= size ? index % size : index;
  1779. }
  1780. function limitIndex(index) {
  1781. return minMaxLimit(index, 0, size - 1);
  1782. }
  1783. function edgeIndex(index) {
  1784. return o_loop ? normalizeIndex(index) : limitIndex(index);
  1785. }
  1786. function getPrevIndex(index) {
  1787. return index > 0 || o_loop ? index - 1 : false;
  1788. }
  1789. function getNextIndex(index) {
  1790. return index < size - 1 || o_loop ? index + 1 : false;
  1791. }
  1792. function setStageShaftMinmaxAndSnap() {
  1793. stageShaftTouchTail.min = o_loop ? -Infinity : -getPosByIndex(size - 1, measures.w, opts.margin, repositionIndex);
  1794. stageShaftTouchTail.max = o_loop ? Infinity : -getPosByIndex(0, measures.w, opts.margin, repositionIndex);
  1795. stageShaftTouchTail.snap = measures.w + opts.margin;
  1796. }
  1797. function setNavShaftMinMax() {
  1798. var isVerticalDir = (opts.navdir === 'vertical');
  1799. var param = isVerticalDir ? $navShaft.height() : $navShaft.width();
  1800. var mainParam = isVerticalDir ? measures.h : measures.nw;
  1801. navShaftTouchTail.min = Math.min(0, mainParam - param);
  1802. navShaftTouchTail.max = 0;
  1803. navShaftTouchTail.direction = opts.navdir;
  1804. $navShaft.toggleClass(grabClass, !(navShaftTouchTail.noMove = navShaftTouchTail.min === navShaftTouchTail.max));
  1805. }
  1806. function eachIndex(indexes, type, fn) {
  1807. if (typeof indexes === 'number') {
  1808. indexes = new Array(indexes);
  1809. var rangeFLAG = true;
  1810. }
  1811. return $.each(indexes, function (i, index) {
  1812. if (rangeFLAG) index = i;
  1813. if (typeof index === 'number') {
  1814. var dataFrame = data[normalizeIndex(index)];
  1815. if (dataFrame) {
  1816. var key = '$' + type + 'Frame',
  1817. $frame = dataFrame[key];
  1818. fn.call(this, i, index, dataFrame, $frame, key, $frame && $frame.data());
  1819. }
  1820. }
  1821. });
  1822. }
  1823. function setMeasures(width, height, ratio, index) {
  1824. if (!measuresSetFLAG || (measuresSetFLAG === '*' && index === startIndex)) {
  1825. width = measureIsValid(opts.width) || measureIsValid(width) || WIDTH;
  1826. height = measureIsValid(opts.height) || measureIsValid(height) || HEIGHT;
  1827. that.resize({
  1828. width: width,
  1829. ratio: opts.ratio || ratio || width / height
  1830. }, 0, index !== startIndex && '*');
  1831. }
  1832. }
  1833. function loadImg(indexes, type, specialMeasures, again) {
  1834. eachIndex(indexes, type, function (i, index, dataFrame, $frame, key, frameData) {
  1835. if (!$frame) return;
  1836. var fullFLAG = that.fullScreen && !frameData.$full && type === 'stage';
  1837. if (frameData.$img && !again && !fullFLAG) return;
  1838. var img = new Image(),
  1839. $img = $(img),
  1840. imgData = $img.data();
  1841. frameData[fullFLAG ? '$full' : '$img'] = $img;
  1842. var srcKey = type === 'stage' ? (fullFLAG ? 'full' : 'img') : 'thumb',
  1843. src = dataFrame[srcKey],
  1844. dummy = fullFLAG ? dataFrame['img'] : dataFrame[type === 'stage' ? 'thumb' : 'img'];
  1845. if (type === 'navThumb') $frame = frameData.$wrap;
  1846. function triggerTriggerEvent(event) {
  1847. var _index = normalizeIndex(index);
  1848. triggerEvent(event, {
  1849. index: _index,
  1850. src: src,
  1851. frame: data[_index]
  1852. });
  1853. }
  1854. function error() {
  1855. $img.remove();
  1856. $.Fotorama.cache[src] = 'error';
  1857. if ((!dataFrame.html || type !== 'stage') && dummy && dummy !== src) {
  1858. dataFrame[srcKey] = src = dummy;
  1859. frameData.$full = null;
  1860. loadImg([index], type, specialMeasures, true);
  1861. } else {
  1862. if (src && !dataFrame.html && !fullFLAG) {
  1863. $frame
  1864. .trigger('f:error')
  1865. .removeClass(loadingClass)
  1866. .addClass(errorClass);
  1867. triggerTriggerEvent('error');
  1868. } else if (type === 'stage') {
  1869. $frame
  1870. .trigger('f:load')
  1871. .removeClass(loadingClass + ' ' + errorClass)
  1872. .addClass(loadedClass);
  1873. triggerTriggerEvent('load');
  1874. setMeasures();
  1875. }
  1876. frameData.state = 'error';
  1877. if (size > 1 && data[index] === dataFrame && !dataFrame.html && !dataFrame.deleted && !dataFrame.video && !fullFLAG) {
  1878. dataFrame.deleted = true;
  1879. that.splice(index, 1);
  1880. }
  1881. }
  1882. }
  1883. function loaded() {
  1884. $.Fotorama.measures[src] = imgData.measures = $.Fotorama.measures[src] || {
  1885. width: img.width,
  1886. height: img.height,
  1887. ratio: img.width / img.height
  1888. };
  1889. setMeasures(imgData.measures.width, imgData.measures.height, imgData.measures.ratio, index);
  1890. $img
  1891. .off('load error')
  1892. .addClass('' + (fullFLAG ? imgFullClass: imgClass))
  1893. .attr('aria-hidden', 'false')
  1894. .prependTo($frame);
  1895. if ($frame.hasClass(stageFrameClass) && !$frame.hasClass(videoContainerClass)) {
  1896. $frame.attr("href", $img.attr("src"));
  1897. }
  1898. fit($img, (
  1899. $.isFunction(specialMeasures) ? specialMeasures() : specialMeasures) || measures);
  1900. $.Fotorama.cache[src] = frameData.state = 'loaded';
  1901. setTimeout(function () {
  1902. $frame
  1903. .trigger('f:load')
  1904. .removeClass(loadingClass + ' ' + errorClass)
  1905. .addClass(loadedClass + ' ' + (fullFLAG ? loadedFullClass : loadedImgClass));
  1906. if (type === 'stage') {
  1907. triggerTriggerEvent('load');
  1908. } else if (dataFrame.thumbratio === AUTO || !dataFrame.thumbratio && opts.thumbratio === AUTO) {
  1909. // danger! reflow for all thumbnails
  1910. dataFrame.thumbratio = imgData.measures.ratio;
  1911. reset();
  1912. }
  1913. }, 0);
  1914. }
  1915. if (!src) {
  1916. error();
  1917. return;
  1918. }
  1919. function waitAndLoad() {
  1920. var _i = 10;
  1921. waitFor(function () {
  1922. return !touchedFLAG || !_i-- && !SLOW;
  1923. }, function () {
  1924. loaded();
  1925. });
  1926. }
  1927. if (!$.Fotorama.cache[src]) {
  1928. $.Fotorama.cache[src] = '*';
  1929. $img
  1930. .on('load', waitAndLoad)
  1931. .on('error', error);
  1932. } else {
  1933. (function justWait() {
  1934. if ($.Fotorama.cache[src] === 'error') {
  1935. error();
  1936. } else if ($.Fotorama.cache[src] === 'loaded') {
  1937. setTimeout(waitAndLoad, 0);
  1938. } else {
  1939. setTimeout(justWait, 100);
  1940. }
  1941. })();
  1942. }
  1943. frameData.state = '';
  1944. img.src = src;
  1945. if (frameData.data.caption) {
  1946. img.alt = frameData.data.caption || "";
  1947. }
  1948. if (frameData.data.full) {
  1949. $(img).data('original', frameData.data.full);
  1950. }
  1951. if (UTIL.isExpectedCaption(dataFrame, opts.showcaption)) {
  1952. $(img).attr('aria-labelledby', dataFrame.labelledby);
  1953. }
  1954. });
  1955. }
  1956. function updateFotoramaState() {
  1957. var $frame = activeFrame[STAGE_FRAME_KEY];
  1958. if ($frame && !$frame.data().state) {
  1959. $spinner.addClass(spinnerShowClass);
  1960. $frame.on('f:load f:error', function () {
  1961. $frame.off('f:load f:error');
  1962. $spinner.removeClass(spinnerShowClass);
  1963. });
  1964. }
  1965. }
  1966. function addNavFrameEvents(frame) {
  1967. addEnterUp(frame, onNavFrameClick);
  1968. addFocus(frame, function () {
  1969. setTimeout(function () {
  1970. lockScroll($nav);
  1971. }, 0);
  1972. slideNavShaft({time: o_transitionDuration, guessIndex: $(this).data().eq, minMax: navShaftTouchTail});
  1973. });
  1974. }
  1975. function frameDraw(indexes, type) {
  1976. eachIndex(indexes, type, function (i, index, dataFrame, $frame, key, frameData) {
  1977. if ($frame) return;
  1978. $frame = dataFrame[key] = $wrap[key].clone();
  1979. frameData = $frame.data();
  1980. frameData.data = dataFrame;
  1981. var frame = $frame[0],
  1982. labelledbyValue = "labelledby" + $.now();
  1983. if (type === 'stage') {
  1984. if (dataFrame.html) {
  1985. $('<div class="' + htmlClass + '"></div>')
  1986. .append(
  1987. dataFrame._html ? $(dataFrame.html)
  1988. .removeAttr('id')
  1989. .html(dataFrame._html) // Because of IE
  1990. : dataFrame.html
  1991. )
  1992. .appendTo($frame);
  1993. }
  1994. if (dataFrame.id) {
  1995. labelledbyValue = dataFrame.id || labelledbyValue;
  1996. }
  1997. dataFrame.labelledby = labelledbyValue;
  1998. if (UTIL.isExpectedCaption(dataFrame, opts.showcaption)) {
  1999. $($.Fotorama.jst.frameCaption({
  2000. caption: dataFrame.caption,
  2001. labelledby: labelledbyValue
  2002. })).appendTo($frame);
  2003. }
  2004. dataFrame.video && $frame
  2005. .addClass(stageFrameVideoClass)
  2006. .append($videoPlay.clone());
  2007. // This solves tabbing problems
  2008. addFocus(frame, function (e) {
  2009. setTimeout(function () {
  2010. lockScroll($stage);
  2011. }, 0);
  2012. clickToShow({index: frameData.eq, user: true}, e);
  2013. });
  2014. $stageFrame = $stageFrame.add($frame);
  2015. } else if (type === 'navDot') {
  2016. addNavFrameEvents(frame);
  2017. $navDotFrame = $navDotFrame.add($frame);
  2018. } else if (type === 'navThumb') {
  2019. addNavFrameEvents(frame);
  2020. frameData.$wrap = $frame.children(':first');
  2021. $navThumbFrame = $navThumbFrame.add($frame);
  2022. if (dataFrame.video) {
  2023. frameData.$wrap.append($videoPlay.clone());
  2024. }
  2025. }
  2026. });
  2027. }
  2028. function callFit($img, measuresToFit) {
  2029. return $img && $img.length && fit($img, measuresToFit);
  2030. }
  2031. function stageFramePosition(indexes) {
  2032. eachIndex(indexes, 'stage', function (i, index, dataFrame, $frame, key, frameData) {
  2033. if (!$frame) return;
  2034. var normalizedIndex = normalizeIndex(index);
  2035. frameData.eq = normalizedIndex;
  2036. toDetach[STAGE_FRAME_KEY][normalizedIndex] = $frame.css($.extend({left: o_fade ? 0 : getPosByIndex(index, measures.w, opts.margin, repositionIndex)}, o_fade && getDuration(0)));
  2037. if (isDetached($frame[0])) {
  2038. $frame.appendTo($stageShaft);
  2039. unloadVideo(dataFrame.$video);
  2040. }
  2041. callFit(frameData.$img, measures);
  2042. callFit(frameData.$full, measures);
  2043. if ($frame.hasClass(stageFrameClass) && !($frame.attr('aria-hidden') === "false" && $frame.hasClass(activeClass))) {
  2044. $frame.attr('aria-hidden', 'true');
  2045. }
  2046. });
  2047. }
  2048. function thumbsDraw(pos, loadFLAG) {
  2049. var leftLimit,
  2050. rightLimit,
  2051. exceedLimit;
  2052. if (o_nav !== 'thumbs' || isNaN(pos)) return;
  2053. leftLimit = -pos;
  2054. rightLimit = -pos + measures.nw;
  2055. if (opts.navdir === 'vertical') {
  2056. pos = pos - opts.thumbheight;
  2057. rightLimit = -pos + measures.h;
  2058. }
  2059. $navThumbFrame.each(function () {
  2060. var $this = $(this),
  2061. thisData = $this.data(),
  2062. eq = thisData.eq,
  2063. getSpecialMeasures = function () {
  2064. return {
  2065. h: o_thumbSide2,
  2066. w: thisData.w
  2067. }
  2068. },
  2069. specialMeasures = getSpecialMeasures(),
  2070. exceedLimit = opts.navdir === 'vertical' ?
  2071. thisData.t > rightLimit : thisData.l > rightLimit;
  2072. specialMeasures.w = thisData.w;
  2073. if ((opts.navdir !== 'vertical' && thisData.l + thisData.w < leftLimit)
  2074. || exceedLimit
  2075. || callFit(thisData.$img, specialMeasures)) return;
  2076. loadFLAG && loadImg([eq], 'navThumb', getSpecialMeasures);
  2077. });
  2078. }
  2079. function frameAppend($frames, $shaft, type) {
  2080. if (!frameAppend[type]) {
  2081. var thumbsFLAG = type === 'nav' && o_navThumbs,
  2082. left = 0,
  2083. top = 0;
  2084. $shaft.append(
  2085. $frames
  2086. .filter(function () {
  2087. var actual,
  2088. $this = $(this),
  2089. frameData = $this.data();
  2090. for (var _i = 0, _l = data.length; _i < _l; _i++) {
  2091. if (frameData.data === data[_i]) {
  2092. actual = true;
  2093. frameData.eq = _i;
  2094. break;
  2095. }
  2096. }
  2097. return actual || $this.remove() && false;
  2098. })
  2099. .sort(function (a, b) {
  2100. return $(a).data().eq - $(b).data().eq;
  2101. })
  2102. .each(function () {
  2103. var $this = $(this),
  2104. frameData = $this.data();
  2105. UTIL.setThumbAttr($this, frameData.data.caption, "aria-label");
  2106. })
  2107. .each(function () {
  2108. if (!thumbsFLAG) return;
  2109. var $this = $(this),
  2110. frameData = $this.data(),
  2111. thumbwidth = Math.round(o_thumbSide2 * frameData.data.thumbratio) || o_thumbSide,
  2112. thumbheight = Math.round(o_thumbSide / frameData.data.thumbratio) || o_thumbSide2;
  2113. frameData.t = top;
  2114. frameData.h = thumbheight;
  2115. frameData.l = left;
  2116. frameData.w = thumbwidth;
  2117. $this.css({width: thumbwidth});
  2118. top += thumbheight + opts.thumbmargin;
  2119. left += thumbwidth + opts.thumbmargin;
  2120. })
  2121. );
  2122. frameAppend[type] = true;
  2123. }
  2124. }
  2125. function getDirection(x) {
  2126. return x - stageLeft > measures.w / 3;
  2127. }
  2128. function disableDirrection(i) {
  2129. return !o_loop && (!(activeIndex + i) || !(activeIndex - size + i)) && !$videoPlaying;
  2130. }
  2131. function arrsUpdate() {
  2132. var disablePrev = disableDirrection(0),
  2133. disableNext = disableDirrection(1);
  2134. $arrPrev
  2135. .toggleClass(arrDisabledClass, disablePrev)
  2136. .attr(disableAttr(disablePrev, false));
  2137. $arrNext
  2138. .toggleClass(arrDisabledClass, disableNext)
  2139. .attr(disableAttr(disableNext, false));
  2140. }
  2141. function thumbArrUpdate() {
  2142. var isLeftDisable = false,
  2143. isRightDisable = false;
  2144. if (opts.navtype === 'thumbs' && !opts.loop) {
  2145. (activeIndex == 0) ? isLeftDisable = true : isLeftDisable = false;
  2146. (activeIndex == opts.data.length - 1) ? isRightDisable = true : isRightDisable = false;
  2147. }
  2148. if (opts.navtype === 'slides') {
  2149. var pos = readPosition($navShaft, opts.navdir);
  2150. pos >= navShaftTouchTail.max ? isLeftDisable = true : isLeftDisable = false;
  2151. pos <= navShaftTouchTail.min ? isRightDisable = true : isRightDisable = false;
  2152. }
  2153. $thumbArrLeft
  2154. .toggleClass(arrDisabledClass, isLeftDisable)
  2155. .attr(disableAttr(isLeftDisable, true));
  2156. $thumbArrRight
  2157. .toggleClass(arrDisabledClass, isRightDisable)
  2158. .attr(disableAttr(isRightDisable, true));
  2159. }
  2160. function stageWheelUpdate() {
  2161. if (stageWheelTail.ok) {
  2162. stageWheelTail.prevent = {'<': disableDirrection(0), '>': disableDirrection(1)};
  2163. }
  2164. }
  2165. function getNavFrameBounds($navFrame) {
  2166. var navFrameData = $navFrame.data(),
  2167. left,
  2168. top,
  2169. width,
  2170. height;
  2171. if (o_navThumbs) {
  2172. left = navFrameData.l;
  2173. top = navFrameData.t;
  2174. width = navFrameData.w;
  2175. height = navFrameData.h;
  2176. } else {
  2177. left = $navFrame.position().left;
  2178. width = $navFrame.width();
  2179. }
  2180. var horizontalBounds = {
  2181. c: left + width / 2,
  2182. min: -left + opts.thumbmargin * 10,
  2183. max: -left + measures.w - width - opts.thumbmargin * 10
  2184. };
  2185. var verticalBounds = {
  2186. c: top + height / 2,
  2187. min: -top + opts.thumbmargin * 10,
  2188. max: -top + measures.h - height - opts.thumbmargin * 10
  2189. };
  2190. return opts.navdir === 'vertical' ? verticalBounds : horizontalBounds;
  2191. }
  2192. function slideThumbBorder(time) {
  2193. var navFrameData = activeFrame[navFrameKey].data();
  2194. slide($thumbBorder, {
  2195. time: time * 1.2,
  2196. pos: (opts.navdir === 'vertical' ? navFrameData.t : navFrameData.l),
  2197. width: navFrameData.w,
  2198. height: navFrameData.h,
  2199. direction: opts.navdir
  2200. });
  2201. }
  2202. function slideNavShaft(options) {
  2203. var $guessNavFrame = data[options.guessIndex][navFrameKey],
  2204. typeOfAnimation = opts.navtype;
  2205. var overflowFLAG,
  2206. time,
  2207. minMax,
  2208. boundTop,
  2209. boundLeft,
  2210. l,
  2211. pos,
  2212. x;
  2213. if ($guessNavFrame) {
  2214. if (typeOfAnimation === 'thumbs') {
  2215. overflowFLAG = navShaftTouchTail.min !== navShaftTouchTail.max;
  2216. minMax = options.minMax || overflowFLAG && getNavFrameBounds(activeFrame[navFrameKey]);
  2217. boundTop = overflowFLAG && (options.keep && slideNavShaft.t ? slideNavShaft.l : minMaxLimit((options.coo || measures.nw / 2) - getNavFrameBounds($guessNavFrame).c, minMax.min, minMax.max));
  2218. boundLeft = overflowFLAG && (options.keep && slideNavShaft.l ? slideNavShaft.l : minMaxLimit((options.coo || measures.nw / 2) - getNavFrameBounds($guessNavFrame).c, minMax.min, minMax.max));
  2219. l = (opts.navdir === 'vertical' ? boundTop : boundLeft);
  2220. pos = overflowFLAG && minMaxLimit(l, navShaftTouchTail.min, navShaftTouchTail.max) || 0;
  2221. time = options.time * 1.1;
  2222. slide($navShaft, {
  2223. time: time,
  2224. pos: pos,
  2225. direction: opts.navdir,
  2226. onEnd: function () {
  2227. thumbsDraw(pos, true);
  2228. thumbArrUpdate();
  2229. }
  2230. });
  2231. setShadow($nav, findShadowEdge(pos, navShaftTouchTail.min, navShaftTouchTail.max, opts.navdir));
  2232. slideNavShaft.l = l;
  2233. } else {
  2234. x = readPosition($navShaft, opts.navdir);
  2235. time = options.time * 1.11;
  2236. pos = validateSlidePos(opts, navShaftTouchTail, options.guessIndex, x, $guessNavFrame, $navWrap, opts.navdir);
  2237. slide($navShaft, {
  2238. time: time,
  2239. pos: pos,
  2240. direction: opts.navdir,
  2241. onEnd: function () {
  2242. thumbsDraw(pos, true);
  2243. thumbArrUpdate();
  2244. }
  2245. });
  2246. setShadow($nav, findShadowEdge(pos, navShaftTouchTail.min, navShaftTouchTail.max, opts.navdir));
  2247. }
  2248. }
  2249. }
  2250. function navUpdate() {
  2251. deactivateFrames(navFrameKey);
  2252. toDeactivate[navFrameKey].push(activeFrame[navFrameKey].addClass(activeClass).attr('data-active', true));
  2253. }
  2254. function deactivateFrames(key) {
  2255. var _toDeactivate = toDeactivate[key];
  2256. while (_toDeactivate.length) {
  2257. _toDeactivate.shift().removeClass(activeClass).attr('data-active', false);
  2258. }
  2259. }
  2260. function detachFrames(key) {
  2261. var _toDetach = toDetach[key];
  2262. $.each(activeIndexes, function (i, index) {
  2263. delete _toDetach[normalizeIndex(index)];
  2264. });
  2265. $.each(_toDetach, function (index, $frame) {
  2266. delete _toDetach[index];
  2267. $frame.detach();
  2268. });
  2269. }
  2270. function stageShaftReposition(skipOnEnd) {
  2271. repositionIndex = dirtyIndex = activeIndex;
  2272. var $frame = activeFrame[STAGE_FRAME_KEY];
  2273. if ($frame) {
  2274. deactivateFrames(STAGE_FRAME_KEY);
  2275. toDeactivate[STAGE_FRAME_KEY].push($frame.addClass(activeClass).attr('data-active', true));
  2276. if ($frame.hasClass(stageFrameClass)) {
  2277. $frame.attr('aria-hidden', 'false');
  2278. }
  2279. skipOnEnd || that.showStage.onEnd(true);
  2280. stop($stageShaft, 0, true);
  2281. detachFrames(STAGE_FRAME_KEY);
  2282. stageFramePosition(activeIndexes);
  2283. setStageShaftMinmaxAndSnap();
  2284. setNavShaftMinMax();
  2285. addEnterUp($stageShaft[0], function () {
  2286. if (!$fotorama.hasClass(fullscreenClass)) {
  2287. that.requestFullScreen();
  2288. $fullscreenIcon.focus();
  2289. }
  2290. });
  2291. }
  2292. }
  2293. function extendMeasures(options, measuresArray) {
  2294. if (!options) return;
  2295. $.each(measuresArray, function (i, measures) {
  2296. if (!measures) return;
  2297. $.extend(measures, {
  2298. width: options.width || measures.width,
  2299. height: options.height,
  2300. minwidth: options.minwidth,
  2301. maxwidth: options.maxwidth,
  2302. minheight: options.minheight,
  2303. maxheight: options.maxheight,
  2304. ratio: getRatio(options.ratio)
  2305. })
  2306. });
  2307. }
  2308. function triggerEvent(event, extra) {
  2309. $fotorama.trigger(_fotoramaClass + ':' + event, [that, extra]);
  2310. }
  2311. function onTouchStart() {
  2312. clearTimeout(onTouchEnd.t);
  2313. touchedFLAG = 1;
  2314. if (opts.stopautoplayontouch) {
  2315. that.stopAutoplay();
  2316. } else {
  2317. pausedAutoplayFLAG = true;
  2318. }
  2319. }
  2320. function onTouchEnd() {
  2321. if (!touchedFLAG) return;
  2322. if (!opts.stopautoplayontouch) {
  2323. releaseAutoplay();
  2324. changeAutoplay();
  2325. }
  2326. onTouchEnd.t = setTimeout(function () {
  2327. touchedFLAG = 0;
  2328. }, TRANSITION_DURATION + TOUCH_TIMEOUT);
  2329. }
  2330. function releaseAutoplay() {
  2331. pausedAutoplayFLAG = !!($videoPlaying || stoppedAutoplayFLAG);
  2332. }
  2333. function changeAutoplay() {
  2334. clearTimeout(changeAutoplay.t);
  2335. waitFor.stop(changeAutoplay.w);
  2336. if (!opts.autoplay || pausedAutoplayFLAG) {
  2337. if (that.autoplay) {
  2338. that.autoplay = false;
  2339. triggerEvent('stopautoplay');
  2340. }
  2341. return;
  2342. }
  2343. if (!that.autoplay) {
  2344. that.autoplay = true;
  2345. triggerEvent('startautoplay');
  2346. }
  2347. var _activeIndex = activeIndex;
  2348. var frameData = activeFrame[STAGE_FRAME_KEY].data();
  2349. changeAutoplay.w = waitFor(function () {
  2350. return frameData.state || _activeIndex !== activeIndex;
  2351. }, function () {
  2352. changeAutoplay.t = setTimeout(function () {
  2353. if (pausedAutoplayFLAG || _activeIndex !== activeIndex) return;
  2354. var _nextAutoplayIndex = nextAutoplayIndex,
  2355. nextFrameData = data[_nextAutoplayIndex][STAGE_FRAME_KEY].data();
  2356. changeAutoplay.w = waitFor(function () {
  2357. return nextFrameData.state || _nextAutoplayIndex !== nextAutoplayIndex;
  2358. }, function () {
  2359. if (pausedAutoplayFLAG || _nextAutoplayIndex !== nextAutoplayIndex) return;
  2360. that.show(o_loop ? getDirectionSign(!o_rtl) : nextAutoplayIndex);
  2361. });
  2362. }, opts.autoplay);
  2363. });
  2364. }
  2365. that.startAutoplay = function (interval) {
  2366. if (that.autoplay) return this;
  2367. pausedAutoplayFLAG = stoppedAutoplayFLAG = false;
  2368. setAutoplayInterval(interval || opts.autoplay);
  2369. changeAutoplay();
  2370. return this;
  2371. };
  2372. that.stopAutoplay = function () {
  2373. if (that.autoplay) {
  2374. pausedAutoplayFLAG = stoppedAutoplayFLAG = true;
  2375. changeAutoplay();
  2376. }
  2377. return this;
  2378. };
  2379. that.showSlide = function (slideDir) {
  2380. var currentPosition = readPosition($navShaft, opts.navdir),
  2381. pos,
  2382. time = 500 * 1.1,
  2383. size = opts.navdir === 'horizontal' ? opts.thumbwidth : opts.thumbheight,
  2384. onEnd = function () {
  2385. thumbArrUpdate();
  2386. };
  2387. if (slideDir === 'next') {
  2388. pos = currentPosition - (size + opts.margin) * thumbsPerSlide;
  2389. }
  2390. if (slideDir === 'prev') {
  2391. pos = currentPosition + (size + opts.margin) * thumbsPerSlide;
  2392. }
  2393. pos = validateRestrictions(pos, navShaftTouchTail);
  2394. thumbsDraw(pos, true);
  2395. slide($navShaft, {
  2396. time: time,
  2397. pos: pos,
  2398. direction: opts.navdir,
  2399. onEnd: onEnd
  2400. });
  2401. };
  2402. that.showWhileLongPress = function (options) {
  2403. if (that.longPress.singlePressInProgress) {
  2404. return;
  2405. }
  2406. var index = calcActiveIndex(options);
  2407. calcGlobalIndexes(index);
  2408. var time = calcTime(options) / 50;
  2409. var _activeFrame = activeFrame;
  2410. that.activeFrame = activeFrame = data[activeIndex];
  2411. var silent = _activeFrame === activeFrame && !options.user;
  2412. that.showNav(silent, options, time);
  2413. return this;
  2414. };
  2415. that.showEndLongPress = function (options) {
  2416. if (that.longPress.singlePressInProgress) {
  2417. return;
  2418. }
  2419. var index = calcActiveIndex(options);
  2420. calcGlobalIndexes(index);
  2421. var time = calcTime(options) / 50;
  2422. var _activeFrame = activeFrame;
  2423. that.activeFrame = activeFrame = data[activeIndex];
  2424. var silent = _activeFrame === activeFrame && !options.user;
  2425. that.showStage(silent, options, time);
  2426. showedFLAG = typeof lastActiveIndex !== 'undefined' && lastActiveIndex !== activeIndex;
  2427. lastActiveIndex = activeIndex;
  2428. return this;
  2429. };
  2430. function calcActiveIndex (options) {
  2431. var index;
  2432. if (typeof options !== 'object') {
  2433. index = options;
  2434. options = {};
  2435. } else {
  2436. index = options.index;
  2437. }
  2438. index = index === '>' ? dirtyIndex + 1 : index === '<' ? dirtyIndex - 1 : index === '<<' ? 0 : index === '>>' ? size - 1 : index;
  2439. index = isNaN(index) ? undefined : index;
  2440. index = typeof index === 'undefined' ? activeIndex || 0 : index;
  2441. return index;
  2442. }
  2443. function calcGlobalIndexes (index) {
  2444. that.activeIndex = activeIndex = edgeIndex(index);
  2445. prevIndex = getPrevIndex(activeIndex);
  2446. nextIndex = getNextIndex(activeIndex);
  2447. nextAutoplayIndex = normalizeIndex(activeIndex + (o_rtl ? -1 : 1));
  2448. activeIndexes = [activeIndex, prevIndex, nextIndex];
  2449. dirtyIndex = o_loop ? index : activeIndex;
  2450. }
  2451. function calcTime (options) {
  2452. var diffIndex = Math.abs(lastActiveIndex - dirtyIndex),
  2453. time = getNumber(options.time, function () {
  2454. return Math.min(o_transitionDuration * (1 + (diffIndex - 1) / 12), o_transitionDuration * 2);
  2455. });
  2456. if (options.slow) {
  2457. time *= 10;
  2458. }
  2459. return time;
  2460. }
  2461. that.showStage = function (silent, options, time, e) {
  2462. if (e !== undefined && e.target.tagName == 'IFRAME') {
  2463. return;
  2464. }
  2465. unloadVideo($videoPlaying, activeFrame.i !== data[normalizeIndex(repositionIndex)].i);
  2466. frameDraw(activeIndexes, 'stage');
  2467. stageFramePosition(SLOW ? [dirtyIndex] : [dirtyIndex, getPrevIndex(dirtyIndex), getNextIndex(dirtyIndex)]);
  2468. updateTouchTails('go', true);
  2469. silent || triggerEvent('show', {
  2470. user: options.user,
  2471. time: time
  2472. });
  2473. pausedAutoplayFLAG = true;
  2474. var overPos = options.overPos;
  2475. var onEnd = that.showStage.onEnd = function (skipReposition) {
  2476. if (onEnd.ok) return;
  2477. onEnd.ok = true;
  2478. skipReposition || stageShaftReposition(true);
  2479. if (!silent) {
  2480. triggerEvent('showend', {
  2481. user: options.user
  2482. });
  2483. }
  2484. if (!skipReposition && o_transition && o_transition !== opts.transition) {
  2485. that.setOptions({transition: o_transition});
  2486. o_transition = false;
  2487. return;
  2488. }
  2489. updateFotoramaState();
  2490. loadImg(activeIndexes, 'stage');
  2491. updateTouchTails('go', false);
  2492. stageWheelUpdate();
  2493. stageCursor();
  2494. releaseAutoplay();
  2495. changeAutoplay();
  2496. if (that.fullScreen) {
  2497. activeFrame[STAGE_FRAME_KEY].find('.' + imgFullClass).attr('aria-hidden', false);
  2498. activeFrame[STAGE_FRAME_KEY].find('.' + imgClass).attr('aria-hidden', true)
  2499. } else {
  2500. activeFrame[STAGE_FRAME_KEY].find('.' + imgFullClass).attr('aria-hidden', true);
  2501. activeFrame[STAGE_FRAME_KEY].find('.' + imgClass).attr('aria-hidden', false)
  2502. }
  2503. };
  2504. if (!o_fade) {
  2505. slide($stageShaft, {
  2506. pos: -getPosByIndex(dirtyIndex, measures.w, opts.margin, repositionIndex),
  2507. overPos: overPos,
  2508. time: time,
  2509. onEnd: onEnd
  2510. });
  2511. } else {
  2512. var $activeFrame = activeFrame[STAGE_FRAME_KEY],
  2513. $prevActiveFrame = data[lastActiveIndex] && activeIndex !== lastActiveIndex ? data[lastActiveIndex][STAGE_FRAME_KEY] : null;
  2514. fade($activeFrame, $prevActiveFrame, $stageFrame, {
  2515. time: time,
  2516. method: opts.transition,
  2517. onEnd: onEnd
  2518. }, fadeStack);
  2519. }
  2520. arrsUpdate();
  2521. };
  2522. that.showNav = function(silent, options, time){
  2523. thumbArrUpdate();
  2524. if (o_nav) {
  2525. navUpdate();
  2526. var guessIndex = limitIndex(activeIndex + minMaxLimit(dirtyIndex - lastActiveIndex, -1, 1));
  2527. slideNavShaft({
  2528. time: time,
  2529. coo: guessIndex !== activeIndex && options.coo,
  2530. guessIndex: typeof options.coo !== 'undefined' ? guessIndex : activeIndex,
  2531. keep: silent
  2532. });
  2533. if (o_navThumbs) slideThumbBorder(time);
  2534. }
  2535. };
  2536. that.show = function (options, e) {
  2537. that.longPress.singlePressInProgress = true;
  2538. var index = calcActiveIndex(options);
  2539. calcGlobalIndexes(index);
  2540. var time = calcTime(options);
  2541. var _activeFrame = activeFrame;
  2542. that.activeFrame = activeFrame = data[activeIndex];
  2543. var silent = _activeFrame === activeFrame && !options.user;
  2544. that.showStage(silent, options, time, e);
  2545. that.showNav(silent, options, time);
  2546. showedFLAG = typeof lastActiveIndex !== 'undefined' && lastActiveIndex !== activeIndex;
  2547. lastActiveIndex = activeIndex;
  2548. that.longPress.singlePressInProgress = false;
  2549. return this;
  2550. };
  2551. that.requestFullScreen = function () {
  2552. if (o_allowFullScreen && !that.fullScreen) {
  2553. //check that this is not video
  2554. var isVideo = $((that.activeFrame || {}).$stageFrame || {}).hasClass('fotorama-video-container');
  2555. if(isVideo) {
  2556. return;
  2557. }
  2558. scrollTop = $WINDOW.scrollTop();
  2559. scrollLeft = $WINDOW.scrollLeft();
  2560. lockScroll($WINDOW);
  2561. updateTouchTails('x', true);
  2562. measuresStash = $.extend({}, measures);
  2563. $fotorama
  2564. .addClass(fullscreenClass)
  2565. .appendTo($BODY.addClass(_fullscreenClass));
  2566. $HTML.addClass(_fullscreenClass);
  2567. unloadVideo($videoPlaying, true, true);
  2568. that.fullScreen = true;
  2569. if (o_nativeFullScreen) {
  2570. fullScreenApi.request(fotorama);
  2571. }
  2572. that.resize();
  2573. loadImg(activeIndexes, 'stage');
  2574. updateFotoramaState();
  2575. triggerEvent('fullscreenenter');
  2576. if (!('ontouchstart' in window)) {
  2577. $fullscreenIcon.focus();
  2578. }
  2579. }
  2580. return this;
  2581. };
  2582. function cancelFullScreen() {
  2583. if (that.fullScreen) {
  2584. that.fullScreen = false;
  2585. if (FULLSCREEN) {
  2586. fullScreenApi.cancel(fotorama);
  2587. }
  2588. $BODY.removeClass(_fullscreenClass);
  2589. $HTML.removeClass(_fullscreenClass);
  2590. $fotorama
  2591. .removeClass(fullscreenClass)
  2592. .insertAfter($anchor);
  2593. measures = $.extend({}, measuresStash);
  2594. unloadVideo($videoPlaying, true, true);
  2595. updateTouchTails('x', false);
  2596. that.resize();
  2597. loadImg(activeIndexes, 'stage');
  2598. lockScroll($WINDOW, scrollLeft, scrollTop);
  2599. triggerEvent('fullscreenexit');
  2600. }
  2601. }
  2602. that.cancelFullScreen = function () {
  2603. if (o_nativeFullScreen && fullScreenApi.is()) {
  2604. fullScreenApi.cancel(document);
  2605. } else {
  2606. cancelFullScreen();
  2607. }
  2608. return this;
  2609. };
  2610. that.toggleFullScreen = function () {
  2611. return that[(that.fullScreen ? 'cancel' : 'request') + 'FullScreen']();
  2612. };
  2613. that.resize = function (options) {
  2614. if (!data) return this;
  2615. var time = arguments[1] || 0,
  2616. setFLAG = arguments[2];
  2617. thumbsPerSlide = getThumbsInSlide($wrap, opts);
  2618. extendMeasures(!that.fullScreen ? optionsToLowerCase(options) : {
  2619. width: $(window).width(),
  2620. maxwidth: null,
  2621. minwidth: null,
  2622. height: $(window).height(),
  2623. maxheight: null,
  2624. minheight: null
  2625. }, [measures, setFLAG || that.fullScreen || opts]);
  2626. var width = measures.width,
  2627. height = measures.height,
  2628. ratio = measures.ratio,
  2629. windowHeight = $WINDOW.height() - (o_nav ? $nav.height() : 0);
  2630. if (measureIsValid(width)) {
  2631. $wrap.css({width: ''});
  2632. $wrap.css({height: ''});
  2633. $stage.css({width: ''});
  2634. $stage.css({height: ''});
  2635. $stageShaft.css({width: ''});
  2636. $stageShaft.css({height: ''});
  2637. $nav.css({width: ''});
  2638. $nav.css({height: ''});
  2639. $wrap.css({minWidth: measures.minwidth || 0, maxWidth: measures.maxwidth || MAX_WIDTH});
  2640. if (o_nav === 'dots') {
  2641. $navWrap.hide();
  2642. }
  2643. width = measures.W = measures.w = $wrap.width();
  2644. measures.nw = o_nav && numberFromWhatever(opts.navwidth, width) || width;
  2645. $stageShaft.css({width: measures.w, marginLeft: (measures.W - measures.w) / 2});
  2646. height = numberFromWhatever(height, windowHeight);
  2647. height = height || (ratio && width / ratio);
  2648. if (height) {
  2649. width = Math.round(width);
  2650. height = measures.h = Math.round(minMaxLimit(height, numberFromWhatever(measures.minheight, windowHeight), numberFromWhatever(measures.maxheight, windowHeight)));
  2651. $stage.css({'width': width, 'height': height});
  2652. if (opts.navdir === 'vertical' && !that.fullscreen) {
  2653. $nav.width(opts.thumbwidth + opts.thumbmargin * 2);
  2654. }
  2655. if (opts.navdir === 'horizontal' && !that.fullscreen) {
  2656. $nav.height(opts.thumbheight + opts.thumbmargin * 2);
  2657. }
  2658. if (o_nav === 'dots') {
  2659. $nav.width(width)
  2660. .height('auto');
  2661. $navWrap.show();
  2662. }
  2663. if (opts.navdir === 'vertical' && that.fullScreen) {
  2664. $stage.css('height', $WINDOW.height());
  2665. }
  2666. if (opts.navdir === 'horizontal' && that.fullScreen) {
  2667. $stage.css('height', $WINDOW.height() - $nav.height());
  2668. }
  2669. if (o_nav) {
  2670. switch (opts.navdir) {
  2671. case 'vertical':
  2672. $navWrap.removeClass(navShafthorizontalClass);
  2673. $navWrap.removeClass(navShaftListClass);
  2674. $navWrap.addClass(navShaftVerticalClass);
  2675. $nav
  2676. .stop()
  2677. .animate({height: measures.h, width: opts.thumbwidth}, time);
  2678. break;
  2679. case 'list':
  2680. $navWrap.removeClass(navShaftVerticalClass);
  2681. $navWrap.removeClass(navShafthorizontalClass);
  2682. $navWrap.addClass(navShaftListClass);
  2683. break;
  2684. default:
  2685. $navWrap.removeClass(navShaftVerticalClass);
  2686. $navWrap.removeClass(navShaftListClass);
  2687. $navWrap.addClass(navShafthorizontalClass);
  2688. $nav
  2689. .stop()
  2690. .animate({width: measures.nw}, time);
  2691. break;
  2692. }
  2693. stageShaftReposition();
  2694. slideNavShaft({guessIndex: activeIndex, time: time, keep: true});
  2695. if (o_navThumbs && frameAppend.nav) slideThumbBorder(time);
  2696. }
  2697. measuresSetFLAG = setFLAG || true;
  2698. ready.ok = true;
  2699. ready();
  2700. }
  2701. }
  2702. stageLeft = $stage.offset().left;
  2703. setStagePosition();
  2704. return this;
  2705. };
  2706. that.setOptions = function (options) {
  2707. $.extend(opts, options);
  2708. reset();
  2709. return this;
  2710. };
  2711. that.shuffle = function () {
  2712. data && shuffle(data) && reset();
  2713. return this;
  2714. };
  2715. function setShadow($el, edge) {
  2716. if (o_shadows) {
  2717. $el.removeClass(shadowsLeftClass + ' ' + shadowsRightClass);
  2718. $el.removeClass(shadowsTopClass + ' ' + shadowsBottomClass);
  2719. edge && !$videoPlaying && $el.addClass(edge.replace(/^|\s/g, ' ' + shadowsClass + '--'));
  2720. }
  2721. }
  2722. that.longPress = {
  2723. threshold: 1,
  2724. count: 0,
  2725. thumbSlideTime: 20,
  2726. progress: function(){
  2727. if (!this.inProgress) {
  2728. this.count++;
  2729. this.inProgress = this.count > this.threshold;
  2730. }
  2731. },
  2732. end: function(){
  2733. if(this.inProgress) {
  2734. this.isEnded = true
  2735. }
  2736. },
  2737. reset: function(){
  2738. this.count = 0;
  2739. this.inProgress = false;
  2740. this.isEnded = false;
  2741. }
  2742. };
  2743. that.destroy = function () {
  2744. that.cancelFullScreen();
  2745. that.stopAutoplay();
  2746. data = that.data = null;
  2747. appendElements();
  2748. activeIndexes = [];
  2749. detachFrames(STAGE_FRAME_KEY);
  2750. reset.ok = false;
  2751. return this;
  2752. };
  2753. /**
  2754. *
  2755. * @returns {jQuery.Fotorama}
  2756. */
  2757. that.playVideo = function () {
  2758. var dataFrame = activeFrame,
  2759. video = dataFrame.video,
  2760. _activeIndex = activeIndex;
  2761. if (typeof video === 'object' && dataFrame.videoReady) {
  2762. o_nativeFullScreen && that.fullScreen && that.cancelFullScreen();
  2763. waitFor(function () {
  2764. return !fullScreenApi.is() || _activeIndex !== activeIndex;
  2765. }, function () {
  2766. if (_activeIndex === activeIndex) {
  2767. dataFrame.$video = dataFrame.$video || $(div(videoClass)).append(createVideoFrame(video));
  2768. dataFrame.$video.appendTo(dataFrame[STAGE_FRAME_KEY]);
  2769. $wrap.addClass(wrapVideoClass);
  2770. $videoPlaying = dataFrame.$video;
  2771. stageNoMove();
  2772. $arrs.blur();
  2773. $fullscreenIcon.blur();
  2774. triggerEvent('loadvideo');
  2775. }
  2776. });
  2777. }
  2778. return this;
  2779. };
  2780. that.stopVideo = function () {
  2781. unloadVideo($videoPlaying, true, true);
  2782. return this;
  2783. };
  2784. that.spliceByIndex = function (index, newImgObj) {
  2785. newImgObj.i = index + 1;
  2786. newImgObj.img && $.ajax({
  2787. url: newImgObj.img,
  2788. type: 'HEAD',
  2789. success: function () {
  2790. data.splice(index, 1, newImgObj);
  2791. reset();
  2792. }
  2793. });
  2794. };
  2795. function unloadVideo($video, unloadActiveFLAG, releaseAutoplayFLAG) {
  2796. if (unloadActiveFLAG) {
  2797. $wrap.removeClass(wrapVideoClass);
  2798. $videoPlaying = false;
  2799. stageNoMove();
  2800. }
  2801. if ($video && $video !== $videoPlaying) {
  2802. $video.remove();
  2803. triggerEvent('unloadvideo');
  2804. }
  2805. if (releaseAutoplayFLAG) {
  2806. releaseAutoplay();
  2807. changeAutoplay();
  2808. }
  2809. }
  2810. function toggleControlsClass(FLAG) {
  2811. $wrap.toggleClass(wrapNoControlsClass, FLAG);
  2812. }
  2813. function stageCursor(e) {
  2814. if (stageShaftTouchTail.flow) return;
  2815. var x = e ? e.pageX : stageCursor.x,
  2816. pointerFLAG = x && !disableDirrection(getDirection(x)) && opts.click;
  2817. if (stageCursor.p !== pointerFLAG
  2818. && $stage.toggleClass(pointerClass, pointerFLAG)) {
  2819. stageCursor.p = pointerFLAG;
  2820. stageCursor.x = x;
  2821. }
  2822. }
  2823. $stage.on('mousemove', stageCursor);
  2824. function clickToShow(showOptions, e) {
  2825. clearTimeout(clickToShow.t);
  2826. if (opts.clicktransition && opts.clicktransition !== opts.transition) {
  2827. setTimeout(function () {
  2828. var _o_transition = opts.transition;
  2829. that.setOptions({transition: opts.clicktransition});
  2830. // now safe to pass base transition to o_transition, so that.show will restor it
  2831. o_transition = _o_transition;
  2832. // this timeout is here to prevent jerking in some browsers
  2833. clickToShow.t = setTimeout(function () {
  2834. that.show(showOptions);
  2835. }, 10);
  2836. }, 0);
  2837. } else {
  2838. that.show(showOptions, e);
  2839. }
  2840. }
  2841. function onStageTap(e, toggleControlsFLAG) {
  2842. var target = e.target,
  2843. $target = $(target);
  2844. if ($target.hasClass(videoPlayClass)) {
  2845. that.playVideo();
  2846. } else if (target === fullscreenIcon) {
  2847. that.toggleFullScreen();
  2848. } else if ($videoPlaying) {
  2849. target === videoClose && unloadVideo($videoPlaying, true, true);
  2850. } else if (!$fotorama.hasClass(fullscreenClass)) {
  2851. that.requestFullScreen();
  2852. }
  2853. }
  2854. function updateTouchTails(key, value) {
  2855. stageShaftTouchTail[key] = navShaftTouchTail[key] = value;
  2856. }
  2857. stageShaftTouchTail = moveOnTouch($stageShaft, {
  2858. onStart: onTouchStart,
  2859. onMove: function (e, result) {
  2860. setShadow($stage, result.edge);
  2861. },
  2862. onTouchEnd: onTouchEnd,
  2863. onEnd: function (result) {
  2864. var toggleControlsFLAG;
  2865. setShadow($stage);
  2866. toggleControlsFLAG = (MS_POINTER && !hoverFLAG || result.touch) &&
  2867. opts.arrows;
  2868. if ((result.moved || (toggleControlsFLAG && result.pos !== result.newPos && !result.control)) && result.$target[0] !== $fullscreenIcon[0]) {
  2869. var index = getIndexByPos(result.newPos, measures.w, opts.margin, repositionIndex);
  2870. that.show({
  2871. index: index,
  2872. time: o_fade ? o_transitionDuration : result.time,
  2873. overPos: result.overPos,
  2874. user: true
  2875. });
  2876. } else if (!result.aborted && !result.control) {
  2877. onStageTap(result.startEvent, toggleControlsFLAG);
  2878. }
  2879. },
  2880. timeLow: 1,
  2881. timeHigh: 1,
  2882. friction: 2,
  2883. select: '.' + selectClass + ', .' + selectClass + ' *',
  2884. $wrap: $stage,
  2885. direction: 'horizontal'
  2886. });
  2887. navShaftTouchTail = moveOnTouch($navShaft, {
  2888. onStart: onTouchStart,
  2889. onMove: function (e, result) {
  2890. setShadow($nav, result.edge);
  2891. },
  2892. onTouchEnd: onTouchEnd,
  2893. onEnd: function (result) {
  2894. function onEnd() {
  2895. slideNavShaft.l = result.newPos;
  2896. releaseAutoplay();
  2897. changeAutoplay();
  2898. thumbsDraw(result.newPos, true);
  2899. thumbArrUpdate();
  2900. }
  2901. if (!result.moved) {
  2902. var target = result.$target.closest('.' + navFrameClass, $navShaft)[0];
  2903. target && onNavFrameClick.call(target, result.startEvent);
  2904. } else if (result.pos !== result.newPos) {
  2905. pausedAutoplayFLAG = true;
  2906. slide($navShaft, {
  2907. time: result.time,
  2908. pos: result.newPos,
  2909. overPos: result.overPos,
  2910. direction: opts.navdir,
  2911. onEnd: onEnd
  2912. });
  2913. thumbsDraw(result.newPos);
  2914. o_shadows && setShadow($nav, findShadowEdge(result.newPos, navShaftTouchTail.min, navShaftTouchTail.max, result.dir));
  2915. } else {
  2916. onEnd();
  2917. }
  2918. },
  2919. timeLow: .5,
  2920. timeHigh: 2,
  2921. friction: 5,
  2922. $wrap: $nav,
  2923. direction: opts.navdir
  2924. });
  2925. stageWheelTail = wheel($stage, {
  2926. shift: true,
  2927. onEnd: function (e, direction) {
  2928. onTouchStart();
  2929. onTouchEnd();
  2930. that.show({index: direction, slow: e.altKey})
  2931. }
  2932. });
  2933. navWheelTail = wheel($nav, {
  2934. onEnd: function (e, direction) {
  2935. onTouchStart();
  2936. onTouchEnd();
  2937. var newPos = stop($navShaft) + direction * .25;
  2938. $navShaft.css(getTranslate(minMaxLimit(newPos, navShaftTouchTail.min, navShaftTouchTail.max), opts.navdir));
  2939. o_shadows && setShadow($nav, findShadowEdge(newPos, navShaftTouchTail.min, navShaftTouchTail.max, opts.navdir));
  2940. navWheelTail.prevent = {'<': newPos >= navShaftTouchTail.max, '>': newPos <= navShaftTouchTail.min};
  2941. clearTimeout(navWheelTail.t);
  2942. navWheelTail.t = setTimeout(function () {
  2943. slideNavShaft.l = newPos;
  2944. thumbsDraw(newPos, true)
  2945. }, TOUCH_TIMEOUT);
  2946. thumbsDraw(newPos);
  2947. }
  2948. });
  2949. $wrap.hover(
  2950. function () {
  2951. setTimeout(function () {
  2952. if (touchedFLAG) return;
  2953. toggleControlsClass(!(hoverFLAG = true));
  2954. }, 0);
  2955. },
  2956. function () {
  2957. if (!hoverFLAG) return;
  2958. toggleControlsClass(!(hoverFLAG = false));
  2959. }
  2960. );
  2961. function onNavFrameClick(e) {
  2962. var index = $(this).data().eq;
  2963. if (opts.navtype === 'thumbs') {
  2964. clickToShow({index: index, slow: e.altKey, user: true, coo: e._x - $nav.offset().left});
  2965. } else {
  2966. clickToShow({index: index, slow: e.altKey, user: true});
  2967. }
  2968. }
  2969. function onArrClick(e) {
  2970. clickToShow({index: $arrs.index(this) ? '>' : '<', slow: e.altKey, user: true});
  2971. }
  2972. smartClick($arrs, function (e) {
  2973. stopEvent(e);
  2974. onArrClick.call(this, e);
  2975. }, {
  2976. onStart: function () {
  2977. onTouchStart();
  2978. stageShaftTouchTail.control = true;
  2979. },
  2980. onTouchEnd: onTouchEnd
  2981. });
  2982. smartClick($thumbArrLeft, function (e) {
  2983. stopEvent(e);
  2984. if (opts.navtype === 'thumbs') {
  2985. that.show('<');
  2986. } else {
  2987. that.showSlide('prev')
  2988. }
  2989. });
  2990. smartClick($thumbArrRight, function (e) {
  2991. stopEvent(e);
  2992. if (opts.navtype === 'thumbs') {
  2993. that.show('>');
  2994. } else {
  2995. that.showSlide('next')
  2996. }
  2997. });
  2998. function addFocusOnControls(el) {
  2999. addFocus(el, function () {
  3000. setTimeout(function () {
  3001. lockScroll($stage);
  3002. }, 0);
  3003. toggleControlsClass(false);
  3004. });
  3005. }
  3006. $arrs.each(function () {
  3007. addEnterUp(this, function (e) {
  3008. onArrClick.call(this, e);
  3009. });
  3010. addFocusOnControls(this);
  3011. });
  3012. addEnterUp(fullscreenIcon, function () {
  3013. if ($fotorama.hasClass(fullscreenClass)) {
  3014. that.cancelFullScreen();
  3015. $stageShaft.focus();
  3016. } else {
  3017. that.requestFullScreen();
  3018. $fullscreenIcon.focus();
  3019. }
  3020. });
  3021. addFocusOnControls(fullscreenIcon);
  3022. function reset() {
  3023. setData();
  3024. setOptions();
  3025. if (!reset.i) {
  3026. reset.i = true;
  3027. // Only once
  3028. var _startindex = opts.startindex;
  3029. activeIndex = repositionIndex = dirtyIndex = lastActiveIndex = startIndex = edgeIndex(_startindex) || 0;
  3030. /*(o_rtl ? size - 1 : 0)*///;
  3031. }
  3032. if (size) {
  3033. if (changeToRtl()) return;
  3034. if ($videoPlaying) {
  3035. unloadVideo($videoPlaying, true);
  3036. }
  3037. activeIndexes = [];
  3038. detachFrames(STAGE_FRAME_KEY);
  3039. reset.ok = true;
  3040. that.show({index: activeIndex, time: 0});
  3041. that.resize();
  3042. } else {
  3043. that.destroy();
  3044. }
  3045. }
  3046. function changeToRtl() {
  3047. if (!changeToRtl.f === o_rtl) {
  3048. changeToRtl.f = o_rtl;
  3049. activeIndex = size - 1 - activeIndex;
  3050. that.reverse();
  3051. return true;
  3052. }
  3053. }
  3054. $.each('load push pop shift unshift reverse sort splice'.split(' '), function (i, method) {
  3055. that[method] = function () {
  3056. data = data || [];
  3057. if (method !== 'load') {
  3058. Array.prototype[method].apply(data, arguments);
  3059. } else if (arguments[0] && typeof arguments[0] === 'object' && arguments[0].length) {
  3060. data = clone(arguments[0]);
  3061. }
  3062. reset();
  3063. return that;
  3064. }
  3065. });
  3066. function ready() {
  3067. if (ready.ok) {
  3068. ready.ok = false;
  3069. triggerEvent('ready');
  3070. }
  3071. }
  3072. reset();
  3073. };
  3074. $.fn.fotorama = function (opts) {
  3075. return this.each(function () {
  3076. var that = this,
  3077. $fotorama = $(this),
  3078. fotoramaData = $fotorama.data(),
  3079. fotorama = fotoramaData.fotorama;
  3080. if (!fotorama) {
  3081. waitFor(function () {
  3082. return !isHidden(that);
  3083. }, function () {
  3084. fotoramaData.urtext = $fotorama.html();
  3085. new $.Fotorama($fotorama,
  3086. $.extend(
  3087. {},
  3088. OPTIONS,
  3089. window.fotoramaDefaults,
  3090. opts,
  3091. fotoramaData
  3092. )
  3093. );
  3094. });
  3095. } else {
  3096. fotorama.setOptions(opts, true);
  3097. }
  3098. });
  3099. };
  3100. $.Fotorama.instances = [];
  3101. function calculateIndexes() {
  3102. $.each($.Fotorama.instances, function (index, instance) {
  3103. instance.index = index;
  3104. });
  3105. }
  3106. function addInstance(instance) {
  3107. $.Fotorama.instances.push(instance);
  3108. calculateIndexes();
  3109. }
  3110. function hideInstance(instance) {
  3111. $.Fotorama.instances.splice(instance.index, 1);
  3112. calculateIndexes();
  3113. }
  3114. $.Fotorama.cache = {};
  3115. $.Fotorama.measures = {};
  3116. $ = $ || {};
  3117. $.Fotorama = $.Fotorama || {};
  3118. $.Fotorama.jst = $.Fotorama.jst || {};
  3119. $.Fotorama.jst.dots = function (v) {
  3120. var __t, __p = '', __e = _.escape;
  3121. __p += '<div class="fotorama__nav__frame fotorama__nav__frame--dot" tabindex="0" role="button" data-gallery-role="nav-frame" data-nav-type="thumb" aria-label>\r\n <div class="fotorama__dot"></div>\r\n</div>';
  3122. return __p
  3123. };
  3124. $.Fotorama.jst.frameCaption = function (v) {
  3125. var __t, __p = '', __e = _.escape;
  3126. __p += '<div class="fotorama__caption" aria-hidden="true">\r\n <div class="fotorama__caption__wrap" id="' +
  3127. ((__t = ( v.labelledby )) == null ? '' : __t) +
  3128. '">' +
  3129. ((__t = ( v.caption )) == null ? '' : __t) +
  3130. '</div>\r\n</div>\r\n';
  3131. return __p
  3132. };
  3133. $.Fotorama.jst.style = function (v) {
  3134. var __t, __p = '', __e = _.escape;
  3135. __p += '.fotorama' +
  3136. ((__t = ( v.s )) == null ? '' : __t) +
  3137. ' .fotorama__nav--thumbs .fotorama__nav__frame{\r\npadding:' +
  3138. ((__t = ( v.m )) == null ? '' : __t) +
  3139. 'px;\r\nheight:' +
  3140. ((__t = ( v.h )) == null ? '' : __t) +
  3141. 'px}\r\n.fotorama' +
  3142. ((__t = ( v.s )) == null ? '' : __t) +
  3143. ' .fotorama__thumb-border{\r\nheight:' +
  3144. ((__t = ( v.h )) == null ? '' : __t) +
  3145. 'px;\r\nborder-width:' +
  3146. ((__t = ( v.b )) == null ? '' : __t) +
  3147. 'px;\r\nmargin-top:' +
  3148. ((__t = ( v.m )) == null ? '' : __t) +
  3149. 'px}';
  3150. return __p
  3151. };
  3152. $.Fotorama.jst.thumb = function (v) {
  3153. var __t, __p = '', __e = _.escape;
  3154. __p += '<div class="fotorama__nav__frame fotorama__nav__frame--thumb" tabindex="0" role="button" data-gallery-role="nav-frame" data-nav-type="thumb" aria-label>\r\n <div class="fotorama__thumb">\r\n </div>\r\n</div>';
  3155. return __p
  3156. };
  3157. })(window, document, location, typeof jQuery !== 'undefined' && jQuery);