Files
2024-12-17 17:34:10 +01:00

207 lines
6.1 KiB
JavaScript

/*! Overthrow. An overflow:auto polyfill for responsive design. (c) 2012: Scott Jehl, Filament Group, Inc. http://filamentgroup.github.com/Overthrow/license.txt */
(function( w, o, undefined ){
// o is overthrow reference from overthrow-polyfill.js
if( o === undefined ){
return;
}
o.scrollIndicatorClassName = "overthrow";
var doc = w.document,
docElem = doc.documentElement,
// o api
nativeOverflow = o.support === "native",
canBeFilledWithPoly = o.canBeFilledWithPoly,
configure = o.configure,
set = o.set,
forget = o.forget,
scrollIndicatorClassName = o.scrollIndicatorClassName;
// find closest overthrow (elem or a parent)
o.closest = function( target, ascend ){
return !ascend && target.className && target.className.indexOf( scrollIndicatorClassName ) > -1 && target || o.closest( target.parentNode );
};
// polyfill overflow
var enabled = false;
o.set = function(){
set();
// If nativeOverflow or it doesn't look like the browser canBeFilledWithPoly, our job is done here. Exit viewport left.
if( enabled || nativeOverflow || !canBeFilledWithPoly ){
return;
}
w.overthrow.addClass();
enabled = true;
o.support = "polyfilled";
o.forget = function(){
forget();
enabled = false;
// Remove touch binding (check for method support since this part isn't qualified by touch support like the rest)
if( doc.removeEventListener ){
doc.removeEventListener( "touchstart", start, false );
}
};
// Fill 'er up!
// From here down, all logic is associated with touch scroll handling
// elem references the overthrow element in use
var elem,
// The last several Y values are kept here
lastTops = [],
// The last several X values are kept here
lastLefts = [],
// lastDown will be true if the last scroll direction was down, false if it was up
lastDown,
// lastRight will be true if the last scroll direction was right, false if it was left
lastRight,
// For a new gesture, or change in direction, reset the values from last scroll
resetVertTracking = function(){
lastTops = [];
lastDown = null;
},
resetHorTracking = function(){
lastLefts = [];
lastRight = null;
},
// On webkit, touch events hardly trickle through textareas and inputs
// Disabling CSS pointer events makes sure they do, but it also makes the controls innaccessible
// Toggling pointer events at the right moments seems to do the trick
// Thanks Thomas Bachem http://stackoverflow.com/a/5798681 for the following
inputs,
setPointers = function( val ){
inputs = elem.querySelectorAll( "textarea, input" );
for( var i = 0, il = inputs.length; i < il; i++ ) {
inputs[ i ].style.pointerEvents = val;
}
},
// For nested overthrows, changeScrollTarget restarts a touch event cycle on a parent or child overthrow
changeScrollTarget = function( startEvent, ascend ){
if( doc.createEvent ){
var newTarget = ( !ascend || ascend === undefined ) && elem.parentNode || elem.touchchild || elem,
tEnd;
if( newTarget !== elem ){
tEnd = doc.createEvent( "HTMLEvents" );
tEnd.initEvent( "touchend", true, true );
elem.dispatchEvent( tEnd );
newTarget.touchchild = elem;
elem = newTarget;
newTarget.dispatchEvent( startEvent );
}
}
},
// Touchstart handler
// On touchstart, touchmove and touchend are freshly bound, and all three share a bunch of vars set by touchstart
// Touchend unbinds them again, until next time
start = function( e ){
// Stop any throw in progress
if( o.intercept ){
o.intercept();
}
// Reset the distance and direction tracking
resetVertTracking();
resetHorTracking();
elem = o.closest( e.target );
if( !elem || elem === docElem || e.touches.length > 1 ){
return;
}
setPointers( "none" );
var touchStartE = e,
scrollT = elem.scrollTop,
scrollL = elem.scrollLeft,
height = elem.offsetHeight,
width = elem.offsetWidth,
startY = e.touches[ 0 ].pageY,
startX = e.touches[ 0 ].pageX,
scrollHeight = elem.scrollHeight,
scrollWidth = elem.scrollWidth,
// Touchmove handler
move = function( e ){
var ty = scrollT + startY - e.touches[ 0 ].pageY,
tx = scrollL + startX - e.touches[ 0 ].pageX,
down = ty >= ( lastTops.length ? lastTops[ 0 ] : 0 ),
right = tx >= ( lastLefts.length ? lastLefts[ 0 ] : 0 );
// If there's room to scroll the current container, prevent the default window scroll
if( ( ty > 0 && ty < scrollHeight - height ) || ( tx > 0 && tx < scrollWidth - width ) ){
e.preventDefault();
}
// This bubbling is dumb. Needs a rethink.
else {
changeScrollTarget( touchStartE );
}
// If down and lastDown are inequal, the y scroll has changed direction. Reset tracking.
if( lastDown && down !== lastDown ){
resetVertTracking();
}
// If right and lastRight are inequal, the x scroll has changed direction. Reset tracking.
if( lastRight && right !== lastRight ){
resetHorTracking();
}
// remember the last direction in which we were headed
lastDown = down;
lastRight = right;
// set the container's scroll
elem.scrollTop = ty;
elem.scrollLeft = tx;
lastTops.unshift( ty );
lastLefts.unshift( tx );
if( lastTops.length > 3 ){
lastTops.pop();
}
if( lastLefts.length > 3 ){
lastLefts.pop();
}
},
// Touchend handler
end = function( e ){
// Bring the pointers back
setPointers( "auto" );
setTimeout( function(){
setPointers( "none" );
}, 450 );
elem.removeEventListener( "touchmove", move, false );
elem.removeEventListener( "touchend", end, false );
};
elem.addEventListener( "touchmove", move, false );
elem.addEventListener( "touchend", end, false );
};
// Bind to touch, handle move and end within
doc.addEventListener( "touchstart", start, false );
};
})( this, this.overthrow );