1((Drupal, once) => { 2 'use strict'; 3 4 Drupal.behaviors.materialBaseFunctions = { 5 attach: (context, settings) => { 6 7 // Header background image and navbar effects. 8 const header = document.getElementById('header'); 9 const headerHeight = header.offsetHeight; 10 const headerBg = document.querySelector('.navbar-fixed #header-bg'); 11 const headerContent = document.querySelector('.navbar-fixed #header-content'); 12 const navbar = document.querySelector('.navbar-fixed #navbar'); 13 const headerBgHeight = headerHeight - 192; 14 15 const handleScroll = () => { 16 const scrollPercent = (headerBgHeight - window.scrollY) / headerBgHeight; 17 if (scrollPercent >= 0) { 18 headerBg.style.opacity = scrollPercent; 19 headerContent.style.opacity = scrollPercent; 20 } 21 22 if (window.scrollY > headerHeight - 128) { 23 navbar.classList.add('navbar-bg'); 24 } else { 25 navbar.classList.remove('navbar-bg'); 26 } 27 28 if (window.scrollY > headerHeight - 64) { 29 navbar.classList.add('navbar-shadow'); 30 } else { 31 navbar.classList.remove('navbar-shadow'); 32 } 33 } 34 35 // Drawer. 36 const handleDrawerOpen = (e) => { 37 e.stopPropagation(); 38 e.preventDefault(); 39 document.body.classList.add('drawer-open'); 40 } 41 42 const handleDrawerClose = (e) => { 43 e.stopPropagation(); 44 e.preventDefault(); 45 document.body.classList.remove('drawer-open'); 46 } 47 48 let touchstartX = 0; 49 let touchendX = 0; 50 51 const handleTouchStart = (event) => { 52 touchstartX = event.changedTouches[0].screenX; 53 } 54 55 const handleTouchEnd = (event) => { 56 touchendX = event.changedTouches[0].screenX; 57 if (touchendX < touchstartX && touchendX - touchstartX < -50) { 58 handleDrawerClose(event); 59 } 60 } 61 62 // Smooth scroll for anchor links. 63 const getTopOffset = () => { 64 let fixedHeight = 24; 65 const bodyClasses = document.body.classList; 66 67 if (bodyClasses.contains('navbar-fixed')) { 68 fixedHeight += 64; 69 } 70 71 if (bodyClasses.contains('toolbar-vertical')) { 72 fixedHeight += 40; 73 } 74 75 if (bodyClasses.contains('toolbar-horizontal')) { 76 fixedHeight += 40; 77 78 if (bodyClasses.contains('toolbar-tray-open')) { 79 fixedHeight += 40; 80 } 81 } 82 83 return fixedHeight; 84 }; 85 86 const handleSmoothScroll = (targetId, e = null) => { 87 if (e) { 88 e.preventDefault(); 89 } 90 91 const target = document.querySelector(targetId); 92 if (!target) { 93 return; 94 } 95 96 const targetOffsetTop = target.getBoundingClientRect().top + window.scrollY; 97 window.scrollTo({ 98 top: targetOffsetTop - getTopOffset(), 99 behavior: 'smooth' 100 }); 101 if (history.pushState) { 102 history.pushState(null, null, targetId); 103 } else { 104 window.location.hash = targetId; 105 } 106 }; 107 108 // Collapsible panels. 109 const handleCollapsiblePanel = (toggle) => { 110 const target = document.querySelector(`#${toggle.dataset.target}`); 111 if (!target) { 112 return; 113 } 114 115 // Updating classes explicitly instead of toggle to reduce wrong usage. 116 if (window.getComputedStyle(target).display === 'none') { 117 slideDown(target, 200); 118 target.classList.add('expanded'); 119 toggle.classList.add('expanded'); 120 } 121 else { 122 slideUp(target, 200); 123 target.classList.remove('expanded'); 124 toggle.classList.remove('expanded'); 125 } 126 }; 127 128 // Dropdown menus. 129 const handleMenuDropdown = (e, toggle) => { 130 e.stopPropagation(); 131 e.preventDefault(); 132 toggle.classList.remove('tooltip-expanded'); 133 const tooltip = toggle.querySelector('.tooltip'); 134 if (tooltip) { 135 tooltip.classList.remove('tooltip-expanded'); 136 } 137 const targetId = `#${toggle.dataset.target}`; 138 const target = document.querySelector(targetId); 139 document.querySelectorAll(`.menu-dropdown:not(${targetId})`).forEach(dropdown => { 140 slideUp(dropdown, 200); 141 dropdown.classList.remove('expanded'); 142 }); 143 document.querySelectorAll(`.menu-dropdown-toggle:not([data-target="${toggle.dataset.target}"])`).forEach(toggle => { 144 toggle.classList.remove('dropdown-expanded'); 145 }); 146 if (!target) { 147 return; 148 } 149 if (window.getComputedStyle(target).display === 'none') { 150 slideDown(target, 200); 151 target.classList.add('expanded'); 152 toggle.classList.add('dropdown-expanded'); 153 } 154 else { 155 slideUp(target, 200); 156 target.classList.remove('expanded'); 157 toggle.classList.remove('dropdown-expanded'); 158 } 159 } 160 161 // Handling boxed textarea filled state. 162 const handleBoxedTextareaFilledState = (element, textarea) => { 163 if (textarea.value !== '') { 164 element.classList.add('filled'); 165 } 166 else { 167 element.classList.remove('filled'); 168 } 169 } 170 171 // Handling filled state for boxed textarea with CodeMirror editor. 172 const handleBoxedTextareaCodemirrorFilledState = (element, editor) => { 173 if (editor.getValue().trim()) { 174 element.classList.add('filled'); 175 } else { 176 element.classList.remove('filled'); 177 } 178 }; 179 180 // Global behaviors of the page. 181 once('materialBaseFunctions', 'html').forEach((element) => { 182 183 // Header background image and navbar effects. 184 handleScroll(); 185 document.addEventListener('scroll', handleScroll); 186 187 // Drawer. 188 const drawer = document.getElementById('drawer');
189 const drawerOverlay = document.getElementById('drawer-overlay'); 190 const navbarMenuToggle = document.getElementById('navbar-menu-toggle'); 191 const drawerMenuClose = document.getElementById('drawer-menu-close'); 192 193 navbarMenuToggle.addEventListener('click', handleDrawerOpen); 194 195 drawerOverlay.addEventListener('click', handleDrawerClose); 196 197 drawerMenuClose.addEventListener('click', handleDrawerClose); 198 199 drawer.addEventListener('touchstart', handleTouchStart); 200 drawer.addEventListener('touchend', handleTouchEnd); 201 202 drawerOverlay.addEventListener('touchstart', handleTouchStart); 203 drawerOverlay.addEventListener('touchend', handleTouchEnd); 204 205 // Smooth scroll for anchor links on page load. 206 if (window.location.hash) { 207 // Small timeout needs for applying body classes. 208 setTimeout(() => handleSmoothScroll(window.location.hash), 100); 209 } 210 211 // Collapsing dropdowns on page click. 212 const menuDropdownToggles = document.querySelectorAll('.menu-dropdown-toggle'); 213 const menuDropdowns = document.querySelectorAll('.menu-dropdown'); 214 215 document.addEventListener('click', () => { 216 menuDropdowns.forEach(dropdown => { 217 slideUp(dropdown, 200); 218 dropdown.classList.remove('expanded'); 219 }); 220 menuDropdownToggles.forEach(toggle => { 221 toggle.classList.remove('dropdown-expanded'); 222 }); 223 }); 224 225 }); 226 227 // Attaching ripple effect to buttons and surfaces. 228 once('rippleRipple', '.ripple', context).forEach(element => new Ripple(element)); 229 once('btnRipple', '.btn', context).forEach(element => new Ripple(element)); 230 once('fabRipple', '.fab', context).forEach(element => new Ripple(element)); 231 232 // Smooth scroll for anchor links. 233 once('anchorLinkClick', 'a[href^="#"]:not([href="#"])', context).forEach(element => { 234 element.addEventListener('click', e => handleSmoothScroll(element.hash, e)); 235 }); 236 237 once('fullPathWithAnchorLinkClick', `a[href^="${window.location.pathname}#"]`, context).forEach(element => { 238 element.addEventListener('click', e => handleSmoothScroll(element.hash, e)); 239 }); 240 241 // Collapsible panels. 242 once('collapsiblePanelToggleClick', '.collapsible-toggle', context).forEach(element => { 243 element.addEventListener('click', () => handleCollapsiblePanel(element)); 244 }); 245 246 // Tooltips. 247 once('tooltipBehavior', '.tooltip', context).forEach(element => { 248 element.addEventListener('mouseenter', () => element.classList.add('tooltip-expanded')); 249 element.addEventListener('mouseleave', () => element.classList.remove('tooltip-expanded')); 250 }); 251 252 // Menu Dropdown. 253 once('menuDropdownToggleClick', '.menu-dropdown-toggle', context).forEach(element => { 254 element.addEventListener('click', e => handleMenuDropdown(e, element)); 255 }); 256 257 // Handling common form item focus state. 258 once('formItemFocusState', '.form-item > input', context).forEach(element => { 259 element.addEventListener('focus', () => element.parentElement.classList.add('focused')); 260 element.addEventListener('blur', () => element.parentElement.classList.remove('focused')); 261 }); 262 263 // Handling textarea form item focus state. 264 once('formItemTextareaFocusState', '.form-item.form-type-textarea', context).forEach(element => { 265 const textarea = element.querySelector('textarea'); 266 textarea.addEventListener('focus', () => element.classList.add('focused')); 267 textarea.addEventListener('blur', () => element.classList.remove('focused')); 268 }); 269 270 // Handling boxed textarea state. 271 once('formItemBoxedTextareaState', '.form-item.textarea-boxed', context).forEach(element => { 272 // Handling empty/filled state for regular field. 273 const textarea = element.querySelector('textarea'); 274 // Initial state. 275 handleBoxedTextareaFilledState(element, textarea); 276 // Updating state after input. 277 textarea.addEventListener('keyup', () => handleBoxedTextareaFilledState(element, textarea)); 278 279 // Handling focused and filled state for boxed textarea with CodeMirror editor. 280 let editorElement; 281 setTimeout(() => { 282 editorElement = element.querySelector('.CodeMirror'); 283 if (!editorElement) { 284 return; 285 } 286 287 const editor = editorElement.CodeMirror; 288 // Initial state. 289 handleBoxedTextareaCodemirrorFilledState(element, editor); 290 // Update state on events. 291 editor.on('focus', () => element.classList.add('focused')); 292 editor.on('blur', () => element.classList.remove('focused')); 293 editor.on('change', () => handleBoxedTextareaCodemirrorFilledState(element, editor)); 294 }, 100); 295 }); 296 297 } 298 }; 299}) (Drupal, once);
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.