1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718 |
- /*! PhotoSwipe - v4.1.1 - 2015-12-24
- * http://photoswipe.com
- * Copyright (c) 2015 Dmitry Semenov; */
- (function (root, factory) {
- if (typeof define === 'function' && define.amd) {
- define(factory);
- } else if (typeof exports === 'object') {
- module.exports = factory();
- } else {
- root.PhotoSwipe = factory();
- }
- })(this, function () {
- 'use strict';
- var PhotoSwipe = function(template, UiClass, items, options){
- /*>>framework-bridge*/
- /**
- *
- * Set of generic functions used by gallery.
- *
- * You're free to modify anything here as long as functionality is kept.
- *
- */
- var framework = {
- features: null,
- bind: function(target, type, listener, unbind) {
- var methodName = (unbind ? 'remove' : 'add') + 'EventListener';
- type = type.split(' ');
- for(var i = 0; i < type.length; i++) {
- if(type[i]) {
- target[methodName]( type[i], listener, false);
- }
- }
- },
- isArray: function(obj) {
- return (obj instanceof Array);
- },
- createEl: function(classes, tag) {
- var el = document.createElement(tag || 'div');
- if(classes) {
- el.className = classes;
- }
- return el;
- },
- getScrollY: function() {
- var yOffset = window.pageYOffset;
- return yOffset !== undefined ? yOffset : document.documentElement.scrollTop;
- },
- unbind: function(target, type, listener) {
- framework.bind(target,type,listener,true);
- },
- removeClass: function(el, className) {
- var reg = new RegExp('(\\s|^)' + className + '(\\s|$)');
- el.className = el.className.replace(reg, ' ').replace(/^\s\s*/, '').replace(/\s\s*$/, '');
- },
- addClass: function(el, className) {
- if( !framework.hasClass(el,className) ) {
- el.className += (el.className ? ' ' : '') + className;
- }
- },
- hasClass: function(el, className) {
- return el.className && new RegExp('(^|\\s)' + className + '(\\s|$)').test(el.className);
- },
- getChildByClass: function(parentEl, childClassName) {
- var node = parentEl.firstChild;
- while(node) {
- if( framework.hasClass(node, childClassName) ) {
- return node;
- }
- node = node.nextSibling;
- }
- },
- arraySearch: function(array, value, key) {
- var i = array.length;
- while(i--) {
- if(array[i][key] === value) {
- return i;
- }
- }
- return -1;
- },
- extend: function(o1, o2, preventOverwrite) {
- for (var prop in o2) {
- if (o2.hasOwnProperty(prop)) {
- if(preventOverwrite && o1.hasOwnProperty(prop)) {
- continue;
- }
- o1[prop] = o2[prop];
- }
- }
- },
- easing: {
- sine: {
- out: function(k) {
- return Math.sin(k * (Math.PI / 2));
- },
- inOut: function(k) {
- return - (Math.cos(Math.PI * k) - 1) / 2;
- }
- },
- cubic: {
- out: function(k) {
- return --k * k * k + 1;
- }
- }
- /*
- elastic: {
- out: function ( k ) {
- var s, a = 0.1, p = 0.4;
- if ( k === 0 ) return 0;
- if ( k === 1 ) return 1;
- if ( !a || a < 1 ) { a = 1; s = p / 4; }
- else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI );
- return ( a * Math.pow( 2, - 10 * k) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) + 1 );
- },
- },
- back: {
- out: function ( k ) {
- var s = 1.70158;
- return --k * k * ( ( s + 1 ) * k + s ) + 1;
- }
- }
- */
- },
- /**
- *
- * @return {object}
- *
- * {
- * raf : request animation frame function
- * caf : cancel animation frame function
- * transfrom : transform property key (with vendor), or null if not supported
- * oldIE : IE8 or below
- * }
- *
- */
- detectFeatures: function() {
- if(framework.features) {
- return framework.features;
- }
- var helperEl = framework.createEl(),
- helperStyle = helperEl.style,
- vendor = '',
- features = {};
- // IE8 and below
- features.oldIE = document.all && !document.addEventListener;
- features.touch = 'ontouchstart' in window;
- if(window.requestAnimationFrame) {
- features.raf = window.requestAnimationFrame;
- features.caf = window.cancelAnimationFrame;
- }
- features.pointerEvent = navigator.pointerEnabled || navigator.msPointerEnabled;
- // fix false-positive detection of old Android in new IE
- // (IE11 ua string contains "Android 4.0")
-
- if(!features.pointerEvent) {
- var ua = navigator.userAgent;
- // Detect if device is iPhone or iPod and if it's older than iOS 8
- // http://stackoverflow.com/a/14223920
- //
- // This detection is made because of buggy top/bottom toolbars
- // that don't trigger window.resize event.
- // For more info refer to _isFixedPosition variable in core.js
- if (/iP(hone|od)/.test(navigator.platform)) {
- var v = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/);
- if(v && v.length > 0) {
- v = parseInt(v[1], 10);
- if(v >= 1 && v < 8 ) {
- features.isOldIOSPhone = true;
- }
- }
- }
- // Detect old Android (before KitKat)
- // due to bugs related to position:fixed
- // http://stackoverflow.com/questions/7184573/pick-up-the-android-version-in-the-browser-by-javascript
-
- var match = ua.match(/Android\s([0-9\.]*)/);
- var androidversion = match ? match[1] : 0;
- androidversion = parseFloat(androidversion);
- if(androidversion >= 1 ) {
- if(androidversion < 4.4) {
- features.isOldAndroid = true; // for fixed position bug & performance
- }
- features.androidVersion = androidversion; // for touchend bug
- }
- features.isMobileOpera = /opera mini|opera mobi/i.test(ua);
- // p.s. yes, yes, UA sniffing is bad, propose your solution for above bugs.
- }
-
- var styleChecks = ['transform', 'perspective', 'animationName'],
- vendors = ['', 'webkit','Moz','ms','O'],
- styleCheckItem,
- styleName;
- for(var i = 0; i < 4; i++) {
- vendor = vendors[i];
- for(var a = 0; a < 3; a++) {
- styleCheckItem = styleChecks[a];
- // uppercase first letter of property name, if vendor is present
- styleName = vendor + (vendor ?
- styleCheckItem.charAt(0).toUpperCase() + styleCheckItem.slice(1) :
- styleCheckItem);
-
- if(!features[styleCheckItem] && styleName in helperStyle ) {
- features[styleCheckItem] = styleName;
- }
- }
- if(vendor && !features.raf) {
- vendor = vendor.toLowerCase();
- features.raf = window[vendor+'RequestAnimationFrame'];
- if(features.raf) {
- features.caf = window[vendor+'CancelAnimationFrame'] ||
- window[vendor+'CancelRequestAnimationFrame'];
- }
- }
- }
-
- if(!features.raf) {
- var lastTime = 0;
- features.raf = function(fn) {
- var currTime = new Date().getTime();
- var timeToCall = Math.max(0, 16 - (currTime - lastTime));
- var id = window.setTimeout(function() { fn(currTime + timeToCall); }, timeToCall);
- lastTime = currTime + timeToCall;
- return id;
- };
- features.caf = function(id) { clearTimeout(id); };
- }
- // Detect SVG support
- features.svg = !!document.createElementNS &&
- !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect;
- framework.features = features;
- return features;
- }
- };
- framework.detectFeatures();
- // Override addEventListener for old versions of IE
- if(framework.features.oldIE) {
- framework.bind = function(target, type, listener, unbind) {
-
- type = type.split(' ');
- var methodName = (unbind ? 'detach' : 'attach') + 'Event',
- evName,
- _handleEv = function() {
- listener.handleEvent.call(listener);
- };
- for(var i = 0; i < type.length; i++) {
- evName = type[i];
- if(evName) {
- if(typeof listener === 'object' && listener.handleEvent) {
- if(!unbind) {
- listener['oldIE' + evName] = _handleEv;
- } else {
- if(!listener['oldIE' + evName]) {
- return false;
- }
- }
- target[methodName]( 'on' + evName, listener['oldIE' + evName]);
- } else {
- target[methodName]( 'on' + evName, listener);
- }
- }
- }
- };
-
- }
- /*>>framework-bridge*/
- /*>>core*/
- //function(template, UiClass, items, options)
- var self = this;
- /**
- * Static vars, don't change unless you know what you're doing.
- */
- var DOUBLE_TAP_RADIUS = 25,
- NUM_HOLDERS = 3;
- /**
- * Options
- */
- var _options = {
- allowPanToNext:true,
- spacing: 0.12,
- bgOpacity: 1,
- mouseUsed: false,
- loop: true,
- pinchToClose: true,
- closeOnScroll: true,
- closeOnVerticalDrag: true,
- 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,
- // 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', self);
- _shout('bindEvents');
- },
- _unbindEvents = function() {
- framework.unbind(window, 'resize', self);
- framework.unbind(window, 'scroll', _globalEventHandlers.scroll);
- framework.unbind(document, 'keydown', self);
- framework.unbind(document, 'mousemove', _onFirstMouseMove);
- if(_features.transform) {
- framework.unbind(self.scrollWrap, 'click', self);
- }
- if(_isDragging) {
- framework.unbind(window, _upMoveEvents, self);
- }
- _shout('unbindEvents');
- },
-
- _calculatePanBounds = function(zoomLevel, update) {
- var bounds = _calculateItemSize( self.currItem, _viewportSize, zoomLevel );
- if(update) {
- _currPanBounds = bounds;
- }
- return bounds;
- },
-
- _getMinZoomLevel = function(item) {
- if(!item) {
- item = self.currItem;
- }
- return item.initialZoomLevel;
- },
- _getMaxZoomLevel = function(item) {
- if(!item) {
- item = self.currItem;
- }
- return item.w > 0 ? _options.maxSpreadZoom : 1;
- },
- // Return true if offset is out of the bounds
- _modifyDestPanOffset = function(axis, destPanBounds, destPanOffset, destZoomLevel) {
- if(destZoomLevel === self.currItem.initialZoomLevel) {
- destPanOffset[axis] = self.currItem.initialPosition[axis];
- return true;
- } else {
- destPanOffset[axis] = _calculatePanOffset(axis, destZoomLevel);
- if(destPanOffset[axis] > destPanBounds.min[axis]) {
- destPanOffset[axis] = destPanBounds.min[axis];
- return true;
- } else if(destPanOffset[axis] < destPanBounds.max[axis] ) {
- destPanOffset[axis] = destPanBounds.max[axis];
- return true;
- }
- }
- return false;
- },
- _setupTransforms = function() {
- if(_transformKey) {
- // setup 3d transforms
- var allow3dTransform = _features.perspective && !_likelyTouchDevice;
- _translatePrefix = 'translate' + (allow3dTransform ? '3d(' : '(');
- _translateSufix = _features.perspective ? ', 0px)' : ')';
- return;
- }
- // Override zoom/pan/move functions in case old browser is used (most likely IE)
- // (so they use left/top/width/height, instead of CSS transform)
-
- _transformKey = 'left';
- framework.addClass(template, 'pswp--ie');
- _setTranslateX = function(x, elStyle) {
- elStyle.left = x + 'px';
- };
- _applyZoomPanToItem = function(item) {
- var zoomRatio = item.fitRatio > 1 ? 1 : item.fitRatio,
- s = item.container.style,
- w = zoomRatio * item.w,
- h = zoomRatio * item.h;
- s.width = w + 'px';
- s.height = h + 'px';
- s.left = item.initialPosition.x + 'px';
- s.top = item.initialPosition.y + 'px';
- };
- _applyCurrentZoomPan = function() {
- if(_currZoomElementStyle) {
- var s = _currZoomElementStyle,
- item = self.currItem,
- zoomRatio = item.fitRatio > 1 ? 1 : item.fitRatio,
- w = zoomRatio * item.w,
- h = zoomRatio * item.h;
- s.width = w + 'px';
- s.height = h + 'px';
- s.left = _panOffset.x + 'px';
- s.top = _panOffset.y + 'px';
- }
-
- };
- },
- _onKeyDown = function(e) {
- var keydownAction = '';
- if(_options.escKey && e.keyCode === 27) {
- keydownAction = 'close';
- } else if(_options.arrowKeys) {
- if(e.keyCode === 37) {
- keydownAction = 'prev';
- } else if(e.keyCode === 39) {
- keydownAction = 'next';
- }
- }
- if(keydownAction) {
- // don't do anything if special key pressed to prevent from overriding default browser actions
- // e.g. in Chrome on Mac cmd+arrow-left returns to previous page
- if( !e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey ) {
- if(e.preventDefault) {
- e.preventDefault();
- } else {
- e.returnValue = false;
- }
- self[keydownAction]();
- }
- }
- },
- _onGlobalClick = function(e) {
- if(!e) {
- return;
- }
- // don't allow click event to pass through when triggering after drag or some other gesture
- if(_moved || _zoomStarted || _mainScrollAnimating || _verticalDragInitiated) {
- e.preventDefault();
- e.stopPropagation();
- }
- },
- _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,
- 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;
- });
|