PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /opt/wp/plugins/jetpack/modules/carousel
Viewing File: /opt/wp/plugins/jetpack/modules/carousel/jetpack-carousel.js
/* global wpcom, jetpackCarouselStrings, DocumentTouch */ ( function () { 'use strict'; var swiper; ///////////////////////////////////// // Utility functions ///////////////////////////////////// var util = ( function () { var noop = function () {}; function texturize( text ) { // Ensure we get a string. text = text + ''; text = text.replace( /'/g, '&#8217;' ).replace( /&#039;/g, '&#8217;' ); text = text .replace( /"/g, '&#8221;' ) .replace( /&#034;/g, '&#8221;' ) .replace( /&quot;/g, '&#8221;' ) .replace( /[\u201D]/g, '&#8221;' ); // Untexturize allowed HTML tags params double-quotes. text = text.replace( /([\w]+)=&#[\d]+;(.+?)&#[\d]+;/g, '$1="$2"' ); return text.trim(); } function applyReplacements( text, replacements ) { if ( ! text ) { return; } if ( ! replacements ) { return text; } return text.replace( /{(\d+)}/g, function ( match, number ) { return typeof replacements[ number ] !== 'undefined' ? replacements[ number ] : match; } ); } function getBackgroundImage( imgEl ) { var canvas = document.createElement( 'canvas' ), context = canvas.getContext && canvas.getContext( '2d' ); if ( ! imgEl ) { return; } context.filter = 'blur(20px) '; context.drawImage( imgEl, 0, 0 ); var url = canvas.toDataURL( 'image/png' ); canvas = null; return url; } return { noop: noop, texturize: texturize, applyReplacements: applyReplacements, getBackgroundImage: getBackgroundImage, }; } )(); ///////////////////////////////////// // DOM-related utility functions ///////////////////////////////////// var domUtil = ( function () { // Helper matches function (not a polyfill), compatible with IE 11. function matches( el, sel ) { if ( Element.prototype.matches ) { return el.matches( sel ); } if ( Element.prototype.msMatchesSelector ) { return el.msMatchesSelector( sel ); } } // Helper closest parent node function (not a polyfill) based on // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill function closest( el, sel ) { if ( el.closest ) { return el.closest( sel ); } var current = el; do { if ( matches( current, sel ) ) { return current; } current = current.parentElement || current.parentNode; } while ( current !== null && current.nodeType === 1 ); return null; } function hide( el ) { if ( el ) { el.style.display = 'none'; } } function show( el ) { if ( el ) { // Everything we show and hide in Carousel is currently a block, // so we can make this really straightforward. el.style.display = 'block'; } } function fade( el, start, end, callback ) { if ( ! el ) { return callback(); } // Prepare for transition. // Ensure the item is in the render tree, in its initial state. el.style.removeProperty( 'display' ); el.style.opacity = start; el.style.transition = 'opacity 0.2s'; el.style.pointerEvents = 'none'; var finished = function ( e ) { if ( e.target === el && e.propertyName === 'opacity' ) { el.style.removeProperty( 'transition' ); el.style.removeProperty( 'opacity' ); el.style.removeProperty( 'pointer-events' ); el.removeEventListener( 'transitionend', finished ); el.removeEventListener( 'transitioncancel', finished ); callback(); } }; requestAnimationFrame( function () { // Double rAF for browser compatibility. requestAnimationFrame( function () { el.addEventListener( 'transitionend', finished ); el.addEventListener( 'transitioncancel', finished ); // Trigger transition. el.style.opacity = end; } ); } ); } function fadeIn( el, callback ) { callback = callback || util.noop; fade( el, '0', '1', callback ); } function fadeOut( el, callback ) { callback = callback || util.noop; fade( el, '1', '0', function () { if ( el ) { el.style.display = 'none'; } callback(); } ); } function emitEvent( el, type, detail ) { var e; try { e = new CustomEvent( type, { bubbles: true, cancelable: true, detail: detail || null, } ); } catch ( err ) { e = document.createEvent( 'CustomEvent' ); e.initCustomEvent( type, true, true, detail || null ); } el.dispatchEvent( e ); } // From: https://easings.net/#easeInOutQuad function easeInOutQuad( num ) { return num < 0.5 ? 2 * num * num : 1 - Math.pow( -2 * num + 2, 2 ) / 2; } function getFooterClearance( container ) { var footer = container.querySelector( '.jp-carousel-info-footer' ); var infoArea = container.querySelector( '.jp-carousel-info-extra' ); var contentArea = container.querySelector( '.jp-carousel-info-content-wrapper' ); if ( footer && infoArea && contentArea ) { var styles = window.getComputedStyle( infoArea ); var padding = parseInt( styles.paddingTop, 10 ) + parseInt( styles.paddingBottom, 10 ); padding = isNaN( padding ) ? 0 : padding; return contentArea.offsetHeight + footer.offsetHeight + padding; } return 0; } function isTouch() { return ( 'ontouchstart' in window || ( window.DocumentTouch && document instanceof DocumentTouch ) ); } function scrollToElement( el, container, callback ) { if ( ! el || ! container ) { if ( callback ) { return callback(); } return; } // For iOS Safari compatibility, use JS to set the minimum height. var infoArea = container.querySelector( '.jp-carousel-info-extra' ); if ( infoArea ) { // 64px is the same height as `.jp-carousel-info-footer` in the CSS. infoArea.style.minHeight = window.innerHeight - 64 + 'px'; } var isScrolling = true; var startTime = Date.now(); var duration = 300; var originalPosition = container.scrollTop; var targetPosition = Math.max( 0, el.offsetTop - Math.max( 0, window.innerHeight - getFooterClearance( container ) ) ); var distance = targetPosition - container.scrollTop; distance = Math.min( distance, container.scrollHeight - window.innerHeight ); function stopScroll() { isScrolling = false; } function runScroll() { var now = Date.now(); var progress = easeInOutQuad( ( now - startTime ) / duration ); progress = progress > 1 ? 1 : progress; var newVal = progress * distance; container.scrollTop = originalPosition + newVal; if ( now <= startTime + duration && isScrolling ) { return requestAnimationFrame( runScroll ); } if ( callback ) { callback(); } if ( infoArea ) { infoArea.style.minHeight = ''; } isScrolling = false; container.removeEventListener( 'wheel', stopScroll ); } // Allow scroll to be cancelled by user interaction. container.addEventListener( 'wheel', stopScroll ); runScroll(); } function getJSONAttribute( el, attr ) { if ( ! el || ! el.hasAttribute( attr ) ) { return undefined; } try { return JSON.parse( el.getAttribute( attr ) ); } catch ( e ) { return undefined; } } function convertToPlainText( html ) { var dummy = document.createElement( 'div' ); dummy.textContent = html; return dummy.innerHTML; } function stripHTML( text ) { return text.replace( /<[^>]*>?/gm, '' ); } return { closest: closest, matches: matches, hide: hide, show: show, fadeIn: fadeIn, fadeOut: fadeOut, scrollToElement: scrollToElement, getJSONAttribute: getJSONAttribute, convertToPlainText: convertToPlainText, stripHTML: stripHTML, emitEvent: emitEvent, isTouch: isTouch, }; } )(); ///////////////////////////////////// // Carousel implementation ///////////////////////////////////// function init() { var commentInterval; var screenPadding; var originalOverflow; var originalHOverflow; var scrollPos; var lastKnownLocationHash = ''; var isUserTyping = false; var gallerySelector = 'div.gallery, div.tiled-gallery, ul.wp-block-gallery, ul.blocks-gallery-grid, ' + 'figure.wp-block-gallery.has-nested-images, div.wp-block-jetpack-tiled-gallery, a.single-image-gallery'; // Selector for items within a gallery or tiled gallery. var galleryItemSelector = '.gallery-item, .tiled-gallery-item, .blocks-gallery-item, ' + ' .tiled-gallery__item'; // Selector for all items including single images. var itemSelector = galleryItemSelector + ', .wp-block-image'; var carousel = {}; var stat = typeof wpcom !== 'undefined' && wpcom.carousel && wpcom.carousel.stat ? wpcom.carousel.stat : util.noop; var pageview = typeof wpcom !== 'undefined' && wpcom.carousel && wpcom.carousel.pageview ? wpcom.carousel.pageview : util.noop; function handleKeyboardEvent( e ) { if ( ! isUserTyping ) { switch ( e.which ) { case 38: // up e.preventDefault(); carousel.overlay.scrollTop -= 100; break; case 40: // down e.preventDefault(); carousel.overlay.scrollTop += 100; break; case 39: // right e.preventDefault(); swiper.slideNext(); break; case 37: // left case 8: // backspace e.preventDefault(); swiper.slidePrev(); break; case 27: // escape e.preventDefault(); closeCarousel(); break; default: break; } } } function disableKeyboardNavigation() { isUserTyping = true; } function enableKeyboardNavigation() { isUserTyping = false; } function calculatePadding() { var baseScreenPadding = 110; screenPadding = baseScreenPadding; if ( window.innerWidth <= 760 ) { screenPadding = Math.round( ( window.innerWidth / 760 ) * baseScreenPadding ); if ( screenPadding < 40 && domUtil.isTouch() ) { screenPadding = 0; } } } function initializeCarousel() { if ( ! carousel.overlay ) { carousel.overlay = document.querySelector( '.jp-carousel-overlay' ); carousel.container = carousel.overlay.querySelector( '.jp-carousel-wrap' ); carousel.gallery = carousel.container.querySelector( '.jp-carousel' ); carousel.info = carousel.overlay.querySelector( '.jp-carousel-info' ); carousel.caption = carousel.info.querySelector( '.jp-carousel-caption' ); carousel.commentField = carousel.overlay.querySelector( '#jp-carousel-comment-form-comment-field' ); carousel.emailField = carousel.overlay.querySelector( '#jp-carousel-comment-form-email-field' ); carousel.authorField = carousel.overlay.querySelector( '#jp-carousel-comment-form-author-field' ); carousel.urlField = carousel.overlay.querySelector( '#jp-carousel-comment-form-url-field' ); calculatePadding(); [ carousel.commentField, carousel.emailField, carousel.authorField, carousel.urlField, ].forEach( function ( field ) { if ( field ) { field.addEventListener( 'focus', disableKeyboardNavigation ); field.addEventListener( 'blur', enableKeyboardNavigation ); } } ); carousel.overlay.addEventListener( 'click', function ( e ) { var target = e.target; var isTargetCloseHint = !! domUtil.closest( target, '.jp-carousel-close-hint' ); var isSmallScreen = !! window.matchMedia( '(max-device-width: 760px)' ).matches; if ( target === carousel.overlay ) { if ( isSmallScreen ) { return; } else { closeCarousel(); } } else if ( isTargetCloseHint ) { closeCarousel(); } else if ( target.classList.contains( 'jp-carousel-image-download' ) ) { stat( 'download_original_click' ); } else if ( target.classList.contains( 'jp-carousel-comment-login' ) ) { handleCommentLoginClick( e ); } else if ( domUtil.closest( target, '#jp-carousel-comment-form-container' ) ) { handleCommentFormClick( e ); } else if ( domUtil.closest( target, '.jp-carousel-photo-icons-container' ) || target.classList.contains( 'jp-carousel-photo-title' ) ) { handleFooterElementClick( e ); } else if ( ! domUtil.closest( target, '.jp-carousel-info' ) ) { return; } } ); window.addEventListener( 'keydown', handleKeyboardEvent ); carousel.overlay.addEventListener( 'jp_carousel.afterOpen', function () { enableKeyboardNavigation(); // Don't show navigation if there's only one image. if ( carousel.slides.length <= 1 ) { return; } // Show dot pagination if slide count is <= 5, otherwise show n/total. if ( carousel.slides.length <= 5 ) { domUtil.show( carousel.info.querySelector( '.jp-swiper-pagination' ) ); } else { domUtil.show( carousel.info.querySelector( '.jp-carousel-pagination' ) ); } } ); carousel.overlay.addEventListener( 'jp_carousel.beforeClose', function () { disableKeyboardNavigation(); // Fixes some themes where closing carousel brings view back to top. document.documentElement.style.removeProperty( 'height' ); // If we disable the swiper (because there's only one image) // we have to re-enable it here again as Swiper doesn't, for some reason, // show the navigation buttons again after reinitialization. if ( swiper ) { swiper.enable(); } // Hide pagination. domUtil.hide( carousel.info.querySelector( '.jp-swiper-pagination' ) ); domUtil.hide( carousel.info.querySelector( '.jp-carousel-pagination' ) ); } ); carousel.overlay.addEventListener( 'jp_carousel.afterClose', function () { // don't force the browser back when the carousel closes. if ( window.history.pushState ) { history.pushState( '', document.title, window.location.pathname + window.location.search ); } else { window.location.href = ''; } lastKnownLocationHash = ''; carousel.isOpen = false; } ); // Prevent native browser zooming carousel.overlay.addEventListener( 'touchstart', function ( e ) { if ( e.touches.length > 1 ) { e.preventDefault(); } } ); } } function handleCommentLoginClick() { var slide = carousel.currentSlide; var attachmentId = slide ? slide.attrs.attachmentId : '0'; window.location.href = jetpackCarouselStrings.login_url + '%23jp-carousel-' + attachmentId; } function updatePostResults( msg, isSuccess ) { var results = carousel.overlay.querySelector( '#jp-carousel-comment-post-results' ); var elClass = 'jp-carousel-comment-post-' + ( isSuccess ? 'success' : 'error' ); results.innerHTML = '<span class="' + elClass + '">' + msg + '</span>'; domUtil.hide( carousel.overlay.querySelector( '#jp-carousel-comment-form-spinner' ) ); carousel.overlay .querySelector( '#jp-carousel-comment-form' ) .classList.remove( 'jp-carousel-is-disabled' ); domUtil.show( results ); } function handleCommentFormClick( e ) { var target = e.target; var data = domUtil.getJSONAttribute( carousel.container, 'data-carousel-extra' ) || {}; var attachmentId = carousel.currentSlide.attrs.attachmentId; var wrapper = document.querySelector( '#jp-carousel-comment-form-submit-and-info-wrapper' ); var spinner = document.querySelector( '#jp-carousel-comment-form-spinner' ); var submit = document.querySelector( '#jp-carousel-comment-form-button-submit' ); var form = document.querySelector( '#jp-carousel-comment-form' ); if ( carousel.commentField && carousel.commentField.getAttribute( 'id' ) === target.getAttribute( 'id' ) ) { // For first page load disableKeyboardNavigation(); domUtil.show( wrapper ); } else if ( domUtil.matches( target, 'input[type="submit"]' ) ) { e.preventDefault(); e.stopPropagation(); domUtil.show( spinner ); form.classList.add( 'jp-carousel-is-disabled' ); var ajaxData = { action: 'post_attachment_comment', nonce: jetpackCarouselStrings.nonce, blog_id: data.blog_id, id: attachmentId, comment: carousel.commentField.value, }; if ( ! ajaxData.comment.length ) { updatePostResults( jetpackCarouselStrings.no_comment_text, false ); return; } if ( Number( jetpackCarouselStrings.is_logged_in ) !== 1 ) { ajaxData.email = carousel.emailField.value; ajaxData.author = carousel.authorField.value; ajaxData.url = carousel.urlField.value; if ( Number( jetpackCarouselStrings.require_name_email ) === 1 ) { if ( ! ajaxData.email.length || ! ajaxData.email.match( '@' ) ) { updatePostResults( jetpackCarouselStrings.no_comment_email, false ); return; } else if ( ! ajaxData.author.length ) { updatePostResults( jetpackCarouselStrings.no_comment_author, false ); return; } } } var xhr = new XMLHttpRequest(); xhr.open( 'POST', jetpackCarouselStrings.ajaxurl, true ); xhr.setRequestHeader( 'X-Requested-With', 'XMLHttpRequest' ); xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8' ); xhr.onreadystatechange = function () { if ( this.readyState === XMLHttpRequest.DONE && this.status >= 200 && this.status < 300 ) { var response; try { response = JSON.parse( this.response ); } catch ( error ) { updatePostResults( jetpackCarouselStrings.comment_post_error, false ); return; } if ( response.comment_status === 'approved' ) { updatePostResults( jetpackCarouselStrings.comment_approved, true ); } else if ( response.comment_status === 'unapproved' ) { updatePostResults( jetpackCarouselStrings.comment_unapproved, true ); } else { // 'deleted', 'spam', false updatePostResults( jetpackCarouselStrings.comment_post_error, false ); } clearCommentTextAreaValue(); fetchComments( attachmentId ); submit.value = jetpackCarouselStrings.post_comment; domUtil.hide( spinner ); form.classList.remove( 'jp-carousel-is-disabled' ); } else { // TODO: Add error handling and display here updatePostResults( jetpackCarouselStrings.comment_post_error, false ); } }; var params = []; for ( var item in ajaxData ) { if ( item ) { // Encode each form element into a URI-compatible string. var encoded = encodeURIComponent( item ) + '=' + encodeURIComponent( ajaxData[ item ] ); // In x-www-form-urlencoded, spaces should be `+`, not `%20`. params.push( encoded.replace( /%20/g, '+' ) ); } } var encodedData = params.join( '&' ); xhr.send( encodedData ); } } /** * Handles clicks to icons and other action elements in the icon container. * @param {MouseEvent|TouchEvent|KeyBoardEvent} Event object. */ function handleFooterElementClick( e ) { e.preventDefault(); var target = e.target; var extraInfoContainer = carousel.info.querySelector( '.jp-carousel-info-extra' ); var photoMetaContainer = carousel.info.querySelector( '.jp-carousel-image-meta' ); var commentsContainer = carousel.info.querySelector( '.jp-carousel-comments-wrapper' ); var infoIcon = carousel.info.querySelector( '.jp-carousel-icon-info' ); var commentsIcon = carousel.info.querySelector( '.jp-carousel-icon-comments' ); function handleInfoToggle() { if ( commentsIcon ) { commentsIcon.classList.remove( 'jp-carousel-selected' ); } infoIcon.classList.toggle( 'jp-carousel-selected' ); if ( commentsContainer ) { commentsContainer.classList.remove( 'jp-carousel-show' ); } if ( photoMetaContainer ) { photoMetaContainer.classList.toggle( 'jp-carousel-show' ); if ( photoMetaContainer.classList.contains( 'jp-carousel-show' ) ) { extraInfoContainer.classList.add( 'jp-carousel-show' ); } else { extraInfoContainer.classList.remove( 'jp-carousel-show' ); } } } function handleCommentToggle() { if ( infoIcon ) { infoIcon.classList.remove( 'jp-carousel-selected' ); } commentsIcon.classList.toggle( 'jp-carousel-selected' ); if ( photoMetaContainer ) { photoMetaContainer.classList.remove( 'jp-carousel-show' ); } if ( commentsContainer ) { commentsContainer.classList.toggle( 'jp-carousel-show' ); if ( commentsContainer.classList.contains( 'jp-carousel-show' ) ) { extraInfoContainer.classList.add( 'jp-carousel-show' ); } else { extraInfoContainer.classList.remove( 'jp-carousel-show' ); } } } if ( domUtil.closest( target, '.jp-carousel-icon-info' ) || target.classList.contains( 'jp-carousel-photo-title' ) ) { if ( photoMetaContainer && photoMetaContainer.classList.contains( 'jp-carousel-show' ) ) { domUtil.scrollToElement( carousel.overlay, carousel.overlay, handleInfoToggle ); } else { handleInfoToggle(); domUtil.scrollToElement( carousel.info, carousel.overlay ); } } if ( domUtil.closest( target, '.jp-carousel-icon-comments' ) ) { if ( commentsContainer && commentsContainer.classList.contains( 'jp-carousel-show' ) ) { domUtil.scrollToElement( carousel.overlay, carousel.overlay, handleCommentToggle ); } else { handleCommentToggle(); domUtil.scrollToElement( carousel.info, carousel.overlay ); } } } function processSingleImageGallery() { var images = document.querySelectorAll( 'a img[data-attachment-id]' ); Array.prototype.forEach.call( images, function ( image ) { var link = image.parentElement; var container = link.parentElement; // Skip if image was already added to gallery by shortcode. if ( container.classList.contains( 'gallery-icon' ) ) { return; } // Skip if image is part of a gallery. if ( domUtil.closest( container, galleryItemSelector ) ) { return; } // Skip if the parent is not actually a link. if ( ! link.hasAttribute( 'href' ) ) { return; } var valid = false; // If link points to 'Media File' (ignoring GET parameters) and flag is set, allow it. if ( link.getAttribute( 'href' ).split( '?' )[ 0 ] === image.getAttribute( 'data-orig-file' ).split( '?' )[ 0 ] && Number( jetpackCarouselStrings.single_image_gallery_media_file ) === 1 ) { valid = true; } // If link points to 'Attachment Page', allow it. if ( link.getAttribute( 'href' ) === image.getAttribute( 'data-permalink' ) ) { valid = true; } // Links to 'Custom URL' or 'Media File' when flag is not set are not valid. if ( ! valid ) { return; } // Make this node a gallery recognizable by event listener above. link.classList.add( 'single-image-gallery' ); // blog_id is needed to allow posting comments to correct blog. link.setAttribute( 'data-carousel-extra', JSON.stringify( { blog_id: Number( jetpackCarouselStrings.blog_id ), } ) ); } ); } function testForData( el ) { return !! ( el && el.getAttribute( 'data-carousel-extra' ) ); } function openOrSelectSlide( gal, index ) { if ( ! carousel.isOpen ) { // The `open` method selects the correct slide during the initialization. loadSwiper( gal, { startIndex: index } ); } else { selectSlideAtIndex( index ); // We have to force swiper to slide to the index onHasChange. swiper.slideTo( index + 1 ); } } function selectSlideAtIndex( index ) { if ( ! index || index < 0 || index > carousel.slides.length ) { index = 0; } carousel.currentSlide = carousel.slides[ index ]; var current = carousel.currentSlide; var attachmentId = current.attrs.attachmentId; var infoIcon = carousel.info.querySelector( '.jp-carousel-icon-info' ); var commentsIcon = carousel.info.querySelector( '.jp-carousel-icon-comments' ); // If the comment/info section is toggled open, it's kept open, but scroll to top of the next slide. if ( ( infoIcon && infoIcon.classList.contains( 'jp-carousel-selected' ) ) || ( commentsIcon && commentsIcon.classList.contains( 'jp-carousel-selected' ) ) ) { if ( carousel.overlay.scrollTop !== 0 ) { domUtil.scrollToElement( carousel.overlay, carousel.overlay ); } } loadFullImage( carousel.slides[ index ] ); if ( Number( jetpackCarouselStrings.display_background_image ) === 1 && ! carousel.slides[ index ].backgroundImage ) { loadBackgroundImage( carousel.slides[ index ] ); } domUtil.hide( carousel.caption ); updateTitleCaptionAndDesc( { caption: current.attrs.caption, title: current.attrs.title, desc: current.attrs.desc, } ); var imageMeta = carousel.slides[ index ].attrs.imageMeta; updateExif( imageMeta ); updateFullSizeLink( current ); if ( Number( jetpackCarouselStrings.display_comments ) === 1 ) { testCommentsOpened( carousel.slides[ index ].attrs.commentsOpened ); fetchComments( attachmentId ); domUtil.hide( carousel.info.querySelector( '#jp-carousel-comment-post-results' ) ); } // Update pagination in footer. var pagination = carousel.info.querySelector( '.jp-carousel-pagination' ); if ( pagination && carousel.slides.length > 5 ) { var currentPage = index + 1; pagination.innerHTML = '<span>' + currentPage + ' / ' + carousel.slides.length + '</span>'; } // Record pageview in WP Stats, for each new image loaded full-screen. if ( jetpackCarouselStrings.stats ) { new Image().src = document.location.protocol + '//pixel.wp.com/g.gif?' + jetpackCarouselStrings.stats + '&post=' + encodeURIComponent( attachmentId ) + '&rand=' + Math.random(); } pageview( attachmentId ); window.location.hash = lastKnownLocationHash = '#jp-carousel-' + attachmentId; } function restoreScroll() { window.scrollTo( window.scrollX || window.pageXOffset || 0, scrollPos || 0 ); } function closeCarousel() { // Make sure to let the page scroll again. document.body.style.overflow = originalOverflow; document.documentElement.style.overflow = originalHOverflow; clearCommentTextAreaValue(); disableKeyboardNavigation(); domUtil.emitEvent( carousel.overlay, 'jp_carousel.beforeClose' ); restoreScroll(); swiper.destroy(); carousel.isOpen = false; // Clear slide data for DOM garbage collection. carousel.slides = []; carousel.currentSlide = undefined; carousel.gallery.innerHTML = ''; domUtil.fadeOut( carousel.overlay, function () { domUtil.emitEvent( carousel.overlay, 'jp_carousel.afterClose' ); } ); } function calculateMaxSlideDimensions() { return { width: window.innerWidth, height: window.innerHeight - 64, //subtract height of bottom info bar, }; } function selectBestImageUrl( args ) { if ( typeof args !== 'object' ) { args = {}; } if ( typeof args.origFile === 'undefined' ) { return ''; } if ( typeof args.origWidth === 'undefined' || typeof args.maxWidth === 'undefined' ) { return args.origFile; } if ( typeof args.mediumFile === 'undefined' || typeof args.largeFile === 'undefined' ) { return args.origFile; } // Check if the image is being served by Photon (using a regular expression on the hostname). var imageLinkParser = document.createElement( 'a' ); imageLinkParser.href = args.largeFile; var isPhotonUrl = /^i[0-2]\.wp\.com$/i.test( imageLinkParser.hostname ); var mediumSizeParts = getImageSizeParts( args.mediumFile, args.origWidth, isPhotonUrl ); var largeSizeParts = getImageSizeParts( args.largeFile, args.origWidth, isPhotonUrl ); var largeWidth = parseInt( largeSizeParts[ 0 ], 10 ); var largeHeight = parseInt( largeSizeParts[ 1 ], 10 ); var mediumWidth = parseInt( mediumSizeParts[ 0 ], 10 ); var mediumHeight = parseInt( mediumSizeParts[ 1 ], 10 ); args.origMaxWidth = args.maxWidth; args.origMaxHeight = args.maxHeight; // Give devices with a higher devicePixelRatio higher-res images (Retina display = 2, Android phones = 1.5, etc) if ( typeof window.devicePixelRatio !== 'undefined' && window.devicePixelRatio > 1 ) { args.maxWidth = args.maxWidth * window.devicePixelRatio; args.maxHeight = args.maxHeight * window.devicePixelRatio; } if ( largeWidth >= args.maxWidth || largeHeight >= args.maxHeight ) { return args.largeFile; } if ( mediumWidth >= args.maxWidth || mediumHeight >= args.maxHeight ) { return args.mediumFile; } if ( isPhotonUrl ) { // args.origFile doesn't point to a Photon url, so in this case we use args.largeFile // to return the photon url of the original image. var largeFileIndex = args.largeFile.lastIndexOf( '?' ); var origPhotonUrl = args.largeFile; if ( largeFileIndex !== -1 ) { origPhotonUrl = args.largeFile.substring( 0, largeFileIndex ); // If we have a really large image load a smaller version // that is closer to the viewable size if ( args.origWidth > args.maxWidth || args.origHeight > args.maxHeight ) { // @2x the max sizes so we get a high enough resolution for zooming. args.origMaxWidth = args.maxWidth * 2; args.origMaxHeight = args.maxHeight * 2; origPhotonUrl += '?fit=' + args.origMaxWidth + '%2C' + args.origMaxHeight; } } return origPhotonUrl; } return args.origFile; } function getImageSizeParts( file, origWidth, isPhotonUrl ) { var size = isPhotonUrl ? file.replace( /.*=([\d]+%2C[\d]+).*$/, '$1' ) : file.replace( /.*-([\d]+x[\d]+)\..+$/, '$1' ); var sizeParts = size !== file ? isPhotonUrl ? size.split( '%2C' ) : size.split( 'x' ) : [ origWidth, 0 ]; // If one of the dimensions is set to 9999, then the actual value of that dimension can't be retrieved from the url. // In that case, we set the value to 0. if ( sizeParts[ 0 ] === '9999' ) { sizeParts[ 0 ] = '0'; } if ( sizeParts[ 1 ] === '9999' ) { sizeParts[ 1 ] = '0'; } return sizeParts; } /** * Returns a number in a fraction format that represents the shutter speed. * @param Number speed * @return String */ function formatShutterSpeed( speed ) { var denominator; // round to one decimal if value > 1s by multiplying it by 10, rounding, then dividing by 10 again if ( speed >= 1 ) { return Math.round( speed * 10 ) / 10 + 's'; } // If the speed is less than one, we find the denominator by inverting // the number. Since cameras usually use rational numbers as shutter // speeds, we should get a nice round number. Or close to one in cases // like 1/30. So we round it. denominator = Math.round( 1 / speed ); return '1/' + denominator + 's'; } function parseTitleOrDesc( value ) { if ( ! value.match( ' ' ) && value.match( '_' ) ) { return ''; } return value; } function updateTitleCaptionAndDesc( data ) { var caption = ''; var title = ''; var desc = ''; var captionMainElement; var captionInfoExtraElement; var titleElement; var descriptionElement; captionMainElement = carousel.overlay.querySelector( '.jp-carousel-photo-caption' ); captionInfoExtraElement = carousel.overlay.querySelector( '.jp-carousel-caption' ); titleElement = carousel.overlay.querySelector( '.jp-carousel-photo-title' ); descriptionElement = carousel.overlay.querySelector( '.jp-carousel-photo-description' ); domUtil.hide( captionMainElement ); domUtil.hide( captionInfoExtraElement ); domUtil.hide( titleElement ); domUtil.hide( descriptionElement ); caption = parseTitleOrDesc( data.caption ) || ''; title = parseTitleOrDesc( data.title ) || ''; desc = parseTitleOrDesc( data.desc ) || ''; if ( caption || title || desc ) { if ( caption ) { captionMainElement.innerHTML = caption; captionInfoExtraElement.innerHTML = caption; domUtil.show( captionMainElement ); domUtil.show( captionInfoExtraElement ); } if ( domUtil.stripHTML( caption ) === domUtil.stripHTML( title ) ) { title = ''; } if ( domUtil.stripHTML( caption ) === domUtil.stripHTML( desc ) ) { desc = ''; } if ( domUtil.stripHTML( title ) === domUtil.stripHTML( desc ) ) { desc = ''; } if ( desc ) { descriptionElement.innerHTML = desc; domUtil.show( descriptionElement ); if ( ! title && ! caption ) { captionMainElement.innerHTML = domUtil.stripHTML( desc ); domUtil.show( captionMainElement ); } } if ( title ) { var plainTitle = domUtil.stripHTML( title ); titleElement.innerHTML = plainTitle; if ( ! caption ) { captionMainElement.innerHTML = plainTitle; captionInfoExtraElement.innerHTML = plainTitle; domUtil.show( captionMainElement ); } domUtil.show( titleElement ); } } } // updateExif updates the contents of the exif UL (.jp-carousel-image-exif) function updateExif( meta ) { if ( ! meta || Number( jetpackCarouselStrings.display_exif ) !== 1 ) { return false; } var ul = carousel.info.querySelector( '.jp-carousel-image-meta ul.jp-carousel-image-exif' ); var html = ''; for ( var key in meta ) { var val = meta[ key ]; var metaKeys = jetpackCarouselStrings.meta_data || []; if ( parseFloat( val ) === 0 || ! val.length || metaKeys.indexOf( key ) === -1 ) { continue; } switch ( key ) { case 'focal_length': val = val + 'mm'; break; case 'shutter_speed': val = formatShutterSpeed( val ); break; case 'aperture': val = 'f/' + val; break; } html += '<li><h5>' + jetpackCarouselStrings[ key ] + '</h5>' + val + '</li>'; } ul.innerHTML = html; ul.style.removeProperty( 'display' ); } // Update the contents of the jp-carousel-image-download link function updateFullSizeLink( currentSlide ) { if ( ! currentSlide ) { return false; } var original; var origSize = [ currentSlide.attrs.origWidth, currentSlide.attrs.origHeight ]; var imageLinkParser = document.createElement( 'a' ); imageLinkParser.href = currentSlide.attrs.src.replace( /\?.+$/, '' ); // Is this a Photon URL? if ( imageLinkParser.hostname.match( /^i[\d]{1}\.wp\.com$/i ) !== null ) { original = imageLinkParser.href; } else { original = currentSlide.attrs.origFile.replace( /\?.+$/, '' ); } var downloadText = carousel.info.querySelector( '.jp-carousel-download-text' ); var permalink = carousel.info.querySelector( '.jp-carousel-image-download' ); downloadText.innerHTML = util.applyReplacements( jetpackCarouselStrings.download_original, origSize ); permalink.setAttribute( 'href', original ); permalink.style.removeProperty( 'display' ); } function testCommentsOpened( opened ) { var commentForm = carousel.container.querySelector( '.jp-carousel-comment-form-container' ); var isOpened = parseInt( opened, 10 ) === 1; if ( isOpened ) { domUtil.fadeIn( commentForm ); } else { domUtil.fadeOut( commentForm ); } } function fetchComments( attachmentId, offset ) { var shouldClear = offset === undefined; var commentsIndicator = carousel.info.querySelector( '.jp-carousel-icon-comments .jp-carousel-has-comments-indicator' ); commentsIndicator.classList.remove( 'jp-carousel-show' ); clearInterval( commentInterval ); if ( ! attachmentId ) { return; } if ( ! offset || offset < 1 ) { offset = 0; } var comments = carousel.info.querySelector( '.jp-carousel-comments' ); var commentsLoading = carousel.info.querySelector( '#jp-carousel-comments-loading' ); domUtil.show( commentsLoading ); if ( shouldClear ) { domUtil.hide( comments ); comments.innerHTML = ''; } var xhr = new XMLHttpRequest(); var url = jetpackCarouselStrings.ajaxurl + '?action=get_attachment_comments' + '&nonce=' + jetpackCarouselStrings.nonce + '&id=' + attachmentId + '&offset=' + offset; xhr.open( 'GET', url ); xhr.setRequestHeader( 'X-Requested-With', 'XMLHttpRequest' ); var onError = function () { domUtil.fadeIn( comments ); domUtil.fadeOut( commentsLoading ); }; xhr.onload = function () { // Ignore the results if they arrive late and we're now on a different slide. if ( ! carousel.currentSlide || carousel.currentSlide.attrs.attachmentId !== attachmentId ) { return; } var isSuccess = xhr.status >= 200 && xhr.status < 300; var data; try { data = JSON.parse( xhr.responseText ); } catch ( e ) { // Do nothing. } if ( ! isSuccess || ! data || ! Array.isArray( data ) ) { return onError(); } if ( shouldClear ) { comments.innerHTML = ''; } for ( var i = 0; i < data.length; i++ ) { var entry = data[ i ]; var comment = document.createElement( 'div' ); comment.classList.add( 'jp-carousel-comment' ); comment.setAttribute( 'id', 'jp-carousel-comment-' + entry.id ); comment.innerHTML = '<div class="comment-gravatar">' + entry.gravatar_markup + '</div>' + '<div class="comment-content">' + '<div class="comment-author">' + entry.author_markup + '</div>' + '<div class="comment-date">' + entry.date_gmt + '</div>' + entry.content + '</div>'; comments.appendChild( comment ); // Set the interval to check for a new page of comments. clearInterval( commentInterval ); commentInterval = setInterval( function () { if ( carousel.container.scrollTop + 150 > window.innerHeight ) { fetchComments( attachmentId, offset + 10 ); clearInterval( commentInterval ); } }, 300 ); } if ( data.length > 0 ) { domUtil.show( comments ); commentsIndicator.innerText = data.length; commentsIndicator.classList.add( 'jp-carousel-show' ); } domUtil.hide( commentsLoading ); }; xhr.onerror = onError; xhr.send(); } function loadFullImage( slide ) { var el = slide.el; var attrs = slide.attrs; var image = el.querySelector( 'img' ); if ( ! image.hasAttribute( 'data-loaded' ) ) { var hasPreview = !! attrs.previewImage; var thumbSize = attrs.thumbSize; if ( ! hasPreview || ( thumbSize && el.offsetWidth > thumbSize.width ) ) { image.src = attrs.src; } else { image.src = attrs.previewImage; } image.setAttribute( 'itemprop', 'image' ); image.setAttribute( 'data-loaded', 1 ); } } function loadBackgroundImage( slide ) { var currentSlide = slide.el; if ( swiper && swiper.slides ) { currentSlide = swiper.slides[ swiper.activeIndex ]; } var image = slide.attrs.originalElement; var isLoaded = image.complete && image.naturalHeight !== 0; if ( isLoaded ) { applyBackgroundImage( slide, currentSlide, image ); return; } image.onload = function () { applyBackgroundImage( slide, currentSlide, image ); }; } function applyBackgroundImage( slide, currentSlide, image ) { var url = util.getBackgroundImage( image ); slide.backgroundImage = url; currentSlide.style.backgroundImage = 'url(' + url + ')'; currentSlide.style.backgroundSize = 'cover'; } function clearCommentTextAreaValue() { if ( carousel.commentField ) { carousel.commentField.value = ''; } } function getOriginalDimensions( el ) { var size = el.getAttribute( 'data-orig-size' ) || ''; if ( size ) { var parts = size.split( ',' ); return { width: parseInt( parts[ 0 ], 10 ), height: parseInt( parts[ 1 ], 10 ) }; } else { return { width: el.getAttribute( 'data-original-width' ) || el.getAttribute( 'width' ) || undefined, height: el.getAttribute( 'data-original-height' ) || el.getAttribute( 'height' ) || undefined, }; } } function initCarouselSlides( items, startIndex ) { carousel.slides = []; var max = calculateMaxSlideDimensions(); // If the startIndex is not 0 then preload the clicked image first. if ( startIndex !== 0 ) { var img = new Image(); img.src = items[ startIndex ].getAttribute( 'data-gallery-src' ); } var useInPageThumbnails = !! domUtil.closest( items[ 0 ], '.tiled-gallery.type-rectangular' ); // create the 'slide' Array.prototype.forEach.call( items, function ( item, i ) { var permalinkEl = domUtil.closest( item, 'a' ); var origFile = item.getAttribute( 'data-orig-file' ) || item.getAttribute( 'src-orig' ); var attrID = item.getAttribute( 'data-attachment-id' ) || item.getAttribute( 'data-id' ) || '0'; var caption = document.querySelector( 'img[data-attachment-id="' + attrID + '"] + figcaption' ); if ( caption ) { caption = caption.innerHTML; } else { caption = item.getAttribute( 'data-image-caption' ); } var attrs = { originalElement: item, attachmentId: attrID, commentsOpened: item.getAttribute( 'data-comments-opened' ) || '0', imageMeta: domUtil.getJSONAttribute( item, 'data-image-meta' ) || {}, title: item.getAttribute( 'data-image-title' ) || '', desc: item.getAttribute( 'data-image-description' ) || '', mediumFile: item.getAttribute( 'data-medium-file' ) || '', largeFile: item.getAttribute( 'data-large-file' ) || '', origFile: origFile || '', thumbSize: { width: item.naturalWidth, height: item.naturalHeight }, caption: caption || '', permalink: permalinkEl && permalinkEl.getAttribute( 'href' ), src: origFile || item.getAttribute( 'src' ) || '', }; var tiledGalleryItem = domUtil.closest( item, '.tiled-gallery-item' ); var tiledCaptionEl = tiledGalleryItem && tiledGalleryItem.querySelector( '.tiled-gallery-caption' ); var tiledCaption = tiledCaptionEl && tiledCaptionEl.innerHTML; if ( tiledCaption ) { attrs.caption = tiledCaption; } var origDimensions = getOriginalDimensions( item ); attrs.origWidth = origDimensions.width || attrs.thumbSize.width; attrs.origHeight = origDimensions.height || attrs.thumbSize.height; if ( typeof wpcom !== 'undefined' && wpcom.carousel && wpcom.carousel.generateImgSrc ) { attrs.src = wpcom.carousel.generateImgSrc( item, max ); } else { attrs.src = selectBestImageUrl( { origFile: attrs.src, origWidth: attrs.origWidth, origHeight: attrs.origHeight, maxWidth: max.width, maxHeight: max.height, mediumFile: attrs.mediumFile, largeFile: attrs.largeFile, } ); } // Set the final src. item.setAttribute( 'data-gallery-src', attrs.src ); if ( attrs.attachmentId !== '0' ) { attrs.title = util.texturize( attrs.title ); attrs.desc = util.texturize( attrs.desc ); attrs.caption = util.texturize( attrs.caption ); // Initially, the image is a 1x1 transparent gif. // The preview is shown as a background image on the slide itself. var image = new Image(); image.src = attrs.src; var slideEl = document.createElement( 'div' ); slideEl.classList.add( 'swiper-slide' ); slideEl.setAttribute( 'itemprop', 'associatedMedia' ); slideEl.setAttribute( 'itemscope', '' ); slideEl.setAttribute( 'itemtype', 'https://schema.org/ImageObject' ); var zoomEl = document.createElement( 'div' ); zoomEl.classList.add( 'swiper-zoom-container' ); carousel.gallery.appendChild( slideEl ); slideEl.appendChild( zoomEl ); zoomEl.appendChild( image ); slideEl.setAttribute( 'data-attachment-id', attrs.attachmentId ); slideEl.setAttribute( 'data-permalink', attrs.permalink ); slideEl.setAttribute( 'data-orig-file', attrs.origFile ); if ( useInPageThumbnails ) { // Use the image already loaded in the gallery as a preview. attrs.previewImage = attrs.src; } var slide = { el: slideEl, attrs: attrs, index: i }; carousel.slides.push( slide ); } } ); } function loadSwiper( gallery, options ) { if ( ! window.Swiper670 ) { var loader = document.querySelector( '#jp-carousel-loading-overlay' ); domUtil.show( loader ); var jsScript = document.createElement( 'script' ); jsScript.id = 'jetpack-carousel-swiper-js'; jsScript.src = window.jetpackSwiperLibraryPath.url; jsScript.async = true; jsScript.onload = function () { domUtil.hide( loader ); openCarousel( gallery, options ); }; jsScript.onerror = function () { domUtil.hide( loader ); }; document.head.appendChild( jsScript ); return; } openCarousel( gallery, options ); } function openCarousel( gallery, options ) { var settings = { imgSelector: '.gallery-item [data-attachment-id], .tiled-gallery-item [data-attachment-id], img[data-attachment-id], img[data-id]', startIndex: 0, }; var data = domUtil.getJSONAttribute( gallery, 'data-carousel-extra' ); var tapTimeout; if ( ! data ) { return; // don't run if the default gallery functions weren't used } initializeCarousel(); if ( carousel.isOpen ) { return; // don't open if already opened } carousel.isOpen = true; // make sure to stop the page from scrolling behind the carousel overlay, so we don't trigger // infiniscroll for it when enabled (Reader, theme infiniscroll, etc). originalOverflow = getComputedStyle( document.body ).overflow; document.body.style.overflow = 'hidden'; // prevent html from overflowing on some of the new themes. originalHOverflow = getComputedStyle( document.documentElement ).overflow; document.documentElement.style.overflow = 'hidden'; scrollPos = window.scrollY || window.pageYOffset || 0; carousel.container.setAttribute( 'data-carousel-extra', JSON.stringify( data ) ); stat( [ 'open', 'view_image' ] ); // If options exist, lets merge them // with our default settings for ( var option in options || {} ) { settings[ option ] = options[ option ]; } if ( settings.startIndex === -1 ) { settings.startIndex = 0; // -1 returned if can't find index, so start from beginning } domUtil.emitEvent( carousel.overlay, 'jp_carousel.beforeOpen' ); carousel.gallery.innerHTML = ''; // Need to set the overlay manually to block or swiper does't initialise properly. carousel.overlay.style.opacity = 1; carousel.overlay.style.display = 'block'; initCarouselSlides( gallery.querySelectorAll( settings.imgSelector ), settings.startIndex ); swiper = new window.Swiper670( '.jp-carousel-swiper-container', { centeredSlides: true, zoom: true, loop: carousel.slides.length > 1, // Turn off interactions and hide navigation arrows if there is only one slide. enabled: carousel.slides.length > 1, pagination: { el: '.jp-swiper-pagination', clickable: true, }, navigation: { nextEl: '.jp-swiper-button-next', prevEl: '.jp-swiper-button-prev', }, initialSlide: settings.startIndex, on: { init: function () { selectSlideAtIndex( settings.startIndex ); }, }, preventClicks: false, preventClicksPropagation: false, preventInteractionOnTransition: ! domUtil.isTouch(), threshold: 5, } ); swiper.on( 'slideChange', function ( swiper ) { var index; // Swiper indexes slides from 1, plus when looping to left last slide ends up // as 0 and looping to right first slide as total slides + 1. These are adjusted // here to match index of carousel.slides. if ( swiper.activeIndex === 0 ) { index = carousel.slides.length - 1; } else if ( swiper.activeIndex === carousel.slides.length + 1 ) { index = 0; } else { index = swiper.activeIndex - 1; } selectSlideAtIndex( index ); carousel.overlay.classList.remove( 'jp-carousel-hide-controls' ); } ); swiper.on( 'zoomChange', function ( swiper, scale ) { if ( scale > 1 ) { carousel.overlay.classList.add( 'jp-carousel-hide-controls' ); } if ( scale === 1 ) { carousel.overlay.classList.remove( 'jp-carousel-hide-controls' ); } } ); swiper.on( 'doubleTap', function ( swiper ) { clearTimeout( tapTimeout ); if ( swiper.zoom.scale === 1 ) { var zoomTimeout = setTimeout( function () { carousel.overlay.classList.remove( 'jp-carousel-hide-controls' ); clearTimeout( zoomTimeout ); }, 150 ); } } ); swiper.on( 'tap', function () { if ( swiper.zoom.scale > 1 ) { tapTimeout = setTimeout( function () { carousel.overlay.classList.toggle( 'jp-carousel-hide-controls' ); }, 150 ); } } ); domUtil.fadeIn( carousel.overlay, function () { domUtil.emitEvent( carousel.overlay, 'jp_carousel.afterOpen' ); } ); } // Register the event listener for starting the gallery document.body.addEventListener( 'click', function ( e ) { var isCompatible = window.CSS && window.CSS.supports && window.CSS.supports( 'display', 'grid' ); // IE11 support is being dropped in August 2021. The new swiper.js libray is not IE11 compat // so just default to opening individual image attachment/media pages for IE. if ( ! isCompatible ) { return; } var target = e.target; var gallery = domUtil.closest( target, gallerySelector ); if ( gallery ) { if ( ! testForData( gallery ) ) { return; } var parent = target.parentElement; var grandparent = parent.parentElement; // If Gallery is made up of individual Image blocks check for custom link before // loading carousel. if ( grandparent && grandparent.classList.contains( 'wp-block-image' ) ) { var parentHref = parent.getAttribute( 'href' ); // If the link does not point to the attachment or media file then assume Image has // a custom link so don't load the carousel. if ( parentHref.split( '?' )[ 0 ] !== target.getAttribute( 'data-orig-file' ).split( '?' )[ 0 ] && parentHref !== target.getAttribute( 'data-permalink' ) ) { return; } } // Do not open the modal if we are looking at a gallery caption from before WP5, which may contain a link. if ( parent.classList.contains( 'gallery-caption' ) ) { return; } // Do not open the modal if we are looking at a caption of a gallery block, which may contain a link. if ( domUtil.matches( parent, 'figcaption' ) ) { return; } // Set height to auto. // Fix some themes where closing carousel brings view back to top. document.documentElement.style.height = 'auto'; e.preventDefault(); // Stopping propagation in case there are parent elements // with .gallery or .tiled-gallery class e.stopPropagation(); var item = domUtil.closest( target, itemSelector ); var index = Array.prototype.indexOf.call( gallery.querySelectorAll( itemSelector ), item ); loadSwiper( gallery, { startIndex: index } ); } } ); // Handle lightbox (single image gallery) for images linking to 'Attachment Page'. if ( Number( jetpackCarouselStrings.single_image_gallery ) === 1 ) { processSingleImageGallery(); document.body.addEventListener( 'is.post-load', function () { processSingleImageGallery(); } ); } // Makes carousel work on page load and when back button leads to same URL with carousel hash // (i.e. no actual document.ready trigger). window.addEventListener( 'hashchange', function () { var hashRegExp = /jp-carousel-(\d+)/; if ( ! window.location.hash || ! hashRegExp.test( window.location.hash ) ) { if ( carousel.isOpen ) { closeCarousel(); } return; } if ( window.location.hash === lastKnownLocationHash && carousel.isOpen ) { return; } if ( window.location.hash && carousel.gallery && ! carousel.isOpen && history.back ) { history.back(); return; } lastKnownLocationHash = window.location.hash; var matchList = window.location.hash.match( hashRegExp ); var attachmentId = parseInt( matchList[ 1 ], 10 ); var galleries = document.querySelectorAll( gallerySelector ); // Find the first thumbnail that matches the attachment ID in the location // hash, then open the gallery that contains it. for ( var i = 0; i < galleries.length; i++ ) { var gallery = galleries[ i ]; var selected; var images = gallery.querySelectorAll( 'img' ); for ( var j = 0; j < images.length; j++ ) { if ( parseInt( images[ j ].getAttribute( 'data-attachment-id' ), 10 ) === attachmentId || parseInt( images[ j ].getAttribute( 'data-id' ), 10 ) === attachmentId ) { selected = j; break; } } if ( selected !== undefined ) { openOrSelectSlide( gallery, selected ); break; } } } ); if ( window.location.hash ) { domUtil.emitEvent( window, 'hashchange' ); } } if ( document.readyState !== 'loading' ) { init(); } else { document.addEventListener( 'DOMContentLoaded', init ); } } )();