12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734 |
- /*! PhotoSwipe - v4.1.2 - 2017-04-05
- * http://photoswipe.com
- * Copyright (c) 2017 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,
- verticalDragRange: 0.75,
- 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.33;
- }
- },
- maxSpreadZoom: 1.33,
- modal: true,
- // not fully implemented yet
- scaleMode: 'fit' // 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 = {},
- _renderMaxResolution = false,
- _orientationChangeTimeout,
- // 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,item) {
- if(!_renderMaxResolution || (item && item !== self.currItem) ) {
- zoom = zoom / (item ? item.fitRatio : self.currItem.fitRatio);
- }
-
- styleObj[_transformKey] = _translatePrefix + x + 'px, ' + y + 'px' + _translateSufix + ' scale(' + zoom + ')';
- },
- _applyCurrentZoomPan = function( allowRenderResolution ) {
- if(_currZoomElementStyle) {
- if(allowRenderResolution) {
- if(_currZoomLevel > self.currItem.fitRatio) {
- if(!_renderMaxResolution) {
- _setImageSize(self.currItem, false, true);
- _renderMaxResolution = true;
- }
- } else {
- if(_renderMaxResolution) {
- _setImageSize(self.currItem);
- _renderMaxResolution = false;
- }
- }
- }
-
- _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,
- item);
- }
- },
- _setTranslateX = function(x, elStyle) {
- elStyle[_transformKey] = _translatePrefix + x + 'px, 0px' + _translateSufix;
- },
- _moveMainScroll = function(x, dragging) {
- if(!_options.loop && dragging) {
- var newSlideIndexOffset = _currentItemIndex + (_slideSize.x * _currPositionIndex - x) / _slideSize.x,
- 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 orientationchange', self);
- _shout('bindEvents');
- },
- _unbindEvents = function() {
- framework.unbind(window, 'resize scroll orientationchange', 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);
- }
- clearTimeout(_orientationChangeTimeout);
- _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();
- }
- },
- _updatePageScrollOffset = 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;
- _shout('updateScrollOffset', _offset);
- },
- applyZoomPan: function(zoomLevel,panX,panY,allowRenderResolution) {
- _panOffset.x = panX;
- _panOffset.y = panY;
- _currZoomLevel = zoomLevel;
- _applyCurrentZoomPan( allowRenderResolution );
- },
- init: function() {
- if(_isOpen || _isDestroying) {
- return;
- }
- var i;
- self.framework = framework; // basic functionality
- 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,
- // Fixes: iOS 10.3 resize event
- // does not update scrollWrap.clientWidth instantly after resize
- // https://github.com/dimsemenov/PhotoSwipe/issues/1315
- orientationchange: function() {
- clearTimeout(_orientationChangeTimeout);
- _orientationChangeTimeout = setTimeout(function() {
- if(_viewportSize.x !== self.scrollWrap.clientWidth) {
- self.updateSize();
- }
- }, 500);
- },
- scroll: _updatePageScrollOffset,
- 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;
- }
-
- template.setAttribute('aria-hidden', 'false');
- if(_options.modal) {
- 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');
- },
- // Close 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 the gallery (unbinds events, cleans up intervals and timeouts to avoid memory leaks)
- destroy: function() {
- _shout('destroy');
- if(_showOrHideTimeout) {
- clearTimeout(_showOrHideTimeout);
- }
-
- template.setAttribute('aria-hidden', 'true');
- template.className = _initalClassName;
- if(_updateSizeInterval) {
- clearInterval(_updateSizeInterval);
- }
- framework.unbind(self.scrollWrap, _downEvents, self);
- // we unbind scroll 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 );
- _renderMaxResolution = false;
-
- _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 );
- _setImageSize(prevItem);
- _applyZoomPanToItem( prevItem );
- }
- }
- // reset diff after update
- _indexDiff = 0;
- self.updateCurrZoomItem();
- _prevItemIndex = _currentItemIndex;
- _shout('afterChange');
-
- },
- updateSize: function(force) {
-
- if(!_isFixedPosition && _options.modal) {
- 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;
- _updatePageScrollOffset();
- _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);
- _setImageSize(item);
- _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( true );
- }
-
- _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);
- 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( now === 1 );
- };
- 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 || el === document) {
- return false;
- }
- // don't search elements above pswp__scroll-wrap
- if(el.getAttribute('class') && el.getAttribute('class').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) {
- if(_mainScrollPos.x !== _slideSize.x * _currPositionIndex) {
- // if main scroll position is shifted – direction is always horizontal
- _direction = 'h';
- } else {
- 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 < _options.verticalDragRange) {
- 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, 200, 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) {
- _shout('initialZoom' + (out ? 'Out' : 'In') );
- _currZoomLevel = item.initialZoomLevel;
- _equalizePoints(_panOffset, item.initialPosition );
- _applyCurrentZoomPan();
- template.style.opacity = out ? 0 : 1;
- _applyBgOpacity(1);
- if(duration) {
- setTimeout(function() {
- onComplete();
- }, duration);
- } else {
- onComplete();
- }
- 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;
- }
-
- },
-
- _appendImage = function(index, item, baseDiv, img, preventAnimation, keepPlaceholder) {
-
- if(item.loadError) {
- return;
- }
- if(img) {
- item.imageAppended = true;
- _setImageSize(item, img, (item === self.currItem && _renderMaxResolution) );
-
- baseDiv.appendChild(img);
- if(keepPlaceholder) {
- setTimeout(function() {
- if(item && item.loaded && item.placeholder) {
- item.placeholder.style.display = 'none';
- item.placeholder = null;
- }
- }, 500);
- }
- }
- },
-
- _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(item, img, maxRes) {
- if(!item.src) {
- return;
- }
- if(!img) {
- img = item.container.lastChild;
- }
- var w = maxRes ? item.w : Math.round(item.w * item.fitRatio),
- h = maxRes ? item.h : Math.round(item.h * item.fitRatio);
-
- if(item.placeholder && !item.loaded) {
- item.placeholder.style.width = w + 'px';
- item.placeholder.style.height = h + 'px';
- }
- 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, false, poolItem.clearPlaceholder);
- }
- }
- _imagesToAppendPool = [];
- }
- };
-
- _registerModule('Controller', {
- publicMethods: {
- lazyLoadItem: function(index) {
- index = _getLoopedId(index);
- var item = _getItemAt(index);
- if(!item || ((item.loaded || item.loading) && !_itemsNeedUpdate)) {
- 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);
- _calculateItemSize(item, _viewportSize);
-
- if(item.src && !item.loadError && !item.loaded) {
- item.loadComplete = function(item) {
- // gallery closed before image finished loading
- if(!_isOpen) {
- return;
- }
- // 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,
- clearPlaceholder:true
- });
- } else {
- _appendImage(index, item, baseDiv, item.img, _mainScrollAnimating || _initialZoomRunning, true);
- }
- } 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(item, placeholder);
- 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.opacity = 1;
- img.src = item.src;
- _setImageSize(item, img);
- _appendImage(index, item, baseDiv, img, true);
- }
-
- 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.modal ) {
- if (!_options.closeOnScroll || _numAnimations || _isDragging) {
- e.preventDefault();
- } else if(_transformKey && Math.abs(e.deltaY) > 2) {
- // close PhotoSwipe
- // if browser supports transforms & scroll changed enough
- _closedByScroll = true;
- self.close();
- }
- }
- return true;
- }
- // 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;
- }
- _calculatePanBounds(_currZoomLevel, true);
- var newPanX = _panOffset.x - _wheelDelta.x,
- newPanY = _panOffset.y - _wheelDelta.y;
- // only prevent scrolling in nonmodal mode when not at edges
- if (_options.modal ||
- (
- newPanX <= _currPanBounds.min.x && newPanX >= _currPanBounds.max.x &&
- newPanY <= _currPanBounds.min.y && newPanY >= _currPanBounds.max.y
- ) ) {
- e.preventDefault();
- }
- // TODO: use rAF instead of mousewheel?
- self.panTo(newPanX, newPanY);
- },
- toggleDesktopZoom: function(centerPoint) {
- centerPoint = centerPoint || {x:_viewportSize.x/2 + _offset.x, y:_viewportSize.y/2 + _offset.y };
- 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 i, vars = hash.split('&');
- for (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];
- }
- if(_options.galleryPIDs) {
- // detect custom pid in hash and search for it among the items collection
- var searchfor = params.pid;
- params.pid = 0; // if custom pid cannot be found, fallback to the first item
- for(i = 0; i < _items.length; i++) {
- if(_items[i].pid === searchfor) {
- params.pid = i;
- break;
- }
- }
- } else {
- 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 pid = (_currentItemIndex + 1);
- var item = _getItemAt( _currentItemIndex );
- if(item.hasOwnProperty('pid')) {
- // carry forward any custom pid assigned to the item
- pid = item.pid;
- }
- var newHash = _initialHash + '&' + 'gid=' + _options.galleryUID + '&' + 'pid=' + pid;
- 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;
- });
|