Skip to content

Commit 8844671

Browse files
committed
feat(EventManager): implement the EventManager
1 parent 91fd5a6 commit 8844671

26 files changed

Lines changed: 495 additions & 69 deletions

modules/angular2/src/core/application.js

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle';
1515
import {ShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
1616
import {XHR} from 'angular2/src/core/compiler/xhr/xhr';
1717
import {XHRImpl} from 'angular2/src/core/compiler/xhr/xhr_impl';
18+
import {EventManager} from 'angular2/src/core/events/event_manager';
19+
import {HammerGesturesPlugin} from 'angular2/src/core/events/hammer_gestures';
1820

1921
var _rootInjector: Injector;
2022

@@ -58,7 +60,7 @@ function _injectorBindings(appComponentType) {
5860
}, [appComponentAnnotatedTypeToken, appDocumentToken]),
5961

6062
bind(appViewToken).toAsyncFactory((changeDetection, compiler, injector, appElement,
61-
appComponentAnnotatedType, strategy) => {
63+
appComponentAnnotatedType, strategy, eventManager) => {
6264
return compiler.compile(appComponentAnnotatedType.type, null).then(
6365
(protoView) => {
6466
var appProtoView = ProtoView.createRootProtoView(protoView, appElement,
@@ -67,18 +69,22 @@ function _injectorBindings(appComponentType) {
6769
// The light Dom of the app element is not considered part of
6870
// the angular application. Thus the context and lightDomInjector are
6971
// empty.
70-
var view = appProtoView.instantiate(null);
72+
var view = appProtoView.instantiate(null, eventManager);
7173
view.hydrate(injector, null, new Object());
7274
return view;
7375
});
7476
}, [ChangeDetection, Compiler, Injector, appElementToken, appComponentAnnotatedTypeToken,
75-
ShadowDomStrategy]),
77+
ShadowDomStrategy, EventManager]),
7678

7779
bind(appChangeDetectorToken).toFactory((rootView) => rootView.changeDetector,
7880
[appViewToken]),
7981
bind(appComponentType).toFactory((rootView) => rootView.elementInjectors[0].getComponent(),
8082
[appViewToken]),
81-
bind(LifeCycle).toFactory(() => new LifeCycle(null, assertionsEnabled()),[])
83+
bind(LifeCycle).toFactory(() => new LifeCycle(null, assertionsEnabled()),[]),
84+
bind(EventManager).toFactory((zone) => {
85+
var plugins = [new HammerGesturesPlugin()];
86+
return new EventManager(plugins, zone);
87+
}, [VmTurnZone]),
8288
];
8389
}
8490

