PageSourceSearch

https://theaxeband.com/assets/js/headroom.js

js theaxeband.com collected 2026-10-08 20:48:23 UTC 13,273 bytes, 465 lines download raw bytes

1/*!
2 * headroom.js v0.9.4 - Give your page some headroom. Hide your header until you need it
3 * Copyright (c) 2017 Nick Williams - http://wicky.nillia.ms/headroom.js
4 * License: MIT
5 */
6
7(function(root, factory) {
8  'use strict';
9
10  if (typeof define === 'function' && define.amd) {
11    // AMD. Register as an anonymous module.
12    define([], factory);
13  } else if (typeof exports === 'object') {
14    // COMMONJS
15    module.exports = factory();
16  } else {
17    // BROWSER
18    root.Headroom = factory();
19  }
20}(this, function() {
21  'use strict';
22
23  /* exported features */
24
25  var features = {
26    bind: !!(function() {}.bind),
27    classList: 'classList' in document.documentElement,
28    rAF: !!(window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame)
29  };
30  window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame;
31
32  /**
33   * Handles debouncing of events via requestAnimationFrame
34   * @see http://www.html5rocks.com/en/tutorials/speed/animations/
35   * @param {Function} callback The callback to handle whichever event
36   */
37  function Debouncer(callback) {
38    this.callback = callback;
39    this.ticking = false;
40  }
41  Debouncer.prototype = {
42    constructor: Debouncer,
43
44    /**
45     * dispatches the event to the supplied callback
46     * @private
47     */
48    update: function() {
49      this.callback && this.callback();
50      this.ticking = false;
51    },
52
53    /**
54     * ensures events don't get stacked
55     * @private
56     */
57    requestTick: function() {
58      if (!this.ticking) {
59        requestAnimationFrame(this.rafCallback || (this.rafCallback = this.update.bind(this)));
60        this.ticking = true;
61      }
62    },
63
64    /**
65     * Attach this as the event listeners
66     */
67    handleEvent: function() {
68      this.requestTick();
69    }
70  };
71  /**
72   * Check if object is part of the DOM
73   * @constructor
74   * @param {Object} obj element to check
75   */
76  function isDOMElement(obj) {
77    return obj && typeof window !== 'undefined' && (obj === window || obj.nodeType);
78  }
79
80  /**
81   * Helper function for extending objects
82   */
83  function extend(object /*, objectN ... */ ) {
84    if (arguments.length <= 0) {
85      throw new Error('Missing arguments in extend function');
86    }
87
88    var result = object || {},
89      key,
90      i;
91
92    for (i = 1; i < arguments.length; i++) {
93      var replacement = arguments[i] || {};
94
95      for (key in replacement) {
96        // Recurse into object except if the object is a DOM element
97        if (typeof result[key] === 'object' && !isDOMElement(result[key])) {
98          result[key] = extend(result[key], replacement[key]);
99        } else {
100          result[key] = result[key] || replacement[key];
101        }
102      }
103    }
104
105    return result;
106  }
107
108  /**
109   * Helper function for normalizing tolerance option to object format
110   */
111  function normalizeTolerance(t) {
112    return t === Object(t) ? t : {
113      down: t,
114      up: t
115    };
116  }
117
118  /**
119   * UI enhancement for fixed headers.
120   * Hides header when scrolling down
121   * Shows header when scrolling up
122   * @constructor
123   * @param {DOMElement} elem the header element
124   * @param {Object} options options for the widget
125   */
126  function Headroom(elem, options) {
127    options = extend(options, Headroom.options);
128
129    this.lastKnownScrollY = 0;
130    this.elem = elem;
131    this.tolerance = normalizeTolerance(options.tolerance);
132    this.classes = options.classes;
133    this.offset = options.offset;
134    this.scroller = options.scroller;
135    this.initialised = false;
136    this.onPin = options.onPin;
137    this.onUnpin = options.onUnpin;
138    this.onTop = options.onTop;
139    this.onNotTop = options.onNotTop;
140    this.onBottom = options.onBottom;
141    this.onNotBottom = options.onNotBottom;
142  }
143  Headroom.prototype = {
144    constructor: Headroom,
145
146    /**
147     * Initialises the widget
148     */
149    init: function() {
150      if (!Headroom.cutsTheMustard) {
151        return;
152      }
153
154      this.debouncer = new Debouncer(this.update.bind(this));
155      this.elem.classList.add(this.classes.initial);
156
157      // defer event registration to handle browser
158      // potentially restoring previous scroll position
159      setTimeout(this.attachEvent.bind(this), 100);
160
161      return this;
162    },
163
164    /**
165     * Unattaches events and removes any classes that were added
166     */
167    destroy: function() {
168      var classes = this.classes;
169
170      this.initialised = false;
171
172      for (var key in classes) {
173        if (classes.hasOwnProperty(key)) {
174          this.elem.classList.remove(classes[key]);
175        }
176      }
177
178      this.scroller.removeEventListener('scroll', this.debouncer, false);
179    },
180
181    /**
182     * Attaches the scroll event
183     * @private
184     */
185    attachEvent: function() {
186      if (!this.initialised) {
187        this.lastKnownScrollY = this.getScrollY();
188        this.initialised = true;
189        this.scroller.addEventListener('scroll', this.debouncer, false);
190
191        this.debouncer.handleEvent();
192      }
193    },
194
195    /**
196     * Unpins the header if it's currently pinned
197     */
198    unpin: function() {
199      var classList = this.elem.classList,
200        classes = this.classes;
201
202      if (classList.contains(classes.pinned) || !classList.contains(classes.unpinned)) {
203        classList.add(classes.unpinned);
204        classList.remove(classes.pinned);
205        this.onUnpin && this.onUnpin.call(this);
206      }
207    },
208
209    /**
210     * Pins the header if it's currently unpinned
211     */
212    pin: function() {
213      var classList = this.elem.classList,
214        classes = this.classes;
215
216      if (classList.contains(classes.unpinned)) {
217        classList.remove(classes.unpinned);
218        classList.add(classes.pinned);
219        this.onPin && this.onPin.call(this);
220      }
221    },
222
223    /**
224     * Handles the top states
225     */
226    top: function() {
227      var classList = this.elem.classList,
228        classes = this.classes;
229
230      if (!classList.contains(classes.top)) {
231        classList.add(classes.top);
232        classList.remove(classes.notTop);
233        this.onTop && this.onTop.call(this);
234      }
235    },
236
237    /**
238     * Handles the not top state
239     */
240    notTop: function() {
241      var classList = this.elem.classList,
242        classes = this.classes;
243
244      if (!classList.contains(classes.notTop)) {
245        classList.add(classes.notTop);
246        classList.remove(classes.top);
247        this.onNotTop && this.onNotTop.call(this);
248      }
249    },
250
251    bottom: function() {
252      var classList = this.elem.classList,
253        classes = this.classes;
254
255      if (!classList.contains(classes.bottom)) {
256        classList.add(classes.bottom);
257        classList.remove(classes.notBottom);
258        this.onBottom && this.onBottom.call(this);
259      }
260    },
261
262    /**
263     * Handles the not top state
264     */
265    notBottom: function() {
266      var classList = this.elem.classList,
267        classes = this.classes;
268
269      if (!classList.contains(classes.notBottom)) {
270        classList.add(classes.notBottom);
271        classList.remove(classes.bottom);
272        this.onNotBottom && this.onNotBottom.call(this);
273      }
274    },
275
276    /**
277     * Gets the Y scroll position
278     * @see https://developer.mozilla.org/en-US/docs/Web/API/Window.scrollY
279     * @return {Number} pixels the page has scrolled along the Y-axis
280     */
281    getScrollY: function() {
282      return (this.scroller.pageYOffset !== undefined) ?
283        this.scroller.pageYOffset :
284        (this.scroller.scrollTop !== undefined) ?
285        this.scroller.scrollTop :
286        (document.documentElement || document.body.parentNode || document.body).scrollTop;
287    },
288
289    /**
290     * Gets the height of the viewport
291     * @see http://andylangton.co.uk/blog/development/get-viewport-size-width-and-height-javascript
292     * @return {int} the height of the viewport in pixels
293     */
294    getViewportHeight: function() {
295      return window.innerHeight ||
296        document.documentElement.clientHeight ||
297        document.body.clientHeight;
298    },
299
300    /**
301     * Gets the physical height of the DOM element
302     * @param  {Object}  elm the element to calculate the physical height of which
303     * @return {int}     the physical height of the element in pixels
304     */
305    getElementPhysicalHeight: function(elm) {
306      return Math.max(
307        elm.offsetHeight,
308        elm.clientHeight
309      );
310    },
311
312    /**
313     * Gets the physical height of the scroller element
314     * @return {int} the physical height of the scroller element in pixels
315     */
316    getScrollerPhysicalHeight: function() {
317      return (this.scroller === window || this.scroller === document.body) ?
318        this.getViewportHeight() :
319        this.getElementPhysicalHeight(this.scroller);
320    },
321
322    /**
323     * Gets the height of the document
324     * @see http://james.padolsey.com/javascript/get-document-height-cross-browser/
325     * @return {int} the height of the document in pixels
326     */
327    getDocumentHeight: function() {
328      var body = document.body,
329        documentElement = document.documentElement;
330
331      return Math.max(
332        body.scrollHeight, documentElement.scrollHeight,
333        body.offsetHeight, documentElement.offsetHeight,
334        body.clientHeight, documentElement.clientHeight
335      );
336    },
337
338    /**
339     * Gets the height of the DOM element
340     * @param  {Object}  elm the element to calculate the height of which
341     * @return {int}     the height of the element in pixels
342     */
343    getElementHeight: function(elm) {
344      return Math.max(
345        elm.scrollHeight,
346        elm.offsetHeight,
347        elm.clientHeight
348      );
349    },
350
351    /**
352     * Gets the height of the scroller element
353     * @return {int} the height of the scroller element in pixels
354     */
355    getScrollerHeight: function() {
356      return (this.scroller === window || this.scroller === document.body) ?
357        this.getDocumentHeight() :
358        this.getElementHeight(this.scroller);
359    },
360
361    /**
362     * determines if the scroll position is outside of document boundaries
363     * @param  {int}  currentScrollY the current y scroll position
364     * @return {bool} true if out of bounds, false otherwise
365     */
366    isOutOfBounds: function(currentScrollY) {
367      var pastTop = currentScrollY < 0,
368        pastBottom = currentScrollY + this.getScrollerPhysicalHeight() > this.getScrollerHeight();
369
370      return pastTop || pastBottom;
371    },
372
373    /**
374     * determines if the tolerance has been exceeded
375     * @param  {int} currentScrollY the current scroll y position
376     * @return {bool} true if tolerance exceeded, false otherwise
377     */
378    toleranceExceeded: function(currentScrollY, direction) {
379      return Math.abs(currentScrollY - this.lastKnownScrollY) >= this.tolerance[direction];
380    },
381
382    /**
383     * determine if it is appropriate to unpin
384     * @param  {int} currentScrollY the current y scroll position
385     * @param  {bool} toleranceExceeded has the tolerance been exceeded?
386     * @return {bool} true if should unpin, false otherwise
387     */
388    shouldUnpin: function(currentScrollY, toleranceExceeded) {
389      var scrollingDown = currentScrollY > this.lastKnownScrollY,
390        pastOffset = currentScrollY >= this.offset;
391
392      return scrollingDown && pastOffset && toleranceExceeded;
393    },
394
395    /**
396     * determine if it is appropriate to pin
397     * @param  {int} currentScrollY the current y scroll position
398     * @param  {bool} toleranceExceeded has the tolerance been exceeded?
399     * @return {bool} true if should pin, false otherwise
400     */
401    shouldPin: function(currentScrollY, toleranceExceeded) {
402      var scrollingUp = currentScrollY < this.lastKnownScrollY,
403        pastOffset = currentScrollY <= this.offset;
404
405      return (scrollingUp && toleranceExceeded) || pastOffset;
406    },
407
408    /**
409     * Handles updating the state of the widget
410     */
411    update: function() {
412      var currentScrollY = this.getScrollY(),
413        scrollDirection = currentScrollY > this.lastKnownScrollY ? 'down' : 'up',
414        toleranceExceeded = this.toleranceExceeded(currentScrollY, scrollDirection);
415
416      if (this.isOutOfBounds(currentScrollY)) { // Ignore bouncy scrolling in OSX
417        return;
418      }
419
420      if (currentScrollY <= this.offset) {
421        this.top();
422      } else {
423        this.notTop();
424      }
425
426      if (currentScrollY + this.getViewportHeight() >= this.getScrollerHeight()) {
427        this.bottom();
428      } else {
429        this.notBottom();
430      }
431
432      if (this.shouldUnpin(currentScrollY, toleranceExceeded)) {
433        this.unpin();
434      } else if (this.shouldPin(currentScrollY, toleranceExceeded)) {
435        this.pin();
436      }
437
438      this.lastKnownScrollY = currentScrollY;
439    }
440  };
441  /**
442   * Default options
443   * @type {Object}
444   */
445  Headroom.options = {
446    tolerance: {
447      up: 0,
448      down: 0
449    },
450    offset: 0,
451    scroller: window,
452    classes: {
453      pinned: 'headroom--pinned',
454      unpinned: 'headroom--unpinned',
455      top: 'headroom--top',
456      notTop: 'headroom--not-top',
457      bottom: 'headroom--bottom',
458      notBottom: 'headroom--not-bottom',
459      initial: 'headroom'
460    }
461  };
462  Headroom.cutsTheMustard = typeof features !== 'undefined' && features.rAF && features.bind && features.classList;
463
464  return Headroom;
465}));

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.