diff options
Diffstat (limited to 'assets/js/cepis-woocommerce-classic.js')
| -rw-r--r-- | assets/js/cepis-woocommerce-classic.js | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/assets/js/cepis-woocommerce-classic.js b/assets/js/cepis-woocommerce-classic.js index 69d8068..481104c 100644 --- a/assets/js/cepis-woocommerce-classic.js +++ b/assets/js/cepis-woocommerce-classic.js @@ -23,6 +23,9 @@ var DIACRITIC = /[\u0300-\u036f]/g; + /** Class the widget gives the region it announces through. */ + var STATUS = "cepis-autofill-status"; + /** * Compare labels the way a person would: accents and case do not count. * @@ -123,6 +126,90 @@ } /** + * The `aria-live` region the widget announces through. + * + * The widget inserts it immediately after the CEP field and points the + * field's `aria-describedby` at it, but only once, when it attaches. + * WooCommerce re-renders the address fields afterwards — that is what + * `country_to_state_changed` does — and the attribute does not survive it + * while the region itself does. So the sibling is what is actually reliable + * here, and the surrounding form row is the fallback for a theme that wraps + * the input in something of its own. + * + * Never widened to a document-wide search: billing and shipping each have a + * region, and picking the wrong one would tell a shopper about the address + * they are not editing. + * + * @param {string} selector CSS selector for the CEP field. + * @returns {HTMLElement|null} + */ + function statusRegion(selector) { + var field = document.querySelector(selector); + + if (!field) { + return null; + } + + var sibling = field.nextElementSibling; + + if (sibling && sibling.classList && sibling.classList.contains(STATUS)) { + return sibling; + } + + var node = field.parentElement; + + for (var depth = 0; node && depth < 2; depth += 1) { + var region = node.querySelector("." + STATUS); + + if (region) { + return region; + } + + node = node.parentElement; + } + + return null; + } + + /** + * Say which municipality a CEP that does not exist would have been in. + * + * Rewrites the line the widget just wrote, and nothing else — no field is + * touched here, on this path, ever. The CEP was not found; there is no + * address to fill anything with, and inventing one from the municipality + * would send the parcel to the wrong town. + * + * Both guards before the write matter, because the answer arrives one round + * trip late: the shopper may have typed on, and the widget may have replaced + * the line with a "found" of its own in the meantime. + * + * @param {string} selector CSS selector for the CEP field. + * @param {string} cep The CEP that was not found. + */ + function explain(selector, cep) { + var helper = window.cepisWooCommerceFaixa; + var region = statusRegion(selector); + + if (!helper || !region) { + return; + } + + var shown = region.textContent; + + helper.message(cep, shown, function (message) { + var current = statusRegion(selector); + var field = document.querySelector(selector); + var typed = field ? String(field.value == null ? "" : field.value).replace(/\D+/g, "") : ""; + + if (message === shown || !current || current.textContent !== shown || typed !== cep) { + return; + } + + current.textContent = message; + }); + } + + /** * Attach one address section — billing or shipping. * * @param {Record<string, string>} map Selectors, keyed by the API's field names. @@ -143,6 +230,11 @@ onResult: function (address) { fillState(map.uf, address.uf, address.estado); }, + onError: function (error, context) { + if (error && "not_found" === error.code) { + explain(map.cep, context.cep); + } + }, }; Object.keys(map).forEach(function (field) { |