207 lines
6.1 KiB
JavaScript
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 );
|