TapClick for a,button

This commit is contained in:
Adam Bradley
2015-07-17 12:24:30 -05:00
parent fe5ce7b8b8
commit 43b8b4ad67
12 changed files with 77 additions and 612 deletions

View File

@ -2,12 +2,13 @@
export * from 'ionic/components/app/app' export * from 'ionic/components/app/app'
export * from 'ionic/components/action-menu/action-menu' export * from 'ionic/components/action-menu/action-menu'
export * from 'ionic/components/aside/aside' export * from 'ionic/components/aside/aside'
export * from 'ionic/components/button/button'
export * from 'ionic/components/checkbox/checkbox' export * from 'ionic/components/checkbox/checkbox'
export * from 'ionic/components/content/content' export * from 'ionic/components/content/content'
export * from 'ionic/components/icon/icon' export * from 'ionic/components/icon/icon'
export * from 'ionic/components/item/item' export * from 'ionic/components/item/item'
export * from 'ionic/components/item/item-group' 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/text-input'
export * from 'ionic/components/form/tap-input' export * from 'ionic/components/form/tap-input'
export * from 'ionic/components/form/label' export * from 'ionic/components/form/label'

View File

@ -8,6 +8,7 @@
import {View, Injectable, NgFor, NgIf} from 'angular2/angular2'; import {View, Injectable, NgFor, NgIf} from 'angular2/angular2';
import {TapClick} from '../button/button';
import {Overlay} from '../overlay/overlay'; import {Overlay} from '../overlay/overlay';
import {Animation} from '../../animations/animation'; import {Animation} from '../../animations/animation';
import * as util from 'ionic/util'; import * as util from 'ionic/util';
@ -15,7 +16,7 @@ import * as util from 'ionic/util';
@View({ @View({
template: ` template: `
<div class="action-menu-backdrop" (click)="_cancel()"></div> <div class="action-menu-backdrop" (click)="_cancel()" tappable></div>
<div class="action-menu-wrapper"> <div class="action-menu-wrapper">
<div class="action-menu-container"> <div class="action-menu-container">
<div class="action-menu-group action-menu-options"> <div class="action-menu-group action-menu-options">
@ -28,7 +29,7 @@ import * as util from 'ionic/util';
</div> </div>
</div> </div>
</div>`, </div>`,
directives: [NgFor, NgIf] directives: [NgFor, NgIf, TapClick]
}) })
class ActionMenuDirective { class ActionMenuDirective {

View File

@ -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();
}
}

View File

@ -8,7 +8,7 @@ import {
} from 'angular2/angular2'; } from 'angular2/angular2';
import {Ion} from '../ion'; import {Ion} from '../ion';
import {IonInputItem} from '../form/form'; import {IonInputItem} from '../form/input';
import {IonicConfig} from '../../config/config'; import {IonicConfig} from '../../config/config';
import {IonicComponent, IonicView} from '../../config/annotations'; import {IonicComponent, IonicView} from '../../config/annotations';
import {Icon} from '../icon/icon'; import {Icon} from '../icon/icon';

View File

