Skip to content

Commit

Permalink
Add fastclick library.
Browse files Browse the repository at this point in the history
  • Loading branch information
Mauvis Ledford committed Oct 23, 2012
1 parent f825867 commit 8d1859a
Show file tree
Hide file tree
Showing 2 changed files with 362 additions and 0 deletions.
6 changes: 6 additions & 0 deletions src/index.html
Expand Up @@ -118,8 +118,14 @@
<script src="js/views/modals/signup.js"></script>
<script src="js/views/modals/settings.js"></script>

<script src="js/vendor/fastclick.js"></script>

@javascript concat end@
<script>
$(function(){
new FastClick(document.body);
});

// End Chartbeat
var _sf_async_config={uid:26394,domain:"dontbreak.me"};(function(){function loadChartbeat(){window._sf_endpt=(new Date()).getTime();var e=document.createElement('script');e.setAttribute('src',(("https:"==document.location.protocol)?"https://a248.e.akamai.net/chartbeat.download.akamai.com/102508/":"http://static.chartbeat.com/")+"js/chartbeat.js");document.body.appendChild(e);}
var oldonload=window.onload;window.onload=(typeof window.onload!='function')?loadChartbeat:function(){oldonload();loadChartbeat();};})();
Expand Down
356 changes: 356 additions & 0 deletions src/js/vendor/fastclick.js
@@ -0,0 +1,356 @@
/**
* @preserve FastClick: polyfill to remove click delays on browsers with touch UIs.
*
* @copyright The Financial Times Limited [All Rights Reserved]
* @license MIT License (see LICENCE.txt)
* @codingstandard ftlabs-jslint
*/

/*jslint browser:true*/
/*global Node, define*/

