PageSourceSearch

https://www.mansionhouseyork.com/themes/jadu-alpha/dist/js/util.js?3.133.0?v=1.2.0

js mansionhouseyork.com collected 2026-10-02 07:16:59 UTC 12,790 bytes, 636 lines download raw bytes

1(function($) {
2
3	/**
4	 * Generate an indented list of links from a nav. Meant for use with panel().
5	 * @return {jQuery} jQuery object.
6	 */
7	$.fn.navList = function() {
8
9		var	$this = $(this);
10		$a = $this.find('a'),
11			b = [];
12
13		$a.each(function() {
14
15			var	$this = $(this),
16				indent = Math.max(0, $this.parents('li').length - 1),
17				href = $this.attr('href'),
18				target = $this.attr('target');
19
20			b.push(
21				'<li><a ' +
22				'class="link depth-' + indent + '"' +
23				( (typeof target !== 'undefined' && target != '') ? ' target="' + target + '"' : '') +
24				( (typeof href !== 'undefined' && href != '') ? ' href="' + href + '"' : '') +
25				'>' +
26				'<span class="indent-' + indent + '"></span>' +
27				$this.text() +
28				'</a></li>'
29			);
30
31		});
32
33		return b.join('');
34
35	};
36
37	/**
38	 * Panel-ify an element.
39	 * @param {object} userConfig User config.
40	 * @return {jQuery} jQuery object.
41	 */
42	$.fn.panel = function(userConfig) {
43
44		// No elements?
45		if (this.length == 0)
46			return $this;
47
48		// Multiple elements?
49		if (this.length > 1) {
50
51			for (var i=0; i < this.length; i++)
52				$(this[i]).panel(userConfig);
53
54			return $this;
55
56		}
57
58		// Vars.
59		var	$this = $(this),
60			$body = $('body'),
61			$window = $(window),
62			id = $this.attr('id'),
63			config;
64
65		// Config.
66		config = $.extend({
67
68			// Delay.
69			delay: 0,
70
71			// Hide panel on link click.
72			hideOnClick: false,
73
74			// Hide panel on escape keypress.
75			hideOnEscape: false,
76
77			// Hide panel on swipe.
78			hideOnSwipe: false,
79
80			// Reset scroll position on hide.
81			resetScroll: false,
82
83			// Reset forms on hide.
84			resetForms: false,
85
86			// Side of viewport the panel will appear.
87			side: null,
88
89			// Target element for "class".
90			target: $this,
91
92			// Class to toggle.
93			visibleClass: 'visible',
94
95			// Focus lock the navigation menu.
96			focusLock: false,
97
98			// First tabbable item (force to lastTabElement on reverse-tab)
99			firstTabElement: null,
100
101			// Last tabbable item (force to firstTabElement on tab)
102			lastTabElement: null,
103		}, userConfig);
104
105		// Expand "target" if it's not a jQuery object already.
106		if (typeof config.target != 'jQuery')
107			config.target = $(config.target);
108
109		// Panel.
110
111		// Methods.
112		$this._hide = function(event) {
113
114			// Already hidden? Bail.
115			if (!config.target.hasClass(config.visibleClass))
116				return;
117
118			// If an event was provided, cancel it.
119			if (event) {
120
121				event.preventDefault();
122				event.stopPropagation();
123
124			}
125
126			// Hide.
127			config.target.removeClass(config.visibleClass);
128
129			// Post-hide stuff.
130			window.setTimeout(function() {
131
132				// Reset scroll position.
133				if (config.resetScroll)
134					$this.scrollTop(0);
135
136				// Reset forms.
137				if (config.resetForms)
138					$this.find('form').each(function() {
139						this.reset();
140					});
141
142			}, config.delay);
143
144		};
145
146		// Vendor fixes.
147		$this
148			.css('-ms-overflow-style', '-ms-autohiding-scrollbar')
149			.css('-webkit-overflow-scrolling', 'touch');
150
151		// Hide on click.
152		if (config.hideOnClick) {
153
154			$this.find('a')
155				.css('-webkit-tap-highlight-color', 'rgba(0,0,0,0)');
156
157			$this
158				.on('click', 'a', function(event) {
159
160					var $a = $(this),
161						href = $a.attr('href'),
162						target = $a.attr('target');
163
164					if (!href || href == '#' || href == '' || href == '#' + id)
165						return;
166
167					// Cancel original event.
168					event.preventDefault();
169					event.stopPropagation();
170
171					// Hide panel.
172					$this._hide();
173
174					// Redirect to href.
175					window.setTimeout(function() {
176
177						if (target == '_blank')
178							window.open(href);
179						else
180							window.location.href = href;
181
182					}, config.delay + 10);
183
184				});
185
186		}
187
188		// Event: Touch stuff.
189		$this.on('touchstart', function(event) {
190
191			$this.touchPosX = event.originalEvent.touches[0].pageX;
192			$this.touchPosY = event.originalEvent.touches[0].pageY;
193
194		})
195
196		$this.on('touchmove', function(event) {
197
198			if ($this.touchPosX === null
199				||	$this.touchPosY === null)
200				return;
201
202			var	diffX = $this.touchPosX - event.originalEvent.touches[0].pageX,
203				diffY = $this.touchPosY - event.originalEvent.touches[0].pageY,
204				th = $this.outerHeight(),
205				ts = ($this.get(0).scrollHeight - $this.scrollTop());
206
207			// Hide on swipe?
208			if (config.hideOnSwipe) {
209
210				var result = false,
211					boundary = 20,
212					delta = 50;
213
214				switch (config.side) {
215
216					case 'left':
217						result = (diffY < boundary && diffY >
217 (-1 * boundary)) && (diffX > delta);
218						break;
219
220					case 'right':
221						result = (diffY < boundary && diffY > (-1 * boundary)) && (diffX < (-1 * delta));
222						break;
223
224					case 'top':
225						result = (diffX < boundary && diffX > (-1 * boundary)) && (diffY > delta);
226						break;
227
228					case 'bottom':
229						result = (diffX < boundary && diffX > (-1 * boundary)) && (diffY < (-1 * delta));
230						break;
231
232					default:
233						break;
234
235				}
236
237				if (result) {
238
239					$this.touchPosX = null;
240					$this.touchPosY = null;
241					$this._hide();
242
243					return false;
244
245				}
246
247			}
248
249			// Prevent vertical scrolling past the top or bottom.
250			if (($this.scrollTop() < 0 && diffY < 0)
251				|| (ts > (th - 2) && ts < (th + 2) && diffY > 0)) {
252
253				event.preventDefault();
254				event.stopPropagation();
255
256			}
257
258		});
259
260		// Event: Prevent certain events inside the panel from bubbling.
261		$this.on('click touchend touchstart touchmove', function(event) {
262			event.stopPropagation();
263		});
264
265		// Event: Hide panel if a child anchor tag pointing to its ID is clicked.
266		$this.on('click', 'a[href="#' + id + '"]', function(event) {
267
268			event.preventDefault();
269			event.stopPropagation();
270
271			config.target.removeClass(config.visibleClass);
272			setTimeout(function () {
273				$('#' + id).attr('aria-hidden','true');
274			}, 500);
275		});
276
277		// Body.
278
279		// Event: Hide panel on body click/tap.
280		$body.on('click touchend', function(event) {
281			$this._hide(event);
282		});
283
284		// Event: Toggle.
285		var ariaToggleTimeout = null;
286		var classToggleTimeout = null;
287		$body.on('click', 'a[href="#' + id + '"]', function(event) {
288			// clear old timeouts to prevent erroneous behaviour with multiple timeouts
289			clearTimeout(ariaToggleTimeout);
290			clearTimeout(classToggleTimeout);
291
292			event.preventDefault();
293			event.stopPropagation();
294
295			if (!config.target.hasClass(config.visibleClass)) {
296				$('#' + id).attr('aria-hidden', 'false');
297			} else {
298				ariaToggleTimeout = setTimeout(function () {
299					$('#' + id).attr('aria-hidden', 'true');
300				}, 500);
301			}
302
303			classToggleTimeout = setTimeout(function () {
304				config.target.toggleClass(config.visibleClass);
305				config.firstTabElement.focus();
306			}, 0);
307		});
308
309		// Window.
310
311		// Event: Hide on ESC.
312		if (config.hideOnEscape) {
313			$body.on('keyup', function(event){
314				if (event.keyCode === 27 ) {
315					$this._hide(event);
316				}
317			});
318		}
319
320		// focus lock within panel
321		if (
322			config.focusLock
323			&& config.firstTabElement !== null
324			&& config.firstTabElement.length
325			&& config.lastTabElement !== null
326			&& config.lastTabElement.length
327		) {
328			// ensure required first and last tab elements are defined
329			config.firstTabElement.on('keydown', function (event) {
330				if (event.which === 9 && event.shiftKey) {
331					event.preventDefault();
332					config.lastTabElement.focus();
333				}
334			});
335
336			config.lastTabElement.on('keydown', function (event) {
337				if (event.which === 9 && !event.shiftKey) {
338					event.preventDefault();
339					config.firstTabElement.focus();
340				}
341			});
342		}
343
344		return $this;
345
346	};
347
348	/**
349	 * Apply "placeholder" attribute polyfill to one or more forms.
350	 * @return {jQuery} jQuery object.
351	 */
352	$.fn.placeholder = function() {
353
354		// Browser natively supports placeholders? Bail.
355		if (typeof (document.createElement('input')).placeholder != 'undefined')
356			return $(this);
357
358		// No elements?
359		if (this.length == 0)
360			return $this;
361
362		// Multiple elements?
363		if (this.length > 1) {
364
365			for (var i=0; i < this.length; i++)
366				$(this[i]).placeholder();
367
368			return $this;
369
370		}
371
372		// Vars.
373		var $this = $(this);
374
375		// Text, TextArea.
376		$this.find('input[type=text],textarea')
377			.each(function() {
378
379				var i = $(this);
380
381				if (i.val() == ''
382					||  i.val() == i.attr('placeholder'))
383					i
384						.addClass('polyfill-placeholder')
385						.val(i.attr('placeholder'));
386
387			})
388			.on('blur', function() {
389
390				var i = $(this);
391
392				if (i.attr('name').match(/-polyfill-field$/))
393					return;
394
395				if (i.val() == '')
396					i
397						.addClass('polyfill-placeholder')
398						.val(i.attr('placeholder'));
399
400			})
401			.on('focus', function() {
402
403				var i = $(this);
404
405				if (i.attr('name').match(/-polyfill-field$/))
406					return;
407
408				if (i.val() == i.attr('placeholder'))
409					i
410						.removeClass('polyfill-placeholder')
411						.val('');
412
413			});
414
415		// Password.
416		$this.find('input[type=password]')
417			.each(function() {
418
419				var i = $(this);
420				var x = $(
421					$('<div>')
422						.append(i.clone())
423						.remove()
424						.html()
425						.replace(/type="password"/i, 'type="text"')
426						.replace(/type=password/i, 'type=text')
427				);
428
429				if (i.attr('id') != '')
430					x.attr('id', i.attr('id') + '-polyfill-field');
431
432				if (i.attr('name') != '')
433					x.attr('name', i.attr('name') + '-polyfill-field');
434
435				x.addClass('polyfill-placeholder')
436					.val(x.attr('placeholder')).insertAfter(i);
437
438				if (i.val() == '')
439					i.hide();
440				else
441					x.hide();
442
443				i
444					.on('blur', function(event) {
445
446						event.preventDefault();
447
448						var x = i.parent().find('input[name=' + i.attr('name') + '-polyfill-field]');
449
450						if (i.val() == '') {
451
452							i.hide();
453							x.show();
454
455						}
456
457					});
458
459				x
460					.on('focus', function(event) {
461
462						event.preventDefault();
463
464						var i = x.parent().find('input[name=' + x.attr('name').replace('-polyfill-field', '') + ']');
465
466						x.hide();
467
468						i
469							.show()
470							.focus();
471
472					})
473					.on('keypress', function(event) {
474
475						event.preventDefault();
476						x.val('');
477
478					});
479
480			});
481
482		// Events.
483		$this
484			.on('submit', function() {
485
486				$this.find('input[type=text],input[type=password],textarea')
487					.each(function(event) {
488
489						var i = $(this);
490
491						if (i.attr('name').match(/-polyfill-field$/))
492							i.attr('name', '');
493
494						if (i.val() == i.attr('placeholder')) {
495
496							i.removeClass('polyfill-placeholder');
497							i.val('');
498
499						}
500
501					});
502
503			})
504			.on('reset', function(event) {
505
506				event.preventDefault();
507
508				$this.find('select')
509					.val($('option:first').val());
510
511				$this.find('input,textarea')
512					.each(function() {
513
514						var i = $(this),
515							x;
516
517						i.removeClass('polyfill-placeholder');
518
519						switch (this.type) {
520
521							case 'submit':
522							case 'reset':
523								break;
524
525							case 'password':
526								i.val(i.attr('defaultValue'));
527
528								x = i.parent().find('input[name=' + i.attr('name') + '-polyfill-field]');
529
530								if (i.val() == '') {
531									i.hide();
532									x.show();
533								}
534								else {
535									i.show();
536									x.hide();
537								}
538
539								break;
540
541							case 'checkbox':
542							case 'radio':
543								i.attr('checked', i.attr('defaultValue'));
544								break;
545
546							case 'text':
547							case 'textarea':
548								i.val(i.attr('defaultValue'));
549
550								if (i.val() == '') {
551									i.addClass('polyfill-placeholder');
552									i.val(i.attr('placeholder'));
553								}
554
555								break;
556
557							default:
558								i.val(i.attr('defaultValue'));
559								break;
560
561						}
562					});
563
564			});
565
566		return $this;
567
568	};
569
570	/**
571	 * Moves elements to/from the first positions of their respective parents.
572	 * @param {jQuery} $elements Elements (or selector) to move.
573	 * @param {bool} condition If true, moves elements to the top. Otherwise, moves elements back to their original locations.
574	 */
575	$.prioritize = function($elements, condition) {
576
577		var key = '__prioritize';
578
579		// Expand $elements if it's not already a jQuery object.
580		if (typeof $elements != 'jQuery')
581			$elements = $($elements);
582
583		// Step through elements.
584		$elements.each(function() {
585
586			var	$e = $(this), $p,
587				$parent = $e.parent();
588
589			// No parent? Bail.
590			if ($parent.length == 0)
591				return;
592
593			// Not moved? Move it.
594			if (!$e.data(key)) {
595
596				// Condition is false? Bail.
597				if (!condition)
598					return;
599
600				// Get placeholder (which will serve as our point of reference for when this element needs to move back).
601				$p = $e.prev();
602
603				// Couldn't find anything? Means this element's already at the top, so bail.
604				if ($p.length == 0)
605					return;
606
607				// Move element to top of parent.
608				$e.prependTo($parent);
609
610				// Mark element as moved.
611				$e.data(key, $p);
612
613			}
614
615			// Moved already?
616			else {
617
618				// Condition is true? Bail.
619				if (condition)
620					return;
621
622				$p = $e.data(key);
623
624				// Move element back to its original location (using our placeholder).
625				$e.insertAfter($p);
626
627				// Unmark element as moved.
628				$e.removeData(key);
629
630			}
631
632		});
633
634	};
635
636})(jQuery);

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.