UNPKG

crossbrowdy

Version:

A Multimedia JavaScript framework to create real cross-platform and hybrid game engines, games, emulators, multimedia libraries and apps.

180 lines (159 loc) 5.7 kB
/* Hammer-time - v0.3.0 * http://github.com/hammerjs/hammer-time * * Copyright Alexander Schmitz and other contributors * Released under the MIT license * * Expiramental fastclick based on a partial polyfill of * touch-action: none; CSS property */ ( function() { // Detect support for necessary features; var MO = window.MutationObserver || window.WebKitMutationObserver; var touchevents = ( "ontouchstart" in window ) || ( window.DocumentTouch && document instanceof DocumentTouch ); var nativeTouchAction = document.documentElement.style[ "touch-action" ] !== undefined || document.documentElement.style[ "-ms-touch-action" ]; // iOS running pages from the homescreen still have the delay var standalone = window.navigator.standalone; // If there is native touch action bail the hammer has already dropped if ( (nativeTouchAction && !standalone) || !touchevents || !MO ) { return; } //Check if a global Hammer object already exists window.Hammer = window.Hammer || {}; // We need to save the last touch start in iOS 8 if it is more then 150ms // it will trigger native fast click which cant ne stoped even with all the might // of thors hammer and a return false var touchMatchNone = /touch-action[:][\s]*(none)[^;'"]*/; var touchMatchManipulation = /touch-action[:][\s]*(manipulation)[^;'"]*/; var touchMatch = /touch-action/; var timeTouch = /(iP(ad|hone|od))/.test( navigator.userAgent ) && ( "indexedDB" in window || !!window.performance ); window.Hammer.time = { // Check if an element has touch action none in its style attribute and thusly should // be hammered upon getTouchAction: function( element ) { return this.checkStyleString( element.getAttribute( "style" ) ); }, checkStyleString: function( style ) { if ( !touchMatch.test( style ) ) { return undefined; } if ( touchMatchNone.test( style ) ) { return "none"; } if ( touchMatchManipulation.test( style ) ) { return "manipulation"; } return true; }, shouldHammer: function( e ) { var parentAction = e.target.hasParent; return ( parentAction && ( !timeTouch || Date.now() - e.target.lastStart < 125 ) ) ? parentAction : false; }, touchHandler: function( e ) { var hammerType = this.shouldHammer( e ); // Check both if we should trigger fast click and the time to avoid a double trigger with // native fast click if ( hammerType === "none" ) { this.dropHammer( e ); } else if ( hammerType === "manipulation" ) { var pos = e.target.getBoundingClientRect(); var scrolled = pos.top !== this.pos.top || pos.left !== this.pos.left; !scrolled && this.dropHammer( e ); } this.scrolled = false; delete e.target.lastStart; delete e.target.hasParent; }, dropHammer: function( e ) { if ( e.type === "touchend" ) { e.target.focus(); // Wait for next tic so events fire in proper order setTimeout( function() { e.target.click(); }, 0 ); } // Prevent the click which will come after this otherwise but with a 300ms delay e.preventDefault(); }, touchStart: function( e ) { this.pos = e.target.getBoundingClientRect(); e.target.hasParent = this.hasParent( e.target ); if ( timeTouch && e.target.hasParent ) { e.target.lastStart = Date.now(); } }, styleWatcher: function( mutations ) { mutations.forEach( this.styleUpdater, this ); }, styleUpdater: function( mutation ) { // We just caused this update bail if ( mutation.target.updateNext ) { mutation.target.updateNext = false; return; } var touchAction = this.getTouchAction( mutation.target ); if ( touchAction ) { if ( touchAction !== "none" ) { mutation.target.hadTouchNone = false; } return; } if ( !touchAction && ( ( mutation.oldValue && this.checkStyleString( mutation.oldValue ) ) || mutation.target.hadTouchNone ) ) { // We save this because during animations which update the style in quick succession // style tag can update quicker then the mutation observer fires so we lose the oldValue // property which contains our refrence to the original which contained touch-action. mutation.target.hadTouchNone = true; // Save the fact that we caused the next update mutation.target.updateNext = false; // Add touch action back to the style attribute mutation.target .setAttribute( "style", mutation.target.getAttribute( "style" ) + " touch-action: none;" ); } }, hasParent: function( node ) { var touchAction; for ( var cur = node; cur && cur.parentNode; cur = cur.parentNode ) { touchAction = this.getTouchAction( cur ); if ( touchAction ) { return touchAction; } } return false; }, installStartEvents: function() { document.addEventListener( "touchstart", this.touchStart.bind( this ) ); document.addEventListener( "mousedown", this.touchStart.bind( this ) ); }, installEndEvents: function() { document.addEventListener( "touchend", this.touchHandler.bind( this ), true ); document.addEventListener( "mouseup", this.touchHandler.bind( this ), true ); }, installObserver: function() { // We need to observe changes to style attributes because if something updates the style // attribute it will remove the touch-action property because browsers santitize the style // attribute when its properties are updated. this.observer = new MO( this.styleWatcher.bind( this ) ).observe( document, { subtree: true, attributes: true, attributeOldValue: true, attributeFilter: [ "style" ] } ); }, install: function() { this.installEndEvents(); this.installStartEvents(); this.installObserver(); } }; window.Hammer.time.install(); } )();