/* Modernizr 2.5.3 (Custom Build) | MIT & BSD * Build: http://www.modernizr.com/download/#-touch-teststyles-prefixes */ window.Modernizr = (function( window, document, undefined ) { var version = '2.5.3', Modernizr = {}, docElement = document.documentElement, mod = 'modernizr', modElem = document.createElement(mod), mStyle = modElem.style, inputElem , toString = {}.toString, prefixes = ' -webkit- -moz- -o- -ms- '.split(' '), tests = {}, inputs = {}, attrs = {}, classes = [], slice = classes.slice, featureName, injectElementWithStyles = function( rule, callback, nodes, testnames ) { var style, ret, node, div = document.createElement('div'), body = document.body, fakeBody = body ? body : document.createElement('body'); if ( parseInt(nodes, 10) ) { while ( nodes-- ) { node = document.createElement('div'); node.id = testnames ? testnames[nodes] : mod + (nodes + 1); div.appendChild(node); } } style = ['­',''].join(''); div.id = mod; (body ? div : fakeBody).innerHTML += style; fakeBody.appendChild(div); if(!body){ fakeBody.style.background = ""; docElement.appendChild(fakeBody); } ret = callback(div, rule); !body ? fakeBody.parentNode.removeChild(fakeBody) : div.parentNode.removeChild(div); return !!ret; }, _hasOwnProperty = ({}).hasOwnProperty, hasOwnProperty; if ( !is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined') ) { hasOwnProperty = function (object, property) { return _hasOwnProperty.call(object, property); }; } else { hasOwnProperty = function (object, property) { return ((property in object) && is(object.constructor.prototype[property], 'undefined')); }; } if (!Function.prototype.bind) { Function.prototype.bind = function bind(that) { var target = this; if (typeof target != "function") { throw new TypeError(); } var args = slice.call(arguments, 1), bound = function () { if (this instanceof bound) { var F = function(){}; F.prototype = target.prototype; var self = new F; var result = target.apply( self, args.concat(slice.call(arguments)) ); if (Object(result) === result) { return result; } return self; } else { return target.apply( that, args.concat(slice.call(arguments)) ); } }; return bound; }; } function setCss( str ) { mStyle.cssText = str; } function setCssAll( str1, str2 ) { return setCss(prefixes.join(str1 + ';') + ( str2 || '' )); } function is( obj, type ) { return typeof obj === type; } function contains( str, substr ) { return !!~('' + str).indexOf(substr); } function testDOMProps( props, obj, elem ) { for ( var i in props ) { var item = obj[props[i]]; if ( item !== undefined) { if (elem === false) return props[i]; if (is(item, 'function')){ return item.bind(elem || obj); } return item; } } return false; } var testBundle = (function( styles, tests ) { var style = styles.join(''), len = tests.length; injectElementWithStyles(style, function( node, rule ) { var style = document.styleSheets[document.styleSheets.length - 1], cssText = style ? (style.cssRules && style.cssRules[0] ? style.cssRules[0].cssText : style.cssText || '') : '', children = node.childNodes, hash = {}; while ( len-- ) { hash[children[len].id] = children[len]; } Modernizr['touch'] = ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch || (hash['touch'] && hash['touch'].offsetTop) === 9; }, len, tests); })([ ,['@media (',prefixes.join('touch-enabled),('),mod,')', '{#touch{top:9px;position:absolute}}'].join('') ], [ ,'touch' ]); tests['touch'] = function() { return Modernizr['touch']; }; for ( var feature in tests ) { if ( hasOwnProperty(tests, feature) ) { featureName = feature.toLowerCase(); Modernizr[featureName] = tests[feature](); classes.push((Modernizr[featureName] ? '' : 'no-') + featureName); } } setCss(''); modElem = inputElem = null; Modernizr._version = version; Modernizr._prefixes = prefixes; Modernizr.testStyles = injectElementWithStyles; return Modernizr; })(this, this.document); ; (function(exports) { var MOUSE_ID = 1; function Pointer(identifier, type, event) { this.screenX = event.screenX || 0; this.screenY = event.screenY || 0; this.pageX = event.pageX || 0; this.pageY = event.pageY || 0; this.clientX = event.clientX || 0; this.clientY = event.clientY || 0; this.tiltX = event.tiltX || 0; this.tiltY = event.tiltY || 0; this.pressure = event.pressure || 0.0; this.hwTimestamp = event.hwTimestamp || 0; this.pointerType = type; this.identifier = identifier; } var PointerTypes = { TOUCH: 'touch', MOUSE: 'mouse', PEN: 'pen' }; function setMouse(mouseEvent) { mouseEvent.target.mouseEvent = mouseEvent; } function unsetMouse(mouseEvent) { mouseEvent.target.mouseEvent = null; } function setTouch(touchEvent) { touchEvent.target.touchList = touchEvent.targetTouches; } /** * Returns an array of all pointers currently on the screen. */ function getPointerList() { // Note: "this" is the element. var pointers = []; var pointer; if (this.touchList) { for (var i = 0; i < this.touchList.length; i++) { var touch = this.touchList[i]; // Add 2 to avoid clashing with the mouse identifier. pointer = new Pointer(touch.identifier + 2, PointerTypes.TOUCH, touch); pointers.push(pointer); } } else if (this.msPointerList) { for (var identifier in this.msPointerList) { if (!this.msPointerList.hasOwnProperty(identifier)) continue; pointer = this.msPointerList[identifier]; pointer = new Pointer(identifier, pointer.textPointerType, pointer); pointers.push(pointer); } } if (this.mouseEvent) { pointers.push(new Pointer(MOUSE_ID, PointerTypes.MOUSE, this.mouseEvent)); } return pointers; } function createCustomEvent(eventName, target, payload) { var event = document.createEvent('Event'); event.initEvent(eventName, true, true); for (var k in payload) { event[k] = payload[k]; } target.dispatchEvent(event); } /*************** Mouse event handlers *****************/ function mouseDownHandler(event) { event.preventDefault(); setMouse(event); var payload = { pointerType: 'mouse', getPointerList: getPointerList.bind(this), originalEvent: event }; createCustomEvent('pointerdown', event.target, payload); } function mouseMoveHandler(event) { event.preventDefault(); if (event.target.mouseEvent) { setMouse(event); } var payload = { pointerType: 'mouse', getPointerList: getPointerList.bind(this), originalEvent: event }; createCustomEvent('pointermove', event.target, payload); } function mouseUpHandler(event) { event.preventDefault(); unsetMouse(event); var payload = { pointerType: 'mouse', getPointerList: getPointerList.bind(this), originalEvent: event }; createCustomEvent('pointerup', event.target, payload); } /*************** Touch event handlers *****************/ function touchStartHandler(event) { console.log('touchstart'); event.preventDefault(); setTouch(event); var payload = { pointerType: 'touch', getPointerList: getPointerList.bind(this), originalEvent: event }; createCustomEvent('pointerdown', event.target, payload); } function touchMoveHandler(event) { event.preventDefault(); setTouch(event); var payload = { pointerType: 'touch', getPointerList: getPointerList.bind(this), originalEvent: event }; createCustomEvent('pointermove', event.target, payload); } function touchEndHandler(event) { event.preventDefault(); setTouch(event); var payload = { pointerType: 'touch', getPointerList: getPointerList.bind(this), originalEvent: event }; createCustomEvent('pointerup', event.target, payload); } function mouseOutHandler(event) { if (event.target.mouseEvent) { console.log(event); event.preventDefault(); unsetMouse(event); var payload = { pointerType: 'mouse', getPointerList: getPointerList.bind(this), originalEvent: event }; createCustomEvent('pointerup', event.target, payload); } } /*************** MSIE Pointer event handlers *****************/ function pointerDownHandler(event) { if (event.pointerType == 2) { event.textPointerType = PointerTypes.TOUCH; } else if (event.pointerType == 3) { event.textPointerType = PointerTypes.PEN; } else if (event.pointerType == 4) { event.textPointerType = PointerTypes.MOUSE; } if (event.textPointerType == PointerTypes.MOUSE) { event.target.msMouseDown = true; } if (!event.target.msPointerList) event.target.msPointerList = {}; event.target.msPointerList[event.pointerId] = event; var payload = { pointerType: event.textPointerType, getPointerList: getPointerList.bind(this), originalEvent: event }; createCustomEvent('pointerdown', event.target, payload); } function pointerMoveHandler(event) { if (event.pointerType == 2) { event.textPointerType = PointerTypes.TOUCH; } else if (event.pointerType == 3) { event.textPointerType = PointerTypes.PEN; } else if (event.pointerType == 4) { event.textPointerType = PointerTypes.MOUSE; } if (event.textPointerType == PointerTypes.MOUSE && !event.target.msMouseDown) { return; } if (!event.target.msPointerList) event.target.msPointerList = {}; event.target.msPointerList[event.pointerId] = event; var payload = { pointerType: event.textPointerType, getPointerList: getPointerList.bind(this), originalEvent: event }; createCustomEvent('pointermove', event.target, payload); } function pointerUpHandler(event) { if (event.target.msPointerList) { delete event.target.msPointerList[event.pointerId]; } if (event.pointerType == 2) { event.textPointerType = PointerTypes.TOUCH; } else if (event.pointerType == 3) { event.textPointerType = PointerTypes.PEN; } else if (event.pointerType == 4) { event.textPointerType = PointerTypes.MOUSE; } if (event.textPointerType == PointerTypes.MOUSE) { event.target.msMouseDown = false; } var payload = { pointerType: event.textPointerType, getPointerList: getPointerList.bind(this), originalEvent: event }; createCustomEvent('pointerup', event.target, payload); } /** * Causes the passed in element to broadcast pointer events instead * of mouse/touch/etc events. */ function emitPointers(el) { if (!el.isPointerEmitter) { // Latch on to all relevant events for this element. if (isPointer()) { el.addEventListener('pointerdown', pointerDownHandler); el.addEventListener('pointermove', pointerMoveHandler); el.addEventListener('pointerup', pointerUpHandler); } else if (isMSPointer()) { el.addEventListener('MSPointerDown', pointerDownHandler); el.addEventListener('MSPointerMove', pointerMoveHandler); el.addEventListener('MSPointerUp', pointerUpHandler); } else { if (isTouch()) { el.addEventListener('touchstart', touchStartHandler); el.addEventListener('touchmove', touchMoveHandler); el.addEventListener('touchend', touchEndHandler); } el.addEventListener('mousedown', mouseDownHandler); el.addEventListener('mousemove', mouseMoveHandler); el.addEventListener('mouseup', mouseUpHandler); // Necessary for the edge case that the mouse is down and you drag out of // the area. el.addEventListener('mouseout', mouseOutHandler); } el.isPointerEmitter = true; } } /** * @return {Boolean} Returns true iff this user agent supports touch events. */ function isTouch() { return Modernizr.touch; } /** * @return {Boolean} Returns true iff this user agent supports MSIE pointer * events. */ function isMSPointer() { return window.navigator.msPointerEnabled; } /** * @return {Boolean} Returns true iff this user agent supports pointer * events. */ function isPointer() { return window.navigator.pointerEnabled; } /** * Option 1: Require emitPointers call on all pointer event emitters. */ //exports.pointer = { // emitPointers: emitPointers, //}; /** * Option 2: Replace addEventListener with a custom version. */ function augmentAddEventListener(baseElementClass, customEventListener) { var oldAddEventListener = baseElementClass.prototype.addEventListener; baseElementClass.prototype.addEventListener = function(type, listener, useCapture) { customEventListener.call(this, type, listener, useCapture); oldAddEventListener.call(this, type, listener, useCapture); }; } function synthesizePointerEvents(type, listener, useCapture) { if (type.indexOf('pointer') === 0) { emitPointers(this); } } // Note: Firefox doesn't work like other browsers... overriding HTMLElement // doesn't actually affect anything. Special case for Firefox: if (navigator.userAgent.match(/Firefox/)) { // TODO: fix this for the general case. augmentAddEventListener(HTMLDivElement, synthesizePointerEvents); augmentAddEventListener(HTMLCanvasElement, synthesizePointerEvents); } else { augmentAddEventListener(HTMLElement, synthesizePointerEvents); } exports._createCustomEvent = createCustomEvent; exports._augmentAddEventListener = augmentAddEventListener; exports.PointerTypes = PointerTypes; })(window); (function(exports) { function synthesizeGestureEvents(type, listener, useCapture) { if (type.indexOf('gesture') === 0) { var handler = Gesture._gestureHandlers[type]; if (handler) { handler(this); } else { console.error('Warning: no handler found for {{evt}}.' .replace('{{evt}}', type)); } } } // Note: Firefox doesn't work like other browsers... overriding HTMLElement // doesn't actually affect anything. Special case for Firefox: if (navigator.userAgent.match(/Firefox/)) { // TODO: fix this for the general case. window._augmentAddEventListener(HTMLDivElement, synthesizeGestureEvents); window._augmentAddEventListener(HTMLCanvasElement, synthesizeGestureEvents); } else { window._augmentAddEventListener(HTMLElement, synthesizeGestureEvents); } exports.Gesture = exports.Gesture || {}; exports.Gesture._gestureHandlers = exports.Gesture._gestureHandlers || {}; })(window); /** * Gesture recognizer for the `doubletap` gesture. * * Taps happen when an element is pressed and then released. */ (function(exports) { var DOUBLETAP_TIME = 300; var WIGGLE_THRESHOLD = 10; /** * A simple object for storing the position of a pointer. */ function PointerPosition(pointer) { this.x = pointer.clientX; this.y = pointer.clientY; } /** * calculate the squared distance of the given pointer from this * PointerPosition's pointer */ PointerPosition.prototype.calculateSquaredDistance = function(pointer) { var dx = this.x - pointer.clientX; var dy = this.y - pointer.clientY; return dx*dx + dy*dy; }; function pointerDown(e) { var pointers = e.getPointerList(); if (pointers.length != 1) return; var now = new Date(); if (now - this.lastDownTime < DOUBLETAP_TIME && this.lastPosition && this.lastPosition.calculateSquaredDistance(pointers[0]) < WIGGLE_THRESHOLD * WIGGLE_THRESHOLD) { this.lastDownTime = 0; this.lastPosition = null; var payload = { }; window._createCustomEvent('gesturedoubletap', e.target, payload); } this.lastPosition = new PointerPosition(pointers[0]); this.lastDownTime = now; } /** * Make the specified element create gesturetap events. */ function emitDoubleTaps(el) { el.addEventListener('pointerdown', pointerDown); } exports.Gesture._gestureHandlers.gesturedoubletap = emitDoubleTaps; })(window); /** * Gesture recognizer for the `longpress` gesture. * * Longpress happens when pointer is pressed and doesn't get released * for a while (without movement). */ (function(exports) { var LONGPRESS_TIME = 600; var WIGGLE_THRESHOLD = 5; /** * A simple object for storing the position of a pointer. */ function PointerPosition(pointer) { this.x = pointer.clientX; this.y = pointer.clientY; } /** * calculate the squared distance of the given pointer from this * PointerPosition's pointer */ PointerPosition.prototype.calculateSquaredDistance = function(pointer) { var dx = this.x - pointer.clientX; var dy = this.y - pointer.clientY; return dx*dx + dy*dy; }; function pointerDown(e) { // Something went down. Clear the last press if there was one. clearTimeout(this.longPressTimer); var pointers = e.getPointerList(); // check that we only have one pointer down if(pointers.length === 1) { // cache the position of the pointer on the target e.target.longpressInitPosition = new PointerPosition(pointers[0]); // Start a timer. this.longPressTimer = setTimeout(function() { payload = {}; window._createCustomEvent('gesturelongpress', e.target, payload); }, LONGPRESS_TIME); } } function pointerMove(e) { var pointers = e.getPointerList(); if(e.pointerType === PointerTypes.MOUSE) { // if the pointer is a mouse we cancel the longpress // as soon as it starts wiggling around clearTimeout(this.longPressTimer); } else if(pointers.length === 1) { // but if the pointer is something else we allow a // for a bit of smudge space var pos = e.target.longpressInitPosition; if(pos && pos.calculateSquaredDistance(pointers[0]) > WIGGLE_THRESHOLD * WIGGLE_THRESHOLD) { clearTimeout(this.longPressTimer); } } } function pointerUp(e) { clearTimeout(this.longPressTimer); } /** * Make the specified element create gesturetap events. */ function emitLongPresses(el) { el.addEventListener('pointerdown', pointerDown); el.addEventListener('pointermove', pointerMove); el.addEventListener('pointerup', pointerUp); } exports.Gesture._gestureHandlers.gesturelongpress = emitLongPresses; })(window); /** * Gesture recognizer for the `scale` gesture. * * Scale happens when two fingers are placed on the screen, and then * they move so the the distance between them is greater or less than a * certain threshold. */ (function(exports) { var SCALE_THRESHOLD = 0.2; function PointerPair(p1, p2) { this.p1 = p1; this.p2 = p2; } /** * Calculate the center of the two pointers. */ PointerPair.prototype.center = function() { return [(this.p1.pageX + this.p2.pageX) / 2, (this.p1.pageY + this.p2.pageY) / 2]; }; /** * Calculate the distance between the two pointers. */ PointerPair.prototype.span = function() { var dx = this.p1.pageX - this.p2.pageX; var dy = this.p1.pageY - this.p2.pageY; return Math.sqrt(dx*dx + dy*dy); }; /** * Given a reference pair, calculate the scale multiplier difference. */ PointerPair.prototype.scaleSince = function(referencePair) { var originalSpan = this.span(); var referenceSpan = referencePair.span(); if (referenceSpan === 0) { return 0; } else return originalSpan / referenceSpan; }; function pointerDown(e) { var pointerList = e.getPointerList(); // If there are exactly two pointers down, if (pointerList.length == 2) { // Record the initial pointer pair. e.target.scaleReferencePair = new PointerPair(pointerList[0], pointerList[1]); } } function pointerMove(e) { var pointerList = e.getPointerList(); // If there are two pointers down, compare to the initial pointer pair. if (pointerList.length == 2 && e.target.scaleReferencePair) { var pair = new PointerPair(pointerList[0], pointerList[1]); // Compute the scaling value according to the difference. var scale = pair.scaleSince(e.target.scaleReferencePair); // If the movement is drastic enough: if (Math.abs(1 - scale) > SCALE_THRESHOLD) { // Create the scale event as a result. var payload = { scale: scale, centerX: (e.target.scaleReferencePair.p1.clientX + e.target.scaleReferencePair.p2.clientX) / 2, centerY: (e.target.scaleReferencePair.p1.clientY + e.target.scaleReferencePair.p2.clientY) / 2 }; window._createCustomEvent('gesturescale', e.target, payload); } } } function pointerUp(e) { e.target.scaleReferencePair = null; } /** * Make the specified element create gesturetap events. */ function emitScale(el) { el.addEventListener('pointerdown', pointerDown); el.addEventListener('pointermove', pointerMove); el.addEventListener('pointerup', pointerUp); } exports.Gesture._gestureHandlers.gesturescale = emitScale; })(window);