( function () { window.WPO_IPS_PeppolEndpointDerivation = window.WPO_IPS_PeppolEndpointDerivation || {}; window.WPO_IPS_PeppolEndpointDerivation.init = function init( CONFIG, adapter ) { const LOCK_COUNTRIES = Array.isArray( CONFIG.countries ) ? CONFIG.countries.map( ( c ) => String( c ).toUpperCase() ) : []; const DEBUG = !!CONFIG.debug; const VAT_SELECTOR = CONFIG.vat_field_selector; const AUTOFILL_ENDPOINT_ROUTE = CONFIG.peppol_autofill_endpoint_route; function log( ...args ) { if ( DEBUG ) console.log( '[WPO IPS Peppol]', ...args ); } function getValue( selector ) { if ( ! selector ) return ''; const el = document.querySelector( selector ); return el ? String( el.value || '' ).trim() : ''; } function isLockCountry( country ) { const c = String( country || '' ).toUpperCase(); return LOCK_COUNTRIES.includes( c ); } function normalizeVat( vat ) { return String( vat || '' ) .replace( /\s+/g, '' ) .toUpperCase() .trim(); } function setFieldValue( el, value ) { if ( ! el ) return false; const newVal = String( value ?? '' ); // React controlled input friendly setter const valueSetter = Object.getOwnPropertyDescriptor( window.HTMLInputElement.prototype, 'value' )?.set; // Some handlers ignore changes while readOnly. Temporarily lift it. const prevReadOnly = !! el.readOnly; el.readOnly = false; if ( valueSetter ) { valueSetter.call( el, newVal ); } else { el.value = newVal; } el.dispatchEvent( new Event( 'input', { bubbles: true } ) ); el.dispatchEvent( new Event( 'change', { bubbles: true } ) ); el.readOnly = prevReadOnly; if ( typeof adapter.onSetFieldValue === 'function' ) { adapter.onSetFieldValue( el, newVal ); } return true; } function reapplyValueAfterUnlock( input, value ) { if ( ! input ) return; requestAnimationFrame( () => { setFieldValue( input, value ); requestAnimationFrame( () => setFieldValue( input, value ) ); } ); } function fetchPeppolEndpointValue( billingCountry, vatValue ) { if ( typeof adapter.fetchEndpoint === 'function' ) { return adapter.fetchEndpoint( billingCountry, vatValue ); } const apiFetch = window.wp?.apiFetch; if ( ! apiFetch ) return Promise.resolve( null ); return apiFetch( { path: AUTOFILL_ENDPOINT_ROUTE, method: 'POST', data: { billing_country: String( billingCountry || '' ).toUpperCase(), vat: String( vatValue || '' ), }, } ); } // Debounce + cache. let endpointLookupTimer = null; let endpointLookupLast = null; // Manual override flag (session only). let manualOverride = false; let manualOverrideValue = ''; // Reset override when VAT/country changes. let lastVat = null; let lastCountry = null; let lastLocked = null; let lastEndpointEl = null; let scheduled = false; function ensureOverrideLink( wrapper, endpointInput, locked ) { if ( ! wrapper ) return; let link = wrapper.querySelector( '.wpo-ips-override' ); if ( ! locked ) { if ( link ) link.remove(); return; } if ( link ) return link; link = document.createElement( 'a' ); link.href = '#'; link.className = 'wpo-ips-override'; link.textContent = CONFIG.override_link_text || 'Override (edit manually)'; link.addEventListener( 'click', ( e ) => { e.preventDefault(); const preservedValue = endpointInput ? String( endpointInput.value || '' ) : ''; manualOverride = true; manualOverrideValue = preservedValue; applyLockState( 'override-click' ); if ( endpointInput ) { if ( preservedValue ) { reapplyValueAfterUnlock( endpointInput, preservedValue ); } endpointInput.focus(); } } ); // Let adapter decide where to place link, default to append. if ( typeof adapter.appendOverrideLink === 'function' ) { adapter.appendOverrideLink( wrapper, endpointInput, link ); } else { wrapper.appendChild( link ); } return link; } function setLocked( endpoint, wrapper, locked ) { endpoint.readOnly = locked; endpoint.style.pointerEvents = locked ? 'none' : ''; endpoint.setAttribute( 'aria-disabled', locked ? 'true' : 'false' ); endpoint.classList.toggle( 'wpo-is-locked', locked ); if ( wrapper ) { wrapper.classList.toggle( 'wpo-ips-locked-wrap', locked ); } } function bindManualOverridePersistence( endpoint ) { if ( ! endpoint || endpoint.__wpoPeppolManualBound ) return; endpoint.__wpoPeppolManualBound = true; function captureCurrentValue() { const current = String( endpoint.value || '' ); if ( current ) { manualOverrideValue = current; } } endpoint.addEventListener( 'input', () => { if ( manualOverride ) { manualOverrideValue = String( endpoint.value || '' ); } } ); endpoint.addEventListener( 'pointerdown', () => { if ( manualOverride ) { captureCurrentValue(); } } ); endpoint.addEventListener( 'mousedown', () => { if ( manualOverride ) { captureCurrentValue(); } } ); endpoint.addEventListener( 'focus', () => { if ( manualOverride && manualOverrideValue ) { reapplyValueAfterUnlock( endpoint, manualOverrideValue ); } } ); endpoint.addEventListener( 'blur', () => { if ( manualOverride ) { captureCurrentValue(); if ( manualOverrideValue ) { reapplyValueAfterUnlock( endpoint, manualOverrideValue ); } } } ); } function applyLockState( source = 'unknown' ) { scheduled = false; const nodes = adapter.getEndpointNodes && adapter.getEndpointNodes(); const wrapper = nodes ? nodes.wrapper : null; const endpoint = nodes ? nodes.endpoint : null; if ( ! endpoint ) return; bindManualOverridePersistence( endpoint ); const country = String( adapter.getBillingCountry ? adapter.getBillingCountry() : '' ).toUpperCase(); const vatValue = getValue( VAT_SELECTOR ); // reset override when VAT/country changes if ( country !== lastCountry || vatValue !== lastVat ) { manualOverride = false; manualOverrideValue = ''; lastCountry = country; lastVat = vatValue; endpointLookupLast = null; } // always locked unless user clicked override link const locked = ! manualOverride; if ( isLockCountry( country ) && ! manualOverride && vatValue !== '' ) { const vat = normalizeVat( vatValue ); const key = country + '|' + vat; if ( endpointLookupLast && endpointLookupLast.key === key ) { if ( endpointLookupLast.value ) { setFieldValue( endpoint, endpointLookupLast.value ); } } else { if ( endpointLookupTimer ) clearTimeout( endpointLookupTimer ); endpointLookupTimer = setTimeout( () => { fetchPeppolEndpointValue( country, vat ) .then( ( res ) => { const id = String( res?.id || '' ).trim(); endpointLookupLast = { key, value: id }; if ( id ) { setFieldValue( endpoint, id ); } } ) .catch( () => {} ); }, 250 ); } } if ( locked === lastLocked && endpoint === lastEndpointEl ) return; ensureOverrideLink( wrapper, endpoint, locked ); setLocked( endpoint, wrapper, locked ); lastLocked = locked; lastEndpointEl = endpoint; log( 'lock state applied', { source, country, vatValue, locked, manualOverride, lastValue: endpointLookupLast ? endpointLookupLast.value : '', manualOverrideValue, } ); } function scheduleApply( source ) { if ( scheduled ) return; scheduled = true; requestAnimationFrame( () => applyLockState( source ) ); } // Expose minimal API for wrapper files. return { apply: applyLockState, schedule: scheduleApply, log, }; }; } )();