diff --git a/assets/javascripts/vendor/fastclick.js b/assets/javascripts/vendor/fastclick.js index b82882e0..60df8b25 100644 --- a/assets/javascripts/vendor/fastclick.js +++ b/assets/javascripts/vendor/fastclick.js @@ -1,7 +1,7 @@ /** * @preserve FastClick: polyfill to remove click delays on browsers with touch UIs. * - * @version 0.6.9 + * @version 0.6.11 * @codingstandard ftlabs-jsv2 * @copyright The Financial Times Limited [All Rights Reserved] * @license MIT License (see LICENSE.txt) @@ -98,6 +98,9 @@ function FastClick(layer) { /** @type function() */ this.onTouchStart = function() { return FastClick.prototype.onTouchStart.apply(self, arguments); }; + /** @type function() */ + this.onTouchMove = function() { return FastClick.prototype.onTouchMove.apply(self, arguments); }; + /** @type function() */ this.onTouchEnd = function() { return FastClick.prototype.onTouchEnd.apply(self, arguments); }; @@ -117,6 +120,7 @@ function FastClick(layer) { layer.addEventListener('click', this.onClick, true); layer.addEventListener('touchstart', this.onTouchStart, false); + layer.addEventListener('touchmove', this.onTouchMove, false); layer.addEventListener('touchend', this.onTouchEnd, false); layer.addEventListener('touchcancel', this.onTouchCancel, false); @@ -241,8 +245,9 @@ FastClick.prototype.needsFocus = function(target) { 'use strict'; switch (target.nodeName.toLowerCase()) { case 'textarea': - case 'select': return true; + case 'select': + return !this.deviceIsAndroid; case 'input': switch (target.type) { case 'button': @@ -281,11 +286,22 @@ FastClick.prototype.sendClick = function(targetElement, event) { // Synthesise a click event, with an extra attribute so it can be tracked clickEvent = document.createEvent('MouseEvents'); - clickEvent.initMouseEvent('click', true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null); + clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null); clickEvent.forwardedTouchEvent = true; targetElement.dispatchEvent(clickEvent); }; +FastClick.prototype.determineEventType = function(targetElement) { + 'use strict'; + + //Issue #159: Android Chrome Select Box does not open with a synthetic click event + if (this.deviceIsAndroid && targetElement.tagName.toLowerCase() === 'select') { + return 'mousedown'; + } + + return 'click'; +}; + /** * @param {EventTarget|Element} targetElement @@ -294,7 +310,8 @@ FastClick.prototype.focus = function(targetElement) { 'use strict'; var length; - if (this.deviceIsIOS && targetElement.setSelectionRange) { + // Issue #160: on iOS 7, some input elements (e.g. date datetime) throw a vague TypeError on setSelectionRange. These elements don't have an integer value for the selectionStart and selectionEnd properties, but unfortunately that can't be used for detection because accessing the properties also throws a TypeError. Just check the type instead. Filed as Apple bug #15122724. + if (this.deviceIsIOS && targetElement.setSelectionRange && targetElement.type.indexOf('date') !== 0 && targetElement.type !== 'time') { length = targetElement.value.length; targetElement.setSelectionRange(length, length); } else { @@ -436,6 +453,28 @@ FastClick.prototype.touchHasMoved = function(event) { }; +/** + * Update the last position. + * + * @param {Event} event + * @returns {boolean} + */ +FastClick.prototype.onTouchMove = function(event) { + 'use strict'; + if (!this.trackingClick) { + return true; + } + + // If the touch has moved, cancel the click tracking + if (this.targetElement !== this.getTargetElementFromEventTarget(event.target) || this.touchHasMoved(event)) { + this.trackingClick = false; + this.targetElement = null; + } + + return true; +}; + + /** * Attempt to find the labelled control for the given label element. * @@ -471,12 +510,6 @@ FastClick.prototype.onTouchEnd = function(event) { 'use strict'; var forElement, trackingClickStart, targetTagName, scrollParent, touch, targetElement = this.targetElement; - // If the touch has moved, cancel the click tracking - if (this.touchHasMoved(event)) { - this.trackingClick = false; - this.targetElement = null; - } - if (!this.trackingClick) { return true; } @@ -487,6 +520,9 @@ FastClick.prototype.onTouchEnd = function(event) { return true; } + // Reset to prevent wrong click cancel on input (issue #156). + this.cancelNextClick = false; + this.lastClickTime = event.timeStamp; trackingClickStart = this.trackingClickStart; @@ -671,6 +707,7 @@ FastClick.prototype.destroy = function() { layer.removeEventListener('click', this.onClick, true); layer.removeEventListener('touchstart', this.onTouchStart, false); + layer.removeEventListener('touchmove', this.onTouchMove, false); layer.removeEventListener('touchend', this.onTouchEnd, false); layer.removeEventListener('touchcancel', this.onTouchCancel, false); }; @@ -684,19 +721,30 @@ FastClick.prototype.destroy = function() { FastClick.notNeeded = function(layer) { 'use strict'; var metaViewport; + var chromeVersion; // Devices that don't support touch don't need FastClick if (typeof window.ontouchstart === 'undefined') { return true; } - if ((/Chrome\/[0-9]+/).test(navigator.userAgent)) { + // Chrome version - zero for other browsers + chromeVersion = +(/Chrome\/([0-9]+)/.exec(navigator.userAgent) || [,0])[1]; + + if (chromeVersion) { - // Chrome on Android with user-scalable="no" doesn't need FastClick (issue #89) if (FastClick.prototype.deviceIsAndroid) { metaViewport = document.querySelector('meta[name=viewport]'); - if (metaViewport && metaViewport.content.indexOf('user-scalable=no') !== -1) { - return true; + + if (metaViewport) { + // Chrome on Android with user-scalable="no" doesn't need FastClick (issue #89) + if (metaViewport.content.indexOf('user-scalable=no') !== -1) { + return true; + } + // Chrome 32 and above with width=device-width or less don't need FastClick + if (chromeVersion > 31 && window.innerWidth <= window.screen.width) { + return true; + } } // Chrome desktop doesn't need FastClick (issue #15)