@@ -106,12 +112,12 @@ export function bootstrap(appComponentType: Type, bindings=null, givenBootstrapE
106112
// TODO(rado): prepopulate template cache, so applications with only
107113
// index.html and main.js are possible.
108114

109-
var appInjector = _createAppInjector(appComponentType, bindings);
115+
var appInjector = _createAppInjector(appComponentType, bindings, zone);
110116

111117
PromiseWrapper.then(appInjector.asyncGet(appViewToken),
112118
(rootView) => {
113119
// retrieve life cycle: may have already been created if injected in root component
114-
var lc=appInjector.get(LifeCycle);
120+
var lc=appInjector.get(LifeCycle);
115121
lc.registerWith(zone, rootView.changeDetector);
116122
lc.tick(); //the first tick that will bootstrap the app
117123

@@ -126,10 +132,11 @@ export function bootstrap(appComponentType: Type, bindings=null, givenBootstrapE
126132
return bootstrapProcess.promise;
127133
}
128134

129-
function _createAppInjector(appComponentType: Type, bindings: List): Injector {
135+
function _createAppInjector(appComponentType: Type, bindings: List, zone: VmTurnZone): Injector {
130136
if (isBlank(_rootInjector)) _rootInjector = new Injector(_rootBindings);
131137
var mergedBindings = isPresent(bindings) ?
132138
ListWrapper.concat(_injectorBindings(appComponentType), bindings) :
133139
_injectorBindings(appComponentType);
140+
ListWrapper.push(mergedBindings, bind(VmTurnZone).toValue(zone));
134141
return _rootInjector.createChild(mergedBindings);
135142
}

modules/angular2/src/core/compiler/element_injector.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -497,8 +497,7 @@ export class ElementInjector extends TreeNode {
497497
if (isPresent(this._eventCallbacks)) {
498498
var callback = MapWrapper.get(this._eventCallbacks, dep.eventEmitterName);
499499
if (isPresent(callback)) {
500-
var locals = MapWrapper.create();
501-
return ProtoView.buildInnerCallback(callback, view, locals);
500+
return ProtoView.buildInnerCallback(callback, view);
502501
}
503502
}
504503
return (_) => {};

modules/angular2/src/core/compiler/view.js

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {Content} from './shadow_dom_emulation/content_tag';
1616
import {LightDom, DestinationLightDom} from './shadow_dom_emulation/light_dom';
1717
import {ShadowDomStrategy} from './shadow_dom_strategy';
1818
import {ViewPool} from './view_pool';
19+
import {EventManager} from 'angular2/src/core/events/event_manager';
1920

2021
const NG_BINDING_CLASS = 'ng-binding';
2122
const NG_BINDING_CLASS_SELECTOR = '.ng-binding';
@@ -294,19 +295,19 @@ export class ProtoView {
294295
}
295296

296297
// TODO(rado): hostElementInjector should be moved to hydrate phase.
297-
instantiate(hostElementInjector: ElementInjector):View {
298-
if (this._viewPool.length() == 0) this._preFillPool(hostElementInjector);
298+
instantiate(hostElementInjector: ElementInjector, eventManager: EventManager):View {
299+
if (this._viewPool.length() == 0) this._preFillPool(hostElementInjector, eventManager);
299300
var view = this._viewPool.pop();
300-
return isPresent(view) ? view : this._instantiate(hostElementInjector);
301+
return isPresent(view) ? view : this._instantiate(hostElementInjector, eventManager);
301302
}
302303

303-
_preFillPool(hostElementInjector: ElementInjector) {
304+
_preFillPool(hostElementInjector: ElementInjector, eventManager: EventManager) {
304305
for (var i = 0; i < VIEW_POOL_PREFILL; i++) {
305-
this._viewPool.push(this._instantiate(hostElementInjector));
306+
this._viewPool.push(this._instantiate(hostElementInjector, eventManager));
306307
}
307308
}
308309

309-
_instantiate(hostElementInjector: ElementInjector): View {
310+
_instantiate(hostElementInjector: ElementInjector, eventManager: EventManager): View {
310311
var rootElementClone = this.instantiateInPlace ? this.element : DOM.clone(this.element);
311312
var elementsWithBindingsDynamic;
312313
if (this.isTemplateElement) {
@@ -387,7 +388,7 @@ export class ProtoView {
387388
var bindingPropagationConfig = null;
388389
if (isPresent(binder.componentDirective)) {
389390
var strategy = this.shadowDomStrategy;
390-
var childView = binder.nestedProtoView.instantiate(elementInjector);
391+
var childView = binder.nestedProtoView.instantiate(elementInjector, eventManager);
391392
view.changeDetector.addChild(childView.changeDetector);
392393

393394
lightDom = strategy.constructLightDom(view, childView, element);
@@ -402,7 +403,8 @@ export class ProtoView {
402403
var viewPort = null;
403404
if (isPresent(binder.templateDirective)) {
404405
var destLightDom = this._directParentElementLightDom(protoElementInjector, preBuiltObjects);
405-
viewPort = new ViewPort(view, element, binder.nestedProtoView, elementInjector, destLightDom);
406+
viewPort = new ViewPort(view, element, binder.nestedProtoView, elementInjector,
407+
eventManager, destLightDom);
406408
ListWrapper.push(viewPorts, viewPort);
407409
}
408410

@@ -416,7 +418,8 @@ export class ProtoView {
416418
if (isPresent(binder.events)) {
417419
MapWrapper.forEach(binder.events, (expr, eventName) => {
418420
if (isBlank(elementInjector) || !elementInjector.hasEventEmitter(eventName)) {
419-
ProtoView._addNativeEventListener(element, eventName, expr, view);
421+
var handler = ProtoView.buildInnerCallback(expr, view);
422+
eventManager.addEventListener(element, eventName, handler);
420423
}
421424
});
422425
}
@@ -432,24 +435,15 @@ export class ProtoView {
432435
this._viewPool.push(view);
433436
}
434437

435-
static _addNativeEventListener(element: Element, eventName: string, expr: AST, view: View) {
438+
static buildInnerCallback(expr:AST, view:View) {
436439
var locals = MapWrapper.create();
437-
var innerCallback = ProtoView.buildInnerCallback(expr, view, locals);
438-
DOM.on(element, eventName, (event) => {
439-
if (event.target === element) {
440-
innerCallback(event);
441-
}
442-
});
443-
}
444-
445-
static buildInnerCallback(expr:AST, view:View, locals: Map) {
446440
return (event) => {
447441
// Most of the time the event will be fired only when the view is
448442
// in the live document. However, in a rare circumstance the
449443
// view might get dehydrated, in between the event queuing up and
450444
// firing.
451445
if (view.hydrated()) {
452-
MapWrapper.set(locals, `$event`, event);
446+
MapWrapper.set(locals, '$event', event);
453447
var context = new ContextWithVariableBindings(view.context, locals);
454448
expr.eval(context);
455449
}

modules/angular2/src/core/compiler/viewport.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,21 @@ import {BaseException} from 'angular2/src/facade/lang';
55
import {Injector} from 'angular2/di';
66
import {ElementInjector} from 'angular2/src/core/compiler/element_injector';
77
import {isPresent, isBlank} from 'angular2/src/facade/lang';
8+
import {EventManager} from 'angular2/src/core/events/event_manager';
89

910
export class ViewPort {
1011
parentView: View;
1112
templateElement: Element;
1213
defaultProtoView: ProtoView;
1314
_views: List<View>;
1415
_lightDom: any;
16+
_eventManager: EventManager;
1517
elementInjector: ElementInjector;
1618
appInjector: Injector;
1719
hostElementInjector: ElementInjector;
1820

1921
constructor(parentView: View, templateElement: Element, defaultProtoView: ProtoView,
20-
elementInjector: ElementInjector, lightDom = null) {
22+
elementInjector: ElementInjector, eventManager: EventManager, lightDom = null) {
2123
this.parentView = parentView;
2224
this.templateElement = templateElement;
2325
this.defaultProtoView = defaultProtoView;
@@ -28,6 +30,7 @@ export class ViewPort {
2830
this._views = [];
2931
this.appInjector = null;
3032
this.hostElementInjector = null;
33+
this._eventManager = eventManager;
3134
}
3235

3336
hydrate(appInjector: Injector, hostElementInjector: ElementInjector) {
@@ -70,7 +73,7 @@ export class ViewPort {
7073
if (!this.hydrated()) throw new BaseException(
7174
'Cannot create views on a dehydrated view port');
7275
// TODO(rado): replace with viewFactory.
73-
var newView = this.defaultProtoView.instantiate(this.hostElementInjector);
76+
var newView = this.defaultProtoView.instantiate(this.hostElementInjector, this._eventManager);
7477
newView.hydrate(this.appInjector, this.hostElementInjector, this.parentView.context);
7578
return this.insert(newView, atIndex);
7679
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import {isBlank, BaseException, isPresent} from 'angular2/src/facade/lang';
2+
import {DOM, Element} from 'angular2/src/facade/dom';
3+
import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
4+
import {VmTurnZone} from 'angular2/src/core/zone/vm_turn_zone';
5+
6+
export class EventManager {
7+
_plugins: List<EventManagerPlugin>;
8+
_zone: VmTurnZone;
9+
10+
constructor(plugins: List<EventManagerPlugin>, zone: VmTurnZone) {
11+
this._zone = zone;
12+
this._plugins = plugins;
13+
for (var i = 0; i < plugins.length; i++) {
14+
plugins[i].manager = this;
15+
}
16+
}
17+
18+
addEventListener(element: Element, eventName: string, handler: Function) {
19+
var plugin = this._findPluginFor(eventName);
20+
21+
if (isPresent(plugin)) {
22+
plugin.addEventListener(element, eventName, handler);
23+
} else {
24+
this._addNativeEventListener(element, eventName, handler);
25+
}
26+
}
27+
28+
getZone(): VmTurnZone {
29+
return this._zone;
30+
}
31+
32+
_findPluginFor(eventName: string): EventManagerPlugin {
33+
var plugins = this._plugins;
34+
for (var i = 0; i < plugins.length; i++) {
35+
var plugin = plugins[i];
36+
if (plugin.supports(eventName)) {
37+
return plugin;
38+
}
39+
}
40+
return null;
41+
}
42+
43+
_addNativeEventListener(element: Element, eventName: string, handler: Function) {
44+
this._zone.runOutsideAngular(() => {
45+
DOM.on(element, eventName, (event) => {
46+
if (event.target === element) {
47+
this._zone.run(function() {
48+
handler(event);
49+
});
50+
}
51+
});
52+
});
53+
}
54+
}
55+
56+
export class EventManagerPlugin {
57+
manager: EventManager;
58+
59+
supports(eventName: string): boolean {
60+
return false;
61+
}
62+
63+
addEventListener(element: Element, eventName: string, handler: Function) {
64+
throw "not implemented";
65+
}
66+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import {EventManagerPlugin} from './event_manager';
2+
import {StringMapWrapper} from 'angular2/src/facade/collection';
3+
4+
var _eventNames = {
5+
// pan
6+
'pan': true,
7+
'panstart': true,
8+
'panmove': true,
9+
'panend': true,
10+
'pancancel': true,
11+
'panleft': true,
12+
'panright': true,
13+
'panup': true,
14+
'pandown': true,
15+
// pinch
16+
'pinch': true,
17+
'pinchstart': true,
18+
'pinchmove': true,
19+
'pinchend': true,
20+
'pinchcancel': true,
21+
'pinchin': true,
22+
'pinchout': true,
23+
// press
24+
'press': true,
25+
'pressup': true,
26+
// rotate
27+
'rotate': true,
28+
'rotatestart': true,
29+
'rotatemove': true,
30+
'rotateend': true,
31+
'rotatecancel': true,
32+
// swipe
33+
'swipe': true,
34+
'swipeleft': true,
35+
'swiperight': true,
36+
'swipeup': true,
37+
'swipedown': true,
38+
// tap
39+
'tap': true,
40+
};
41+
42+
43+
export class HammerGesturesPluginCommon extends EventManagerPlugin {
44+
constructor() {
45+
super();
46+
}
47+
48+
supports(eventName: string): boolean {
49+
eventName = eventName.toLowerCase();
50+
return StringMapWrapper.contains(_eventNames, eventName);
51+
}
52+
}

0 commit comments

Comments
 (0)