mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-19 03:32:21 +08:00
Activator: activated
This commit is contained in:
@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
import {View, Injectable, NgFor, NgIf} from 'angular2/angular2';
|
import {View, Injectable, NgFor, NgIf} from 'angular2/angular2';
|
||||||
|
|
||||||
import {TapClick} from '../button/button';
|
|
||||||
import {Icon} from '../icon/icon';
|
import {Icon} from '../icon/icon';
|
||||||
import {Overlay} from '../overlay/overlay';
|
import {Overlay} from '../overlay/overlay';
|
||||||
import {Animation} from '../../animations/animation';
|
import {Animation} from '../../animations/animation';
|
||||||
@ -75,7 +74,7 @@ import * as util from 'ionic/util';
|
|||||||
'</div>' +
|
'</div>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'</action-menu-wrapper>',
|
'</action-menu-wrapper>',
|
||||||
directives: [NgFor, NgIf, TapClick, Icon]
|
directives: [NgFor, NgIf, Icon]
|
||||||
})
|
})
|
||||||
class ActionMenuDirective {
|
class ActionMenuDirective {
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import {Platform} from '../../platform/platform';
|
|||||||
import * as util from '../../util/util';
|
import * as util from '../../util/util';
|
||||||
|
|
||||||
// injectables
|
// injectables
|
||||||
|
import {Activator} from '../../util/activator';
|
||||||
import {ActionMenu} from '../action-menu/action-menu';
|
import {ActionMenu} from '../action-menu/action-menu';
|
||||||
import {Modal} from '../modal/modal';
|
import {Modal} from '../modal/modal';
|
||||||
import {Popup} from '../popup/popup';
|
import {Popup} from '../popup/popup';
|
||||||
@ -272,6 +273,7 @@ export function ionicBootstrap(rootComponentType, config) {
|
|||||||
Platform.prepareReady(config);
|
Platform.prepareReady(config);
|
||||||
|
|
||||||
// TODO: probs need a better way to inject global injectables
|
// TODO: probs need a better way to inject global injectables
|
||||||
|
let activator = new Activator(app, config, window, document);
|
||||||
let actionMenu = new ActionMenu(app, config);
|
let actionMenu = new ActionMenu(app, config);
|
||||||
let modal = new Modal(app, config);
|
let modal = new Modal(app, config);
|
||||||
let popup = new Popup(app, config);
|
let popup = new Popup(app, config);
|
||||||
@ -280,6 +282,7 @@ export function ionicBootstrap(rootComponentType, config) {
|
|||||||
let appBindings = Injector.resolve([
|
let appBindings = Injector.resolve([
|
||||||
bind(IonicApp).toValue(app),
|
bind(IonicApp).toValue(app),
|
||||||
bind(IonicConfig).toValue(config),
|
bind(IonicConfig).toValue(config),
|
||||||
|
bind(Activator).toValue(activator),
|
||||||
bind(ActionMenu).toValue(actionMenu),
|
bind(ActionMenu).toValue(actionMenu),
|
||||||
bind(Modal).toValue(modal),
|
bind(Modal).toValue(modal),
|
||||||
bind(Popup).toValue(popup),
|
bind(Popup).toValue(popup),
|
||||||
|
@ -4,7 +4,13 @@ import {App} from 'ionic/ionic';
|
|||||||
@App({
|
@App({
|
||||||
templateUrl: 'main.html'
|
templateUrl: 'main.html'
|
||||||
})
|
})
|
||||||
class E2EApp {}
|
class E2EApp {
|
||||||
|
|
||||||
|
tapTest(eleType) {
|
||||||
|
console.debug('test click', eleType);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function onEvent(ev) {
|
function onEvent(ev) {
|
||||||
@ -74,6 +80,8 @@ console.debug = function() {
|
|||||||
|
|
||||||
if(arguments[0] === 'click') msg = '<span style="color:purple">' + msg + '</span>';
|
if(arguments[0] === 'click') msg = '<span style="color:purple">' + msg + '</span>';
|
||||||
|
|
||||||
|
if(arguments[0] === 'test click') msg = '<span style="color:orange">' + msg + '</span>';
|
||||||
|
|
||||||
msgs.unshift( msg );
|
msgs.unshift( msg );
|
||||||
|
|
||||||
if(msgs.length > 25) {
|
if(msgs.length > 25) {
|
||||||
|
@ -1,6 +1,41 @@
|
|||||||
|
|
||||||
<button>
|
<ion-row>
|
||||||
<div><div><p>TAP ME</p></div></div>
|
|
||||||
</button>
|
<ion-col>
|
||||||
|
<button (^click)="tapTest('button')" block>
|
||||||
|
<div><div><p>Button</p></div></div>
|
||||||
|
</button>
|
||||||
|
</ion-col>
|
||||||
|
|
||||||
|
<ion-col>
|
||||||
|
<a button (^click)="tapTest('link')" block>
|
||||||
|
Link
|
||||||
|
</a>
|
||||||
|
</ion-col>
|
||||||
|
|
||||||
|
</ion-row>
|
||||||
|
|
||||||
|
<ion-row>
|
||||||
|
|
||||||
|
<ion-col>
|
||||||
|
<div button (^click)="tapTest('div')" block>
|
||||||
|
Div w/ (click)
|
||||||
|
</div>
|
||||||
|
</ion-col>
|
||||||
|
|
||||||
|
<ion-col>
|
||||||
|
<div button block>
|
||||||
|
Div w/out (click)
|
||||||
|
</div>
|
||||||
|
</ion-col>
|
||||||
|
|
||||||
|
</ion-row>
|
||||||
|
|
||||||
|
|
||||||
<div id="logs"></div>
|
<div id="logs"></div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.activated {
|
||||||
|
background-color: yellow !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
import {Directive, ElementRef, Optional, Host, onDestroy, NgZone, Query, QueryList} from 'angular2/angular2';
|
import {Directive} from 'angular2/angular2';
|
||||||
|
|
||||||
import {Icon} from '../icon/icon';
|
|
||||||
import {IonicConfig} from '../../config/config';
|
|
||||||
import {Activator} from '../../util/activator';
|
|
||||||
import * as dom from '../../util/dom';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO
|
* TODO
|
||||||
@ -17,9 +13,7 @@ import * as dom from '../../util/dom';
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
export class Button {
|
export class Button {
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
*/
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.iconLeft = this.iconRight = this.iconOnly = false;
|
this.iconLeft = this.iconRight = this.iconOnly = false;
|
||||||
}
|
}
|
||||||
@ -34,190 +28,3 @@ export class Button {
|
|||||||
this.iconOnly = icon.iconOnly;
|
this.iconOnly = icon.iconOnly;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
*/
|
|
||||||
@Directive({
|
|
||||||
selector: '[tap-disabled]'
|
|
||||||
})
|
|
||||||
export class TapDisabled {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
*/
|
|
||||||
@Directive({
|
|
||||||
selector: 'button,[button],[tappable],ion-checkbox,ion-radio',
|
|
||||||
host: {
|
|
||||||
'(^touchstart)': 'touchStart($event)',
|
|
||||||
'(^touchend)': 'touchEnd($event)',
|
|
||||||
'(^touchcancel)': 'pointerCancel()',
|
|
||||||
'(^mousedown)': 'mouseDown($event)',
|
|
||||||
'(^mouseup)': 'mouseUp($event)',
|
|
||||||
'(^click)': 'click($event)',
|
|
||||||
}
|
|
||||||
})
|
|
||||||
export class TapClick {
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
* @param {ElementRef} elementRef TODO
|
|
||||||
* @param {IonicConfig} config TODO
|
|
||||||
* @param {NgZone} ngZone TODO
|
|
||||||
* @param {TapDisabled=} tapDisabled TODO
|
|
||||||
*/
|
|
||||||
constructor(
|
|
||||||
elementRef: ElementRef,
|
|
||||||
config: IonicConfig,
|
|
||||||
ngZone: NgZone,
|
|
||||||
@Optional() @Host() tapDisabled: TapDisabled
|
|
||||||
) {
|
|
||||||
this.ele = elementRef.nativeElement;
|
|
||||||
this.tapEnabled = !tapDisabled;
|
|
||||||
this.tapPolyfill = config.setting('tapPolyfill');
|
|
||||||
this.zone = ngZone;
|
|
||||||
|
|
||||||
let self = this;
|
|
||||||
self.pointerMove = function(ev) {
|
|
||||||
let moveCoord = dom.pointerCoord(ev);
|
|
||||||
console.log('pointerMove', moveCoord, self.start)
|
|
||||||
|
|
||||||
if ( dom.hasPointerMoved(10, self.start, moveCoord) ) {
|
|
||||||
self.pointerCancel();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
* @param {TODO} ev TODO
|
|
||||||
*/
|
|
||||||
touchStart(ev) {
|
|
||||||
this.pointerStart(ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
* @param {TODO} ev TODO
|
|
||||||
*/
|
|
||||||
touchEnd(ev) {
|
|
||||||
let self = this;
|
|
||||||
|
|
||||||
if (this.tapPolyfill && this.tapEnabled) {
|
|
||||||
|
|
||||||
let endCoord = dom.pointerCoord(ev);
|
|
||||||
|
|
||||||
this.disableClick = true;
|
|
||||||
this.zone.runOutsideAngular(() => {
|
|
||||||
clearTimeout(self.disableTimer);
|
|
||||||
self.disableTimer = setTimeout(() => {
|
|
||||||
self.disableClick = false;
|
|
||||||
}, 600);
|
|
||||||
});
|
|
||||||
|
|
||||||
if ( this.start && !dom.hasPointerMoved(3, this.start, endCoord) ) {
|
|
||||||
let clickEvent = document.createEvent('MouseEvents');
|
|
||||||
clickEvent.initMouseEvent('click', true, true, window, 1, 0, 0, endCoord.x, endCoord.y, false, false, false, false, 0, null);
|
|
||||||
clickEvent.isIonicTap = true;
|
|
||||||
this.ele.dispatchEvent(clickEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
this.pointerEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
* @param {TODO} ev TODO
|
|
||||||
*/
|
|
||||||
mouseDown(ev) {
|
|
||||||
if (this.disableClick) {
|
|
||||||
ev.preventDefault();
|
|
||||||
ev.stopPropagation();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
this.pointerStart(ev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
* @param {TODO} ev TODO
|
|
||||||
*/
|
|
||||||
mouseUp(ev) {
|
|
||||||
if (this.disableClick) {
|
|
||||||
ev.preventDefault();
|
|
||||||
ev.stopPropagation();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.pointerEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
* @param {TODO} ev TODO
|
|
||||||
*/
|
|
||||||
pointerStart(ev) {
|
|
||||||
this.start = dom.pointerCoord(ev);
|
|
||||||
|
|
||||||
this.zone.runOutsideAngular(() => {
|
|
||||||
Activator.start(ev.currentTarget);
|
|
||||||
Activator.moveListeners(this.pointerMove, true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
*/
|
|
||||||
pointerEnd() {
|
|
||||||
this.zone.runOutsideAngular(() => {
|
|
||||||
Activator.end();
|
|
||||||
Activator.moveListeners(this.pointerMove, false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
*/
|
|
||||||
pointerCancel() {
|
|
||||||
this.start = null;
|
|
||||||
|
|
||||||
this.zone.runOutsideAngular(() => {
|
|
||||||
Activator.clear();
|
|
||||||
Activator.moveListeners(this.pointerMove, false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the supplied click event should be allowed or not.
|
|
||||||
* @param {MouseEvent} ev The click event.
|
|
||||||
* @return {boolean} True if click event should be allowed, otherwise false.
|
|
||||||
*/
|
|
||||||
allowClick(ev) {
|
|
||||||
if (!ev.isIonicTap) {
|
|
||||||
if (this.disableClick || !this.start) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
* @param {MouseEvent} ev TODO
|
|
||||||
*/
|
|
||||||
click(ev) {
|
|
||||||
if (!this.allowClick(ev)) {
|
|
||||||
ev.preventDefault();
|
|
||||||
ev.stopPropagation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
*/
|
|
||||||
onDestroy() {
|
|
||||||
this.ele = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@ -10,7 +10,6 @@ import {Ion} from '../ion';
|
|||||||
import {IonInput} from '../form/input';
|
import {IonInput} 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 {TapClick} from '../button/button';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name ionCheckbox
|
* @name ionCheckbox
|
||||||
@ -37,6 +36,7 @@ import {TapClick} from '../button/button';
|
|||||||
host: {
|
host: {
|
||||||
'class': 'item',
|
'class': 'item',
|
||||||
'role': 'checkbox',
|
'role': 'checkbox',
|
||||||
|
'tappable': 'true',
|
||||||
'[attr.tab-index]': 'tabIndex',
|
'[attr.tab-index]': 'tabIndex',
|
||||||
'[attr.aria-checked]': 'checked',
|
'[attr.aria-checked]': 'checked',
|
||||||
'[attr.aria-disabled]': 'disabled',
|
'[attr.aria-disabled]': 'disabled',
|
||||||
@ -59,16 +59,13 @@ export class Checkbox extends Ion {
|
|||||||
* @param {ElementRef} elementRef TODO
|
* @param {ElementRef} elementRef TODO
|
||||||
* @param {IonicConfig} ionicConfig TODO
|
* @param {IonicConfig} ionicConfig TODO
|
||||||
* @param {NgControl=} ngControl TODO
|
* @param {NgControl=} ngControl TODO
|
||||||
* @param {TapClick=} tapClick TODO
|
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
elementRef: ElementRef,
|
elementRef: ElementRef,
|
||||||
config: IonicConfig,
|
config: IonicConfig,
|
||||||
@Optional() ngControl: NgControl,
|
@Optional() ngControl: NgControl
|
||||||
tapClick: TapClick
|
|
||||||
) {
|
) {
|
||||||
super(elementRef, config);
|
super(elementRef, config);
|
||||||
this.tapClick = tapClick;
|
|
||||||
this.tabIndex = 0;
|
this.tabIndex = 0;
|
||||||
this.id = IonInput.nextId();
|
this.id = IonInput.nextId();
|
||||||
|
|
||||||
@ -102,12 +99,10 @@ export class Checkbox extends Ion {
|
|||||||
* @param {MouseEvent} ev The click event.
|
* @param {MouseEvent} ev The click event.
|
||||||
*/
|
*/
|
||||||
click(ev) {
|
click(ev) {
|
||||||
if (this.tapClick.allowClick(ev)) {
|
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.toggle();
|
this.toggle();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
|
@ -3,7 +3,6 @@ import {ElementRef, Host, Optional, NgControl, Query, QueryList} from 'angular2/
|
|||||||
import {IonicDirective, IonicComponent, IonicView} from '../../config/annotations';
|
import {IonicDirective, IonicComponent, IonicView} from '../../config/annotations';
|
||||||
import {IonicConfig} from '../../config/config';
|
import {IonicConfig} from '../../config/config';
|
||||||
import {Ion} from '../ion';
|
import {Ion} from '../ion';
|
||||||
import {TapClick} from '../button/button';
|
|
||||||
import {ListHeader} from '../list/list';
|
import {ListHeader} from '../list/list';
|
||||||
|
|
||||||
|
|
||||||
@ -176,6 +175,7 @@ export class RadioGroup extends Ion {
|
|||||||
host: {
|
host: {
|
||||||
'class': 'item',
|
'class': 'item',
|
||||||
'role': 'radio',
|
'role': 'radio',
|
||||||
|
'tappable': 'true',
|
||||||
'[attr.id]': 'id',
|
'[attr.id]': 'id',
|
||||||
'[attr.tab-index]': 'tabIndex',
|
'[attr.tab-index]': 'tabIndex',
|
||||||
'[attr.aria-checked]': 'checked',
|
'[attr.aria-checked]': 'checked',
|
||||||
@ -199,16 +199,13 @@ export class RadioButton extends Ion {
|
|||||||
* @param {RadioGroup=} group The parent radio group, if any.
|
* @param {RadioGroup=} group The parent radio group, if any.
|
||||||
* @param {ElementRef} elementRef TODO
|
* @param {ElementRef} elementRef TODO
|
||||||
* @param {IonicConfig} config TODO
|
* @param {IonicConfig} config TODO
|
||||||
* @param {TapClick} tapClick TODO
|
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
@Host() @Optional() group: RadioGroup,
|
@Host() @Optional() group: RadioGroup,
|
||||||
elementRef: ElementRef,
|
elementRef: ElementRef,
|
||||||
config: IonicConfig,
|
config: IonicConfig
|
||||||
tapClick: TapClick
|
|
||||||
) {
|
) {
|
||||||
super(elementRef, config);
|
super(elementRef, config)
|
||||||
this.tapClick = tapClick;
|
|
||||||
this.group = group;
|
this.group = group;
|
||||||
this.tabIndex = 0;
|
this.tabIndex = 0;
|
||||||
}
|
}
|
||||||
@ -220,12 +217,10 @@ export class RadioButton extends Ion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
click(ev) {
|
click(ev) {
|
||||||
if (this.tapClick.allowClick(ev)) {
|
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.check();
|
this.check();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the checked state of this radio button.
|
* Update the checked state of this radio button.
|
||||||
|
@ -23,6 +23,7 @@ import {pointerCoord} from '../../util/dom';
|
|||||||
@Directive({
|
@Directive({
|
||||||
selector: '.media-switch',
|
selector: '.media-switch',
|
||||||
host: {
|
host: {
|
||||||
|
'tappable': 'true',
|
||||||
'(^touchstart)': 'swtch.pointerDown($event)',
|
'(^touchstart)': 'swtch.pointerDown($event)',
|
||||||
'(^mousedown)': 'swtch.pointerDown($event)',
|
'(^mousedown)': 'swtch.pointerDown($event)',
|
||||||
'[class.activated]': 'swtch.isActivated'
|
'[class.activated]': 'swtch.isActivated'
|
||||||
|
@ -15,7 +15,6 @@ import {
|
|||||||
Segment, SegmentButton, SegmentControlValueAccessor,
|
Segment, SegmentButton, SegmentControlValueAccessor,
|
||||||
RadioGroup, RadioButton, SearchBar,
|
RadioGroup, RadioButton, SearchBar,
|
||||||
Nav, NavbarTemplate, Navbar, NavPush, NavPop, NavRouter,
|
Nav, NavbarTemplate, Navbar, NavPush, NavPop, NavRouter,
|
||||||
TapClick, TapDisabled,
|
|
||||||
IdRef,
|
IdRef,
|
||||||
ShowWhen, HideWhen,
|
ShowWhen, HideWhen,
|
||||||
|
|
||||||
@ -87,10 +86,6 @@ export const IonicDirectives = [
|
|||||||
forwardRef(() => ShowWhen),
|
forwardRef(() => ShowWhen),
|
||||||
forwardRef(() => HideWhen),
|
forwardRef(() => HideWhen),
|
||||||
|
|
||||||
// Gestures
|
|
||||||
forwardRef(() => TapClick),
|
|
||||||
forwardRef(() => TapDisabled),
|
|
||||||
|
|
||||||
// Material
|
// Material
|
||||||
forwardRef(() => MaterialButton)
|
forwardRef(() => MaterialButton)
|
||||||
];
|
];
|
||||||
|
@ -1,61 +1,289 @@
|
|||||||
import {raf} from './dom';
|
import {raf, pointerCoord, hasPointerMoved} from './dom';
|
||||||
|
|
||||||
var queueElements = {}; // elements that should get an active state in XX milliseconds
|
|
||||||
var activeElements = {}; // elements that are currently active
|
|
||||||
var keyId = 0; // a counter for unique keys for the above ojects
|
|
||||||
var ACTIVATED_CLASS = 'activated';
|
|
||||||
var DEACTIVATE_TIMEOUT = 180;
|
|
||||||
|
|
||||||
|
|
||||||
export class Activator {
|
export class Activator {
|
||||||
|
|
||||||
static start(ele) {
|
constructor(app: IonicApp, config: IonicConfig, window, document) {
|
||||||
queueElements[++keyId] = ele;
|
const self = this;
|
||||||
if (keyId > 9) keyId = 0;
|
self.app = app;
|
||||||
raf(Activator.activate);
|
self.config = config;
|
||||||
|
self.win = window;
|
||||||
|
self.doc = document;
|
||||||
|
|
||||||
|
self.id = 0;
|
||||||
|
self.queue = {};
|
||||||
|
self.active = {};
|
||||||
|
self.activatedClass = 'activated';
|
||||||
|
self.deactivateTimeout = 180;
|
||||||
|
self.pointerTolerance = 4;
|
||||||
|
self.isTouch = false;
|
||||||
|
self.disableClick = 0;
|
||||||
|
self.disableClickLimit = 2500;
|
||||||
|
|
||||||
|
self.tapPolyfill = config.setting('tapPolyfill');
|
||||||
|
|
||||||
|
function bindDom(type, listener, useCapture) {
|
||||||
|
document.addEventListener(type, listener, useCapture);
|
||||||
}
|
}
|
||||||
|
|
||||||
static activate() {
|
bindDom('click', function(ev) {
|
||||||
// activate all elements in the queue
|
self.click(ev);
|
||||||
for (var key in queueElements) {
|
}, true);
|
||||||
if (queueElements[key]) {
|
|
||||||
queueElements[key].classList.add(ACTIVATED_CLASS);
|
|
||||||
activeElements[key] = queueElements[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
queueElements = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
static end() {
|
bindDom('touchstart', function(ev) {
|
||||||
setTimeout(Activator.clear, DEACTIVATE_TIMEOUT);
|
self.isTouch = true;
|
||||||
}
|
self.pointerStart(ev);
|
||||||
|
});
|
||||||
|
|
||||||
static clear() {
|
bindDom('touchend', function(ev) {
|
||||||
// clear out any elements that are queued to be set to active
|
self.isTouch = true;
|
||||||
queueElements = {};
|
self.touchEnd(ev);
|
||||||
|
});
|
||||||
|
|
||||||
// in the next frame, remove the active class from all active elements
|
bindDom('touchcancel', function(ev) {
|
||||||
raf(Activator.deactivate);
|
self.isTouch = true;
|
||||||
}
|
self.touchCancel(ev);
|
||||||
|
});
|
||||||
|
|
||||||
static deactivate() {
|
bindDom('mousedown', function(ev) {
|
||||||
|
self.mouseDown(ev);
|
||||||
|
}, true);
|
||||||
|
|
||||||
for (var key in activeElements) {
|
bindDom('mouseup', function(ev) {
|
||||||
if (activeElements[key]) {
|
self.mouseUp(ev);
|
||||||
activeElements[key].classList.remove(ACTIVATED_CLASS);
|
}, true);
|
||||||
}
|
|
||||||
delete activeElements[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static moveListeners(pointerMove, shouldAdd) {
|
|
||||||
document.removeEventListener('touchmove', pointerMove);
|
self.pointerMove = function(ev) {
|
||||||
document.removeEventListener('mousemove', pointerMove);
|
let moveCoord = pointerCoord(ev);
|
||||||
|
console.log('pointerMove', moveCoord, self.start)
|
||||||
|
|
||||||
|
if ( hasPointerMoved(10, self.start, moveCoord) ) {
|
||||||
|
self.pointerCancel();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
self.moveListeners = function(shouldAdd) {
|
||||||
|
document.removeEventListener('touchmove', self.pointerMove);
|
||||||
|
document.removeEventListener('mousemove', self.pointerMove);
|
||||||
|
|
||||||
if (shouldAdd) {
|
if (shouldAdd) {
|
||||||
document.addEventListener('touchmove', pointerMove);
|
bindDom('touchmove', self.pointerMove);
|
||||||
document.addEventListener('mousemove', pointerMove);
|
bindDom('mousemove', self.pointerMove);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
* @param {TODO} ev TODO
|
||||||
|
*/
|
||||||
|
touchEnd(ev) {
|
||||||
|
let self = this;
|
||||||
|
|
||||||
|
if (self.tapPolyfill && self.start) {
|
||||||
|
let endCoord = pointerCoord(ev);
|
||||||
|
|
||||||
|
if (!hasPointerMoved(self.pointerTolerance, self.start, endCoord)) {
|
||||||
|
console.debug('create click');
|
||||||
|
|
||||||
|
self.disableClick = Date.now();
|
||||||
|
|
||||||
|
let clickEvent = self.doc.createEvent('MouseEvents');
|
||||||
|
clickEvent.initMouseEvent('click', true, true, self.win, 1, 0, 0, endCoord.x, endCoord.y, false, false, false, false, 0, null);
|
||||||
|
clickEvent.isIonicTap = true;
|
||||||
|
ev.target.dispatchEvent(clickEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.pointerEnd(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
* @param {TODO} ev TODO
|
||||||
|
*/
|
||||||
|
mouseDown(ev) {
|
||||||
|
if (this.isDisabledClick()) {
|
||||||
|
console.debug('mouseDown prevent');
|
||||||
|
preventEvent(ev);
|
||||||
|
|
||||||
|
} else if (!self.isTouch) {
|
||||||
|
this.pointerStart(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
* @param {TODO} ev TODO
|
||||||
|
*/
|
||||||
|
mouseUp(ev) {
|
||||||
|
if (this.isDisabledClick()) {
|
||||||
|
console.debug('mouseUp prevent');
|
||||||
|
preventEvent(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self.isTouch) {
|
||||||
|
this.pointerEnd(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
* @param {TODO} ev TODO
|
||||||
|
*/
|
||||||
|
pointerStart(ev) {
|
||||||
|
let targetEle = this.getActivatableTarget(ev.target);
|
||||||
|
|
||||||
|
if (targetEle) {
|
||||||
|
this.start = pointerCoord(ev);
|
||||||
|
|
||||||
|
this.queueActivate(targetEle);
|
||||||
|
this.moveListeners(true);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.start = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
*/
|
||||||
|
pointerEnd(ev) {
|
||||||
|
this.endActive();
|
||||||
|
this.moveListeners(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
*/
|
||||||
|
pointerCancel() {
|
||||||
|
console.debug('pointerCancel')
|
||||||
|
this.clearActive();
|
||||||
|
this.moveListeners(false);
|
||||||
|
this.disableClick = Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
isDisabledClick() {
|
||||||
|
return this.disableClick + this.disableClickLimit > Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the supplied click event should be allowed or not.
|
||||||
|
* @param {MouseEvent} ev The click event.
|
||||||
|
* @return {boolean} True if click event should be allowed, otherwise false.
|
||||||
|
*/
|
||||||
|
allowClick(ev) {
|
||||||
|
if (!ev.isIonicTap) {
|
||||||
|
if (this.isDisabledClick()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
* @param {MouseEvent} ev TODO
|
||||||
|
*/
|
||||||
|
click(ev) {
|
||||||
|
if (!this.allowClick(ev)) {
|
||||||
|
console.debug('click prevent');
|
||||||
|
preventEvent(ev);
|
||||||
|
}
|
||||||
|
this.isTouch = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getActivatableTarget(ele) {
|
||||||
|
var targetEle = ele;
|
||||||
|
for (var x = 0; x < 4; x++) {
|
||||||
|
if (!targetEle) break;
|
||||||
|
if (this.isActivatable(targetEle)) return targetEle;
|
||||||
|
targetEle = targetEle.parentElement;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
isActivatable(ele) {
|
||||||
|
if (/^(A|BUTTON)$/.test(ele.tagName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let attributes = ele.attributes;
|
||||||
|
for (let i = 0, l = attributes.length; i < l; i++) {
|
||||||
|
if (/click|tappable/.test(attributes[i].name)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
queueActivate(ele) {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
self.queue[++self.id] = ele;
|
||||||
|
if (self.id > 19) self.id = 0;
|
||||||
|
|
||||||
|
raf(function(){
|
||||||
|
// activate all elements in the queue
|
||||||
|
for (var key in self.queue) {
|
||||||
|
if (self.queue[key]) {
|
||||||
|
self.queue[key].classList.add(self.activatedClass);
|
||||||
|
self.active[key] = self.queue[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.queue = {};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
endActive() {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
self.clearActive();
|
||||||
|
}, this.deactivateTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearActive() {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
// clear out any elements that are queued to be set to active
|
||||||
|
self.queue = {};
|
||||||
|
|
||||||
|
// in the next frame, remove the active class from all active elements
|
||||||
|
raf(function() {
|
||||||
|
for (var key in self.active) {
|
||||||
|
if (self.active[key]) {
|
||||||
|
self.active[key].classList.remove(self.activatedClass);
|
||||||
|
}
|
||||||
|
delete self.active[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
clickBlock(enable) {
|
||||||
|
console.log('clickBlock', enable);
|
||||||
|
|
||||||
|
this.doc.removeEventListener('click', preventEvent, true);
|
||||||
|
this.doc.removeEventListener('touchmove', preventEvent, true);
|
||||||
|
this.doc.removeEventListener('touchstart', preventEvent, true);
|
||||||
|
this.doc.removeEventListener('touchend', preventEvent, true);
|
||||||
|
|
||||||
|
if (enable) {
|
||||||
|
this.doc.addEventListener('click', preventEvent, true);
|
||||||
|
this.doc.addEventListener('touchmove', preventEvent, true);
|
||||||
|
this.doc.addEventListener('touchstart', preventEvent, true);
|
||||||
|
this.doc.addEventListener('touchend', preventEvent, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function preventEvent(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user