PageSourceSearch

https://stonemaiergames.com/wp-content/plugins/wp-google-maps/js/v8/autocomplete.js?ver=10.1.10%2Bpro-10.1.06

js stonemaiergames.com collected 2026-10-02 02:27:23 UTC 19,010 bytes, 620 lines download raw bytes

1/**
2 * @namespace WPGMZA
3 * @module Autocomplete
4 * @requires WPGMZA.EventDispatcher
5 */
6jQuery(function($) {
7    /**
8	 * Autocomplete base class 
9	 */
10	WPGMZA.Autocomplete = function(element, options) {
11		WPGMZA.EventDispatcher.call(this);
12
13        if(!options){
14			options = {};
15        }
16
17        this.element = element;
18        this.options = options;
19
20        this.provider = false;
21
22        this.state = {
23            ready : false,
24            busy : false,
25            internal : false,
26            term : false,
27            items : []
28        };
29
30        this.components = {};
31
32        if(this.canAutoload()){
33            this.autoload();
34        }
35	}
36	
37	WPGMZA.extend(WPGMZA.Autocomplete, WPGMZA.EventDispatcher);
38
39    /* Valid provider list */
40    WPGMZA.Autocomplete.Providers = {
41		GOOGLE_AUTOCOMPLETE : 1,
42		GOOGLE_PLACES : 2,
43		CLOUD_API : 3,
44        NOMINATIM : 4,
45        AZURE_MAPS : 5,
46        LOCATION_IQ : 6
47	};
48
49    /* Default intervals */
50    WPGMZA.Autocomplete.Intervals = {
51        KEYSTROKE : 700,
52        FOCUS : 400
53    };
54    
55    /**
56     * Static instance creation method
57     * 
58     * @param any element 
59     * @param object options 
60     * 
61     * @return any
62     */
63	WPGMZA.Autocomplete.createInstance = function(element, options) {
64		let constructor;
65		
66        let provider = WPGMZA.settings && WPGMZA.settings.address_provider ? WPGMZA.settings.address_provider : false;
67        if(!provider){
68            /* Provider unknown, use engine default */
69            if(WPGMZA.settings && WPGMZA.settings.engine){
70                switch(WPGMZA.settings.engine){
71                    case 'google-maps':
72                        /* Google Maps - Prefer autocomplete, with auto-swap to places */
73                        provider = WPGMZA.Autocomplete.Providers.GOOGLE_AUTOCOMPLETE;
74                        break;
75                    case 'open-layers':
76                    case 'open-layers-latest':
77                    case 'leaflet':
78                    case 'leaflet-stadia':
79                    case 'leaflet-maptiler':
80                    case 'leaflet-zerocost':
81                        /* Nominatim */
82                        provider = WPGMZA.Autocomplete.Providers.NOMINATIM;
83                        break;
84                    case 'leaflet-azure':
85                        /* Azure */
86                        provider = WPGMZA.Autocomplete.Providers.AZURE_MAPS;
87                        break;
88                    case 'leaflet-locationiq':
89                        /* LocationIQ */
90                        provider = WPGMZA.Autocomplete.Providers.LOCATION_IQ;
91                        break;
92                }
93            }
94
95            if(WPGMZA.CloudAPI && WPGMZA.CloudAPI.isBeingUsed){
96                provider = WPGMZA.Autocomplete.Providers.CLOUD_API;
97            }
98        } else if (typeof provider === 'string'){
99            switch(provider){
100                case 'google-maps':
101                    /* Google Maps - Prefer autocomplete, with auto-swap to places */
102                    provider = WPGMZA.Autocomplete.Providers.GOOGLE_AUTOCOMPLETE;
103                    break;
104                case 'nominatim':
105                    /* Nominatim */
106                    provider = WPGMZA.Autocomplete.Providers.NOMINATIM;
107                    break;
108                case 'azure-maps':
109                    /* Azure */
110                    provider = WPGMZA.Autocomplete.Providers.AZURE_MAPS;
111                    break;
112                case 'location-iq':
113                    /* Azure */
114                    provider = WPGMZA.Autocomplete.Providers.LOCATION_IQ;
115                    break;
116            }
117        }
118
119        switch(provider){
120            case WPGMZA.Autocomplete.Providers.GOOGLE_AUTOCOMPLETE:
121            case WPGMZA.Autocomplete.Providers.GOOGLE_PLACES:
122                /* Google Autocomplete */
123                constructor = WPGMZA.GoogleAutocomplete;
124                break;
125            case WPGMZA.Autocomplete.Providers.CLOUD_API:
126                /* Cloud (Legacy) Autocomplete */
127                /* Note : Does not extend autocomplete base - Functions independently */
128                constructor = WPGMZA.CloudAutocomplete;
129                break;
130            case WPGMZA.Autocomplete.Providers.NOMINATIM:
131                /* Nominatim Autocomplete */
132                constructor = WPGMZA.NominatimAutocomplete;
133                break;
134            case WPGMZA.Autocomplete.Providers.AZURE_MAPS:
135                /* Azure Autocomplete */
136                constructor = WPGMZA.AzureAutocomplete;
137                break;
138            case WPGMZA.Autocomplete.Providers.LOCATION_IQ:
139                /* LocationIQ Autocomplete */
140                constructor = WPGMZA.LocationIQAutocomplete;
141                break;
142            default:
143                /* Use the base - Which will likely do nothing */
144                constructor = WPGMZA.Autocomplete;
145                break;
146        }
147
148        return new constructor(element, options);
149	}
150
151    /**
152     * Check if we are allowed to autoload
153     * 
154     * This is true for all address inputs, with the exception of the marker address admin input 
155     * 
156     * @return bool
157     */
158    WPGMZA.Autocomplete.prototype.canAutoload = function(){
159		if(this.element && this.element.id && this.element.id === 'wpgmza_add_address_map_editor'){
160			return false;
161		}
162		return true;
163    }
164
165    /**
166     * Autoload the instance
167     * 
168     * This is only helpful if you have extended the system into a sub-class as the default class will not
169     * produce any UI
170     * 
171     * @return void
172     */
173    WPGMZA.Autocomplete.prototype.autoload = function(){
174        this.provider = this.getProvider();
175    }
176
177    /**
178     * Prepare the system for internal suggestions
179     * 
180     * Marks the instance as internal, generates components, links back to element and places placeholders
181     * 
182     * Once complete, binds the events to the element
183     * 
184     * @return void
185     */
186    WPGMZA.Autocomplete.prototype.prepare = function(){
187        this.setInternal(true);
188        this.generate();
189
190        this.element.wpgmzaAutocomplete = this;
191        this.element.setAttribute('data-address-provider', this.getProviderSlug());
192
193        let placeholder = this.options.placeholder ? this.placeholder : false;
194        if(!placeholder && !this.element.getAttribute('placeholder')){
195            placeholder = WPGMZA.localized_strings && WPGMZA.localized_strings.autocomplete_placeholder ? WPGMZA.localized_strings.autocomplete_placeholder : false;
196        }
197
198        if(placeholder){
199			this.element.setAttribute('placeholder', placeholder);
200		}
201
202        this.element.setAttribute('autocomplete', 'off');
203
204        this.bind();
205    }
206
207    /**
208     * Generate the components list element
209     * 
210     * We use this for presenting the list to the end user
211     * 
212     * @return void
213     */
214    WPGMZA.Autocomplete.prototype.generate = function(){
215        if(!this.isInternal()){
216            return;
217        }
218
219        this.components.list = document.createElement('div');
220
221        this.components.list.classList.add('wpgmza-hidden');
222        this.components.list.classList.add('wpgmza-internal-autocomplete-list');
223
224		document.body.appendChild(this.components.list);
225    }
226
227    /**
228     * Bind event listeners to the UI elements
229     * 
230     * Only runs in internal mode
231     * 
232     * @return void
233     */
234    WPGMZA.Autocomplete.prototype.bind = function(){
235        if(!this.isInternal()){
236            return;
237        }
238
239        this.element.addEventListener("keyup", (event) => {
240			if(event.key.length > 1){
241				/* Ignore these as they are likely mutators */
242				if(event.key !== "Backspace"){
243					return;
244				}
245			}
246
247			if(this.state.timer){
248				clearTimeout(this.state.timer);
249			}
250
251			/* We have a valid key so we can go about performing a search */
252
253			this.state.timer = setTimeout(() => {
254                if(this.element.value && this.element.value.trim() !== this.state.term){
255                    this.setBusy(true);
256				    this.find();
257                }
258			}, this.options.keystrokeInterval ? this.options.keystrokeInterval : WPGMZA.Autocomplete.Intervals.KEYSTROKE);
259		});
260
261		this.element.addEventListener("focusout", (event) => {
262			setTimeout(() => {
263                if(document.activeElement === this.element){
264                    /* Has not actually lost focus yet */
265                    return;
266                }
267				this.hide();
268			}, this.options.focusInterval ? this.options.focusInterval : WPGMZA.Autocomplete.Intervals.FOCUS);
269		});
270
271		this.element.addEventListener("focusin", (event) => {
272			this.show();
273			this.autoplace();
274		});
275
276		this.element.addEventListener("click", (event) => {
277			this.show();
278			this.autoplace();
279		});
280
281		document.addEventListener("scroll", (event) => {
282			this.hide();
283		});
284    }
285
286    /**
287     * Find locations based on the search term
288     * 
289     * This will only run if/when you have an extension class that contains additional logic for the requests
290     * 
291     * The base method will only set the search term, hide the popup and clear any pending items
292     * 
293     * @return void
294     */
295    WPGMZA.Autocomplete.prototype.find = function(){
296        this.setTerm(this.element.value);
297        
298        if(this.getTerm()){
299            /* We have a term, a request will likely run, clear the list */
300            this.hide();
301            
302            this.clear();
303        } else {
304            this.setBusy(false);
305        }
306    }
307
308    /**
309     * Present items to the user to select from the list
310     * 
311     * This requires items to be stored in the state, with the correct formatting for our internal modules
312     * 
313     * Once ready we will bind event listeners and show the list
314     * 
315     * @return void
316     */
317    WPGMZA.Autocomplete.prototype.present = function(){
318        if(this.state.items && this.state.items.length){
319            let html = '';
320			for(let location of this.state.items){
321                if(location.address){
322                    let dataAddress = location.address.replace(/<[^>]*>/g, '');
323                    html += `<div class='wpgmza-internal-autocomplete-location' data-address='${dataAddress}' data-coordinates='${location.coordinates ? location.coordinates : ''}'>`;
324                    if(location.display){
325                        /* Has a different display value */
326                        html += `<strong data-autocomplete-field='display'>${location.display}</strong>`;
327					    html += `<span data-autocomplete-field='adr'>${location.address}</span>`;
328                    } else {
329                        /* Use address as if it was display */
330                        html += `<strong data-autocomplete-field='display'>${location.address}</strong>`;
331                    }
332
333					if(location.type || location.country){
334					    html += `<span data-autocomplete-meta-list>`;
335                        if(location.type){
336                            html += `<small data-autocomplete-field='type'>${location.type}</small>`;
337                        }
338
339                        if(location.country){
340                            html += `<small data-autocomplete-field='country'>${location.country}</small>`;
341                        }
342                        html += `</span>`;
343
344                    }
345
346					html += `</div>`;
347                }
348
349                if(html && html.length){
350                    this.components.list.innerHTML = html;
351
352                    this.show();
353                    this.autoplace();
354
355                    /* Now bind the event listener, and make it fill the field on click */
356                    const items = this.components.list.querySelectorAll('.wpgmza-internal-autocomplete-location');
357                    for(let item of items){
358                        item.addEventListener('click', (event) => {
359                            event.preventDefault();
360
361                            let address = item.getAttribute('data-address');
362                            if(address){
363                                this.element.value = address;
364                            }
365                            
366                            this.hide();
367                            this.clear();
368
369                            this.state.term = "";
370                        });
371                    }
372                }
373			}
374        } else {
375            this.hide();
376        }
377    }
378
379    /**
380     * Show the suggestion list
381     * 
382     * Only works if we have items in the list
383     * 
384     * @return void
385     */
386    WPGMZA.Autocomplete.prototype.show = function(){
387        if(!this.isInternal()){
388            return;
389        }
390
391        if(this.components && this.components.list && this.state.items && this.state.items.length){
392            this.components.list.classList.remove('wpgmza-hidden');
393        }
394	}
395
396    /**
397     * Hide the suggestion list
398     * 
399     * @return void
400     */
401	WPGMZA.Autocomplete.prototype.hide = function(){
402        if(!this.isInternal()){
403            return;
404        }
405
406        if(this.components && this.components.list){
407            this.components.list.classList.add('wpgmza-hidden');
408        }
409	}
410
411    /**
412     * Correct placement of the autocomplete list
413     * 
414     * Useful as/when a content shift or browser scroll happens
415     * 
416     * @return void
417     */
418	WPGMZA.Autocomplete.prototype.autoplace = function(){
419        if(!this.isInternal()){
420            return;
421        }
422
423        if(this.components && this.components.list){
424            const target = this.element;
425            const boundingRect = target.getBoundingClientRect();
426            if(boundingRect.width){
427                this.components.list.style.width = `${boundingRect.width}px`;
428                this.components.list.style.left = `${boundingRect.left}px`;
429                this.components.list.style.top = `${boundingRect.bottom}px`;
430            }
431        }
432	}
433
434    /**
435     * Clear the items and empty the HTML within the list
436     * 
437     * @return void
438     */
439    WPGMZA.Autocomplete.prototype.clear = function(){
440        if(!this.isInternal()){
441            return;
442        }
443
444        this.clearItems();
445        this.components.list.innerHtml = "";
446    }
447
448    /**
449     * Get the current search term
450     * 
451     * @return string
452     */
453    WPGMZA.Autocomplete.prototype.getTerm = function(){
454        return this.state.term && typeof this.state.term === 'string' && this.state.term.length ? this.state.term : false;
455    }
456
457    /**
458     * Set the current search term
459     * 
460     * @param string term
461     * 
462     * @return void
463     */
464    WPGMZA.Autocomplete.prototype.setTerm = function(term){
465        if(term && term.trim() !== this.state.term){
466            this.state.term = term.trim();
467        }
468    }
469
470    /**
471     * Clear the current items list
472     * 
473     * @return void
474     */
475    WPGMZA.Autocomplete.prototype.clearItems = function(){
476        this.setItems([]);
477    }
478
479    /**
480     * Set the current items list within the state controller
481     * 
482     * This must be an array, and will be passed through the remapItem method for filtering within any sub-class
483     * 
484     * @param array items 
485     * 
486     * @return void
487     */
488    WPGMZA.Autocomplete.prototype.setItems = function(items){
489        let remapped = [];
490        if(items && items instanceof Array){
491            for(let item of items){
492                let remap = this.remapItem(item);
493                if(remap){
494                    remapped.push(remap);
495                }
496            }
497        }
498        this.state.items = remapped;
499    }
500
501    /**
502     * Remap a location item into our local structure
503     * 
504     * In this base method we do nothing with this, but in your sub class, you would convert the returned data into
505     * the same structure we expect (display, address, coordinates, type)
506     * 
507     * @param object item 
508     * 
509     * @return object
510     */
511    WPGMZA.Autocomplete.prototype.remapItem = function(item){
512        return item;
513    }
514
515    /**
516     * Get the internal service provider
517     * 
518     * This can change internally, as seen in the Google Autocomplete module where we might switch between legacy and the places API
519     * 
520     * In this case we return false to prevent any application
521     * 
522     * @return int
523     */
524    WPGMZA.Autocomplete.prototype.getProvider = function() {
525        return false;
526    }
527
528    /**
529     * Converts a provider type (enum) into a string tag
530     * 
531     * @return string
532     */
533    WPGMZA.Autocomplete.prototype.getProviderSlug = function(){
534        let slug = 'generic'
535        switch(this.getProvider()){
536            case WPGMZA.Autocomplete.Providers.GOOGLE_AUTOCOMPLETE:
537                slug = 'google-autocomplete';
538                break;
539            case WPGMZA.Autocomplete.Providers.GOOGLE_PLACES:
540                slug = 'google-places-search';
541                break;
542            case WPGMZA.Autocomplete.Providers.CLOUD_API:
543                slug = 'legacy-cloud';
544                break;
545            case WPGMZA.Autocomplete.Providers.GOOGLE_PLACES:
546                slug = 'nominatim';
547                break;
548        }
549        return slug;
550    }
551
552    /**
553     * Check if this autocomplete instance is being managed internally for it's UI or not
554     * 
555     * @return bool
556     */
557    WPGMZA.Autocomplete.prototype.isInternal = function(){
558        return this.state.internal;
559    }
560
561    /**
562     * Set this instance as an internal module
563     * 
564     * @param bool internal 
565     * 
566     * @return void
567     */
568    WPGMZA.Autocomplete.prototype.setInternal = function(internal){
569        this.state.internal = internal;
570    }
571
572    /**
573     * Check if this instance is ready to be used
574     * 
575     * @return bool
576     */
577    WPGMZA.Autocomplete.prototype.isReady = function(){
578        return this.state.ready;
579    }
580
581    /**
582     * Set this instance as ready to be used
583     * 
584     * You should do this in your sub modules during the prepare or autoload phases
585     * 
586     * @param bool ready 
587     * 
588     * @return void
589     */
590    WPGMZA.Autocomplete.prototype.setReady = function(ready){
591        this.state.ready = ready;
592    }
593
594    /**
595     * Mark this instance as busy or not
596     * 
597     * We will add a special attribute to the element or it's parent to help with styling this 
598     * 
599     * @param bool busy 
600     * 
601     * @return void
602     */
603    WPGMZA.Autocomplete.prototype.setBusy = function(busy){
604        this.state.busy = busy;
605
606        if(this.element){
607            let target = this.element;
608            if(target.parentNode && target.parentNode.classList){
609                const parentClassList = ['wpgmza-address-container', 'wpgmza-marker-editor-address-field', 'wpgmza-directions-input-row'];
610                for(let parentClass of parentClassList){
611                    if(target.parentNode.classList.contains(parentClass)){
612                        target = target.parentNode;
613                    }
614                }
615            }
616
617            target.setAttribute('data-autocomplete-state', busy ? 'busy' : '');
618        }
619    }
620});

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.