1// listen for rlt authentication events and pass them to children of this document. 2( function() { 3 var currentToken; 4 var parentOrigin; 5 var iframeOrigins; 6 var registeredIframes = []; 7 var initializationListeners = []; 8 var hasBeenInitialized = false; 9 var RLT_KEY = 'jetpack:wpcomRLT'; 10 11 // should we inject RLT into this iframe? 12 function rltShouldAuthorizeIframe( frameOrigin ) { 13 if ( ! Array.isArray( iframeOrigins ) ) { 14 return false; 15 } 16 return iframeOrigins.includes( frameOrigin ); 17 } 18 19 function rltInvalidateWindowToken( token, target, origin ) { 20 if ( target && typeof target.postMessage === 'function' ) { 21 try { 22 target.postMessage( JSON.stringify( { 23 type: 'rltMessage', 24 data: { 25 event: 'invalidate', 26 token: token, 27 sourceOrigin: window.location.origin, 28 }, 29 } ), origin ); 30 } catch ( err ) { 31 return; 32 } 33 } 34 } 35 36 /** 37 * PUBLIC METHODS 38 */ 39 window.rltInvalidateToken = function( token, sourceOrigin ) { 40 // invalidate in current context 41 if ( token === currentToken ) { 42 currentToken = null; 43 } 44 45 // remove from localstorage, but only if in a top level window, not iframe 46 try { 47 if ( window.location === window.parent.location && window.localStorage ) { 48 if ( window.localStorage.getItem(RLT_KEY) === token ) { 49 window.localStorage.removeItem(RLT_KEY); 50 } 51 } 52 } catch( e ) { 53 console.info("localstorage access for invalidate denied - probably blocked third-party access", window.location.href); 54 } 55 56 // invalidate in registered iframes 57 for ( const [ frameOrigin, frameWindow ] of registeredIframes ) { 58 if ( frameOrigin !== sourceOrigin ) { 59 rltInvalidateWindowToken( token, frameWindow, frameOrigin ); 60 } 61 } 62 63 // invalidate in parent 64 if ( parentOrigin && parentOrigin !== sourceOrigin && window.parent ) { 65 rltInvalidateWindowToken( token, window.parent, parentOrigin ); 66 } 67 } 68 69 window.rltInjectToken = function( token, target, origin ) { 70 if ( target && typeof target.postMessage === 'function' ) { 71 try { 72 target.postMessage( JSON.stringify( { 73 type: 'loginMessage', 74 data: { 75 event: 'login', 76 success: true, 77 type: 'rlt', 78 token: token, 79 sourceOrigin: window.location.origin, 80 }, 81 } ), origin ); 82 } catch ( err ) { 83 return; 84 } 85 } 86 }; 87 88 window.rltIsAuthenticated = function() { 89 return !! currentToken; 90 }; 91 92 window.rltGetToken = function() { 93 return currentToken; 94 }; 95 96 window.rltAddInitializationListener = function( listener ) { 97 // if RLT is already initialized, call the listener immediately 98 if ( hasBeenInitialized ) { 99 listener( currentToken ); 100 } else { 101 initializationListeners.push( listener ); 102 } 103 }; 104 105 // store the token in localStorage 106 window.rltStoreToken = function( token ) { 107 currentToken = token; 108 try { 109 if ( window.location === window.parent.location && window.localStorage ) { 110 window.localStorage.setItem( RLT_KEY, token ); 111 } 112 } catch( e ) { 113 console.info("localstorage access denied - probably blocked third-party access", window.location.href); 114 } 115 } 116 117 window.rltInitialize = function( config ) { 118 if ( ! config || typeof window.postMessage !== 'function' ) { 119 return; 120 } 121 122 currentToken = config.token; 123 iframeOrigins = config.iframeOrigins; 124 parentOrigin = config.parentOrigin; // needed? 125 126 // load token from localStorage if possible, but only in top level window 127 try { 128 if ( ! currentToken && window.location === window.parent.location && window.localStorage ) { 129 currentToken = window.localStorage.getItem(RLT_KEY); 130 } 131 } catch( e ) { 132 console.info("localstorage access denied - probably blocked third-party access", window.location.href); 133 } 134 135 // listen for RLT events from approved origins 136 window.addEventListener( 'message', function( e ) { 137 var message = e && e.data; 138 if ( typeof message === 'string' ) { 139 try { 140 message = JSON.parse( message ); 141 } catch ( err ) { 142 return; 143 } 144 } 145 146 var type = message && message.type; 147 var data = message && message.data; 148 149 if ( type === 'loginMessage' ) { 150 if ( data && data.type === 'rlt' && data.token !== currentToken ) { 151 // put into localStorage if running in top-level window (not iframe) 152 rltStoreToken( data.token ); 153 154 // send to registered iframes 155 for ( const [ frameOrigin, frameWindow ] of registeredIframes ) { 156 rltInjectToken( currentToken, frameWindow, frameOrigin ); 157 } 158 159 // send to the parent, unless the event was sent _by_ the parent 160 if ( parentOrigin && parentOrigin !== data.sourceOrigin && window.parent ) { 161 rltInjectToken( currentToken, window.parent, parentOrigin ); 162 } 163 } 164 } 165 166 if ( type === 'rltMessage' ) {
167 if ( data && data.event === 'invalidate' && data.token === currentToken ) { 168 rltInvalidateToken( data.token ); 169 } 170 171 if ( data && data.event === 'register' ) { 172 if ( rltShouldAuthorizeIframe( e.origin ) ) { 173 registeredIframes.push( [ e.origin, e.source ] ); 174 if ( currentToken ) { 175 rltInjectToken( currentToken, e.source, e.origin ); 176 } 177 } 178 } 179 } 180 } ); 181 182 initializationListeners.forEach( function( listener ) { 183 listener( currentToken ); 184 } ); 185 186 initializationListeners = []; 187 188 // inform the parent that we are ready to receive the RLT token 189 window.parent.postMessage( { 190 type: 'rltMessage', 191 data: { 192 event: 'register' 193 }, 194 }, '*' ); 195 196 hasBeenInitialized = true; 197 }; 198} )(); 199; 200( function () { 201 let cookies = document.cookie.split( /;\s*/ ); 202 let cookie = false; 203 204 for ( let i = 0; i < cookies.length; i++ ) { 205 if ( cookies[ i ].match( /^wp_api=/ ) ) { 206 cookies = cookies[ i ].split( '=' ); 207 cookie = cookies[ 1 ]; 208 break; 209 } 210 } 211 212 function setAllLinks( followId, wasFollowing ) { 213 const allLinks = document.querySelectorAll( `a.f-${ followId }` ); 214 215 if ( wasFollowing ) { 216 allLinks.forEach( ( el ) => { 217 el.classList.remove( 'wpcom-following' ); 218 el.classList.add( 'wpcom-follow' ); 219 el.textContent = 'Follow'; 220 } ); 221 } else { 222 allLinks.forEach( ( el ) => { 223 el.classList.add( 'wpcom-following' ); 224 el.classList.remove( 'wpcom-follow' ); 225 el.textContent = 'Following'; 226 } ); 227 } 228 } 229 230 function setAllRestLinks( followId, wasFollowing, followText, followingText ) { 231 const sel = `data-blog-id="${ followId }"`; 232 const allLinks = document.querySelectorAll( 233 `a.wpcom-follow-rest[${ sel }], a.wpcom-following-rest[${ sel }]` 234 ); 235 236 if ( wasFollowing ) { 237 allLinks.forEach( ( el ) => { 238 el.classList.remove( 'wpcom-following-rest' ); 239 el.classList.add( 'wpcom-follow-rest' ); 240 el.textContent = followText; 241 } ); 242 } else { 243 allLinks.forEach( ( el ) => { 244 el.classList.add( 'wpcom-following-rest' ); 245 el.classList.remove( 'wpcom-follow-rest' ); 246 el.textContent = followingText; 247 } ); 248 } 249 } 250 251 let bubbleInitDone = false; 252 let isBubbleVisible = false; 253 let bubbleTimeout; 254 255 window.wpFollowButton = { 256 enable: function () { 257 document.querySelectorAll( 'a.wpcom-follow, a.wpcom-following' ).forEach( ( link ) => 258 link.addEventListener( 'click', ( e ) => { 259 e.preventDefault(); 260 261 const blogId = link.getAttribute( 'data-id' ); 262 const blogUrl = link.getAttribute( 'data-url' ); 263 const isFollowing = link.classList.contains( 'wpcom-following' ); 264 265 let followId; 266 267 if ( blogId ) { 268 followId = blogId; 269 } else { 270 const fClass = Array.from( link.classList ).find( ( c ) => c.startsWith( 'f-' ) ); 271 if ( fClass ) { 272 followId = fClass.slice( 2 ); 273 } 274 } 275 276 if ( ! followId ) { 277 return; 278 } 279 280 // Ensure followId represents a number. 281 followId = String( parseInt( followId, 10 ) || 0 ); 282 283 // Preemptively update all links (assume success). 284 setAllLinks( followId, isFollowing ); 285 286 const href = link.getAttribute( 'href' ); 287 if ( ! href ) { 288 return; 289 } 290 291 const url = new URL( href, window.location ); 292 293 fetch( url.origin + '/wp-admin/admin-ajax.php', { 294 method: 'POST', 295 headers: { 296 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 297 Accept: 'application/json', 298 }, 299 body: new URLSearchParams( { 300 action: isFollowing ? 'ab_unsubscribe_from_blog' : 'ab_subscribe_to_blog', 301 _wpnonce: url.searchParams.get( '_wpnonce' ) || undefined, 302 blog_id: blogId, 303 blog_url: blogUrl, 304 source: url.searchParams.get( 'src' ) || undefined, 305 } ).toString(), 306 } ) 307 .then( ( response ) => { 308 if ( ! response.ok ) { 309 throw new Error(); 310 } 311 return response.json(); 312 } ) 313 .then( ( json ) => { 314 if ( typeof json.errors === 'object' ) { 315 throw new Error(); 316 } 317 } ) 318 .catch( () => { 319 // Error; toggle back. 320 setAllLinks( followId, ! isFollowing ); 321 } ); 322 } ) 323 ); 324 }, 325 326 enable_rest: function ( link ) { 327 link.addEventListener( 'click', ( e ) => { 328 e.preventDefault(); 329 let blogId = link.getAttribute( 'data-blog-id' ); 330 const followText = link.getAttribute( 'data-follow-text' ); 331 const followingText = link.getAttribute( 'data-following-text' ); 332 const wasFollowing = link.classList.contains( 'wpcom-following-rest' ); 333 let restPath = `/sites/${ blogId }/follows/new`; 334 335 if ( wasFollowing ) { 336 restPath = `/sites/${ blogId }/follows/mine/delete`; 337 } 338 // Ensure followId represents a number. 339 blogId = String( parseInt( blogId, 10 ) || 0 ); 340 341 // Select and update ALL follow buttons on the page (could be more than one). 342 setAllRestLinks( blogId, wasFollowing, followText, followingText ); 343 344 this.ajax( { 345 type: 'POST', 346 path: restPath, 347 success: function ( res ) { 348 if ( ! res.success ) { 349 // Error; toggle back. 350 setAllRestLinks( blogId, ! wasFollowing, followText, followingText ); 351 this.hideBubble(); 352 } 353 }, 354 error: function () { 355 // Error; toggle back. 356 setAllRestLinks( blogId, ! wasFollowing, followText, followingText ); 357 this.hideBubble(); 358 }, 359 } ); 360 361 // Show post-follow bubble.
362 if ( ! wasFollowing ) { 363 e.stopPropagation(); 364 this.showBubble( link ); 365 this.bumpStat( link.getAttribute( 'data-stat-src' ) ); 366 } 367 } ); 368 369 // Show unfollow text on hover. 370 link.addEventListener( 'mouseenter', () => { 371 const isFollowing = link.classList.contains( 'wpcom-following-rest' ); 372 if ( isFollowing ) { 373 link.textContent = link.getAttribute( 'data-following-hover-text' ); 374 } 375 } ); 376 link.addEventListener( 'mouseleave', () => { 377 const isFollowing = link.classList.contains( 'wpcom-following-rest' ); 378 if ( isFollowing ) { 379 link.textContent = link.getAttribute( 'data-following-text' ); 380 } 381 } ); 382 }, 383 384 showBubble: function ( link ) { 385 const pos = { 386 left: link.offsetLeft, 387 top: link.offsetTop, 388 }; 389 390 let bubble = document.querySelector( 'div.follow-bubble' ); 391 if ( ! bubble ) { 392 bubble = document.createElement( 'div' ); 393 bubble.classList.add( 'wpcom-bubble', 'action-bubble', 'follow-bubble' ); 394 } 395 bubble.innerHTML = ` 396 <div class="bubble-txt"> 397 New posts from this blog will now appear in 398 <a style='float:none;' href='http://wordpress.com/reader/'>your reader</a>. 399 </div> 400 `; 401 link.parentElement.appendChild( bubble ); 402 403 bubble.style.display = 'block'; 404 const left = pos.left + link.offsetWidth / 2 - bubble.offsetWidth / 2; 405 const top = pos.top + link.offsetHeight + 8; 406 407 bubble.style.left = left + 'px'; 408 bubble.style.top = top + 'px'; 409 bubble.classList.add( 'fadein' ); 410 isBubbleVisible = true; 411 412 bubbleTimeout = setTimeout( this.hideBubble, 10000 ); 413 414 if ( bubbleInitDone ) { 415 return; 416 } 417 418 const clickHandler = ( e ) => { 419 if ( 420 ! e.target.classList.contains( 'follow-bubble' ) && 421 ! e.target.closest( 'div.follow-bubble' ) 422 ) { 423 this.hideBubble(); 424 } 425 }; 426 427 document.body.addEventListener( 'click', clickHandler ); 428 document.body.addEventListener( 'touchstart', clickHandler ); 429 430 document.addEventListener( 'scroll', () => { 431 if ( ! isBubbleVisible ) { 432 return; 433 } 434 this.hideBubble(); 435 } ); 436 437 bubbleInitDone = true; 438 }, 439 440 hideBubble: function () { 441 const bubble = document.querySelector( 'div.follow-bubble' ); 442 bubble.style.display = 'none'; 443 isBubbleVisible = false; 444 clearTimeout( bubbleTimeout ); 445 }, 446 447 // Common method for rendering a follow button from the object data 448 create: function ( { params }, source ) { 449 const isFollowing = params.is_following; 450 451 const followLink = document.createElement( 'a' ); 452 followLink.classList.add( isFollowing ? 'wpcom-following-rest' : 'wpcom-follow-rest' ); 453 454 Object.entries( { 455 href: `http://public-api.wordpress.com/sites/${ encodeURIComponent( 456 params.site_id 457 ) }/follows`, 458 title: `${ params.blog_title } (${ params.blog_domain })`, 459 'data-blog-id': params.site_id, 460 'data-stat-src': params[ 'stat-source' ], 461 'data-follow-text': params[ 'follow-text' ], 462 'data-following-text': params[ 'following-text' ], 463 'data-following-hover-text': params[ 'following-hover-text' ], 464 } ).forEach( ( [ key, val ] ) => followLink.setAttribute( key, val ) ); 465 466 followLink.textContent = isFollowing ? params[ 'following-text' ] : params[ 'follow-text' ]; 467 468 const followButton = document.createElement( 'div' ); 469 followButton.classList.add( 'wpcom-follow-container' ); 470 followButton.classList.add( 'wpcom-follow-attached' ); 471 followButton.style.display = 'inline-block'; 472 followButton.appendChild( followLink ); 473 474 this.enable_rest( followLink, source ); 475 return followButton; 476 }, 477 478 createAll: function () { 479 document 480 .querySelectorAll( '.wpcom-follow-container:not(.wpcom-follow-attached)' ) 481 .forEach( ( el ) => { 482 const json = JSON.parse( el.getAttribute( 'data-json' ) || '{}' ); 483 el.replaceWith( 484 this.create( json, el.getAttribute( 'data-follow-source' ) ) 485 ); 486 } ); 487 }, 488 489 ajax: function ( options ) { 490 if ( document.location.host === 'public-api.wordpress.com' ) { 491 // Regular AJAX call. 492 fetch( 'https://public-api.wordpress.com/rest/v1' + options.path, { 493 method: options.type, 494 headers: { 495 Authorization: `X-WPCOOKIE ${ cookie }:1:${ document.location.host }`, 496 Accept: 'application/json', 497 } 498 } ) 499 .then( ( response ) => { 500 if ( ! response.ok ) { 501 throw new Error(); 502 } 503 return response.json(); 504 } ) 505 .then( ( json ) => { 506 if ( typeof json.errors === 'object' ) { 507 throw new Error(); 508 } 509 options.success( json ); 510 } ) 511 .catch( () => { 512 options.error(); 513 } ); 514 } else { 515 // Proxied AJAX call. 516 const request = { 517 path: options.path, 518 method: options.type, 519 }; 520 521 const proxyRequestHandler = 522 window.WPCOM_Proxy_Request || ( window.jQuery && window.jQuery.wpcom_proxy_request ); 523 524 if ( ! proxyRequestHandler ) { 525 console.error( 'Cannot make AJAX call: missing proxy request handler.' ); 526 setTimeout( () => options.error() ); 527 return; 528 } 529 530 proxyRequestHandler( request ).done( ( response, statusCode ) => { 531 if ( statusCode === 200 ) { 532 options.success( response ); 533 } else { 534 options.error( statusCode ); 535 } 536 } ); 537 } 538 }, 539 540 bumpStat: function ( source ) { 541 new Image().src = `${ 542 document.location.protocol 543 }//pixel.wp.com/g.gif?v=wpcom-no-pv&x_follow_source=${ encodeURIComponent( 544 source 545 ) }&baba=${ Math.random() }`; 546 }, 547 }; 548
549 function init() { 550 window.wpFollowButton.enable(); 551 window.wpFollowButton.createAll(); 552 } 553 554 if ( document.readyState !== 'loading' ) { 555 init(); 556 } else { 557 document.addEventListener( 'DOMContentLoaded', init ); 558 } 559} )(); 560; 561( function () { 562 'use strict'; 563 564 function init() { 565 document.querySelectorAll( '.bump-view' ).forEach( function ( el ) { 566 el.addEventListener( 'click', function ( e ) { 567 var statarg = 'wref='; 568 var statval = el.getAttribute( 'data-bump-view' ); 569 var href = el.getAttribute( 'href' ); 570 el.setAttribute( 571 'href', 572 href + ( href.indexOf( '?' ) !== -1 ? '&' : '?' ) + statarg + statval 573 ); 574 } ); 575 } ); 576 } 577 578 if ( document.readyState !== 'loading' ) { 579 init(); 580 } else { 581 document.addEventListener( 'DOMContentLoaded', init ); 582 } 583} )(); 584; 585!function(){function e(){const e=document.createElement("iframe");e.style.cssText="display:none",e.name="__tcfapiLocator",document.body.appendChild(e)}window.__tcfapi=function(){let e=[].slice.call(arguments),t=e[0],n=e[2];"ping"===t?"function"==typeof n&&n({gdprApplies:!1,cmpLoaded:!1,cmpStatus:"stub",displayStatus:"disabled",apiVersion:"2"}):"getTCData"!==t&&"addEventListener"!==t||"function"==typeof n&&n({gdprApplies:!1,tcfPolicyVersion:5,cmpId:258,cmpVersion:1},!0)},window.addEventListener("message",(function(e){let t="string"==typeof e.data,n={};try{n=t?JSON.parse(e.data):e.data}catch(e){}let a=n.__tcfapiCall;a&&window.__tcfapi(a.command,a.version,(function(n,c){let i={__tcfapiReturn:{returnValue:n,success:c,callId:a.callId}};t&&(i=JSON.stringify(i)),e&&e.source&&e.source.postMessage&&e.source.postMessage(i,"*")}),a.parameter)}),!1),"loading"!==document.readyState?e():document.addEventListener("DOMContentLoaded",e)}();;
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.