vendor: 11,161 bytes, lines 1-437
1/*! 2 * headroom.js v0.12.0 - Give your page some headroom. Hide your header until you need it 3 * Copyright (c) 2020 Nick Williams - http://wicky.nillia.ms/headroom.js 4 * License: MIT 5 */ 6 7(function (global, factory) { 8 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 9 typeof define === 'function' && define.amd ? define(factory) : 10 (global = global || self, global.Headroom = factory()); 11}(this, function () { 'use strict'; 12 13 function isBrowser() { 14 return typeof window !== "undefined"; 15 } 16 17 /** 18 * Used to detect browser support for adding an event listener with options 19 * Credit: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener 20 */ 21 function passiveEventsSupported() { 22 var supported = false; 23 24 try { 25 var options = { 26 // eslint-disable-next-line getter-return 27 get passive() { 28 supported = true; 29 } 30 }; 31 window.addEventListener("test", options, options); 32 window.removeEventListener("test", options, options); 33 } catch (err) { 34 supported = false; 35 } 36 37 return supported; 38 } 39 40 function isSupported() { 41 return !!( 42 isBrowser() && 43 function() {}.bind && 44 "classList" in document.documentElement && 45 Object.assign && 46 Object.keys && 47 requestAnimationFrame 48 ); 49 } 50 51 function isDocument(obj) { 52 return obj.nodeType === 9; // Node.DOCUMENT_NODE === 9 53 } 54 55 function isWindow(obj) { 56 // `obj === window` or `obj instanceof Window` is not sufficient, 57 // as the obj may be the window of an iframe. 58 return obj && obj.document && isDocument(obj.document); 59 } 60 61 function windowScroller(win) { 62 var doc = win.document; 63 var body = doc.body; 64 var html = doc.documentElement; 65 66 return { 67 /** 68 * @see http://james.padolsey.com/javascript/get-document-height-cross-browser/ 69 * @return {Number} the scroll height of the document in pixels 70 */ 71 scrollHeight: function() { 72 return Math.max( 73 body.scrollHeight, 74 html.scrollHeight, 75 body.offsetHeight, 76 html.offsetHeight, 77 body.clientHeight, 78 html.clientHeight 79 ); 80 }, 81 82 /** 83 * @see http://andylangton.co.uk/blog/development/get-viewport-size-width-and-height-javascript 84 * @return {Number} the height of the viewport in pixels 85 */ 86 height: function() { 87 return win.innerHeight || html.clientHeight || body.clientHeight; 88 }, 89 90 /** 91 * Gets the Y scroll position 92 * @return {Number} pixels the page has scrolled along the Y-axis 93 */ 94 scrollY: function() { 95 if (win.pageYOffset !== undefined) { 96 return win.pageYOffset; 97 } 98 99 return (html || body.parentNode || body).scrollTop; 100 } 101 }; 102 } 103 104 function elementScroller(element) { 105 return { 106 /** 107 * @return {Number} the scroll height of the element in pixels 108 */ 109 scrollHeight: function() { 110 return Math.max( 111 element.scrollHeight, 112 element.offsetHeight, 113 element.clientHeight 114 ); 115 }, 116 117 /** 118 * @return {Number} the height of the element in pixels 119 */ 120 height: function() { 121 return Math.max(element.offsetHeight, element.clientHeight); 122 }, 123 124 /** 125 * Gets the Y scroll position 126 * @return {Number} pixels the element has scrolled along the Y-axis 127 */ 128 scrollY: function() { 129 return element.scrollTop; 130 } 131 }; 132 } 133 134 function createScroller(element) { 135 return isWindow(element) ? windowScroller(element) : elementScroller(element); 136 } 137 138 /** 139 * @param element EventTarget 140 */ 141 function trackScroll(element, options, callback) { 142 var isPassiveSupported = passiveEventsSupported(); 143 var rafId; 144 var scrolled = false; 145 var scroller = createScroller(element); 146 var lastScrollY = scroller.scrollY(); 147 var details = {}; 148 149 function update() { 150 var scrollY = Math.round(scroller.scrollY()); 151 var height = scroller.height(); 152 var scrollHeight = scroller.scrollHeight(); 153 154 // reuse object for less memory churn 155 details.scrollY = scrollY; 156 details.lastScrollY = lastScrollY; 157 details.direction = scrollY > lastScrollY ? "down" : "up"; 158 details.distance = Math.abs(scrollY - lastScrollY); 159 details.isOutOfBounds = scrollY < 0 || scrollY + height > scrollHeight; 160 details.top = scrollY <= options.offset[details.direction]; 161 details.bottom = scrollY + height >= scrollHeight; 162 details.toleranceExceeded = 163 details.distance > options.tolerance[details.direction]; 164 165 callback(details); 166 167 lastScrollY = scrollY; 168 scrolled = false; 169 } 170 171 function handleScroll() { 172 if (!scrolled) { 173 scrolled = true; 174 rafId = requestAnimationFrame(update); 175 } 176 } 177 178 var eventOptions = isPassiveSupported 179 ? { passive: true, capture: false } 180 : false; 181 182 element.addEventListener("scroll", handleScroll, eventOptions); 183 update(); 184 185 return { 186 destroy: function() { 187 cancelAnimationFrame(rafId); 188 element.removeEventListener("scroll", handleScroll, eventOptions); 189 } 190 }; 191 } 192 193 function normalizeUpDown(t) { 194 return t === Object(t) ? t : { down: t, up: t }; 195 } 196 197 /** 198 * UI enhancement for fixed headers. 199 * Hides header when scrolling down 200 * Shows header when scrolling up 201 * @constructor 202 * @param {DOMElement} elem the header element 203 * @param {Object} options options for the widget 204 */ 205 function Headroom(elem, options) { 206 options = options || {}; 207 Object.assign(this, Headroom.options, options); 208 this.classes = Object.assign({}, Headroom.options.classes, options.classes); 209 210 this.elem = elem; 211 this.tolerance = normalizeUpDown(this.tolerance); 212 this.offset = normalizeUpDown(this.offset); 213 this.initialised = false; 214 this.frozen = false; 215 } 216 Headroom.prototype = { 217 constructor: Headroom, 218 219 /** 220 * Start listening to scrolling 221 * @public 222 */ 223 init: function() { 224 if (Headroom.cutsTheMustard && !this.initialised) { 225 this.addClass("initial"); 226 this.initialised = true; 227 228 // defer event registration to handle browser 229 // potentially restoring previous scroll position 230 setTimeout( 231 function(self) { 232 self.scrollTracker = trackScroll( 233 self.scroller, 234 { offset: self.offset, tolerance: self.tolerance }, 235 self.update.bind(self) 236 ); 237 }, 238 100, 239 this 240 ); 241 } 242 243 return this; 244 }, 245 246 /** 247 * Destroy the widget, clearing up after itself 248 * @public 249 */ 250 destroy: function() { 251 this.initialised = false; 252 Object.keys(this.classes).forEach(this.removeClass, this); 253 this.scrollTracker.destroy(); 254 }, 255 256 /** 257 * Unpin the element 258 * @public 259 */ 260 unpin: function() { 261 if (this.hasClass("pinned") || !this.hasClass("unpinned")) { 262 this.addClass("unpinned"); 263 this.removeClass("pinned"); 264 265 if (this.onUnpin) { 266 this.onUnpin.call(this); 267 } 268 } 269 }, 270 271 /** 272 * Pin the element 273 * @public 274 */ 275 pin: function() { 276 if (this.hasClass("unpinned")) { 277 this.addClass("pinned"); 278 this.removeClass("unpinned"); 279 280 if (this.onPin) { 281 this.onPin.call(this); 282 } 283 } 284 }, 285 286 /** 287 * Freezes the current state of the widget 288 * @public 289 */ 290 freeze: function() { 291 this.frozen = true; 292 this.addClass("frozen"); 293 }, 294 295 /** 296 * Re-enables the default behaviour of the widget 297 * @public 298 */ 299 unfreeze: function() { 300 this.frozen = false; 301 this.removeClass("frozen"); 302 }, 303 304 top: function() { 305 if (!this.hasClass("top")) { 306 this.addClass("top"); 307 this.removeClass("notTop"); 308 309 if (this.onTop) { 310 this.onTop.call(this); 311 } 312 } 313 }, 314 315 notTop: function() { 316 if (!this.hasClass("notTop")) { 317 this.addClass("notTop"); 318 this.removeClass("top"); 319 320 if (this.onNotTop) { 321 this.onNotTop.call(this); 322 } 323 } 324 }, 325 326 bottom: function() { 327 if (!this.hasClass("bottom")) { 328 this.addClass("bottom"); 329 this.removeClass("notBottom"); 330 331 if (this.onBottom) { 332 this.onBottom.call(this); 333 } 334 } 335 }, 336 337 notBottom: function() { 338 if (!this.hasClass("notBottom")) { 339 this.addClass("notBottom"); 340 this.removeClass("bottom"); 341 342 if (this.onNotBottom) { 343 this.onNotBottom.call(this); 344 } 345 } 346 }, 347 348 shouldUnpin: function(details) { 349 var scrollingDown = details.direction === "down"; 350 351 return scrollingDown && !details.top && details.toleranceExceeded; 352 }, 353 354 shouldPin: function(details) { 355 var scrollingUp = details.direction === "up"; 356 357 return (scrollingUp && details.toleranceExceeded) || details.top; 358 }, 359 360 addClass: function(className) { 361 this.elem.classList.add.apply( 362 this.elem.classList, 363 this.classes[className].split(" ") 364 ); 365 }, 366 367 removeClass: function(className) { 368 this.elem.classList.remove.apply( 369 this.elem.classList, 370 this.classes[className].split(" ") 371 ); 372 }, 373 374 hasClass: function(className) { 375 return this.classes[className].split(" ").every(function(cls) { 376 return this.classList.contains(cls); 377 }, this.elem); 378 }, 379 380 update: function(details) { 381 if (details.isOutOfBounds) { 382 // Ignore bouncy scrolling in OSX 383 return; 384 } 385 386 if (this.frozen === true) { 387 return; 388 } 389 390 if (details.top) { 391 this.top(); 392 } else { 393 this.notTop(); 394 } 395 396 if (details.bottom) { 397 this.bottom(); 398 } else { 399 this.notBottom(); 400 } 401 402 if (this.shouldUnpin(details)) { 403 this.unpin(); 404 } else if (this.shouldPin(details)) { 405 this.pin(); 406 } 407 } 408 }; 409 410 /** 411 * Default options 412 * @type {Object} 413 */ 414 Headroom.options = { 415 tolerance: { 416 up: 0, 417 down: 0 418 }, 419 offset: 0, 420 scroller: isBrowser() ? window : null, 421 classes: { 422 frozen: "headroom--frozen", 423 pinned: "headroom--pinned", 424 unpinned: "headroom--unpinned", 425 top: "headroom--top", 426 notTop: "headroom--not-top", 427 bottom: "headroom--bottom", 428 notBottom: "headroom--not-bottom", 429 initial: "headroom" 430 } 431 }; 432 433 Headroom.cutsTheMustard = isSupported(); 434 435 return Headroom; 436 437}));
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.