1import { slData, config } from './wpsl-shared.js'; 2import { helpers } from './wpsl-helpers.js'; 3import { search } from './wpsl-search.js'; 4import { geolocation } from './wpsl-geolocation.js'; 5import { accessibility } from './wpsl-accessibility.js'; 6 7/** 8 * Button handlers for WPSL frontend 9 * 10 * @since 3.0.0 11 */ 12export const buttons = { 13 args: { 14 autoLoad: false, 15 wasEmpty: true 16 }, 17 18 /** 19 * Execute search based on current state. 20 * 21 * @since 3.0.0 22 * @param {object} args Search arguments 23 * @param {string} typeField Type field value 24 */ 25 executeSearch: function( args, typeField ) { 26 if ( ( typeField.length && typeField != 'location' ) || ( ! typeField && typeof wpslSettings.search !== 'undefined' && wpslSettings.search.namesEnabled ) ) { 27 config.search.namesEnabled = 1; 28 config.search.skipGeocode = true; 29 30 jQuery( '.wpsl-icon-direction' ).hide(); 31 32 search.reset(); 33 search.run( args ); 34 } else { 35 config.search.namesEnabled = 0; 36 delete config.search.skipGeocode; 37 38 jQuery( '.wpsl-icon-direction' ).show(); 39 search.reset(); 40 41 // Coordinates are known when a suggestion was picked from the 42 // autocomplete, or when the search widget passed the location 43 // of a geolocation request along with the searched text. 44 if ( slData.autoCompleteLatLng ) { 45 args.latLng = slData.autoCompleteLatLng; 46 47 // Border enforcement needs the country code and the statistics 48 // add-on needs the address components, both of which only come 49 // from the reverse geocode response. Skipping it when either is 50 // active leaves restrictions[borders] as 'undefined,undefined'. 51 if ( ! config.search.restrictions.borders && typeof config.collectStatistics === 'undefined' ) { 52 slData.skipReverseGeocode = true; 53 } else { 54 slData.skipReverseGeocode = false; 55 } 56 57 search.prepare( args ); 58 } else { 59 search.submitActions( buttons.args ); 60 } 61 } 62 }, 63 64 /** 65 * Bind search button and input field events. 66 * 67 * @since 3.0.0 68 * @returns {void} 69 */ 70 bindSearch: function() { 71 const $input = helpers.input.getSearchField(); 72 73 let typeField = '', 74 args = {}; 75 76 // Make sure the 'enter' input triggers the 77 // search instead of opening the next dropdown. 78 $input.on( 'keypress', function( e ) { 79 if ( e.which == 13 ) { 80 jQuery( '#wpsl-search-btn' ).trigger( 'click' ); 81 82 return false; 83 } 84 }); 85 86 // v3 templates: show / hide the 'clear' button and remove the error state. 87 if ( helpers.flexboxAvailable() ) { 88 $input.on( 'input', function() { 89 const isEmpty = jQuery( this ).val().length === 0; 90 91 buttons.updateClearSearchVisibility( isEmpty ); 92 93 // Remove error state when user starts typing 94 if ( ! isEmpty ) { 95 jQuery( '.wpsl-search-wrap' ).removeClass( 'wpsl-error' ); 96 } 97 98 // Force a fresh geocode instead of reusing stale coordinates. 99 slData.autoCompleteLatLng = ''; 100 }); 101 } else { 102 // For v2 templates, remove error class from input field when user starts typing 103 $input.on( 'input', function() { 104 if ( jQuery( this ).val().length > 0 ) { 105 jQuery( this ).removeClass( 'wpsl-error' ); 106 } 107 108 // Force a fresh geocode instead of reusing stale coordinates. 109 slData.autoCompleteLatLng = ''; 110 }); 111 } 112 113 jQuery( '#wpsl-search-btn' ).off( 'click keydown' ).on( 'click keydown', function( e ) { 114 // Allow both click and keyboard activation (Enter or Space) 115 if ( e.type === 'keydown' && e.which !== 13 && e.which !== 32 ) { 116 return; 117 } 118 119 if ( e.type === 'keydown' ) { 120 e.preventDefault(); 121 } 122 123 jQuery( '#wpsl-search-btn' ).attr( 'disabled', true ); 124 $input.removeClass(); 125 126 // Make sure the previous results and 127 // filter are hidden for v3.x+ templates. 128 if ( helpers.flexboxAvailable() ) { 129 jQuery( '#wpsl-result-list' ).hide(); 130 jQuery( '.wpsl-search-wrap' ).removeClass( 'wpsl-error' ); 131 132 helpers.panel.maybeCloseFilters(); 133 } 134 135 slData.useBasicMode = false; 136 slData.firstLoadInProgress = false; 137 138 if ( jQuery( '#wpsl-search-wrap input[name=types]' ).length ) { 139 typeField = jQuery( '#wpsl-search-wrap input[name=types]' ).val().trim(); 140 } 141 142 if ( $input.val() ) { 143 buttons.executeSearch( args, typeField ); 144 } else if ( helpers.flexboxAvailable() && jQuery( '.wpsl-filter input[type="checkbox"]:checked, .wpsl-custom-c
144heckboxes input[type="checkbox"]:checked' ).length > 0 ) { 145 // Filter-only search ( category and / or custom panels like country, no user input required, v3.x+ templates ) 146 config.search.skipGeocode = true; 147 148 // Clear the existing markers / results before running, like the geocoded search path does. 149 search.reset(); 150 search.run( args ); 151 } else if ( jQuery( '#wpsl-wrap' ).hasClass( 'wpsl-cat-filter-only' ) ) { 152 config.search.skipGeocode = true; 153 154 search.reset(); 155 search.run( args ); 156 } else { 157 jQuery( '#wpsl-search-btn' ).attr( 'disabled', false ); 158 $input.removeClass(); 159 160 if ( helpers.flexboxAvailable() ) { 161 jQuery( '.wpsl-search-wrap' ).addClass( 'wpsl-error' ); 162 } else { 163 $input.addClass( 'wpsl-error' ); 164 } 165 166 $input.trigger( 'focus' ); 167 } 168 169 return false; 170 }); 171 }, 172 173 /** 174 * Update the visibility of the clear search input button. 175 * 176 * @since 3.0.0 177 * @param {boolean} isEmpty Whether the search input is empty 178 * @returns {void} 179 */ 180 updateClearSearchVisibility: function( isEmpty ) { 181 const $clearBtn = jQuery( '#wpsl-clear-search-input' ); 182 183 if ( ! isEmpty ) { 184 $clearBtn.show(); 185 186 if ( ! $clearBtn.data( 'bound' ) ) { 187 this.bindEmptySearchInput(); 188 $clearBtn.data( 'bound', true ); 189 } 190 } else { 191 $clearBtn.hide(); 192 search.reset(); 193 } 194 }, 195 196 /** 197 * Bind map control icons (reset and direction buttons). 198 * 199 * @since 3.0.0 200 * @returns {void} 201 */ 202 bindMapControlIcons: function() { 203 if ( jQuery( '.wpsl-icon-reset, #wpsl-reset-map' ).length > 0 ) { 204 205 this.bindResetMapBtn(); 206 207 // Hide it to prevent users from clicking it 208 // before the store location are placed on the map. 209 if ( jQuery.isEmptyObject( slData.viewport ) ) { 210 jQuery( '.wpsl-icon-reset' ).hide(); 211 } else { 212 // Google Maps only adds the controls once its tiles are in, by 213 // which time the autoload search has already stored the 214 // viewport and tried to show a button that wasn't there yet. 215 helpers.map.revealResetBtn(); 216 } 217 } 218 219 // Bind the direction button to trigger a new geolocation request. 220 jQuery( '.wpsl-icon-direction' ).on( 'click keydown', function( e ) { 221 // For keyboard events, only respond to Enter or Space 222 if ( e.type === 'keydown' && ! accessibility.keyboard.isActivationKey( e ) ) { 223 return; 224 } 225 226 if ( e.type === 'keydown' ) { 227 e.preventDefault(); 228 } 229 230 jQuery( this ).addClass( 'wpsl-user-activated' ); 231 jQuery( '#wpsl-search-btn' ).attr( 'disabled', true ); 232 233 slData.directions.active = false; 234 235 // If the direction details are showing, then hide them. 236 if ( jQuery( '#wpsl-direction-details' ).is( ':visible' ) ) { 237 jQuery( '.wpsl-back' ).trigger( 'click' ); 238 } 239 240 geolocation.run( true ); 241 }); 242 }, 243 244 /** 245 * Bind the clear search input button. 246 * 247 * @since 3.0.0 248 * @returns {void} 249 */ 250 bindEmptySearchInput: function() { 251 jQuery( '#wpsl-clear-search-input' ).off( 'click keydown' ).on( 'click keydown', function( e ) { 252 253 // Allow both click and keyboard activation (Enter or Space) 254 if ( e.type === 'keydown' && e.which !== 13 && e.which !== 32 ) { 255 return; 256 } 257 258 if ( e.type === 'keydown' ) { 259 e.preventDefault(); 260 } 261 262 helpers.input.getSearchField().val( '' ); 263 jQuery( '#wpsl-clear-search-input' ).hide(); 264 265 jQuery( '.wpsl-autocomplete-search-results' ).hide(); 266 267 // Close filters if they're open (same as search button behavior) 268 if ( helpers.flexboxAvailable() ) { 269 helpers.panel.maybeCloseFilters(); 270 } 271 272 search.reset(); 273 274 return false; 275 }); 276 }, 277 278 /** 279 * Make sure that when the reset button ( bottom corner of the map ) is 280 * clicked the map is restored to how it was on pageload. 281 * 282 * @since 3.0.0 283 * @returns {void} 284 */ 285 bindResetMapBtn: function() { 286 jQuery( '.wpsl-icon-reset , #wpsl-reset-map' ).on( 'click keydown', function( e ) { 287 // For keyboard events, only respond to Enter or Space 288 if ( e.type === 'keydown' && ! accessibility.keyboard.isActivationKey( e ) ) { 289 return; 290 } 291 292 if ( e.type === 'keydown' ) { 293 e.preventDefault(); 294 } 295 296 helpers.results.restoreInitialState(); 297 }); 298 }, 299 300 /** 301 * Bind the back button that returns from the directions to the results. 302 * 303 * @since 3.0.0 304 * @returns {void} 305 */ 306 bindRemoveDirections: function() { 307 jQuery( '#wpsl-result-list' ).on( 'click', '.wpsl-back', function() { 308 slData.provider.api.directions.restoreResults(); 309 310 // Move focus to the first store result so the user can keep cycling 311 // the list. setTimeout(0) defers until restoreResults() has finished 312 // showing #wpsl-stores. 313 setTimeout( function() { 314 const firstResult = document.querySelector( 315 '#wpsl-stores ul li .wpsl-location-name a, #wpsl-stores ul li a[href]' 316 ); 317 318 if ( firstResult ) {
319 firstResult.focus(); 320 } else { 321 // Fallback: focus the result list container itself 322 const resultList = document.querySelector( '#wpsl-stores' ); 323 324 if ( resultList ) { 325 resultList.setAttribute( 'tabindex', '-1' ); 326 resultList.focus(); 327 } 328 } 329 }, 0 ); 330 331 return false; 332 }); 333 }, 334};
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.