/** * Classic (shortcode) WooCommerce checkout. * * The form is rendered by PHP and nothing owns the fields afterwards, so the * widget is left to do the whole job: debounce, request, write, announce. Most * of this file is handing it the selectors WooCommerce uses and the settings * the store owner chose. * * Selectors are passed as strings rather than elements on purpose. The widget * resolves them on every use and delegates its listener from `document`, so a * theme or one-page-checkout plugin that re-renders the billing form does not * break the autofill. * * @module */ (function () { var config = window.cepisWooCommerce; if (!config || !window.CepisAutofill || !config.fields) { return; } var DIACRITIC = /[\u0300-\u036f]/g; /** * Compare labels the way a person would: accents and case do not count. * * @param {unknown} value Anything printable. * @returns {string} */ function fold(value) { return String(value == null ? "" : value) .normalize("NFD") .replace(DIACRITIC, "") .trim() .toLowerCase(); } /** * Set a value the way a person would, so validation in the page sees it. * * @param {HTMLInputElement|HTMLSelectElement} element Field to write. * @param {string} value Value to put in it. */ function write(element, value) { var descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(element), "value"); if (descriptor && descriptor.set) { descriptor.set.call(element, value); } else { element.value = value; } element.dispatchEvent(new Event("input", { bubbles: true })); element.dispatchEvent(new Event("change", { bubbles: true })); } /** * Fill the state field. * * This one field is kept away from the widget, because it needs the opposite * of the widget's `overwrite` rule. A street name is worth protecting — the * shopper may know better than the database. A state is not: the CEP decides * it. And WooCommerce pre-selects the store's own state on a fresh checkout, * which `overwrite: "auto"` would read as a choice the shopper made and * never touch, quietly filing a Rio address under São Paulo. * * Nothing is cleared when no option matches, and this runs only after a * successful lookup, so a CEP that does not exist leaves the field alone. * * Focus is put back where it was. WooCommerce runs the state field through * selectWoo, which takes focus for itself when the value changes, and that * would otherwise strand the cursor on the state box instead of the house * number the shopper still has to type. * * @param {string} selector CSS selector for the state field. * @param {string} uf Two-letter state code from the API. * @param {string} estado Full state name from the API. */ function fillState(selector, uf, estado) { var element = selector ? document.querySelector(selector) : null; if (!element || !uf) { return; } var value = uf; if ("SELECT" === element.tagName) { var wanted = [uf, estado].filter(Boolean).map(fold); var options = Array.prototype.slice.call(element.options); var match = null; for (var i = 0; i < options.length; i += 1) { if ( wanted.indexOf(fold(options[i].value)) !== -1 || wanted.indexOf(fold(options[i].textContent)) !== -1 ) { match = options[i].value; break; } } if (null === match) { return; } value = match; } if (element.value === value) { return; } var active = document.activeElement; write(element, value); if (active && active !== document.activeElement && "function" === typeof active.focus) { active.focus(); } } /** * Attach one address section — billing or shipping. * * @param {Record} map Selectors, keyed by the API's field names. * @returns {object|null} The widget handle, or null when there is no CEP field. */ function section(map) { if (!map || !map.cep) { return null; } var options = { baseUrl: config.baseUrl, overwrite: config.overwrite, focusNumero: !!config.focusNumero, debounceMs: config.debounceMs, timeoutMs: config.timeoutMs, messages: config.messages, onResult: function (address) { fillState(map.uf, address.uf, address.estado); }, }; Object.keys(map).forEach(function (field) { if ("uf" !== field) { options[field] = map[field]; } }); return window.CepisAutofill.attach(options); } var handles = []; Object.keys(config.fields).forEach(function (name) { var handle = section(config.fields[name]); if (handle) { handles.push(handle); } }); window.cepisWooCommerceHandles = handles; })();