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