/**
* Outputs Javascript to handle California IP detection, consent modal, and setting of default cookies.
*/
( function () {
/* global ccpaSettings */
// Minimal Mozilla Cookie library.
// https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie/Simple_document.cookie_framework
var cookieLib = {
getItem: function ( e ) {
return (
( e &&
decodeURIComponent(
document.cookie.replace(
new RegExp(
'(?:(?:^|.*;)\\s*' +
encodeURIComponent( e ).replace( /[-.+*]/g, '\\$&' ) +
'\\s*\\=\\s*([^;]*).*$)|^.*$'
),
'$1'
)
) ) ||
null
);
},
setItem: function ( e, o, n, t, r, i ) {
if ( ! e || /^(?:expires|max-age|path|domain|secure)$/i.test( e ) ) {
return ! 1;
}
var c = '';
if ( n ) {
switch ( n.constructor ) {
case Number:
c = n === 1 / 0 ? '; expires=Fri, 31 Dec 9999 23:59:59 GMT' : '; max-age=' + n;
break;
case String:
c = '; expires=' + n;
break;
case Date:
c = '; expires=' + n.toUTCString();
}
}
return (
( 'rootDomain' !== r && '.rootDomain' !== r ) ||
( r =
( '.rootDomain' === r ? '.' : '' ) +
document.location.hostname.split( '.' ).slice( -2 ).join( '.' ) ),
( document.cookie =
encodeURIComponent( e ) +
'=' +
encodeURIComponent( o ) +
c +
( r ? '; domain=' + r : '' ) +
( t ? '; path=' + t : '' ) +
( i ? '; secure' : '' ) ),
! 0
);
},
};
// Implement IAB USP API.
window.__uspapi = function ( command, version, callback ) {
// Validate callback.
if ( typeof callback !== 'function' ) {
return;
}
// Validate the given command.
if ( command !== 'getUSPData' || version !== 1 ) {
callback( null, false );
return;
}
// Check for GPC. If set, override any stored cookie.
if ( navigator.globalPrivacyControl ) {
callback( { version: 1, uspString: '1YYN' }, true );
return;
}
// Check for cookie.
var consent = cookieLib.getItem( 'usprivacy' );
// Invalid cookie.
if ( null === consent ) {
callback( null, false );
return;
}
// Everything checks out. Fire the provided callback with the consent data.
callback( { version: 1, uspString: consent }, true );
};
var setDefaultOptInCookie = function () {
var value = ccpaSettings.defaultOptInCookieString;
var domain =
'.wordpress.com' === location.hostname.slice( -14 ) ? '.rootDomain' : location.hostname;
cookieLib.setItem( 'usprivacy', value, 365 * 24 * 60 * 60, '/', domain );
};
var setDefaultOptOutCookie = function () {
var value = ccpaSettings.defaultOptOutCookieString;
var domain =
'.wordpress.com' === location.hostname.slice( -14 ) ? '.rootDomain' : location.hostname;
cookieLib.setItem( 'usprivacy', value, 24 * 60 * 60, '/', domain );
};
var setDefaultNotApplicableCookie = function () {
var value = '1---';
var domain =
'.wordpress.com' === location.hostname.slice( -14 ) ? '.rootDomain' : location.hostname;
cookieLib.setItem( 'usprivacy', value, 24 * 60 * 60, '/', domain );
};
var setCcpaAppliesCookie = function ( value ) {
var domain =
'.wordpress.com' === location.hostname.slice( -14 ) ? '.rootDomain' : location.hostname;
cookieLib.setItem( 'ccpa_applies', value, 24 * 60 * 60, '/', domain );
};
var injectLoadingMessage = function () {
var wrapper = document.createElement( 'div' );
document.body.insertBefore( wrapper, document.body.firstElementChild );
wrapper.outerHTML =
'<div id="ccpa-loading" class="cleanslate ccpa__loading-wrapper">' +
'<div class="ccpa__loading-overlay">' +
'<span class="ccpa__loading-message">' +
ccpaSettings.strings.pleaseWait +
'...</span>' +
'</div>' +
'</div>';
};
var destroyModal = function () {
var node = document.querySelector( '#ccpa-modal' );
if ( node ) {
node.parentElement.removeChild( node );
}
};
var injectModal = function () {
destroyModal();
injectLoadingMessage();
var request = new XMLHttpRequest();
request.open(
'GET',
ccpaSettings.ajaxUrl + '?action=privacy_optout_markup&security=' + ccpaSettings.ajaxNonce,
true
);
request.onreadystatechange = function () {
if ( 4 === this.readyState ) {
if ( 200 === this.status ) {
document.getElementById( 'ccpa-loading' ).remove();
var wrapper = document.createElement( 'div' );
document.body.insertBefore( wrapper, document.body.firstElementChild );
wrapper.outerHTML = this.response;
document.getElementById( 'ccpa-opt-out' ).focus();
var optOut = document.querySelector( '#ccpa-modal .opt-out' );
optOut.addEventListener( 'click', function ( e ) {
var post = new XMLHttpRequest();
post.open( 'POST', ccpaSettings.ajaxUrl, true );
post.setRequestHeader(
'Content-Type',
'application/x-www-form-urlencoded; charset=UTF-8'
);
post.onreadystatechange = function () {
if ( 4 === this.readyState ) {
if ( 200 === this.status ) {
var result = JSON.parse( this.response );
if ( result && result.success ) {
// Note: Cooke is set in HTTP response from POST, so only need to update the toggle switch state.
if ( result.data ) {
e.target.parentNode.classList.add( 'is-checked' );
e.target.parentNode.parentNode.classList.add( 'is-checked' );
} else {
e.target.parentNode.classList.remove( 'is-checked' );
e.target.parentNode.parentNode.classList.remove( 'is-checked' );
}
}
}
}
};
post.send(
'action=privacy_optout&optout=' +
e.target.checked +
'&security=' +
ccpaSettings.ajaxNonce
);
} );
// Set initial toggle status based on cookie data.
var usprivacyCookie = cookieLib.getItem( 'usprivacy' );
var optout = usprivacyCookie && 'Y' === usprivacyCookie[ 2 ];
var toggle = document.querySelector( '#ccpa-modal .opt-out' );
toggle.checked = optout;
if ( optout ) {
toggle.parentNode.classList.add( 'is-checked' );
toggle.parentNode.parentNode.classList.add( 'is-checked' );
}
var buttons = document.querySelectorAll( '#ccpa-modal .components-button' );
Array.prototype.forEach.call( buttons, function ( el ) {
el.addEventListener( 'click', function () {
destroyModal();
} );
} );
}
}
};
request.send();
};
var dispatchInitializedEvent = function ( ccpaApplies ) {
// Dispatches a custom event with data indicating if the CCPA applies or not once it has been determined.
// Sites can listen for this event and do additional processing, e.g. showing or hiding additional elements.
var event = document.createEvent( 'CustomEvent' );
event.initCustomEvent( 'wordads-ccpa-initialized', true, false, { ccpaApplies: ccpaApplies } );
document.dispatchEvent( event );
};
var initialize = function ( ccpaApplies, usprivacyCookie ) {
// Get any Do Not Sell links on the page.
var dnsLinks = document.querySelectorAll( '.ccpa-do-not-sell' );
// No usprivacy cookie, so we need to set it.
if ( null === usprivacyCookie ) {
if ( ccpaApplies ) {
if ( 0 === dnsLinks.length ) {
// Could not find a Do Not Sell link as required, so default to opt-OUT just to be safe.
setDefaultOptOutCookie();
} else {
// Found a Do Not Sell link, so set default opt-in.
setDefaultOptInCookie();
}
} else {
// CCPA does not apply.
setDefaultNotApplicableCookie();
}
}
// If CCPA does not apply, and we are not overriding it for admins, then we can stop here.
if ( ! ccpaApplies && 'false' === ccpaSettings.forceApplies ) {
dispatchInitializedEvent( false );
return;
}
// Displays Do Not Sell links and adds handlers to display the modal when clicked.
Array.prototype.forEach.call( dnsLinks, function ( dnsLink ) {
dnsLink.addEventListener( 'click', function ( e ) {
e.preventDefault();
if ( ! ccpaSettings.stylesLoaded ) {
// Load wordads-ccpa.min.css.
var ccpaCss = document.createElement( 'link' );
ccpaCss.rel = 'stylesheet';
ccpaCss.type = 'text/css';
ccpaCss.href = ccpaSettings.ccpaCssUrl;
document.getElementsByTagName( 'HEAD' )[ 0 ].appendChild( ccpaCss );
ccpaSettings.stylesLoaded = true;
}
injectModal();
} );
// Make the link visible.
dnsLink.style.display = '';
} );
// CCPA applies (or we're forcing it to display for admins). Let any listeners know.
dispatchInitializedEvent( true );
};
// Setup CCPA on DOM loaded.
document.addEventListener( 'DOMContentLoaded', function () {
// Look for usprivacy cookies first.
var usprivacyCookie = cookieLib.getItem( 'usprivacy' );
// Found a usprivacy cookie.
if ( null !== usprivacyCookie ) {
// CCPA does not apply.
if ( '1---' === usprivacyCookie ) {
initialize( false, usprivacyCookie );
} else {
// CCPA applies.
initialize( true, usprivacyCookie );
}
// No more processing needed.
return;
}
// We don't have a usprivacy cookie, so check to see if we have a CCPA applies cookie.
var ccpaCookie = cookieLib.getItem( 'ccpa_applies' );
// No CCPA applies cookie found, so we'll need to geolocate if this visitor is from applicable US state.
// This needs to happen client side because we do not have region geo data in our $SERVER headers,
// only country data -- therefore we can't vary cache on the region.
if ( null === ccpaCookie ) {
var request = new XMLHttpRequest();
request.open( 'GET', 'https://public-api.wordpress.com/geo/', true );
request.onreadystatechange = function () {
if ( 4 === this.readyState ) {
if ( 200 === this.status ) {
// Got a geo response. Parse out the region data.
var data = JSON.parse( this.response );
var region = data.region ? data.region.toLowerCase() : '';
var ccpaApplies =
[
'california',
'colorado',
'connecticut',
'delaware',
'indiana',
'iowa',
'montana',
'new jersey',
'oregon',
'tennessee',
'texas',
'utah',
'virginia',
].indexOf( region ) > -1;
// Set CCPA applies cookie. This keeps us from having to make a geo request too frequently.
setCcpaAppliesCookie( ccpaApplies );
// Perform the rest of the initialization.
initialize( ccpaApplies, null );
} else {
// Geolocation request failed, so default to CCPA applies just to be safe.
setCcpaAppliesCookie( true );
// Perform the rest of the initialization.
initialize( true, null );
}
}
};
// Send the geo request.
request.send();
} else {
// We found a CCPA applies cookie. Continue with initialization.
initialize( 'true' === ccpaCookie, null );
}
} );
} )();