@ -3,7 +3,7 @@ import {Component, Directive, View, Parent, ElementRef, forwardRef} from 'angula
import {IonicConfig} from '../../config/config'; import {IonicConfig} from '../../config/config';
import * as dom from '../../util/dom'; import * as dom from '../../util/dom';
import {Platform} from '../../platform/platform'; import {Platform} from '../../platform/platform';
import {IonInput} from './form'; import {IonInput} from './input';
@Component({ @Component({

View File

@ -1,6 +1,6 @@
import {Parent, Ancestor, Optional, ElementRef, Attribute, Directive} from 'angular2/angular2'; import {Parent, Ancestor, Optional, ElementRef, Attribute, Directive} from 'angular2/angular2';
import {IonInput} from './form'; import {IonInput} from './input';
import {IonicApp} from '../app/app'; import {IonicApp} from '../app/app';
import {IonicConfig} from '../../config/config'; import {IonicConfig} from '../../config/config';
import {Content} from '../content/content'; import {Content} from '../content/content';

View File

@ -2,7 +2,7 @@ import {Directive, View, Parent, Ancestor, Optional, ElementRef, Attribute, forw
import {IonicDirective} from '../../config/annotations'; import {IonicDirective} from '../../config/annotations';
import {IonicConfig} from '../../config/config'; import {IonicConfig} from '../../config/config';
import {IonInput, IonInputItem} from './form'; import {IonInput, IonInputItem} from './input';
import {IonicApp} from '../app/app'; import {IonicApp} from '../app/app';
import {Content} from '../content/content'; import {Content} from '../content/content';
import {ClickBlock} from '../../util/click-block'; import {ClickBlock} from '../../util/click-block';

View File

@ -93,4 +93,8 @@ export class SwipeHandle {
return (this.viewCtrl ? this.viewCtrl.swipeBackEnabled() : false); return (this.viewCtrl ? this.viewCtrl.swipeBackEnabled() : false);
} }
onDestroy() {
this.gesture && self.gesture.destroy();
}
} }

View File

@ -16,6 +16,7 @@ import {
Segment, SegmentButton, SegmentControlValueAccessor, Segment, SegmentButton, SegmentControlValueAccessor,
RadioGroup, RadioButton, SearchBar, RadioGroup, RadioButton, SearchBar,
Nav, NavbarTemplate, Navbar, NavPush, NavPop, Nav, NavbarTemplate, Navbar, NavPush, NavPop,
TapClick, TapDisabled,
Register Register
} from '../ionic'; } from '../ionic';
@ -71,7 +72,11 @@ export const IonicDirectives = [
forwardRef(() => Navbar), forwardRef(() => Navbar),
forwardRef(() => NavPush), forwardRef(() => NavPush),
forwardRef(() => NavPop), forwardRef(() => NavPop),
forwardRef(() => Register) forwardRef(() => Register),
// Gestures
forwardRef(() => TapClick),
forwardRef(() => TapDisabled),
]; ];
class IonicViewImpl extends View { class IonicViewImpl extends View {

View File

@ -1,6 +1,7 @@
import * as util from 'ionic/util'; import * as util from 'ionic/util';
import {Hammer} from 'ionic/gestures/hammer'; import {Hammer} from 'ionic/gestures/hammer';
export class Gesture { export class Gesture {
constructor(element, opts = {}) { constructor(element, opts = {}) {
util.defaults(opts, { util.defaults(opts, {
@ -18,6 +19,7 @@ export class Gesture {
this._callbacks = {}; this._callbacks = {};
} }
options(opts = {}) { options(opts = {}) {
util.extend(this._options, opts); util.extend(this._options, opts);
} }
@ -31,6 +33,7 @@ export class Gesture {
listen() { listen() {
this.hammertime = Hammer(this.element, this._options); this.hammertime = Hammer(this.element, this._options);
} }
unlisten() { unlisten() {
this.hammertime.destroy(); this.hammertime.destroy();
this.hammertime = null; this.hammertime = null;
@ -41,6 +44,7 @@ export class Gesture {
} }
this._callbacks = {} this._callbacks = {}
} }
destroy() { destroy() {
this.unlisten() this.unlisten()
} }

View File

@ -16,7 +16,6 @@ export * from 'ionic/routing/url-state'
export * from 'ionic/util/click-block' export * from 'ionic/util/click-block'
export * from 'ionic/util/focus' export * from 'ionic/util/focus'
export * from 'ionic/util/tap'
export * from 'ionic/animations/animation' export * from 'ionic/animations/animation'
export * from 'ionic/animations/builtins' export * from 'ionic/animations/builtins'

View File

@ -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
* <div data-tap-disabled="true">
* <div id="google-map"></div>
* </div>
* ```
*
* ### 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;
}