diff --git a/docs/app/js/preload.js b/docs/app/js/preload.js new file mode 100644 index 00000000000..e48022d8259 --- /dev/null +++ b/docs/app/js/preload.js @@ -0,0 +1,2 @@ +// Used only to optionally test +// jQuery w/ docs (see docs/gulpfile#docs-js) diff --git a/src/components/fabSpeedDial/fabController.js b/src/components/fabSpeedDial/fabController.js index 4178a4dd4c2..9240a237932 100644 --- a/src/components/fabSpeedDial/fabController.js +++ b/src/components/fabSpeedDial/fabController.js @@ -46,7 +46,7 @@ function setupListeners() { var eventTypes = [ - 'mousedown', 'mouseup', 'click', 'touchstart', 'touchend', 'focusin', 'focusout' + 'click', 'focusin', 'focusout' ]; // Add our listeners @@ -111,55 +111,17 @@ }); } + var lastSrc; function parseEvents(latestEvent) { - events.push(latestEvent.type); - - // Handle desktop click - if (equalsEvents(['mousedown', 'focusout?', 'focusin?', 'mouseup', 'click'])) { - handleItemClick(latestEvent); - resetEvents(); - return; - } - - // Handle mobile click/tap (and keyboard enter) - if (equalsEvents(['touchstart?', 'touchend?', 'click'])) { + if (latestEvent.srcEvent && latestEvent.srcEvent == lastSrc) return; + if (latestEvent.type == 'click') { handleItemClick(latestEvent); - resetEvents(); - return; - } - - // Handle tab keys (focusin) - if (equalsEvents(['focusin'])) { + } else if (latestEvent.type == 'focusin') { vm.open(); - resetEvents(); - return; - } - - // Handle tab keys (focusout) - if (equalsEvents(['focusout'])) { + } else if (latestEvent.type == 'focusout') { vm.close(); - resetEvents(); - return; } - - eventUnhandled(); - } - - /* - * No event was handled, so setup a timeout to clear the events - * - * TODO: Use $mdUtil.debounce()? - */ - var resetEventsTimeout; - - function eventUnhandled() { - if (resetEventsTimeout) { - window.clearTimeout(resetEventsTimeout); - } - - resetEventsTimeout = window.setTimeout(function() { - resetEvents(); - }, 250); + lastSrc = latestEvent.srcEvent; } function resetActionIndex() { diff --git a/src/components/tooltip/tooltip.js b/src/components/tooltip/tooltip.js index 8ccb70fdcc5..e0677b18346 100644 --- a/src/components/tooltip/tooltip.js +++ b/src/components/tooltip/tooltip.js @@ -168,13 +168,13 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe elementFocusedOnWindowBlur = false; return; } - parent.on('blur mouseleave touchend touchcancel', leaveHandler ); + parent.on('blur mouseleave touchcancel', leaveHandler ); setVisible(true); }; - var leaveHandler = function () { + var leaveHandler = function (e) { var autohide = scope.hasOwnProperty('autohide') ? scope.autohide : attr.hasOwnProperty('mdAutohide'); if (autohide || mouseActive || ($document[0].activeElement !== parent[0]) ) { - parent.off('blur mouseleave touchend touchcancel', leaveHandler ); + parent.off('blur mouseleave touchcancel', leaveHandler ); parent.triggerHandler("blur"); setVisible(false); } diff --git a/src/core/services/gesture/gesture.js b/src/core/services/gesture/gesture.js index db07cc5d709..cbcede7e221 100644 --- a/src/core/services/gesture/gesture.js +++ b/src/core/services/gesture/gesture.js @@ -81,16 +81,55 @@ }; if (self.isHijackingClicks) { + var maxClickDistance = 6; self.handler('click', { options: { - maxDistance: 6 + maxDistance: maxClickDistance }, - onEnd: function (ev, pointer) { + onEnd: checkDistanceAndEmit('click') + }); + + self.handler('focus', { + options: { + maxDistance: maxClickDistance + }, + onEnd: function(ev, pointer) { if (pointer.distance < this.state.options.maxDistance) { - this.dispatchEvent(ev, 'click'); + if (canFocus(ev.target)) { + this.dispatchEvent(ev, 'focus', pointer); + ev.target.focus(); + } + } + + function canFocus(element) { + return element.hasAttribute('href') || + (( element.nodeName == 'INPUT' || element.nodeName == 'BUTTON' || + element.nodeName == 'SELECT' || element.nodeName == 'TEXTAREA' ) && !element.hasAttribute('DISABLED')) || + ( element.hasAttribute('tabindex') && element.getAttribute('tabindex') != '-1' ); } } }); + + self.handler('mouseup', { + options: { + maxDistance: maxClickDistance + }, + onEnd: checkDistanceAndEmit('mouseup') + }); + + self.handler('mousedown', { + onStart: function(ev) { + this.dispatchEvent(ev, 'mousedown'); + } + }); + } + + function checkDistanceAndEmit(eventName) { + return function(ev, pointer) { + if (pointer.distance < this.state.options.maxDistance) { + this.dispatchEvent(ev, eventName, pointer); + } + }; } /* @@ -407,10 +446,10 @@ eventPointer = eventPointer || pointer; var eventObj; - if (eventType === 'click') { + if (eventType === 'click' || eventType == 'mouseup' || eventType == 'mousedown' ) { eventObj = document.createEvent('MouseEvents'); eventObj.initMouseEvent( - 'click', true, true, window, srcEvent.detail, + eventType, true, true, window, srcEvent.detail, eventPointer.x, eventPointer.y, eventPointer.x, eventPointer.y, srcEvent.ctrlKey, srcEvent.altKey, srcEvent.shiftKey, srcEvent.metaKey, srcEvent.button, srcEvent.relatedTarget || null @@ -466,6 +505,33 @@ } } }, true); + + document.addEventListener('mouseup', function clickHijacker(ev) { + var isKeyClick = !ev.clientX && !ev.clientY; + if (!isKeyClick && !ev.$material && !ev.isIonicTap + && !isInputEventFromLabelClick(ev)) { + ev.preventDefault(); + ev.stopPropagation(); + } + }, true); + + document.addEventListener('mousedown', function clickHijacker(ev) { + var isKeyClick = !ev.clientX && !ev.clientY; + if (!isKeyClick && !ev.$material && !ev.isIonicTap + && !isInputEventFromLabelClick(ev)) { + ev.preventDefault(); + ev.stopPropagation(); + } + }, true); + + document.addEventListener('focus', function clickHijacker(ev) { + var isKeyClick = !ev.clientX && !ev.clientY; + if (!isKeyClick && !ev.$material && !ev.isIonicTap + && !isInputEventFromLabelClick(ev)) { + ev.preventDefault(); + ev.stopPropagation(); + } + }, true); isInitialized = true; }