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/infinite-scroll
Viewing File: /opt/wp/plugins/jetpack/modules/infinite-scroll/infinity.js
/* globals infiniteScroll, _wpmejsSettings, ga, _gaq, WPCOM_sharing_counts, MediaElementPlayer */ ( function () { // Open closure. // Local vars. var Scroller, ajaxurl, stats, type, text, totop, loading_text; // IE requires special handling var isIE = -1 != navigator.userAgent.search( 'MSIE' ); if ( isIE ) { var IEVersion = navigator.userAgent.match( /MSIE\s?(\d+)\.?\d*;/ ); IEVersion = parseInt( IEVersion[ 1 ] ); } // HTTP ajaxurl when site is HTTPS causes Access-Control-Allow-Origin failure in Desktop and iOS Safari if ( 'https:' == document.location.protocol ) { infiniteScroll.settings.ajaxurl = infiniteScroll.settings.ajaxurl.replace( 'http://', 'https://' ); } /** * Loads new posts when users scroll near the bottom of the page. */ Scroller = function ( settings ) { var self = this; // Initialize our variables this.id = settings.id; this.body = document.body; this.window = window; this.element = document.getElementById( settings.id ); this.wrapperClass = settings.wrapper_class; this.ready = true; this.disabled = false; this.page = 1; this.offset = settings.offset; this.currentday = settings.currentday; this.order = settings.order; this.throttle = false; this.click_handle = settings.click_handle; this.google_analytics = settings.google_analytics; this.history = settings.history; this.origURL = window.location.href; // Handle element this.handle = document.createElement( 'div' ); this.handle.setAttribute( 'id', 'infinite-handle' ); this.handle.innerHTML = '<span><button>' + text.replace( '\\', '' ) + '</button></span>'; // Footer settings this.footer = { el: document.getElementById( 'infinite-footer' ), wrap: settings.footer, }; // Bind methods used as callbacks this.checkViewportOnLoadBound = self.checkViewportOnLoad.bind( this ); // Core's native MediaElement.js implementation needs special handling this.wpMediaelement = null; // We have two type of infinite scroll // cases 'scroll' and 'click' if ( type == 'scroll' ) { // Bind refresh to the scroll event // Throttle to check for such case every 300ms // On event the case becomes a fact this.window.addEventListener( 'scroll', function () { self.throttle = true; } ); // Go back top method self.gotop(); setInterval( function () { if ( self.throttle ) { // Once the case is the case, the action occurs and the fact is no more self.throttle = false; // Reveal or hide footer self.thefooter(); // Fire the refresh self.refresh(); self.determineURL(); // determine the url } }, 250 ); // Ensure that enough posts are loaded to fill the initial viewport, to compensate for short posts and large displays. self.ensureFilledViewport(); this.body.addEventListener( 'is.post-load', self.checkViewportOnLoadBound ); } else if ( type == 'click' ) { if ( this.click_handle ) { this.element.appendChild( this.handle ); } this.handle.addEventListener( 'click', function () { // Handle the handle if ( self.click_handle ) { self.handle.parentNode.removeChild( self.handle ); } // Fire the refresh self.refresh(); } ); } // Initialize any Core audio or video players loaded via IS this.body.addEventListener( 'is.post-load', self.initializeMejs ); }; /** * Normalize the access to the document scrollTop value. */ Scroller.prototype.getScrollTop = function () { return window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0; }; /** * Polyfill jQuery.extend. */ Scroller.prototype.extend = function ( out ) { out = out || {}; for ( var i = 1; i < arguments.length; i++ ) { if ( ! arguments[ i ] ) { continue; } for ( var key in arguments[ i ] ) { if ( arguments[ i ].hasOwnProperty( key ) ) { out[ key ] = arguments[ i ][ key ]; } } } return out; }; /** * Check whether we should fetch any additional posts. */ Scroller.prototype.check = function () { var wrapperMeasurements = this.measure( this.element, [ this.wrapperClass ] ); // Fetch more posts when we're less than 2 screens away from the bottom. return wrapperMeasurements.bottom < 2 * this.window.innerHeight; }; /** * Renders the results from a successful response. */ Scroller.prototype.render = function ( response ) { var childrenToAppend = Array.prototype.slice.call( response.fragment.childNodes ); this.body.classList.add( 'infinity-success' ); // Render the retrieved nodes. while ( childrenToAppend.length > 0 ) { var currentNode = childrenToAppend.shift(); this.element.appendChild( currentNode ); } this.trigger( this.body, 'is.post-load', { jqueryEventName: 'post-load', data: response, } ); this.ready = true; }; /** * Returns the object used to query for new posts. */ Scroller.prototype.query = function () { return { page: this.page + this.offset, // Load the next page. currentday: this.currentday, order: this.order, scripts: window.infiniteScroll.settings.scripts, styles: window.infiniteScroll.settings.styles, query_args: window.infiniteScroll.settings.query_args, query_before: window.infiniteScroll.settings.query_before, last_post_date: window.infiniteScroll.settings.last_post_date, }; }; Scroller.prototype.animate = function ( cb, duration ) { var start = performance.now(); requestAnimationFrame( function animate( time ) { var timeFraction = Math.min( 1, ( time - start ) / duration ); cb( timeFraction ); if ( timeFraction < 1 ) { requestAnimationFrame( animate ); } } ); }; /** * Scroll back to top. */ Scroller.prototype.gotop = function () { var blog = document.getElementById( 'infinity-blog-title' ); var self = this; if ( ! blog ) { return; } blog.setAttribute( 'title', totop ); blog.addEventListener( 'click', function ( e ) { var sourceScroll = self.window.pageYOffset; e.preventDefault(); self.animate( function ( progress ) { var currentScroll = sourceScroll - sourceScroll * progress; document.documentElement.scrollTop = document.body.scrollTop = currentScroll; }, 200 ); } ); }; /** * The infinite footer. */ Scroller.prototype.thefooter = function () { var self = this, pageWrapper, footerContainer, width, sourceBottom, targetBottom, footerEnabled = this.footer && this.footer.el; if ( ! footerEnabled ) { return; } // Check if we have an id for the page wrapper if ( 'string' === typeof this.footer.wrap ) { try { pageWrapper = document.getElementById( this.footer.wrap ); width = pageWrapper.getBoundingClientRect(); width = width.width; } catch ( err ) { width = 0; } // Make the footer match the width of the page if ( width > 479 ) { footerContainer = this.footer.el.querySelector( '.container' ); if ( footerContainer ) { footerContainer.style.width = width + 'px'; } } } // Reveal footer sourceBottom = parseInt( self.footer.el.style.bottom || -50, 10 ); targetBottom = this.window.pageYOffset >= 350 ? 0 : -50; if ( sourceBottom !== targetBottom ) { self.animate( function ( progress ) { var currentBottom = sourceBottom + ( targetBottom - sourceBottom ) * progress; self.footer.el.style.bottom = currentBottom + 'px'; if ( 1 === progress ) { sourceBottom = targetBottom; } }, 200 ); } }; /** * Recursively convert a JS object into URL encoded data. */ Scroller.prototype.urlEncodeJSON = function ( obj, prefix ) { var params = [], encodedKey, newPrefix; for ( var key in obj ) { encodedKey = encodeURIComponent( key ); newPrefix = prefix ? prefix + '[' + encodedKey + ']' : encodedKey; if ( 'object' === typeof obj[ key ] ) { if ( ! Array.isArray( obj[ key ] ) || obj[ key ].length > 0 ) { params.push( this.urlEncodeJSON( obj[ key ], newPrefix ) ); } else { // Explicitly expose empty arrays with no values params.push( newPrefix + '[]=' ); } } else { params.push( newPrefix + '=' + encodeURIComponent( obj[ key ] ) ); } } return params.join( '&' ); }; /** * Controls the flow of the refresh. Don't mess. */ Scroller.prototype.refresh = function () { var self = this, query, xhr, loader, customized; // If we're disabled, ready, or don't pass the check, bail. if ( this.disabled || ! this.ready || ! this.check() ) { return; } // Let's get going -- set ready to false to prevent // multiple refreshes from occurring at once. this.ready = false; // Create a loader element to show it's working. if ( this.click_handle ) { if ( ! loader ) { document.getElementById( 'infinite-aria' ).textContent = loading_text; loader = document.createElement( 'div' ); loader.classList.add( 'infinite-loader' ); loader.setAttribute( 'role', 'progress' ); loader.innerHTML = '<div class="spinner"><div class="spinner-inner"><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div></div>'; } this.element.appendChild( loader ); } // Generate our query vars. query = self.extend( { action: 'infinite_scroll', }, this.query() ); // Inject Customizer state. if ( 'undefined' !== typeof wp && wp.customize && wp.customize.settings.theme ) { customized = {}; query.wp_customize = 'on'; query.theme = wp.customize.settings.theme.stylesheet; wp.customize.each( function ( setting ) { if ( setting._dirty ) { customized[ setting.id ] = setting(); } } ); query.customized = JSON.stringify( customized ); query.nonce = wp.customize.settings.nonce.preview; } // Fire the ajax request. xhr = new XMLHttpRequest(); xhr.open( 'POST', infiniteScroll.settings.ajaxurl, true ); xhr.setRequestHeader( 'X-Requested-With', 'XMLHttpRequest' ); xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8' ); xhr.send( self.urlEncodeJSON( query ) ); // Allow refreshes to occur again if an error is triggered. xhr.onerror = function () { if ( self.click_handle ) { loader.parentNode.removeChild( loader ); } self.ready = true; }; // Success handler xhr.onload = function () { var response = JSON.parse( xhr.responseText ), httpCheck = xhr.status >= 200 && xhr.status < 300, responseCheck = 'undefined' !== typeof response.html; if ( ! response || ! httpCheck || ! responseCheck ) { if ( self.click_handle ) { loader.parentNode.removeChild( loader ); } return; } // On success, let's hide the loader circle. if ( self.click_handle ) { loader.parentNode.removeChild( loader ); } // If additional scripts are required by the incoming set of posts, parse them if ( response.scripts && Array.isArray( response.scripts ) ) { response.scripts.forEach( function ( item ) { var elementToAppendTo = item.footer ? 'body' : 'head'; // Add script handle to list of those already parsed window.infiniteScroll.settings.scripts.push( item.handle ); // Output extra data, if present if ( item.extra_data ) { self.appendInlineScript( item.extra_data, elementToAppendTo ); } if ( item.before_handle ) { self.appendInlineScript( item.before_handle, elementToAppendTo ); } // Build script tag and append to DOM in requested location var script = document.createElement( 'script' ); script.type = 'text/javascript'; script.src = item.src; script.id = item.handle; // Dynamically loaded scripts are async by default. // We don't want that, it breaks stuff, e.g. wp-mediaelement init. script.async = false; if ( item.after_handle ) { script.onload = function () { self.appendInlineScript( item.after_handle, elementToAppendTo ); }; } // If MediaElement.js is loaded in by item set of posts, don't initialize the players a second time as it breaks them all if ( 'wp-mediaelement' === item.handle ) { self.body.removeEventListener( 'is.post-load', self.initializeMejs ); } if ( 'wp-mediaelement' === item.handle && 'undefined' === typeof mejs ) { self.wpMediaelement = {}; self.wpMediaelement.tag = script; self.wpMediaelement.element = elementToAppendTo; setTimeout( self.maybeLoadMejs.bind( self ), 250 ); } else { document.getElementsByTagName( elementToAppendTo )[ 0 ].appendChild( script ); } } ); } // If additional stylesheets are required by the incoming set of posts, parse them if ( response.styles && Array.isArray( response.styles ) ) { response.styles.forEach( function ( item ) { // Add stylesheet handle to list of those already parsed window.infiniteScroll.settings.styles.push( item.handle ); // Build link tag var style = document.createElement( 'link' ); style.rel = 'stylesheet'; style.href = item.src; style.id = item.handle + '-css'; // Destroy link tag if a conditional statement is present and either the browser isn't IE, or the conditional doesn't evaluate true if ( item.conditional && ( ! isIE || ! eval( item.conditional.replace( /%ver/g, IEVersion ) ) ) ) { style = false; } // Append link tag if necessary if ( style ) { document.getElementsByTagName( 'head' )[ 0 ].appendChild( style ); } } ); } // Convert the response.html to a fragment element. // Using a div instead of DocumentFragment, because the latter doesn't support innerHTML. response.fragment = document.createElement( 'div' ); response.fragment.innerHTML = response.html; // Increment the page number self.page++; // Record pageview in WP Stats, if available. if ( stats ) { new Image().src = document.location.protocol + '//pixel.wp.com/g.gif?' + stats + '&post=0&baba=' + Math.random(); } // Add new posts to the postflair object if ( 'object' === typeof response.postflair && 'object' === typeof WPCOM_sharing_counts ) { WPCOM_sharing_counts = self.extend( WPCOM_sharing_counts, response.postflair ); // eslint-disable-line no-global-assign } // Render the results self.render.call( self, response ); // If 'click' type and there are still posts to fetch, add back the handle if ( type == 'click' ) { // add focus to new posts, only in button mode as we know where page focus currently is and only if we have a wrapper if ( infiniteScroll.settings.wrapper ) { document .querySelector( '#infinite-view-' + ( self.page + self.offset - 1 ) + ' a:first-of-type' ) .focus( { preventScroll: true, } ); } if ( response.lastbatch ) { if ( self.click_handle ) { // Update body classes self.body.classList.add( 'infinity-end' ); self.body.classList.remove( 'infinity-success' ); } else { self.trigger( this.body, 'infinite-scroll-posts-end' ); } } else { if ( self.click_handle ) { self.element.appendChild( self.handle ); } else { self.trigger( this.body, 'infinite-scroll-posts-more' ); } } } else if ( response.lastbatch ) { self.disabled = true; self.body.classList.add( 'infinity-end' ); self.body.classList.remove( 'infinity-success' ); } // Update currentday to the latest value returned from the server if ( response.currentday ) { self.currentday = response.currentday; } // Fire Google Analytics pageview if ( self.google_analytics ) { var ga_url = self.history.path.replace( /%d/, self.page ); if ( 'object' === typeof _gaq ) { _gaq.push( [ '_trackPageview', ga_url ] ); } if ( 'function' === typeof ga ) { ga( 'send', 'pageview', ga_url ); } } }; return xhr; }; /** * Given JavaScript blob and the name of a parent tag, this helper function will * generate a script tag, insert the JavaScript blob, and append it to the parent. * * It's important to note that the JavaScript blob will be evaluated immediately. If * you need a parent script to load first, use that script element's onload handler. * * @param {string} script The blob of JavaScript to run. * @param {string} parentTag The tag name of the parent element. */ Scroller.prototype.appendInlineScript = function ( script, parentTag ) { var element = document.createElement( 'script' ), scriptContent = document.createTextNode( '//<![CDATA[ \n' + script + '\n//]]>' ); element.type = 'text/javascript'; element.appendChild( scriptContent ); document.getElementsByTagName( parentTag )[ 0 ].appendChild( element ); }; /** * Core's native media player uses MediaElement.js * The library's size is sufficient that it may not be loaded in time for Core's helper to invoke it, so we need to delay until `mejs` exists. */ Scroller.prototype.maybeLoadMejs = function () { if ( null === this.wpMediaelement ) { return; } if ( 'undefined' === typeof mejs ) { setTimeout( this.maybeLoadMejs.bind( this ), 250 ); } else { document .getElementsByTagName( this.wpMediaelement.element )[ 0 ] .appendChild( this.wpMediaelement.tag ); this.wpMediaelement = null; // Ensure any subsequent IS loads initialize the players this.body.addEventListener( 'is.post-load', this.initializeMejs ); } }; /** * Initialize the MediaElement.js player for any posts not previously initialized */ Scroller.prototype.initializeMejs = function ( e ) { // Are there media players in the incoming set of posts? if ( ! e.detail || ! e.detail.html || ( -1 === e.detail.html.indexOf( 'wp-audio-shortcode' ) && -1 === e.detail.html.indexOf( 'wp-video-shortcode' ) ) ) { return; } // Don't bother if mejs isn't loaded for some reason if ( 'undefined' === typeof mejs ) { return; } // Adapted from wp-includes/js/mediaelement/wp-mediaelement.js // Modified to not initialize already-initialized players, as Mejs doesn't handle that well var settings = {}; var audioVideoElements; if ( typeof _wpmejsSettings !== 'undefined' ) { settings.pluginPath = _wpmejsSettings.pluginPath; } settings.success = function ( mejs ) { var autoplay = mejs.attributes.autoplay && 'false' !== mejs.attributes.autoplay; if ( 'flash' === mejs.pluginType && autoplay ) { mejs.addEventListener( 'canplay', function () { mejs.play(); }, false ); } }; audioVideoElements = document.querySelectorAll( '.wp-audio-shortcode, .wp-video-shortcode' ); audioVideoElements = Array.prototype.slice.call( audioVideoElements ); // Only process already unprocessed shortcodes. audioVideoElements = audioVideoElements.filter( function ( el ) { while ( el.parentNode ) { if ( el.classList.contains( 'mejs-container' ) ) { return false; } el = el.parentNode; } return true; } ); for ( var i = 0; i < audioVideoElements.length; i++ ) { new MediaElementPlayer( audioVideoElements[ i ], settings ); } }; /** * Get element measurements relative to the viewport. * * @returns {object} */ Scroller.prototype.measure = function ( element, expandClasses ) { expandClasses = expandClasses || []; var childrenToTest = Array.prototype.slice.call( element.children ); var currentChild, minTop = Number.MAX_VALUE, maxBottom = 0, currentChildRect, i; while ( childrenToTest.length > 0 ) { currentChild = childrenToTest.shift(); for ( i = 0; i < expandClasses.length; i++ ) { // Expand (= measure) child elements of nodes with class names from expandClasses. if ( currentChild.classList.contains( expandClasses[ i ] ) ) { childrenToTest = childrenToTest.concat( Array.prototype.slice.call( currentChild.children ) ); break; } } currentChildRect = currentChild.getBoundingClientRect(); minTop = Math.min( minTop, currentChildRect.top ); maxBottom = Math.max( maxBottom, currentChildRect.bottom ); } var viewportMiddle = Math.round( window.innerHeight / 2 ); // isActive = does the middle of the viewport cross the element? var isActive = minTop <= viewportMiddle && maxBottom >= viewportMiddle; /** * Factor = percentage of viewport above the middle line occupied by the element. * * Negative factors are assigned for elements below the middle line. That's on purpose * to only allow "page 2" to change the URL once it's in the middle of the viewport. */ var factor = ( Math.min( maxBottom, viewportMiddle ) - Math.max( minTop, 0 ) ) / viewportMiddle; return { top: minTop, bottom: maxBottom, height: maxBottom - minTop, factor: factor, isActive: isActive, }; }; /** * Trigger IS to load additional posts if the initial posts don't fill the window. * * On large displays, or when posts are very short, the viewport may not be filled with posts, * so we overcome this by loading additional posts when IS initializes. */ Scroller.prototype.ensureFilledViewport = function () { var self = this, windowHeight = self.window.innerHeight, wrapperMeasurements = self.measure( self.element, [ self.wrapperClass ] ); // Only load more posts once. This prevents infinite loops when there are no more posts. self.body.removeEventListener( 'is.post-load', self.checkViewportOnLoadBound ); // Load more posts if space permits, otherwise stop checking for a full viewport. if ( wrapperMeasurements.bottom < windowHeight ) { self.ready = true; self.refresh(); } }; /** * Event handler for ensureFilledViewport(), tied to the post-load trigger. * Necessary to ensure that the variable `this` contains the scroller when used in ensureFilledViewport(). Since this function is tied to an event, `this` becomes the DOM element the event is tied to. */ Scroller.prototype.checkViewportOnLoad = function () { this.ensureFilledViewport(); }; function fullscreenState() { return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement ? 1 : 0; } var previousFullScrenState = fullscreenState(); /** * Identify archive page that corresponds to majority of posts shown in the current browser window. */ Scroller.prototype.determineURL = function () { var self = this, pageNum = -1, currentFullScreenState = fullscreenState(), wrapperEls, maxFactor = 0; // xor - check if the state has changed if ( previousFullScrenState ^ currentFullScreenState ) { // If we just switched to/from fullscreen, // don't do the div clearing/caching or the // URL setting. Doing so can break video playback // if the video goes to fullscreen. previousFullScrenState = currentFullScreenState; return; } previousFullScrenState = currentFullScreenState; wrapperEls = document.querySelectorAll( '.' + self.wrapperClass ); for ( var i = 0; i < wrapperEls.length; i++ ) { var setMeasurements = self.measure( wrapperEls[ i ] ); // If it exists, pick a set that is crossed by the middle of the viewport. if ( setMeasurements.isActive ) { pageNum = parseInt( wrapperEls[ i ].dataset.pageNum, 10 ); break; } // If there is such a set, pick the one that occupies the most space // above the middle of the viewport. if ( setMeasurements.factor > maxFactor ) { pageNum = parseInt( wrapperEls[ i ].dataset.pageNum, 10 ); maxFactor = setMeasurements.factor; } // Otherwise default to -1 } self.updateURL( pageNum ); }; /** * Update address bar to reflect archive page URL for a given page number. * Checks if URL is different to prevent pollution of browser history. */ Scroller.prototype.updateURL = function ( page ) { // IE only supports pushState() in v10 and above, so don't bother if those conditions aren't met. if ( ! window.history.pushState ) { return; } var self = this, pageSlug = self.origURL; if ( -1 !== page ) { pageSlug = window.location.protocol + '//' + self.history.host + self.history.path.replace( /%d/, page ) + self.history.parameters; } if ( window.location.href != pageSlug ) { history.pushState( null, null, pageSlug ); } }; /** * Pause scrolling. */ Scroller.prototype.pause = function () { this.disabled = true; }; /** * Resume scrolling. */ Scroller.prototype.resume = function () { this.disabled = false; }; /** * Emits custom JS events. * * @param {Node} el * @param {string} eventName * @param {*} data */ Scroller.prototype.trigger = function ( el, eventName, opts ) { opts = opts || {}; /** * Emit the event in a jQuery way for backwards compatibility where necessary. */ if ( opts.jqueryEventName && 'undefined' !== typeof jQuery ) { jQuery( el ).trigger( opts.jqueryEventName, opts.data || null ); } /** * Emit the event in a standard way. */ var e; try { e = new CustomEvent( eventName, { bubbles: true, cancelable: true, detail: opts.data || null, } ); } catch ( err ) { e = document.createEvent( 'CustomEvent' ); e.initCustomEvent( eventName, true, true, opts.data || null ); } el.dispatchEvent( e ); }; /** * Ready, set, go! */ var jetpackInfinityModule = function () { var bodyClasses = infiniteScroll.settings.body_class.split( ' ' ); // Check for our variables if ( 'object' !== typeof infiniteScroll ) { return; } bodyClasses.forEach( function ( className ) { if ( className ) { document.body.classList.add( className ); } } ); // Set ajaxurl (for brevity) ajaxurl = infiniteScroll.settings.ajaxurl; // Set stats, used for tracking stats stats = infiniteScroll.settings.stats; // Define what type of infinity we have, grab text for click-handle type = infiniteScroll.settings.type; text = infiniteScroll.settings.text; totop = infiniteScroll.settings.totop; // aria text loading_text = infiniteScroll.settings.loading_text; // Initialize the scroller (with the ID of the element from the theme) infiniteScroll.scroller = new Scroller( infiniteScroll.settings ); /** * Monitor user scroll activity to update URL to correspond to archive page for current set of IS posts */ if ( type == 'click' ) { var timer = null; window.addEventListener( 'scroll', function () { // run the real scroll handler once every 250 ms. if ( timer ) { return; } timer = setTimeout( function () { infiniteScroll.scroller.determineURL(); timer = null; }, 250 ); } ); } }; /** * Ready, set, go! */ if ( document.readyState === 'interactive' || document.readyState === 'complete' ) { jetpackInfinityModule(); } else { document.addEventListener( 'DOMContentLoaded', jetpackInfinityModule ); } } )(); // Close closure