fotorama.dev.js 100 KB

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