PageSourceSearch

https://assets.manchester.ac.uk/shared/js/global58.js

js manchester.ac.uk collected 2026-09-24 06:05:28 UTC 62,035 bytes, 1,956 lines download raw bytes

1/*------------------------------------------------*/
2/* Reset the global cache for ajax loaded scripts */
3/*------------------------------------------------*/
4$.ajaxSetup({
5	cache: true
6});
7	
8/* Global DOM elements */
9var html;
10var body;
11var bodyWidth;
12var sideMenuShowHide = $(".primaryLeftNav a.showHideIcon");
13var hideMenuTimer;
14var didScroll = false;
15var scrollTimer = false;
16
17/* Global Vars */
18var accordionOffsetTop = 0;
19var showFullScreenContent = false;
20var carouselInitialised = false;
21var screenResizerTimer;
22var ua = navigator.userAgent;
23var firefox41Plus = false;
24var hostname = window.location.hostname;
25var breadcrumbTop;
26var $window;
27var shareToolbar;
28var shareTooltipDesktop;
29var jwPlayerLibraryUrl = "https://content.jwplatform.com/libraries/oCVSW4JO.js";
30var jqueryUiLibraryUrl = "https://assets.manchester.ac.uk/corporate/js/libs/jquery-ui-1.13.2.custom/js/jquery-ui.min.js";
31var tnsLibraryUrl = "https://assets.manchester.ac.uk/shared/js/tiny-slider-2.9.4-cust-03.js";
32
33if (ua.indexOf("Firefox") > 0) {
34	var firefoxVersion = parseFloat(ua.slice(ua.indexOf("Firefox") + 8));
35
36	if (firefoxVersion >= 41) {
37		firefox41Plus = true;
38	}
39}
40
41//---------------------------------
42//Equal Height for any class passed
43//---------------------------------
44$.fn.equalizeHeights = function() {
45	"use strict";
46
47	return this.height(Math.max.apply(this, $(this).map(function (i, e) {
48		return $(e).height();
49	}).get()));
50};
51//-------------------------------------
52//END Equal Height for any class passed
53//-------------------------------------
54
55//Get all ATTRs for DOM element
56$.fn.getAttributes = function() {
57	"use strict";
58
59	var attributes = {}; 
60
61	if (!this.length) {
62		return this;
63	}
64
65	$.each(this[0].attributes, function(index, attr) {
66		attributes[attr.name] = attr.value;
67	});
68
69	return attributes;
70};
71
72document.addEventListener("DOMContentLoaded", function() {
73	//var lazyloadImages = document.querySelectorAll("img.lazy2");
74	let lazyloadImages = document.querySelectorAll(".lazy2:not(.noLazy)[data-src]");
75
76	if ("IntersectionObserver" in window) {
77		let imageObserver = new IntersectionObserver(function(entries, observer) {
78			entries.forEach(function(entry) {
79				if (entry.isIntersecting) {
80					let lazyImage = entry.target;
81					if (lazyImage.dataset.src != "") lazyImage.src = lazyImage.dataset.src;
82					lazyImage.classList.remove("lazy2");
83					imageObserver.unobserve(lazyImage);
84					lazyImage.removeAttribute("data-src");
85				}
86			});
87		}, {rootMargin: "200px"});
88
89		lazyloadImages.forEach(function(lazyImage) {
90			imageObserver.observe(lazyImage);
91		});
92	} else {
93		// NodeList.forEach polyfill for IE11:
94		if (window.NodeList && !NodeList.prototype.forEach) NodeList.prototype.forEach = Array.prototype.forEach;
95
96		lazyloadImages.forEach(function(lazyImage) {
97			lazyImage.src = lazyImage.dataset.src;
98		});
99	}
100
101	const dropdownsWithCopy = document.querySelectorAll(".dropdown-menu-with-copy-form select");
102
103	dropdownsWithCopy.forEach(function(dropdown) {		
104		dropdown.addEventListener("change", function (event) {
105			dropdown.parentElement.parentElement.querySelectorAll(".dropdown-menu-with-copy-field").forEach(function(element) {
106				element.setAttribute("hidden", "");
107			});
108
109			let selectedElement = document.getElementById(event.target.value);
110
111			if (selectedElement) {
112				selectedElement.removeAttribute("hidden");
113			}
114
115			const contentBox = dropdown.parentElement.parentElement.querySelector(".dropdown-menu-with-copy-content");
116
117			if (event.target.value === "") {
118				contentBox.setAttribute("tabindex", "-1");
119			} else {
120				contentBox.setAttribute("tabindex", "0");
121			}
122		});
123	});
124});
125
126$(document).ready(function() {
127	"use strict";
128
129	/*----------------------------------------------------------------------------------------------*/
130	/*		CUSTOM SCRIPT																			*/
131	/*----------------------------------------------------------------------------------------------*/
132
133	html = $("html");
134	body = $("body");
135	$window = $(window);
136
137	$(".currentsection").parents("li").last().addClass("currentmainbranch");
138
139	/*-----------*/
140	/* MEGA MENU */
141	/*-----------*/
142	$(".megaMenu").hoverIntent({
143		over: showMegaMenu,
144		out: hideMegaMenu,
145		timeout: 400,
146		selector: "li.menuItem"
147	});
148	/*---------------*/
149	/* END MEGA MENU */
150	/*---------------*/
151
152	/*-------------------------------*/
153	/* 2 touch process for mega menu */
154	/*-------------------------------*/
155	$(".touch .menuItem a.megaLink, .touchevents .menuItem a.megaLink").on("touchstart", function(e) {
156		//e.stopPropogation;
157		var $this = $(this);
158		
159		if (!$this.parent().hasClass("active")) {
160			$(".touch .menuItem, .touchevents .menuItem").removeClass("active");
161			$(".megaContent").removeAttr("style");
162
163			$this.parent().addClass("active");
164			$this.next(".megaContent").addClass("active");
165			return false;
166		} else {
167			return true;
168		}
169	});
170
171	body.on("touchstart", function(e) {
172		//if parent of this click is NOT clickout AND this click is NOT clickout then hide stuff
173		if (!$(e.target).parents().is(".clickOut")) {
174			$(".touch .menuItem, .touchevents .menuItem").removeClass("active");
175			$(".megaContent").removeClass("active").removeAttr("style");
176		}
177	});
178	/*-----------------------------------*/
179	/* END 2 touch process for mega menu */
180	/*-----------------------------------*/
181
182	/*----------------------------------*/
183	/* Auto expand menu - Memory effect */
184	/*----------------------------------*/
185		
186	/* Initialise if menu shows persistent active state */
187	var memoryEffect = true;
188	
189	/* Show menu expanded to show active state */
190	if ($('.primaryLeftNav li.activeMenuLink').length && memoryEffect) {
191		$('li.activeMenuLink').parents('ul').each(function() {
192			var $this = $(this);
193			$this.show();
194			$this.parent().addClass('open');
195		});
196	}
197	/*--------------------------------------*/
198	/* END Auto expand menu - Memory effect */
199	/*--------------------------------------*/
200		
201	/*-------------------------------*/
202	/* Show / Hide Sub Nav (DESKTOP) */
203	/*-------------------------------*/
204
205	/* Initialise collapse menu */
206	var collapseSideAll = true;  //false = collapse this branch | true = collapse every open branch
207
208	/* Collapse menu functions */
209	$(sideMenuShowHide).on("click", function(e) {
210		var $this = $(this);
211		e.preventDefault();
212
213		if (!$this.parent().parent().hasClass('open')) {
214			/* OPEN STUFF */
215			if (collapseSideAll) {
216				var prevLI = $this.parent().parent().siblings('li.open');  //get the siblings branch that is open
217
218				$(prevLI).find('ul').slideUp(200).promise().done(function() {
219					//find ALL the UL's in the open branch and close them
220					$(prevLI).removeClass('open');
221
222					var branch = getSideBranch(prevLI);
223					closeSideBranch(branch);  //close all the child branches
224				});
225			}
226
227			$this.parent().parent().addClass('open');
227 //adds "open" to the LI
228			$this.parent().parent().find('ul').first().slideDown(200); 
229		} else {
230			/* CLOSE STUFF */
231			var branch = getSideBranch( $this.parent().parent() );
232
233			$this.parent().parent().removeClass('open'); 
234			$this.parent().parent().find('ul').first().slideUp(200, function() {
235				closeSideBranch(branch);
236			}); 
237		}
238	});
239	/*-----------------------------------*/
240	/* END Show / Hide Sub Nav (DESKTOP) */
241	/*-----------------------------------*/	
242
243	/*------------------*/
244	/* Tabs / Accordion */
245	/*------------------*/
246		
247	/* Tabs */
248	$(".tabTitles li a.tabLink").on("click", function(e) {
249		e.preventDefault();
250		var $this = $(this);
251
252		$(".tabTitles li").removeClass("activeTabLink");
253		$this.parent().addClass("activeTabLink");
254
255		var thisIndex = $this.parent().index();
256
257		$(".tabPanelContainer .tabPanel").addClass("jsHide");
258		$(".tabPanelContainer .tabPanel:eq("+thisIndex+")").removeClass("jsHide");
259
260		//***lazyload removal***
261		forceLazyLoad(thisIndex);
262	});
263
264	if (window.location.hash.substr(1,5) == "d.en.") {
265		var hash = window.location.hash.substr(1).replace("d.en.", "");
266
267		if ($("#d\\.en\\." + hash).hasClass("internalTitle")) {
268			$("#d\\.en\\." + hash + " a.accordionLink").addClass("activeAccordionLink");
269			$("#d\\.en\\." + hash).next().slideDown().addClass("activeAccordionPanel");
270
271			if ($("#d\\.en\\." + hash).next().find(".video, .audio").length > 0) {
272				//console.log("#d\\.en\\." + hash.replace("d.en.", "") + "-tabPanelWrapper");
273				if (window.jwplayer) {
274					initialiseVideos("#d\\.en\\." + hash.replace("d.en.", "") + "-tabPanelWrapper");
275					initialiseAudio("#d\\.en\\." + hash.replace("d.en.", "") + "-tabPanelWrapper");
276				} else {
277					$.getScript(jwPlayerLibraryUrl, function() {
278						initialiseVideos("#d\\.en\\." + hash.replace("d.en.", "") + "-tabPanelWrapper");
279						initialiseAudio("#d\\.en\\." + hash.replace("d.en.", "") + "-tabPanelWrapper");
280					});
281				}
282			}
283
284			//***lazyload removal***
285			var thisIndex = $("#d\\.en\\." + hash).parent().index();
286			forceLazyLoad(thisIndex);
287		}
288	}
289
290	/* Accordion */
291	$("h2.internalTitle a.accordionLink").on("click", function(e) {
292		e.preventDefault();
293		var $this = $(this);
294
295		/* If we're NOT clicking the already active title */
296		if (!$this.hasClass("activeAccordionLink")) {
297			$this.addClass("activeAccordionLink");
298			$this.parent().next().slideDown().addClass("activeAccordionPanel");
299
300			if ($this.parent().next().find(".video, .audio").length > 0) {
301				if (window.jwplayer) {
302					initialiseVideos("#d\\.en\\." + $this.parent().attr("id").replace("d.en.", "") + "-tabPanelWrapper");
303					initialiseAudio("#d\\.en\\." + $this.parent().attr("id").replace("d.en.", "") + "-tabPanelWrapper");
304				} else {
305					$.getScript(jwPlayerLibraryUrl, function() {
306						initialiseVideos("#d\\.en\\." + $this.parent().attr("id").replace("d.en.", "") + "-tabPanelWrapper");
307						initialiseAudio("#d\\.en\\." + $this.parent().attr("id").replace("d.en.", "") + "-tabPanelWrapper");
308					});
309				}
310			}
311
312			//***lazyload removal***
313			// Lazy load images in this accordion item.
314			$("#d\\.en\\." + $this.parent().attr("id").replace("d.en.", "")).parent().find("img").lazyload();
315
316			// Equalise the heights of colour promo boxes within rows.
317			if ($(".inner .clearfix .sixcol.colourPromo").length > 1) {
318				$(".inner .clearfix").has(".sixcol.colourPromo").each(function() {
319					equaliseHeightRows(".sixcol.colourPromo", this);
320				});
321			}
322
323			if ($(".inner .clearfix .fourcol.colourPromo").length > 1) {
324				$(".inner .clearfix").has(".fourcol.colourPromo").each(function() {
325					equaliseHeightRows(".fourcol.colourPromo", this);
326				});
327			}
328
329			if (bodyWidth >= 960) {
330				if ($("div.tabPanelWrapper.activeAccordionPanel ul.gridList.displayAsGrid").not(".noEqualise").length > 0) {
331					$("div.tabPanelWrapper.activeAccordionPanel ul.gridList.displayAsGrid").not(".noEqualise").each(function() {
332						equaliseHeightRows("div.tabPanelWrapper.activeAccordionPanel ul.gridList.displayAsGrid li", this);
333					});
334				}
335			} else {
336				$("ul.gridList.displayAsGrid li").removeAttr("style");
337			}
338		} else {
339			$this.removeClass("activeAccordionLink");
340			$this.parent().next().slideUp().removeClass("activeAccordionPanel");
341		}
342	});
343
344	/* Accordion new */
345	if (window.location.hash.substr(1,5) == "d.en.") {
346		var hashAccordion = window.location.hash.substr(1).replace("d.en.", "");
347
348		if ($("#d\\.en\\." + hashAccordion).hasClass("accordion-item")) {
349			$("#d\\.en\\." + hashAccordion).addClass("open");
350			$("#d\\.en\\." + hashAccordion).find("button").attr("aria-expanded", "true");
351			$("#d\\.en\\." + hashAccordion).find(".accordion-item-content").removeAttr("hidden");
352		}
353	}
354
355	$(".accordion-item h2 button, .accordion-item h3 button, .accordion-item h4 button, .accordion-item h5 button").on("click tap", function() {
356		var $this = $(this);
357		var targetAccordionItemId = $this.attr("aria-controls");
358
359		if ($("#" + targetAccordionItemId).attr("hidden") == "hidden") {
360			$this.parent().parent().addClass("open");
361			$this.attr("aria-expanded", "true");
362			//***lazyload removal***
363			$("#" + targetAccordionItemId).removeAttr("hidden").find("img").lazyload();
364		} else {
365			$this.parent().parent().removeClass("open");
366			$this.attr("aria-expanded", "false");
367			$("#" + targetAccordionItemId).attr("hidden", "hidden");
368		}
369	});
370	/*----------------------*/
371	/* END Tabs / Accordion */
372	/*----------------------*/
373	
374	/*----------------------------------------------------------------------------------------------*/
375	/*		FORM SCRIPT																				*/
376	/*----------------------------------------------------------------------------------------------*/
377
378	/*------------------*/
379	/* Fake Placeholder */
380	/*------------------*/
381	if (!Modernizr.placeholder) {
382		$("input:not('.noIEPlaceholder'), textarea").each(function() { // loop through all inputs & textareas
383			var $this = $(this);
384			var thisPlaceholderText = $this.attr("placeholder"); // get the placeholder text
385
386			if ($this.val() === "" && thisPlaceholderText !== "") {
387				// if the current "value" attribute is empty AND placeholder text isn't empty
388
389				$this.val(thisPlaceholderText); // set the "value" attibute to the placeholder text
390				$this.addClass("placeholderTextColor"); // set the text to the fake color (placeholder colour)
391
392				$this.on("focus", function() {
393					if ($this.val() === thisPlaceholderText) {
394						// if the "value" attribute is equal to the placeholder value, i.e. empty
395
396						$this.val(""); // clear the "value" attribute
397						$this.addClass("formFocusTextColor"); // change the colour to the proper input colour
398					}
399				});
400
401				$this.on("keyup", function() {
402					if ($this.val() !== "" && $this.val() !== thisPlaceholderText) {
403						$this.removeClass("placeholderTextColor");
404					}
405				});
406
407				$this.on("blur", function() {
408					if ($this.val() === "") {
409						// if the "value" attribute is empty, (no text was entered)
410
411						$this.removeClass("formFocusTextColor").addClass("placeholderTextColor"); // remove the colour to the proper input colour, reverts back to placeholder colour
412						$this.val(thisPlaceholderText); // set the "value" attibute to the placeholder text
413					}
414				});
415			}
416		});
417	}
418	/*----------------------*/
419	/* END Fake Placeholder */
420	/*----------------------*/
421
422	/*-------------------*/
423	/* Postcode validate */
424	/*-------------------*/
425	$(".postcodeCheck").on("blur", function() {
426		var $this = $(this);
427
428		if ($this.val() !== "") {
429			var myregex = /^(([gG][iI][rR] {0,}0[aA]{2})|((([a-pr-uwyzA-PR-UWYZ][a-hk-yA-HK-Y]?[0-9][0-9]?)|(([a-pr-uwyzA-PR-UWYZ][0-9][a-hjkstuwA-HJKSTUW])|([a-pr-uwyzA-PR-UWYZ][a-hk-yA-HK-Y][0-9][abehmnprv-yABEHMNPRV-Y]))) {0,}[0-9][abd-hjlnp-uw-zABD-HJLNP-UW-Z]{2}))$/;
430
431			var matches = myregex.exec($this.val());
432
433			if (matches) {
434				$(".crossIcon").remove();
435				$this.after("<div class=\"tickIcon\" />");
436				$this.addClass("doubleIconPadding");
437			} else {
438				$(".tickIcon").remove();
439				$this.after("<div class=\"crossIcon\" />");
440				$this.addClass("doubleIconPadding");
441			}
442		} else {
443			$(".crossIcon").remove();
444			$(".tickIcon").remove();
445			$this.removeClass("doubleIconPadding");
446		}
447	});
448	/*-----------------------*/
449	/* END Postcode validate */
450	/*-----------------------*/
451
452	/*-----------------------*/
453	/* Email format validate */
454	/*-----------------------*/
455	$(".emailCheck").on("blur", function() {
456		var $this = $(this);
457
458		if ($this.val() !== "") {
459			var myregex = /^([0-9a-zA-Z]([-\.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$/;
460
461			var matches = myregex.exec($this.val());
462
463			if (matches) {
464				$(".crossIcon").remove();
465				$this.after("<div class=\"tickIcon\" />");
466				$this.addClass("doubleIconPadding");
467			} else {
468				$(".tickIcon").remove();
469				$this.after("<div class=\"crossIcon\" />");
470				$this.addClass("doubleIconPadding");
471			}
472		} else {
473			$(".crossIcon").remove();
474			$(".tickIcon").remove();
475			$this.removeClass("doubleIconPadding");
476		}
477	});
478	/*---------------------------*/
479	/* END Email format validate */
480	/*---------------------------*/
481
482	/*--------------*/
483	/* Numbers only */
484	/*--------------*/
485	var inputObject = $(".numberCheck");
486
487	// Restrict the INPUT to numbers only
488	$(inputObject).on("keydown", function(event) {
489		checkKeycode(event);
490	});
491
492	/*------------------*/
493	/* END Numbers only */
494	/*------------------*/
495
496	/*----------------------------------------*/
497	/* Dropdown: show/hide hidden form fields */
498	/*----------------------------------------*/
499	$(".titleSelect").on("change", function() {
500		var $this = $(this);
501		var changeTo = $this.val();
502		var extraRow = $this.parent().find(".extraRow");
503
504		if (changeTo === "other" || changeTo === "Other") {
505			extraRow.slideDown().removeClass("hidden");
506		} else {
507			extraRow.slideUp().addClass("hidden");
508		}
509	});
510	/*--------------------------------------------*/
511	/* END Dropdown: show/hide hidden form fields */
512	/*--------------------------------------------*/
513		
514		
515	/*------------------------------------------------------------------------------*/
516	/* Character Counter - http://hycus.com/2011/03/25/simple-jquery-character-count
517	/*------------------------------------------------------------------------------*/
518	$(".commentsCount").on("keyup", function() {
519		var $this = $(this);
520		var maxchar = 500;
521		var count = $this.val().length;
522		var remainingchar = maxchar - count;
523
524		if (remainingchar < 0) {
525			$("#counterNumber").html("0");
526			$this.val($this.val().slice(0, 500));
527		} else {
528			$("#counterNumber").html(remainingchar);
529		}
530	});
531	/*----------------------------------------------------------------------------------*/
532	/* END Character Counter - http://hycus.com/2011/03/25/simple-jquery-character-count
533	/*----------------------------------------------------------------------------------*/
534
535	/*----------------*/
536	/* Add Clear Icon */
537	/*----------------*/
538	$(".gt-ie7 input:text, .gt-ie7 input[type='email'], .gt-ie7 input:password, .gt-ie7 input.numberCheck, .gt-ie7 input.emailCheck, .gt-ie7 textarea").not(".noClear").each(function() {
539		var $this = $(this);
540		$this.wrap("<div class=\"inputWrapper\" />");
541		$this.after("<div class=\"clearIcon\" />");
542		$this.addClass("clearIconPadding");
543	});
544	/*--------------------*/
545	/* END Add Clear Icon */
546	/*--------------------*/
547
548	/*-------------------*/
549	/* Add Password Icon */
550	/*-------------------*/
551	$(".gt-ie7 input:password").not(".noClear").each(function() {
552		var $this = $(this);
553		$this.after("<div class=\"passwordIcon\" />");
554		$this.addClass("doubleIconPadding");
555	});
556	/*-----------------------*/
557	/* END Add Password Icon */
558	/*-----------------------*/
559
560	/*------------------------*/
561	/* Show / Hide clear icon */
562	/*------------------------*/
563	// var clearInputTimer; - if no errors, delete this
564
565	/* FOCUS */
566	inputFocus();
567
568	/* BLUR */
569	inputBlur();
570	/*----------------------------*/
571	/* END Show / Hide clear icon */
572	/*----------------------------*/
573
574	/*--------------------*/
575	/* Password show/hide */
576	/*--------------------*/
577
578	//Create a new HTML string for our password input box
579
580	$(".passwordIcon").on("click", function() {
581		//DOM Element - password input
582		var $this = $(this);
583		var passwordInputBox = $this.prev("input");
584
585		// Get password input attributes
586		var attrList = $(passwordInputBox).getAttributes();
587
588		// Upate the VALUE ATTR with what has been typed
589		$(attrList).attr("value", $(passwordInputBox).val());
590
591		var passwordInputClone;
592
593		if (!$this.hasClass("passwordIcon-show")) {
594			// is checkbox checked
595			$this.addClass("passwordIcon-show");
596			$(attrList).attr("type", "text"); // Change TYPE to TEXT
597			passwordInputClone = createNewPasswordInput(attrList); // Create new INPUT box with changed type & value attributes
598		} else {
599			$this.removeClass("passwordIcon-show");
600			$(attrList).attr("type", "password"); // Change TYPE to TEXT
601			passwordInputClone = createNewPasswordInput(attrList); // Create new INPUT box with changed type & value attributes
602		}
603
604		$(passwordInputBox).replaceWith(passwordInputClone);
605
606		inputFocus();
607		inputBlur();
608		listenForEscKey();
609	});
610	/*------------------------*/
611	/* END Password show/hide */
612	/*------------------------*/
613
614	/*-------------------------------------------*/
615	/* Clear the input field with the clear icon */
616	/*-------------------------------------------*/
617	$(".clearIcon").on("click", function() {
618		var $this = $(this);
619
620		$this.siblings("input").val("");
621		$this.siblings("textarea").val("");
622		$("#counterNumber").text("500");
623		$this.hide();
624		$this.siblings(".passwordIcon").removeClass("movePasswordIcon");
625		$(".crossIcon").remove();
626		$(".tickIcon").remove();
627		$this.removeClass("doubleIconPadding");
628	});
629	/*-----------------------------------------------*/
630	/* END Clear the input field with the clear icon */
631	/*-----------------------------------------------*/
632
633	/*----------------------------------------*/
634	/* Clear the input field with the Esc key */
635	/*----------------------------------------*/
636	listenForEscKey();
637	/*--------------------------------------------*/
638	/* END Clear the input field with the Esc key */
639	/*--------------------------------------------*/
640
641	/*----------------------------------------------------------------------------------------------*/
642	/*		PLUGIN SCRIPT																			*/
643	/*----------------------------------------------------------------------------------------------*/
644	$(".studyEq").on("load", function() {
645		bodyWidths();
646	});
647
648	/*------------------*/
649	/* Lazy load images */
650	/*------------------*/
651
652	//***lazyload removal***
653	$("img.lazy:not('img.noLazy')").show().lazyload({
654		effect: "fadeIn"
655//			failure_limit: 2,
656
657//			threshold: 400
658
659//			skip_invisible: false
660	});
661
662	$("img.lazynews").lazyload({
663		effect: "fadeIn",
664		threshold: 50,
665		failure_limit: 5
666//			skip_invisible: false,
667//			container: $("#carousel")
668	});
669
670	/*----------------------*/
671	/* END Lazy load images */
672	/*----------------------*/
673
674	$(".embed-container iframe, .videowrapper iframe").removeAttr("width height frameborder");
675	$(".embed-container-wakelet iframe").removeAttr("width height scrolling");
676	$(".embed-container.videoportal iframe").attr("allowfullscreen", "");
677
678	$("p iframe[src*='youtube.com'], p iframe[src*='youtube-nocookie.com']").each(function() {
679		$(this).unwrap().wrap('<div class="videobox2 mobile fullwidth"></div>');
680	});
681
682	//***lazyload removal***
683	$(".poster").lazyload();
684
685	if ($("div.video, div.audio").not("#course-profile .video, body.umw .video").length > 0) {
686		if (window.jwplayer) {
687			initialiseVideos();
688			initialiseAudio();
689		} else {
690			$.getScript(jwPlayerLibraryUrl, function() {
691				initialiseVideos();
692				initialiseAudio();
693			});
694		}
695	}
696	
697	if ($(".image-gallery").length > 0) {
698		$.getScript(tnsLibraryUrl, function() {
699			initialiseImageGalleries();
700		});
701	}
702
703	if ($(".autocomplete").length > 0) {
704		$.getScript(jqueryUiLibraryUrl, function() {
705			$(".gsa-autocomplete").each(function() {
706				var $this = $(this);
707				var site = $this.find(".site").val();
708				var client = $this.find(".client").val();
709
710				$(".q", this).autocomplete({
711					source: function(request, response) {
712						$.ajax({
713							url: "/search/suggest/?q=" + request.term + "&site=" + site + "&client=" + client,
714							dataType: "json",
715							success: function(data) {
716								response($.map(data.results, function(item) {
717									return {
718										label: item.name,
719										value: item.name
720									};
721								}));
722							}
723						});
724					},
725					minLength: 2,
726					select: function(event, ui) {
727						$(this).val(ui.item.value);
728						$(this).closest("form").submit();
729					},
730					open: function() {
731						$(this).addClass("ui-autocomplete-loading");
732					},
733					close: function() {
734						$(this).removeClass("ui-autocomplete-loading");
735					}
736				});
737			});
738
739			$(".coursefinder-autocomplete").each(function() {
740				var studyLevel = $(this).attr("data-studylevel");
741				var xmlLocation;
742				var courseBaseUrl;
743
744				switch (studyLevel) {
745					case "UG":
746						var year = $(this).attr("data-year");
747						courseBaseUrl = "/study/undergraduate/courses/" + year + "/";
748						xmlLocation = "/study/undergraduate/courses/" + year + "/finder-xml/";
749						break;
750					case "PGT":
751						courseBaseUrl = "/study/masters/courses/list/";
752						xmlLocation = "/study/masters/courses/list/finder-xml/";
753						break;
754					case "PGR":
755						courseBaseUrl = "/study/postgraduate-research/programmes/list/";
756						xmlLocation = "/study/postgraduate-research/programmes/list/finder-xml/";
757						break;
758					case "DL":
759						courseBaseUrl = "/study/online-blended-learning/what-you-can-study/courses/";
760						xmlLocation = "/study/online-blended-learning/what-you-can-study/courses/finder-xml/";
761						break;
762				}
763
764				if (studyLevel === "all" || studyLevel === "UG-multi") {
765					$("#studyLevel").on("change", function() {
766						var studyLevel = $(this).val();
767						var studyLevelAttr = $(this).find(":selected").attr("data-studylevel");
768						if (studyLevelAttr !== "" && studyLevelAttr !== undefined) studyLevel = studyLevelAttr;
769
770						switch (studyLevel) {
771							case "UG":
772								var year = $(this).find(":selected").attr("data-year");
773								courseBaseUrl = "/study/undergraduate/courses/" + year + "/";
774								xmlLocation = "/study/undergraduate/courses/" + year + "/finder-xml/";
775								break;
776							case "PGT":
777								courseBaseUrl = "/study/masters/courses/list/";
778								xmlLocation = "/study/masters/courses/list/finder-xml/";
779								break;
780							case "PGR":
781								courseBaseUrl = "/study/postgraduate-research/programmes/list/";
782								xmlLocation = "/study/postgraduate-research/programmes/list/finder-xml/";
783								break;
784							case "DL":
785								courseBaseUrl = "/study/online-blended-learning/what-you-can-study/courses/";
786								xmlLocation = "/study/online-blended-learning/what-you-can-study/courses/finder-xml/";
787								break;
788						}
789
790						$.ajax({
791							url: xmlLocation,
792							dataType: "xml",
793							success: function(xmlResponse) {
794								var data = $("c", xmlResponse).map(function() {
795									return {
796										name: $("n", this).text(),
797										keywords: $("k", this).text(),
798										value: $("n", this).text(),
799										code: $("i", this).text()
800									};
801								}).get();
802
803								$("#courseKeywords").autocomplete({
804//										source: data,
805									source: function(request, response) {
806										function hasMatch(s) {
807											return s.toLowerCase().indexOf(request.term.toLowerCase()) !== -1;
808										}
809
810										var i, l, obj, matches = [];
811
812										if (request.term === "") {
813											response([]);
814											return;
815										}
816
817										for (i = 0, l = data.length; i < l; i++) {
818											obj = data[i];
819											if (hasMatch(obj.name) || hasMatch(obj.keywords)) {
820												matches.push(obj);
821											}
822										}
823
824										response(matches);
825									},
826//										appendTo: "#courseFinder .inputWrapper",
827									position: { my: "left top", at: "left bottom" },
828									minLength: 2,
829									delay: 0,
830									select: function(event, ui) {
831										$(this).blur();
832										var courseUrl = courseBaseUrl + ui.item.code + "/";
833										window.location = courseUrl;
834									}
835								});
836							}
837						});
838					}).trigger("change");
839				} else {
840					$.ajax({
841						url: xmlLocation,
842						dataType: "xml",
843						success: function(xmlResponse) {
844							var data = $("c", xmlResponse).map(function() {
845								return {
846									name: $("n", this).text(),
847									keywords: $("k", this).text(),
848									value: $("n", this).text(),
849									code: $("i", this).text()
850								};
851							}).get();
852
853							$("#courseKeywords").autocomplete({
854//									source: data,
855								source: function(request, response) {
856									function hasMatch(s) {
857										return s.toLowerCase().indexOf(request.term.toLowerCase()) !== -1;
858									}
859
860									var i, l, obj, matches = [];
861
862									if (request.term === "") {
863										response([]);
864										return;
865									}
866
867									for (i = 0, l = data.length; i < l; i++) {
868										obj = data[i];
869										if (hasMatch(obj.name) || hasMatch(obj.keywords)) {
870											matches.push(obj);
871										}
872									}
873
874									response(matches);
875								},
876//									appendTo: "#courseFinder .inputWrapper",
877								position: { my: "left top", at: "left bottom" },
878								minLength: 2,
879								delay: 0,
880								select: function(event, ui) {
881									$(this).blur();
882									var courseUrl = courseBaseUrl + ui.item.code + "/";
883									window.location = courseUrl;
884								}
885							});
886						}
887					});
888				}
889			});
890		});
891	}
892
893	$("#courseFinderForm").on("submit", function(e) {
894		var courseKeywords = $("#courseKeywords").val();
895
896		if (courseKeywords === "" || courseKeywords === "Enter keywords") {
897			$("#courseSearchErrorMessage").text("Please enter course keywords.");
898			$("#courseKeywords").trigger("focus");
899			return false;
900		} else {
901			var studyLevel = $("#studyLevel").val();
902			var coursePageUrl;
903
904			switch(studyLevel) {
905				case "PGT":
906					coursePageUrl = "/study/masters/courses/list/";
907					break;
908				case "PGR":
909					coursePageUrl = "/study/postgraduate-research/programmes/list/";
910					break;
911				case "DL":
912					coursePageUrl = "/study/online-blended-learning/what-you-can-study/courses/";
913					break;
914				default:
915					var year = $("#studyLevel option:selected").attr("data-year");
916					coursePageUrl = "/study/undergraduate/courses/" + year + "/";
917			}
918
919			coursePageUrl += "?k=" + courseKeywords;
920
921			document.location = coursePageUrl;
922		}
923	});
924
925	if (hostname === "www.manchester.ac.uk" || hostname === "manchester.ac.uk") {
926		$("#webFilter").prop("checked", true);
927	}
928
929	$("input[name='searchFilter']").on("change", function() {
930		var searchFilterValue = $(this).val();
931
932		if (hostname === "www.staffnet.manchester.ac.uk" || hostname === "staffnet.cmsstage.manchester.ac.uk") {
933			if (searchFilterValue === "microsite") {
934				$("#searchBox").attr("placeholder", "Search this site").autocomplete({ disabled: true });
935			} else if (searchFilterValue === "staff") {
936				$("#searchBox").attr("placeholder", "Search for staff").autocomplete({ disabled: true });
937			} else {
938				$("#searchBox").attr("placeholder", "Search StaffNet").autocomplete({ disabled: true });
939			}
940		} else {
941			if (searchFilterValue === "staff") {
942				$("#searchBox").attr("placeholder", "Search for staff").autocomplete({ disabled: true });
943			} else {
944				$("#searchBox").attr("placeholder", "Search the website").autocomplete({ disabled: false });
945			}
946		}
947	});
948
949	$("a.showAllUnits").on("click", function() {
950//		var thisId = $(this).prop("id");
951//		var headingId = thisId.replace("show", "");
952		var $this = $(this);
953
954		$this.parent().parent().parent().parent().find("tr.hidedefault").show();
955		$this.parent().parent().parent().hide();
956
957		return false;
958	});
959
960	$("a.t4Edit-page, a.newWindow").attr("target", "_blank");
961
962	$(".pageSelector").hide();
963
964	if (typeof(Storage) !== "undefined" && html.hasClass("localstorage")) {
965		if (!localStorage.showShareTooltip) {
966			localStorage.showShareTooltip = false;
967			$("<div class=\"share-tooltip desktop\"><div class=\"arrow\"></div><p><img src=\"https://assets.manchester.ac.uk/corporate/images/design/icon-close.svg\" alt=\"Close this message\" class=\"close\" />Share this page</p></div>").hide().appendTo("#breadcrumb-share-container").fadeIn("slow");
968		}
969	}
970
971	if ((body.hasClass("inner") || body.hasClass("landing")) && $(".share-toolbar").length > 0) {
972		var documentClick;
973		var shareToolbarButtonClickEnabled = true;
974		var shareToolbarUl = $(".share-toolbar ul");
975		shareTooltipDesktop = $(".share-tooltip.desktop");
976		breadcrumbTop = $(".breadcrumbNav").offset().top;
977		shareToolbar = $(".share-toolbar");
978
979		if ($("html").hasClass("touchevents")) {
980			$(".share-toolbar ul, .share-toolbar ul li").on("touchend", function(e) {
981				e.stopPropagation();
982			});
983
984			$(".share-toolbar div").on("click touchend", function(e) {
985				e.stopPropagation();
986				e.preventDefault();
987
988				if (e.type === "touchend") {
989					if (shareToolbarUl.hasClass("open")) {
990						shareToolbarUl.slideUp().removeClass("open");
991					} else {
992						shareToolbarUl.slideDown().addClass("open");
993					}
994				} else if (shareToolbarButtonClickEnabled === true && e.type === "click") {
995					if (shareToolbarUl.hasClass("open")) {
996					} else {
997						shareToolbarUl.slideDown().addClass("open");
998					}
999				}
1000
1001				if (shareTooltipDesktop.length) {
1002					shareTooltipDesktop.remove();
1003				}
1004
1005				return false;
1006			});
1007
1008			$(document).on("touchstart", function() {
1009				documentClick = true;
1010			});
1011
1012			$(document).on("touchmove", function() {
1013				documentClick = false;
1014			});
1015
1016			$(document).on("touchend", function() {
1017				if (documentClick && shareToolbarUl.hasClass("open")) {
1018					shareToolbarUl.slideUp().removeClass("open");
1019					shareToolbarButtonClickEnabled = false;
1020					setTimeout(function() {
1021						shareToolbarButtonClickEnabled = true;
1022					}, 500);
1023				}
1024			});
1025		} else {
1026			$(".share-toolbar ul, .share-toolbar ul li").on("click", function(e) {
1027				e.stopPropagation();
1028			});
1029
1030			//$(".share-toolbar div a").on("click", function(e) {
1031			$(".share-toolbar button").on("click", function(e) {
1032				let $this = $(this);
1033				e.stopPropagation();
1034				e.preventDefault();
1035
1036				if (shareToolbarUl.hasClass("open")) {
1037					$this.attr("aria-expanded", "false");
1038					shareToolbarUl.slideUp().removeClass("open");
1039				} else {
1040					$this.attr("aria-expanded", "true");
1041					shareToolbarUl.slideDown().addClass("open");
1042				}
1043
1044				if (shareTooltipDesktop.length) {
1045					shareTooltipDesktop.remove();
1046				}
1047			});
1048
1049			$(document).on("click", function() {
1050				if (shareToolbarUl.hasClass("open")) {
1051					shareToolbarUl.slideUp().removeClass("open");
1052					$(".share-toolbar button").attr("aria-expanded", "false");
1053				}
1054			});
1055		}
1056	}
1057
1058	$(".share-toolbar ul a, #sharing-menu-container .sectionMenu a").on("click", function(e) {
1059		e.stopPropagation();
1060
1061		var width = 575,
1062			height = 400,
1063			left = ($(window).width() - width) / 2,
1064			top = ($(window).height() - height) / 2,
1065			url = this.href,
1066			opts = "status=1" + ",width=" + width + ",height=" + height + ",top=" + top + ",left=" + left;
1067
1068		window.open(url, "share", opts);
1069		return false;
1070	});
1071
1072	$(".share-tooltip").on("click", function() {
1073		$(this).remove();
1074	});
1075
1076	if ($(".breadcrumbNav").css("display") === "none") {
1077		breadcrumbTop = 0;
1078	}
1079
1080	if ((body.hasClass("inner") || body.hasClass("landing")) && $(".share-toolbar").length && !$(".share-toolbar").hasClass("no-slideout")) {
1081		$window.on("scroll", function() {
1082			if (!didScroll) {
1083				scrollTimer = setInterval(function() {
1084					if (didScroll) {
1085						didScroll = false;
1086						clearTimeout(scrollTimer);
1087
1088						if (breadcrumbTop > 0 && $window.scrollTop() > breadcrumbTop) {
1089							shareToolbar.addClass("slideout");
1090//							$shareTooltip.hide();
1091							shareTooltipDesktop.addClass("slideout");
1092						} else {
1093							shareToolbar.removeClass("slideout");
1094//							$shareTooltip.show();
1095							shareTooltipDesktop.removeClass("slideout");
1096						}
1097					}
1098				}, 200);
1099			}
1100
1101			didScroll = true;
1102		});
1103	}
1104
1105	$(".icon-tabs").each(function() {
1106		var tabsScroll;
1107		var totalWidth = 0;
1108		var tabsContainerWidth = 0;
1109
1110		$("li", this).each(function(index) {
1111			tabsContainerWidth += parseInt($(this).width(), 10) + 20;
1112		});
1113
1114		$(".tabs-container", this).css("width", tabsContainerWidth  + "px");
1115
1116		var tabsContainerWrapper = $(".tabs-container-wrapper", this);
1117		var tabsContainerWrapperId = $(tabsContainerWrapper).attr("id");
1118
1119		tabsScroll = new IScroll("#" + tabsContainerWrapperId, {
1120		//tabsScroll = new IScroll(".tabs-container-wrapper", {
1121			scrollX: true,
1122			scrollY: false,
1123			eventPassthrough: true,
1124			disablePointer: true, // important to disable the pointer events that causes the issues
1125			disableTouch: false, // false if you want the slider to be usable with touch devices
1126			disableMouse: false, // false if you want the slider to be usable with a mouse (desktop)
1127			//probeType: 1,
1128			tap: true,
1129			click: true
1130		});
1131
1132		if (tabsContainerWrapper.width() < tabsContainerWidth) {
1133			tabsContainerWrapper.addClass("scroll-right");
1134		}
1135
1136		//tabsScroll.on("scroll", function() {
1137		tabsScroll.on("scrollEnd", function() {
1138			if (this.x < 0) {
1139				tabsContainerWrapper.addClass("scroll-left");
1140			} else {
1141				tabsContainerWrapper.removeClass("scroll-left");
1142			}
1143
1144			if (tabsContainerWrapper.width() - this.x < tabsContainerWidth) {
1145				tabsContainerWrapper.addClass("scroll-right");
1146			} else {
1147				tabsContainerWrapper.removeClass("scroll-right");
1148			}
1149		});
1150
1151		$(".tabs-container .tab", this).on("click", function(event) {
1152			var $this = $(this);
1153			var thisId = $this.prop("id");
1154			event.preventDefault();
1155			$this.parent().parent().find("li").removeClass("current");
1156			$this.parent().addClass("current");
1157			$this.closest(".icon-tabs").find(".tab-content.current").removeClass("current");
1158			$this.closest(".icon-tabs").find("#" + thisId.replace("-link", "")).addClass("current");
1159			tabsScroll.scrollToElement(this, 500, true, 0);
1160		});
1161	});
1162
1163	$(".on-this-page a").on("click tap", function(e) {
1164		e.preventDefault();
1165		var $this = $(this);
1166		var destinationIdHashIndex = $this.attr("href").indexOf("#");
1167		var destinationId = $this.attr("href").substr(destinationIdHashIndex + 1).replace("d.en.", "");
1168		const targetElement = document.getElementById("d.en." + destinationId);
1169
1170		targetElement.focus({ preventScroll: true });
1171
1172		$("html, body").animate({
1173			scrollTop: $("#d\\.en\\." + destinationId).offset().top - 10
1174		}, 500, function() {});
1175	});
1176});
1177
1178window.addEventListener("load", () => {
1179	const dropdowns = document.querySelectorAll('.dropdown-menu-with-copy-form select');
1180	dropdowns.forEach(function(dropdown) {
1181		dropdown.dispatchEvent(new Event("change"));
1182	});
1183});
1184
1185$(window).on("load", function() {
1186	"use strict";
1187
1188	$("html, body").trigger("scroll");
1189
1190//	var timeout = setTimeout(function() { $("img.lazy2").trigger("sporty") }, 5000);
1191
1192	bodyWidths();
1193	$('.eqHeights').equalizeHeights();
1194	
1195	accordionOffset();
1196	/*---------------------------------------*/
1197	/* END Get the offset of accordion panel */
1198	/*---------------------------------------*/
1199
1200	/*----------------------------------------*/
1201	/* Equalise Tab Widths - Table based tabs */
1202	/*----------------------------------------*/
1203	carouselTabWidth();
1204	/*--------------------------------------------*/
1205	/* END Equalise Tab Widths - Table based tabs */
1206	/*--------------------------------------------*/
1207
1208	/*-------------------*/
1209	/* Masthead Carousel */
1210	/*-------------------*/
1211	if ($(".masthead-container").length) {
1212		// Call the masthead height script on load
1213		setCarouselHeight();
1214	}
1215
1216	$(".masthead-pagination").on("click", "a", function(e) {
1217		e.preventDefault();
1218		var $this = $(this);
1219
1220		$this.parent().addClass("active").siblings().removeClass("active");
1221
1222		var whichLink = $this.parent().index();
1223		var panelToShow = $(".masthead-slide:eq(" + whichLink + ")");
1224
1225		if (!$(panelToShow).hasClass("active")) {
1226			$(panelToShow).hide().addClass("working").fadeIn(function() {
1227				$(".masthead-slide").removeClass("active");
1228				$(this).addClass("active").removeClass("working");
1229			});
1230		}
1231	});
1232	/*-----------------------*/
1233	/* END Masthead Carousel */
1234	/*-----------------------*/
1235
1236	/*-----------------*/
1237	/* Window Resizing */
1238	/*-----------------*/
1239	$(window).on("resize", function() {
1240		clearTimeout(screenResizerTimer);
1241
1242		screenResizerTimer = setTimeout(function() {
1243			bodyWidths();
1244
1245			accordionOffset();
1246
1247			if ((body.hasClass("inner") || body.hasClass("landing")) && $(".share-toolbar").length > 0 && (hostname === "www.manchester.ac.uk" || hostname === "manchester.ac.uk" || hostname === "mhn.cmsstage.manchester.ac.uk")) {
1248				breadcrumbTop = $(".breadcrumbNav").offset().top;
1249
1250				if ($(".breadcrumbNav").css("display") === "none") {
1251					breadcrumbTop = 0;
1252				}
1253
1254				if (breadcrumbTop > 0 && $window.scrollTop() > breadcrumbTop) {
1255					shareToolbar.addClass("slideout");
1256//					$shareTooltip.hide();
1257					shareTooltipDesktop.addClass("slideout");
1258				} else {
1259					shareToolbar.removeClass("slideout");
1260//					$shareTooltip.show();
1261					shareTooltipDesktop.removeClass("slideout");
1262				}
1263			}
1264
1265			// setCourseFinderHeight()
1266
1267			if ($(".masthead-container").length) {
1268				setCarouselHeight();
1269			}
1270		}, 500);
1271	});
1272
1273	if ($("ul.gridList.displayAsGrid").length > 0) {
1274		$(window).on("scroll", function() {
1275			clearTimeout(screenResizerTimer);
1276
1277			screenResizerTimer = setTimeout(function() {
1278				bodyWidths();
1279			}, 200);
1280		});
1281	}
1282	/*---------------------*/
1283	/* END Window Resizing */
1284	/*---------------------*/
1285});
1286
1287/*------------*/
1288/* Body width */
1289/*------------*/
1290function bodyWidths() {
1291	"use strict";
1292
1293	bodyWidth = viewportSize.getWidth();  //get the true viewport width
1294
1295	/*--------------*/
1296	/*		600		*/
1297	/*--------------*/
1298	if (bodyWidth >= 600) {
1299		if ($(".carousel").length && !carouselInitialised) {
1300			initCarousel();
1301		}
1302	} else {
1303		if ($(".carousel").length) {
1304			$(".carousel").trigger("destroy"); // Destroy the carousel.
1305			$(".carousel").removeAttr("style"); // Remove attributes that destroy method doesn't.
1306			$(".slide").removeAttr("style");
1307			carouselInitialised = false;
1308		}
1309	}
1310
1311	/*--------------*/
1312	/*		960		*/
1313	/*--------------*/
1314	if (bodyWidth >= 960) {
1315		if ($(".home .row.study .colourPromo").length > 1) {
1316			equaliseHeightRows(".colourPromo", $(".row.study"));
1317		}
1318
1319		if ($("ul.gridList.displayAsGrid").not(".noEqualise").length > 0) {
1320			$("ul.gridList.displayAsGrid").not(".noEqualise").each(function() {
1321				equaliseHeightRows("ul.gridList.displayAsGrid li", this);
1322			});
1323		}
1324	} else {
1325		if ($(".home .row.study .colourPromo").length > 1) {
1326			$(".row.study .colourPromo").removeAttr("style");
1327		}
1328
1329		$("ul.gridList.displayAsGrid li").removeAttr("style");
1330	}
1331
1332	// Equalise the heights of colour promo boxes within rows.
1333	if ($(".inner .clearfix .sixcol.colourPromo").length > 1) {
1334		$(".inner .clearfix").has(".sixcol.colourPromo").each(function() {
1335			equaliseHeightRows(".sixcol.colourPromo", this);
1336		});
1337	}
1338
1339	if ($(".inner .clearfix .fourcol.colourPromo").length > 1) {
1340		$(".inner .clearfix").has(".fourcol.colourPromo").each(function() {
1341			equaliseHeightRows(".fourcol.colourPromo", this);
1342		});
1343	}
1344}
1345/*----------------*/
1346/* END Body width */
1347/*----------------*/
1348
1349/*--------------------------*/
1350/* Equalise heights in rows */
1351
1352/*--------------------------*/
1353function equaliseHeightRows(objsToEqualise) {
1354	"use strict";
1355
1356	$(objsToEqualise).height('auto');
1357	var currentTallest = 0,
1358		currentRowStart = 0,
1359		rowDivs = [],
1360		$el,
1361		topPosition = 0,
1362		currentDiv;
1363
1364	$(objsToEqualise).each(function() {
1365		$el = $(this);
1366		topPosition = $el.position().top;
1367
1368		if (currentRowStart !== topPosition) {
1369			/* we just came to a new row.  Set all the heights on the completed row */
1370			for (currentDiv = 0 ; currentDiv < rowDivs.length ; currentDiv++) {
1371				rowDivs[currentDiv].height(currentTallest);
1372			}
1373
1374			/* set the variables for the new row */
1375			rowDivs.length = 0; // empty the array
1376			currentRowStart = topPosition;
1377			currentTallest = $el.height();
1378			rowDivs.push($el);
1379		} else {
1380			/* another div on the current row.  Add it to the list and check if it's taller */
1381			rowDivs.push($el);
1382			currentTallest = (currentTallest < $el.height()) ? ($el.height()) : (currentTallest);
1383		}
1384
1385		/* do the last row */
1386		for (currentDiv = 0 ; currentDiv < rowDivs.length ; currentDiv++) {
1387			rowDivs[currentDiv].height(currentTallest);
1388		}
1389	});
1390}
1391/*------------------------------*/
1392/* END Equalise heights in rows */
1393/*------------------------------*/
1394
1395/*----------*/
1396/* Carousel */
1397/*----------*/
1398function initCarousel() {
1399	"use strict";
1400
1401	if ($(".carousel").length) {
1402		//if it exists and hasn't been initialised
1403		$(".carousel").each(function() {
1404			var thisCarousel = $(this);
1405
1406			thisCarousel.carouFredSel(
1407			{
1408				responsive:	true,
1409				auto: false,
1410				items: {
1411					start: -1,
1412					width: 350,
1413					visible:{
1414						min: 2,
1415						max: 10
1416					}
1417				},
1418				scroll: {
1419					items: 1,
1420					onBefore : function(oldItems, newItems) {
1421						$(window).trigger("scroll");
1422					}
1423				},
1424				prev: {
1425//					button: ".prev",
1426					button: thisCarousel.closest(".carousel-wrapper").find(".carousel-prev"),
1427					key: "left"
1428				},
1429				next: {
1430//					button: ".next",
1431//					button: $(this).parent().find(".carousel-next"),
1432					button: thisCarousel.closest(".carousel-wrapper").find(".carousel-next"),
1433					key: "right"
1434				}
1435			});
1436//			}).prev();
1437//			}).next();
1438
1439//			$('#carousel').trigger('prev');
1440
1441			thisCarousel.swipe(
1442			{
1443				swipeLeft: function(event, direction, distance, duration, fingerCount) {
1444					thisCarousel.trigger('next');
1445				},
1446				swipeRight: function(event, direction, distance, duration, fingerCount) {
1447					thisCarousel.trigger('prev');
1448				},
1449				tap: function(event, target) {
1450					$(target).parent().trigger('click');
1451				},
1452
1453				allowPageScroll: 'vertical',
1454				excludedElements: "button, input, select, textarea, .noSwipe"
1455			});
1456		});
1457
1458		carouselInitialised = true;
1459	}
1460}
1461/*--------------*/
1462/* END Carousel */
1463/*--------------*/
1464
1465function setCarouselHeight() {
1466	"use strict";
1467
1468	var slideHeight = $('.masthead-slide').height();
1469	$('.masthead-container').height(slideHeight);
1470}
1471
1472/*-----------------------------------*/
1473/* Get the offset of accordion panel */
1474/*-----------------------------------*/
1475function accordionOffset() {
1476	"use strict";
1477
1478	if ($('.tabAccordionContainer').length) {
1479		var accordionOffset = $('.tabAccordionContainer').offset();
1480		accordionOffsetTop = parseInt(accordionOffset.top);
1481	}
1482}
1483
1484
1485function carouselTabWidth() {
1486	"use strict";
1487
1488	if ($('table.masthead-pagination').length) {
1489		var td = $('table.masthead-pagination td');
1490		var tdWidth = 100 / td.length;
1491
1492		$(td).css({'width':''+tdWidth+'%'});
1493	}
1494}
1495
1496/*------------------------------------------------------*/
1497/* Custom lazy load										*/
1498/* Find all lazy load images, swap src, make opacity 0	*/
1499/* Animate fade in										*/
1500/*------------------------------------------------------*/
1501
1502//***lazyload removal***
1503function forceLazyLoad(position) {
1504	"use strict";
1505
1506	$(".tabPanelContainer .tabPanel:eq(" + position + ") img.noLazy").each(function() {
1507		var $this = $(this);
1508		if (!$this.hasClass("loaded")) {
1509			$this.addClass("loaded");
1510			var origImg = $this.attr("data-original");
1511
1512			$this.css({"opacity":"0"}).attr("src", origImg);
1513
1514			$this.animate({"opacity":"1"});
1515		}
1516	});
1517}
1518/*----------------------*/
1519/* END Custom lazy load */
1520/*----------------------*/
1521
1522function listenForEscKey() {
1523	"use strict";
1524
1525	$("input:text, input.password, input.numberCheck, input.emailCheck, textarea").on("keydown", function(event) {
1526		if (event.keyCode === 27) {
1527			$(this).val('');
1528		}
1529	});
1530}
1531
1532function createNewPasswordInput(attrList) {
1533	"use strict";
1534
1535	var htmlString = "<input ";
1536
1537	$.each(attrList, function(key, value) { 
1538		htmlString += key + "=\"" + value + "\" ";
1539	});
1540
1541	htmlString += ">";
1542	return htmlString;
1543}
1544
1545function inputFocus() {
1546	"use strict";
1547
1548	$(".gt-ie7 input:text, .gt-ie7 input:password, .gt-ie7 input.numberCheck, .gt-ie7 input.emailCheck, .gt-ie7 textarea").on("focus", function() {
1549		var $this = $(this);
1550		$this.siblings(".clearIcon").show();
1551		$this.siblings(".passwordIcon").addClass("movePasswordIcon");
1552	});
1553}
1554
1555function inputBlur() {
1556	"use strict";
1557
1558	$(".gt-ie7 input:text, .gt-ie7 input:password, .gt-ie7 input.numberCheck, .gt-ie7 input.emailCheck, .gt-ie7 textarea").on("blur", function() {
1559		var $this = $(this);
1560		if ($this.val() === "") {
1561			$this.siblings(".clearIcon").hide();
1562			$this.siblings(".passwordIcon").removeClass("movePasswordIcon");
1563		}
1564	});
1565}
1566
1567function checkKeycode(event) {
1568	"use strict";
1569
1570	/*TRIGGER LIST*/
1571	if ((!event.shiftKey && !event.ctrlKey && !event.altKey) &&  ((event.keyCode >= 48 && event.keyCode <= 57) || (event.keyCode >= 96 && event.keyCode <= 105))) {
1572		return;
1573	} else if (event.keyCode === 8 || event.keyCode === 9 || event.keyCode === 35 || event.keyCode === 36 || event.keyCode === 37 || event.keyCode === 39 || event.keyCode === 46) {
1574		/*IGNORE LIST - If its one of these keys, just ignore it*/
1575		return;
1576	} else if (event.keyCode === 27) {
1577		//RESET THE INPUT TO BLANK
1578		$(inputObject).val('');
1579	} else {
1580		/*PREVENT LIST - else, its not ignorable, or a number, therefore we don't want it*/
1581		event.preventDefault();
1582	}
1583}
1584
1585function getSideBranch(elem) {
1586	//LI parent of clicked A is passed
1587	"use strict";
1588
1589	var thisChildLists = $(elem).find('ul');
1589  //get all child UL's in every sub level in this LI branch
1590	var thisChildLinks = thisChildLists.find('a.showHideIcon');  //get all A's in every sub level that can be expanded
1591	var thisChildLinksActive = thisChildLists.find('li.activeMenuLink');  //get all A's in every sub level that can be expanded
1592
1593	return {'thisChildLists': thisChildLists, 'thisChildLinks': thisChildLinks, 'thisChildLinksActive':thisChildLinksActive};
1594}
1595
1596function closeSideBranch(branch) {
1597	"use strict";
1598
1599	$(branch.thisChildLists).hide();  //Hide all the UL's at every sub level
1600	$(branch.thisChildLinks).parent().parent().removeClass('open');  //remove "open" class from all open LI's
1601}
1602
1603function showMegaMenu() {
1604	//"use strict";
1605
1606	$(this).addClass("hover");
1607}
1608
1609function hideMegaMenu() {
1610	//"use strict";
1611
1612	$(this).removeClass("hover");
1613}
1614
1615function initialiseVideos(container) {
1616	container = container || ".mainContentContainer";
1617	var videoNotSelector;
1618
1619	if (container == ".mainContentContainer") {
1620		videoNotSelector = "#course-profile .video, .tabPanelWrapper .video";
1621	} else {
1622		videoNotSelector = "#course-profile .video";
1623	}
1624
1625	$(container + " .video.single").not(videoNotSelector).each(function() {
1626		var $this = $(this);
1627		$this.find(".loading").show();
1628		var containerId = $this.prop("id");
1629		var videoSource = $this.attr("data-videosource");
1630		var videoId = $this.attr("data-videoid");
1631		var videoUrl = $this.attr("data-videourl");
1632		var poster = $this.attr("data-poster");
1633		var videoTitle = $this.attr("data-videotitle");
1634		var subtitles1File = $this.attr("data-subtitlestrack1file");
1635		var subtitles1Label = $this.attr("data-subtitlestrack1label");
1636		var subtitles2File = $this.attr("data-subtitlestrack2file");
1637		var subtitles2Label = $this.attr("data-subtitlestrack2label");
1638		var subtitles3File = $this.attr("data-subtitlestrack3file");
1639		var subtitles3Label = $this.attr("data-subtitlestrack3label");
1640		var tracks = [];
1641
1642		if (subtitles1File != "" && subtitles1Label != "") {
1643			tracks.push({
1644				"kind": "captions",
1645				"file": subtitles1File,
1646				"label": subtitles1Label,
1647				"default": true
1648			});
1649		}
1650
1651		if (subtitles2File != "" && subtitles2Label != "") {
1652			tracks.push({
1653				"kind": "captions",
1654				"file": subtitles2File,
1655				"label": subtitles2Label
1656			});
1657		}
1658
1659		if (subtitles3File != "" && subtitles3Label != "") {
1660			tracks.push({
1661				"kind": "captions",
1662				"file": subtitles3File,
1663				"label": subtitles3Label
1664			});
1665		}
1666
1667		var width;
1668		var height;
1669		var primaryVideoType = "html5";
1670		var jwPlayerInstance;
1671
1672		if ($("html").hasClass("ie7")) {
1673			width = 640;
1674			height = 360;
1675		} else {
1676			width = "100%";
1677			height = 0;
1678		}
1679
1680		if (videoSource === "YouTube") {
1681			jwPlayerInstance = jwplayer(containerId).setup({
1682				"id": containerId,
1683				width: width,
1684				aspectratio: "16:9",
1685//				"stretching": "fill",
1686				"image": poster,
1687				file: "http://www.youtube.com/watch?v=" + videoId,
1688				title: videoTitle,
1689				sharing: {
1690					heading: "Share video"
1691				}
1692			});
1693
1694			jwPlayerInstance.on("ready", function() {
1695				$("#" + containerId).addClass("youtube");
1696			});
1697		} else if (videoSource === "Video Portal") {
1698			jwPlayerInstance = jwplayer(containerId).setup({
1699				"id": containerId,
1700				width: width,
1701				aspectratio: "16:9",
1702//				"stretching": "fill",
1703				"image": poster,
1704				file: videoUrl,
1705				title: videoTitle,
1706				sharing: {
1707					heading: "Share video"
1708				},
1709				"tracks": tracks
1710			});
1711
1712			jwPlayerInstance.on("ready", function() {
1713				$("#" + containerId).addClass("videoportal");
1714			});
1715		} else {
1716			if (firefox41Plus) {
1717				primaryVideoType = "flash";
1718			}
1719
1720			jwPlayerInstance = jwplayer(containerId).setup({
1721				"id": containerId,
1722				width: width,
1723				aspectratio: "16:9",
1724//				"stretching": "uniform",
1725//				"stretching": "none",
1726//				"stretching": "exactfit",
1727//				"stretching": "fill",
1728				"image": poster,
1729				file: "http://helix.stream.manchester.ac.uk/flash/" + videoId + "_hi.mp4",
1730				primary: primaryVideoType,
1731				preload: "metadata",
1732				sharing: {
1733					heading: "Share video"
1734				}
1735			});
1736		}
1737	});
1738
1739	$(container + " .video.playlist").not(videoNotSelector).each(function() {
1740		var $this = $(this);
1741		$this.find(".loading").show();
1742		$this.next(".videoPlaylistNavWrapper").show();
1743		var playlistId = $this.attr("data-playlistid");
1744		var playlistContainsUniversityVideo = $this.attr("data-containsuniversityvideo");
1745		var aspectRatio = $this.attr("data-aspectratio");
1746		var stretching = $this.attr("data-stretching");
1747		var primaryVideoType = "html5";
1748
1749		if (firefox41Plus && playlistContainsUniversityVideo === "true") {
1750			primaryVideoType = "flash";
1751		}
1752
1753		var jwPlayerInstance = jwplayer(this).setup({
1754			playlist: window["videoPlaylist" + playlistId],
1755			visualplaylist: false,
1756			width: "100%",
1757			aspectratio: aspectRatio,
1758//			stretching: stretching,
1759			primary: primaryVideoType,
1760			preload: "metadata",
1761//			controls: false,
1762//			displaytitle: true,
1763//			displaydescription: true,
1764			sharing: {
1765				heading: "Share video"
1766			}
1767		});
1768
1769		var playlistScroll;
1770
1771		jwPlayerInstance.on("ready", function() {
1772			var playlist = jwPlayerInstance.getPlaylist();
1773			var playlistHtml = "";
1774
1775			for (var index = 0; index < playlist.length; index++) {
1776				playlistHtml += "<li";
1777
1778				playlistHtml += "><a href=\"#\" onclick=\"return false\" data-playlistid=\"" + playlistId + "\" data-index=\"" + index + "\"><img src=\"" + playlist[index].image + "\" alt=\"\" /><div><p class=\"title\">" + playlist[index].title + "</p>";
1779
1780				if (playlist[index].description !== undefined) {
1781					playlistHtml += "<p class=\"description\">" + playlist[index].description + "</p>";
1782				}
1783
1784				playlistHtml += "</div></a></li>";
1785			}
1786
1787			$("#videoPlaylist" + playlistId + "Nav").append(playlistHtml);
1788
1789			// Insert a little delay in introducing iScroll to allow the browser to finish updating the DOM. This only seems to be necessary for Firefox, but it won't negatively affect the experience in any browser.
1790			setTimeout(function() {
1791				playlistScroll = new IScroll("#videoPlaylist" + playlistId + "NavWrapper", {
1792					scrollbars: true,
1793					interactiveScrollbars: true,
1794					mouseWheel: true,
1795					tap: true
1796				});
1797			}, 300);
1798
1799			$("#videoPlaylist" + playlistId + "NavWrapper a").on("tap", function(event) {
1800				var $this = $(this);
1801//				event.preventDefault();
1802				playThis("videoPlaylist" + $this.attr("data-playlistid"), $this.attr("data-index"));
1803//				playlistScroll.scrollToElement(this, 500);
1804			});
1805		});
1806
1807		jwPlayerInstance.on("playlistItem", function(playlistItem) {
1808			if (playlistItem.item.file.indexOf("youtube") === -1) {
1809				$(".jwplayer").removeClass("youtube");
1810			} else {
1811				$(".jwplayer").addClass("youtube");
1812			}
1813
1814			$("#videoPlaylist" + playlistId + "Nav li").removeClass("current");
1815			$("#videoPlaylist" + playlistId + "Nav li").eq(jwPlayerInstance.getPlaylistIndex()).addClass("current");
1816			playlistScroll.refresh();
1817			playlistScroll.scrollToElement(document.querySelector("#videoPlaylist" + playlistId + "Nav li:nth-child(" + (jwPlayerInstance.getPlaylistIndex() + 1) + ")"), 1000);
1818		});
1819	});
1820}
1821
1822function playThis(videoPlayerId, videoIndex) {
1823	var jwPlayerInstance = jwplayer(videoPlayerId);
1824	jwPlayerInstance.playlistItem(parseInt(videoIndex));
1825}
1826
1827function initialiseAudio(container) {
1828	container = container || ".mainContentContainer";
1829	var audioNotSelector;
1830
1831	if (container == ".mainContentContainer") {
1832		audioNotSelector = "#course-profile .audio, .tabPanelWrapper .audio";
1833	} else {
1834		audioNotSelector = "#course-profile .audio";
1835	}
1836
1837	$(container + " .audio").not(audioNotSelector).each(function() {
1838		var $this = $(this);
1839		$this.find(".loading").show();
1840		var containerId = $this.prop("id");
1841		var audioUrl = $this.attr("data-audiourl");
1842		var audioTitle = $this.attr("data-audiotitle");
1843		var jwPlayerInstance = jwplayer(containerId).setup({
1844			"id": containerId,
1845			width: "100%",
1846			height: 40,
1847			aspectratio: "",
1848			file: audioUrl,
1849			title: audioTitle
1850		});
1851
1852		jwPlayerInstance.on("ready", function() {
1853			$("#" + containerId).addClass("videoportal");
1854		});			
1855	});
1856}
1857
1858function initialiseImageGalleries(container) {
1859	container = container || ".mainContentContainer";
1860
1861	$(container + " div.image-gallery:visible:not(.initialised)").each(function() {
1862		let $this = $(this);
1863		const setId = $this.data("setid");
1864		const hasThumbnails = $this.hasClass("with-thumbnails");
1865		let galleryScroll;
1866		let controls = true;
1867		let navContainer = false;
1868
1869		if (hasThumbnails) {
1870			navContainer = "#set-" + setId + "-thumbs";
1871			controls = false;
1872		}
1873
1874		var carousel = tns({
1875			"container": "#" + "set-" + setId,
1876			"lazyload": true,
1877			"items": 1, // *** maybe we should have this as an option for each gallery?
1878			"autoHeight": true, // *** maybe we should have this as an option for each gallery?
1879			"autoWidth": false,
1880			"loop": false,
1881			"mouseDrag": true,
1882			"arrowKeys": false, // *** maybe we should have this as an option for each gallery?
1883			"controls": controls,
1884			"controlsText": ["<", ">"],
1885			"navAsThumbnails": hasThumbnails,
1886			"navContainer": navContainer,
1887			"navPosition": "bottom",
1888			onInit() {
1889				$this.addClass("initialised");
1890
1891				if (hasThumbnails) {
1892					galleryScroll = new IScroll("#set-" + setId + "-thumbs-wrapper", {
1893						scrollX: true,
1894						scrollY: false,
1895						eventPassthrough: true,
1896						disablePointer: true, // important to disable the pointer events that causes the issues
1897						disableTouch: false, // false if you want the slider to be usable with touch devices
1898						disableMouse: false, // false if you want the slider to be usable with a mouse (desktop)
1899						interactiveScrollbars: true,
1900						tap: true,
1901						click: true
1902					});
1903
1904					$("#set-" + setId + "-thumbs-wrapper button").on("click keypress", function() {
1905						galleryScroll.scrollToElement(this, 500, true, 0);
1906					});
1907				}
1908
1909				if (controls) {
1910					$("#image-gallery-" + setId + " .tns-controls").attr("tabindex", "-1").find("button").attr("tabindex", "0");
1911				}
1912			}
1913		});
1914
1915		if (hasThumbnails) {
1916			carousel.events.on("indexChanged", function(info) {
1917				galleryScroll.scrollToElement(document.querySelector("#set-" + setId + "-thumbs button:nth-of-type(" + (info.index + 1) + ")"), 500, true, 0);
1918
1919				// We're updating the slider height here to account for differences in image aspect ratio in conjunction with lazy loading being used. If we have consistent aspect ratio for all images, then we won't need to update the slider height here.
1920				setTimeout(function() {
1921					carousel.updateSliderHeight();
1922				}, 250);
1923			});
1924
1925			var lastKeyCode = 0;
1926
1927			$("#set-" + setId + "-thumbs button").on("focus", function() {
1928				if (lastKeyCode == 9) {
1929					// tabbed onto button
1930					galleryScroll.scrollToElement(document.querySelector("#set-" + setId + "-thumbs button:nth-of-type(" + ($(this).index() + 1) + ")"), 500, true, 0);
1931				}
1932			}).on("keydown", function(e) {
1933				if (e.which == 9) setTimeout(function() {
1934					lastKeyCode = 0
1935				}, 50);
1936
1937				lastKeyCode = e.which;
1938			});
1939		}
1940
1941		carousel.events.on("indexChanged", function(info) {
1942			//console.log("Tiny Slider indexChanged event fired for gallery with ID = " + info.container.id + ". New slide index = " + info.index);
1943			let galleryIframes = document.querySelectorAll("#" + info.container.id + " iframe");
1944
1945			// Set tabindex of all iframes in this carousel to -1.
1946			galleryIframes.forEach(function(currentValue) {
1947				currentValue.setAttribute("tabindex", "-1");
1948			});
1949
1950			// Then check if the newly selected slide contains an iframe and if so, remove its tabindex attribute, so that it's focusable.
1951			//console.log("#" + info.container.id + " iframe");
1952			let galleryActiveSlideIframe = document.querySelector("#" + info.container.id + " figure:nth-of-type(" + (info.index + 1) + ") iframe");
1953			if (galleryActiveSlideIframe != null) galleryActiveSlideIframe.removeAttribute("tabindex");
1954		});
1955	});
1956}

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.