1/** 2 * @file 3 * Global utilities. 4 * 5 */ 6(function ($, Drupal) { 7 "use strict"; 8 9 // Drupal.behaviors.som = { 10 // attach: function (context, settings) { 11 // var position = $(window).scrollTop(); 12 // $(window).scroll(function () { 13 // if ($(this).scrollTop() > 50) { 14 // $("body").addClass("scrolled"); 15 // } else { 16 // $("body").removeClass("scrolled"); 17 // } 18 // var scroll = $(window).scrollTop(); 19 // if (scroll > position) { 20 // $("body").addClass("scrolldown"); 21 // $("body").removeClass("scrollup"); 22 // } else { 23 // $("body").addClass("scrollup"); 24 // $("body").removeClass("scrolldown"); 25 // } 26 // position = scroll; 27 // }); 28 // }, 29 // }; 30 31 Drupal.behaviors.som = { 32 attach: function (context, settings) { 33 $(document).ready(function () { 34 35 $.each( $(".masonry"), function() { 36 var masonrySelector = $(this); 37 var masonryOptions = { 38 itemSelector: ".masonry .views-row", 39 horizontalOrder: true, 40 }; 41 42 if (masonrySelector.hasClass("format-tile")) { 43 let grid = masonrySelector.find(".view"); 44 grid.masonry(masonryOptions); 45 } 46 47 // blog view buttons toggle 48 $(".blog-tile-button").on("click", function () { 49 let $masonry = $(this) 50 .parents(".blog-format-switcher") 51 .siblings(".masonry"); 52 let $grid = $masonry.find(".view"); 53 let isMasonryActive = Masonry.data($($grid)[0]) 54 ? Masonry.data($($grid)[0]).options.initLayout 55 : false; 56 57 $masonry.addClass("format-tile").removeClass("format-list"); 58 59 if (!isMasonryActive) { 60 $grid.masonry(masonryOptions); // re-initialize 61 } 62 }); 63 64 $(".blog-list-button").on("click", function () { 65 let $masonry = $(this) 66 .parents(".blog-format-switcher") 67 .siblings(".masonry"); 68 let $grid = $masonry.find(".view"); 69 let isMasonryActive = Masonry.data($($grid)[0]) 70 ? Masonry.data($($grid)[0]).options.initLayout 71 : false; 72 73 $masonry.addClass("format-list").removeClass("format-tile"); 74 75 if (isMasonryActive) { 76 $grid.masonry("destroy"); // destroy 77 } 78 }); 79 }); 80 81 // Replace the Search button with an icon in Stories unfiltered view exposed filter 82 $("#edit-submit-stories-view").replaceWith( 83 '<button class="btn" type="submit" id="edit-submit-stories-view"><i class="fa fa-search"></i></button>' 84 ); 85 $("#edit-keyword").attr("placeholder", "Search"); 86 87 // Open the accordion if linked from URL 88 if (window.location.hash != "") { 89 var accordionItemID = window.location.hash; 90 $(accordionItemID).collapse("show"); 91 92 $("html, body").animate( 93 { scrollTop: $(accordionItemID).offset().top - 80 }, 94 500, 95 "linear" 96 ); 97 } 98 99 // Labs sidebar mobile collapse button behavior 100 $("#labs-sidebar-additional").on("show.bs.collapse", function () { 101 var $button = $('[data-bs-target="#labs-sidebar-additional"]'); 102 $button.find(".expand-text").addClass("d-none"); 103 $button.find(".collapse-text").removeClass("d-none"); 104 $button.find(".expand-icon").addClass("d-none"); 105 $button.find(".collapse-icon").removeClass("d-none"); 106 $button.attr("aria-expanded", "true"); 107 }); 108 109 $("#labs-sidebar-additional").on("hide.bs.collapse", function () { 110 var $button = $('[data-bs-target="#labs-sidebar-additional"]'); 111 $button.find(".expand-text").removeClass("d-none"); 112 $button.find(".collapse-text").addClass("d-none"); 113 $button.find(".expand-icon").removeClass("d-none"); 114 $button.find(".collapse-icon").addClass("d-none"); 115 $button.attr("aria-expanded", "false"); 116 }); 117 }); 118 119 function a11yClick(event) { 120 if (event.type === "click") { 121 return true; 122 } else if (event.type === "keypress") { 123 var code = event.charCode || event.keyCode; 124 if (code === 32 || code === 13) { 125 return true; 126 } 127 } else { 128 return false;
129 } 130 } 131 132 // Sidebar menu behavior 133 var sideNavCaret = $(".sidebar-menu .caret"); 134 var sideNavCaretCurrent = $(".sidebar-menu .active.has-submenu > .caret"); 135 136 // Initially add correct expanded attributes for caret on load 137 sideNavCaretCurrent.attr("aria-expanded", "true"); 138 sideNavCaretCurrent.next(".submenu").css("display", "block"); 139 140 // Toggle attributes and animations 141 sideNavCaret.on("click keypress", function (e) { 142 if (a11yClick(e) === true) { 143 const _d = $(this).next(".submenu"); 144 _d.slideToggle(); 145 var _s = $(this).attr("aria-expanded"); 146 $(this).toggleClass("expanded"); 147 if (_s == "false") { 148 $(this).attr("aria-expanded", "true"); 149 } else { 150 $(this).attr("aria-expanded", "false"); 151 } 152 } 153 }); 154 }, 155 }; 156 157 Drupal.behaviors.add_screenreader_menu_actions = { 158 attach: function () { 159 var $mm_itemToggle = $( 160 "#block-mainnavigation .navbar-nav > .nav-item > .dropdown-toggle" 161 ); 162 var $mm_item = $("#block-mainnavigation .navbar-nav > .nav-item"); 163 164 // Keyboard toggle 165 $mm_itemToggle.attr("aria-haspopup", "true"); 166 $mm_itemToggle.bind("click", function () { 167 var expandedState = $(this).attr("aria-expanded"); 168 if (expandedState == "true") { 169 $(this).next().attr("aria-hidden", "true"); 170 } else { 171 $(this).next().attr("aria-hidden", "false"); 172 } 173 }); 174 // mouse hover 175 $mm_item.bind("mouseenter", function () { 176 if (window.matchMedia("(min-width: 992px)").matches) { 177 $(this).find("> button").attr("aria-expanded", "true"); 178 $(this).find("> ul").addClass("show").attr("aria-hidden", "false"); 179 } 180 }); 181 $mm_item.bind("mouseleave", function () { 182 if (window.matchMedia("(min-width: 992px)").matches) { 183 $(this).find("> button").attr("aria-expanded", "false"); 184 $(this).find("> ul").removeClass("show").attr("aria-hidden", "true"); 185 } 186 }); 187 }, 188 }; 189 190 Drupal.behaviors.polite_sticky_header = { 191 attach: function (context, settings) { 192 var $header = $('.header', context); 193 var $body = $('body'); 194 var headerHeight = 0; 195 var lastScrollTop = 0; 196 var scrollThreshold = 50; // Minimum scroll distance before triggering 197 var isSticky = false; 198 var isHidden = false; 199 var scrollTimeout; 200 201 // Initialize header height and sticky state 202 function initHeader() { 203 headerHeight = $header.outerHeight(); 204 // Don't add padding initially - only when header becomes sticky 205 } 206 207 // Update header state based on scroll position and direction 208 function updateHeaderState() { 209 var scrollTop = $(window).scrollTop(); 210 var scrollDelta = scrollTop - lastScrollTop; 211 212 // Only trigger if scroll distance is significant 213 if (Math.abs(scrollDelta) < scrollThreshold) { 214 return; 215 } 216 217 // Determine scroll direction 218 var isScrollingDown = scrollDelta > 0; 219 var isScrollingUp = scrollDelta < 0; 220 221 // Handle sticky positioning - snap immediately when scrolling down past header height 222 if (scrollTop > headerHeight && !isSticky) { 223 $header.addClass('header--sticky'); 224 $body.addClass('header-sticky-active').css('padding-top', headerHeight + 'px'); 225 isSticky = true; 226 isHidden = false; 227 228 // Enable transitions after a brief delay to allow immediate positioning 229 setTimeout(function() { 230 $header.addClass('header--transitions-enabled'); 231 }, 50); 232 } else if (scrollTop <= headerHeight && isSticky) { 233 // Calculate seamless position when scrolling back up within header height 234 var scrollDifference = scrollTop - headerHeight; 235 var headerOffset = Math.max(0, scrollDifference); 236 237 // Position header based on scroll difference 238 $header.css('top', headerOffset + 'px'); 239 240 // If we've scrolled all the way back to the top, return to normal flow 241 if (scrollTop <= 0) { 242 $header.removeClass('header--sticky header--hidden header--visible header--transitions-enabled').css('top', ''); 243 $body.removeClass('header-sticky-active').css('padding-top', '0'); 244 isSticky = false;
245 isHidden = false; 246 return; 247 } 248 } 249 250 // Handle show/hide animations with smooth transitions when sticky 251 if (isSticky) { 252 if (isScrollingUp && isHidden) { 253 // Show header with smooth transition when scrolling up 254 $header.removeClass('header--hidden').addClass('header--visible'); 255 isHidden = false; 256 } else if (isScrollingDown && !isHidden) { 257 // Hide header with smooth transition when scrolling down 258 $header.removeClass('header--visible').addClass('header--hidden'); 259 isHidden = true; 260 } 261 } 262 263 lastScrollTop = scrollTop; 264 } 265 266 // Throttled scroll handler 267 function handleScroll() { 268 if (scrollTimeout) { 269 clearTimeout(scrollTimeout); 270 } 271 scrollTimeout = setTimeout(updateHeaderState, 10); 272 } 273 274 // Initialize on page load 275 $(document).ready(function() { 276 initHeader(); 277 278 // Handle window resize 279 $(window).on('resize', function() { 280 initHeader(); 281 }); 282 283 // Handle scroll events 284 $(window).on('scroll', handleScroll); 285 }); 286 }, 287 }; 288})(jQuery, Drupal);
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.