From 43b8b4ad67f52023d176c483bb0aa93af45a59ff Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 17 Jul 2015 12:24:30 -0500 Subject: [PATCH] TapClick for a,button --- ionic/components.ts | 3 +- ionic/components/action-menu/action-menu.ts | 5 +- ionic/components/button/button.ts | 54 ++ ionic/components/checkbox/checkbox.ts | 2 +- ionic/components/form/focus-holder.ts | 2 +- ionic/components/form/tap-input.ts | 2 +- ionic/components/form/text-input.ts | 2 +- ionic/components/nav/swipe-handle.ts | 4 + ionic/config/annotations.ts | 7 +- ionic/gestures/gesture.ts | 4 + ionic/ionic.ts | 1 - ionic/util/tap.ts | 603 -------------------- 12 files changed, 77 insertions(+), 612 deletions(-) create mode 100644 ionic/components/button/button.ts delete mode 100644 ionic/util/tap.ts diff --git a/ionic/components.ts b/ionic/components.ts index 19f64eec56..a98961691f 100644 --- a/ionic/components.ts +++ b/ionic/components.ts @@ -2,12 +2,13 @@ export * from 'ionic/components/app/app' export * from 'ionic/components/action-menu/action-menu' export * from 'ionic/components/aside/aside' +export * from 'ionic/components/button/button' export * from 'ionic/components/checkbox/checkbox' export * from 'ionic/components/content/content' export * from 'ionic/components/icon/icon' export * from 'ionic/components/item/item' export * from 'ionic/components/item/item-group' -export * from 'ionic/components/form/form' +export * from 'ionic/components/form/input' export * from 'ionic/components/form/text-input' export * from 'ionic/components/form/tap-input' export * from 'ionic/components/form/label' diff --git a/ionic/components/action-menu/action-menu.ts b/ionic/components/action-menu/action-menu.ts index 027ea4c2b4..fe669bbd93 100644 --- a/ionic/components/action-menu/action-menu.ts +++ b/ionic/components/action-menu/action-menu.ts @@ -8,6 +8,7 @@ import {View, Injectable, NgFor, NgIf} from 'angular2/angular2'; +import {TapClick} from '../button/button'; import {Overlay} from '../overlay/overlay'; import {Animation} from '../../animations/animation'; import * as util from 'ionic/util'; @@ -15,7 +16,7 @@ import * as util from 'ionic/util'; @View({ template: ` -
+
@@ -28,7 +29,7 @@ import * as util from 'ionic/util';
`, - directives: [NgFor, NgIf] + directives: [NgFor, NgIf, TapClick] }) class ActionMenuDirective { diff --git a/ionic/components/button/button.ts b/ionic/components/button/button.ts new file mode 100644 index 0000000000..0a406611f0 --- /dev/null +++ b/ionic/components/button/button.ts @@ -0,0 +1,54 @@ +import {Directive, ElementRef, Optional, Ancestor} from 'angular2/angular2'; + +import {IonicConfig} from '../../config/config'; +import {Gesture} from '../../gestures/gesture'; +import * as dom from '../../util/dom'; + + +@Directive({ + selector: '[tap-disabled]' +}) +export class TapDisabled {} + + +@Directive({ + selector: 'a,button,[tappable]' +}) +export class TapClick { + + constructor( + elementRef: ElementRef, + config: IonicConfig, + @Optional() @Ancestor() tapDisabled: TapDisabled + ) { + + if (config.setting('tapPolyfill') && !this.tapGesture && !tapDisabled) { + this.tapGesture = new Gesture(elementRef.nativeElement); + this.tapGesture.listen(); + + this.tapGesture.on('tap', (gestureEv) => { + this.onTap(gestureEv.gesture.srcEvent); + }); + } + + } + + onTap(ev) { + if (ev && this.tapGesture) { + ev.preventDefault(); + ev.stopPropagation(); + + let c = dom.pointerCoord(ev); + + let clickEvent = document.createEvent("MouseEvents"); + clickEvent.initMouseEvent('click', true, true, window, 1, 0, 0, c.x, c.y, false, false, false, false, 0, null); + clickEvent.isIonicTap = true; + this.tapGesture.element.dispatchEvent(clickEvent); + } + } + + onDestroy() { + this.tapGesture && this.tapGesture.destroy(); + } + +} diff --git a/ionic/components/checkbox/checkbox.ts b/ionic/components/checkbox/checkbox.ts index 3939f674e7..e54fd584aa 100644 --- a/ionic/components/checkbox/checkbox.ts +++ b/ionic/components/checkbox/checkbox.ts @@ -8,7 +8,7 @@ import { } from 'angular2/angular2'; import {Ion} from '../ion'; -import {IonInputItem} from '../form/form'; +import {IonInputItem} from '../form/input'; import {IonicConfig} from '../../config/config'; import {IonicComponent, IonicView} from '../../config/annotations'; import {Icon} from '../icon/icon'; diff --git a/ionic/components/form/focus-holder.ts b/ionic/components/form/focus-holder.ts index 415060fbc1..ae14af6499 100644 --- a/ionic/components/form/focus-holder.ts +++ b/ionic/components/form/focus-holder.ts @@ -3,7 +3,7 @@ import {Component, Directive, View, Parent, ElementRef, forwardRef} from 'angula import {IonicConfig} from '../../config/config'; import * as dom from '../../util/dom'; import {Platform} from '../../platform/platform'; -import {IonInput} from './form'; +import {IonInput} from './input'; @Component({ diff --git a/ionic/components/form/tap-input.ts b/ionic/components/form/tap-input.ts index 93ae80af8c..b8712c35f4 100644 --- a/ionic/components/form/tap-input.ts +++ b/ionic/components/form/tap-input.ts @@ -1,6 +1,6 @@ import {Parent, Ancestor, Optional, ElementRef, Attribute, Directive} from 'angular2/angular2'; -import {IonInput} from './form'; +import {IonInput} from './input'; import {IonicApp} from '../app/app'; import {IonicConfig} from '../../config/config'; import {Content} from '../content/content'; diff --git a/ionic/components/form/text-input.ts b/ionic/components/form/text-input.ts index deb06b7f91..0fc17624ac 100644 --- a/ionic/components/form/text-input.ts +++ b/ionic/components/form/text-input.ts @@ -2,7 +2,7 @@ import {Directive, View, Parent, Ancestor, Optional, ElementRef, Attribute, forw import {IonicDirective} from '../../config/annotations'; import {IonicConfig} from '../../config/config'; -import {IonInput, IonInputItem} from './form'; +import {IonInput, IonInputItem} from './input'; import {IonicApp} from '../app/app'; import {Content} from '../content/content'; import {ClickBlock} from '../../util/click-block'; diff --git a/ionic/components/nav/swipe-handle.ts b/ionic/components/nav/swipe-handle.ts index f92878eaa7..9d7c63bbcc 100644 --- a/ionic/components/nav/swipe-handle.ts +++ b/ionic/components/nav/swipe-handle.ts @@ -93,4 +93,8 @@ export class SwipeHandle { return (this.viewCtrl ? this.viewCtrl.swipeBackEnabled() : false); } + onDestroy() { + this.gesture && self.gesture.destroy(); + } + } diff --git a/ionic/config/annotations.ts b/ionic/config/annotations.ts index c2c0221d98..4721d42e06 100644 --- a/ionic/config/annotations.ts +++ b/ionic/config/annotations.ts @@ -16,6 +16,7 @@ import { Segment, SegmentButton, SegmentControlValueAccessor, RadioGroup, RadioButton, SearchBar, Nav, NavbarTemplate, Navbar, NavPush, NavPop, + TapClick, TapDisabled, Register } from '../ionic'; @@ -71,7 +72,11 @@ export const IonicDirectives = [ forwardRef(() => Navbar), forwardRef(() => NavPush), forwardRef(() => NavPop), - forwardRef(() => Register) + forwardRef(() => Register), + + // Gestures + forwardRef(() => TapClick), + forwardRef(() => TapDisabled), ]; class IonicViewImpl extends View { diff --git a/ionic/gestures/gesture.ts b/ionic/gestures/gesture.ts index 5b3ff89a38..9b4a7d1836 100644 --- a/ionic/gestures/gesture.ts +++ b/ionic/gestures/gesture.ts @@ -1,6 +1,7 @@ import * as util from 'ionic/util'; import {Hammer} from 'ionic/gestures/hammer'; + export class Gesture { constructor(element, opts = {}) { util.defaults(opts, { @@ -18,6 +19,7 @@ export class Gesture { this._callbacks = {}; } + options(opts = {}) { util.extend(this._options, opts); } @@ -31,6 +33,7 @@ export class Gesture { listen() { this.hammertime = Hammer(this.element, this._options); } + unlisten() { this.hammertime.destroy(); this.hammertime = null; @@ -41,6 +44,7 @@ export class Gesture { } this._callbacks = {} } + destroy() { this.unlisten() } diff --git a/ionic/ionic.ts b/ionic/ionic.ts index 12595b6f9d..80f99394d1 100644 --- a/ionic/ionic.ts +++ b/ionic/ionic.ts @@ -16,7 +16,6 @@ export * from 'ionic/routing/url-state' export * from 'ionic/util/click-block' export * from 'ionic/util/focus' -export * from 'ionic/util/tap' export * from 'ionic/animations/animation' export * from 'ionic/animations/builtins' diff --git a/ionic/util/tap.ts b/ionic/util/tap.ts deleted file mode 100644 index a7fce7596a..0000000000 --- a/ionic/util/tap.ts +++ /dev/null @@ -1,603 +0,0 @@ -import {dom} from './dom' -import {Platform} from '../platform/platform' - -/** - * @ngdoc page - * @name tap - * @module ionic - * @description - * On touch devices such as a phone or tablet, some browsers implement a 300ms delay between - * the time the user stops touching the display and the moment the browser executes the - * click. This delay was initially introduced so the browser can know whether the user wants to - * double-tap to zoom in on the webpage. Basically, the browser waits roughly 300ms to see if - * the user is double-tapping, or just tapping on the display once. - * - * Out of the box, Ionic automatically removes the 300ms delay in order to make Ionic apps - * feel more "native" like. Resultingly, other solutions such as - * [fastclick](https://github.com/ftlabs/fastclick) and Angular's - * [ngTouch](https://docs.angularjs.org/api/ngTouch) should not be included, to avoid conflicts. - * - * Some browsers already remove the delay with certain settings, such as the CSS property - * `touch-events: none` or with specific meta tag viewport values. However, each of these - * browsers still handle clicks differently, such as when to fire off or cancel the event - * (like scrolling when the target is a button, or holding a button down). - * For browsers that already remove the 300ms delay, consider Ionic's tap system as a way to - * normalize how clicks are handled across the various devices so there's an expected response - * no matter what the device, platform or version. Additionally, Ionic will prevent - * ghostclicks which even browsers that remove the delay still experience. - * - * In some cases, third-party libraries may also be working with touch events which can interfere - * with the tap system. For example, mapping libraries like Google or Leaflet Maps often implement - * a touch detection system which conflicts with Ionic's tap system. - * - * ### Disabling the tap system - * - * To disable the tap for an element and all of its children elements, - * add the attribute `data-tap-disabled="true"`. - * - * ```html - *
- *
- *
- * ``` - * - * ### Additional Notes: - * - * - Ionic tap works with Ionic's JavaScript scrolling - * - Elements can come and go from the DOM and Ionic tap doesn't keep adding and removing - * listeners - * - No "tap delay" after the first "tap" (you can tap as fast as you want, they all click) - * - Minimal events listeners, only being added to document - * - Correct focus in/out on each input type (select, textearea, range) on each platform/device - * - Shows and hides virtual keyboard correctly for each platform/device - * - Works with labels surrounding inputs - * - Does not fire off a click if the user moves the pointer too far - * - Adds and removes an 'activated' css class - * - Multiple [unit tests](https://github.com/driftyco/ionic/blob/master/test/unit/utils/tap.unit.js) for each scenario - * - */ -/* - - IONIC TAP - --------------- - - Both touch and mouse events are added to the document.body on DOM ready - - If a touch event happens, it does not use mouse event listeners - - On touchend, if the distance between start and end was small, trigger a click - - In the triggered click event, add a 'isIonicTap' property - - The triggered click receives the same x,y coordinates as as the end event - - On document.body click listener (with useCapture=true), only allow clicks with 'isIonicTap' - - Triggering clicks with mouse events work the same as touch, except with mousedown/mouseup - - Tapping inputs is disabled during scrolling -*/ - -var tapDoc; // the element which the listeners are on (document.body) -var tapActiveEle; // the element which is active (probably has focus) -var tapEnabledTouchEvents; -var tapMouseResetTimer; -var tapPointerMoved; -var tapPointerStart; -var tapTouchFocusedInput; -var tapLastTouchTarget; -var tapTouchMoveListener = 'touchmove'; - -// how much the coordinates can be off between start/end, but still a click -var TAP_RELEASE_TOLERANCE = 12; // default tolerance -var TAP_RELEASE_BUTTON_TOLERANCE = 50; // button elements should have a larger tolerance - -var tapEventListeners = { - 'click': tapClickGateKeeper, - - 'mousedown': tapMouseDown, - 'mouseup': tapMouseUp, - 'mousemove': tapMouseMove, - - 'touchstart': tapTouchStart, - 'touchend': tapTouchEnd, - 'touchcancel': tapTouchCancel, - 'touchmove': tapTouchMove, - - 'focusin': tapFocusIn, - 'focusout': tapFocusOut -}; - -Platform.ready().then(config => { - - if (config.setting('tapPolyfill')) { - // console.log('Tap.register, tapPolyfill') - // Tap.register(document); - } - -}); - - -export let Tap = { - - register: function(ele) { - tapDoc = ele; - - tapEventListener('click', true, true); - tapEventListener('mouseup'); - tapEventListener('mousedown'); - - tapEventListener('touchstart'); - tapEventListener('touchend'); - tapEventListener('touchcancel'); - - tapEventListener('focusin'); - tapEventListener('focusout'); - - return function() { - for (var type in tapEventListeners) { - tapEventListener(type, false); - } - tapDoc = null; - tapActiveEle = null; - tapEnabledTouchEvents = false; - tapPointerMoved = false; - tapPointerStart = null; - }; - }, - - ignoreScrollStart: function(e) { - return (e.defaultPrevented) || // defaultPrevented has been assigned by another component handling the event - (/^(file|range)$/i).test(e.target.type) || - (e.target.dataset ? e.target.dataset.preventScroll : e.target.getAttribute('data-prevent-scroll')) == 'true' || // manually set within an elements attributes - (!!(/^(object|embed)$/i).test(e.target.tagName)) || // flash/movie/object touches should not try to scroll - Tap.isElementTapDisabled(e.target); // check if this element, or an ancestor, has `data-tap-disabled` attribute - }, - - isTextInput: function(ele) { - return !!ele && - (ele.tagName == 'TEXTAREA' || - ele.contentEditable === 'true' || - (ele.tagName == 'INPUT' && !(/^(radio|checkbox|range|file|submit|reset|color|image|button)$/i).test(ele.type))); - }, - - isDateInput: function(ele) { - return !!ele && - (ele.tagName == 'INPUT' && (/^(date|time|datetime-local|month|week)$/i).test(ele.type)); - }, - - isKeyboardElement: function(ele) { - if (Platform.isDevice('ipad') ) { - return Tap.isTextInput(ele) && !Tap.isDateInput(ele); - } else { - return Tap.isTextInput(ele) || ( !!ele && ele.tagName == "SELECT"); - } - }, - - isLabelWithTextInput: function(ele) { - var container = tapContainingElement(ele, false); - - return !!container && - Tap.isTextInput(tapTargetElement(container)); - }, - - containsOrIsTextInput: function(ele) { - return Tap.isTextInput(ele) || Tap.isLabelWithTextInput(ele); - }, - - cloneFocusedInput: function(container) { - if (Tap.hasCheckedClone) return; - Tap.hasCheckedClone = true; - - dom.raf(function() { - var focusInput = container.querySelector(':focus'); - if (Tap.isTextInput(focusInput)) { - var clonedInput = focusInput.cloneNode(true); - - clonedInput.value = focusInput.value; - clonedInput.classList.add('cloned-text-input'); - clonedInput.readOnly = true; - if (focusInput.isContentEditable) { - clonedInput.contentEditable = focusInput.contentEditable; - clonedInput.innerHTML = focusInput.innerHTML; - } - focusInput.parentElement.insertBefore(clonedInput, focusInput); - focusInput.classList.add('previous-input-focus'); - - clonedInput.scrollTop = focusInput.scrollTop; - } - }); - }, - - hasCheckedClone: false, - - removeClonedInputs: function(container) { - Tap.hasCheckedClone = false; - - dom.raf(function() { - var clonedInputs = container.querySelectorAll('.cloned-text-input'); - var previousInputFocus = container.querySelectorAll('.previous-input-focus'); - var x; - - for (x = 0; x < clonedInputs.length; x++) { - clonedInputs[x].parentElement.removeChild(clonedInputs[x]); - } - - for (x = 0; x < previousInputFocus.length; x++) { - previousInputFocus[x].classList.remove('previous-input-focus'); - previousInputFocus[x].style.top = ''; - - // TODO(tlancina): Get this back to life - // if ( ionic.keyboard.isOpen && !ionic.keyboard.isClosing ) { - // previousInputFocus[x].focus(); - // } - } - }); - }, - - requiresNativeClick: function(ele) { - if (!ele || ele.disabled || (/^(file|range)$/i).test(ele.type) || (/^(object|video)$/i).test(ele.tagName) || Tap.isLabelContainingFileInput(ele)) { - return true; - } - return Tap.isElementTapDisabled(ele); - }, - - isLabelContainingFileInput: function(ele) { - var lbl = tapContainingElement(ele); - if (lbl.tagName !== 'LABEL') return false; - var fileInput = lbl.querySelector('input[type=file]'); - if (fileInput && fileInput.disabled === false) return true; - return false; - }, - - isElementTapDisabled: function(ele) { - if (ele && ele.nodeType === 1) { - var element = ele; - while (element) { - if ((element.dataset ? element.dataset.tapDisabled : element.getAttribute('data-tap-disabled')) == 'true') { - return true; - } - element = element.parentElement; - } - } - return false; - }, - - setTolerance: function(releaseTolerance, releaseButtonTolerance) { - TAP_RELEASE_TOLERANCE = releaseTolerance; - TAP_RELEASE_BUTTON_TOLERANCE = releaseButtonTolerance; - }, - - cancelClick: function() { - // used to cancel any simulated clicks which may happen on a touchend/mouseup - // gestures uses this method within its tap and hold events - tapPointerMoved = true; - }, - - pointerCoord: function(event) { - // This method can get coordinates for both a mouse click - // or a touch depending on the given event - var c = { x: 0, y: 0 }; - if (event) { - var touches = event.touches && event.touches.length ? event.touches : [event]; - var e = (event.changedTouches && event.changedTouches[0]) || touches[0]; - if (e) { - c.x = e.clientX || e.pageX || 0; - c.y = e.clientY || e.pageY || 0; - } - } - return c; - } - -}; - -function tapEventListener(type, enable, useCapture) { - if (enable !== false) { - tapDoc.addEventListener(type, tapEventListeners[type], useCapture); - } else { - tapDoc.removeEventListener(type, tapEventListeners[type]); - } -} - -function tapClick(e) { - // simulate a normal click by running the element's click method then focus on it - var container = tapContainingElement(e.target); - var ele = tapTargetElement(container); - - if (Tap.requiresNativeClick(ele) || tapPointerMoved) return false; - - var c = Tap.pointerCoord(e); - - //console.log('tapClick', e.type, ele.tagName, '('+c.x+','+c.y+')'); - triggerMouseEvent('click', ele, c.x, c.y); - - // if it's an input, focus in on the target, otherwise blur - tapHandleFocus(ele); -} - -function triggerMouseEvent(type, ele, x, y) { - // using initMouseEvent instead of MouseEvent for our Android friends - var clickEvent = document.createEvent("MouseEvents"); - clickEvent.initMouseEvent(type, true, true, window, 1, 0, 0, x, y, false, false, false, false, 0, null); - clickEvent.isIonicTap = true; - ele.dispatchEvent(clickEvent); -} - -function tapClickGateKeeper(e) { - //console.log('click ' + Date.now() + ' isIonicTap: ' + (e.isIonicTap ? true : false)); - if (e.target.type == 'submit' && e.detail === 0) { - // do not prevent click if it came from an "Enter" or "Go" keypress submit - return null; - } - - // do not allow through any click events that were not created by Tap - - // TODO(mlynch): re-enable this check - //if ((ionic.scroll.isScrolling && Tap.containsOrIsTextInput(e.target)) || (!e.isIonicTap && !Tap.requiresNativeClick(e.target))) { - if (!e.isIonicTap && !Tap.requiresNativeClick(e.target)) { - //console.log('clickPrevent', e.target.tagName); - e.stopPropagation(); - - if (!Tap.isLabelWithTextInput(e.target)) { - // labels clicks from native should not preventDefault othersize keyboard will not show on input focus - e.preventDefault(); - } - return false; - } -} - -// MOUSE -function tapMouseDown(e) { - //console.log('mousedown ' + Date.now()); - if (e.isIonicTap || tapIgnoreEvent(e)) return null; - - if (tapEnabledTouchEvents) { - console.log('mousedown', 'stop event'); - e.stopPropagation(); - - if ((!Tap.isTextInput(e.target) || tapLastTouchTarget !== e.target) && !(/^(select|option)$/i).test(e.target.tagName)) { - // If you preventDefault on a text input then you cannot move its text caret/cursor. - // Allow through only the text input default. However, without preventDefault on an - // input the 300ms delay can change focus on inputs after the keyboard shows up. - // The focusin event handles the chance of focus changing after the keyboard shows. - e.preventDefault(); - } - - return false; - } - - tapPointerMoved = false; - tapPointerStart = Tap.pointerCoord(e); - - tapEventListener('mousemove'); - - // TODO(mlynch): re-enable - // ionic.activator.start(e); -} - -function tapMouseUp(e) { - //console.log("mouseup " + Date.now()); - if (tapEnabledTouchEvents) { - e.stopPropagation(); - e.preventDefault(); - return false; - } - - if (tapIgnoreEvent(e) || (/^(select|option)$/i).test(e.target.tagName)) return false; - - if (!tapHasPointerMoved(e)) { - tapClick(e); - } - tapEventListener('mousemove', false); - // TODO(mlynch): re-enable - // ionic.activator.end(); - tapPointerMoved = false; -} - -function tapMouseMove(e) { - if (tapHasPointerMoved(e)) { - tapEventListener('mousemove', false); - // TODO(mlynch): re-enable - // ionic.activator.end(); - tapPointerMoved = true; - return false; - } -} - - -// TOUCH -function tapTouchStart(e) { - //console.log("touchstart " + Date.now()); - if (tapIgnoreEvent(e)) return; - - tapPointerMoved = false; - - tapEnableTouchEvents(); - tapPointerStart = Tap.pointerCoord(e); - - tapEventListener(tapTouchMoveListener); - // TODO(mlynch): re-enable - //ionic.activator.start(e); - - if (Tap.isLabelWithTextInput(e.target)) { - // if the tapped element is a label, which has a child input - // then preventDefault so iOS doesn't ugly auto scroll to the input - // but do not prevent default on Android or else you cannot move the text caret - // and do not prevent default on Android or else no virtual keyboard shows up - - var textInput = tapTargetElement(tapContainingElement(e.target)); - if (textInput !== tapActiveEle) { - // don't preventDefault on an already focused input or else iOS's text caret isn't usable - e.preventDefault(); - } - } -} - -function tapTouchEnd(e) { - //console.log('touchend ' + Date.now()); - if (tapIgnoreEvent(e)) return; - - tapEnableTouchEvents(); - if (!tapHasPointerMoved(e)) { - tapClick(e); - - if ((/^(select|option)$/i).test(e.target.tagName)) { - e.preventDefault(); - } - } - - tapLastTouchTarget = e.target; - tapTouchCancel(); -} - -function tapTouchMove(e) { - if (tapHasPointerMoved(e)) { - tapPointerMoved = true; - tapEventListener(tapTouchMoveListener, false); - - // TODO(mlynch): re-enable - // ionic.activator.end(); - return false; - } -} - -function tapTouchCancel() { - tapEventListener(tapTouchMoveListener, false); - // TODO(mlynch): re-enable - // ionic.activator.end(); - tapPointerMoved = false; -} - -function tapEnableTouchEvents() { - tapEnabledTouchEvents = true; - clearTimeout(tapMouseResetTimer); - tapMouseResetTimer = setTimeout(function() { - tapEnabledTouchEvents = false; - }, 600); -} - -function tapIgnoreEvent(e) { - if (e.isTapHandled) return true; - e.isTapHandled = true; - - // TODO(mlynch): re-enable - /* - if (ionic.scroll.isScrolling && Tap.containsOrIsTextInput(e.target)) { - e.preventDefault(); - return true; - } - */ - if (Tap.containsOrIsTextInput(e.target)) { - e.preventDefault(); - return true; - } -} - -function tapHandleFocus(ele) { - tapTouchFocusedInput = null; - - var triggerFocusIn = false; - - if (ele.tagName == 'SELECT') { - // trick to force Android options to show up - triggerMouseEvent('mousedown', ele, 0, 0); - ele.focus && ele.focus(); - triggerFocusIn = true; - - } else if (tapActiveElement() === ele) { - // already is the active element and has focus - triggerFocusIn = true; - - } else if ((/^(input|textarea)$/i).test(ele.tagName) || ele.isContentEditable) { - triggerFocusIn = true; - ele.focus && ele.focus(); - ele.value = ele.value; - if (tapEnabledTouchEvents) { - tapTouchFocusedInput = ele; - } - - } else { - tapFocusOutActive(); - } - - if (triggerFocusIn) { - tapActiveElement(ele); - // ionic.trigger('ionic.focusin', { - // target: ele - // }, true); - } -} - -function tapFocusOutActive() { - var ele = tapActiveElement(); - if (ele && ((/^(input|textarea|select)$/i).test(ele.tagName) || ele.isContentEditable)) { - console.log('tapFocusOutActive', ele.tagName); - ele.blur(); - } - tapActiveElement(null); -} - -function tapFocusIn(e) { - //console.log('focusin ' + Date.now()); - // Because a text input doesn't preventDefault (so the caret still works) there's a chance - // that its mousedown event 300ms later will change the focus to another element after - // the keyboard shows up. - - if (tapEnabledTouchEvents && - Tap.isTextInput(tapActiveElement()) && - Tap.isTextInput(tapTouchFocusedInput) && - tapTouchFocusedInput !== e.target) { - - // 1) The pointer is from touch events - // 2) There is an active element which is a text input - // 3) A text input was just set to be focused on by a touch event - // 4) A new focus has been set, however the target isn't the one the touch event wanted - console.log('focusin', 'tapTouchFocusedInput'); - tapTouchFocusedInput.focus(); - tapTouchFocusedInput = null; - } - //ionic.scroll.isScrolling = false; -} - -function tapFocusOut() { - //console.log("focusout"); - tapActiveElement(null); -} - -function tapActiveElement(ele) { - if (arguments.length) { - tapActiveEle = ele; - } - return tapActiveEle || document.activeElement; -} - -function tapHasPointerMoved(endEvent) { - if (!endEvent || endEvent.target.nodeType !== 1 || !tapPointerStart || (tapPointerStart.x === 0 && tapPointerStart.y === 0)) { - return false; - } - var endCoordinates = Tap.pointerCoord(endEvent); - - var hasClassList = !!(endEvent.target.classList && endEvent.target.classList.contains && - typeof endEvent.target.classList.contains === 'function'); - var releaseTolerance = hasClassList && endEvent.target.classList.contains('button') ? - TAP_RELEASE_BUTTON_TOLERANCE : - TAP_RELEASE_TOLERANCE; - - return Math.abs(tapPointerStart.x - endCoordinates.x) > releaseTolerance || - Math.abs(tapPointerStart.y - endCoordinates.y) > releaseTolerance; -} - -function tapContainingElement(ele, allowSelf) { - var climbEle = ele; - for (var x = 0; x < 6; x++) { - if (!climbEle) break; - if (climbEle.tagName === 'LABEL') return climbEle; - climbEle = climbEle.parentElement; - } - if (allowSelf !== false) return ele; -} - -function tapTargetElement(ele) { - if (ele && ele.tagName === 'LABEL') { - if (ele.control) return ele.control; - - // older devices do not support the "control" property - if (ele.querySelector) { - var control = ele.querySelector('input,textarea,select'); - if (control) return control; - } - } - return ele; -}