first
This commit is contained in:
534
safekiso-server/modules/base/wwwroot/admindek/bower_components/stroll/js/stroll.js
vendored
Normal file
534
safekiso-server/modules/base/wwwroot/admindek/bower_components/stroll/js/stroll.js
vendored
Normal file
@@ -0,0 +1,534 @@
|
||||
/*!
|
||||
* stroll.js 1.2 - CSS scroll effects
|
||||
* http://lab.hakim.se/scroll-effects
|
||||
* MIT licensed
|
||||
*
|
||||
* Copyright (C) 2012 Hakim El Hattab, http://hakim.se
|
||||
*/
|
||||
(function(){
|
||||
|
||||
"use strict";
|
||||
|
||||
// When a list is configured as 'live', this is how frequently
|
||||
// the DOM will be polled for changes
|
||||
var LIVE_INTERVAL = 500;
|
||||
|
||||
var IS_TOUCH_DEVICE = !!( 'ontouchstart' in window );
|
||||
|
||||
// All of the lists that are currently bound
|
||||
var lists = [];
|
||||
|
||||
// Set to true when there are lists to refresh
|
||||
var active = false;
|
||||
|
||||
/**
|
||||
* Updates all currently bound lists.
|
||||
*/
|
||||
function refresh() {
|
||||
if( active ) {
|
||||
requestAnimFrame( refresh );
|
||||
|
||||
for( var i = 0, len = lists.length; i < len; i++ ) {
|
||||
lists[i].update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts monitoring a list and applies classes to each of
|
||||
* its contained elements based on its position relative to
|
||||
* the list's viewport.
|
||||
*
|
||||
* @param {HTMLElement} element
|
||||
* @param {Object} options Additional arguments;
|
||||
* - live; Flags if the DOM should be repeatedly checked for changes
|
||||
* repeatedly. Useful if the list contents is changing. Use
|
||||
* scarcely as it has an impact on performance.
|
||||
*/
|
||||
function add( element, options ) {
|
||||
// Only allow ul/ol
|
||||
if( !element.nodeName || /^(ul|ol)$/i.test( element.nodeName ) === false ) {
|
||||
return false;
|
||||
}
|
||||
// Delete duplicates (but continue and re-bind this list to get the
|
||||
// latest properties and list items)
|
||||
else if( contains( element ) ) {
|
||||
remove( element );
|
||||
}
|
||||
|
||||
var list = IS_TOUCH_DEVICE ? new TouchList( element ) : new List( element );
|
||||
|
||||
// Handle options
|
||||
if( options && options.live ) {
|
||||
list.syncInterval = setInterval( function() {
|
||||
list.sync.call( list );
|
||||
}, LIVE_INTERVAL );
|
||||
}
|
||||
|
||||
// Synchronize the list with the DOM
|
||||
list.sync();
|
||||
|
||||
// Add this element to the collection
|
||||
lists.push( list );
|
||||
|
||||
// Start refreshing if this was the first list to be added
|
||||
if( lists.length === 1 ) {
|
||||
active = true;
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops monitoring a list element and removes any classes
|
||||
* that were applied to its list items.
|
||||
*
|
||||
* @param {HTMLElement} element
|
||||
*/
|
||||
function remove( element ) {
|
||||
for( var i = 0; i < lists.length; i++ ) {
|
||||
var list = lists[i];
|
||||
|
||||
if( list.element == element ) {
|
||||
list.destroy();
|
||||
lists.splice( i, 1 );
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
// Stopped refreshing if the last list was removed
|
||||
if( lists.length === 0 ) {
|
||||
active = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified element has already been bound.
|
||||
*/
|
||||
function contains( element ) {
|
||||
for( var i = 0, len = lists.length; i < len; i++ ) {
|
||||
if( lists[i].element == element ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls 'method' for each DOM element discovered in
|
||||
* 'target'.
|
||||
*
|
||||
* @param target String selector / array of UL elements /
|
||||
* jQuery object / single UL element
|
||||
* @param method A function to call for each element target
|
||||
*/
|
||||
function batch( target, method, options ) {
|
||||
var i, len;
|
||||
|
||||
// Selector
|
||||
if( typeof target === 'string' ) {
|
||||
var targets = document.querySelectorAll( target );
|
||||
|
||||
for( i = 0, len = targets.length; i < len; i++ ) {
|
||||
method.call( null, targets[i], options );
|
||||
}
|
||||
}
|
||||
// Array (jQuery)
|
||||
else if( typeof target === 'object' && typeof target.length === 'number' ) {
|
||||
for( i = 0, len = target.length; i < len; i++ ) {
|
||||
method.call( null, target[i], options );
|
||||
}
|
||||
}
|
||||
// Single element
|
||||
else if( target.nodeName ) {
|
||||
method.call( null, target, options );
|
||||
}
|
||||
else {
|
||||
throw 'Stroll target was of unexpected type.';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the client is capable of running the library.
|
||||
*/
|
||||
function isCapable() {
|
||||
return !!document.body.classList;
|
||||
}
|
||||
|
||||
/**
|
||||
* The basic type of list; applies past & future classes to
|
||||
* list items based on scroll state.
|
||||
*/
|
||||
function List( element ) {
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the latest properties from the DOM to ensure that
|
||||
* this list is in sync with its contents.
|
||||
*/
|
||||
List.prototype.sync = function() {
|
||||
this.items = Array.prototype.slice.apply( this.element.children );
|
||||
|
||||
// Caching some heights so we don't need to go back to the DOM so much
|
||||
this.listHeight = this.element.offsetHeight;
|
||||
|
||||
// One loop to get the offsets from the DOM
|
||||
for( var i = 0, len = this.items.length; i < len; i++ ) {
|
||||
var item = this.items[i];
|
||||
item._offsetHeight = item.offsetHeight;
|
||||
item._offsetTop = item.offsetTop;
|
||||
item._offsetBottom = item._offsetTop + item._offsetHeight;
|
||||
item._state = '';
|
||||
}
|
||||
|
||||
// Force an update
|
||||
this.update( true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply past/future classes to list items outside of the viewport
|
||||
*/
|
||||
List.prototype.update = function( force ) {
|
||||
var scrollTop = this.element.pageYOffset || this.element.scrollTop,
|
||||
scrollBottom = scrollTop + this.listHeight;
|
||||
|
||||
// Quit if nothing changed
|
||||
if( scrollTop !== this.lastTop || force ) {
|
||||
this.lastTop = scrollTop;
|
||||
|
||||
// One loop to make our changes to the DOM
|
||||
for( var i = 0, len = this.items.length; i < len; i++ ) {
|
||||
var item = this.items[i];
|
||||
|
||||
// Above list viewport
|
||||
if( item._offsetBottom < scrollTop ) {
|
||||
// Exclusion via string matching improves performance
|
||||
if( item._state !== 'past' ) {
|
||||
item._state = 'past';
|
||||
item.classList.add( 'past' );
|
||||
item.classList.remove( 'future' );
|
||||
}
|
||||
}
|
||||
// Below list viewport
|
||||
else if( item._offsetTop > scrollBottom ) {
|
||||
// Exclusion via string matching improves performance
|
||||
if( item._state !== 'future' ) {
|
||||
item._state = 'future';
|
||||
item.classList.add( 'future' );
|
||||
item.classList.remove( 'past' );
|
||||
}
|
||||
}
|
||||
// Inside of list viewport
|
||||
else if( item._state ) {
|
||||
if( item._state === 'past' ) item.classList.remove( 'past' );
|
||||
if( item._state === 'future' ) item.classList.remove( 'future' );
|
||||
item._state = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up after this list and disposes of it.
|
||||
*/
|
||||
List.prototype.destroy = function() {
|
||||
clearInterval( this.syncInterval );
|
||||
|
||||
for( var j = 0, len = this.items.length; j < len; j++ ) {
|
||||
var item = this.items[j];
|
||||
|
||||
item.classList.remove( 'past' );
|
||||
item.classList.remove( 'future' );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A list specifically for touch devices. Simulates the style
|
||||
* of scrolling you'd see on a touch device but does not rely
|
||||
* on webkit-overflow-scrolling since that makes it impossible
|
||||
* to read the up-to-date scroll position.
|
||||
*/
|
||||
function TouchList( element ) {
|
||||
this.element = element;
|
||||
this.element.style.overflow = 'hidden';
|
||||
|
||||
this.top = {
|
||||
value: 0,
|
||||
natural: 0
|
||||
};
|
||||
|
||||
this.touch = {
|
||||
value: 0,
|
||||
offset: 0,
|
||||
start: 0,
|
||||
previous: 0,
|
||||
lastMove: Date.now(),
|
||||
accellerateTimeout: -1,
|
||||
isAccellerating: false,
|
||||
isActive: false
|
||||
};
|
||||
|
||||
this.velocity = 0;
|
||||
}
|
||||
TouchList.prototype = new List();
|
||||
|
||||
/**
|
||||
* Fetches the latest properties from the DOM to ensure that
|
||||
* this list is in sync with its contents. This is typically
|
||||
* only used once (per list) at initialization.
|
||||
*/
|
||||
TouchList.prototype.sync = function() {
|
||||
this.items = Array.prototype.slice.apply( this.element.children );
|
||||
|
||||
this.listHeight = this.element.offsetHeight;
|
||||
|
||||
var item;
|
||||
|
||||
// One loop to get the properties we need from the DOM
|
||||
for( var i = 0, len = this.items.length; i < len; i++ ) {
|
||||
item = this.items[i];
|
||||
item._offsetHeight = item.offsetHeight;
|
||||
item._offsetTop = item.offsetTop;
|
||||
item._offsetBottom = item._offsetTop + item._offsetHeight;
|
||||
item._state = '';
|
||||
|
||||
// Animating opacity is a MAJOR performance hit on mobile so we can't allow it
|
||||
item.style.opacity = 1;
|
||||
}
|
||||
|
||||
this.top.natural = this.element.scrollTop;
|
||||
this.top.value = this.top.natural;
|
||||
this.top.max = item._offsetBottom - this.listHeight;
|
||||
|
||||
// Force an update
|
||||
this.update( true );
|
||||
|
||||
this.bind();
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the events for this list. References to proxy methods
|
||||
* are kept for unbinding if the list is disposed of.
|
||||
*/
|
||||
TouchList.prototype.bind = function() {
|
||||
var scope = this;
|
||||
|
||||
this.touchStartDelegate = function( event ) {
|
||||
scope.onTouchStart( event );
|
||||
};
|
||||
|
||||
this.touchMoveDelegate = function( event ) {
|
||||
scope.onTouchMove( event );
|
||||
};
|
||||
|
||||
this.touchEndDelegate = function( event ) {
|
||||
scope.onTouchEnd( event );
|
||||
};
|
||||
|
||||
this.element.addEventListener( 'touchstart', this.touchStartDelegate, false );
|
||||
this.element.addEventListener( 'touchmove', this.touchMoveDelegate, false );
|
||||
this.element.addEventListener( 'touchend', this.touchEndDelegate, false );
|
||||
}
|
||||
|
||||
TouchList.prototype.onTouchStart = function( event ) {
|
||||
event.preventDefault();
|
||||
|
||||
if( event.touches.length === 1 ) {
|
||||
this.touch.isActive = true;
|
||||
this.touch.start = event.touches[0].clientY;
|
||||
this.touch.previous = this.touch.start;
|
||||
this.touch.value = this.touch.start;
|
||||
this.touch.offset = 0;
|
||||
|
||||
if( this.velocity ) {
|
||||
this.touch.isAccellerating = true;
|
||||
|
||||
var scope = this;
|
||||
|
||||
this.touch.accellerateTimeout = setTimeout( function() {
|
||||
scope.touch.isAccellerating = false;
|
||||
scope.velocity = 0;
|
||||
}, 500 );
|
||||
}
|
||||
else {
|
||||
this.velocity = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TouchList.prototype.onTouchMove = function( event ) {
|
||||
if( event.touches.length === 1 ) {
|
||||
var previous = this.touch.value;
|
||||
|
||||
this.touch.value = event.touches[0].clientY;
|
||||
this.touch.lastMove = Date.now();
|
||||
|
||||
var sameDirection = ( this.touch.value > this.touch.previous && this.velocity < 0 )
|
||||
|| ( this.touch.value < this.touch.previous && this.velocity > 0 );
|
||||
|
||||
if( this.touch.isAccellerating && sameDirection ) {
|
||||
clearInterval( this.touch.accellerateTimeout );
|
||||
|
||||
// Increase velocity significantly
|
||||
this.velocity += ( this.touch.previous - this.touch.value ) / 10;
|
||||
}
|
||||
else {
|
||||
this.velocity = 0;
|
||||
|
||||
this.touch.isAccellerating = false;
|
||||
this.touch.offset = Math.round( this.touch.start - this.touch.value );
|
||||
}
|
||||
|
||||
this.touch.previous = previous;
|
||||
}
|
||||
}
|
||||
|
||||
TouchList.prototype.onTouchEnd = function( event ) {
|
||||
var distanceMoved = this.touch.start - this.touch.value;
|
||||
|
||||
if( !this.touch.isAccellerating ) {
|
||||
// Apply velocity based on the start position of the touch
|
||||
this.velocity = ( this.touch.start - this.touch.value ) / 10;
|
||||
}
|
||||
|
||||
// Don't apply any velocity if the touch ended in a still state
|
||||
if( Date.now() - this.touch.lastMove > 200 || Math.abs( this.touch.previous - this.touch.value ) < 5 ) {
|
||||
this.velocity = 0;
|
||||
}
|
||||
|
||||
this.top.value += this.touch.offset;
|
||||
|
||||
// Reset the variables used to determne swipe speed
|
||||
this.touch.offset = 0;
|
||||
this.touch.start = 0;
|
||||
this.touch.value = 0;
|
||||
this.touch.isActive = false;
|
||||
this.touch.isAccellerating = false;
|
||||
|
||||
clearInterval( this.touch.accellerateTimeout );
|
||||
|
||||
// If a swipe was captured, prevent event propagation
|
||||
if( Math.abs( this.velocity ) > 4 || Math.abs( distanceMoved ) > 10 ) {
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Apply past/future classes to list items outside of the viewport
|
||||
*/
|
||||
TouchList.prototype.update = function( force ) {
|
||||
// Determine the desired scroll top position
|
||||
var scrollTop = this.top.value + this.velocity + this.touch.offset;
|
||||
|
||||
// Only scroll the list if there's input
|
||||
if( this.velocity || this.touch.offset ) {
|
||||
// Scroll the DOM and add on the offset from touch
|
||||
this.element.scrollTop = scrollTop;
|
||||
|
||||
// Keep the scroll value within bounds
|
||||
scrollTop = Math.max( 0, Math.min( this.element.scrollTop, this.top.max ) );
|
||||
|
||||
// Cache the currently set scroll top and touch offset
|
||||
this.top.value = scrollTop - this.touch.offset;
|
||||
}
|
||||
|
||||
// If there is no active touch, decay velocity
|
||||
if( !this.touch.isActive || this.touch.isAccellerating ) {
|
||||
this.velocity *= 0.95;
|
||||
}
|
||||
|
||||
// Cut off early, the last fraction of velocity doesn't have
|
||||
// much impact on movement
|
||||
if( Math.abs( this.velocity ) < 0.15 ) {
|
||||
this.velocity = 0;
|
||||
}
|
||||
|
||||
// Only proceed if the scroll position has changed
|
||||
if( scrollTop !== this.top.natural || force ) {
|
||||
this.top.natural = scrollTop;
|
||||
this.top.value = scrollTop - this.touch.offset;
|
||||
|
||||
var scrollBottom = scrollTop + this.listHeight;
|
||||
|
||||
// One loop to make our changes to the DOM
|
||||
for( var i = 0, len = this.items.length; i < len; i++ ) {
|
||||
var item = this.items[i];
|
||||
|
||||
// Above list viewport
|
||||
if( item._offsetBottom < scrollTop ) {
|
||||
// Exclusion via string matching improves performance
|
||||
if( this.velocity <= 0 && item._state !== 'past' ) {
|
||||
item.classList.add( 'past' );
|
||||
item._state = 'past';
|
||||
}
|
||||
}
|
||||
// Below list viewport
|
||||
else if( item._offsetTop > scrollBottom ) {
|
||||
// Exclusion via string matching improves performance
|
||||
if( this.velocity >= 0 && item._state !== 'future' ) {
|
||||
item.classList.add( 'future' );
|
||||
item._state = 'future';
|
||||
}
|
||||
}
|
||||
// Inside of list viewport
|
||||
else if( item._state ) {
|
||||
if( item._state === 'past' ) item.classList.remove( 'past' );
|
||||
if( item._state === 'future' ) item.classList.remove( 'future' );
|
||||
item._state = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Cleans up after this list and disposes of it.
|
||||
*/
|
||||
TouchList.prototype.destroy = function() {
|
||||
List.prototype.destroy.apply( this );
|
||||
|
||||
this.element.removeEventListener( 'touchstart', this.touchStartDelegate, false );
|
||||
this.element.removeEventListener( 'touchmove', this.touchMoveDelegate, false );
|
||||
this.element.removeEventListener( 'touchend', this.touchEndDelegate, false );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Public API
|
||||
*/
|
||||
window.stroll = {
|
||||
/**
|
||||
* Binds one or more lists for scroll effects.
|
||||
*
|
||||
* @see #add()
|
||||
*/
|
||||
bind: function( target, options ) {
|
||||
if( isCapable() ) {
|
||||
batch( target, add, options );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Unbinds one or more lists from scroll effects.
|
||||
*
|
||||
* @see #remove()
|
||||
*/
|
||||
unbind: function( target ) {
|
||||
if( isCapable() ) {
|
||||
batch( target, remove );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.requestAnimFrame = (function(){
|
||||
return window.requestAnimationFrame ||
|
||||
window.webkitRequestAnimationFrame ||
|
||||
window.mozRequestAnimationFrame ||
|
||||
window.oRequestAnimationFrame ||
|
||||
window.msRequestAnimationFrame ||
|
||||
function( callback ){
|
||||
window.setTimeout(callback, 1000 / 60);
|
||||
};
|
||||
})()
|
||||
|
||||
})();
|
||||
Reference in New Issue
Block a user