feat(EventManager): implement the EventManager
This commit is contained in:
parent
91fd5a69bf
commit
8844671c8d
|
@ -15,6 +15,8 @@ import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle';
|
|||
import {ShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
|
||||
import {XHR} from 'angular2/src/core/compiler/xhr/xhr';
|
||||
import {XHRImpl} from 'angular2/src/core/compiler/xhr/xhr_impl';
|
||||
import {EventManager} from 'angular2/src/core/events/event_manager';
|
||||
import {HammerGesturesPlugin} from 'angular2/src/core/events/hammer_gestures';
|
||||
|
||||
var _rootInjector: Injector;
|
||||
|
||||
|
@ -58,7 +60,7 @@ function _injectorBindings(appComponentType) {
|
|||
}, [appComponentAnnotatedTypeToken, appDocumentToken]),
|
||||
|
||||
bind(appViewToken).toAsyncFactory((changeDetection, compiler, injector, appElement,
|
||||
appComponentAnnotatedType, strategy) => {
|
||||
appComponentAnnotatedType, strategy, eventManager) => {
|
||||
return compiler.compile(appComponentAnnotatedType.type, null).then(
|
||||
(protoView) => {
|
||||
var appProtoView = ProtoView.createRootProtoView(protoView, appElement,
|
||||
|
@ -67,18 +69,22 @@ function _injectorBindings(appComponentType) {
|
|||
// The light Dom of the app element is not considered part of
|
||||
// the angular application. Thus the context and lightDomInjector are
|
||||
// empty.
|
||||
var view = appProtoView.instantiate(null);
|
||||
var view = appProtoView.instantiate(null, eventManager);
|
||||
view.hydrate(injector, null, new Object());
|
||||
return view;
|
||||
});
|
||||
}, [ChangeDetection, Compiler, Injector, appElementToken, appComponentAnnotatedTypeToken,
|
||||
ShadowDomStrategy]),
|
||||
ShadowDomStrategy, EventManager]),
|
||||
|
||||
bind(appChangeDetectorToken).toFactory((rootView) => rootView.changeDetector,
|
||||
[appViewToken]),
|
||||
bind(appComponentType).toFactory((rootView) => rootView.elementInjectors[0].getComponent(),
|
||||
[appViewToken]),
|
||||
bind(LifeCycle).toFactory(() => new LifeCycle(null, assertionsEnabled()),[])
|
||||
bind(LifeCycle).toFactory(() => new LifeCycle(null, assertionsEnabled()),[]),
|
||||
bind(EventManager).toFactory((zone) => {
|
||||
var plugins = [new HammerGesturesPlugin()];
|
||||
return new EventManager(plugins, zone);
|
||||
}, [VmTurnZone]),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -106,12 +112,12 @@ export function bootstrap(appComponentType: Type, bindings=null, givenBootstrapE
|
|||
// TODO(rado): prepopulate template cache, so applications with only
|
||||
// index.html and main.js are possible.
|
||||
|
||||
var appInjector = _createAppInjector(appComponentType, bindings);
|
||||
var appInjector = _createAppInjector(appComponentType, bindings, zone);
|
||||
|
||||
PromiseWrapper.then(appInjector.asyncGet(appViewToken),
|
||||
(rootView) => {
|
||||
// retrieve life cycle: may have already been created if injected in root component
|
||||
var lc=appInjector.get(LifeCycle);
|
||||
var lc=appInjector.get(LifeCycle);
|
||||
lc.registerWith(zone, rootView.changeDetector);
|
||||
lc.tick(); //the first tick that will bootstrap the app
|
||||
|
||||
|
@ -126,10 +132,11 @@ export function bootstrap(appComponentType: Type, bindings=null, givenBootstrapE
|
|||
return bootstrapProcess.promise;
|
||||
}
|
||||
|
||||
function _createAppInjector(appComponentType: Type, bindings: List): Injector {
|
||||
function _createAppInjector(appComponentType: Type, bindings: List, zone: VmTurnZone): Injector {
|
||||
if (isBlank(_rootInjector)) _rootInjector = new Injector(_rootBindings);
|
||||
var mergedBindings = isPresent(bindings) ?
|
||||
ListWrapper.concat(_injectorBindings(appComponentType), bindings) :
|
||||
_injectorBindings(appComponentType);
|
||||
ListWrapper.push(mergedBindings, bind(VmTurnZone).toValue(zone));
|
||||
return _rootInjector.createChild(mergedBindings);
|
||||
}
|
||||
|
|
|
@ -497,8 +497,7 @@ export class ElementInjector extends TreeNode {
|
|||
if (isPresent(this._eventCallbacks)) {
|
||||
var callback = MapWrapper.get(this._eventCallbacks, dep.eventEmitterName);
|
||||
if (isPresent(callback)) {
|
||||
var locals = MapWrapper.create();
|
||||
return ProtoView.buildInnerCallback(callback, view, locals);
|
||||
return ProtoView.buildInnerCallback(callback, view);
|
||||
}
|
||||
}
|
||||
return (_) => {};
|
||||
|
|
|
@ -16,6 +16,7 @@ import {Content} from './shadow_dom_emulation/content_tag';
|
|||
import {LightDom, DestinationLightDom} from './shadow_dom_emulation/light_dom';
|
||||
import {ShadowDomStrategy} from './shadow_dom_strategy';
|
||||
import {ViewPool} from './view_pool';
|
||||
import {EventManager} from 'angular2/src/core/events/event_manager';
|
||||
|
||||
const NG_BINDING_CLASS = 'ng-binding';
|
||||
const NG_BINDING_CLASS_SELECTOR = '.ng-binding';
|
||||
|
@ -294,19 +295,19 @@ export class ProtoView {
|
|||
}
|
||||
|
||||
// TODO(rado): hostElementInjector should be moved to hydrate phase.
|
||||
instantiate(hostElementInjector: ElementInjector):View {
|
||||
if (this._viewPool.length() == 0) this._preFillPool(hostElementInjector);
|
||||
instantiate(hostElementInjector: ElementInjector, eventManager: EventManager):View {
|
||||
if (this._viewPool.length() == 0) this._preFillPool(hostElementInjector, eventManager);
|
||||
var view = this._viewPool.pop();
|
||||
return isPresent(view) ? view : this._instantiate(hostElementInjector);
|
||||
return isPresent(view) ? view : this._instantiate(hostElementInjector, eventManager);
|
||||
}
|
||||
|
||||
_preFillPool(hostElementInjector: ElementInjector) {
|
||||
_preFillPool(hostElementInjector: ElementInjector, eventManager: EventManager) {
|
||||
for (var i = 0; i < VIEW_POOL_PREFILL; i++) {
|
||||
this._viewPool.push(this._instantiate(hostElementInjector));
|
||||
this._viewPool.push(this._instantiate(hostElementInjector, eventManager));
|
||||
}
|
||||
}
|
||||
|
||||
_instantiate(hostElementInjector: ElementInjector): View {
|
||||
_instantiate(hostElementInjector: ElementInjector, eventManager: EventManager): View {
|
||||
var rootElementClone = this.instantiateInPlace ? this.element : DOM.clone(this.element);
|
||||
var elementsWithBindingsDynamic;
|
||||
if (this.isTemplateElement) {
|
||||
|
@ -387,7 +388,7 @@ export class ProtoView {
|
|||
var bindingPropagationConfig = null;
|
||||
if (isPresent(binder.componentDirective)) {
|
||||
var strategy = this.shadowDomStrategy;
|
||||
var childView = binder.nestedProtoView.instantiate(elementInjector);
|
||||
var childView = binder.nestedProtoView.instantiate(elementInjector, eventManager);
|
||||
view.changeDetector.addChild(childView.changeDetector);
|
||||
|
||||
lightDom = strategy.constructLightDom(view, childView, element);
|
||||
|
@ -402,7 +403,8 @@ export class ProtoView {
|
|||
var viewPort = null;
|
||||
if (isPresent(binder.templateDirective)) {
|
||||
var destLightDom = this._directParentElementLightDom(protoElementInjector, preBuiltObjects);
|
||||
viewPort = new ViewPort(view, element, binder.nestedProtoView, elementInjector, destLightDom);
|
||||
viewPort = new ViewPort(view, element, binder.nestedProtoView, elementInjector,
|
||||
eventManager, destLightDom);
|
||||
ListWrapper.push(viewPorts, viewPort);
|
||||
}
|
||||
|
||||
|
@ -416,7 +418,8 @@ export class ProtoView {
|
|||
if (isPresent(binder.events)) {
|
||||
MapWrapper.forEach(binder.events, (expr, eventName) => {
|
||||
if (isBlank(elementInjector) || !elementInjector.hasEventEmitter(eventName)) {
|
||||
ProtoView._addNativeEventListener(element, eventName, expr, view);
|
||||
var handler = ProtoView.buildInnerCallback(expr, view);
|
||||
eventManager.addEventListener(element, eventName, handler);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -432,24 +435,15 @@ export class ProtoView {
|
|||
this._viewPool.push(view);
|
||||
}
|
||||
|
||||
static _addNativeEventListener(element: Element, eventName: string, expr: AST, view: View) {
|
||||
static buildInnerCallback(expr:AST, view:View) {
|
||||
var locals = MapWrapper.create();
|
||||
var innerCallback = ProtoView.buildInnerCallback(expr, view, locals);
|
||||
DOM.on(element, eventName, (event) => {
|
||||
if (event.target === element) {
|
||||
innerCallback(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static buildInnerCallback(expr:AST, view:View, locals: Map) {
|
||||
return (event) => {
|
||||
// Most of the time the event will be fired only when the view is
|
||||
// in the live document. However, in a rare circumstance the
|
||||
// view might get dehydrated, in between the event queuing up and
|
||||
// firing.
|
||||
if (view.hydrated()) {
|
||||
MapWrapper.set(locals, `$event`, event);
|
||||
MapWrapper.set(locals, '$event', event);
|
||||
var context = new ContextWithVariableBindings(view.context, locals);
|
||||
expr.eval(context);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import {BaseException} from 'angular2/src/facade/lang';
|
|||
import {Injector} from 'angular2/di';
|
||||
import {ElementInjector} from 'angular2/src/core/compiler/element_injector';
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {EventManager} from 'angular2/src/core/events/event_manager';
|
||||
|
||||
export class ViewPort {
|
||||
parentView: View;
|
||||
|
@ -12,12 +13,13 @@ export class ViewPort {
|
|||
defaultProtoView: ProtoView;
|
||||
_views: List<View>;
|
||||
_lightDom: any;
|
||||
_eventManager: EventManager;
|
||||
elementInjector: ElementInjector;
|
||||
appInjector: Injector;
|
||||
hostElementInjector: ElementInjector;
|
||||
|
||||
constructor(parentView: View, templateElement: Element, defaultProtoView: ProtoView,
|
||||
elementInjector: ElementInjector, lightDom = null) {
|
||||
elementInjector: ElementInjector, eventManager: EventManager, lightDom = null) {
|
||||
this.parentView = parentView;
|
||||
this.templateElement = templateElement;
|
||||
this.defaultProtoView = defaultProtoView;
|
||||
|
@ -28,6 +30,7 @@ export class ViewPort {
|
|||
this._views = [];
|
||||
this.appInjector = null;
|
||||
this.hostElementInjector = null;
|
||||
this._eventManager = eventManager;
|
||||
}
|
||||
|
||||
hydrate(appInjector: Injector, hostElementInjector: ElementInjector) {
|
||||
|
@ -70,7 +73,7 @@ export class ViewPort {
|
|||
if (!this.hydrated()) throw new BaseException(
|
||||
'Cannot create views on a dehydrated view port');
|
||||
// TODO(rado): replace with viewFactory.
|
||||
var newView = this.defaultProtoView.instantiate(this.hostElementInjector);
|
||||
var newView = this.defaultProtoView.instantiate(this.hostElementInjector, this._eventManager);
|
||||
newView.hydrate(this.appInjector, this.hostElementInjector, this.parentView.context);
|
||||
return this.insert(newView, atIndex);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
import {isBlank, BaseException, isPresent} from 'angular2/src/facade/lang';
|
||||
import {DOM, Element} from 'angular2/src/facade/dom';
|
||||
import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {VmTurnZone} from 'angular2/src/core/zone/vm_turn_zone';
|
||||
|
||||
export class EventManager {
|
||||
_plugins: List<EventManagerPlugin>;
|
||||
_zone: VmTurnZone;
|
||||
|
||||
constructor(plugins: List<EventManagerPlugin>, zone: VmTurnZone) {
|
||||
this._zone = zone;
|
||||
this._plugins = plugins;
|
||||
for (var i = 0; i < plugins.length; i++) {
|
||||
plugins[i].manager = this;
|
||||
}
|
||||
}
|
||||
|
||||
addEventListener(element: Element, eventName: string, handler: Function) {
|
||||
var plugin = this._findPluginFor(eventName);
|
||||
|
||||
if (isPresent(plugin)) {
|
||||
plugin.addEventListener(element, eventName, handler);
|
||||
} else {
|
||||
this._addNativeEventListener(element, eventName, handler);
|
||||
}
|
||||
}
|
||||
|
||||
getZone(): VmTurnZone {
|
||||
return this._zone;
|
||||
}
|
||||
|
||||
_findPluginFor(eventName: string): EventManagerPlugin {
|
||||
var plugins = this._plugins;
|
||||
for (var i = 0; i < plugins.length; i++) {
|
||||
var plugin = plugins[i];
|
||||
if (plugin.supports(eventName)) {
|
||||
return plugin;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
_addNativeEventListener(element: Element, eventName: string, handler: Function) {
|
||||
this._zone.runOutsideAngular(() => {
|
||||
DOM.on(element, eventName, (event) => {
|
||||
if (event.target === element) {
|
||||
this._zone.run(function() {
|
||||
handler(event);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class EventManagerPlugin {
|
||||
manager: EventManager;
|
||||
|
||||
supports(eventName: string): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
addEventListener(element: Element, eventName: string, handler: Function) {
|
||||
throw "not implemented";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
import {EventManagerPlugin} from './event_manager';
|
||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
var _eventNames = {
|
||||
// pan
|
||||
'pan': true,
|
||||
'panstart': true,
|
||||
'panmove': true,
|
||||
'panend': true,
|
||||
'pancancel': true,
|
||||
'panleft': true,
|
||||
'panright': true,
|
||||
'panup': true,
|
||||
'pandown': true,
|
||||
// pinch
|
||||
'pinch': true,
|
||||
'pinchstart': true,
|
||||
'pinchmove': true,
|
||||
'pinchend': true,
|
||||
'pinchcancel': true,
|
||||
'pinchin': true,
|
||||
'pinchout': true,
|
||||
// press
|
||||
'press': true,
|
||||
'pressup': true,
|
||||
// rotate
|
||||
'rotate': true,
|
||||
'rotatestart': true,
|
||||
'rotatemove': true,
|
||||
'rotateend': true,
|
||||
'rotatecancel': true,
|
||||
// swipe
|
||||
'swipe': true,
|
||||
'swipeleft': true,
|
||||
'swiperight': true,
|
||||
'swipeup': true,
|
||||
'swipedown': true,
|
||||
// tap
|
||||
'tap': true,
|
||||
};
|
||||
|
||||
|
||||
export class HammerGesturesPluginCommon extends EventManagerPlugin {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
supports(eventName: string): boolean {
|
||||
eventName = eventName.toLowerCase();
|
||||
return StringMapWrapper.contains(_eventNames, eventName);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
library angular.events;
|
||||
|
||||
import 'dart:html';
|
||||
import './hammer_common.dart';
|
||||
import '../../facade/dom.dart' show Element;
|
||||
import '../../facade/lang.dart' show BaseException;
|
||||
|
||||
import 'dart:js' as js;
|
||||
|
||||
class HammerGesturesPlugin extends HammerGesturesPluginCommon {
|
||||
bool supports(String eventName) {
|
||||
if (!super.supports(eventName)) return false;
|
||||
|
||||
if (!js.context.hasProperty('Hammer')) {
|
||||
throw new BaseException('Hammer.js is not loaded, can not bind ${eventName} event');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
addEventListener(Element element, String eventName, Function handler) {
|
||||
var zone = this.manager.getZone();
|
||||
eventName = eventName.toLowerCase();
|
||||
|
||||
zone.runOutsideAngular(() {
|
||||
// Creating the manager bind events, must be done outside of angular
|
||||
var mc = new js.JsObject(js.context['Hammer'], [element]);
|
||||
|
||||
var jsObj = mc.callMethod('get', ['pinch']);
|
||||
jsObj.callMethod('set', [new js.JsObject.jsify({'enable': true})]);
|
||||
jsObj = mc.callMethod('get', ['rotate']);
|
||||
jsObj.callMethod('set', [new js.JsObject.jsify({'enable': true})]);
|
||||
|
||||
mc.callMethod('on', [
|
||||
eventName,
|
||||
(eventObj) {
|
||||
zone.run(() {
|
||||
var dartEvent = new HammerEvent._fromJsEvent(eventObj);
|
||||
handler(dartEvent);
|
||||
});
|
||||
}
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class HammerEvent {
|
||||
num angle;
|
||||
num centerX;
|
||||
num centerY;
|
||||
int deltaTime;
|
||||
int deltaX;
|
||||
int deltaY;
|
||||
int direction;
|
||||
int distance;
|
||||
num rotation;
|
||||
num scale;
|
||||
Node target;
|
||||
int timeStamp;
|
||||
String type;
|
||||
num velocity;
|
||||
num velocityX;
|
||||
num velocityY;
|
||||
js.JsObject jsEvent;
|
||||
|
||||
HammerEvent._fromJsEvent(js.JsObject event) {
|
||||
angle = event['angle'];
|
||||
var center = event['center'];
|
||||
centerX = center['x'];
|
||||
centerY = center['y'];
|
||||
deltaTime = event['deltaTime'];
|
||||
deltaX = event['deltaX'];
|
||||
deltaY = event['deltaY'];
|
||||
direction = event['direction'];
|
||||
distance = event['distance'];
|
||||
rotation = event['rotation'];
|
||||
scale = event['scale'];
|
||||
target = event['target'];
|
||||
timeStamp = event['timeStamp'];
|
||||
type = event['type'];
|
||||
velocity = event['velocity'];
|
||||
velocityX = event['velocityX'];
|
||||
velocityY = event['velocityY'];
|
||||
jsEvent = event;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
import {HammerGesturesPluginCommon} from './hammer_common';
|
||||
import {Element} from 'angular2/src/facade/dom';
|
||||
import {isPresent, BaseException} from 'angular2/src/facade/lang';
|
||||
|
||||
export class HammerGesturesPlugin extends HammerGesturesPluginCommon {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
supports(eventName:string):boolean {
|
||||
if (!super.supports(eventName)) return false;
|
||||
|
||||
if (!isPresent(window.Hammer)) {
|
||||
throw new BaseException(`Hammer.js is not loaded, can not bind ${eventName} event`);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
addEventListener(element:Element, eventName:string, handler:Function) {
|
||||
var zone = this.manager.getZone();
|
||||
eventName = eventName.toLowerCase();
|
||||
|
||||
zone.runOutsideAngular(function () {
|
||||
// Creating the manager bind events, must be done outside of angular
|
||||
var mc = new Hammer(element);
|
||||
mc.get('pinch').set({enable: true});
|
||||
mc.get('rotate').set({enable: true});
|
||||
|
||||
mc.on(eventName, function (eventObj) {
|
||||
zone.run(function () {
|
||||
handler(eventObj);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -60,6 +60,7 @@ class MapWrapper {
|
|||
// TODO: how to export StringMap=Map as a type?
|
||||
class StringMapWrapper {
|
||||
static HashMap create() => new HashMap();
|
||||
static bool contains(Map map, key) => map.containsKey(key);
|
||||
static get(Map map, key) => map[key];
|
||||
static void set(Map map, key, value) {
|
||||
map[key] = value;
|
||||
|
|
|
@ -40,6 +40,9 @@ export class StringMapWrapper {
|
|||
// http://jsperf.com/ng2-object-create-null
|
||||
return { };
|
||||
}
|
||||
static contains(map, key) {
|
||||
return map.hasOwnProperty(key);
|
||||
}
|
||||
static get(map, key) {
|
||||
return map.hasOwnProperty(key) ? map[key] : undefined;
|
||||
}
|
||||
|
|
|
@ -131,7 +131,7 @@ export function main() {
|
|||
});
|
||||
});
|
||||
|
||||
it("should make the provided binings available to the application component", (done) => {
|
||||
it("should make the provided bindings available to the application component", (done) => {
|
||||
var injectorPromise = bootstrap(HelloRootCmp3, [
|
||||
testBindings,
|
||||
bind("appBinding").toValue("BoundValue")
|
||||
|
|
|
@ -410,7 +410,7 @@ export function main() {
|
|||
});
|
||||
|
||||
it('should return viewPort', function () {
|
||||
var viewPort = new ViewPort(null, null, null, null);
|
||||
var viewPort = new ViewPort(null, null, null, null, null);
|
||||
var inj = injector([], null, null, new PreBuiltObjects(null, null, viewPort, null, null));
|
||||
|
||||
expect(inj.get(ViewPort)).toEqual(viewPort);
|
||||
|
|
|
@ -37,7 +37,7 @@ export function main() {
|
|||
var view, ctx, cd;
|
||||
function createView(pv) {
|
||||
ctx = new MyComp();
|
||||
view = pv.instantiate(null);
|
||||
view = pv.instantiate(null, null);
|
||||
view.hydrate(new Injector([]), null, ctx);
|
||||
cd = view.changeDetector;
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ export function main() {
|
|||
});
|
||||
|
||||
it('should consume directive watch expression change.', (done) => {
|
||||
var tpl =
|
||||
var tpl =
|
||||
'<div>' +
|
||||
'<div my-dir [elprop]="ctxProp"></div>' +
|
||||
'<div my-dir elprop="Hi there!"></div>' +
|
||||
|
|
|
@ -83,7 +83,7 @@ export function main() {
|
|||
|
||||
function instantiateView(protoView) {
|
||||
evalContext = new Context();
|
||||
view = protoView.instantiate(null);
|
||||
view = protoView.instantiate(null, null);
|
||||
view.hydrate(new Injector([]), null, evalContext);
|
||||
changeDetector = view.changeDetector;
|
||||
}
|
||||
|
|
|
@ -345,7 +345,7 @@ class MyComp {
|
|||
}
|
||||
|
||||
function createView(pv) {
|
||||
var view = pv.instantiate(null);
|
||||
var view = pv.instantiate(null, null);
|
||||
view.hydrate(new Injector([]), null, {});
|
||||
return view;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,8 @@ import {Injector} from 'angular2/di';
|
|||
import {View} from 'angular2/src/core/compiler/view';
|
||||
import {ViewPort} from 'angular2/src/core/compiler/viewport';
|
||||
import {reflector} from 'angular2/src/reflection/reflection';
|
||||
|
||||
import {VmTurnZone} from 'angular2/src/core/zone/vm_turn_zone';
|
||||
import {EventManager} from 'angular2/src/core/events/event_manager';
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(ViewPort)
|
||||
|
@ -43,9 +44,9 @@ export function main() {
|
|||
describe('view', function() {
|
||||
var parser, someComponentDirective, someTemplateDirective;
|
||||
|
||||
function createView(protoView) {
|
||||
function createView(protoView, eventManager: EventManager = null) {
|
||||
var ctx = new MyEvaluationContext();
|
||||
var view = protoView.instantiate(null);
|
||||
var view = protoView.instantiate(null, eventManager);
|
||||
view.hydrate(null, null, ctx);
|
||||
return view;
|
||||
}
|
||||
|
@ -60,7 +61,7 @@ export function main() {
|
|||
var view;
|
||||
beforeEach(() => {
|
||||
var pv = new ProtoView(el('<div id="1"></div>'), new DynamicProtoChangeDetector(), null);
|
||||
view = pv.instantiate(null);
|
||||
view = pv.instantiate(null, null);
|
||||
});
|
||||
|
||||
it('should be dehydrated by default', () => {
|
||||
|
@ -81,7 +82,7 @@ export function main() {
|
|||
var fakeView = new FakeView();
|
||||
pv.returnToPool(fakeView);
|
||||
|
||||
expect(pv.instantiate(null)).toBe(fakeView);
|
||||
expect(pv.instantiate(null, null)).toBe(fakeView);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -115,7 +116,7 @@ export function main() {
|
|||
});
|
||||
});
|
||||
|
||||
describe('instatiated and hydrated', function() {
|
||||
describe('instantiated and hydrated', function() {
|
||||
|
||||
function createCollectDomNodesTestCases(useTemplateElement:boolean) {
|
||||
|
||||
|
@ -126,7 +127,7 @@ export function main() {
|
|||
it('should collect the root node in the ProtoView element', () => {
|
||||
var pv = new ProtoView(templateAwareCreateElement('<div id="1"></div>'),
|
||||
new DynamicProtoChangeDetector(), null);
|
||||
var view = pv.instantiate(null);
|
||||
var view = pv.instantiate(null, null);
|
||||
view.hydrate(null, null, null);
|
||||
expect(view.nodes.length).toBe(1);
|
||||
expect(view.nodes[0].getAttribute('id')).toEqual('1');
|
||||
|
@ -140,7 +141,7 @@ export function main() {
|
|||
pv.bindElement(null);
|
||||
pv.bindElementProperty(parser.parseBinding('a', null), 'prop', reflector.setter('prop'));
|
||||
|
||||
var view = pv.instantiate(null);
|
||||
var view = pv.instantiate(null, null);
|
||||
view.hydrate(null, null, null);
|
||||
expect(view.bindElements.length).toEqual(1);
|
||||
expect(view.bindElements[0]).toBe(view.nodes[0]);
|
||||
|
@ -152,7 +153,7 @@ export function main() {
|
|||
pv.bindElement(null);
|
||||
pv.bindElementProperty(parser.parseBinding('b', null), 'a', reflector.setter('a'));
|
||||
|
||||
var view = pv.instantiate(null);
|
||||
var view = pv.instantiate(null, null);
|
||||
view.hydrate(null, null, null);
|
||||
expect(view.bindElements.length).toEqual(1);
|
||||
expect(view.bindElements[0]).toBe(view.nodes[0].childNodes[1]);
|
||||
|
@ -169,7 +170,7 @@ export function main() {
|
|||
pv.bindTextNode(0, parser.parseBinding('a', null));
|
||||
pv.bindTextNode(2, parser.parseBinding('b', null));
|
||||
|
||||
var view = pv.instantiate(null);
|
||||
var view = pv.instantiate(null, null);
|
||||
view.hydrate(null, null, null);
|
||||
expect(view.textNodes.length).toEqual(2);
|
||||
expect(view.textNodes[0]).toBe(view.nodes[0].childNodes[0]);
|
||||
|
@ -182,7 +183,7 @@ export function main() {
|
|||
pv.bindElement(null);
|
||||
pv.bindTextNode(0, parser.parseBinding('b', null));
|
||||
|
||||
var view = pv.instantiate(null);
|
||||
var view = pv.instantiate(null, null);
|
||||
view.hydrate(null, null, null);
|
||||
expect(view.textNodes.length).toEqual(1);
|
||||
expect(view.textNodes[0]).toBe(view.nodes[0].childNodes[1].childNodes[0]);
|
||||
|
@ -197,7 +198,7 @@ export function main() {
|
|||
var pv = new ProtoView(template, new DynamicProtoChangeDetector(),
|
||||
new NativeShadowDomStrategy());
|
||||
pv.instantiateInPlace = true;
|
||||
var view = pv.instantiate(null);
|
||||
var view = pv.instantiate(null, null);
|
||||
view.hydrate(null, null, null);
|
||||
expect(view.nodes[0]).toBe(template);
|
||||
});
|
||||
|
@ -206,7 +207,7 @@ export function main() {
|
|||
var template = el('<div></div>')
|
||||
var view = new ProtoView(template, new DynamicProtoChangeDetector(),
|
||||
new NativeShadowDomStrategy())
|
||||
.instantiate(null);
|
||||
.instantiate(null, null);
|
||||
view.hydrate(null, null, null);
|
||||
expect(view.nodes[0]).not.toBe(template);
|
||||
});
|
||||
|
@ -226,7 +227,7 @@ export function main() {
|
|||
new DynamicProtoChangeDetector(), null);
|
||||
pv.bindElement(new ProtoElementInjector(null, 1, [SomeDirective]));
|
||||
|
||||
var view = pv.instantiate(null);
|
||||
var view = pv.instantiate(null, null);
|
||||
view.hydrate(null, null, null);
|
||||
expect(view.elementInjectors.length).toBe(1);
|
||||
expect(view.elementInjectors[0].get(SomeDirective) instanceof SomeDirective).toBe(true);
|
||||
|
@ -239,7 +240,7 @@ export function main() {
|
|||
pv.bindElement(protoParent);
|
||||
pv.bindElement(new ProtoElementInjector(protoParent, 1, [AnotherDirective]));
|
||||
|
||||
var view = pv.instantiate(null);
|
||||
var view = pv.instantiate(null, null);
|
||||
view.hydrate(null, null, null);
|
||||
expect(view.elementInjectors.length).toBe(2);
|
||||
expect(view.elementInjectors[0].get(SomeDirective) instanceof SomeDirective).toBe(true);
|
||||
|
@ -257,7 +258,7 @@ export function main() {
|
|||
var hostProtoInjector = new ProtoElementInjector(null, 0, []);
|
||||
var hostInjector = hostProtoInjector.instantiate(null, null, null);
|
||||
var view;
|
||||
expect(() => view = pv.instantiate(hostInjector)).not.toThrow();
|
||||
expect(() => view = pv.instantiate(hostInjector, null)).not.toThrow();
|
||||
expect(testProtoElementInjector.parentElementInjector).toBe(view.elementInjectors[0]);
|
||||
expect(testProtoElementInjector.hostElementInjector).toBeNull();
|
||||
});
|
||||
|
@ -271,7 +272,7 @@ export function main() {
|
|||
|
||||
var hostProtoInjector = new ProtoElementInjector(null, 0, []);
|
||||
var hostInjector = hostProtoInjector.instantiate(null, null, null);
|
||||
expect(() => pv.instantiate(hostInjector)).not.toThrow();
|
||||
expect(() => pv.instantiate(hostInjector, null)).not.toThrow();
|
||||
expect(testProtoElementInjector.parentElementInjector).toBeNull();
|
||||
expect(testProtoElementInjector.hostElementInjector).toBe(hostInjector);
|
||||
});
|
||||
|
@ -286,7 +287,7 @@ export function main() {
|
|||
pv.bindElement(protoParent);
|
||||
pv.bindElement(new ProtoElementInjector(protoParent, 1, [AnotherDirective]));
|
||||
|
||||
var view = pv.instantiate(null);
|
||||
var view = pv.instantiate(null, null);
|
||||
view.hydrate(null, null, null);
|
||||
expect(view.rootElementInjectors.length).toBe(1);
|
||||
expect(view.rootElementInjectors[0].get(SomeDirective) instanceof SomeDirective).toBe(true);
|
||||
|
@ -298,7 +299,7 @@ export function main() {
|
|||
pv.bindElement(new ProtoElementInjector(null, 1, [SomeDirective]));
|
||||
pv.bindElement(new ProtoElementInjector(null, 2, [AnotherDirective]));
|
||||
|
||||
var view = pv.instantiate(null);
|
||||
var view = pv.instantiate(null, null);
|
||||
view.hydrate(null, null, null);
|
||||
expect(view.rootElementInjectors.length).toBe(2)
|
||||
expect(view.rootElementInjectors[0].get(SomeDirective) instanceof SomeDirective).toBe(true);
|
||||
|
@ -321,7 +322,7 @@ export function main() {
|
|||
|
||||
function createNestedView(protoView) {
|
||||
ctx = new MyEvaluationContext();
|
||||
var view = protoView.instantiate(null);
|
||||
var view = protoView.instantiate(null, null);
|
||||
view.hydrate(new Injector([]), null, ctx);
|
||||
return view;
|
||||
}
|
||||
|
@ -439,7 +440,7 @@ export function main() {
|
|||
var view, ctx, called, receivedEvent, dispatchedEvent;
|
||||
|
||||
function createViewAndContext(protoView) {
|
||||
view = createView(protoView);
|
||||
view = createView(protoView, new EventManager([], new FakeVmTurnZone()));
|
||||
ctx = view.context;
|
||||
called = 0;
|
||||
receivedEvent = null;
|
||||
|
@ -458,7 +459,7 @@ export function main() {
|
|||
var pv = new ProtoView(el('<div class="ng-binding"><div></div></div>'),
|
||||
new DynamicProtoChangeDetector(), null);
|
||||
pv.bindElement(new TestProtoElementInjector(null, 0, []));
|
||||
pv.bindEvent('click', parser.parseBinding('callMe(\$event)', null));
|
||||
pv.bindEvent('click', parser.parseBinding('callMe($event)', null));
|
||||
return pv;
|
||||
}
|
||||
|
||||
|
@ -493,7 +494,7 @@ export function main() {
|
|||
var pv = new ProtoView(el('<div class="ng-binding"><div></div></div>'),
|
||||
new DynamicProtoChangeDetector(), null);
|
||||
pv.bindElement(new TestProtoElementInjector(null, 0, [EventEmitterDirective]));
|
||||
pv.bindEvent('click', parser.parseBinding('callMe(\$event)', null));
|
||||
pv.bindEvent('click', parser.parseBinding('callMe($event)', null));
|
||||
|
||||
createViewAndContext(pv);
|
||||
var dir = view.elementInjectors[0].get(EventEmitterDirective);
|
||||
|
@ -611,7 +612,7 @@ export function main() {
|
|||
it('should create the root component when instantiated', () => {
|
||||
var rootProtoView = ProtoView.createRootProtoView(pv, element,
|
||||
someComponentDirective, new DynamicProtoChangeDetector(), new NativeShadowDomStrategy());
|
||||
var view = rootProtoView.instantiate(null);
|
||||
var view = rootProtoView.instantiate(null, null);
|
||||
view.hydrate(new Injector([]), null, null);
|
||||
expect(view.rootElementInjectors[0].get(SomeComponent)).not.toBe(null);
|
||||
});
|
||||
|
@ -619,7 +620,7 @@ export function main() {
|
|||
it('should inject the protoView into the shadowDom', () => {
|
||||
var rootProtoView = ProtoView.createRootProtoView(pv, element,
|
||||
someComponentDirective, new DynamicProtoChangeDetector(), new NativeShadowDomStrategy());
|
||||
var view = rootProtoView.instantiate(null);
|
||||
var view = rootProtoView.instantiate(null, null);
|
||||
view.hydrate(new Injector([]), null, null);
|
||||
expect(element.shadowRoot.childNodes[0].childNodes[0].nodeValue).toEqual('hi');
|
||||
});
|
||||
|
@ -723,3 +724,17 @@ class TestProtoElementInjector extends ProtoElementInjector {
|
|||
return super.instantiate(parent, host, events);
|
||||
}
|
||||
}
|
||||
|
||||
class FakeVmTurnZone extends VmTurnZone {
|
||||
constructor() {
|
||||
super({enableLongStackTrace: false});
|
||||
}
|
||||
|
||||
run(fn) {
|
||||
fn();
|
||||
}
|
||||
|
||||
runOutsideAngular(fn) {
|
||||
fn();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ export function main() {
|
|||
parentView = createView([dom.childNodes[0]]);
|
||||
protoView = new ProtoView(el('<div>hi</div>'), new DynamicProtoChangeDetector(), new NativeShadowDomStrategy());
|
||||
elementInjector = new ElementInjector(null, null, null, null);
|
||||
viewPort = new ViewPort(parentView, insertionElement, protoView, elementInjector);
|
||||
viewPort = new ViewPort(parentView, insertionElement, protoView, elementInjector, null);
|
||||
customViewWithOneNode = createView([el('<div>single</div>')]);
|
||||
customViewWithTwoNodes = createView([el('<div>one</div>'), el('<div>two</div>')]);
|
||||
});
|
||||
|
@ -216,7 +216,7 @@ export function main() {
|
|||
new DynamicProtoChangeDetector(), new NativeShadowDomStrategy());
|
||||
pv.bindElement(new ProtoElementInjector(null, 1, [SomeDirective]));
|
||||
pv.bindTextNode(0, parser.parseBinding('foo', null));
|
||||
fancyView = pv.instantiate(null);
|
||||
fancyView = pv.instantiate(null, null);
|
||||
});
|
||||
|
||||
it('hydrating should update rootElementInjectors and parent change detector', () => {
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
import {describe, ddescribe, it, iit, xit, xdescribe, expect, beforeEach, el} from 'angular2/test_lib';
|
||||
import {EventManager, EventManagerPlugin} from 'angular2/src/core/events/event_manager';
|
||||
import {VmTurnZone} from 'angular2/src/core/zone/vm_turn_zone';
|
||||
import {List, ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {DOM, Element} from 'angular2/src/facade/dom';
|
||||
|
||||
export function main() {
|
||||
describe('EventManager', () => {
|
||||
|
||||
it('should delegate event bindings to plugins', () => {
|
||||
var element = el('<div></div>');
|
||||
var handler = (e) => e;
|
||||
var plugin = new FakeEventManagerPlugin(['click']);
|
||||
var manager = new EventManager([plugin], new FakeVmTurnZone());
|
||||
manager.addEventListener(element, 'click', handler);
|
||||
expect(MapWrapper.get(plugin._eventHandlers, 'click')).toBe(handler);
|
||||
});
|
||||
|
||||
it('should delegate event bindings to the first plugin supporting the event', () => {
|
||||
var element = el('<div></div>');
|
||||
var clickHandler = (e) => e;
|
||||
var dblClickHandler = (e) => e;
|
||||
var plugin1= new FakeEventManagerPlugin(['dblclick']);
|
||||
var plugin2 = new FakeEventManagerPlugin(['click', 'dblclick']);
|
||||
var manager = new EventManager([plugin1, plugin2], new FakeVmTurnZone());
|
||||
manager.addEventListener(element, 'click', clickHandler);
|
||||
manager.addEventListener(element, 'dblclick', dblClickHandler);
|
||||
expect(MapWrapper.contains(plugin1._eventHandlers, 'click')).toBe(false);
|
||||
expect(MapWrapper.get(plugin2._eventHandlers, 'click')).toBe(clickHandler);
|
||||
expect(MapWrapper.contains(plugin2._eventHandlers, 'dblclick')).toBe(false);
|
||||
expect(MapWrapper.get(plugin1._eventHandlers, 'dblclick')).toBe(dblClickHandler);
|
||||
});
|
||||
|
||||
it('should fall back to native events when no plugin can handle the event', () => {
|
||||
var element = el('<div></div>');
|
||||
var dispatchedEvent = DOM.createMouseEvent('click');
|
||||
var receivedEvent = null;
|
||||
var handler = (e) => { receivedEvent = e; };
|
||||
var plugin = new FakeEventManagerPlugin(['dblclick']);
|
||||
var manager = new EventManager([plugin], new FakeVmTurnZone());
|
||||
manager.addEventListener(element, 'click', handler);
|
||||
DOM.dispatchEvent(element, dispatchedEvent);
|
||||
expect(receivedEvent).toBe(dispatchedEvent);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class FakeEventManagerPlugin extends EventManagerPlugin {
|
||||
_supports: List<string>;
|
||||
_eventHandlers: Map;
|
||||
constructor(supports: List<string>) {
|
||||
super();
|
||||
this._supports = supports;
|
||||
this._eventHandlers = MapWrapper.create();
|
||||
}
|
||||
|
||||
supports(eventName: string): boolean {
|
||||
return ListWrapper.contains(this._supports, eventName);
|
||||
}
|
||||
|
||||
addEventListener(element: Element, eventName: string, handler: Function) {
|
||||
MapWrapper.set(this._eventHandlers, eventName, handler);
|
||||
}
|
||||
}
|
||||
|
||||
class FakeVmTurnZone extends VmTurnZone {
|
||||
constructor() {
|
||||
super({enableLongStackTrace: false});
|
||||
}
|
||||
|
||||
run(fn) {
|
||||
fn();
|
||||
}
|
||||
|
||||
runOutsideAngular(fn) {
|
||||
fn();
|
||||
}
|
||||
}
|
|
@ -26,7 +26,7 @@ export function main() {
|
|||
|
||||
function createView(pv) {
|
||||
component = new TestComponent();
|
||||
view = pv.instantiate(null);
|
||||
view = pv.instantiate(null, null);
|
||||
view.hydrate(new Injector([]), null, component);
|
||||
cd = view.changeDetector;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ export function main() {
|
|||
|
||||
function createView(pv) {
|
||||
component = new TestComponent();
|
||||
view = pv.instantiate(null);
|
||||
view = pv.instantiate(null, null);
|
||||
view.hydrate(new Injector([]), null, component);
|
||||
cd = view.changeDetector;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ export function main() {
|
|||
|
||||
function createView(pv) {
|
||||
component = new TestComponent();
|
||||
view = pv.instantiate(null);
|
||||
view = pv.instantiate(null, null);
|
||||
view.hydrate(new Injector([]), null, component);
|
||||
cd = view.changeDetector;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ export function main() {
|
|||
|
||||
function createView(pv) {
|
||||
component = new TestComponent();
|
||||
view = pv.instantiate(null);
|
||||
view = pv.instantiate(null, null);
|
||||
view.hydrate(new Injector([]), null, component);
|
||||
cd = view.changeDetector;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ export function main() {
|
|||
new NativeShadowDomStrategy());
|
||||
|
||||
compiler.compile(componentType, el(template)).then((pv) => {
|
||||
var view = pv.instantiate(null);
|
||||
var view = pv.instantiate(null, null);
|
||||
view.hydrate(new Injector([]), null, context);
|
||||
detectChanges(view);
|
||||
callback(view);
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,38 @@
|
|||
import {bootstrap, Component, TemplateConfig} from 'angular2/core';
|
||||
import {reflector} from 'angular2/src/reflection/reflection';
|
||||
import {ReflectionCapabilities} from 'angular2/src/reflection/reflection_capabilities';
|
||||
|
||||
@Component({
|
||||
selector: 'gestures-app',
|
||||
template: new TemplateConfig({
|
||||
url: 'template.html'
|
||||
})
|
||||
})
|
||||
class GesturesCmp {
|
||||
swipeDirection: string;
|
||||
pinchScale: number;
|
||||
rotateAngle: number;
|
||||
|
||||
constructor() {
|
||||
this.swipeDirection = '-';
|
||||
this.pinchScale = 1;
|
||||
this.rotateAngle = 0;
|
||||
}
|
||||
|
||||
onSwipe(event) {
|
||||
this.swipeDirection = event.deltaX > 0 ? 'right' : 'left';
|
||||
}
|
||||
|
||||
onPinch(event) {
|
||||
this.pinchScale = event.scale;
|
||||
}
|
||||
|
||||
onRotate(event) {
|
||||
this.rotateAngle = event.rotation;
|
||||
}
|
||||
}
|
||||
|
||||
export function main() {
|
||||
reflector.reflectionCapabilities = new ReflectionCapabilities();
|
||||
bootstrap(GesturesCmp);
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<style type="text/css">
|
||||
.row {
|
||||
height: 33%;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid black;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="row" (swipe)="onSwipe($event)">Swipe (direction = {{swipeDirection}})</div>
|
||||
<div class="row" (pinch)="onPinch($event)">pinch (scale = {{pinchScale}})</div>
|
||||
<div class="row" (rotate)="onRotate($event)">Rotate (angle = {{rotateAngle}})</div>
|
||||
<div class="row"></div>
|
Loading…
Reference in New Issue