mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
added headroom scrolling
This commit is contained in:
parent
feffdb0bd4
commit
19fd673e46
12 changed files with 470 additions and 28 deletions
|
@ -204,6 +204,16 @@
|
||||||
color: #ddd;
|
color: #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*.cardBox:not(.visualCardBox) .outerCardFooter .cardText {
|
||||||
|
font-size: 14px;
|
||||||
|
background: rgba(51, 51, 51,.6);
|
||||||
|
padding: 5px;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
.cardBox:not(.visualCardBox) .outerCardFooter .cardText:last-child {
|
||||||
|
margin-bottom: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
@media all and (max-width: 600px) {
|
@media all and (max-width: 600px) {
|
||||||
|
|
||||||
.packageReviewText {
|
.packageReviewText {
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
.backdropContainer {
|
.backdropContainer {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 50px;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
@ -773,7 +773,7 @@ span.itemCommunityRating:not(:empty) + .userDataIcons {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media all and (max-height: 600px), (max-width: 600px) {
|
@media all and (max-height: 540px), (max-width: 540px) {
|
||||||
|
|
||||||
.itemBackdrop:not(.noBackdrop) {
|
.itemBackdrop:not(.noBackdrop) {
|
||||||
height: 280px;
|
height: 280px;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.libraryPage:not(.metadataEditorPage):not(.noSecondaryNavPage) {
|
.libraryPage:not(.metadataEditorPage):not(.noSecondaryNavPage) {
|
||||||
padding-top: 98px !important;
|
padding-top: 99px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.libraryMenuDivider {
|
.libraryMenuDivider {
|
||||||
|
@ -124,7 +124,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.libraryViewNav {
|
.libraryViewNav {
|
||||||
height: 48px;
|
height: 49px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
@ -132,7 +132,7 @@
|
||||||
top: 50px;
|
top: 50px;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 15px;
|
font-size: 14px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
padding: 0 0 0;
|
padding: 0 0 0;
|
||||||
overflow-x: scroll;
|
overflow-x: scroll;
|
||||||
|
@ -142,7 +142,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.viewMenuBar {
|
.viewMenuBar {
|
||||||
background-color: #000;
|
background-color: #111;
|
||||||
}
|
}
|
||||||
|
|
||||||
.libraryViewNav {
|
.libraryViewNav {
|
||||||
|
@ -224,12 +224,12 @@
|
||||||
.libraryViewNav a {
|
.libraryViewNav a {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 12px 0 9px;
|
padding: 12px 0 9px;
|
||||||
color: #ddd !important;
|
color: rgba(255,255,255,.8) !important;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
margin: 0 0;
|
margin: 0 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
border-bottom: 4px solid transparent;
|
border-bottom: 5px solid transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.libraryViewNav a:not(.ui-btn-active):hover {
|
.libraryViewNav a:not(.ui-btn-active):hover {
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
|
.tvGuideHeader.headroom--unpinned {
|
||||||
|
-webkit-transform: translateY(-98px);
|
||||||
|
transform: translateY(-98px);
|
||||||
|
}
|
||||||
|
|
||||||
.tvProgramSectionHeader {
|
.tvProgramSectionHeader {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
@ -90,7 +94,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.tvProgram:hover, .tvProgram:hover .tvProgramInfo {
|
.tvProgram:hover, .tvProgram:hover .tvProgramInfo {
|
||||||
background-color: #4d90fe;
|
background-color: #444;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timerCircle {
|
.timerCircle {
|
||||||
|
@ -215,7 +219,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.channelHeaderCell:hover {
|
.channelHeaderCell:hover {
|
||||||
background-color: #38c;
|
background-color: #444;
|
||||||
}
|
}
|
||||||
|
|
||||||
.channelList {
|
.channelList {
|
||||||
|
@ -254,7 +258,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.programCellInner:hover {
|
.programCellInner:hover {
|
||||||
background-color: #38c;
|
background-color: #444;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeslotCellInner {
|
.timeslotCellInner {
|
||||||
|
|
|
@ -162,6 +162,30 @@ h1 a:hover {
|
||||||
margin: -10px 0 0 -10px;
|
margin: -10px 0 0 -10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note: I have omitted any vendor-prefixes for clarity.
|
||||||
|
* Adding them is left as an exercise for the reader.
|
||||||
|
*/
|
||||||
|
.headroom {
|
||||||
|
-webkit-transition: transform 200ms linear;
|
||||||
|
transition: transform 200ms linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headroom--pinned {
|
||||||
|
-webkit-transform: translateY(0%);
|
||||||
|
transform: translateY(0%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.headroom--unpinned {
|
||||||
|
-webkit-transform: translateY(-100%);
|
||||||
|
transform: translateY(-100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.libraryViewNav.headroom--unpinned {
|
||||||
|
-webkit-transform: translateY(-200%);
|
||||||
|
transform: translateY(-200%);
|
||||||
|
}
|
||||||
|
|
||||||
.largePanel {
|
.largePanel {
|
||||||
width: 270px;
|
width: 270px;
|
||||||
}
|
}
|
||||||
|
@ -597,7 +621,7 @@ h1 .imageLink {
|
||||||
z-index: 1097;
|
z-index: 1097;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border: 0 !important;
|
border: 0 !important;
|
||||||
background-color: #1a1a1a;
|
background-color: rgba(26,26,26,.9);
|
||||||
}
|
}
|
||||||
|
|
||||||
.footerNotification {
|
.footerNotification {
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
<div data-role="content" style="padding-top: 5px;padding-left:0!important;padding-right:0!important;">
|
<div data-role="content" style="padding-top: 5px;padding-left:0!important;padding-right:0!important;">
|
||||||
|
|
||||||
<div class="tvGuide">
|
<div class="tvGuide">
|
||||||
<div style="white-space: nowrap;position:fixed;top:98px;left:0;z-index:100; max-width: 100%;">
|
<div style="white-space: nowrap;position:fixed;top:98px;left:0;z-index:100; max-width: 100%;" class="tvGuideHeader">
|
||||||
<div class="channelTimeslotHeader">
|
<div class="channelTimeslotHeader">
|
||||||
<div class="timeslotHeaderInner">
|
<div class="timeslotHeaderInner">
|
||||||
<a href="#popupConfig" data-rel="popup" class="accentButton" style="display:block;padding-left:5px;"><i class="fa fa-calendar"></i><span class="currentDate"></span></a>
|
<a href="#popupConfig" data-rel="popup" class="accentButton" style="display:block;padding-left:5px;"><i class="fa fa-calendar"></i><span class="currentDate"></span></a>
|
||||||
|
|
|
@ -99,12 +99,6 @@
|
||||||
|
|
||||||
function enabled() {
|
function enabled() {
|
||||||
|
|
||||||
// Gets real messy and jumps around the page when scrolling
|
|
||||||
// Can be reviewed later.
|
|
||||||
if ($.browser.msie) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var userId = Dashboard.getCurrentUserId();
|
var userId = Dashboard.getCurrentUserId();
|
||||||
|
|
||||||
var val = store.getItem('enableBackdrops-' + userId);
|
var val = store.getItem('enableBackdrops-' + userId);
|
||||||
|
|
|
@ -90,6 +90,13 @@
|
||||||
$('.libraryMenuButton').createHoverTouch().on('hovertouch', showLibraryMenu);
|
$('.libraryMenuButton').createHoverTouch().on('hovertouch', showLibraryMenu);
|
||||||
$('.dashboardMenuButton').createHoverTouch().on('hovertouch', showDashboardMenu);
|
$('.dashboardMenuButton').createHoverTouch().on('hovertouch', showDashboardMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// grab an element
|
||||||
|
var viewMenuBar = document.getElementsByClassName("viewMenuBar")[0];
|
||||||
|
// construct an instance of Headroom, passing the element
|
||||||
|
var headroom = new Headroom(viewMenuBar);
|
||||||
|
// initialise
|
||||||
|
headroom.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getItemHref(item, context) {
|
function getItemHref(item, context) {
|
||||||
|
@ -534,6 +541,15 @@
|
||||||
// Scroll back up so in case vertical scroll was messed with
|
// Scroll back up so in case vertical scroll was messed with
|
||||||
$(document).scrollTop(0);
|
$(document).scrollTop(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$('.libraryViewNav', page).each(function() {
|
||||||
|
|
||||||
|
// construct an instance of Headroom, passing the element
|
||||||
|
var headroom = new Headroom(this);
|
||||||
|
// initialise
|
||||||
|
headroom.init();
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function initializeApiClient(apiClient) {
|
function initializeApiClient(apiClient) {
|
||||||
|
|
|
@ -419,6 +419,15 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$('.tvGuideHeader', page).each(function () {
|
||||||
|
|
||||||
|
// construct an instance of Headroom, passing the element
|
||||||
|
var headroom = new Headroom(this);
|
||||||
|
// initialise
|
||||||
|
headroom.init();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
}).on('pageshow', "#liveTvGuidePage", function () {
|
}).on('pageshow', "#liveTvGuidePage", function () {
|
||||||
|
|
||||||
var page = this;
|
var page = this;
|
||||||
|
|
|
@ -99,12 +99,7 @@
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// See backrops.js for comments on this
|
|
||||||
if ($.browser.msie) {
|
|
||||||
$('.fldEnableBackdrops', page).hide();
|
|
||||||
} else {
|
|
||||||
$('.fldEnableBackdrops', page).show();
|
$('.fldEnableBackdrops', page).show();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
window.WebClientPreferencesPage = {
|
window.WebClientPreferencesPage = {
|
||||||
|
|
|
@ -1327,7 +1327,7 @@ var Dashboard = {
|
||||||
var isBackdrop = imageType.toLowerCase() == 'backdrop';
|
var isBackdrop = imageType.toLowerCase() == 'backdrop';
|
||||||
|
|
||||||
if (isBackdrop) {
|
if (isBackdrop) {
|
||||||
quality -= 10;
|
quality -= 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($.browser.safari && $.browser.mobile) {
|
if ($.browser.safari && $.browser.mobile) {
|
||||||
|
|
390
dashboard-ui/thirdparty/headroom.js
vendored
Normal file
390
dashboard-ui/thirdparty/headroom.js
vendored
Normal file
|
@ -0,0 +1,390 @@
|
||||||
|
/*!
|
||||||
|
* headroom.js v0.7.0 - Give your page some headroom. Hide your header until you need it
|
||||||
|
* Copyright (c) 2014 Nick Williams - http://wicky.nillia.ms/headroom.js
|
||||||
|
* License: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(window, document) {
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/* exported features */
|
||||||
|
|
||||||
|
var features = {
|
||||||
|
bind : !!(function(){}.bind),
|
||||||
|
classList : 'classList' in document.documentElement,
|
||||||
|
rAF : !!(window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame)
|
||||||
|
};
|
||||||
|
window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles debouncing of events via requestAnimationFrame
|
||||||
|
* @see http://www.html5rocks.com/en/tutorials/speed/animations/
|
||||||
|
* @param {Function} callback The callback to handle whichever event
|
||||||
|
*/
|
||||||
|
function Debouncer (callback) {
|
||||||
|
this.callback = callback;
|
||||||
|
this.ticking = false;
|
||||||
|
}
|
||||||
|
Debouncer.prototype = {
|
||||||
|
constructor : Debouncer,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dispatches the event to the supplied callback
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
update : function() {
|
||||||
|
this.callback && this.callback();
|
||||||
|
this.ticking = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ensures events don't get stacked
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
requestTick : function() {
|
||||||
|
if(!this.ticking) {
|
||||||
|
requestAnimationFrame(this.rafCallback || (this.rafCallback = this.update.bind(this)));
|
||||||
|
this.ticking = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attach this as the event listeners
|
||||||
|
*/
|
||||||
|
handleEvent : function() {
|
||||||
|
this.requestTick();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Check if object is part of the DOM
|
||||||
|
* @constructor
|
||||||
|
* @param {Object} obj element to check
|
||||||
|
*/
|
||||||
|
function isDOMElement(obj) {
|
||||||
|
return obj && typeof window !== 'undefined' && (obj === window || obj.nodeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function for extending objects
|
||||||
|
*/
|
||||||
|
function extend (object /*, objectN ... */) {
|
||||||
|
if(arguments.length <= 0) {
|
||||||
|
throw new Error('Missing arguments in extend function');
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = object || {},
|
||||||
|
key,
|
||||||
|
i;
|
||||||
|
|
||||||
|
for (i = 1; i < arguments.length; i++) {
|
||||||
|
var replacement = arguments[i] || {};
|
||||||
|
|
||||||
|
for (key in replacement) {
|
||||||
|
// Recurse into object except if the object is a DOM element
|
||||||
|
if(typeof result[key] === 'object' && ! isDOMElement(result[key])) {
|
||||||
|
result[key] = extend(result[key], replacement[key]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result[key] = result[key] || replacement[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function for normalizing tolerance option to object format
|
||||||
|
*/
|
||||||
|
function normalizeTolerance (t) {
|
||||||
|
return t === Object(t) ? t : { down : t, up : t };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UI enhancement for fixed headers.
|
||||||
|
* Hides header when scrolling down
|
||||||
|
* Shows header when scrolling up
|
||||||
|
* @constructor
|
||||||
|
* @param {DOMElement} elem the header element
|
||||||
|
* @param {Object} options options for the widget
|
||||||
|
*/
|
||||||
|
function Headroom (elem, options) {
|
||||||
|
options = extend(options, Headroom.options);
|
||||||
|
|
||||||
|
this.lastKnownScrollY = 0;
|
||||||
|
this.elem = elem;
|
||||||
|
this.debouncer = new Debouncer(this.update.bind(this));
|
||||||
|
this.tolerance = normalizeTolerance(options.tolerance);
|
||||||
|
this.classes = options.classes;
|
||||||
|
this.offset = options.offset;
|
||||||
|
this.scroller = options.scroller;
|
||||||
|
this.initialised = false;
|
||||||
|
this.onPin = options.onPin;
|
||||||
|
this.onUnpin = options.onUnpin;
|
||||||
|
this.onTop = options.onTop;
|
||||||
|
this.onNotTop = options.onNotTop;
|
||||||
|
}
|
||||||
|
Headroom.prototype = {
|
||||||
|
constructor : Headroom,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialises the widget
|
||||||
|
*/
|
||||||
|
init : function() {
|
||||||
|
if(!Headroom.cutsTheMustard) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.elem.classList.add(this.classes.initial);
|
||||||
|
|
||||||
|
// defer event registration to handle browser
|
||||||
|
// potentially restoring previous scroll position
|
||||||
|
setTimeout(this.attachEvent.bind(this), 100);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unattaches events and removes any classes that were added
|
||||||
|
*/
|
||||||
|
destroy : function() {
|
||||||
|
var classes = this.classes;
|
||||||
|
|
||||||
|
this.initialised = false;
|
||||||
|
this.elem.classList.remove(classes.unpinned, classes.pinned, classes.top, classes.initial);
|
||||||
|
this.scroller.removeEventListener('scroll', this.debouncer, false);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches the scroll event
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
attachEvent : function() {
|
||||||
|
if(!this.initialised){
|
||||||
|
this.lastKnownScrollY = this.getScrollY();
|
||||||
|
this.initialised = true;
|
||||||
|
this.scroller.addEventListener('scroll', this.debouncer, false);
|
||||||
|
|
||||||
|
this.debouncer.handleEvent();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unpins the header if it's currently pinned
|
||||||
|
*/
|
||||||
|
unpin : function() {
|
||||||
|
var classList = this.elem.classList,
|
||||||
|
classes = this.classes;
|
||||||
|
|
||||||
|
if(classList.contains(classes.pinned) || !classList.contains(classes.unpinned)) {
|
||||||
|
classList.add(classes.unpinned);
|
||||||
|
classList.remove(classes.pinned);
|
||||||
|
this.onUnpin && this.onUnpin.call(this);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pins the header if it's currently unpinned
|
||||||
|
*/
|
||||||
|
pin : function() {
|
||||||
|
var classList = this.elem.classList,
|
||||||
|
classes = this.classes;
|
||||||
|
|
||||||
|
if(classList.contains(classes.unpinned)) {
|
||||||
|
classList.remove(classes.unpinned);
|
||||||
|
classList.add(classes.pinned);
|
||||||
|
this.onPin && this.onPin.call(this);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the top states
|
||||||
|
*/
|
||||||
|
top : function() {
|
||||||
|
var classList = this.elem.classList,
|
||||||
|
classes = this.classes;
|
||||||
|
|
||||||
|
if(!classList.contains(classes.top)) {
|
||||||
|
classList.add(classes.top);
|
||||||
|
classList.remove(classes.notTop);
|
||||||
|
this.onTop && this.onTop.call(this);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the not top state
|
||||||
|
*/
|
||||||
|
notTop : function() {
|
||||||
|
var classList = this.elem.classList,
|
||||||
|
classes = this.classes;
|
||||||
|
|
||||||
|
if(!classList.contains(classes.notTop)) {
|
||||||
|
classList.add(classes.notTop);
|
||||||
|
classList.remove(classes.top);
|
||||||
|
this.onNotTop && this.onNotTop.call(this);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the Y scroll position
|
||||||
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/Window.scrollY
|
||||||
|
* @return {Number} pixels the page has scrolled along the Y-axis
|
||||||
|
*/
|
||||||
|
getScrollY : function() {
|
||||||
|
return (this.scroller.pageYOffset !== undefined)
|
||||||
|
? this.scroller.pageYOffset
|
||||||
|
: (this.scroller.scrollTop !== undefined)
|
||||||
|
? this.scroller.scrollTop
|
||||||
|
: (document.documentElement || document.body.parentNode || document.body).scrollTop;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the height of the viewport
|
||||||
|
* @see http://andylangton.co.uk/blog/development/get-viewport-size-width-and-height-javascript
|
||||||
|
* @return {int} the height of the viewport in pixels
|
||||||
|
*/
|
||||||
|
getViewportHeight : function () {
|
||||||
|
return window.innerHeight
|
||||||
|
|| document.documentElement.clientHeight
|
||||||
|
|| document.body.clientHeight;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the height of the document
|
||||||
|
* @see http://james.padolsey.com/javascript/get-document-height-cross-browser/
|
||||||
|
* @return {int} the height of the document in pixels
|
||||||
|
*/
|
||||||
|
getDocumentHeight : function () {
|
||||||
|
var body = document.body,
|
||||||
|
documentElement = document.documentElement;
|
||||||
|
|
||||||
|
return Math.max(
|
||||||
|
body.scrollHeight, documentElement.scrollHeight,
|
||||||
|
body.offsetHeight, documentElement.offsetHeight,
|
||||||
|
body.clientHeight, documentElement.clientHeight
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the height of the DOM element
|
||||||
|
* @param {Object} elm the element to calculate the height of which
|
||||||
|
* @return {int} the height of the element in pixels
|
||||||
|
*/
|
||||||
|
getElementHeight : function (elm) {
|
||||||
|
return Math.max(
|
||||||
|
elm.scrollHeight,
|
||||||
|
elm.offsetHeight,
|
||||||
|
elm.clientHeight
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the height of the scroller element
|
||||||
|
* @return {int} the height of the scroller element in pixels
|
||||||
|
*/
|
||||||
|
getScrollerHeight : function () {
|
||||||
|
return (this.scroller === window || this.scroller === document.body)
|
||||||
|
? this.getDocumentHeight()
|
||||||
|
: this.getElementHeight(this.scroller);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* determines if the scroll position is outside of document boundaries
|
||||||
|
* @param {int} currentScrollY the current y scroll position
|
||||||
|
* @return {bool} true if out of bounds, false otherwise
|
||||||
|
*/
|
||||||
|
isOutOfBounds : function (currentScrollY) {
|
||||||
|
var pastTop = currentScrollY < 0,
|
||||||
|
pastBottom = currentScrollY + this.getViewportHeight() > this.getScrollerHeight();
|
||||||
|
|
||||||
|
return pastTop || pastBottom;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* determines if the tolerance has been exceeded
|
||||||
|
* @param {int} currentScrollY the current scroll y position
|
||||||
|
* @return {bool} true if tolerance exceeded, false otherwise
|
||||||
|
*/
|
||||||
|
toleranceExceeded : function (currentScrollY, direction) {
|
||||||
|
return Math.abs(currentScrollY-this.lastKnownScrollY) >= this.tolerance[direction];
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* determine if it is appropriate to unpin
|
||||||
|
* @param {int} currentScrollY the current y scroll position
|
||||||
|
* @param {bool} toleranceExceeded has the tolerance been exceeded?
|
||||||
|
* @return {bool} true if should unpin, false otherwise
|
||||||
|
*/
|
||||||
|
shouldUnpin : function (currentScrollY, toleranceExceeded) {
|
||||||
|
var scrollingDown = currentScrollY > this.lastKnownScrollY,
|
||||||
|
pastOffset = currentScrollY >= this.offset;
|
||||||
|
|
||||||
|
return scrollingDown && pastOffset && toleranceExceeded;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* determine if it is appropriate to pin
|
||||||
|
* @param {int} currentScrollY the current y scroll position
|
||||||
|
* @param {bool} toleranceExceeded has the tolerance been exceeded?
|
||||||
|
* @return {bool} true if should pin, false otherwise
|
||||||
|
*/
|
||||||
|
shouldPin : function (currentScrollY, toleranceExceeded) {
|
||||||
|
var scrollingUp = currentScrollY < this.lastKnownScrollY,
|
||||||
|
pastOffset = currentScrollY <= this.offset;
|
||||||
|
|
||||||
|
return (scrollingUp && toleranceExceeded) || pastOffset;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles updating the state of the widget
|
||||||
|
*/
|
||||||
|
update : function() {
|
||||||
|
var currentScrollY = this.getScrollY(),
|
||||||
|
scrollDirection = currentScrollY > this.lastKnownScrollY ? 'down' : 'up',
|
||||||
|
toleranceExceeded = this.toleranceExceeded(currentScrollY, scrollDirection);
|
||||||
|
|
||||||
|
if(this.isOutOfBounds(currentScrollY)) { // Ignore bouncy scrolling in OSX
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentScrollY <= this.offset ) {
|
||||||
|
this.top();
|
||||||
|
} else {
|
||||||
|
this.notTop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.shouldUnpin(currentScrollY, toleranceExceeded)) {
|
||||||
|
this.unpin();
|
||||||
|
}
|
||||||
|
else if(this.shouldPin(currentScrollY, toleranceExceeded)) {
|
||||||
|
this.pin();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastKnownScrollY = currentScrollY;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Default options
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
Headroom.options = {
|
||||||
|
tolerance : {
|
||||||
|
up : 0,
|
||||||
|
down : 0
|
||||||
|
},
|
||||||
|
offset : 0,
|
||||||
|
scroller: window,
|
||||||
|
classes : {
|
||||||
|
pinned : 'headroom--pinned',
|
||||||
|
unpinned : 'headroom--unpinned',
|
||||||
|
top : 'headroom--top',
|
||||||
|
notTop : 'headroom--not-top',
|
||||||
|
initial : 'headroom'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Headroom.cutsTheMustard = typeof features !== 'undefined' && features.rAF && features.bind && features.classList;
|
||||||
|
|
||||||
|
window.Headroom = Headroom;
|
||||||
|
|
||||||
|
}(window, document));
|
Loading…
Add table
Add a link
Reference in a new issue