1/** 2 * Woot.ro City Dropdown Handler 3 * 4 * Handles city dropdown based on country and county selection. 5 * County -> City cascade with AJAX loading. 6 */ 7(function ($) { 8 'use strict'; 9 10 if (typeof woot_city_params === 'undefined') { 11 return; 12 } 13 14 var params = woot_city_params; 15 var countriesWithCities = params.countries_with_cities || []; 16 var i18nSelectCity = params.i18n_select_city || 'Select a cityâ¦'; 17 var i18nLoading = params.i18n_loading || 'Loadingâ¦'; 18 var isInitialLoad = true; 19 20 /** 21 * Check if country has city nomenclature 22 */ 23 function hasCities(countryCode) { 24 return countryCode && countriesWithCities.indexOf(countryCode.toUpperCase()) !== -1; 25 } 26 27 /** 28 * Get field elements for a prefix (billing/shipping) 29 */ 30 function getFields(prefix) { 31 return { 32 country: $('#' + prefix + '_country'), 33 state: $('#' + prefix + '_state'), 34 city: $('#' + prefix + '_city') 35 }; 36 } 37 38 /** 39 * Initialize selectWoo on a select element 40 */ 41 function initSelectWoo($select) { 42 if (!$select.length || !$select.is('select')) { 43 return; 44 } 45 46 var selectFn = $.fn.selectWoo || $.fn.select2; 47 if (!selectFn) { 48 return; 49 } 50 51 try { 52 if ($select.data('select2')) { 53 $select.select2('destroy'); 54 } 55 } catch (e) {} 56 57 selectFn.call($select, { 58 placeholder: $select.data('placeholder') || i18nSelectCity, 59 allowClear: true, 60 width: '100%' 61 }); 62 } 63 64 /** 65 * Convert city field to select if it's a text input 66 */ 67 function ensureSelect($field) { 68 if ($field.is('select')) { 69 return $field; 70 } 71 72 var $select = $('<select>') 73 .attr('name', $field.attr('name')) 74 .attr('id', $field.attr('id')) 75 .addClass('woot-city-select input-text') 76 .attr('data-placeholder', i18nSelectCity) 77 .html('<option value=""></option>'); 78 79 $field.replaceWith($select); 80 return $('#' + $field.attr('id')); 81 } 82 83 /** 84 * Convert city field to text input 85 */ 86 function ensureTextInput($field) { 87 if ($field.is('input[type="text"]')) { 88 return $field; 89 } 90 91 var currentVal = $field.val() || ''; 92 93 try { 94 if ($field.data('select2')) { 95 $field.select2('destroy'); 96 } 97 } catch (e) {} 98 99 var $input = $('<input type="text">') 100 .attr('name', $field.attr('name')) 101 .attr('id', $field.attr('id')) 102 .addClass('input-text') 103 .val(currentVal); 104 105 $field.replaceWith($input); 106 return $('#' + $field.attr('id')); 107 } 108 109 /** 110 * Load cities for a prefix 111 */ 112 function loadCities(prefix, savedValue) { 113 var fields = getFields(prefix); 114 if (!fields.city.length) { 115 return; 116 } 117 118 var country = fields.country.val() || ''; 119 var state = fields.state.val() || ''; 120 121 // Country without city nomenclature - use text input 122 if (!hasCities(country)) { 123 ensureTextInput(fields.city); 124 return; 125 } 126 127 // Ensure it's a select 128 var $select = ensureSelect(fields.city); 129 130 // No state selected - disable and show placeholder 131 if (!state) { 132 $select.html('<option value=""></option>'); 133 $select.prop('disabled', true).val(''); 134 initSelectWoo($select); 135 return; 136 } 137 138 // Load cities via AJAX 139 $select.prop('disabled', true); 140 $select.html('<option value="">' + i18nLoading + '</option>'); 141 142 try { 143 if ($select.data('select2')) { 144 $select.select2('destroy'); 145 } 146 } catch (e) {} 147 148 $.post(params.ajax_url, { 149 action: 'woot_get_cities', 150 country: country, 151 state: state 152 }, function (response) { 153 var html = '<option value=""></option>'; 154 155 if (response.success && response.data && response.data.cities) {
156 response.data.cities.forEach(function (city) { 157 html += '<option value="' + $('<div>').text(city).html() + '">' + $('<div>').text(city).html() + '</option>'; 158 }); 159 } 160 161 $select.html(html); 162 $select.prop('disabled', false); 163 164 if (savedValue) { 165 $select.val(savedValue); 166 } 167 168 initSelectWoo($select); 169 $select.trigger('change'); 170 }); 171 } 172 173 /** 174 * Handle state/country change 175 */ 176 function onFieldChange(prefix) { 177 if (isInitialLoad) { 178 return; 179 } 180 loadCities(prefix, ''); 181 } 182 183 /** 184 * Initialize 185 */ 186 function init() { 187 ['billing', 'shipping'].forEach(function (prefix) { 188 var fields = getFields(prefix); 189 if (!fields.city.length) { 190 return; 191 } 192 193 var country = fields.country.val() || ''; 194 var savedValue = fields.city.data('saved-value') || fields.city.val() || ''; 195 196 if (hasCities(country)) { 197 loadCities(prefix, savedValue); 198 } 199 }); 200 201 // Mark initial load complete after a delay 202 setTimeout(function () { 203 isInitialLoad = false; 204 }, 500); 205 } 206 207 // Document ready 208 $(function () { 209 // Event handlers 210 $(document.body).on('change', '#billing_state', function () { 211 onFieldChange('billing'); 212 }); 213 214 $(document.body).on('change', '#shipping_state', function () { 215 onFieldChange('shipping'); 216 }); 217 218 $(document.body).on('change', '#billing_country, #shipping_country', function () { 219 var prefix = this.id.replace('_country', ''); 220 onFieldChange(prefix); 221 }); 222 223 $(document.body).on('country_to_state_changed', function (event, countryCode, $container) { 224 void event; void countryCode; // Unused but required by WooCommerce event signature 225 var prefix = $container.find('#billing_country').length ? 'billing' : 'shipping'; 226 onFieldChange(prefix); 227 }); 228 229 // Reinitialize selectWoo after checkout updates 230 $(document.body).on('updated_checkout', function () { 231 $('select.woot-city-select').each(function () { 232 if (!$(this).prop('disabled')) { 233 initSelectWoo($(this)); 234 } 235 }); 236 }); 237 238 // Start 239 setTimeout(init, 200); 240 }); 241 242})(jQuery);
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.