(function() {
'use strict';

var


/**
* Android requires an exception for labels.
*
* @type boolean
*/
android = navigator.userAgent.indexOf('Android') > 0,


/**
* Earlier versions of Chrome for Android don't report themselves as "Chrome" but "CrMo" - check for both.
*
* @type boolean
*/
chromeAndroid = android && (/Chrome|CrMo/).test(navigator.userAgent),


/**
* Playbook requires a greater scroll boundary.
*
* @type number
*/
scrollBoundary = (android || (navigator.userAgent.indexOf('PlayBook') === -1)) ? 5 : 20;


/**
* Determine whether a given element requires a native click.
*
* @param {Element} target DOM element
* @returns {boolean} Returns true if the element needs a native click
*/
function needsClick(target) {
switch (target.nodeName.toLowerCase()) {
case 'label':
case 'video':
return true;
default:
return (/\bneedsclick\b/).test(target.className);
}
}

/**
* Determine whether a given element requires a call to focus to simulate click into element.
*
* @param {Element} target target DOM element.
* @return {boolean} Returns true if the element requires a call to focus to simulate native click.
*/
function needsFocus(target) {
switch(target.nodeName.toLowerCase()) {
case 'textarea':
case 'select':
return true;
case 'input':
switch (target.type) {
case 'button':
case 'checkbox':
case 'file':
case 'image':
case 'radio':
case 'submit':
return false;
default:
return true;
}
break;
default:
return (/\bneedsfocus\b/).test(target.className);
}
}


/**
* Retrieve an element based on coordinates within the window.
*
* @param {number} x
* @param {number} y
* @return {Element}
*/
function eleAtWindowPosition(x, y) {

// On Chrome for Android, amend coordinates by the device pixel ratio.
if (chromeAndroid && window.devicePixelRatio) {
x *= window.devicePixelRatio;
y *= window.devicePixelRatio;
}

return document.elementFromPoint(x, y);
}


/**
* Instantiate fast-clicking listeners on the specificed layer.
*
* @constructor
* @param {Element} layer The layer to listen on
*/
function FastClick(layer) {
var


/**
* @type Function
*/
oldOnClick,


/**
* The position and page scroll amount when click had started to be tracked.
*
* @type Object
*/
clickStart = { x: 0, y: 0, scrollX: 0, scrollY: 0 },


/**
* Whether a click is currently being tracked.
*
* @type boolean
*/
trackingClick = false,


/**
* Maximum distance (37 pixels) to the power of two.
*
* @type number
*/
bound = Math.pow(37, 2),


/**
* On touch start, record the position and scroll offset.
*
* @param {Event} event
* @returns {boolean}
*/
onTouchStart = function(event) {
trackingClick = true;

clickStart.x = event.targetTouches[0].pageX;
clickStart.y = event.targetTouches[0].pageY;
if (clickStart.x === event.targetTouches[0].clientX) {
clickStart.x += window.pageXOffset;
}
if (clickStart.y === event.targetTouches[0].clientY) {
clickStart.y += window.pageYOffset;
}
clickStart.scrollX = window.pageXOffset;
clickStart.scrollY = window.pageYOffset;

return true;
},


/**
* Update the last position.
*
* @param {Event} event
* @returns {boolean}
*/
onTouchMove = function(event) {
if (!trackingClick) {
return true;
}

// Detect whether a click has left the bounds of would be defined as a click, defined as a circle of radius sqrt(bound) around the start point.
if ((Math.pow(event.targetTouches[0].pageX - clickStart.x, 2) + Math.pow(event.targetTouches[0].pageY - clickStart.y, 2)) > bound) {
trackingClick = false;

// If the touch has moved, cancel the click tracking
} else if (Math.abs(window.pageXOffset - clickStart.scrollX) > scrollBoundary || Math.abs(window.pageYOffset - clickStart.scrollY) > scrollBoundary) {
trackingClick = false;
}

return true;
},


/**
* On touch end, determine whether to send a click event at once.
*
* @param {Event} event
* @returns {boolean}
*/
onTouchEnd = function(event) {
var targetElement, forElement, targetCoordinates, clickEvent;

if (!trackingClick) {
return true;
}

trackingClick = false;

// Set up the coordinates to match
targetCoordinates = {
x: clickStart.x - clickStart.scrollX,
y: clickStart.y - clickStart.scrollY
};

// Derive the element to click as a result of the touch.
targetElement = eleAtWindowPosition(targetCoordinates.x, targetCoordinates.y);

// If we're not clicking anything exit early
if (!targetElement) {
return false;
}

// If the targetted node is a text node, target the parent instead
if (targetElement.nodeType === Node.TEXT_NODE) {
targetElement = targetElement.parentElement;
}

if (targetElement.nodeName.toLowerCase() === 'label' && targetElement.htmlFor) {
forElement = document.getElementById(targetElement.htmlFor);
if (forElement) {
targetElement.focus();
if (android) {
return false;
}

targetElement = forElement;
}
} else if (needsFocus(targetElement)) {
targetElement.focus();
return false;
}

// Prevent the actual click from going though - unless the target node is marked as requiring
// real clicks or if it is in the whitelist in which case only non-programmatic clicks are permitted
// to open the options list and so the original event is required.
if (needsClick(targetElement)) {
return false;
}

// Synthesise a click event, with an extra attribute so it can be tracked
clickEvent = document.createEvent('MouseEvents');
clickEvent.initMouseEvent('click', true, true, window, 1, 0, 0, targetCoordinates.x, targetCoordinates.y, false, false, false, false, 0, null);
clickEvent.forwardedTouchEvent = true;
targetElement.dispatchEvent(clickEvent);

event.preventDefault();

return false;
},


/**
* On touch cancel, stop tracking the click.
*/
onTouchCancel = function() {
trackingClick = false;
},


/**
* On actual clicks, determine whether this is a touch-generated click, a click action occurring
* naturally after a delay after a touch (which needs to be cancelled to avoid duplication), or
* an actual click which should be permitted.
*
* @param {Event} event
* @returns {boolean}
*/
onClick = function(event) {
var targetElement;

if (event.forwardedTouchEvent) {
return true;
}

// Programmatically generated events targeting a specific element should be permitted
if (!event.cancelable) {
return true;
}

targetElement = eleAtWindowPosition(clickStart.x - clickStart.scrollX, clickStart.y - clickStart.scrollY);

// Derive and check the target element to see whether the click needs to be permitted;
// unless explicitly enabled, prevent non-touch click events from triggering actions,
// to prevent ghost/doubleclicks.
if (!targetElement || !needsClick(targetElement)) {

// Prevent any user-added listeners declared on FastClick element from being fired.
if (event.stopImmediatePropagation) {
event.stopImmediatePropagation();
}

// Cancel the event
event.stopPropagation();
event.preventDefault();

return false;
}

// If clicks are permitted, return true for the action to go through.
return true;
};

if (!layer || !layer.nodeType) {
throw new TypeError('Layer must be a document node');
}

// Devices that don't support touch don't need FastClick
if (typeof window.ontouchstart === 'undefined') {
return;
}

// Set up event handlers as required
layer.addEventListener('click', onClick, true);
layer.addEventListener('touchstart', onTouchStart, true);
layer.addEventListener('touchmove', onTouchMove, true);
layer.addEventListener('touchend', onTouchEnd, true);
layer.addEventListener('touchcancel', onTouchCancel, true);

// If a handler is already declared in the element's onclick attribute, it will be fired before
// FastClick's onClick handler. Fix this by pulling out the user-defined handler function and
// adding it as listener.
if (typeof layer.onclick === 'function') {

// Android browser on at least 3.2 requires a new reference to the function in layer.onclick
// - the old one won't work if passed to addEventListener directly.
oldOnClick = layer.onclick;
layer.addEventListener('click', function(event) {
oldOnClick(event);
}, false);
layer.onclick = null;
}
}

if (typeof define === 'function' && define.amd) {

// AMD. Register as an anonymous module.
define(function() {
return FastClick;
});
} else {

// Browser global
window.FastClick = FastClick;
}
}());

0 comments on commit 8d1859a

Please sign in to comment.