PageSourceSearch

https://www.virtualbelfastmuraltour.com/js/global.js

js virtualbelfastmuraltour.com collected 2026-10-02 21:11:47 UTC 6,204 bytes, 207 lines download raw bytes

1var mapLatlng = { lat: 54.59, lng: - 5.93 };
2var mapOptions = { zoom: 2, center: mapLatlng, mapTypeId: 'roadmap' };
3var BelfastLat = 54.5948;
4var BelfastLng = -5.926;
5var VBMTHex = "#6af400";
6var VBMTHexAlt = "#F40000";
7var EntityName = "Mural";
8function placeMarker(location, text, url) {
9	var marker = new google.maps.Marker({ position: location, map: map, title: text });
10	google.maps.event.addListener(marker, 'click', function () {
11		window.location = url;
12	});
13}
14function print_mail_to_link() {
15	document.write("<a href=\"mailto");
16	document.write(":" + lhs + "@");
17	document.write(rhs + "\">" + lhs + "@" + rhs + "<\/a>");
18}
19
20function setSelectedIndex(s, v) {
21	for (var i = 0; i < s.options.length; i++) {
22		if (s.options[i].value == v) {
23			s.options[i].selected = true;
24			return;
25		}
26	}
27}
28
29function sendcr(table, tableid, field, newvalue, reason, isencoded) {
30	var rn = Math.floor(Math.random() * 9999);
31
32	$.ajax({
33		url: 'request/addcr.php',
34		type: "POST",
35		data: {
36			rn: rn,
37			cr_table: table,
38			cr_tableid: tableid,
39			cr_field: field,
40			cr_new: newvalue,
41			cr_reason: reason,
42			isencoded: isencoded
43		},
44		success: function (result) {
45			//console.log(result);
46		},
47		error: function (x, y, z) {
48			console.log(y);
49		}
50	});
51}
52
53const monthNames = ["January", "February", "March", "April", "May", "June",
54	"July", "August", "September", "October", "November", "December"
55];
56
57const nth = (d) => {
58	if (d > 3 && d < 21) return 'th';
59	switch (d % 10) {
60		case 1: return "st";
61		case 2: return "nd";
62		case 3: return "rd";
63		default: return "th";
64	}
65};
66
67
68function urldecode(str) {
69	let newStr = '';
70	const len = str.length;
71
72	for (let i = 0; i < len; i++) {
73		let c = str.charAt(i);
74
75		if (c === '+') {
76			newStr += ' ';
77		}
78		else if (c === '%') {
79			const hex = str.substr(i + 1, 2);
80			const code = parseInt(hex, 16);
81			newStr += String.fromCharCode(code);
82			i += 2;
83		}
84		else {
85			newStr += c;
86		}
87	}
88
89	return newStr;
90}
91
92
93
94
95// ------------------------------
96// Photo attribution / citation
97// ------------------------------
98document.addEventListener('DOMContentLoaded', function () {
99	function getCanonicalUrl() {
100		const link = document.querySelector('link[rel=canonical]');
101		return link && link.href ? link.href : window.location.href.split('#')[0];
102	}
103
104	const rawPageTitle = (document.title || '').trim();
105	// Strip a trailing "in Belfast" or "Mural in Belfast" (case-insensitive)
106	const cleanedTitle = rawPageTitle.replace(/\s+(?:Mural\s+)?in Belfast\s*$/i, '');
107	const pageTitle = cleanedTitle;
108	const pageUrl = getCanonicalUrl();
109
110	const caps = document.querySelectorAll('.photo-credit');
111	// console.log('[photo-credit] found:', caps.length);
112
113	caps.forEach(function (cap, idx) {
114		const author = (cap.dataset.author || 'Virtual Belfast Mural Tour').trim();
115		const license = (cap.dataset.license || 'Free to use with attribution').trim();
116		const licenseUrl = (cap.dataset.licenseUrl || '').trim();
117		const workTitle = (cap.dataset.workTitle || '').trim();
118
119		// Use workTitle if present, otherwise pageTitle (with suffix stripped)
120		const citeTitle = (workTitle || pageTitle) + ' - Virtual Belfast Mural Tour';
121		const licenseHtml = licenseUrl
122			? '<a href="' + licenseUrl + '" rel="license noopener noreferrer">' + license + '</a>'
123			: license;
124
125		// Unique id for accessibility
126		const bodyId = 'photo-attr-body-' + idx;
127
128		cap.innerHTML = ''
129			+ '<details class="photo-attr">'
130			+ '  <summary class="photo-attr__summary" role="button" aria-controls="' + bodyId + '">'
131			+ '    Photo Attribution/Citation Details'
132			+ '  </summary>'
133			+ '  <div class="photo-attr__body" id="' + bodyId + '">'
134			+ '    <span>Photo by <span class="photo-author">' + author + '</span>.</span> '
135			+ '    <span> Please credit: "<span class="photo-cite-title">' + citeTitle + '</span>"</span> '
136			+ '    <span> (<a href="' + pageUrl + '" rel="noopener noreferrer">' + pageUrl + '</a>).</span> '
137			+ '    <span> License: ' + licenseHtml + '.</span> '
138			+ '    <button type="button" class="copy-citation" aria-label="Copy citation">Copy</button>'
139			+ '  </div>'
140			+ '</details>';
141
142		const btn = cap.querySelector('.copy-citation');
143		btn.addEventListener('click', function (e) {
144			e.preventDefault();
145			const text = 'Photo by ' + author + '. Please credit: “' + citeTitle + '” (' + pageUrl + '). '
146				+ 'License: ' + license + (licenseUrl ? ' – ' + licenseUrl : '') + '.';
147			copyText(text, btn).finally(function () {
148				setTimeout(function () { btn.textContent = 'Copy'; }, 1500);
149			});
150		});
151	});
152});
153
154function copyText(text, btn) {
155	// Prefer the async Clipboard API when available + secure
156	if (navigator.clipboard && window.isSecureContext) {
157		return navigator.clipboard.writeText(text)
158			.then(() => { btn.textContent = 'Copied!'; })
159			.catch((err) => {
160				console.warn('[copy] clipboard.writeText failed:', err);
161				return legacyCopy(text, btn);
162			});
163	} else {
164		return legacyCopy(text, btn);
165	}
166
167	function legacyCopy(txt, buttonEl) {
168		try {
169			const ta = document.createElement('textarea');
170			ta.value = txt;
171			// Keep off-screen but selectable
172			ta.style.position = 'fixed';
173			ta.style.top = '-1000px';
174			ta.style.opacity = '0';
175			document.body.appendChild(ta);
176
177			// iOS needs a special selection flow
178			if (/ipad|iphone|ipod/i.test(navigator.userAgent)) {
179				const range = document.createRange();
180				range.selectNodeContents(ta);
181				const sel = window.getSelection();
182				sel.removeAllRanges();
183				sel.addRange(range);
184				ta.setSelectionRange(0, ta.value.length);
185			} else {
186				ta.select();
187			}
188
189			const ok = document.execCommand('copy');
190			document.body.removeChild(ta);
191
192			if (ok) {
193				buttonEl.textContent = 'Copied!';
194				return Promise.resolve();
195			} else {
196				throw new Error('execCommand returned false');
197			}
198		} catch (e) {
199			console.warn('[copy] execCommand fallback failed:', e);
200			alert('Sorry, your browser blocked copying. Please select and copy manually.');
201			return Promise.reject(e);
202		}
203	}
204}
205
206
207

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.