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.