PageSourceSearch

https://www.cox-pta.org/wp-content/plugins/rallytrain-engine/assets/js/maps-choice.js?ver=2.191.0

js cox-pta.org collected 2026-10-02 01:06:48 UTC 7,615 bytes, 165 lines download raw bytes

1/* The venue card. Tapping a venue (or any map link on an iPhone or iPad)
2   opens a small sheet instead of dropping the person onto Google's web map
3   and its "upgrade to the app" interstitial (Furi, 9-3): the venue's photo
4   when it has one, its name, the address with a Copy button, any special
5   instructions ("park in the north lot"), then the ways to open it: Apple
6   Maps where it exists, Google Maps, the browser. The Apple Maps link is
7   built from the venue's address when the link carries it
8   (data-rt-map-address), else from the query in the Google URL, else from
9   the link's own words. A plain map link with no venue data only gets the
10   sheet on iOS, where the interstitial problem lives. */
11( function () {
12	'use strict';
13
14	var ua = navigator.userAgent || '';
15	var isIOS = /iPhone|iPad|iPod/.test( ua ) || ( /Macintosh/.test( ua ) && navigator.maxTouchPoints > 1 );
16	var isMac = /Macintosh/.test( ua ) && ! isIOS;
17	var isAndroid = /Android/.test( ua );
18	var isPhone = isIOS || isAndroid;
19
20	var MAP_HOSTS = /(^|\.)(maps\.google\.[a-z.]+|google\.[a-z.]+\/maps|maps\.app\.goo\.gl|goo\.gl\/maps|maps\.apple\.com)/i;
21
22	function isMapLink( a ) {
23		var href = a.getAttribute( 'href' ) || '';
24		if ( /^geo:/i.test( href ) ) { return true; }
25		var m = href.replace( /^https?:\/\//i, '' );
26		return MAP_HOSTS.test( m ) || /^www\.google\.[a-z.]+\/maps/i.test( m ) || /^google\.[a-z.]+\/maps/i.test( m );
27	}
28
29	function venueFrom( a ) {
30		var v = {
31			name: a.getAttribute( 'data-rt-map-name' ) || '',
32			address: a.getAttribute( 'data-rt-map-address' ) || '',
33			notes: a.getAttribute( 'data-rt-map-notes' ) || '',
34			image: a.getAttribute( 'data-rt-map-image' ) || ''
35		};
36		if ( v.name || v.address ) { return v; }
37		try {
38			var u = new URL( a.href, window.location.href );
39			var q = u.searchParams.get( 'q' ) || u.searchParams.get( 'query' ) || u.searchParams.get( 'daddr' ) || u.searchParams.get( 'destination' ) || '';
40			if ( ! q ) {
41				var m = u.pathname.match( /\/maps\/(?:place|search)\/([^/]+)/ );
42				if ( m ) { q = decodeURIComponent( m[ 1 ].replace( /\+/g, ' ' ) ); }
43			}
44			if ( q ) { v.name = q; return v; }
45		} catch ( e ) { /* not a URL we can read */ }
46		v.name = ( a.textContent || '' ).replace( /\s+/g, ' ' ).trim();
47		return v;
48	}
49
50	function appleUrl( v ) {
51		var url = 'https://maps.apple.com/?';
52		if ( v.address ) {
53			url += 'address=' + encodeURIComponent( v.address ) + ( v.name ? '&q=' + encodeURIComponent( v.name ) : '' );
54		} else {
55			url += 'q=' + encodeURIComponent( v.name );
56		}
57		return url;
58	}
59
60	function escapeHtml( s ) {
61		return String( s ).replace( /[&<>"']/g, function ( c ) {
62			return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[ c ];
63		} );
64	}
65
66	var sheet = null;
67
68	function close() {
69		if ( sheet ) { sheet.remove(); sheet = null; }
70		document.removeEventListener( 'keydown', onKey );
71	}
72	function onKey( e ) { if ( e.key === 'Escape' ) { close(); } }
73
74	function copyText( text, btn ) {
75		function done( ok ) {
76			if ( ! btn ) { return; }
77			var was = btn.innerHTML;
78			btn.innerHTML = ok ? '<i class="fa-solid fa-check" aria-hidden="true"></i> Copied' : 'Could not copy';
79			btn.classList.toggle( 'is-done', ok );
80			setTimeout( function () { btn.innerHTML = was; btn.classList.remove( 'is-done' ); }, 1600 );
81		}
82		function fallback( t ) {
83			var ta = document.createElement( 'textarea' );
84			ta.value = t;
85			ta.setAttribute( 'readonly', '' );
86			ta.style.cssText = 'position:fixed;top:0;left:0;opacity:0';
87			document.body.appendChild( ta );
88			ta.select();
89			var ok = false;
90			try { ok = document.execCommand( 'copy' ); } catch ( e ) { ok = false; }
91			ta.remove();
92			return ok;
93		}
94		if ( navigator.clipboard && navigator.clipboard.writeText ) {
95			navigator.clipboard.writeText( text ).then( function () { done( true ); }, function () { done( fallback( text ) ); } );
96		} else {
97			done( fallback( text ) );
98		}
99	}
100
101	function open( a ) {
102		close();
103		var v = venueFrom( a );
104		var label = v.name || ( a.textContent || '' ).trim() || 'this location';
105		var href = a.href;
106		/* Real links, tapped by a real finger: that is the one path iOS
107		   reliably hands to the Maps app (maps.apple.com) or to the Google
108		   Maps app when it is installed (its universal link), and that
109		   Safari never treats as a blocked popup. On a desktop the Google
110		   link opens a tab, since there is no app to hand off to. */
111		var choices = '';
112		if ( isIOS || isMac ) {
113			choices += '<a class="rt-maps-choice-btn" href="' + escapeHtml( appleUrl( v ) ) + '"' + ( isMac ? ' target="_blank" rel="noopener"' : '' ) + '><i class="fa-brands fa-apple" aria-hidden="true"></i> Open in Apple Maps</a>';
114		}
115		choices += '<a class="rt-maps-choice-btn" href="' + escapeHtml( href ) + '"' + ( isPhone ? '' : ' target="_blank" rel="noopener"' ) + '><i class="fa-brands fa-google" aria-hidden="true"></i> Open in Google Maps</a>';
116		if ( isPhone ) {
117			choices += '<a class="rt-maps-choice-btn rt-maps-choice-btn--web" href="' + escapeHtml( href ) + '" target="_blank" rel="noopener"><i class="fa-solid fa-globe" aria-hidden="true"></i> Open in browser</a>';
118		}
119		sheet = document.createElement( 'div' );
120		sheet.className = 'rt-maps-choice';
121		sheet.setAttribute( 'role', 'dialog' );
122		sheet.setAttribute( 'aria-modal', 'true' );
123		sheet.setAttribute( 'aria-label', label );
124		sheet.innerHTML =
125			'<div class="rt-maps-choice-scrim" data-close></div>' +
126			'<div class="rt-maps-choice-sheet">' +
127				'<button type="button" class="rt-maps-choice-x" data-close aria-label="Close"><i class="fa-solid fa-xmark" aria-hidden="true"></i></button>' +
128				( v.image ? '<img class="rt-maps-choice-photo" src="' + escapeHtml( v.image ) + '" alt="">' : '' ) +
129				'<p class="rt-maps-choice-title">' + escapeHtml( label ) + '</p>' +
130				( v.address
131					? '<p class="rt-maps-choice-address"><span>' + escapeHtml( v.address ) + '</span> <button type="button" class="rt-maps-choice-copy" data-copy><i class="fa-regular fa-copy" aria-hidden="true"></i> Copy</button></p>'
132					: '' ) +
133				( v.notes
134					? '<p class="rt-maps-choice-notes"><i class="fa-solid fa-circle-info" aria-hidden="true"></i> <span>' + escapeHtml( v.notes ).replace( /\n/g, '<br>' ) + '</span></p>'
135					: '' ) +
136				choices +
137				'<button type="button" class="rt-maps-choice-btn rt-maps-choice-btn--cancel" data-close>Cancel</button>' +
138			'</div>';
139		document.body.appendChild( sheet );
140		sheet.addEventListener( 'click', function ( e ) {
141			var t = e.target.closest( '[data-copy], [data-close], a.rt-maps-choice-btn' );
142			if ( ! t ) { return; }
143			if ( t.hasAttribute( 'data-close' ) ) { close(); return; }
144			if ( t.hasAttribute( 'data-copy' ) ) { copyText( v.address, t ); return; }
145			// A choice is a plain link: let it navigate, fold the sheet after.
146			setTimeout( close, 0 );
147		} );
148		document.addEventListener( 'keydown', onKey );
149		var first = sheet.querySelector( 'a.rt-maps-choice-btn' );
150		if ( first ) { first.focus(); }
151	}
152
153	document.addEventListener( 'click', function ( e ) {
154		var a = e.target.closest( 'a[href]' );
155		if ( ! a || ! isMapLink( a ) || a.hasAttribute( 'data-rt-map-direct' ) || a.closest( '.rt-maps-choice' ) ) { return; }
156		var hasVenue = a.hasAttribute( 'data-rt-map-name' ) || a.hasAttribute( 'data-rt-map-address' );
157		if ( ! hasVenue && ! isIOS ) { return; }
158		if ( e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1 ) { return; }
159		e.preventDefault();
160		open( a );
161	}, true );
162
163	/* For checking the sheet from a desktop browser: rtMapsChoice.open(link). */
164	window.rtMapsChoice = { isIOS: isIOS, open: open, isMapLink: isMapLink };
165}() );

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.