1( function ( wc_order_attribution ) { 2 'use strict'; 3 // Cache params reference for shorter reusability. 4 const params = wc_order_attribution.params; 5 6 // Helper functions. 7 const $ = document.querySelector.bind( document ); 8 const propertyAccessor = ( obj, path ) => path.split( '.' ).reduce( ( acc, part ) => acc && acc[ part ], obj ); 9 const returnNull = () => null; 10 const stringifyFalsyInputValue = ( value ) => value === null || value === undefined ? '' : value; 11 12 // Hardcode Checkout store key (`wc.wcBlocksData.CHECKOUT_STORE_KEY`), as we no longer have `wc-blocks-checkout` as a dependency. 13 const CHECKOUT_STORE_KEY = 'wc/store/checkout'; 14 15 /** 16 * Get the order attribution data. 17 * 18 * Returns object full of `null`s if tracking is disabled or if sourcebuster.js is blocked. 19 * 20 * @returns {Object} Schema compatible object. 21 */ 22 wc_order_attribution.getAttributionData = function() { 23 const accessor = params.allowTracking && isSbjsAvailable() ? propertyAccessor : returnNull; 24 const getter = isSbjsAvailable() ? sbjs.get : {}; 25 const entries = Object.entries( wc_order_attribution.fields ) 26 .map( ( [ key, property ] ) => [ key, accessor( getter, property ) ] ); 27 return Object.fromEntries( entries ); 28 } 29 30 /** 31 * Update `wc_order_attribution` input elements' values. 32 * 33 * @param {Object} values Object containing field values. 34 */ 35 function updateFormValues( values ) { 36 // Update `<wc-order-attribution-inputs>` elements if any exist. 37 for( const element of document.querySelectorAll( 'wc-order-attribution-inputs' ) ) { 38 element.values = values; 39 } 40 41 }; 42 43 /** 44 * Update Checkout extension data. 45 * 46 * @param {Object} values Object containing field values. 47 */ 48 function updateCheckoutBlockData( values ) { 49 // Update Checkout block data if available. 50 if ( 51 window.wp && 52 window.wp.data && 53 window.wp.data.dispatch && 54 window.wc && 55 window.wc.wcBlocksData 56 ) { 57 window.wp.data 58 .dispatch( window.wc.wcBlocksData.CHECKOUT_STORE_KEY ) 59 .setExtensionData( 60 'woocommerce/order-attribution', 61 values, 62 true 63 ); 64 } 65 } 66 67 /** 68 * Determine whether sourcebuster.js is available. 69 * 70 * @returns {boolean} Whether sourcebuster.js is available. 71 */ 72 function isSbjsAvailable() { 73 return typeof sbjs !== 'undefined'; 74 } 75 76 /** 77 * Initialize sourcebuster & set data, or clear cookies & data. 78 * 79 * @param {boolean} allow Whether to allow tracking or disable it. 80 */ 81 wc_order_attribution.setOrderTracking = function( allow ) { 82 params.allowTracking = allow; 83 if ( ! allow ) { 84 // Reset cookies, and clear form data. 85 removeTrackingCookies(); 86 } else if ( ! isSbjsAvailable() ) { 87 return; // Do nothing, as sourcebuster.js is not loaded. 88 } else { 89 // If not done yet, initialize sourcebuster.js which populates `sbjs.get` object. 90 sbjs.init( { 91 lifetime: Number( params.lifetime ), 92 session_length: Number( params.session ), 93 base64: Boolean( params.base64 ), 94 timezone_offset: '0', // utc 95 } ); 96 } 97 const values = wc_order_attribution.getAttributionData(); 98 updateFormValues( values ); 99 updateCheckoutBlockData( values ); 100 } 101 102 /** 103 * Remove sourcebuster.js cookies. 104 * To be called whenever tracking is disabled or consent is revoked. 105 */ 106 function removeTrackingCookies() { 107 const domain = window.location.hostname; 108 const sbCookies = [ 109 'sbjs_current', 110 'sbjs_current_add', 111 'sbjs_first', 112 'sbjs_first_add', 113 'sbjs_session', 114 'sbjs_udata', 115 'sbjs_migrations', 116 'sbjs_promo' 117 ]; 118 119 // Remove cookies 120 sbCookies.forEach( ( name ) => { 121 document.cookie = `${name}=; path=/; max-age=-999; domain=.${domain};`; 122 } ); 123 } 124 125 // Run init. 126 wc_order_attribution.setOrderTracking( params.allowTracking ); 127 128 // Work around the lack of explicit script dependency for the checkout block. 129 // Conditionally, wait for and use 'wp-data' & 'wc-blocks-checkout. 130 131 // Wait for (async) block checkout initialization and set source values once loaded. 132 function eventuallyInitializeCheckoutBlock() { 133 if ( 134 window.wp && window.wp.data && typeof window.wp.data.subscribe === 'function' 135 ) { 136 // Update checkout block data once more if the checkout store was loaded after this script. 137 const unsubscribe = window.wp.data.subscribe( function () { 138 unsubscribe(); 139 updateCheckoutBlockData( wc_order_attribution.getAttributionData() ); 140 }, CHECKOUT_STORE_KEY ); 141 } 142 }; 143 // Wait for DOMContentLoaded to make sure wp.data is in place, if applicable for the page. 144 if (document.readyState === "loading") { 145 document.addEventListener("DOMContentLoaded", eventuallyInitializeCheckoutBlock); 146 } else { 147 eventuallyInitializeCheckoutBlock(); 148 } 149 150 /** 151 * Define an element to contribute order attribute values to the enclosing form. 152 * To be used with the classic checkout. 153 */ 154 window.customElements.define( 'wc-order-attribution-inputs', class extends HTMLElement { 155 // Our bundler version does not support private class members, so we use a convention of `_` prefix. 156 // #values 157 // #fieldNames 158 constructor(){ 159 super(); 160 // Cache fieldNames available at the construction time, to avoid malformed behavior if they change in runtime. 161 this._fieldNames = Object.keys( wc_order_attribution.fields ); 162 // Allow values to be lazily set before CE upgrade. 163 if ( this.hasOwnProperty( '_values' ) ) { 164 let values = this.values; 165 // Restore the setter. 166 delete this.values; 167 this.values = values || {}; 168 } 169 } 170 /** 171 * Stamp input elements to the element's light DOM. 172 * 173 * We could use `.elementInternals.setFromValue` and avoid sprouting `<input>` elements, 174 * but it's not yet supported in Safari. 175 */ 176 connectedCallback() { 177 this.innerHTML = '';
178 const inputs = new DocumentFragment(); 179 for( const fieldName of this._fieldNames ) { 180 const input = document.createElement( 'input' ); 181 input.type = 'hidden'; 182 input.name = `${params.prefix}${fieldName}`; 183 input.value = stringifyFalsyInputValue( ( this.values && this.values[ fieldName ] ) || '' ); 184 inputs.appendChild( input ); 185 } 186 this.appendChild( inputs ); 187 } 188 189 /** 190 * Update form values. 191 */ 192 set values( values ) { 193 this._values = values; 194 if( this.isConnected ) { 195 for( const fieldName of this._fieldNames ) { 196 const input = this.querySelector( `input[name="${params.prefix}${fieldName}"]` ); 197 if( input ) { 198 input.value = stringifyFalsyInputValue( this.values[ fieldName ] ); 199 } else { 200 console.warn( 201 `Field "${fieldName}" not found. ` + 202 `Most likely, the '<wc-order-attribution-inputs>' element was manipulated.` 203 ); 204 } 205 } 206 } 207 } 208 get values() { 209 return this._values; 210 } 211 } ); 212 213 214}( window.wc_order_attribution ) );
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.