1/* Cascading country/state/city â cache, loading states, optional city. */ 2jQuery(function ($) { 3 var i18n = (tc_csca_auto_ajax && tc_csca_auto_ajax.i18n) ? tc_csca_auto_ajax.i18n : {}; 4 var cache = { states: {}, cities: {} }; 5 6 function t(key, fallback) { 7 return i18n[key] || fallback; 8 } 9 10 function isEmptyId(id) { 11 return !id || id === "0" || id === 0 || id === "0001"; 12 } 13 14 function emptyLabel($el, key, fallback) { 15 return $el.attr("data-placeholder") || t(key, fallback); 16 } 17 18 function optionHtml(value, dataId, label) { 19 return "<option value='" + value + "' data-id='" + dataId + "'>" + label + "</option>"; 20 } 21 22 function setLoading($el) { 23 $el.prop("disabled", true).attr("aria-busy", "true"); 24 $el.html(optionHtml("0001", "0001", t("loading", "Loading..."))); 25 } 26 27 function setEmpty($el, label, disable) { 28 $el.attr("aria-busy", "false"); 29 $el.html(optionHtml("0", "0", label)); 30 $el.prop("disabled", !!disable); 31 } 32 33 function fillOptions($el, rows, emptyLbl) { 34 $el.attr("aria-busy", "false").prop("disabled", false); 35 $el.html(optionHtml("0", "0", emptyLbl)); 36 if (!rows || !rows.length) { 37 return false; 38 } 39 for (var i = 0; i < rows.length; i++) { 40 $el.append(optionHtml(rows[i].name, rows[i].id, rows[i].name)); 41 } 42 return true; 43 } 44 45 /** 46 * Resolve paired select: 47 * 1) data-tc-group 48 * 2) same index in form 49 * 3) first match (legacy) 50 */ 51 function findPaired($el, typeClass) { 52 var $form = $el.closest("form"); 53 var group = $el.attr("data-tc-group"); 54 if (group) { 55 var $byGroup = $form.find("select." + typeClass + '[data-tc-group="' + group + '"]'); 56 if ($byGroup.length) { 57 return $byGroup.first(); 58 } 59 } 60 61 var fromClass = $el.hasClass("country_auto") 62 ? "country_auto" 63 : $el.hasClass("state_auto") 64 ? "state_auto" 65 : "city_auto"; 66 67 var $fromAll = $form.find("select." + fromClass); 68 var idx = $fromAll.index($el); 69 var $targets = $form.find("select." + typeClass); 70 71 if (idx >= 0 && $targets.eq(idx).length) { 72 return $targets.eq(idx); 73 } 74 return $targets.first(); 75 } 76 77 function fetchStates(cnt, done) { 78 if (cache.states[cnt]) { 79 done(cache.states[cnt]); 80 return; 81 } 82 $.ajax({ 83 url: tc_csca_auto_ajax.ajax_url, 84 type: "post", 85 dataType: "json", 86 data: { 87 action: "tc_csca_get_states", 88 nonce_ajax: tc_csca_auto_ajax.nonce, 89 cnt: cnt 90 } 91 }) 92 .done(function (response) { 93 var rows = response && response.length ? response : []; 94 cache.states[cnt] = rows; 95 done(rows); 96 }) 97 .fail(function () { 98 done(null); 99 }); 100 } 101 102 function fetchCities(sid, done) { 103 if (cache.cities[sid]) { 104 done(cache.cities[sid]); 105 return; 106 } 107 $.ajax({ 108 url: tc_csca_auto_ajax.ajax_url, 109 type: "post", 110 dataType: "json", 111 data: { 112 action: "tc_csca_get_cities", 113 nonce_ajax: tc_csca_auto_ajax.nonce, 114 sid: sid 115 } 116 }) 117 .done(function (response) { 118 var rows = response && response.length ? response : []; 119 cache.cities[sid] = rows; 120 done(rows); 121 }) 122 .fail(function () { 123 done(null); 124 }); 125 } 126 127 // Country â State (+ reset City if present). City field is optional. 128 $(document).on("change", "select.country_auto", function () { 129 var $country = $(this); 130 var $state = findPaired($country, "state_auto"); 131 var $city = findPaired($country, "city_auto"); 132 133 if (!$state.length) { 134 return; 135 } 136 137 var cnt = $country.children("option:selected").attr("data-id"); 138 var stateLbl = emptyLabel($state, "select_state", "Select State"); 139 var cityLbl = $city.length ? emptyLabel($city, "select_city", "Select City") : ""; 140 141 if ($city.length) { 142 setEmpty($city, cityLbl, true); 143 } 144 145 if (isEmptyId(cnt)) { 146 setEmpty($state, stateLbl, true); 147 return; 148 } 149 150 setLoading($state); 151 fetchStates(cnt, function (rows) { 152 if (rows === null) { 153 setEmpty($state, t("state_not_found", "State list not found"), false); 154 return; 155 } 156 if (!fillOptions($state, rows, stateLbl)) { 157 setEmpty($state, t("state_not_found", "State list not found"), false); 158 } 159 }); 160 }); 161 162 // State â City (no-op if form has country+state only). 163 $(document).on("change", "select.state_auto", function () { 164 var $state = $(this); 165 var $city = findPaired($state, "city_auto"); 166 167 if (!$city.length) { 168 return; 169 } 170 171 var sid = $state.children("option:selected").attr("data-id"); 172 var cityLbl = emptyLabel($city, "select_city", "Select City"); 173 174 if (isEmptyId(sid)) { 175 setEmpty($city, cityLbl, true); 176 return; 177 } 178 179 setLoading($city); 180 fetchCities(sid, function (rows) { 181 if (rows === null) { 182 setEmpty($city, t("city_not_found", "City list not found"), false); 183 return; 184 } 185 if (!fillOptions($city, rows, cityLbl)) { 186 setEmpty($city, t("city_not_found", "City list not found"), false); 187 } 188 }); 189 }); 190 191 // Initial: disable empty dependent fields (city optional). 192 $("select.state_auto, select.city_auto").each(function () { 193 var $el = $(this); 194 if (isEmptyId($el.children("option:selected").attr("data-id"))) { 195 $el.prop("disabled", true); 196 } 197 }); 198 199 // Disabled fields are omitted from submit â re-enable so CF7 still receives values. 200 function reenableDependent(form) { 201 $(form).find("select.state_auto, select.city_auto").prop("disabled", false); 202 } 203 $(document).on("submit", "form.wpcf7-form", function () { 204 reenableDependent(this); 205 }); 206 $(document).on("wpcf7beforesubmit", "form.wpcf7-form", function () { 207 reenableDependent(this); 208 }); 209});
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.