PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.7
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.7
16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 All 506 releases
jetpack / modules / wordads / js / wordads-ccpa.js

wordads-ccpa.js in Jetpack – WP Security, Backup, Speed, & Growth 16.3-a.7, at modules/wordads/js/wordads-ccpa.js

356 lines 10.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Outputs Javascript to handle California IP detection, consent modal, and setting of default cookies.
3 */
4 ( function () {
5 /* global ccpaSettings */
6
7 // Minimal Mozilla Cookie library.
8 // https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie/Simple_document.cookie_framework
9 var cookieLib = {
10 getItem: function ( e ) {
11 return (
12 ( e &&
13 decodeURIComponent(
14 document.cookie.replace(
15 new RegExp(
16 '(?:(?:^|.*;)\\s*' +
17 encodeURIComponent( e ).replace( /[-.+*]/g, '\\$&' ) +
18 '\\s*\\=\\s*([^;]*).*$)|^.*$'
19 ),
20 '$1'
21 )
22 ) ) ||
23 null
24 );
25 },
26 setItem: function ( e, o, n, t, r, i ) {
27 if ( ! e || /^(?:expires|max-age|path|domain|secure)$/i.test( e ) ) {
28 return ! 1;
29 }
30 var c = '';
31 if ( n ) {
32 switch ( n.constructor ) {
33 case Number:
34 c = n === 1 / 0 ? '; expires=Fri, 31 Dec 9999 23:59:59 GMT' : '; max-age=' + n;
35 break;
36 case String:
37 c = '; expires=' + n;
38 break;
39 case Date:
40 c = '; expires=' + n.toUTCString();
41 }
42 }
43 return (
44 ( 'rootDomain' !== r && '.rootDomain' !== r ) ||
45 ( r =
46 ( '.rootDomain' === r ? '.' : '' ) +
47 document.location.hostname.split( '.' ).slice( -2 ).join( '.' ) ),
48 ( document.cookie =
49 encodeURIComponent( e ) +
50 '=' +
51 encodeURIComponent( o ) +
52 c +
53 ( r ? '; domain=' + r : '' ) +
54 ( t ? '; path=' + t : '' ) +
55 ( i ? '; secure' : '' ) ),
56 ! 0
57 );
58 },
59 };
60
61 // Implement IAB USP API.
62 window.__uspapi = function ( command, version, callback ) {
63 // Validate callback.
64 if ( typeof callback !== 'function' ) {
65 return;
66 }
67
68 // Validate the given command.
69 if ( command !== 'getUSPData' || version !== 1 ) {
70 callback( null, false );
71 return;
72 }
73
74 // Check for GPC. If set, override any stored cookie.
75 if ( navigator.globalPrivacyControl ) {
76 callback( { version: 1, uspString: '1YYN' }, true );
77 return;
78 }
79
80 // Check for cookie.
81 var consent = cookieLib.getItem( 'usprivacy' );
82
83 // Invalid cookie.
84 if ( null === consent ) {
85 callback( null, false );
86 return;
87 }
88
89 // Everything checks out. Fire the provided callback with the consent data.
90 callback( { version: 1, uspString: consent }, true );
91 };
92
93 var setDefaultOptInCookie = function () {
94 var value = ccpaSettings.defaultOptInCookieString;
95 var domain =
96 '.wordpress.com' === location.hostname.slice( -14 ) ? '.rootDomain' : location.hostname;
97 cookieLib.setItem( 'usprivacy', value, 365 * 24 * 60 * 60, '/', domain );
98 };
99
100 var setDefaultOptOutCookie = function () {
101 var value = ccpaSettings.defaultOptOutCookieString;
102 var domain =
103 '.wordpress.com' === location.hostname.slice( -14 ) ? '.rootDomain' : location.hostname;
104 cookieLib.setItem( 'usprivacy', value, 24 * 60 * 60, '/', domain );
105 };
106
107 var setDefaultNotApplicableCookie = function () {
108 var value = '1---';
109 var domain =
110 '.wordpress.com' === location.hostname.slice( -14 ) ? '.rootDomain' : location.hostname;
111 cookieLib.setItem( 'usprivacy', value, 24 * 60 * 60, '/', domain );
112 };
113
114 var setCcpaAppliesCookie = function ( value ) {
115 var domain =
116 '.wordpress.com' === location.hostname.slice( -14 ) ? '.rootDomain' : location.hostname;
117 cookieLib.setItem( 'ccpa_applies', value, 24 * 60 * 60, '/', domain );
118 };
119
120 var injectLoadingMessage = function () {
121 var wrapper = document.createElement( 'div' );
122 document.body.insertBefore( wrapper, document.body.firstElementChild );
123 wrapper.outerHTML =
124 '<div id="ccpa-loading" class="cleanslate ccpa__loading-wrapper">' +
125 '<div class="ccpa__loading-overlay">' +
126 '<span class="ccpa__loading-message">' +
127 ccpaSettings.strings.pleaseWait +
128 '...</span>' +
129 '</div>' +
130 '</div>';
131 };
132
133 var destroyModal = function () {
134 var node = document.querySelector( '#ccpa-modal' );
135
136 if ( node ) {
137 node.parentElement.removeChild( node );
138 }
139 };
140
141 var injectModal = function () {
142 destroyModal();
143
144 injectLoadingMessage();
145
146 var request = new XMLHttpRequest();
147 request.open(
148 'GET',
149 ccpaSettings.ajaxUrl + '?action=privacy_optout_markup&security=' + ccpaSettings.ajaxNonce,
150 true
151 );
152 request.onreadystatechange = function () {
153 if ( 4 === this.readyState ) {
154 if ( 200 === this.status ) {
155 document.getElementById( 'ccpa-loading' ).remove();
156 var wrapper = document.createElement( 'div' );
157 document.body.insertBefore( wrapper, document.body.firstElementChild );
158 wrapper.outerHTML = this.response;
159 document.getElementById( 'ccpa-opt-out' ).focus();
160
161 var optOut = document.querySelector( '#ccpa-modal .opt-out' );
162 optOut.addEventListener( 'click', function ( e ) {
163 var post = new XMLHttpRequest();
164 post.open( 'POST', ccpaSettings.ajaxUrl, true );
165 post.setRequestHeader(
166 'Content-Type',
167 'application/x-www-form-urlencoded; charset=UTF-8'
168 );
169 post.onreadystatechange = function () {
170 if ( 4 === this.readyState ) {
171 if ( 200 === this.status ) {
172 var result = JSON.parse( this.response );
173
174 if ( result && result.success ) {
175 // Note: Cooke is set in HTTP response from POST, so only need to update the toggle switch state.
176 if ( result.data ) {
177 e.target.parentNode.classList.add( 'is-checked' );
178 e.target.parentNode.parentNode.classList.add( 'is-checked' );
179 } else {
180 e.target.parentNode.classList.remove( 'is-checked' );
181 e.target.parentNode.parentNode.classList.remove( 'is-checked' );
182 }
183 }
184 }
185 }
186 };
187 post.send(
188 'action=privacy_optout&optout=' +
189 e.target.checked +
190 '&security=' +
191 ccpaSettings.ajaxNonce
192 );
193 } );
194
195 // Set initial toggle status based on cookie data.
196 var usprivacyCookie = cookieLib.getItem( 'usprivacy' );
197 var optout = usprivacyCookie && 'Y' === usprivacyCookie[ 2 ];
198 var toggle = document.querySelector( '#ccpa-modal .opt-out' );
199
200 toggle.checked = optout;
201
202 if ( optout ) {
203 toggle.parentNode.classList.add( 'is-checked' );
204 toggle.parentNode.parentNode.classList.add( 'is-checked' );
205 }
206
207 var buttons = document.querySelectorAll( '#ccpa-modal .components-button' );
208 Array.prototype.forEach.call( buttons, function ( el ) {
209 el.addEventListener( 'click', function () {
210 destroyModal();
211 } );
212 } );
213 }
214 }
215 };
216
217 request.send();
218 };
219
220 var dispatchInitializedEvent = function ( ccpaApplies ) {
221 // Dispatches a custom event with data indicating if the CCPA applies or not once it has been determined.
222 // Sites can listen for this event and do additional processing, e.g. showing or hiding additional elements.
223 var event = document.createEvent( 'CustomEvent' );
224 event.initCustomEvent( 'wordads-ccpa-initialized', true, false, { ccpaApplies: ccpaApplies } );
225 document.dispatchEvent( event );
226 };
227
228 var initialize = function ( ccpaApplies, usprivacyCookie ) {
229 // Get any Do Not Sell links on the page.
230 var dnsLinks = document.querySelectorAll( '.ccpa-do-not-sell' );
231
232 // No usprivacy cookie, so we need to set it.
233 if ( null === usprivacyCookie ) {
234 if ( ccpaApplies ) {
235 if ( 0 === dnsLinks.length ) {
236 // Could not find a Do Not Sell link as required, so default to opt-OUT just to be safe.
237 setDefaultOptOutCookie();
238 } else {
239 // Found a Do Not Sell link, so set default opt-in.
240 setDefaultOptInCookie();
241 }
242 } else {
243 // CCPA does not apply.
244 setDefaultNotApplicableCookie();
245 }
246 }
247
248 // If CCPA does not apply, and we are not overriding it for admins, then we can stop here.
249 if ( ! ccpaApplies && 'false' === ccpaSettings.forceApplies ) {
250 dispatchInitializedEvent( false );
251 return;
252 }
253
254 // Displays Do Not Sell links and adds handlers to display the modal when clicked.
255 Array.prototype.forEach.call( dnsLinks, function ( dnsLink ) {
256 dnsLink.addEventListener( 'click', function ( e ) {
257 e.preventDefault();
258
259 if ( ! ccpaSettings.stylesLoaded ) {
260 // Load wordads-ccpa.min.css.
261 var ccpaCss = document.createElement( 'link' );
262 ccpaCss.rel = 'stylesheet';
263 ccpaCss.type = 'text/css';
264 ccpaCss.href = ccpaSettings.ccpaCssUrl;
265 document.getElementsByTagName( 'HEAD' )[ 0 ].appendChild( ccpaCss );
266
267 ccpaSettings.stylesLoaded = true;
268 }
269
270 injectModal();
271 } );
272
273 // Make the link visible.
274 dnsLink.style.display = '';
275 } );
276
277 // CCPA applies (or we're forcing it to display for admins). Let any listeners know.
278 dispatchInitializedEvent( true );
279 };
280
281 // Setup CCPA on DOM loaded.
282 document.addEventListener( 'DOMContentLoaded', function () {
283 // Look for usprivacy cookies first.
284 var usprivacyCookie = cookieLib.getItem( 'usprivacy' );
285
286 // Found a usprivacy cookie.
287 if ( null !== usprivacyCookie ) {
288 // CCPA does not apply.
289 if ( '1---' === usprivacyCookie ) {
290 initialize( false, usprivacyCookie );
291 } else {
292 // CCPA applies.
293 initialize( true, usprivacyCookie );
294 }
295
296 // No more processing needed.
297 return;
298 }
299
300 // We don't have a usprivacy cookie, so check to see if we have a CCPA applies cookie.
301 var ccpaCookie = cookieLib.getItem( 'ccpa_applies' );
302
303 // No CCPA applies cookie found, so we'll need to geolocate if this visitor is from applicable US state.
304 // This needs to happen client side because we do not have region geo data in our $SERVER headers,
305 // only country data -- therefore we can't vary cache on the region.
306 if ( null === ccpaCookie ) {
307 var request = new XMLHttpRequest();
308 request.open( 'GET', 'https://public-api.wordpress.com/geo/', true );
309
310 request.onreadystatechange = function () {
311 if ( 4 === this.readyState ) {
312 if ( 200 === this.status ) {
313 // Got a geo response. Parse out the region data.
314 var data = JSON.parse( this.response );
315 var region = data.region ? data.region.toLowerCase() : '';
316 var ccpaApplies =
317 [
318 'california',
319 'colorado',
320 'connecticut',
321 'delaware',
322 'indiana',
323 'iowa',
324 'montana',
325 'new jersey',
326 'oregon',
327 'tennessee',
328 'texas',
329 'utah',
330 'virginia',
331 ].indexOf( region ) > -1;
332
333 // Set CCPA applies cookie. This keeps us from having to make a geo request too frequently.
334 setCcpaAppliesCookie( ccpaApplies );
335
336 // Perform the rest of the initialization.
337 initialize( ccpaApplies, null );
338 } else {
339 // Geolocation request failed, so default to CCPA applies just to be safe.
340 setCcpaAppliesCookie( true );
341
342 // Perform the rest of the initialization.
343 initialize( true, null );
344 }
345 }
346 };
347
348 // Send the geo request.
349 request.send();
350 } else {
351 // We found a CCPA applies cookie. Continue with initialization.
352 initialize( 'true' === ccpaCookie, null );
353 }
354 } );
355 } )();
356