123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666 |
- /*! PhotoSwipe - v4.0.7 - 2015-03-18
- * http://photoswipe.com
- * Copyright (c) 2015 Dmitry Semenov; */
- (function (root, factory) {
- if (typeof define === 'function' && define.amd) {
- define(factory);
- } else if (typeof exports === 'object') {
- module.exports = factory();
- } else {
- root.PhotoSwipe = factory();
- }
- })(this, function () {
- 'use strict';
- var PhotoSwipe = function(template, UiClass, items, options){
- /*>>framework-bridge*/
- /**
- *
- * Set of generic functions used by gallery.
- *
- * You're free to modify anything here as long as functionality is kept.
- *
- */
- var framework = {
- features: null,
- bind: function(target, type, listener, unbind) {
- var methodName = (unbind ? 'remove' : 'add') + 'EventListener';
- type = type.split(' ');
- for(var i = 0; i < type.length; i++) {
- if(type[i]) {
- target[methodName]( type[i], listener, false);
- }
- }
- },
- isArray: function(obj) {
- return (obj instanceof Array);
- },
- createEl: function(classes, tag) {
- var el = document.createElement(tag || 'div');
- if(classes) {
- el.className = classes;
- }
- return el;
- },
- getScrollY: function() {
- var yOffset = window.pageYOffset;
- return yOffset !== undefined ? yOffset : document.documentElement.scrollTop;
- },
- unbind: function(target, type, listener) {
- framework.bind(target,type,listener,true);
- },
- removeClass: function(el, className) {
- var reg = new RegExp('(\\s|^)' + className + '(\\s|$)');
- el.className = el.className.replace(reg, ' ').replace(/^\s\s*/, '').replace(/\s\s*$/, '');
- },
- addClass: function(el, className) {
- if( !framework.hasClass(el,className) ) {
- el.className += (el.className ? ' ' : '') + className;
- }
- },
- hasClass: function(el, className) {
- return el.className && new RegExp('(^|\\s)' + className + '(\\s|$)').test(el.className);
- },
- getChildByClass: function(parentEl, childClassName) {
- var node = parentEl.firstChild;
- while(node) {
- if( framework.hasClass(node, childClassName) ) {
- return node;
- }
- node = node.nextSibling;
- }
- },
- arraySearch: function(array, value, key) {
- var i = array.length;
- while(i--) {
- if(array[i][key] === value) {
- return i;
- }
- }
- return -1;
- },
- extend: function(o1, o2, preventOverwrite) {
- for (var prop in o2) {
- if (o2.hasOwnProperty(prop)) {
- if(preventOverwrite && o1.hasOwnProperty(prop)) {
- continue;
- }
- o1[prop] = o2[prop];
- }
- }
- },
- easing: {
- sine: {
- out: function(k) {
- return Math.sin(k * (Math.PI / 2));
- },
- inOut: function(k) {
- return - (Math.cos(Math.PI * k) - 1) / 2;
- }
- },
- cubic: {
- out: function(k) {
- return --k * k * k + 1;
- }
- }
- /*
- elastic: {
- out: function ( k ) {
- var s, a = 0.1, p = 0.4;
- if ( k === 0 ) return 0;
- if ( k === 1 ) return 1;
- if ( !a || a < 1 ) { a = 1; s = p / 4; }
- else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI );
- return ( a * Math.pow( 2, - 10 * k) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) + 1 );
- },
- },
- back: {
- out: function ( k ) {
- var s = 1.70158;
- return --k * k * ( ( s + 1 ) * k + s ) + 1;
- }
- }
- */
- },
- /**
- *
- * @return {object}
- *
- * {
- * raf : request animation frame function
- * caf : cancel animation frame function
- * transfrom : transform property key (with vendor), or null if not supported
- * oldIE : IE8 or below
- * }
- *
- */
- detectFeatures: function() {
- if(framework.features) {
- return framework.features;
- }
- var helperEl = framework.createEl(),
- helperStyle = helperEl.style,
- vendor = '',
- features = {};
- // IE8 and below
- features.oldIE = document.all && !document.addEventListener;
- features.touch = 'ontouchstart' in window;
- if(window.requestAnimationFrame) {
- features.raf = window.requestAnimationFrame;
- features.caf = window.cancelAnimationFrame;
- }
- features.pointerEvent = navigator.pointerEnabled || navigator.msPointerEnabled;
- // fix false-positive detection of old Android in new IE
- // (IE11 ua string contains "Android 4.0")
-
- if(!features.pointerEvent) {
- var ua = navigator.userAgent;
- // Detect if device is iPhone or iPod and if it's older than iOS 8
- // http://stackoverflow.com/a/14223920
- //
- // This detection is made because of buggy top/bottom toolbars
- // that don't trigger window.resize event.
- // For more info refer to _isFixedPosition variable in core.js
- if (/iP(hone|od)/.test(navigator.platform)) {
- var v = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/);
- if(v && v.length > 0) {
- v = parseInt(v[1], 10);
- if(v >= 1 && v < 8 ) {
- features.isOldIOSPhone = true;
- }
- }
- }
- // Detect old Android (before KitKat)
- // due to bugs related to position:fixed
- // http://stackoverflow.com/questions/7184573/pick-up-the-android-version-in-the-browser-by-javascript
-
- var match = ua.match(/Android\s([0-9\.]*)/);
- var androidversion = match ? match[1] : 0;
- androidversion = parseFloat(androidversion);
- if(androidversion >= 1 ) {
- if(androidversion < 4.4) {
- features.isOldAndroid = true; // for fixed position bug & performance
- }
- features.androidVersion = androidversion; // for touchend bug
- }
- features.isMobileOpera = /opera mini|opera mobi/i.test(ua);
- // p.s. yes, yes, UA sniffing is bad, propose your solution for above bugs.
- }
-
- var styleChecks = ['transform', 'perspective', 'animationName'],
- vendors = ['', 'webkit','Moz','ms','O'],
- styleCheckItem,
- styleName;
- for(var i = 0; i < 4; i++) {
- vendor = vendors[i];
- for(var a = 0; a < 3; a++) {
- styleCheckItem = styleChecks[a];
- // uppercase first letter of property name, if vendor is present
- styleName = vendor + (vendor ?
- styleCheckItem.charAt(0).toUpperCase() + styleCheckItem.slice(1) :
- styleCheckItem);
-
- if(!features[styleCheckItem] && styleName in helperStyle ) {
- features[styleCheckItem] = styleName;
- }
- }
- if(vendor && !features.raf) {
- vendor = vendor.toLowerCase();
- features.raf = window[vendor+'RequestAnimationFrame'];
- if(features.raf) {
- features.caf = window[vendor+'CancelAnimationFrame'] ||
- window[vendor+'CancelRequestAnimationFrame'];
- }
- }
- }
-
- if(!features.raf) {
- var lastTime = 0;
- features.raf = function(fn) {
- var currTime = new Date().getTime();
- var timeToCall = Math.max(0, 16 - (currTime - lastTime));
- var id = window.setTimeout(function() { fn(currTime + timeToCall); }, timeToCall);
- lastTime = currTime + timeToCall;
- return id;
- };
- features.caf = function(id) { clearTimeout(id); };
- }
- // Detect SVG support
- features.svg = !!document.createElementNS &&
- !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect;
- framework.features = features;
- return features;
- }
- };
- framework.detectFeatures();
- // Override addEventListener for old versions of IE
- if(framework.features.oldIE) {
- framework.bind = function(target, type, listener, unbind) {
-
- type = type.split(' ');
- var methodName = (unbind ? 'detach' : 'attach') + 'Event',
- evName,
- _handleEv = function() {
- listener.handleEvent.call(listener);
- };
- for(var i = 0; i < type.length; i++) {
- evName = type[i];
- if(evName) {
- if(typeof listener === 'object' && listener.handleEvent) {
- if(!unbind) {
- listener['oldIE' + evName] = _handleEv;
- } else {
- if(!listener['oldIE' + evName]) {
- return false;
- }
- }
- target[methodName]( 'on' + evName, listener['oldIE' + evName]);
- } else {
- target[methodName]( 'on' + evName, listener);
- }
- }
- }
- };
-
- }
- /*>>framework-bridge*/
- /*>>core*/
- //function(template, UiClass, items, options)
- var self = this;
- /**
- * Static vars, don't change unless you know what you're doing.
- */
- var DOUBLE_TAP_RADIUS = 25,
- NUM_HOLDERS = 3;
- /**
- * Options
- */
- var _options = {
- allowPanToNext:true,
- spacing: 0.12,
- bgOpacity: 1,
- mouseUsed: false,
- loop: true,
- pinchToClose: true,
- closeOnScroll: true,
- closeOnVerticalDrag: true,
- hideAnimationDuration: 333,
- showAnimationDuration: 333,
- showHideOpacity: false,
- focus: true,
- escKey: true,
- arrowKeys: true,
- mainScrollEndFriction: 0.35,
- panEndFriction: 0.35,
- isClickableElement: function(el) {
- return el.tagName === 'A';
- },
- getDoubleTapZoom: function(isMouseClick, item) {
- if(isMouseClick) {
- return 1;
- } else {
- return item.initialZoomLevel < 0.7 ? 1 : 1.5;
- }
- },
- maxSpreadZoom: 2,
- // not fully implemented yet
- scaleMode: 'fit', // TODO
- modal: true, // TODO
- alwaysFadeIn: false // TODO
- };
- framework.extend(_options, options);
- /**
- * Private helper variables & functions
- */
- var _getEmptyPoint = function() {
- return {x:0,y:0};
- };
- var _isOpen,
- _isDestroying,
- _closedByScroll,
- _currentItemIndex,
- _containerStyle,
- _containerShiftIndex,
- _currPanDist = _getEmptyPoint(),
- _startPanOffset = _getEmptyPoint(),
- _panOffset = _getEmptyPoint(),
- _upMoveEvents, // drag move, drag end & drag cancel events array
- _downEvents, // drag start events array
- _globalEventHandlers,
- _viewportSize = {},
- _currZoomLevel,
- _startZoomLevel,
- _translatePrefix,
- _translateSufix,
- _updateSizeInterval,
- _itemsNeedUpdate,
- _currPositionIndex = 0,
- _offset,
- _slideSize = _getEmptyPoint(), // size of slide area, including spacing
- _itemHolders,
- _prevItemIndex,
- _indexDiff = 0, // difference of indexes since last content update
- _dragStartEvent,
- _dragMoveEvent,
- _dragEndEvent,
- _dragCancelEvent,
- _transformKey,
- _pointerEventEnabled,
- _isFixedPosition = true,
- _likelyTouchDevice,
- _modules = [],
- _requestAF,
- _cancelAF,
- _initalClassName,
- _initalWindowScrollY,
- _oldIE,
- _currentWindowScrollY,
- _features,
- _windowVisibleSize = {},
- // Registers PhotoSWipe module (History, Controller ...)
- _registerModule = function(name, module) {
- framework.extend(self, module.publicMethods);
- _modules.push(name);
- },
- _getLoopedId = function(index) {
- var numSlides = _getNumItems();
- if(index > numSlides - 1) {
- return index - numSlides;
- } else if(index < 0) {
- return numSlides + index;
- }
- return index;
- },
-
- // Micro bind/trigger
- _listeners = {},
- _listen = function(name, fn) {
- if(!_listeners[name]) {
- _listeners[name] = [];
- }
- return _listeners[name].push(fn);
- },
- _shout = function(name) {
- var listeners = _listeners[name];
- if(listeners) {
- var args = Array.prototype.slice.call(arguments);
- args.shift();
- for(var i = 0; i < listeners.length; i++) {
- listeners[i].apply(self, args);
- }
- }
- },
- _getCurrentTime = function() {
- return new Date().getTime();
- },
- _applyBgOpacity = function(opacity) {
- _bgOpacity = opacity;
- self.bg.style.opacity = opacity * _options.bgOpacity;
- },
-
- _applyZoomTransform = function(styleObj,x,y,zoom) {
- styleObj[_transformKey] = _translatePrefix + x + 'px, ' + y + 'px' + _translateSufix + ' scale(' + zoom + ')';
- },
- _applyCurrentZoomPan = function() {
- if(_currZoomElementStyle) {
- _applyZoomTransform(_currZoomElementStyle, _panOffset.x, _panOffset.y, _currZoomLevel);
- }
- },
- _applyZoomPanToItem = function(item) {
- if(item.container) {
- _applyZoomTransform(item.container.style,
- item.initialPosition.x,
- item.initialPosition.y,
- item.initialZoomLevel);
- }
- },
- _setTranslateX = function(x, elStyle) {
- elStyle[_transformKey] = _translatePrefix + x + 'px, 0px' + _translateSufix;
- },
- _moveMainScroll = function(x, dragging) {
- if(!_options.loop && dragging) {
- // if of current item during scroll (float)
- var newSlideIndexOffset = _currentItemIndex + (_slideSize.x * _currPositionIndex - x)/_slideSize.x;
- var delta = Math.round(x - _mainScrollPos.x);
- if( (newSlideIndexOffset < 0 && delta > 0) ||
- (newSlideIndexOffset >= _getNumItems()-1 && delta < 0) ) {
- x = _mainScrollPos.x + delta * _options.mainScrollEndFriction;
- }
- }
-
- _mainScrollPos.x = x;
- _setTranslateX(x, _containerStyle);
- },
- _calculatePanOffset = function(axis, zoomLevel) {
- var m = _midZoomPoint[axis] - _offset[axis];
- return _startPanOffset[axis] + _currPanDist[axis] + m - m * ( zoomLevel / _startZoomLevel );
- },
-
- _equalizePoints = function(p1, p2) {
- p1.x = p2.x;
- p1.y = p2.y;
- if(p2.id) {
- p1.id = p2.id;
- }
- },
- _roundPoint = function(p) {
- p.x = Math.round(p.x);
- p.y = Math.round(p.y);
- },
- _mouseMoveTimeout = null,
- _onFirstMouseMove = function() {
- // Wait until mouse move event is fired at least twice during 100ms
- // We do this, because some mobile browsers trigger it on touchstart
- if(_mouseMoveTimeout ) {
- framework.unbind(document, 'mousemove', _onFirstMouseMove);
- framework.addClass(template, 'pswp--has_mouse');
- _options.mouseUsed = true;
- _shout('mouseUsed');
- }
- _mouseMoveTimeout = setTimeout(function() {
- _mouseMoveTimeout = null;
- }, 100);
- },
- _bindEvents = function() {
- framework.bind(document, 'keydown', self);
- if(_features.transform) {
- // don't bind click event in browsers that don't support transform (mostly IE8)
- framework.bind(self.scrollWrap, 'click', self);
- }
-
- if(!_options.mouseUsed) {
- framework.bind(document, 'mousemove', _onFirstMouseMove);
- }
- framework.bind(window, 'resize scroll', self);
- _shout('bindEvents');
- },
- _unbindEvents = function() {
- framework.unbind(window, 'resize', self);
- framework.unbind(window, 'scroll', _globalEventHandlers.scroll);
- framework.unbind(document, 'keydown', self);
- framework.unbind(document, 'mousemove', _onFirstMouseMove);
- if(_features.transform) {
- framework.unbind(self.scrollWrap, 'click', self);
- }
- if(_isDragging) {
- framework.unbind(window, _upMoveEvents, self);
- }
- _shout('unbindEvents');
- },
-
- _calculatePanBounds = function(zoomLevel, update) {
- var bounds = _calculateItemSize( self.currItem, _viewportSize, zoomLevel );
- if(update) {
- _currPanBounds = bounds;
- }
- return bounds;
- },
-
- _getMinZoomLevel = function(item) {
- if(!item) {
- item = self.currItem;
- }
- return item.initialZoomLevel;
- },
- _getMaxZoomLevel = function(item) {
- if(!item) {
- item = self.currItem;
- }
- return item.w > 0 ? _options.maxSpreadZoom : 1;
- },
- // Return true if offset is out of the bounds
- _modifyDestPanOffset = function(axis, destPanBounds, destPanOffset, destZoomLevel) {
- if(destZoomLevel === self.currItem.initialZoomLevel) {
- destPanOffset[axis] = self.currItem.initialPosition[axis];
- return true;
- } else {
- destPanOffset[axis] = _calculatePanOffset(axis, destZoomLevel);
- if(destPanOffset[axis] > destPanBounds.min[axis]) {
- destPanOffset[axis] = destPanBounds.min[axis];
- return true;
- } else if(destPanOffset[axis] < destPanBounds.max[axis] ) {
- destPanOffset[axis] = destPanBounds.max[axis];
- return true;
- }
- }
- return false;
- },
- _setupTransforms = function() {
- if(_transformKey) {
- // setup 3d transforms
- var allow3dTransform = _features.perspective && !_likelyTouchDevice;
- _translatePrefix = 'translate' + (allow3dTransform ? '3d(' : '(');
- _translateSufix = _features.perspective ? ', 0px)' : ')';
- return;
- }
- // Override zoom/pan/move functions in case old browser is used (most likely IE)
- // (so they use left/top/width/height, instead of CSS transform)
-
- _transformKey = 'left';
- framework.addClass(template, 'pswp--ie');
- _setTranslateX = function(x, elStyle) {
- elStyle.left = x + 'px';
- };
- _applyZoomPanToItem = function(item) {
- var zoomRatio = item.fitRatio > 1 ? 1 : item.fitRatio,
- s = item.container.style,
- w = zoomRatio * item.w,
- h = zoomRatio * item.h;
- s.width = w + 'px';
- s.height = h + 'px';
- s.left = item.initialPosition.x + 'px';
- s.top = item.initialPosition.y + 'px';
- };
- _applyCurrentZoomPan = function() {
- if(_currZoomElementStyle) {
- var s = _currZoomElementStyle,
- item = self.currItem,
- zoomRatio = item.fitRatio > 1 ? 1 : item.fitRatio,
- w = zoomRatio * item.w,
- h = zoomRatio * item.h;
- s.width = w + 'px';
- s.height = h + 'px';
- s.left = _panOffset.x + 'px';
- s.top = _panOffset.y + 'px';
- }
-
- };
- },
- _onKeyDown = function(e) {
- var keydownAction = '';
- if(_options.escKey && e.keyCode === 27) {
- keydownAction = 'close';
- } else if(_options.arrowKeys) {
- if(e.keyCode === 37) {
- keydownAction = 'prev';
- } else if(e.keyCode === 39) {
- keydownAction = 'next';
- }
- }
- if(keydownAction) {
- // don't do anything if special key pressed to prevent from overriding default browser actions
- // e.g. in Chrome on Mac cmd+arrow-left returns to previous page
- if( !e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey ) {
- if(e.preventDefault) {
- e.preventDefault();
- } else {
- e.returnValue = false;
- }
- self[keydownAction]();
- }
- }
- },
- _onGlobalClick = function(e) {
- if(!e) {
- return;
- }
- // don't allow click event to pass through when triggering after drag or some other gesture
- if(_moved || _zoomStarted || _mainScrollAnimating || _verticalDragInitiated) {
- e.preventDefault();
- e.stopPropagation();
- }
- },
- _onPageScroll = function() {
- self.setScrollOffset(0, framework.getScrollY());
- };
-
-
- // Micro animation engine
- var _animations = {},
- _numAnimations = 0,
- _stopAnimation = function(name) {
- if(_animations[name]) {
- if(_animations[name].raf) {
- _cancelAF( _animations[name].raf );
- }
- _numAnimations--;
- delete _animations[name];
- }
- },
- _registerStartAnimation = function(name) {
- if(_animations[name]) {
- _stopAnimation(name);
- }
- if(!_animations[name]) {
- _numAnimations++;
- _animations[name] = {};
- }
- },
- _stopAllAnimations = function() {
- for (var prop in _animations) {
- if( _animations.hasOwnProperty( prop ) ) {
- _stopAnimation(prop);
- }
-
- }
- },
- _animateProp = function(name, b, endProp, d, easingFn, onUpdate, onComplete) {
- var startAnimTime = _getCurrentTime(), t;
- _registerStartAnimation(name);
- var animloop = function(){
- if ( _animations[name] ) {
-
- t = _getCurrentTime() - startAnimTime; // time diff
- //b - beginning (start prop)
- //d - anim duration
- if ( t >= d ) {
- _stopAnimation(name);
- onUpdate(endProp);
- if(onComplete) {
- onComplete();
- }
- return;
- }
- onUpdate( (endProp - b) * easingFn(t/d) + b );
- _animations[name].raf = _requestAF(animloop);
- }
- };
- animloop();
- };
-
- var publicMethods = {
- // make a few local variables and functions public
- shout: _shout,
- listen: _listen,
- viewportSize: _viewportSize,
- options: _options,
- isMainScrollAnimating: function() {
- return _mainScrollAnimating;
- },
- getZoomLevel: function() {
- return _currZoomLevel;
- },
- getCurrentIndex: function() {
- return _currentItemIndex;
- },
- isDragging: function() {
- return _isDragging;
- },
- isZooming: function() {
- return _isZooming;
- },
- setScrollOffset: function(x,y) {
- _offset.x = x;
- _currentWindowScrollY = _offset.y = y;
- },
- applyZoomPan: function(zoomLevel,panX,panY) {
- _panOffset.x = panX;
- _panOffset.y = panY;
- _currZoomLevel = zoomLevel;
- _applyCurrentZoomPan();
- },
- init: function() {
- if(_isOpen || _isDestroying) {
- return;
- }
- var i;
- self.framework = framework; // basic function
- self.template = template; // root DOM element of PhotoSwipe
- self.bg = framework.getChildByClass(template, 'pswp__bg');
- _initalClassName = template.className;
- _isOpen = true;
-
- _features = framework.detectFeatures();
- _requestAF = _features.raf;
- _cancelAF = _features.caf;
- _transformKey = _features.transform;
- _oldIE = _features.oldIE;
-
- self.scrollWrap = framework.getChildByClass(template, 'pswp__scroll-wrap');
- self.container = framework.getChildByClass(self.scrollWrap, 'pswp__container');
- _containerStyle = self.container.style; // for fast access
- // Objects that hold slides (there are only 3 in DOM)
- self.itemHolders = _itemHolders = [
- {el:self.container.children[0] , wrap:0, index: -1},
- {el:self.container.children[1] , wrap:0, index: -1},
- {el:self.container.children[2] , wrap:0, index: -1}
- ];
- // hide nearby item holders until initial zoom animation finishes (to avoid extra Paints)
- _itemHolders[0].el.style.display = _itemHolders[2].el.style.display = 'none';
- _setupTransforms();
- // Setup global events
- _globalEventHandlers = {
- resize: self.updateSize,
- scroll: _onPageScroll,
- keydown: _onKeyDown,
- click: _onGlobalClick
- };
- // disable show/hide effects on old browsers that don't support CSS animations or transforms,
- // old IOS, Android and Opera mobile. Blackberry seems to work fine, even older models.
- var oldPhone = _features.isOldIOSPhone || _features.isOldAndroid || _features.isMobileOpera;
- if(!_features.animationName || !_features.transform || oldPhone) {
- _options.showAnimationDuration = _options.hideAnimationDuration = 0;
- }
- // init modules
- for(i = 0; i < _modules.length; i++) {
- self['init' + _modules[i]]();
- }
-
- // init
- if(UiClass) {
- var ui = self.ui = new UiClass(self, framework);
- ui.init();
- }
- _shout('firstUpdate');
- _currentItemIndex = _currentItemIndex || _options.index || 0;
- // validate index
- if( isNaN(_currentItemIndex) || _currentItemIndex < 0 || _currentItemIndex >= _getNumItems() ) {
- _currentItemIndex = 0;
- }
- self.currItem = _getItemAt( _currentItemIndex );
-
- if(_features.isOldIOSPhone || _features.isOldAndroid) {
- _isFixedPosition = false;
- }
-
- if(_options.modal) {
- template.setAttribute('aria-hidden', 'false');
- if(!_isFixedPosition) {
- template.style.position = 'absolute';
- template.style.top = framework.getScrollY() + 'px';
- } else {
- template.style.position = 'fixed';
- }
- }
- if(_currentWindowScrollY === undefined) {
- _shout('initialLayout');
- _currentWindowScrollY = _initalWindowScrollY = framework.getScrollY();
- }
-
- // add classes to root element of PhotoSwipe
- var rootClasses = 'pswp--open ';
- if(_options.mainClass) {
- rootClasses += _options.mainClass + ' ';
- }
- if(_options.showHideOpacity) {
- rootClasses += 'pswp--animate_opacity ';
- }
- rootClasses += _likelyTouchDevice ? 'pswp--touch' : 'pswp--notouch';
- rootClasses += _features.animationName ? ' pswp--css_animation' : '';
- rootClasses += _features.svg ? ' pswp--svg' : '';
- framework.addClass(template, rootClasses);
- self.updateSize();
- // initial update
- _containerShiftIndex = -1;
- _indexDiff = null;
- for(i = 0; i < NUM_HOLDERS; i++) {
- _setTranslateX( (i+_containerShiftIndex) * _slideSize.x, _itemHolders[i].el.style);
- }
- if(!_oldIE) {
- framework.bind(self.scrollWrap, _downEvents, self); // no dragging for old IE
- }
- _listen('initialZoomInEnd', function() {
- self.setContent(_itemHolders[0], _currentItemIndex-1);
- self.setContent(_itemHolders[2], _currentItemIndex+1);
- _itemHolders[0].el.style.display = _itemHolders[2].el.style.display = 'block';
- if(_options.focus) {
- // focus causes layout,
- // which causes lag during the animation,
- // that's why we delay it untill the initial zoom transition ends
- template.focus();
- }
-
- _bindEvents();
- });
- // set content for center slide (first time)
- self.setContent(_itemHolders[1], _currentItemIndex);
-
- self.updateCurrItem();
- _shout('afterInit');
- if(!_isFixedPosition) {
- // On all versions of iOS lower than 8.0, we check size of viewport every second.
- //
- // This is done to detect when Safari top & bottom bars appear,
- // as this action doesn't trigger any events (like resize).
- //
- // On iOS8 they fixed this.
- //
- // 10 Nov 2014: iOS 7 usage ~40%. iOS 8 usage 56%.
-
- _updateSizeInterval = setInterval(function() {
- if(!_numAnimations && !_isDragging && !_isZooming && (_currZoomLevel === self.currItem.initialZoomLevel) ) {
- self.updateSize();
- }
- }, 1000);
- }
- framework.addClass(template, 'pswp--visible');
- },
- // Closes the gallery, then destroy it
- close: function() {
- if(!_isOpen) {
- return;
- }
- _isOpen = false;
- _isDestroying = true;
- _shout('close');
- _unbindEvents();
- _showOrHide( self.currItem, null, true, self.destroy);
- },
- // destroys gallery (unbinds events, cleans up intervals and timeouts to avoid memory leaks)
- destroy: function() {
- _shout('destroy');
- if(_showOrHideTimeout) {
- clearTimeout(_showOrHideTimeout);
- }
-
- if(_options.modal) {
- template.setAttribute('aria-hidden', 'true');
- template.className = _initalClassName;
- }
- if(_updateSizeInterval) {
- clearInterval(_updateSizeInterval);
- }
- framework.unbind(self.scrollWrap, _downEvents, self);
- // we unbind lost event at the end, as closing animation may depend on it
- framework.unbind(window, 'scroll', self);
- _stopDragUpdateLoop();
- _stopAllAnimations();
- _listeners = null;
- },
- /**
- * Pan image to position
- * @param {Number} x
- * @param {Number} y
- * @param {Boolean} force Will ignore bounds if set to true.
- */
- panTo: function(x,y,force) {
- if(!force) {
- if(x > _currPanBounds.min.x) {
- x = _currPanBounds.min.x;
- } else if(x < _currPanBounds.max.x) {
- x = _currPanBounds.max.x;
- }
- if(y > _currPanBounds.min.y) {
- y = _currPanBounds.min.y;
- } else if(y < _currPanBounds.max.y) {
- y = _currPanBounds.max.y;
- }
- }
-
- _panOffset.x = x;
- _panOffset.y = y;
- _applyCurrentZoomPan();
- },
-
- handleEvent: function (e) {
- e = e || window.event;
- if(_globalEventHandlers[e.type]) {
- _globalEventHandlers[e.type](e);
- }
- },
- goTo: function(index) {
- index = _getLoopedId(index);
- var diff = index - _currentItemIndex;
- _indexDiff = diff;
- _currentItemIndex = index;
- self.currItem = _getItemAt( _currentItemIndex );
- _currPositionIndex -= diff;
-
- _moveMainScroll(_slideSize.x * _currPositionIndex);
-
- _stopAllAnimations();
- _mainScrollAnimating = false;
- self.updateCurrItem();
- },
- next: function() {
- self.goTo( _currentItemIndex + 1);
- },
- prev: function() {
- self.goTo( _currentItemIndex - 1);
- },
- // update current zoom/pan objects
- updateCurrZoomItem: function(emulateSetContent) {
- if(emulateSetContent) {
- _shout('beforeChange', 0);
- }
- // itemHolder[1] is middle (current) item
- if(_itemHolders[1].el.children.length) {
- var zoomElement = _itemHolders[1].el.children[0];
- if( framework.hasClass(zoomElement, 'pswp__zoom-wrap') ) {
- _currZoomElementStyle = zoomElement.style;
- } else {
- _currZoomElementStyle = null;
- }
- } else {
- _currZoomElementStyle = null;
- }
-
- _currPanBounds = self.currItem.bounds;
- _startZoomLevel = _currZoomLevel = self.currItem.initialZoomLevel;
- _panOffset.x = _currPanBounds.center.x;
- _panOffset.y = _currPanBounds.center.y;
- if(emulateSetContent) {
- _shout('afterChange');
- }
- },
- invalidateCurrItems: function() {
- _itemsNeedUpdate = true;
- for(var i = 0; i < NUM_HOLDERS; i++) {
- if( _itemHolders[i].item ) {
- _itemHolders[i].item.needsUpdate = true;
- }
- }
- },
- updateCurrItem: function(beforeAnimation) {
- if(_indexDiff === 0) {
- return;
- }
- var diffAbs = Math.abs(_indexDiff),
- tempHolder;
- if(beforeAnimation && diffAbs < 2) {
- return;
- }
- self.currItem = _getItemAt( _currentItemIndex );
-
- _shout('beforeChange', _indexDiff);
- if(diffAbs >= NUM_HOLDERS) {
- _containerShiftIndex += _indexDiff + (_indexDiff > 0 ? -NUM_HOLDERS : NUM_HOLDERS);
- diffAbs = NUM_HOLDERS;
- }
- for(var i = 0; i < diffAbs; i++) {
- if(_indexDiff > 0) {
- tempHolder = _itemHolders.shift();
- _itemHolders[NUM_HOLDERS-1] = tempHolder; // move first to last
- _containerShiftIndex++;
- _setTranslateX( (_containerShiftIndex+2) * _slideSize.x, tempHolder.el.style);
- self.setContent(tempHolder, _currentItemIndex - diffAbs + i + 1 + 1);
- } else {
- tempHolder = _itemHolders.pop();
- _itemHolders.unshift( tempHolder ); // move last to first
- _containerShiftIndex--;
- _setTranslateX( _containerShiftIndex * _slideSize.x, tempHolder.el.style);
- self.setContent(tempHolder, _currentItemIndex + diffAbs - i - 1 - 1);
- }
-
- }
- // reset zoom/pan on previous item
- if(_currZoomElementStyle && Math.abs(_indexDiff) === 1) {
- var prevItem = _getItemAt(_prevItemIndex);
- if(prevItem.initialZoomLevel !== _currZoomLevel) {
- _calculateItemSize(prevItem , _viewportSize );
- _applyZoomPanToItem( prevItem );
- }
- }
- // reset diff after update
- _indexDiff = 0;
- self.updateCurrZoomItem();
- _prevItemIndex = _currentItemIndex;
- _shout('afterChange');
-
- },
- updateSize: function(force) {
-
- if(!_isFixedPosition) {
- var windowScrollY = framework.getScrollY();
- if(_currentWindowScrollY !== windowScrollY) {
- template.style.top = windowScrollY + 'px';
- _currentWindowScrollY = windowScrollY;
- }
- if(!force && _windowVisibleSize.x === window.innerWidth && _windowVisibleSize.y === window.innerHeight) {
- return;
- }
- _windowVisibleSize.x = window.innerWidth;
- _windowVisibleSize.y = window.innerHeight;
- //template.style.width = _windowVisibleSize.x + 'px';
- template.style.height = _windowVisibleSize.y + 'px';
- }
- _viewportSize.x = self.scrollWrap.clientWidth;
- _viewportSize.y = self.scrollWrap.clientHeight;
-
- _offset = {x:0,y:_currentWindowScrollY};//framework.getOffset(template);
- _slideSize.x = _viewportSize.x + Math.round(_viewportSize.x * _options.spacing);
- _slideSize.y = _viewportSize.y;
- _moveMainScroll(_slideSize.x * _currPositionIndex);
- _shout('beforeResize'); // even may be used for example to switch image sources
- // don't re-calculate size on inital size update
- if(_containerShiftIndex !== undefined) {
- var holder,
- item,
- hIndex;
- for(var i = 0; i < NUM_HOLDERS; i++) {
- holder = _itemHolders[i];
- _setTranslateX( (i+_containerShiftIndex) * _slideSize.x, holder.el.style);
- hIndex = _currentItemIndex+i-1;
- if(_options.loop && _getNumItems() > 2) {
- hIndex = _getLoopedId(hIndex);
- }
- // update zoom level on items and refresh source (if needsUpdate)
- item = _getItemAt( hIndex );
- // re-render gallery item if `needsUpdate`,
- // or doesn't have `bounds` (entirely new slide object)
- if( item && (_itemsNeedUpdate || item.needsUpdate || !item.bounds) ) {
- self.cleanSlide( item );
-
- self.setContent( holder, hIndex );
- // if "center" slide
- if(i === 1) {
- self.currItem = item;
- self.updateCurrZoomItem(true);
- }
- item.needsUpdate = false;
- } else if(holder.index === -1 && hIndex >= 0) {
- // add content first time
- self.setContent( holder, hIndex );
- }
- if(item && item.container) {
- _calculateItemSize(item, _viewportSize);
- _applyZoomPanToItem( item );
- }
-
- }
- _itemsNeedUpdate = false;
- }
- _startZoomLevel = _currZoomLevel = self.currItem.initialZoomLevel;
- _currPanBounds = self.currItem.bounds;
- if(_currPanBounds) {
- _panOffset.x = _currPanBounds.center.x;
- _panOffset.y = _currPanBounds.center.y;
- _applyCurrentZoomPan();
- }
-
- _shout('resize');
- },
-
- // Zoom current item to
- zoomTo: function(destZoomLevel, centerPoint, speed, easingFn, updateFn) {
- /*
- if(destZoomLevel === 'fit') {
- destZoomLevel = self.currItem.fitRatio;
- } else if(destZoomLevel === 'fill') {
- destZoomLevel = self.currItem.fillRatio;
- }
- */
- if(centerPoint) {
- _startZoomLevel = _currZoomLevel;
- _midZoomPoint.x = Math.abs(centerPoint.x) - _panOffset.x ;
- _midZoomPoint.y = Math.abs(centerPoint.y) - _panOffset.y ;
- _equalizePoints(_startPanOffset, _panOffset);
- }
- var destPanBounds = _calculatePanBounds(destZoomLevel, false),
- destPanOffset = {};
- _modifyDestPanOffset('x', destPanBounds, destPanOffset, destZoomLevel);
- _modifyDestPanOffset('y', destPanBounds, destPanOffset, destZoomLevel);
- var initialZoomLevel = _currZoomLevel;
- var initialPanOffset = {
- x: _panOffset.x,
- y: _panOffset.y
- };
- _roundPoint(destPanOffset);
- // _startZoomLevel = destZoomLevel;
- var onUpdate = function(now) {
- if(now === 1) {
- _currZoomLevel = destZoomLevel;
- _panOffset.x = destPanOffset.x;
- _panOffset.y = destPanOffset.y;
- } else {
- _currZoomLevel = (destZoomLevel - initialZoomLevel) * now + initialZoomLevel;
- _panOffset.x = (destPanOffset.x - initialPanOffset.x) * now + initialPanOffset.x;
- _panOffset.y = (destPanOffset.y - initialPanOffset.y) * now + initialPanOffset.y;
- }
- if(updateFn) {
- updateFn(now);
- }
- _applyCurrentZoomPan();
- };
- if(speed) {
- _animateProp('customZoomTo', 0, 1, speed, easingFn || framework.easing.sine.inOut, onUpdate);
- } else {
- onUpdate(1);
- }
- }
- };
- /*>>core*/
- /*>>gestures*/
- /**
- * Mouse/touch/pointer event handlers.
- *
- * separated from @core.js for readability
- */
- var MIN_SWIPE_DISTANCE = 30,
- DIRECTION_CHECK_OFFSET = 10; // amount of pixels to drag to determine direction of swipe
- var _gestureStartTime,
- _gestureCheckSpeedTime,
- // pool of objects that are used during dragging of zooming
- p = {}, // first point
- p2 = {}, // second point (for zoom gesture)
- delta = {},
- _currPoint = {},
- _startPoint = {},
- _currPointers = [],
- _startMainScrollPos = {},
- _releaseAnimData,
- _posPoints = [], // array of points during dragging, used to determine type of gesture
- _tempPoint = {},
- _isZoomingIn,
- _verticalDragInitiated,
- _oldAndroidTouchEndTimeout,
- _currZoomedItemIndex = 0,
- _centerPoint = _getEmptyPoint(),
- _lastReleaseTime = 0,
- _isDragging, // at least one pointer is down
- _isMultitouch, // at least two _pointers are down
- _zoomStarted, // zoom level changed during zoom gesture
- _moved,
- _dragAnimFrame,
- _mainScrollShifted,
- _currentPoints, // array of current touch points
- _isZooming,
- _currPointsDistance,
- _startPointsDistance,
- _currPanBounds,
- _mainScrollPos = _getEmptyPoint(),
- _currZoomElementStyle,
- _mainScrollAnimating, // true, if animation after swipe gesture is running
- _midZoomPoint = _getEmptyPoint(),
- _currCenterPoint = _getEmptyPoint(),
- _direction,
- _isFirstMove,
- _opacityChanged,
- _bgOpacity,
- _wasOverInitialZoom,
- _isEqualPoints = function(p1, p2) {
- return p1.x === p2.x && p1.y === p2.y;
- },
- _isNearbyPoints = function(touch0, touch1) {
- return Math.abs(touch0.x - touch1.x) < DOUBLE_TAP_RADIUS && Math.abs(touch0.y - touch1.y) < DOUBLE_TAP_RADIUS;
- },
- _calculatePointsDistance = function(p1, p2) {
- _tempPoint.x = Math.abs( p1.x - p2.x );
- _tempPoint.y = Math.abs( p1.y - p2.y );
- return Math.sqrt(_tempPoint.x * _tempPoint.x + _tempPoint.y * _tempPoint.y);
- },
- _stopDragUpdateLoop = function() {
- if(_dragAnimFrame) {
- _cancelAF(_dragAnimFrame);
- _dragAnimFrame = null;
- }
- },
- _dragUpdateLoop = function() {
- if(_isDragging) {
- _dragAnimFrame = _requestAF(_dragUpdateLoop);
- _renderMovement();
- }
- },
- _canPan = function() {
- return !(_options.scaleMode === 'fit' && _currZoomLevel === self.currItem.initialZoomLevel);
- },
-
- // find the closest parent DOM element
- _closestElement = function(el, fn) {
- if(!el) {
- return false;
- }
- // don't search elements above pswp__scroll-wrap
- if(el.className && el.className.indexOf('pswp__scroll-wrap') > -1 ) {
- return false;
- }
- if( fn(el) ) {
- return el;
- }
- return _closestElement(el.parentNode, fn);
- },
- _preventObj = {},
- _preventDefaultEventBehaviour = function(e, isDown) {
- _preventObj.prevent = !_closestElement(e.target, _options.isClickableElement);
- _shout('preventDragEvent', e, isDown, _preventObj);
- return _preventObj.prevent;
- },
- _convertTouchToPoint = function(touch, p) {
- p.x = touch.pageX;
- p.y = touch.pageY;
- p.id = touch.identifier;
- return p;
- },
- _findCenterOfPoints = function(p1, p2, pCenter) {
- pCenter.x = (p1.x + p2.x) * 0.5;
- pCenter.y = (p1.y + p2.y) * 0.5;
- },
- _pushPosPoint = function(time, x, y) {
- if(time - _gestureCheckSpeedTime > 50) {
- var o = _posPoints.length > 2 ? _posPoints.shift() : {};
- o.x = x;
- o.y = y;
- _posPoints.push(o);
- _gestureCheckSpeedTime = time;
- }
- },
- _calculateVerticalDragOpacityRatio = function() {
- var yOffset = _panOffset.y - self.currItem.initialPosition.y; // difference between initial and current position
- return 1 - Math.abs( yOffset / (_viewportSize.y / 2) );
- },
-
- // points pool, reused during touch events
- _ePoint1 = {},
- _ePoint2 = {},
- _tempPointsArr = [],
- _tempCounter,
- _getTouchPoints = function(e) {
- // clean up previous points, without recreating array
- while(_tempPointsArr.length > 0) {
- _tempPointsArr.pop();
- }
- if(!_pointerEventEnabled) {
- if(e.type.indexOf('touch') > -1) {
- if(e.touches && e.touches.length > 0) {
- _tempPointsArr[0] = _convertTouchToPoint(e.touches[0], _ePoint1);
- if(e.touches.length > 1) {
- _tempPointsArr[1] = _convertTouchToPoint(e.touches[1], _ePoint2);
- }
- }
-
- } else {
- _ePoint1.x = e.pageX;
- _ePoint1.y = e.pageY;
- _ePoint1.id = '';
- _tempPointsArr[0] = _ePoint1;//_ePoint1;
- }
- } else {
- _tempCounter = 0;
- // we can use forEach, as pointer events are supported only in modern browsers
- _currPointers.forEach(function(p) {
- if(_tempCounter === 0) {
- _tempPointsArr[0] = p;
- } else if(_tempCounter === 1) {
- _tempPointsArr[1] = p;
- }
- _tempCounter++;
- });
- }
- return _tempPointsArr;
- },
- _panOrMoveMainScroll = function(axis, delta) {
- var panFriction,
- overDiff = 0,
- newOffset = _panOffset[axis] + delta[axis],
- startOverDiff,
- dir = delta[axis] > 0,
- newMainScrollPosition = _mainScrollPos.x + delta.x,
- mainScrollDiff = _mainScrollPos.x - _startMainScrollPos.x,
- newPanPos,
- newMainScrollPos;
- // calculate fdistance over the bounds and friction
- if(newOffset > _currPanBounds.min[axis] || newOffset < _currPanBounds.max[axis]) {
- panFriction = _options.panEndFriction;
- // Linear increasing of friction, so at 1/4 of viewport it's at max value.
- // Looks not as nice as was expected. Left for history.
- // panFriction = (1 - (_panOffset[axis] + delta[axis] + panBounds.min[axis]) / (_viewportSize[axis] / 4) );
- } else {
- panFriction = 1;
- }
-
- newOffset = _panOffset[axis] + delta[axis] * panFriction;
- // move main scroll or start panning
- if(_options.allowPanToNext || _currZoomLevel === self.currItem.initialZoomLevel) {
- if(!_currZoomElementStyle) {
-
- newMainScrollPos = newMainScrollPosition;
- } else if(_direction === 'h' && axis === 'x' && !_zoomStarted ) {
-
- if(dir) {
- if(newOffset > _currPanBounds.min[axis]) {
- panFriction = _options.panEndFriction;
- overDiff = _currPanBounds.min[axis] - newOffset;
- startOverDiff = _currPanBounds.min[axis] - _startPanOffset[axis];
- }
-
- // drag right
- if( (startOverDiff <= 0 || mainScrollDiff < 0) && _getNumItems() > 1 ) {
- newMainScrollPos = newMainScrollPosition;
- if(mainScrollDiff < 0 && newMainScrollPosition > _startMainScrollPos.x) {
- newMainScrollPos = _startMainScrollPos.x;
- }
- } else {
- if(_currPanBounds.min.x !== _currPanBounds.max.x) {
- newPanPos = newOffset;
- }
-
- }
- } else {
- if(newOffset < _currPanBounds.max[axis] ) {
- panFriction =_options.panEndFriction;
- overDiff = newOffset - _currPanBounds.max[axis];
- startOverDiff = _startPanOffset[axis] - _currPanBounds.max[axis];
- }
- if( (startOverDiff <= 0 || mainScrollDiff > 0) && _getNumItems() > 1 ) {
- newMainScrollPos = newMainScrollPosition;
- if(mainScrollDiff > 0 && newMainScrollPosition < _startMainScrollPos.x) {
- newMainScrollPos = _startMainScrollPos.x;
- }
- } else {
- if(_currPanBounds.min.x !== _currPanBounds.max.x) {
- newPanPos = newOffset;
- }
- }
- }
- //
- }
- if(axis === 'x') {
- if(newMainScrollPos !== undefined) {
- _moveMainScroll(newMainScrollPos, true);
- if(newMainScrollPos === _startMainScrollPos.x) {
- _mainScrollShifted = false;
- } else {
- _mainScrollShifted = true;
- }
- }
- if(_currPanBounds.min.x !== _currPanBounds.max.x) {
- if(newPanPos !== undefined) {
- _panOffset.x = newPanPos;
- } else if(!_mainScrollShifted) {
- _panOffset.x += delta.x * panFriction;
- }
- }
- return newMainScrollPos !== undefined;
- }
- }
- if(!_mainScrollAnimating) {
-
- if(!_mainScrollShifted) {
- if(_currZoomLevel > self.currItem.fitRatio) {
- _panOffset[axis] += delta[axis] * panFriction;
-
- }
- }
-
- }
-
- },
- // Pointerdown/touchstart/mousedown handler
- _onDragStart = function(e) {
- // Allow dragging only via left mouse button.
- // As this handler is not added in IE8 - we ignore e.which
- //
- // http://www.quirksmode.org/js/events_properties.html
- // https://developer.mozilla.org/en-US/docs/Web/API/event.button
- if(e.type === 'mousedown' && e.button > 0 ) {
- return;
- }
- if(_initialZoomRunning) {
- e.preventDefault();
- return;
- }
- if(_oldAndroidTouchEndTimeout && e.type === 'mousedown') {
- return;
- }
- if(_preventDefaultEventBehaviour(e, true)) {
- e.preventDefault();
- }
- _shout('pointerDown');
- if(_pointerEventEnabled) {
- var pointerIndex = framework.arraySearch(_currPointers, e.pointerId, 'id');
- if(pointerIndex < 0) {
- pointerIndex = _currPointers.length;
- }
- _currPointers[pointerIndex] = {x:e.pageX, y:e.pageY, id: e.pointerId};
- }
-
- var startPointsList = _getTouchPoints(e),
- numPoints = startPointsList.length;
- _currentPoints = null;
- _stopAllAnimations();
- // init drag
- if(!_isDragging || numPoints === 1) {
-
- _isDragging = _isFirstMove = true;
- framework.bind(window, _upMoveEvents, self);
- _isZoomingIn =
- _wasOverInitialZoom =
- _opacityChanged =
- _verticalDragInitiated =
- _mainScrollShifted =
- _moved =
- _isMultitouch =
- _zoomStarted = false;
- _direction = null;
- _shout('firstTouchStart', startPointsList);
- _equalizePoints(_startPanOffset, _panOffset);
- _currPanDist.x = _currPanDist.y = 0;
- _equalizePoints(_currPoint, startPointsList[0]);
- _equalizePoints(_startPoint, _currPoint);
- //_equalizePoints(_startMainScrollPos, _mainScrollPos);
- _startMainScrollPos.x = _slideSize.x * _currPositionIndex;
- _posPoints = [{
- x: _currPoint.x,
- y: _currPoint.y
- }];
- _gestureCheckSpeedTime = _gestureStartTime = _getCurrentTime();
- //_mainScrollAnimationEnd(true);
- _calculatePanBounds( _currZoomLevel, true );
-
- // Start rendering
- _stopDragUpdateLoop();
- _dragUpdateLoop();
-
- }
- // init zoom
- if(!_isZooming && numPoints > 1 && !_mainScrollAnimating && !_mainScrollShifted) {
- _startZoomLevel = _currZoomLevel;
- _zoomStarted = false; // true if zoom changed at least once
- _isZooming = _isMultitouch = true;
- _currPanDist.y = _currPanDist.x = 0;
- _equalizePoints(_startPanOffset, _panOffset);
- _equalizePoints(p, startPointsList[0]);
- _equalizePoints(p2, startPointsList[1]);
- _findCenterOfPoints(p, p2, _currCenterPoint);
- _midZoomPoint.x = Math.abs(_currCenterPoint.x) - _panOffset.x;
- _midZoomPoint.y = Math.abs(_currCenterPoint.y) - _panOffset.y;
- _currPointsDistance = _startPointsDistance = _calculatePointsDistance(p, p2);
- }
- },
- // Pointermove/touchmove/mousemove handler
- _onDragMove = function(e) {
- e.preventDefault();
- if(_pointerEventEnabled) {
- var pointerIndex = framework.arraySearch(_currPointers, e.pointerId, 'id');
- if(pointerIndex > -1) {
- var p = _currPointers[pointerIndex];
- p.x = e.pageX;
- p.y = e.pageY;
- }
- }
- if(_isDragging) {
- var touchesList = _getTouchPoints(e);
- if(!_direction && !_moved && !_isZooming) {
- var diff = Math.abs(touchesList[0].x - _currPoint.x) - Math.abs(touchesList[0].y - _currPoint.y);
- // check the direction of movement
- if(Math.abs(diff) >= DIRECTION_CHECK_OFFSET) {
- _direction = diff > 0 ? 'h' : 'v';
- _currentPoints = touchesList;
- }
- } else {
- _currentPoints = touchesList;
- }
- }
- },
- //
- _renderMovement = function() {
- if(!_currentPoints) {
- return;
- }
- var numPoints = _currentPoints.length;
- if(numPoints === 0) {
- return;
- }
- _equalizePoints(p, _currentPoints[0]);
- delta.x = p.x - _currPoint.x;
- delta.y = p.y - _currPoint.y;
- if(_isZooming && numPoints > 1) {
- // Handle behaviour for more than 1 point
- _currPoint.x = p.x;
- _currPoint.y = p.y;
-
- // check if one of two points changed
- if( !delta.x && !delta.y && _isEqualPoints(_currentPoints[1], p2) ) {
- return;
- }
- _equalizePoints(p2, _currentPoints[1]);
- if(!_zoomStarted) {
- _zoomStarted = true;
- _shout('zoomGestureStarted');
- }
-
- // Distance between two points
- var pointsDistance = _calculatePointsDistance(p,p2);
- var zoomLevel = _calculateZoomLevel(pointsDistance);
- // slightly over the of initial zoom level
- if(zoomLevel > self.currItem.initialZoomLevel + self.currItem.initialZoomLevel / 15) {
- _wasOverInitialZoom = true;
- }
- // Apply the friction if zoom level is out of the bounds
- var zoomFriction = 1,
- minZoomLevel = _getMinZoomLevel(),
- maxZoomLevel = _getMaxZoomLevel();
- if ( zoomLevel < minZoomLevel ) {
-
- if(_options.pinchToClose && !_wasOverInitialZoom && _startZoomLevel <= self.currItem.initialZoomLevel) {
- // fade out background if zooming out
- var minusDiff = minZoomLevel - zoomLevel;
- var percent = 1 - minusDiff / (minZoomLevel / 1.2);
- _applyBgOpacity(percent);
- _shout('onPinchClose', percent);
- _opacityChanged = true;
- } else {
- zoomFriction = (minZoomLevel - zoomLevel) / minZoomLevel;
- if(zoomFriction > 1) {
- zoomFriction = 1;
- }
- zoomLevel = minZoomLevel - zoomFriction * (minZoomLevel / 3);
- }
-
- } else if ( zoomLevel > maxZoomLevel ) {
- // 1.5 - extra zoom level above the max. E.g. if max is x6, real max 6 + 1.5 = 7.5
- zoomFriction = (zoomLevel - maxZoomLevel) / ( minZoomLevel * 6 );
- if(zoomFriction > 1) {
- zoomFriction = 1;
- }
- zoomLevel = maxZoomLevel + zoomFriction * minZoomLevel;
- }
- if(zoomFriction < 0) {
- zoomFriction = 0;
- }
- // distance between touch points after friction is applied
- _currPointsDistance = pointsDistance;
- // _centerPoint - The point in the middle of two pointers
- _findCenterOfPoints(p, p2, _centerPoint);
-
- // paning with two pointers pressed
- _currPanDist.x += _centerPoint.x - _currCenterPoint.x;
- _currPanDist.y += _centerPoint.y - _currCenterPoint.y;
- _equalizePoints(_currCenterPoint, _centerPoint);
- _panOffset.x = _calculatePanOffset('x', zoomLevel);
- _panOffset.y = _calculatePanOffset('y', zoomLevel);
- _isZoomingIn = zoomLevel > _currZoomLevel;
- _currZoomLevel = zoomLevel;
- _applyCurrentZoomPan();
- } else {
- // handle behaviour for one point (dragging or panning)
- if(!_direction) {
- return;
- }
- if(_isFirstMove) {
- _isFirstMove = false;
- // subtract drag distance that was used during the detection direction
- if( Math.abs(delta.x) >= DIRECTION_CHECK_OFFSET) {
- delta.x -= _currentPoints[0].x - _startPoint.x;
- }
-
- if( Math.abs(delta.y) >= DIRECTION_CHECK_OFFSET) {
- delta.y -= _currentPoints[0].y - _startPoint.y;
- }
- }
- _currPoint.x = p.x;
- _currPoint.y = p.y;
- // do nothing if pointers position hasn't changed
- if(delta.x === 0 && delta.y === 0) {
- return;
- }
- if(_direction === 'v' && _options.closeOnVerticalDrag) {
- if(!_canPan()) {
-
- _currPanDist.y += delta.y;
- _panOffset.y += delta.y;
- var opacityRatio = _calculateVerticalDragOpacityRatio();
- _verticalDragInitiated = true;
- _shout('onVerticalDrag', opacityRatio);
- _applyBgOpacity(opacityRatio);
- _applyCurrentZoomPan();
- return ;
- }
- }
- _pushPosPoint(_getCurrentTime(), p.x, p.y);
- _moved = true;
- _currPanBounds = self.currItem.bounds;
-
- var mainScrollChanged = _panOrMoveMainScroll('x', delta);
- if(!mainScrollChanged) {
- _panOrMoveMainScroll('y', delta);
- _roundPoint(_panOffset);
- _applyCurrentZoomPan();
- }
- }
- },
-
- // Pointerup/pointercancel/touchend/touchcancel/mouseup event handler
- _onDragRelease = function(e) {
- if(_features.isOldAndroid ) {
- if(_oldAndroidTouchEndTimeout && e.type === 'mouseup') {
- return;
- }
- // on Android (v4.1, 4.2, 4.3 & possibly older)
- // ghost mousedown/up event isn't preventable via e.preventDefault,
- // which causes fake mousedown event
- // so we block mousedown/up for 600ms
- if( e.type.indexOf('touch') > -1 ) {
- clearTimeout(_oldAndroidTouchEndTimeout);
- _oldAndroidTouchEndTimeout = setTimeout(function() {
- _oldAndroidTouchEndTimeout = 0;
- }, 600);
- }
-
- }
- _shout('pointerUp');
- if(_preventDefaultEventBehaviour(e, false)) {
- e.preventDefault();
- }
- var releasePoint;
- if(_pointerEventEnabled) {
- var pointerIndex = framework.arraySearch(_currPointers, e.pointerId, 'id');
-
- if(pointerIndex > -1) {
- releasePoint = _currPointers.splice(pointerIndex, 1)[0];
- if(navigator.pointerEnabled) {
- releasePoint.type = e.pointerType || 'mouse';
- } else {
- var MSPOINTER_TYPES = {
- 4: 'mouse', // event.MSPOINTER_TYPE_MOUSE
- 2: 'touch', // event.MSPOINTER_TYPE_TOUCH
- 3: 'pen' // event.MSPOINTER_TYPE_PEN
- };
- releasePoint.type = MSPOINTER_TYPES[e.pointerType];
- if(!releasePoint.type) {
- releasePoint.type = e.pointerType || 'mouse';
- }
- }
- }
- }
- var touchList = _getTouchPoints(e),
- gestureType,
- numPoints = touchList.length;
- if(e.type === 'mouseup') {
- numPoints = 0;
- }
- // Do nothing if there were 3 touch points or more
- if(numPoints === 2) {
- _currentPoints = null;
- return true;
- }
- // if second pointer released
- if(numPoints === 1) {
- _equalizePoints(_startPoint, touchList[0]);
- }
- // pointer hasn't moved, send "tap release" point
- if(numPoints === 0 && !_direction && !_mainScrollAnimating) {
- if(!releasePoint) {
- if(e.type === 'mouseup') {
- releasePoint = {x: e.pageX, y: e.pageY, type:'mouse'};
- } else if(e.changedTouches && e.changedTouches[0]) {
- releasePoint = {x: e.changedTouches[0].pageX, y: e.changedTouches[0].pageY, type:'touch'};
- }
- }
- _shout('touchRelease', e, releasePoint);
- }
- // Difference in time between releasing of two last touch points (zoom gesture)
- var releaseTimeDiff = -1;
- // Gesture completed, no pointers left
- if(numPoints === 0) {
- _isDragging = false;
- framework.unbind(window, _upMoveEvents, self);
- _stopDragUpdateLoop();
- if(_isZooming) {
- // Two points released at the same time
- releaseTimeDiff = 0;
- } else if(_lastReleaseTime !== -1) {
- releaseTimeDiff = _getCurrentTime() - _lastReleaseTime;
- }
- }
- _lastReleaseTime = numPoints === 1 ? _getCurrentTime() : -1;
-
- if(releaseTimeDiff !== -1 && releaseTimeDiff < 150) {
- gestureType = 'zoom';
- } else {
- gestureType = 'swipe';
- }
- if(_isZooming && numPoints < 2) {
- _isZooming = false;
- // Only second point released
- if(numPoints === 1) {
- gestureType = 'zoomPointerUp';
- }
- _shout('zoomGestureEnded');
- }
- _currentPoints = null;
- if(!_moved && !_zoomStarted && !_mainScrollAnimating && !_verticalDragInitiated) {
- // nothing to animate
- return;
- }
-
- _stopAllAnimations();
-
- if(!_releaseAnimData) {
- _releaseAnimData = _initDragReleaseAnimationData();
- }
-
- _releaseAnimData.calculateSwipeSpeed('x');
- if(_verticalDragInitiated) {
- var opacityRatio = _calculateVerticalDragOpacityRatio();
- if(opacityRatio < 0.6) {
- self.close();
- } else {
- var initalPanY = _panOffset.y,
- initialBgOpacity = _bgOpacity;
- _animateProp('verticalDrag', 0, 1, 300, framework.easing.cubic.out, function(now) {
-
- _panOffset.y = (self.currItem.initialPosition.y - initalPanY) * now + initalPanY;
- _applyBgOpacity( (1 - initialBgOpacity) * now + initialBgOpacity );
- _applyCurrentZoomPan();
- });
- _shout('onVerticalDrag', 1);
- }
- return;
- }
- // main scroll
- if( (_mainScrollShifted || _mainScrollAnimating) && numPoints === 0) {
- var itemChanged = _finishSwipeMainScrollGesture(gestureType, _releaseAnimData);
- if(itemChanged) {
- return;
- }
- gestureType = 'zoomPointerUp';
- }
- // prevent zoom/pan animation when main scroll animation runs
- if(_mainScrollAnimating) {
- return;
- }
-
- // Complete simple zoom gesture (reset zoom level if it's out of the bounds)
- if(gestureType !== 'swipe') {
- _completeZoomGesture();
- return;
- }
-
- // Complete pan gesture if main scroll is not shifted, and it's possible to pan current image
- if(!_mainScrollShifted && _currZoomLevel > self.currItem.fitRatio) {
- _completePanGesture(_releaseAnimData);
- }
- },
- // Returns object with data about gesture
- // It's created only once and then reused
- _initDragReleaseAnimationData = function() {
- // temp local vars
- var lastFlickDuration,
- tempReleasePos;
- // s = this
- var s = {
- lastFlickOffset: {},
- lastFlickDist: {},
- lastFlickSpeed: {},
- slowDownRatio: {},
- slowDownRatioReverse: {},
- speedDecelerationRatio: {},
- speedDecelerationRatioAbs: {},
- distanceOffset: {},
- backAnimDestination: {},
- backAnimStarted: {},
- calculateSwipeSpeed: function(axis) {
-
- if( _posPoints.length > 1) {
- lastFlickDuration = _getCurrentTime() - _gestureCheckSpeedTime + 50;
- tempReleasePos = _posPoints[_posPoints.length-2][axis];
- } else {
- lastFlickDuration = _getCurrentTime() - _gestureStartTime; // total gesture duration
- tempReleasePos = _startPoint[axis];
- }
- s.lastFlickOffset[axis] = _currPoint[axis] - tempReleasePos;
- s.lastFlickDist[axis] = Math.abs(s.lastFlickOffset[axis]);
- if(s.lastFlickDist[axis] > 20) {
- s.lastFlickSpeed[axis] = s.lastFlickOffset[axis] / lastFlickDuration;
- } else {
- s.lastFlickSpeed[axis] = 0;
- }
- if( Math.abs(s.lastFlickSpeed[axis]) < 0.1 ) {
- s.lastFlickSpeed[axis] = 0;
- }
-
- s.slowDownRatio[axis] = 0.95;
- s.slowDownRatioReverse[axis] = 1 - s.slowDownRatio[axis];
- s.speedDecelerationRatio[axis] = 1;
- },
- calculateOverBoundsAnimOffset: function(axis, speed) {
- if(!s.backAnimStarted[axis]) {
- if(_panOffset[axis] > _currPanBounds.min[axis]) {
- s.backAnimDestination[axis] = _currPanBounds.min[axis];
-
- } else if(_panOffset[axis] < _currPanBounds.max[axis]) {
- s.backAnimDestination[axis] = _currPanBounds.max[axis];
- }
- if(s.backAnimDestination[axis] !== undefined) {
- s.slowDownRatio[axis] = 0.7;
- s.slowDownRatioReverse[axis] = 1 - s.slowDownRatio[axis];
- if(s.speedDecelerationRatioAbs[axis] < 0.05) {
- s.lastFlickSpeed[axis] = 0;
- s.backAnimStarted[axis] = true;
- _animateProp('bounceZoomPan'+axis,_panOffset[axis],
- s.backAnimDestination[axis],
- speed || 300,
- framework.easing.sine.out,
- function(pos) {
- _panOffset[axis] = pos;
- _applyCurrentZoomPan();
- }
- );
- }
- }
- }
- },
- // Reduces the speed by slowDownRatio (per 10ms)
- calculateAnimOffset: function(axis) {
- if(!s.backAnimStarted[axis]) {
- s.speedDecelerationRatio[axis] = s.speedDecelerationRatio[axis] * (s.slowDownRatio[axis] +
- s.slowDownRatioReverse[axis] -
- s.slowDownRatioReverse[axis] * s.timeDiff / 10);
- s.speedDecelerationRatioAbs[axis] = Math.abs(s.lastFlickSpeed[axis] * s.speedDecelerationRatio[axis]);
- s.distanceOffset[axis] = s.lastFlickSpeed[axis] * s.speedDecelerationRatio[axis] * s.timeDiff;
- _panOffset[axis] += s.distanceOffset[axis];
- }
- },
- panAnimLoop: function() {
- if ( _animations.zoomPan ) {
- _animations.zoomPan.raf = _requestAF(s.panAnimLoop);
- s.now = _getCurrentTime();
- s.timeDiff = s.now - s.lastNow;
- s.lastNow = s.now;
-
- s.calculateAnimOffset('x');
- s.calculateAnimOffset('y');
- _applyCurrentZoomPan();
-
- s.calculateOverBoundsAnimOffset('x');
- s.calculateOverBoundsAnimOffset('y');
- if (s.speedDecelerationRatioAbs.x < 0.05 && s.speedDecelerationRatioAbs.y < 0.05) {
- // round pan position
- _panOffset.x = Math.round(_panOffset.x);
- _panOffset.y = Math.round(_panOffset.y);
- _applyCurrentZoomPan();
-
- _stopAnimation('zoomPan');
- return;
- }
- }
- }
- };
- return s;
- },
- _completePanGesture = function(animData) {
- // calculate swipe speed for Y axis (paanning)
- animData.calculateSwipeSpeed('y');
- _currPanBounds = self.currItem.bounds;
-
- animData.backAnimDestination = {};
- animData.backAnimStarted = {};
- // Avoid acceleration animation if speed is too low
- if(Math.abs(animData.lastFlickSpeed.x) <= 0.05 && Math.abs(animData.lastFlickSpeed.y) <= 0.05 ) {
- animData.speedDecelerationRatioAbs.x = animData.speedDecelerationRatioAbs.y = 0;
- // Run pan drag release animation. E.g. if you drag image and release finger without momentum.
- animData.calculateOverBoundsAnimOffset('x');
- animData.calculateOverBoundsAnimOffset('y');
- return true;
- }
- // Animation loop that controls the acceleration after pan gesture ends
- _registerStartAnimation('zoomPan');
- animData.lastNow = _getCurrentTime();
- animData.panAnimLoop();
- },
- _finishSwipeMainScrollGesture = function(gestureType, _releaseAnimData) {
- var itemChanged;
- if(!_mainScrollAnimating) {
- _currZoomedItemIndex = _currentItemIndex;
- }
-
- var itemsDiff;
- if(gestureType === 'swipe') {
- var totalShiftDist = _currPoint.x - _startPoint.x,
- isFastLastFlick = _releaseAnimData.lastFlickDist.x < 10;
- // if container is shifted for more than MIN_SWIPE_DISTANCE,
- // and last flick gesture was in right direction
- if(totalShiftDist > MIN_SWIPE_DISTANCE &&
- (isFastLastFlick || _releaseAnimData.lastFlickOffset.x > 20) ) {
- // go to prev item
- itemsDiff = -1;
- } else if(totalShiftDist < -MIN_SWIPE_DISTANCE &&
- (isFastLastFlick || _releaseAnimData.lastFlickOffset.x < -20) ) {
- // go to next item
- itemsDiff = 1;
- }
- }
- var nextCircle;
- if(itemsDiff) {
-
- _currentItemIndex += itemsDiff;
- if(_currentItemIndex < 0) {
- _currentItemIndex = _options.loop ? _getNumItems()-1 : 0;
- nextCircle = true;
- } else if(_currentItemIndex >= _getNumItems()) {
- _currentItemIndex = _options.loop ? 0 : _getNumItems()-1;
- nextCircle = true;
- }
- if(!nextCircle || _options.loop) {
- _indexDiff += itemsDiff;
- _currPositionIndex -= itemsDiff;
- itemChanged = true;
- }
-
-
- }
- var animateToX = _slideSize.x * _currPositionIndex;
- var animateToDist = Math.abs( animateToX - _mainScrollPos.x );
- var finishAnimDuration;
- if(!itemChanged && animateToX > _mainScrollPos.x !== _releaseAnimData.lastFlickSpeed.x > 0) {
- // "return to current" duration, e.g. when dragging from slide 0 to -1
- finishAnimDuration = 333;
- } else {
- finishAnimDuration = Math.abs(_releaseAnimData.lastFlickSpeed.x) > 0 ?
- animateToDist / Math.abs(_releaseAnimData.lastFlickSpeed.x) :
- 333;
- finishAnimDuration = Math.min(finishAnimDuration, 400);
- finishAnimDuration = Math.max(finishAnimDuration, 250);
- }
- if(_currZoomedItemIndex === _currentItemIndex) {
- itemChanged = false;
- }
-
- _mainScrollAnimating = true;
-
- _shout('mainScrollAnimStart');
- _animateProp('mainScroll', _mainScrollPos.x, animateToX, finishAnimDuration, framework.easing.cubic.out,
- _moveMainScroll,
- function() {
- _stopAllAnimations();
- _mainScrollAnimating = false;
- _currZoomedItemIndex = -1;
-
- if(itemChanged || _currZoomedItemIndex !== _currentItemIndex) {
- self.updateCurrItem();
- }
-
- _shout('mainScrollAnimComplete');
- }
- );
- if(itemChanged) {
- self.updateCurrItem(true);
- }
- return itemChanged;
- },
- _calculateZoomLevel = function(touchesDistance) {
- return 1 / _startPointsDistance * touchesDistance * _startZoomLevel;
- },
- // Resets zoom if it's out of bounds
- _completeZoomGesture = function() {
- var destZoomLevel = _currZoomLevel,
- minZoomLevel = _getMinZoomLevel(),
- maxZoomLevel = _getMaxZoomLevel();
- if ( _currZoomLevel < minZoomLevel ) {
- destZoomLevel = minZoomLevel;
- } else if ( _currZoomLevel > maxZoomLevel ) {
- destZoomLevel = maxZoomLevel;
- }
- var destOpacity = 1,
- onUpdate,
- initialOpacity = _bgOpacity;
- if(_opacityChanged && !_isZoomingIn && !_wasOverInitialZoom && _currZoomLevel < minZoomLevel) {
- //_closedByScroll = true;
- self.close();
- return true;
- }
- if(_opacityChanged) {
- onUpdate = function(now) {
- _applyBgOpacity( (destOpacity - initialOpacity) * now + initialOpacity );
- };
- }
- self.zoomTo(destZoomLevel, 0, 300, framework.easing.cubic.out, onUpdate);
- return true;
- };
- _registerModule('Gestures', {
- publicMethods: {
- initGestures: function() {
- // helper function that builds touch/pointer/mouse events
- var addEventNames = function(pref, down, move, up, cancel) {
- _dragStartEvent = pref + down;
- _dragMoveEvent = pref + move;
- _dragEndEvent = pref + up;
- if(cancel) {
- _dragCancelEvent = pref + cancel;
- } else {
- _dragCancelEvent = '';
- }
- };
- _pointerEventEnabled = _features.pointerEvent;
- if(_pointerEventEnabled && _features.touch) {
- // we don't need touch events, if browser supports pointer events
- _features.touch = false;
- }
- if(_pointerEventEnabled) {
- if(navigator.pointerEnabled) {
- addEventNames('pointer', 'down', 'move', 'up', 'cancel');
- } else {
- // IE10 pointer events are case-sensitive
- addEventNames('MSPointer', 'Down', 'Move', 'Up', 'Cancel');
- }
- } else if(_features.touch) {
- addEventNames('touch', 'start', 'move', 'end', 'cancel');
- _likelyTouchDevice = true;
- } else {
- addEventNames('mouse', 'down', 'move', 'up');
- }
- _upMoveEvents = _dragMoveEvent + ' ' + _dragEndEvent + ' ' + _dragCancelEvent;
- _downEvents = _dragStartEvent;
- if(_pointerEventEnabled && !_likelyTouchDevice) {
- _likelyTouchDevice = (navigator.maxTouchPoints > 1) || (navigator.msMaxTouchPoints > 1);
- }
- // make variable public
- self.likelyTouchDevice = _likelyTouchDevice;
-
- _globalEventHandlers[_dragStartEvent] = _onDragStart;
- _globalEventHandlers[_dragMoveEvent] = _onDragMove;
- _globalEventHandlers[_dragEndEvent] = _onDragRelease; // the Kraken
- if(_dragCancelEvent) {
- _globalEventHandlers[_dragCancelEvent] = _globalEventHandlers[_dragEndEvent];
- }
- // Bind mouse events on device with detected hardware touch support, in case it supports multiple types of input.
- if(_features.touch) {
- _downEvents += ' mousedown';
- _upMoveEvents += ' mousemove mouseup';
- _globalEventHandlers.mousedown = _globalEventHandlers[_dragStartEvent];
- _globalEventHandlers.mousemove = _globalEventHandlers[_dragMoveEvent];
- _globalEventHandlers.mouseup = _globalEventHandlers[_dragEndEvent];
- }
- if(!_likelyTouchDevice) {
- // don't allow pan to next slide from zoomed state on Desktop
- _options.allowPanToNext = false;
- }
- }
- }
- });
- /*>>gestures*/
- /*>>show-hide-transition*/
- /**
- * show-hide-transition.js:
- *
- * Manages initial opening or closing transition.
- *
- * If you're not planning to use transition for gallery at all,
- * you may set options hideAnimationDuration and showAnimationDuration to 0,
- * and just delete startAnimation function.
- *
- */
- var _showOrHideTimeout,
- _showOrHide = function(item, img, out, completeFn) {
- if(_showOrHideTimeout) {
- clearTimeout(_showOrHideTimeout);
- }
- _initialZoomRunning = true;
- _initialContentSet = true;
-
- // dimensions of small thumbnail {x:,y:,w:}.
- // Height is optional, as calculated based on large image.
- var thumbBounds;
- if(item.initialLayout) {
- thumbBounds = item.initialLayout;
- item.initialLayout = null;
- } else {
- thumbBounds = _options.getThumbBoundsFn && _options.getThumbBoundsFn(_currentItemIndex);
- }
- var duration = out ? _options.hideAnimationDuration : _options.showAnimationDuration;
- var onComplete = function() {
- _stopAnimation('initialZoom');
- if(!out) {
- _applyBgOpacity(1);
- if(img) {
- img.style.display = 'block';
- }
- framework.addClass(template, 'pswp--animated-in');
- _shout('initialZoom' + (out ? 'OutEnd' : 'InEnd'));
- } else {
- self.template.removeAttribute('style');
- self.bg.removeAttribute('style');
- }
- if(completeFn) {
- completeFn();
- }
- _initialZoomRunning = false;
- };
- // if bounds aren't provided, just open gallery without animation
- if(!duration || !thumbBounds || thumbBounds.x === undefined) {
- var finishWithoutAnimation = function() {
- _shout('initialZoom' + (out ? 'Out' : 'In') );
- _currZoomLevel = item.initialZoomLevel;
- _equalizePoints(_panOffset, item.initialPosition );
- _applyCurrentZoomPan();
- // no transition
- template.style.opacity = out ? 0 : 1;
- _applyBgOpacity(1);
- onComplete();
- };
- finishWithoutAnimation();
-
- return;
- }
- var startAnimation = function() {
- var closeWithRaf = _closedByScroll,
- fadeEverything = !self.currItem.src || self.currItem.loadError || _options.showHideOpacity;
-
- // apply hw-acceleration to image
- if(item.miniImg) {
- item.miniImg.style.webkitBackfaceVisibility = 'hidden';
- }
- if(!out) {
- _currZoomLevel = thumbBounds.w / item.w;
- _panOffset.x = thumbBounds.x;
- _panOffset.y = thumbBounds.y - _initalWindowScrollY;
- self[fadeEverything ? 'template' : 'bg'].style.opacity = 0.001;
- _applyCurrentZoomPan();
- }
- _registerStartAnimation('initialZoom');
-
- if(out && !closeWithRaf) {
- framework.removeClass(template, 'pswp--animated-in');
- }
- if(fadeEverything) {
- if(out) {
- framework[ (closeWithRaf ? 'remove' : 'add') + 'Class' ](template, 'pswp--animate_opacity');
- } else {
- setTimeout(function() {
- framework.addClass(template, 'pswp--animate_opacity');
- }, 30);
- }
- }
- _showOrHideTimeout = setTimeout(function() {
- _shout('initialZoom' + (out ? 'Out' : 'In') );
-
- if(!out) {
- // "in" animation always uses CSS transitions (instead of rAF).
- // CSS transition work faster here,
- // as developer may also want to animate other things,
- // like ui on top of sliding area, which can be animated just via CSS
-
- _currZoomLevel = item.initialZoomLevel;
- _equalizePoints(_panOffset, item.initialPosition );
- _applyCurrentZoomPan();
- _applyBgOpacity(1);
- if(fadeEverything) {
- template.style.opacity = 1;
- } else {
- _applyBgOpacity(1);
- }
- _showOrHideTimeout = setTimeout(onComplete, duration + 20);
- } else {
- // "out" animation uses rAF only when PhotoSwipe is closed by browser scroll, to recalculate position
- var destZoomLevel = thumbBounds.w / item.w,
- initialPanOffset = {
- x: _panOffset.x,
- y: _panOffset.y
- },
- initialZoomLevel = _currZoomLevel,
- initalBgOpacity = _bgOpacity,
- onUpdate = function(now) {
-
- if(now === 1) {
- _currZoomLevel = destZoomLevel;
- _panOffset.x = thumbBounds.x;
- _panOffset.y = thumbBounds.y - _currentWindowScrollY;
- } else {
- _currZoomLevel = (destZoomLevel - initialZoomLevel) * now + initialZoomLevel;
- _panOffset.x = (thumbBounds.x - initialPanOffset.x) * now + initialPanOffset.x;
- _panOffset.y = (thumbBounds.y - _currentWindowScrollY - initialPanOffset.y) * now + initialPanOffset.y;
- }
-
- _applyCurrentZoomPan();
- if(fadeEverything) {
- template.style.opacity = 1 - now;
- } else {
- _applyBgOpacity( initalBgOpacity - now * initalBgOpacity );
- }
- };
- if(closeWithRaf) {
- _animateProp('initialZoom', 0, 1, duration, framework.easing.cubic.out, onUpdate, onComplete);
- } else {
- onUpdate(1);
- _showOrHideTimeout = setTimeout(onComplete, duration + 20);
- }
- }
-
- }, out ? 25 : 90); // Main purpose of this delay is to give browser time to paint and
- // create composite layers of PhotoSwipe UI parts (background, controls, caption, arrows).
- // Which avoids lag at the beginning of scale transition.
- };
- startAnimation();
-
- };
- /*>>show-hide-transition*/
- /*>>items-controller*/
- /**
- *
- * Controller manages gallery items, their dimensions, and their content.
- *
- */
- var _items,
- _tempPanAreaSize = {},
- _imagesToAppendPool = [],
- _initialContentSet,
- _initialZoomRunning,
- _controllerDefaultOptions = {
- index: 0,
- errorMsg: '<div class="pswp__error-msg"><a href="%url%" target="_blank">The image</a> could not be loaded.</div>',
- forceProgressiveLoading: false, // TODO
- preload: [1,1],
- getNumItemsFn: function() {
- return _items.length;
- }
- };
- var _getItemAt,
- _getNumItems,
- _initialIsLoop,
- _getZeroBounds = function() {
- return {
- center:{x:0,y:0},
- max:{x:0,y:0},
- min:{x:0,y:0}
- };
- },
- _calculateSingleItemPanBounds = function(item, realPanElementW, realPanElementH ) {
- var bounds = item.bounds;
- // position of element when it's centered
- bounds.center.x = Math.round((_tempPanAreaSize.x - realPanElementW) / 2);
- bounds.center.y = Math.round((_tempPanAreaSize.y - realPanElementH) / 2) + item.vGap.top;
- // maximum pan position
- bounds.max.x = (realPanElementW > _tempPanAreaSize.x) ?
- Math.round(_tempPanAreaSize.x - realPanElementW) :
- bounds.center.x;
-
- bounds.max.y = (realPanElementH > _tempPanAreaSize.y) ?
- Math.round(_tempPanAreaSize.y - realPanElementH) + item.vGap.top :
- bounds.center.y;
-
- // minimum pan position
- bounds.min.x = (realPanElementW > _tempPanAreaSize.x) ? 0 : bounds.center.x;
- bounds.min.y = (realPanElementH > _tempPanAreaSize.y) ? item.vGap.top : bounds.center.y;
- },
- _calculateItemSize = function(item, viewportSize, zoomLevel) {
- if (item.src && !item.loadError) {
- var isInitial = !zoomLevel;
-
- if(isInitial) {
- if(!item.vGap) {
- item.vGap = {top:0,bottom:0};
- }
- // allows overriding vertical margin for individual items
- _shout('parseVerticalMargin', item);
- }
- _tempPanAreaSize.x = viewportSize.x;
- _tempPanAreaSize.y = viewportSize.y - item.vGap.top - item.vGap.bottom;
- if (isInitial) {
- var hRatio = _tempPanAreaSize.x / item.w;
- var vRatio = _tempPanAreaSize.y / item.h;
- item.fitRatio = hRatio < vRatio ? hRatio : vRatio;
- //item.fillRatio = hRatio > vRatio ? hRatio : vRatio;
- var scaleMode = _options.scaleMode;
- if (scaleMode === 'orig') {
- zoomLevel = 1;
- } else if (scaleMode === 'fit') {
- zoomLevel = item.fitRatio;
- }
- if (zoomLevel > 1) {
- zoomLevel = 1;
- }
- item.initialZoomLevel = zoomLevel;
-
- if(!item.bounds) {
- // reuse bounds object
- item.bounds = _getZeroBounds();
- }
- }
- if(!zoomLevel) {
- return;
- }
- _calculateSingleItemPanBounds(item, item.w * zoomLevel, item.h * zoomLevel);
- if (isInitial && zoomLevel === item.initialZoomLevel) {
- item.initialPosition = item.bounds.center;
- }
- return item.bounds;
- } else {
- item.w = item.h = 0;
- item.initialZoomLevel = item.fitRatio = 1;
- item.bounds = _getZeroBounds();
- item.initialPosition = item.bounds.center;
- // if it's not image, we return zero bounds (content is not zoomable)
- return item.bounds;
- }
- return false;
- },
-
- _appendImage = function(index, item, baseDiv, img, preventAnimation, keepPlaceholder) {
-
- if(item.loadError) {
- return;
- }
- var animate,
- isSwiping = self.isDragging() && !self.isZooming(),
- slideMightBeVisible = index === _currentItemIndex || self.isMainScrollAnimating() || isSwiping;
- // fade in loaded image only when current holder is active, or might be visible
- if(!preventAnimation && (_likelyTouchDevice || _options.alwaysFadeIn) && slideMightBeVisible) {
- animate = true;
- }
- if(img) {
- if(animate) {
- img.style.opacity = 0;
- }
- item.imageAppended = true;
- _setImageSize(img, item.w, item.h);
-
- baseDiv.appendChild(img);
- if(animate) {
- setTimeout(function() {
- img.style.opacity = 1;
- if(keepPlaceholder) {
- setTimeout(function() {
- // hide image placeholder "behind"
- if(item && item.loaded && item.placeholder) {
- item.placeholder.style.display = 'none';
- item.placeholder = null;
- }
- }, 500);
- }
- }, 50);
- }
- }
- },
-
- _preloadImage = function(item) {
- item.loading = true;
- item.loaded = false;
- var img = item.img = framework.createEl('pswp__img', 'img');
- var onComplete = function() {
- item.loading = false;
- item.loaded = true;
- if(item.loadComplete) {
- item.loadComplete(item);
- } else {
- item.img = null; // no need to store image object
- }
- img.onload = img.onerror = null;
- img = null;
- };
- img.onload = onComplete;
- img.onerror = function() {
- item.loadError = true;
- onComplete();
- };
- img.src = item.src;// + '?a=' + Math.random();
- return img;
- },
- _checkForError = function(item, cleanUp) {
- if(item.src && item.loadError && item.container) {
- if(cleanUp) {
- item.container.innerHTML = '';
- }
- item.container.innerHTML = _options.errorMsg.replace('%url%', item.src );
- return true;
-
- }
- },
- _setImageSize = function(img, w, h) {
- img.style.width = w + 'px';
- img.style.height = h + 'px';
- },
- _appendImagesPool = function() {
- if(_imagesToAppendPool.length) {
- var poolItem;
- for(var i = 0; i < _imagesToAppendPool.length; i++) {
- poolItem = _imagesToAppendPool[i];
- if( poolItem.holder.index === poolItem.index ) {
- _appendImage(poolItem.index, poolItem.item, poolItem.baseDiv, poolItem.img);
- }
- }
- _imagesToAppendPool = [];
- }
- };
-
- _registerModule('Controller', {
- publicMethods: {
- lazyLoadItem: function(index) {
- index = _getLoopedId(index);
- var item = _getItemAt(index);
- if(!item || item.loaded || item.loading) {
- return;
- }
- _shout('gettingData', index, item);
- if (!item.src) {
- return;
- }
- _preloadImage(item);
- },
- initController: function() {
- framework.extend(_options, _controllerDefaultOptions, true);
- self.items = _items = items;
- _getItemAt = self.getItemAt;
- _getNumItems = _options.getNumItemsFn; //self.getNumItems;
- _initialIsLoop = _options.loop;
- if(_getNumItems() < 3) {
- _options.loop = false; // disable loop if less then 3 items
- }
- _listen('beforeChange', function(diff) {
- var p = _options.preload,
- isNext = diff === null ? true : (diff > 0),
- preloadBefore = Math.min(p[0], _getNumItems() ),
- preloadAfter = Math.min(p[1], _getNumItems() ),
- i;
- for(i = 1; i <= (isNext ? preloadAfter : preloadBefore); i++) {
- self.lazyLoadItem(_currentItemIndex+i);
- }
- for(i = 1; i <= (isNext ? preloadBefore : preloadAfter); i++) {
- self.lazyLoadItem(_currentItemIndex-i);
- }
- });
- _listen('initialLayout', function() {
- self.currItem.initialLayout = _options.getThumbBoundsFn && _options.getThumbBoundsFn(_currentItemIndex);
- });
- _listen('mainScrollAnimComplete', _appendImagesPool);
- _listen('initialZoomInEnd', _appendImagesPool);
- _listen('destroy', function() {
- var item;
- for(var i = 0; i < _items.length; i++) {
- item = _items[i];
- // remove reference to DOM elements, for GC
- if(item.container) {
- item.container = null;
- }
- if(item.placeholder) {
- item.placeholder = null;
- }
- if(item.img) {
- item.img = null;
- }
- if(item.preloader) {
- item.preloader = null;
- }
- if(item.loadError) {
- item.loaded = item.loadError = false;
- }
- }
- _imagesToAppendPool = null;
- });
- },
- getItemAt: function(index) {
- if (index >= 0) {
- return _items[index] !== undefined ? _items[index] : false;
- }
- return false;
- },
- allowProgressiveImg: function() {
- // 1. Progressive image loading isn't working on webkit/blink
- // when hw-acceleration (e.g. translateZ) is applied to IMG element.
- // That's why in PhotoSwipe parent element gets zoom transform, not image itself.
- //
- // 2. Progressive image loading sometimes blinks in webkit/blink when applying animation to parent element.
- // That's why it's disabled on touch devices (mainly because of swipe transition)
- //
- // 3. Progressive image loading sometimes doesn't work in IE (up to 11).
- // Don't allow progressive loading on non-large touch devices
- return _options.forceProgressiveLoading || !_likelyTouchDevice || _options.mouseUsed || screen.width > 1200;
- // 1200 - to eliminate touch devices with large screen (like Chromebook Pixel)
- },
- setContent: function(holder, index) {
- if(_options.loop) {
- index = _getLoopedId(index);
- }
- var prevItem = self.getItemAt(holder.index);
- if(prevItem) {
- prevItem.container = null;
- }
-
- var item = self.getItemAt(index),
- img;
-
- if(!item) {
- holder.el.innerHTML = '';
- return;
- }
- // allow to override data
- _shout('gettingData', index, item);
- holder.index = index;
- holder.item = item;
- // base container DIV is created only once for each of 3 holders
- var baseDiv = item.container = framework.createEl('pswp__zoom-wrap');
-
- if(!item.src && item.html) {
- // if(item.html.tagName) {
- // baseDiv.appendChild(item.html);
- // } else {
- baseDiv.innerHTML = item.html;
- // }
- }
- _checkForError(item);
-
- if(item.src && !item.loadError && !item.loaded) {
- item.loadComplete = function(item) {
- // gallery closed before image finished loading
- if(!_isOpen) {
- return;
- }
- // Apply hw-acceleration only after image is loaded.
- // This is webkit progressive image loading bugfix.
- // https://bugs.webkit.org/show_bug.cgi?id=108630
- // https://code.google.com/p/chromium/issues/detail?id=404547
- if(item.img) {
- item.img.style.webkitBackfaceVisibility = 'hidden';
- }
- // check if holder hasn't changed while image was loading
- if(holder && holder.index === index ) {
- if( _checkForError(item, true) ) {
- item.loadComplete = item.img = null;
- _calculateItemSize(item, _viewportSize);
- _applyZoomPanToItem(item);
- if(holder.index === _currentItemIndex) {
- // recalculate dimensions
- self.updateCurrZoomItem();
- }
- return;
- }
- if( !item.imageAppended ) {
- if(_features.transform && (_mainScrollAnimating || _initialZoomRunning) ) {
- _imagesToAppendPool.push({
- item:item,
- baseDiv:baseDiv,
- img:item.img,
- index:index,
- holder:holder
- });
- } else {
- _appendImage(index, item, baseDiv, item.img, _mainScrollAnimating || _initialZoomRunning);
- }
- } else {
- // remove preloader & mini-img
- if(!_initialZoomRunning && item.placeholder) {
- item.placeholder.style.display = 'none';
- item.placeholder = null;
- }
- }
- }
- item.loadComplete = null;
- item.img = null; // no need to store image element after it's added
- _shout('imageLoadComplete', index, item);
- };
- if(framework.features.transform) {
-
- var placeholderClassName = 'pswp__img pswp__img--placeholder';
- placeholderClassName += (item.msrc ? '' : ' pswp__img--placeholder--blank');
- var placeholder = framework.createEl(placeholderClassName, item.msrc ? 'img' : '');
- if(item.msrc) {
- placeholder.src = item.msrc;
- }
-
- _setImageSize(placeholder, item.w, item.h);
- baseDiv.appendChild(placeholder);
- item.placeholder = placeholder;
- }
-
-
- if(!item.loading) {
- _preloadImage(item);
- }
- if( self.allowProgressiveImg() ) {
- // just append image
- if(!_initialContentSet && _features.transform) {
- _imagesToAppendPool.push({
- item:item,
- baseDiv:baseDiv,
- img:item.img,
- index:index,
- holder:holder
- });
- } else {
- _appendImage(index, item, baseDiv, item.img, true, true);
- }
- }
-
- } else if(item.src && !item.loadError) {
- // image object is created every time, due to bugs of image loading & delay when switching images
- img = framework.createEl('pswp__img', 'img');
- img.style.webkitBackfaceVisibility = 'hidden';
- img.style.opacity = 1;
- img.src = item.src;
- _setImageSize(img, item.w, item.h);
- _appendImage(index, item, baseDiv, img, true);
- }
-
- _calculateItemSize(item, _viewportSize);
- if(!_initialContentSet && index === _currentItemIndex) {
- _currZoomElementStyle = baseDiv.style;
- _showOrHide(item, (img ||item.img) );
- } else {
- _applyZoomPanToItem(item);
- }
- holder.el.innerHTML = '';
- holder.el.appendChild(baseDiv);
- },
- cleanSlide: function( item ) {
- if(item.img ) {
- item.img.onload = item.img.onerror = null;
- }
- item.loaded = item.loading = item.img = item.imageAppended = false;
- }
- }
- });
- /*>>items-controller*/
- /*>>tap*/
- /**
- * tap.js:
- *
- * Displatches tap and double-tap events.
- *
- */
- var tapTimer,
- tapReleasePoint = {},
- _dispatchTapEvent = function(origEvent, releasePoint, pointerType) {
- var e = document.createEvent( 'CustomEvent' ),
- eDetail = {
- origEvent:origEvent,
- target:origEvent.target,
- releasePoint: releasePoint,
- pointerType:pointerType || 'touch'
- };
- e.initCustomEvent( 'pswpTap', true, true, eDetail );
- origEvent.target.dispatchEvent(e);
- };
- _registerModule('Tap', {
- publicMethods: {
- initTap: function() {
- _listen('firstTouchStart', self.onTapStart);
- _listen('touchRelease', self.onTapRelease);
- _listen('destroy', function() {
- tapReleasePoint = {};
- tapTimer = null;
- });
- },
- onTapStart: function(touchList) {
- if(touchList.length > 1) {
- clearTimeout(tapTimer);
- tapTimer = null;
- }
- },
- onTapRelease: function(e, releasePoint) {
- if(!releasePoint) {
- return;
- }
- if(!_moved && !_isMultitouch && !_numAnimations) {
- var p0 = releasePoint;
- if(tapTimer) {
- clearTimeout(tapTimer);
- tapTimer = null;
- // Check if taped on the same place
- if ( _isNearbyPoints(p0, tapReleasePoint) ) {
- _shout('doubleTap', p0);
- return;
- }
- }
- if(releasePoint.type === 'mouse') {
- _dispatchTapEvent(e, releasePoint, 'mouse');
- return;
- }
- var clickedTagName = e.target.tagName.toUpperCase();
- // avoid double tap delay on buttons and elements that have class pswp__single-tap
- if(clickedTagName === 'BUTTON' || framework.hasClass(e.target, 'pswp__single-tap') ) {
- _dispatchTapEvent(e, releasePoint);
- return;
- }
- _equalizePoints(tapReleasePoint, p0);
- tapTimer = setTimeout(function() {
- _dispatchTapEvent(e, releasePoint);
- tapTimer = null;
- }, 300);
- }
- }
- }
- });
- /*>>tap*/
- /*>>desktop-zoom*/
- /**
- *
- * desktop-zoom.js:
- *
- * - Binds mousewheel event for paning zoomed image.
- * - Manages "dragging", "zoomed-in", "zoom-out" classes.
- * (which are used for cursors and zoom icon)
- * - Adds toggleDesktopZoom function.
- *
- */
- var _wheelDelta;
-
- _registerModule('DesktopZoom', {
- publicMethods: {
- initDesktopZoom: function() {
- if(_oldIE) {
- // no zoom for old IE (<=8)
- return;
- }
- if(_likelyTouchDevice) {
- // if detected hardware touch support, we wait until mouse is used,
- // and only then apply desktop-zoom features
- _listen('mouseUsed', function() {
- self.setupDesktopZoom();
- });
- } else {
- self.setupDesktopZoom(true);
- }
- },
- setupDesktopZoom: function(onInit) {
- _wheelDelta = {};
- var events = 'wheel mousewheel DOMMouseScroll';
-
- _listen('bindEvents', function() {
- framework.bind(template, events, self.handleMouseWheel);
- });
- _listen('unbindEvents', function() {
- if(_wheelDelta) {
- framework.unbind(template, events, self.handleMouseWheel);
- }
- });
- self.mouseZoomedIn = false;
- var hasDraggingClass,
- updateZoomable = function() {
- if(self.mouseZoomedIn) {
- framework.removeClass(template, 'pswp--zoomed-in');
- self.mouseZoomedIn = false;
- }
- if(_currZoomLevel < 1) {
- framework.addClass(template, 'pswp--zoom-allowed');
- } else {
- framework.removeClass(template, 'pswp--zoom-allowed');
- }
- removeDraggingClass();
- },
- removeDraggingClass = function() {
- if(hasDraggingClass) {
- framework.removeClass(template, 'pswp--dragging');
- hasDraggingClass = false;
- }
- };
- _listen('resize' , updateZoomable);
- _listen('afterChange' , updateZoomable);
- _listen('pointerDown', function() {
- if(self.mouseZoomedIn) {
- hasDraggingClass = true;
- framework.addClass(template, 'pswp--dragging');
- }
- });
- _listen('pointerUp', removeDraggingClass);
- if(!onInit) {
- updateZoomable();
- }
-
- },
- handleMouseWheel: function(e) {
- if(_currZoomLevel <= self.currItem.fitRatio) {
- if(!_options.closeOnScroll) {
- e.preventDefault();
- } else {
- // close PhotoSwipe
- // if browser supports transforms & scroll changed enough
- if( _transformKey && Math.abs(e.deltaY) > 2 ) {
- _closedByScroll = true;
- self.close();
- }
- }
- return true;
- }
- e.preventDefault();
- // allow just one event to fire
- e.stopPropagation();
- // https://developer.mozilla.org/en-US/docs/Web/Events/wheel
- _wheelDelta.x = 0;
- if('deltaX' in e) {
- if(e.deltaMode === 1 /* DOM_DELTA_LINE */) {
- // 18 - average line height
- _wheelDelta.x = e.deltaX * 18;
- _wheelDelta.y = e.deltaY * 18;
- } else {
- _wheelDelta.x = e.deltaX;
- _wheelDelta.y = e.deltaY;
- }
- } else if('wheelDelta' in e) {
- if(e.wheelDeltaX) {
- _wheelDelta.x = -0.16 * e.wheelDeltaX;
- }
- if(e.wheelDeltaY) {
- _wheelDelta.y = -0.16 * e.wheelDeltaY;
- } else {
- _wheelDelta.y = -0.16 * e.wheelDelta;
- }
- } else if('detail' in e) {
- _wheelDelta.y = e.detail;
- } else {
- return;
- }
-
- // TODO: use rAF instead of mousewheel?
- _calculatePanBounds(_currZoomLevel, true);
- self.panTo(_panOffset.x - _wheelDelta.x, _panOffset.y - _wheelDelta.y);
- },
- toggleDesktopZoom: function(centerPoint) {
- centerPoint = centerPoint || {x:_viewportSize.x/2, y:_viewportSize.y/2 + _currentWindowScrollY };
- var doubleTapZoomLevel = _options.getDoubleTapZoom(true, self.currItem);
- var zoomOut = _currZoomLevel === doubleTapZoomLevel;
-
- self.mouseZoomedIn = !zoomOut;
- self.zoomTo(zoomOut ? self.currItem.initialZoomLevel : doubleTapZoomLevel, centerPoint, 333);
- framework[ (!zoomOut ? 'add' : 'remove') + 'Class'](template, 'pswp--zoomed-in');
- }
- }
- });
- /*>>desktop-zoom*/
- /*>>history*/
- /**
- *
- * history.js:
- *
- * - Back button to close gallery.
- *
- * - Unique URL for each slide: example.com/&pid=1&gid=3
- * (where PID is picture index, and GID and gallery index)
- *
- * - Switch URL when slides change.
- *
- */
- var _historyDefaultOptions = {
- history: true,
- galleryUID: 1
- };
- var _historyUpdateTimeout,
- _hashChangeTimeout,
- _hashAnimCheckTimeout,
- _hashChangedByScript,
- _hashChangedByHistory,
- _hashReseted,
- _initialHash,
- _historyChanged,
- _closedFromURL,
- _urlChangedOnce,
- _windowLoc,
- _supportsPushState,
- _getHash = function() {
- return _windowLoc.hash.substring(1);
- },
- _cleanHistoryTimeouts = function() {
- if(_historyUpdateTimeout) {
- clearTimeout(_historyUpdateTimeout);
- }
- if(_hashAnimCheckTimeout) {
- clearTimeout(_hashAnimCheckTimeout);
- }
- },
- // pid - Picture index
- // gid - Gallery index
- _parseItemIndexFromURL = function() {
- var hash = _getHash(),
- params = {};
- if(hash.length < 5) { // pid=1
- return params;
- }
- var vars = hash.split('&');
- for (var i = 0; i < vars.length; i++) {
- if(!vars[i]) {
- continue;
- }
- var pair = vars[i].split('=');
- if(pair.length < 2) {
- continue;
- }
- params[pair[0]] = pair[1];
- }
- params.pid = parseInt(params.pid,10)-1;
- if( params.pid < 0 ) {
- params.pid = 0;
- }
- return params;
- },
- _updateHash = function() {
- if(_hashAnimCheckTimeout) {
- clearTimeout(_hashAnimCheckTimeout);
- }
- if(_numAnimations || _isDragging) {
- // changing browser URL forces layout/paint in some browsers, which causes noticable lag during animation
- // that's why we update hash only when no animations running
- _hashAnimCheckTimeout = setTimeout(_updateHash, 500);
- return;
- }
-
- if(_hashChangedByScript) {
- clearTimeout(_hashChangeTimeout);
- } else {
- _hashChangedByScript = true;
- }
- var newHash = _initialHash + '&' + 'gid=' + _options.galleryUID + '&' + 'pid=' + (_currentItemIndex + 1);
- if(!_historyChanged) {
- if(_windowLoc.hash.indexOf(newHash) === -1) {
- _urlChangedOnce = true;
- }
- // first time - add new hisory record, then just replace
- }
- var newURL = _windowLoc.href.split('#')[0] + '#' + newHash;
- if( _supportsPushState ) {
- if('#' + newHash !== window.location.hash) {
- history[_historyChanged ? 'replaceState' : 'pushState']('', document.title, newURL);
- }
- } else {
- if(_historyChanged) {
- _windowLoc.replace( newURL );
- } else {
- _windowLoc.hash = newHash;
- }
- }
-
-
- _historyChanged = true;
- _hashChangeTimeout = setTimeout(function() {
- _hashChangedByScript = false;
- }, 60);
- };
-
- _registerModule('History', {
-
- publicMethods: {
- initHistory: function() {
- framework.extend(_options, _historyDefaultOptions, true);
- if( !_options.history ) {
- return;
- }
- _windowLoc = window.location;
- _urlChangedOnce = false;
- _closedFromURL = false;
- _historyChanged = false;
- _initialHash = _getHash();
- _supportsPushState = ('pushState' in history);
- if(_initialHash.indexOf('gid=') > -1) {
- _initialHash = _initialHash.split('&gid=')[0];
- _initialHash = _initialHash.split('?gid=')[0];
- }
-
- _listen('afterChange', self.updateURL);
- _listen('unbindEvents', function() {
- framework.unbind(window, 'hashchange', self.onHashChange);
- });
- var returnToOriginal = function() {
- _hashReseted = true;
- if(!_closedFromURL) {
- if(_urlChangedOnce) {
- history.back();
- } else {
- if(_initialHash) {
- _windowLoc.hash = _initialHash;
- } else {
- if (_supportsPushState) {
- // remove hash from url without refreshing it or scrolling to top
- history.pushState('', document.title, _windowLoc.pathname + _windowLoc.search );
- } else {
- _windowLoc.hash = '';
- }
- }
- }
-
- }
- _cleanHistoryTimeouts();
- };
- _listen('unbindEvents', function() {
- if(_closedByScroll) {
- // if PhotoSwipe is closed by scroll, we go "back" before the closing animation starts
- // this is done to keep the scroll position
- returnToOriginal();
- }
- });
- _listen('destroy', function() {
- if(!_hashReseted) {
- returnToOriginal();
- }
- });
- _listen('firstUpdate', function() {
- _currentItemIndex = _parseItemIndexFromURL().pid;
- });
-
-
- var index = _initialHash.indexOf('pid=');
- if(index > -1) {
- _initialHash = _initialHash.substring(0, index);
- if(_initialHash.slice(-1) === '&') {
- _initialHash = _initialHash.slice(0, -1);
- }
- }
-
- setTimeout(function() {
- if(_isOpen) { // hasn't destroyed yet
- framework.bind(window, 'hashchange', self.onHashChange);
- }
- }, 40);
-
- },
- onHashChange: function() {
- if(_getHash() === _initialHash) {
- _closedFromURL = true;
- self.close();
- return;
- }
- if(!_hashChangedByScript) {
- _hashChangedByHistory = true;
- self.goTo( _parseItemIndexFromURL().pid );
- _hashChangedByHistory = false;
- }
-
- },
- updateURL: function() {
- // Delay the update of URL, to avoid lag during transition,
- // and to not to trigger actions like "refresh page sound" or "blinking favicon" to often
-
- _cleanHistoryTimeouts();
-
- if(_hashChangedByHistory) {
- return;
- }
- if(!_historyChanged) {
- _updateHash(); // first time
- } else {
- _historyUpdateTimeout = setTimeout(_updateHash, 800);
- }
- }
-
- }
- });
- /*>>history*/
- framework.extend(self, publicMethods); };
- return PhotoSwipe;
- });
|