1/* 2 * Bones Scripts File 3 * Author: Eddie Machado 4 * 5 * This file is called automatically in the footer. 6*/ 7 8 9/* 10 * Get Viewport Dimensions 11 * returns object with viewport dimensions to match css in width and height properties 12 * ( source: http://andylangton.co.uk/blog/development/get-viewport-size-width-and-height-javascript ) 13*/ 14function updateViewportDimensions() { 15 var w = window, d = document, e = d.documentElement, g = d.getElementsByTagName('body')[0], x = w.innerWidth || e.clientWidth || g.clientWidth, y = w.innerHeight || e.clientHeight || g.clientHeight; 16 return { width: x, height: y }; 17} 18// setting the viewport width 19var viewport = updateViewportDimensions(); 20 21 22/* 23 * Throttle Resize-triggered Events 24 * Wrap your actions in this function to throttle the frequency of firing them off, for better performance, esp. on mobile. 25 * ( source: http://stackoverflow.com/questions/2854407/javascript-jquery-window-resize-how-to-fire-after-the-resize-is-completed ) 26*/ 27var waitForFinalEvent = (function () { 28 var timers = {}; 29 return function (callback, ms, uniqueId) { 30 if (!uniqueId) { uniqueId = "Don't call this twice without a uniqueId"; } 31 if (timers[uniqueId]) { clearTimeout(timers[uniqueId]); } 32 timers[uniqueId] = setTimeout(callback, ms); 33 }; 34}); 35 36// how long to wait before deciding the resize has stopped, in ms. Around 50-100 should work ok. 37var timeToWaitForLast = 100; 38 39 40/* 41 * Here's an example so you can see how we're using the above function 42 * 43 * If we want to only do it on a certain page, we can setup checks so we do it 44 * as efficient as possible. 45 * 46 * if( typeof is_home === "undefined" ) var is_home = $('body').hasClass('home'); 47 * 48 * This once checks to see if you're on the home page based on the body class 49 * We can then use that check to perform actions on the home page only 50 * 51 * When the window is resized, we perform this function 52 * $(window).resize(function () { 53 * 54 * // if we're on the home page, we wait the set amount (in function above) then fire the function 55 * if( is_home ) { waitForFinalEvent( function() { 56 * 57 * // update the viewport, in case the window size has changed 58 * viewport = updateViewportDimensions(); 59 * 60 * // if we're above or equal to 768 fire this off 61 * if( viewport.width >= 768 ) { 62 * console.log('On home page and window sized to 768 width or more.'); 63 * } else { 64 * // otherwise, let's do this instead 65 * console.log('Not on home page, or window sized to less than 768.'); 66 * } 67 * 68 * }, timeToWaitForLast, "your-function-identifier-string"); } 69 * }); 70 * 71 * 72*/ 73 74/* 75 Put all your regular jQuery in here. 76*/ 77jQuery(document).ready(function ($) { 78 /* 79 * Apply parallax effect to header image 80 */ 81 $(window).scroll(function (e) { 82 var scrolled = $(window).scrollTop(); 83 $('.header-image').css('top', -(scrolled * 0.1) + 'px'); 84 }); 85 86 // We're going to swap out the gravatars. 87 // In the functions.php file, you can see we're not loading the gravatar 88 // images on mobile to save bandwidth. Once we hit an acceptable viewport 89 // then we can swap out those images since they are located in a data attribute. 90function loadGravatars() { 91// set the viewport using the function above 92 viewport = updateViewportDimensions(); 93 // if the viewport is tablet or larger, we load in the gravatars 94 if (viewport.width >= 768) { 95 $('.comment img[data-gravatar]').each(function(){ 96 $(this).attr('src',$(this).attr('data-gravatar')); 97 }); 98 } 99}; // end function 100 101 /* 102 * Let's fire off the gravatar function 103 * You can remove this if you don't need it 104 */ 105 loadGravatars(); 106 107 108 109 110 // mobile support menu collapse and expand 111// $('#support-nav-toggle').click(function(){ 112// $(this).toggleClass('open'); 113// $('.support-navigation').toggleClass('open'); 114// 115// }); 116 117 118 // Prevent cookies from blocking tabbing through main page content 119 const cookieDiv1s = document.querySelectorAll(".cky-consent-container"); 120 const cookieDiv2s = document.querySelectorAll(".cky-modal"); 121 const allOther = document.querySelector("#content"); 122 function moveCookies() { 123 cookieDiv1s.forEach((cookieDiv1) => { 124 allOther.after(cookieDiv1); 125 });
126 cookieDiv2s.forEach((cookieDiv2) => { 127 allOther.after(cookieDiv2); 128 }); 129 130 } 131 window.addEventListener("load", (event) => { 132 moveCookies(); 133 }); 134// 135 136 // Menu script from https://www.a11y-collective.com/blog/mastering-web-accessibility-making-drop-down-menus-user-friendly/ 137 const menuItems = document.querySelectorAll(".menu-wrapper"); 138 const innerContent = document.querySelector("#inner-content"); 139 140 let expandedItem = null; 141 142 function collapseAll() { 143 const otherSubs = document.querySelectorAll("#site-navigation .sub-menu"); 144 const otherButtons = document.querySelectorAll("#site-navigation .nav-button"); 145 otherSubs.forEach((otherSub) => { 146 otherSub.setAttribute("aria-hidden","true"); 147 otherSub.classList.remove("open"); 148 }); 149 otherButtons.forEach((otherButton) => { 150 otherButton.setAttribute("aria-expanded","false"); 151 otherButton.classList.remove("open"); 152 }); 153 } 154 155 const expandSubMenu = (item) => { 156 const subMenu = item.querySelector(".sub-menu"); 157 const button = item.querySelector(".nav-button"); 158 expandedItem = item; 159 160 // first close all other header menus 161 collapseAll(); 162 subMenu.setAttribute("aria-hidden","false"); 163 button.setAttribute("aria-expanded","true"); 164 subMenu.classList.add("open"); 165 button.classList.add("open"); 166 item.classList.add("open"); 167 item.setAttribute("aria-expanded","true"); 168 item.dataset.expanded = "true"; 169 let firstLink = subMenu.querySelector("a"); 170 if (firstLink !== null){ 171 firstLink.focus(); // Focus on the first link in the submenu 172 } 173 //subMenu.querySelector("#s").focus(); // Focus on the first link in the submenu 174 175 176 }; 177 178 const collapseSubMenu = (item) => { 179 const subMenu = item.querySelector(".sub-menu"); 180 const button = item.querySelector(".nav-button"); 181 expandedItem = null; 182 183 subMenu.setAttribute("aria-hidden","true"); 184 button.setAttribute("aria-expanded","false"); 185 subMenu.classList.remove("open"); 186 button.classList.remove("open"); 187 item.classList.remove("open"); 188 item.setAttribute("aria-expanded","false"); 189 item.dataset.expanded = "false"; 190 button.focus(); // Focus back on the button 191 }; 192 193 194 // trigger functions on button click 195 menuItems.forEach((item) => { 196 const button = item.querySelector(".nav-button"); 197 198 button.addEventListener("click", (event) => { 199 if (button.ariaExpanded === "false") { 200 expandSubMenu(item); 201 } else { 202 collapseSubMenu(item); 203 } 204 }); 205 206 // Handling keyboard navigation 207 button.addEventListener("keydown", (event) => { 208 if (event.key === "Enter" || event.key === " ") { // Space or Enter key 209 event.preventDefault(); // Prevent the default action to stop scrolling when pressing Space 210 if (button.ariaExpanded === "false") { 211 expandSubMenu(item); 212 } else { 213 collapseSubMenu(item); 214 } 215 } 216 }); 217 218 // Handling tab key inside submenu to loop back to the button 219 const subMenuLinks = item.querySelectorAll("ul a"); 220 if (subMenuLinks.length) { 221 const lastLink = subMenuLinks[subMenuLinks.length - 1]; 222 lastLink.addEventListener("keydown", (event) => { 223 if (event.key === "Tab" && !event.shiftKey) { 224 event.preventDefault(); 225 button.focus(); // Move focus back to the button 226 } 227 }); 228 } 229 }); 230 allOther.addEventListener("click", (event) => { 231 collapseAll(); 232 }); 233 allOther.addEventListener("keydown", (event) => { 234 collapseAll(); 235 }); 236 237 // Close all menus if user clicks on anything in main area 238 239// innerContent.addEventListener("click", (event) => { 240// collapseAll(); 241// }); 242 243 // Handling keyboard navigation 244// innerContent.addEventListener("keydown", (event) => { 245// collapseAll(); 246// }); 247 248 249 250 251 252// document.addEventListener("keydown", (event) => { 253// if (event.key === "Tab") { 254// if (!expandedItem) { 255// return; 256// } 257// 258// const subMenu = expandedItem.querySelector(".sub-menu"); 259// 260// const focusedElement = expandedItem.querySelector(":focus"); 261// const firstFocusableElement = expandedItem.querySelector("a"); 262// firstFocusableElement.classList.add('first'); 263// const lastFocusableElement = subMenu.lastElementChild.querySelector("a"); 264// lastFocusableElement.classList.add('last'); 265// 266// if (!event.shiftKey && focusedElement === lastFocusableElement) { 267// collapseSubMenu(expandedItem); 268// return; 269// } 270// 271// if (event.shiftKey && focusedElement === firstFocusableElement) { 272// collapseSubMenu(expandedItem); 273// return; 274// } 275// } 276// 277// if (event.key == "Escape") { 278// collapseSubMenu(expandedItem); 279// } 280// }); 281 282 283 284 // make script aboave target .menu-wrapper > .widget:first-child .widgettitle and add .setAttribute("aria-expanded","fal
284se"); 285 286 const dynamicSidebarOpen = document.querySelectorAll(".menu-wrapper-special"); 287 288 // trigger functions on button click 289 dynamicSidebarOpen.forEach((sidebar) => { 290 sidebar.setAttribute("tabindex","-1"); 291 const buttonSpecial = sidebar.querySelector(".menu-wrapper-special .widgettitle"); 292 293 buttonSpecial.setAttribute("aria-expanded","false"); 294 buttonSpecial.setAttribute("tabIndex","0"); 295 296 const expandSidebar = (sidebar) => { 297 const subSidebar = buttonSpecial.nextElementSibling; 298 expandedSidebar = sidebar; 299 300 subSidebar.setAttribute("aria-hidden","false"); 301 buttonSpecial.setAttribute("aria-expanded","true"); 302 subSidebar.classList.add("open"); 303 buttonSpecial.classList.add("open"); 304 sidebar.classList.add("open"); 305 sidebar.dataset.expanded = "true"; 306 subSidebar.querySelectorAll("a")[0].focus(); // Focus on the first link in the submenu 307 308 309 }; 310 311 const collapseSidebar = (sidebar) => { 312 const subSidebar = buttonSpecial.nextElementSibling; 313 expandedSidebar = null; 314 315 subSidebar.setAttribute("aria-hidden","true"); 316 buttonSpecial.setAttribute("aria-expanded","false"); 317 subSidebar.classList.remove("open"); 318 buttonSpecial.classList.remove("open"); 319 sidebar.classList.remove("open"); 320 sidebar.dataset.expanded = "false"; 321 buttonSpecial.focus(); // Focus back on the button 322 }; 323 324 325 buttonSpecial.addEventListener("click", (event) => { 326 if (buttonSpecial.ariaExpanded === "false") { 327 expandSidebar(sidebar); 328 } else { 329 collapseSidebar(sidebar); 330 } 331 }); 332 333 // Handling keyboard navigation 334 buttonSpecial.addEventListener("keydown", (event) => { 335 if (event.key === "Enter" || event.key === " ") { // Space or Enter key 336 event.preventDefault(); // Prevent the default action to stop scrolling when pressing Space 337 if (buttonSpecial.ariaExpanded === "false") { 338 expandSidebar(sidebar); 339 } else { 340 collapseSidebar(sidebar); 341 } 342 } 343 }); 344 }); 345 346// document.addEventListener("keydown2", (event) => { 347// if (event.key === "Tab") { 348// if (!expandedSidebar) { 349// return; 350// } 351// const buttonSpecial = sidebar.querySelector(".menu-wrapper-special > .widget:first-child .widgettitle"); 352// const subSidebar = buttonSpecial.nextElementSibling; 353// 354// const focusedElement2 = expandedSidebar.querySelector(":focus"); 355// const firstFocusableElement2 = expandedSidebar.querySelector("a"); 356// const lastFocusableElement2 = subSidebar.lastElementChild.querySelector("a"); 357// 358// if (!event.shiftKey && focusedElement2 === lastFocusableElement2) { 359// collapseSidebar(expandedSidebar); 360// return; 361// } 362// 363// if (event.shiftKey && focusedElement2 === firstFocusableElement2) { 364// collapseSidebar(expandedSidebar); 365// return; 366// } 367// } 368// 369// if (event.key == "Escape") { 370// collapseSidebar(expandedSidebar); 371// } 372// }); 373 374 375 376 377 //Special case for upper header 378 const upperItems = document.querySelectorAll(".upper-menu-wrapper"); 379 const homeTranslate = document.querySelector("#home-translate"); 380 381 let expandedUpperItem = null; 382 383 function collapseAllUpper() { 384 const otherUpperWraps = document.querySelectorAll(".upper-menu-wrapper"); 385 const otherUpperSubs = document.querySelectorAll(".upper-sub-menu"); 386 const otherUpperButtons = document.querySelectorAll(".upper-nav-button"); 387 otherUpperWraps.forEach((otherUpperWrap) => { 388 //otherUpperWrap.setAttribute("aria-hidden","true"); 389 otherUpperWrap.classList.remove("open"); 390 });
391 otherUpperSubs.forEach((otherUpperSub) => { 392 otherUpperSub.setAttribute("aria-hidden","true"); 393 otherUpperSub.classList.remove("open"); 394 }); 395 otherUpperButtons.forEach((otherUpperButton) => { 396 otherUpperButton.setAttribute("aria-expanded","false"); 397 otherUpperButton.classList.remove("open"); 398 }); 399 } 400 401 const expandUpperSubMenu = (upperItem) => { 402 const upperSubMenu = upperItem.querySelector(".upper-sub-menu"); 403 const upperButton = upperItem.querySelector(".upper-nav-button"); 404 expandedUpperItem = upperItem; 405 406 collapseAllUpper(); 407 homeTranslate.classList.add("open"); 408 upperButton.setAttribute("aria-expanded","true"); 409 upperSubMenu.classList.add("open"); 410 upperButton.classList.add("open"); 411 upperItem.classList.add("open"); 412 upperItem.dataset.expanded = "true"; 413// const searchFocus = document.getElementById("s"); 414// if (upperButton.classList.contains("search-toggle")) { 415// searchFocus.focus(); // Focus on the search field 416// }; 417 418 }; 419 420 const collapseUpperSubMenu = (upperItem) => { 421 const upperSubMenu = upperItem.querySelector(".upper-sub-menu"); 422 const upperButton = upperItem.querySelector(".upper-nav-button"); 423 expandedUpperItem = null; 424 425 homeTranslate.classList.remove("open"); 426 upperSubMenu.setAttribute("aria-hidden","true"); 427 upperButton.setAttribute("aria-expanded","false"); 428 upperSubMenu.classList.remove("open"); 429 upperButton.classList.remove("open"); 430 upperItem.classList.remove("open"); 431 upperItem.dataset.expanded = "false"; 432 upperButton.focus(); // Focus back on the button 433 }; 434 435 436 // trigger functions on button click 437 upperItems.forEach((upperItem) => { 438 const upperButton = upperItem.querySelector(".upper-nav-button"); 439 440 upperButton.addEventListener("click", (event) => { 441 442 if (upperButton.ariaExpanded === "false") { 443 expandUpperSubMenu(upperItem); 444 445 } else { 446 collapseUpperSubMenu(upperItem); 447 } 448 }); 449 450 // Handling keyboard navigation 451 upperButton.addEventListener("keydown", (event) => { 452 if (event.key === "Enter" || event.key === " ") { // Space or Enter key 453 event.preventDefault(); // Prevent the default action to stop scrolling when pressing Space 454 455 if (upperButton.ariaExpanded === "false") { 456 expandUpperSubMenu(upperItem); 457 458 } else { 459 collapseUpperSubMenu(upperItem); 460 } 461 } 462 }); 463 }); 464 465// document.addEventListener("keydown2", (event) => { 466// if (event.key === "Tab") { 467// if (!expandedUpperItem) { 468// return; 469// } 470// if (event.key == "Escape") { 471// collapseUpperSubMenu(upperItem); 472// } 473// } 474// }); 475 476 477 478 479 // SKIP LINKS 480 if (window.location.pathname !== "/" && window.location.pathname !== "https://www.mosaicnet.ca/") { 481 482 var mainContent = document.getElementById('main-skip'); 483 var skipMain = document.getElementById('skip-to-main'); 484 skipMain.onclick = function(e) { 485 e.preventDefault(); 486 mainContent.focus(); 487 }; 488 489 } 490 var innerContentSkip = document.getElementById('inner-content-skip'); 491 492 var skipLink = document.getElementById('skip'); 493 494 skipLink.onclick = function(e) { 495 e.preventDefault(); 496 innerContentSkip.focus(); 497 } 498 499 500 // REPLACED BY ACCESSIBLE SCRIPT ABOVE 501// function accordion() { 502// $(".accordion-toggle, .accordion > .widget:first-child .widgettitle").click(function(){ 503// if ($(this).hasClass("open")){ 504// $(this).parents('.accordion').removeClass("open"); 505// $(this).parents('.accordion').children(".accordion-box").removeClass("open"); 506// $(this).removeClass("open"); 507// 508// } else { 509// $(this).parents('.accordion').addClass("open"); 510// $(this).parents('.accordion').children(".accordion-box").addClass("open"); 511// $(this).addClass("open"); 512// } 513// }); 514// } 515// $(".accordion").onload = accordion(); 516 517 518// if(navigator.userAgent.match("CriOS")){ 519// let div = document.getElementById("google_translate_element"); 520// div.innerHTML = "<p style='margin-top: 0;'><small>If you don't see the language selector, please use the built-in browser translate function instead.</small></p>"; 521// }; 522// translateExists = document.querySelectorAll(".goog-te-gadget"); 523// if (translateExists === null) { 524// translateReplace(); 525// } 526 527// if (navigator.cookieEnabled) { 528// // Cookies are enabled 529// console.log('Cookies are enabled'); 530// } else { 531// // Cookies are disabled 532// translateReplace(); 533// } 534 535 if (window.getCkyConsent) { 536 const consent = window.getCkyConsent(); 537 if (consent && consent.categories) { 538 // User has accepted cookies 539 console.log('Analytics cookies are accepted'); 540 } else { 541 // User has not accepted cookies or cookies are disabled 542 translateReplace(); 543 } 544 } else { 545 // CookieYes is not initialized or an error occurred 546 translateReplace(); 547 console.log('CookieYes is not initialized'); 548 } 549 550 const translateContents = document.getElementById("google_translate_element").innerHTML; 551 document.addEventListener("cookieyes_consent_update", function (eventData) { 552 if (eventData && eventData.detail && eventData.detail.rejected.length > 0) { 553 // Cookies were rejected, execute your function here 554 translateReplace(); 555 } else { 556 translateRefresh(); 557 } 558 }); 559 560 function translateReplace() { 561 let div = document.getElementById("google_translate_element"); 562 div.innerHTML = "<p style='margin-top: 0;'><small>To translate, please accept cookies or use other browser translate options.
562</small></p>"; 563 } 564 function translateRefresh() { 565 let div = document.getElementById("google_translate_element"); 566 div.innerHTML = "<p style='margin-top: 0;'><small>Please refresh the page to access this translator.</small></p>"; 567 } 568 569 570 571 // fixed sidebar for in-page nav from http://stackoverflow.com/questions/23081656/fixing-an-element-when-it-reaches-the-top-of-the-page 572 573 if ($(".jsscrollfix").length > 0){ 574 575 viewport = updateViewportDimensions(); 576 // if the viewport is tablet or larger, we use this sticky sidebar function 577 if (viewport.width >= 1140) { 578 var stickySidebar = $('.jsscrollfix').offset().top; 579 var footerSidebar = $('.site-footer').height() + $('.jsscrollfix').height(); 580 var stickyHeight = footerSidebar + stickySidebar; 581 582 var contentHeight = $('#inner-content').height(); 583 584 var elementHeights = $('.jsscrollfix').map(function() { 585 return $(this).height(); 586 }).get(); 587 588 // Math.max takes a variable number of arguments 589 // `apply` is equivalent to passing each height as an argument 590 var maxHeight = Math.max.apply(null, elementHeights); 591 //if (viewport.width >= 1700) { 592 // if (maxHeight < 680) { 593 // maxHeight = 680; 594 // } 595 //} else { 596 if (maxHeight < 600) { 597 maxHeight = 600; 598 } 599 //} 600 601 // Set each height to the max height 602 $('.jsscrollfix').height(maxHeight); 603 604 605 var docCalcunstick = contentHeight - maxHeight; 606 var docHeightunstick = docCalcunstick + stickySidebar; 607 608 $(window).scroll(function(e) { 609 $("#inner-content").css("min-height", maxHeight); 610 611 if (viewport.width < 1140) { 612 e.stopPropagation(); 613 } else if (contentHeight < maxHeight) { 614 e.stopPropagation(); 615 } else { 616 $('.fixparent').css('height', contentHeight ); 617 618 if ($(window).scrollTop() > stickySidebar) { 619 $('.jsscrollfix').addClass('fixed'); 620 621 622 if ( $(window).scrollTop() > docHeightunstick ) { 623 624 $('.jsscrollfix').removeClass('fixed'); 625 $('.jsscrollfix').addClass('unfixed'); 626 627 } else { 628 $('.jsscrollfix').addClass('fixed'); 629 $('.jsscrollfix').removeClass('unfixed'); 630 } 631 } 632 else { 633 $('.jsscrollfix').removeClass('fixed'); 634 } 635 } 636 637 }); 638 } else { 639 $('.fixparent').css('height', '' ); 640 //$('.jsscrollfix').css('height', '' ); 641 } 642 643 $(window).resize(function () { 644 viewport = updateViewportDimensions(); 645 646 $('.fixparent').css('height', '' ); 647 $('.jsscrollfix').css('height', '' ); 648 }); 649 } 650 651 652 653 // smooth scroll 654 $(function() { 655 $('a[href*="#"]:not([href="#"])').click(function() { 656 if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) { 657 var target = $(this.hash); 658 target = target.length ? target : $('[name=' + this.hash.slice(1) +']'); 659 if (target.length) { 660 $('html,body').animate({ 661 scrollTop: target.offset().top 662 }, 1000); 663 if( $(target).hasClass('accordion-toggle')) { 664 target.addClass("open"); 665 target.siblings(".accordion-box").addClass("open"); 666 } 667 return false; 668 669 } 670 } 671 }); 672 }); 673 674 675}); /* end of as page load scripts */
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.