PageSourceSearch

https://hauntedeurekasprings.com/wp-content/plugins/wp-google-maps/js/v8/atlas-major-tabs.js?ver=10.1.09

js hauntedeurekasprings.com collected 2026-09-25 18:41:12 UTC 19,303 bytes, 559 lines download raw bytes

1/**
2 * @namespace WPGMZA
3 * @module AtlasMajorTabs
4 * @requires WPGMZA.SidebarGroupings
5 *
6 * Bridges the Atlas Major tab bar to the existing SidebarGroupings navigation system.
7 * Tabs carry data-am-group attributes that map to data-group values on groupings.
8 */
9jQuery(function($) {
10
11	if(WPGMZA.currentPage != "map-edit")
12		return;
13
14	if(!document.querySelector('.wpgmza-atlas-major'))
15		return;
16
17	WPGMZA.AtlasMajorTabs = function(){
18		var self = this;
19		this.tabBar = $('.am-tabs');
20		this.tabs = this.tabBar.find('.am-tab[data-am-group]');
21		this.activeGroup = null;
22
23		this.tabs.on('click', function(){
24			var groupId = $(this).data('am-group');
25			if(groupId){
26				/* Belt-and-braces: discard any in-progress drawing
27				   BEFORE switching tabs. The `grouping-closed`
28				   listener further down also handles this path (via
29				   closeAll → grouping-closed → our synthesized
30				   feature-block-closed), but firing here too
31				   guarantees the discard runs regardless of the
32				   closeAll timing or any race with the tab switch.
33				   discardChanges is gated on `this.feature` so a
34				   double-discard is a no-op. */
35				$('.grouping[data-feature-discard]').each(function(){
36					$(this).trigger('feature-block-closed');
37				});
38
39				self.activateTab($(this), groupId);
40			}
41		});
42
43		/* Make the entire .heading.has-back row clickable, not just
44		   the small caret-left arrow on its left edge. The core
45		   SidebarGroupings handler binds to `.grouping .item` clicks
46		   only, so anywhere else in the heading row was inert.
47		   This caused a confusing UX: the whole row hover-highlights
48		   (signalling clickability) but only the arrow column actually
49		   navigated back. We forward clicks anywhere on the row to
50		   the inner .item.caret-left, which then bubbles through the
51		   existing handler. Scoped to .wpgmza-atlas-major so Atlas
52		   Novus behaviour is unchanged. */
53		$(document.body).on('click', '.wpgmza-atlas-major .heading.has-back', function(event){
54			/* If the click hit the .item directly, the existing
55			   SidebarGroupings delegation will handle it — don't
56			   double-fire. */
57			if($(event.target).closest('.item').length) return;
58			$(this).find('> .item').first().trigger('click');
59		});
60
61		/* Release the drawing manager when a feature grouping is
62		   being closed by navigation away (e.g. clicking a settings
63		   tab while in the Circles / Polygons / etc. feature panel
64		   with the drawing manager active).
65		   Background: sidebar-groupings.js:openTabByGroupId fires
66		   `feature-block-closed` ONLY when the TARGET grouping has
67		   `data-feature-discard="true"` — that's the
68		   feature-to-feature switch path (Circles → Polygons fires
69		   discard on the polygon grouping; all FeaturePanels
70		   listen on `.grouping` and react). Settings groupings
71		   don't carry `data-feature-discard`, so the
72		   feature-to-settings path NEVER fires the trigger —
73		   drawing manager stays active, the in-progress shape stays
74		   parked, and the user can't return to that feature panel
75		   without a page reload.
76		   Fix: listen for `grouping-closed` (fired by
77		   sidebar-groupings.js:closeAll for every closing
78		   grouping) and synthesize a `feature-block-closed`
79		   event on any closing grouping that carries
80		   `data-feature-discard`. The existing
81		   feature-panel.js:51 listener then triggers
82		   onTabDeactivated → discardChanges and resets drawing
83		   mode to MODE_NONE. Works regardless of navigation path
84		   (tab bar, back arrow, programmatic switch). */
85		$(document.body).on('grouping-closed', function(event, groupId){
86			if(!groupId) return;
87			var closingGrouping = $('.grouping[data-group="' + groupId + '"]').first();
88			if(!closingGrouping.length) return;
89			if(closingGrouping.data('feature-discard')){
90				closingGrouping.trigger('feature-block-closed');
91			}
92		});
93
94		/* Listen for grouping changes from other sources (context menu, quick actions) */
95		$(document.body).on('grouping-opened', function(event, groupId){
96			self.syncTabState(groupId);
97		});
98
99		/* Also catch the event on .grouping elements directly (jQuery trigger doesn't always bubble) */
100		$('.grouping').on('grouping-opened', function(event, groupId){
101			self.syncTabState(groupId);
102		});
103
104		/* Activate the first tab on load. Retry if
105		 * WPGMZA.mapEditPage.sidebarGroupings isn't ready yet — the
106		 * AtlasMajorTabs constructor runs on window.load, but
107		 * sidebarGroupings is created during MapEditPage's own init
108		 * which can lag. Without the retry we'd silently bail and the
109		 * markers grouping would stay `display:none` until the user
110		 * clicks the tab themselves. */
111		var firstTab = this.tabs.filter('.on').first();
112		if(firstTab.length){
113			var initialGroup = firstTab.data('am-group');
114			if(initialGroup){
115				var attempts = 0;
116				var tryActivate = function(){
117					if(WPGMZA.mapEditPage && WPGMZA.mapEditPage.sidebarGroupings){
118						self.activateTab(firstTab, initialGroup);
119						return true;
120					}
121					return false;
122				};
123				if(!tryActivate()){
124					var retry = setInterval(function(){
125						attempts++;
126						if(tryActivate() || attempts > 30){
127							clearInterval(retry);
128						}
129					}, 100);
130				}
131			}
132		}
133
134		/* Sub-tab navigation (horizontal tabs within panels like Store Locator) */
135		$('.am-subnav').on('click', '.am-subnav-item', function(){
136			var subnav = $(this).closest('.am-subnav');
137			subnav.find('.am-subnav-item').removeClass('on');
138			$(this).addClass('on');
139		});
140
141		/* Sync sub-tab active state when groupings open from other sources */
142		$(document.body).on('grouping-opened', function(event, groupId){
143			$('.am-subnav .am-subnav-item').each(function(){
144				var itemGroup = $(this).data('group');
145				if(itemGroup === groupId){
146					$(this).closest('.am-subnav').find('.am-subnav-item').removeClass('on');
147					$(this).addClass('on');
148				}
149			});
150		});
151
152		/* Auto-focus the Address/GPS input when the marker editor opens in
153		   Add mode. FeaturePanel flips the visibility of
154		   .wpgmza-feature-drawing-instructions vs
155		   .wpgmza-feature-editing-instructions (showInstructions) on mode
156		   change — we defer one tick so that DOM swap has landed before
157		   we check. */
158		$(document.body).on('grouping-opened', function(event, groupId){
159			if(groupId !== 'map-markers-editor'){
160				return;
161			}
162			setTimeout(function(){
163				var grouping = $('[data-group="map-markers-editor"]');
164				var drawing = grouping.find('.wpgmza-feature-drawing-instructions');
165				if(drawing.length && drawing.is(':visible')){
166					var addressInput = grouping.find('#wpgmza_add_address_map_editor');
167					if(addressInput.length && !addressInput.is(':disabled')){
168						addressInput.trigger('focus');
169					}
170				}
171			}, 0);
172		});
173
174		/* Sync toolbar map name with the form field */
175		this.syncToolbarFields();
176	}
177
178	/* Groupings that are navigation hubs — when opened, auto-navigate
179	   to the first visible child item */
180	WPGMZA.AtlasMajorTabs.AUTO_NAV_HUBS = [
181		'map-settings-themes'
182	];
183
184	WPGMZA.AtlasMajorTabs.prototype.activateTab = function(tabElement, groupId){
185		/* Update tab active state */
186		this.tabs.removeClass('on');
187		tabElement.addClass('on');
188		this.activeGroup = groupId;
189
190		/* Open the grouping via SidebarGroupings */
191		if(WPGMZA.mapEditPage && WPGMZA.mapEditPage.sidebarGroupings){
192			WPGMZA.mapEditPage.sidebarGroupings.openTabByGroupId(groupId);
193
194			/* For nav hubs, auto-click the first visible child sub-tab */
195			if(WPGMZA.AtlasMajorTabs.AUTO_NAV_HUBS.indexOf(groupId) !== -1){
196				setTimeout(function(){
197					var grouping = $('[data-group="' + groupId + '"]');
198					/* Try visible am-subnav-item first (Atlas Major sub-tabs) */
199					var firstVisible = grouping.find('.am-subnav .am-subnav-item:visible').first();
200					if(!firstVisible.length){
201						/* Fallback to hidden nav items */
202						firstVisible = grouping.find('.am-hidden-nav .item.caret-right').first();
203					}
204					if(firstVisible.length){
205						firstVisible.trigger('click');
206					}
207				}, 50);
208			}
209		}
210	}
211
212	WPGMZA.AtlasMajorTabs.prototype.syncTabState = function(groupId){
213		/* Find the tab that owns this group or a parent of it */
214		var matched = false;
215		var self = this;
216
217		this.tabs.each(function(){
218			var tabGroup = $(this).data('am-group');
219
220			/* Direct match */
221			if(tabGroup === groupId){
222				self.tabs.removeClass('on');
223				$(this).addClass('on');
224				self.activeGroup = groupId;
225				matched = true;
226				return false;
227			}
228
229			/* Prefix match — e.g. tab targets "map-settings-store-locator-general"
230			   but opened group is "map-settings-store-locator-style".
231			   Strip the last segment of both and compare the prefix.
232			   Only applies when prefix has 3+ segments to avoid false matches
233			   like "map-markers" matching "map-datasets". */
234			var tabPrefix = tabGroup.replace(/-[^-]+$/, '');
235			var groupPrefix = groupId.replace(/-[^-]+$/, '');
236			var segmentCount = tabPrefix.split('-').length;
237			if(tabPrefix === groupPrefix && tabPrefix !== tabGroup && segmentCount >= 3){
238				self.tabs.removeClass('on');
239				$(this).addClass('on');
240				self.activeGroup = tabGroup;
241				matched = true;
242				return false;
243			}
244		});
245
246		if(!matched){
247			/* Check if the opened group is a child of a tab's group */
248			this.tabs.each(function(){
249				var tabGroup = $(this).data('am-group');
250				var openedGrouping = $('[data-group="' + groupId + '"]');
251
252				/* Walk up the back-navigation chain to find parent */
253				var backItem = openedGrouping.find('.heading .item.caret-left[data-group]');
254				while(backItem.length){
255					var parentGroup = backItem.data('group');
256					if(parentGroup === tabGroup){
257						self.tabs.removeClass('on');
258						$(this).addClass('on');
259						self.activeGroup = tabGroup;
260						matched = true;
261						return false;
262					}
263					var parentGrouping = $('[data-group="' + parentGroup + '"]');
264					backItem = parentGrouping.find('.heading .item.caret-left[data-group]');
265				}
266			});
267		}
268	}
269
270	WPGMZA.AtlasMajorTabs.prototype.syncToolbarFields = function(){
271		var titleInput = $('#wpgmza_title');
272		var toolbarTitle = $('#am_wpgmza_title');
273		var shortcodeInput = $('#shortcode_input');
274		var toolbarShortcode = $('#am_shortcode_display');
275
276		if(titleInput.length && toolbarTitle.length){
277			/* Initial sync */
278			toolbarTitle.val(titleInput.val());
279
280			/* Two-way bind */
281			toolbarTitle.on('input change', function(){
282				titleInput.val($(this).val()).trigger('change');
283			});
284			titleInput.on('input change', function(){
285				toolbarTitle.val($(this).val());
286			});
287		}
288
289		if(shortcodeInput.length && toolbarShortcode.length){
290			/* Sync shortcode display — PHP sets the value attribute server-side */
291			var updateShortcode = function(){
292				/* Try property first, then attribute, then raw DOM */
293				var val = shortcodeInput.val()
294					|| shortcodeInput.attr('value')
295					|| shortcodeInput[0].getAttribute('value')
296					|| '';
297				if(val && val.trim()){
298					toolbarShortcode.text(val);
299					return true;
300				}
301				return false;
302			};
303
304			shortcodeInput.on('input change', updateShortcode);
305
306			/* Immediate attempt */
307			if(!updateShortcode()){
308				/* Poll until value appears */
309				var pollCount = 0;
310				var pollInterval = setInterval(function(){
311					if(updateShortcode() || pollCount > 30){
312						clearInterval(pollInterval);
313					}
314					pollCount++;
315				}, 500);
316			}
317		}
318
319		/* Copy shortcode on pill click */
320		$('.am-sc-pill').on('click', function(){
321			var text = (shortcodeInput.val() || toolbarShortcode.text() || '').trim();
322			if(!text) return;
323
324			/* Use temp input + execCommand as primary (works in all contexts) */
325			var $temp = $('<input>');
326			$('body').append($temp);
327			$temp.val(text).select();
328			document.execCommand('copy');
329			$temp.remove();
330
331			if(WPGMZA.notification){
332				WPGMZA.notification("Shortcode Copied");
333			}
334		});
335
336		/* Override the core .wpgmza_copy_shortcode handler — unbind then rebind with centered notification */
337		$('body').off('click', '.wpgmza_copy_shortcode');
338		$('body').on('click', '.wpgmza_copy_shortcode', function(){
339			var text = $(this).val();
340			if(!text) return;
341
342			var $temp = $('<input>');
343			$('body').append($temp);
344			$temp.val(text).select();
345			document.execCommand('copy');
346			$temp.remove();
347
348			if(WPGMZA.notification){
349				WPGMZA.notification("Shortcode Copied");
350			}
351		});
352	}
353
354	/**
355	 * Quick-add marker: when user types address in the markers tab bar
356	 * and clicks Add, populate the editor's address field and navigate to editor.
357	 */
358	WPGMZA.AtlasMajorTabs.prototype.initQuickAddMarker = function(){
359		var self = this;
360		var quickInput = $('#am-quick-add-address');
361		var quickBtn = $('.am-marker-quick-add');
362		var resultsContainer = $('#am-quick-add-autocomplete-results');
363
364		if(!quickInput.length || !quickBtn.length)
365			return;
366
367		/* Add wpgmza-address class NOW (after MapEditPage init has finished
368		   its $("input.wpgmza-address").each loop) so the body-level keypress
369		   delegation picks up our input, but no AddressInput/Google autocomplete
370		   gets created on it. */
371		quickInput.addClass('wpgmza-address');
372
373		/* Override shouldAddressFieldUseEnhancedAutocomplete so the core
374		   keypress handler on .wpgmza-address also fires for our input.
375		   We wrap the original method — no core code changes needed. */
376		if(WPGMZA.mapEditPage){
377			var originalCheck = WPGMZA.mapEditPage.shouldAddressFieldUseEnhancedAutocomplete;
378			WPGMZA.mapEditPage.shouldAddressFieldUseEnhancedAutocomplete = function(element){
379				if(element && element.id === 'am-quick-add-address'){
380					return true;
381				}
382				return originalCheck.call(this, element);
383			};
384		}
385
386		/* The core renders results into #wpgmza_autocomplete_search_results
387		   which lives inside the editor accordion. We observe that element
388		   and mirror its content into our local results container. */
389		var coreResults = $('#wpgmza_autocomplete_search_results');
390		var quickAddActive = false;
391
392		quickInput.on('focusin', function(){ quickAddActive = true; });
393		quickInput.on('focusout', function(){
394			setTimeout(function(){
395				quickAddActive = false;
396				resultsContainer.hide();
397			}, 400);
398		});
399
400		var coreDisabled = $('#wpgmza_autoc_disabled');
401
402		function mirrorCoreElement(coreEl, localContainer){
403			if(!coreEl.length) return;
404
405			var observer = new MutationObserver(function(){
406				if(!quickAddActive) return;
407				var html = coreEl.html();
408				if(html && html.trim().length > 0){
409					localContainer.html(html).show();
410					coreEl.hide();
411				} else {
412					localContainer.hide();
413				}
414			});
415			observer.observe(coreEl[0], { childList: true, subtree: true, characterData: true });
416
417			var styleObserver = new MutationObserver(function(){
418				if(!quickAddActive) return;
419				if(coreEl.is(':visible') && coreEl.html().trim().length > 0){
420					localContainer.html(coreEl.html()).show();
421					coreEl.hide();
422				}
423			});
424			styleObserver.observe(coreEl[0], { attributes: true, attributeFilter: ['style'] });
425		}
426
427		/* Mirror both the results and the disabled/warning div */
428		mirrorCoreElement(coreResults, resultsContainer);
429
430		if(coreDisabled.length){
431			var disabledContainer = $('<div id="am-quick-add-autoc-disabled"></div>');
432			quickInput.closest('.am-ml-bar').append(disabledContainer);
433			disabledContainer.hide();
434			mirrorCoreElement(coreDisabled, disabledContainer);
435		}
436
437		/* When a result is clicked in our container, fill our input */
438		resultsContainer.on('click', '.wpgmza_ac_result', function(e){
439			e.preventDefault();
440			e.stopPropagation();
441			var index = $(this).data('id');
442			var lat = $(this).data('lat');
443			var lng = $(this).data('lng');
444			var name = $(this).find('[id^="wpgmza_item_address_"]').html() || $(this).text();
445			quickInput.val(name.trim());
446			resultsContainer.hide();
447		});
448
449		/* When "Add" is clicked, copy the address to the editor input
450		   and navigate to the editor grouping */
451		quickBtn.on('click', function(e){
452			var address = quickInput.val().trim();
453
454			setTimeout(function(){
455				if(address){
456					var editorAddress = $('#wpgmza_add_address_map_editor');
457					if(editorAddress.length){
458						editorAddress.val(address).trigger('change').trigger('input');
459					}
460				}
461				quickInput.val('');
462				resultsContainer.hide();
463			}, 200);
464		});
465
466		/* Enter key in quick input triggers Add */
467		quickInput.on('keydown', function(e){
468			if(e.key === 'Enter'){
469				e.preventDefault();
470				quickBtn.trigger('click');
471			}
472		});
473	}
474
475	/* Initialize after map edit page is ready */
476	$(document.body).on('wpgmza_map_edit_page_created', function(){
477		if(!WPGMZA.atlasMajorTabs){
478			WPGMZA.atlasMajorTabs = new WPGMZA.AtlasMajorTabs();
479			WPGMZA.atlasMajorTabs.initQuickAddMarker();
480		}
481	});
482
483	/* Fallback initialization */
484	$(window).on('load', function(){
485		if(!WPGMZA.atlasMajorTabs && document.querySelector('.wpgmza-atlas-major')){
486			WPGMZA.atlasMajorTabs = new WPGMZA.AtlasMajorTabs();
487			WPGMZA.atlasMajorTabs.initQuickAddMarker();
488		}
489		initDualZoomSliders();
490	});
491
492	/* ========================================================
493	   DUAL-HANDLE ZOOM RANGE SLIDER
494	   Visual wrapper around the hidden map_max_zoom / map_min_zoom
495	   inputs. Two overlapping range inputs share a track; the fill
496	   bar shows the allowed zoom range between them.
497	   ======================================================== */
498	function initDualZoomSliders(){
499		$('.wpgmza-dual-zoom-slider').each(function(){
500			var $slider = $(this);
501			if($slider.data('wpgmza-dual-zoom-inited')) return;
502			$slider.data('wpgmza-dual-zoom-inited', true);
503
504			var $outInput  = $slider.find('.wpgmza-dual-zoom-out-input');
505			var $inInput   = $slider.find('.wpgmza-dual-zoom-in-input');
506			var $outLabel  = $slider.find('.wpgmza-dual-zoom-out-label');
507			var $inLabel   = $slider.find('.wpgmza-dual-zoom-in-label');
508			var $fill      = $slider.find('.wpgmza-dual-zoom-fill');
509
510			/* Sync from hidden inputs (holds saved values) into the range inputs */
511			var $hiddenOut = $slider.closest('fieldset').find('input[name="map_max_zoom"]');
512			var $hiddenIn  = $slider.closest('fieldset').find('input[name="map_min_zoom"]');
513
514			var savedOut = parseInt($hiddenOut.val());
515			var savedIn  = parseInt($hiddenIn.val());
516			if(!isNaN(savedOut)) $outInput.val(savedOut);
517			if(!isNaN(savedIn))  $inInput.val(savedIn);
518
519			var min = parseInt($slider.data('min')) || 0;
520			var max = parseInt($slider.data('max')) || 22;
521
522			function update(fromInput){
523				var outVal = parseInt($outInput.val());
524				var inVal  = parseInt($inInput.val());
525
526				/* Enforce outVal <= inVal, push whichever handle is driving */
527				if(outVal > inVal){
528					if(fromInput === 'out'){
529						$inInput.val(outVal);
530						inVal = outVal;
531					} else {
532						$outInput.val(inVal);
533						outVal = inVal;
534					}
535				}
536
537				$outLabel.text(outVal);
538				$inLabel.text(inVal);
539				$hiddenOut.val(outVal);
540				$hiddenIn.val(inVal);
541
542				var range = max - min;
543				var leftPct  = ((outVal - min) / range) * 100;
544				var rightPct = ((inVal - min) / range) * 100;
545				$fill.css({left: leftPct + '%', width: (rightPct - leftPct) + '%'});
546
547				/* Trigger change on the hidden inputs so live preview picks it up */
548				$hiddenOut.trigger('change');
549				$hiddenIn.trigger('change');
550			}
551
552			$outInput.on('input', function(){ update('out'); });
553			$inInput.on('input', function(){ update('in'); });
554
555			/* Initial render */
556			update();
557		});
558	}
559});

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.