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 );
 |