refactor(core): move render/dom from core
Currently, core depends on DomRenderer, which depends on the browser. This means that if you depend on angular2/core, you will always pull in the browser dom adapter and the browser render, regardless if you need them or not. This PR moves the browser dom adapter and the browser renderer out of core. BREAKING CHANGE If you import browser adapter or dom renderer directly (not via angular2/core), you will have to change the import path.
This commit is contained in:
@@ -1,434 +0,0 @@
|
||||
import {Inject, Injectable, OpaqueToken} from 'angular2/src/core/di';
|
||||
import {AnimationBuilder} from 'angular2/src/animate/animation_builder';
|
||||
import {
|
||||
isPresent,
|
||||
isBlank,
|
||||
RegExpWrapper,
|
||||
CONST_EXPR,
|
||||
stringify,
|
||||
StringWrapper
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||
|
||||
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
||||
|
||||
import {EventManager} from './events/event_manager';
|
||||
|
||||
import {DomSharedStylesHost} from './shared_styles_host';
|
||||
import {WtfScopeFn, wtfLeave, wtfCreateScope} from '../../profile/profile';
|
||||
|
||||
import {
|
||||
Renderer,
|
||||
RenderProtoViewRef,
|
||||
RenderViewRef,
|
||||
RenderElementRef,
|
||||
RenderFragmentRef,
|
||||
RenderViewWithFragments,
|
||||
RenderTemplateCmd,
|
||||
RenderEventDispatcher,
|
||||
RenderComponentTemplate
|
||||
} from '../api';
|
||||
|
||||
import {DOCUMENT} from './dom_tokens';
|
||||
import {createRenderView, NodeFactory, encapsulateStyles} from '../view_factory';
|
||||
import {DefaultRenderView, DefaultRenderFragmentRef, DefaultProtoViewRef} from '../view';
|
||||
import {camelCaseToDashCase} from './util';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata';
|
||||
|
||||
// TODO(tbosch): solve SVG properly once https://github.com/angular/angular/issues/4417 is done
|
||||
const XLINK_NAMESPACE = 'http://www.w3.org/1999/xlink';
|
||||
const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
|
||||
const SVG_ELEMENT_NAMES = CONST_EXPR({
|
||||
'altGlyph': true,
|
||||
'altGlyphDef': true,
|
||||
'altGlyphItem': true,
|
||||
'animate': true,
|
||||
'animateColor': true,
|
||||
'animateMotion': true,
|
||||
'animateTransform': true,
|
||||
'circle': true,
|
||||
'clipPath': true,
|
||||
'color-profile': true,
|
||||
'cursor': true,
|
||||
'defs': true,
|
||||
'desc': true,
|
||||
'ellipse': true,
|
||||
'feBlend': true,
|
||||
'feColorMatrix': true,
|
||||
'feComponentTransfer': true,
|
||||
'feComposite': true,
|
||||
'feConvolveMatrix': true,
|
||||
'feDiffuseLighting': true,
|
||||
'feDisplacementMap': true,
|
||||
'feDistantLight': true,
|
||||
'feFlood': true,
|
||||
'feFuncA': true,
|
||||
'feFuncB': true,
|
||||
'feFuncG': true,
|
||||
'feFuncR': true,
|
||||
'feGaussianBlur': true,
|
||||
'feImage': true,
|
||||
'feMerge': true,
|
||||
'feMergeNode': true,
|
||||
'feMorphology': true,
|
||||
'feOffset': true,
|
||||
'fePointLight': true,
|
||||
'feSpecularLighting': true,
|
||||
'feSpotLight': true,
|
||||
'feTile': true,
|
||||
'feTurbulence': true,
|
||||
'filter': true,
|
||||
'font': true,
|
||||
'font-face': true,
|
||||
'font-face-format': true,
|
||||
'font-face-name': true,
|
||||
'font-face-src': true,
|
||||
'font-face-uri': true,
|
||||
'foreignObject': true,
|
||||
'g': true,
|
||||
// TODO(tbosch): this needs to be disabled
|
||||
// because of an internal project.
|
||||
// We will fix SVG soon, so this will go away...
|
||||
// 'glyph': true,
|
||||
'glyphRef': true,
|
||||
'hkern': true,
|
||||
'image': true,
|
||||
'line': true,
|
||||
'linearGradient': true,
|
||||
'marker': true,
|
||||
'mask': true,
|
||||
'metadata': true,
|
||||
'missing-glyph': true,
|
||||
'mpath': true,
|
||||
'path': true,
|
||||
'pattern': true,
|
||||
'polygon': true,
|
||||
'polyline': true,
|
||||
'radialGradient': true,
|
||||
'rect': true,
|
||||
'set': true,
|
||||
'stop': true,
|
||||
'style': true,
|
||||
'svg': true,
|
||||
'switch': true,
|
||||
'symbol': true,
|
||||
'text': true,
|
||||
'textPath': true,
|
||||
'title': true,
|
||||
'tref': true,
|
||||
'tspan': true,
|
||||
'use': true,
|
||||
'view': true,
|
||||
'vkern': true
|
||||
});
|
||||
|
||||
const SVG_ATTR_NAMESPACES = CONST_EXPR({'href': XLINK_NAMESPACE, 'xlink:href': XLINK_NAMESPACE});
|
||||
|
||||
export abstract class DomRenderer extends Renderer implements NodeFactory<Node> {
|
||||
abstract registerComponentTemplate(template: RenderComponentTemplate);
|
||||
|
||||
abstract resolveComponentTemplate(templateId: string): RenderComponentTemplate;
|
||||
|
||||
abstract createProtoView(componentTemplateId: string,
|
||||
cmds: RenderTemplateCmd[]): RenderProtoViewRef;
|
||||
|
||||
abstract createRootHostView(hostProtoViewRef: RenderProtoViewRef, fragmentCount: number,
|
||||
hostElementSelector: string): RenderViewWithFragments;
|
||||
|
||||
abstract createView(protoViewRef: RenderProtoViewRef,
|
||||
fragmentCount: number): RenderViewWithFragments;
|
||||
|
||||
abstract destroyView(viewRef: RenderViewRef);
|
||||
|
||||
abstract createRootContentInsertionPoint();
|
||||
|
||||
getNativeElementSync(location: RenderElementRef): any {
|
||||
return resolveInternalDomView(location.renderView).boundElements[location.boundElementIndex];
|
||||
}
|
||||
|
||||
getRootNodes(fragment: RenderFragmentRef): Node[] { return resolveInternalDomFragment(fragment); }
|
||||
|
||||
attachFragmentAfterFragment(previousFragmentRef: RenderFragmentRef,
|
||||
fragmentRef: RenderFragmentRef) {
|
||||
var previousFragmentNodes = resolveInternalDomFragment(previousFragmentRef);
|
||||
if (previousFragmentNodes.length > 0) {
|
||||
var sibling = previousFragmentNodes[previousFragmentNodes.length - 1];
|
||||
let nodes = resolveInternalDomFragment(fragmentRef);
|
||||
moveNodesAfterSibling(sibling, nodes);
|
||||
this.animateNodesEnter(nodes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through all nodes being added to the DOM and animates them if necessary
|
||||
* @param nodes
|
||||
*/
|
||||
animateNodesEnter(nodes: Node[]) {
|
||||
for (let i = 0; i < nodes.length; i++) this.animateNodeEnter(nodes[i]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs animations if necessary
|
||||
* @param node
|
||||
*/
|
||||
abstract animateNodeEnter(node: Node);
|
||||
|
||||
/**
|
||||
* If animations are necessary, performs animations then removes the element; otherwise, it just
|
||||
* removes the element.
|
||||
* @param node
|
||||
*/
|
||||
abstract animateNodeLeave(node: Node);
|
||||
|
||||
attachFragmentAfterElement(elementRef: RenderElementRef, fragmentRef: RenderFragmentRef) {
|
||||
var parentView = resolveInternalDomView(elementRef.renderView);
|
||||
var element = parentView.boundElements[elementRef.boundElementIndex];
|
||||
var nodes = resolveInternalDomFragment(fragmentRef);
|
||||
moveNodesAfterSibling(element, nodes);
|
||||
this.animateNodesEnter(nodes);
|
||||
}
|
||||
|
||||
abstract detachFragment(fragmentRef: RenderFragmentRef);
|
||||
|
||||
hydrateView(viewRef: RenderViewRef) { resolveInternalDomView(viewRef).hydrate(); }
|
||||
|
||||
dehydrateView(viewRef: RenderViewRef) { resolveInternalDomView(viewRef).dehydrate(); }
|
||||
|
||||
createTemplateAnchor(attrNameAndValues: string[]): Node {
|
||||
return this.createElement('script', attrNameAndValues);
|
||||
}
|
||||
abstract createElement(name: string, attrNameAndValues: string[]): Node;
|
||||
abstract mergeElement(existing: Node, attrNameAndValues: string[]);
|
||||
abstract createShadowRoot(host: Node, templateId: string): Node;
|
||||
createText(value: string): Node { return DOM.createTextNode(isPresent(value) ? value : ''); }
|
||||
appendChild(parent: Node, child: Node) { DOM.appendChild(parent, child); }
|
||||
abstract on(element: Node, eventName: string, callback: Function);
|
||||
abstract globalOn(target: string, eventName: string, callback: Function): Function;
|
||||
|
||||
setElementProperty(location: RenderElementRef, propertyName: string, propertyValue: any): void {
|
||||
var view = resolveInternalDomView(location.renderView);
|
||||
DOM.setProperty(<Element>view.boundElements[location.boundElementIndex], propertyName,
|
||||
propertyValue);
|
||||
}
|
||||
|
||||
setElementAttribute(location: RenderElementRef, attributeName: string,
|
||||
attributeValue: string): void {
|
||||
var view = resolveInternalDomView(location.renderView);
|
||||
var element = view.boundElements[location.boundElementIndex];
|
||||
var dashCasedAttributeName = camelCaseToDashCase(attributeName);
|
||||
if (isPresent(attributeValue)) {
|
||||
DOM.setAttribute(element, dashCasedAttributeName, stringify(attributeValue));
|
||||
} else {
|
||||
DOM.removeAttribute(element, dashCasedAttributeName);
|
||||
}
|
||||
}
|
||||
|
||||
setElementClass(location: RenderElementRef, className: string, isAdd: boolean): void {
|
||||
var view = resolveInternalDomView(location.renderView);
|
||||
var element = view.boundElements[location.boundElementIndex];
|
||||
if (isAdd) {
|
||||
DOM.addClass(element, className);
|
||||
} else {
|
||||
DOM.removeClass(element, className);
|
||||
}
|
||||
}
|
||||
|
||||
setElementStyle(location: RenderElementRef, styleName: string, styleValue: string): void {
|
||||
var view = resolveInternalDomView(location.renderView);
|
||||
var element = view.boundElements[location.boundElementIndex];
|
||||
var dashCasedStyleName = camelCaseToDashCase(styleName);
|
||||
if (isPresent(styleValue)) {
|
||||
DOM.setStyle(element, dashCasedStyleName, stringify(styleValue));
|
||||
} else {
|
||||
DOM.removeStyle(element, dashCasedStyleName);
|
||||
}
|
||||
}
|
||||
|
||||
invokeElementMethod(location: RenderElementRef, methodName: string, args: any[]): void {
|
||||
var view = resolveInternalDomView(location.renderView);
|
||||
var element = <Element>view.boundElements[location.boundElementIndex];
|
||||
DOM.invoke(element, methodName, args);
|
||||
}
|
||||
|
||||
setText(viewRef: RenderViewRef, textNodeIndex: number, text: string): void {
|
||||
var view = resolveInternalDomView(viewRef);
|
||||
DOM.setText(view.boundTextNodes[textNodeIndex], text);
|
||||
}
|
||||
|
||||
setEventDispatcher(viewRef: RenderViewRef, dispatcher: RenderEventDispatcher): void {
|
||||
resolveInternalDomView(viewRef).setEventDispatcher(dispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DomRenderer_ extends DomRenderer {
|
||||
private _componentTpls: Map<string, RenderComponentTemplate> =
|
||||
new Map<string, RenderComponentTemplate>();
|
||||
private _document;
|
||||
|
||||
constructor(private _eventManager: EventManager,
|
||||
private _domSharedStylesHost: DomSharedStylesHost, private _animate: AnimationBuilder,
|
||||
@Inject(DOCUMENT) document) {
|
||||
super();
|
||||
this._document = document;
|
||||
}
|
||||
|
||||
registerComponentTemplate(template: RenderComponentTemplate) {
|
||||
this._componentTpls.set(template.id, template);
|
||||
if (template.encapsulation !== ViewEncapsulation.Native) {
|
||||
var encapsulatedStyles = encapsulateStyles(template);
|
||||
this._domSharedStylesHost.addStyles(encapsulatedStyles);
|
||||
}
|
||||
}
|
||||
|
||||
createProtoView(componentTemplateId: string, cmds: RenderTemplateCmd[]): RenderProtoViewRef {
|
||||
return new DefaultProtoViewRef(this._componentTpls.get(componentTemplateId), cmds);
|
||||
}
|
||||
|
||||
resolveComponentTemplate(templateId: string): RenderComponentTemplate {
|
||||
return this._componentTpls.get(templateId);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_createRootHostViewScope: WtfScopeFn = wtfCreateScope('DomRenderer#createRootHostView()');
|
||||
createRootHostView(hostProtoViewRef: RenderProtoViewRef, fragmentCount: number,
|
||||
hostElementSelector: string): RenderViewWithFragments {
|
||||
var s = this._createRootHostViewScope();
|
||||
var element = DOM.querySelector(this._document, hostElementSelector);
|
||||
if (isBlank(element)) {
|
||||
wtfLeave(s);
|
||||
throw new BaseException(`The selector "${hostElementSelector}" did not match any elements`);
|
||||
}
|
||||
return wtfLeave(s, this._createView(hostProtoViewRef, element));
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_createViewScope = wtfCreateScope('DomRenderer#createView()');
|
||||
createView(protoViewRef: RenderProtoViewRef, fragmentCount: number): RenderViewWithFragments {
|
||||
var s = this._createViewScope();
|
||||
return wtfLeave(s, this._createView(protoViewRef, null));
|
||||
}
|
||||
|
||||
private _createView(protoViewRef: RenderProtoViewRef,
|
||||
inplaceElement: HTMLElement): RenderViewWithFragments {
|
||||
var dpvr = <DefaultProtoViewRef>protoViewRef;
|
||||
var view = createRenderView(dpvr.template, dpvr.cmds, inplaceElement, this);
|
||||
var sdRoots = view.nativeShadowRoots;
|
||||
for (var i = 0; i < sdRoots.length; i++) {
|
||||
this._domSharedStylesHost.addHost(sdRoots[i]);
|
||||
}
|
||||
return new RenderViewWithFragments(view, view.fragments);
|
||||
}
|
||||
|
||||
destroyView(viewRef: RenderViewRef) {
|
||||
var view = <DefaultRenderView<Node>>viewRef;
|
||||
var sdRoots = view.nativeShadowRoots;
|
||||
for (var i = 0; i < sdRoots.length; i++) {
|
||||
this._domSharedStylesHost.removeHost(sdRoots[i]);
|
||||
}
|
||||
}
|
||||
|
||||
animateNodeEnter(node: Node) {
|
||||
if (DOM.isElementNode(node) && DOM.hasClass(node, 'ng-animate')) {
|
||||
DOM.addClass(node, 'ng-enter');
|
||||
this._animate.css()
|
||||
.addAnimationClass('ng-enter-active')
|
||||
.start(<HTMLElement>node)
|
||||
.onComplete(() => { DOM.removeClass(node, 'ng-enter'); });
|
||||
}
|
||||
}
|
||||
|
||||
animateNodeLeave(node: Node) {
|
||||
if (DOM.isElementNode(node) && DOM.hasClass(node, 'ng-animate')) {
|
||||
DOM.addClass(node, 'ng-leave');
|
||||
this._animate.css()
|
||||
.addAnimationClass('ng-leave-active')
|
||||
.start(<HTMLElement>node)
|
||||
.onComplete(() => {
|
||||
DOM.removeClass(node, 'ng-leave');
|
||||
DOM.remove(node);
|
||||
});
|
||||
} else {
|
||||
DOM.remove(node);
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_detachFragmentScope = wtfCreateScope('DomRenderer#detachFragment()');
|
||||
detachFragment(fragmentRef: RenderFragmentRef) {
|
||||
var s = this._detachFragmentScope();
|
||||
var fragmentNodes = resolveInternalDomFragment(fragmentRef);
|
||||
for (var i = 0; i < fragmentNodes.length; i++) {
|
||||
this.animateNodeLeave(fragmentNodes[i]);
|
||||
}
|
||||
wtfLeave(s);
|
||||
}
|
||||
createElement(name: string, attrNameAndValues: string[]): Node {
|
||||
var isSvg = SVG_ELEMENT_NAMES[name] == true;
|
||||
var el = isSvg ? DOM.createElementNS(SVG_NAMESPACE, name) : DOM.createElement(name);
|
||||
this._setAttributes(el, attrNameAndValues, isSvg);
|
||||
return el;
|
||||
}
|
||||
mergeElement(existing: Node, attrNameAndValues: string[]) {
|
||||
DOM.clearNodes(existing);
|
||||
this._setAttributes(existing, attrNameAndValues, false);
|
||||
}
|
||||
private _setAttributes(node: Node, attrNameAndValues: string[], isSvg: boolean) {
|
||||
for (var attrIdx = 0; attrIdx < attrNameAndValues.length; attrIdx += 2) {
|
||||
var attrName = attrNameAndValues[attrIdx];
|
||||
var attrValue = attrNameAndValues[attrIdx + 1];
|
||||
var attrNs = isSvg ? SVG_ATTR_NAMESPACES[attrName] : null;
|
||||
if (isPresent(attrNs)) {
|
||||
DOM.setAttributeNS(node, XLINK_NAMESPACE, attrName, attrValue);
|
||||
} else {
|
||||
DOM.setAttribute(node, attrName, attrValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
createRootContentInsertionPoint(): Node {
|
||||
return DOM.createComment('root-content-insertion-point');
|
||||
}
|
||||
createShadowRoot(host: Node, templateId: string): Node {
|
||||
var sr = DOM.createShadowRoot(host);
|
||||
var tpl = this._componentTpls.get(templateId);
|
||||
for (var i = 0; i < tpl.styles.length; i++) {
|
||||
DOM.appendChild(sr, DOM.createStyleElement(tpl.styles[i]));
|
||||
}
|
||||
return sr;
|
||||
}
|
||||
on(element: Node, eventName: string, callback: Function) {
|
||||
this._eventManager.addEventListener(<HTMLElement>element, eventName,
|
||||
decoratePreventDefault(callback));
|
||||
}
|
||||
globalOn(target: string, eventName: string, callback: Function): Function {
|
||||
return this._eventManager.addGlobalEventListener(target, eventName,
|
||||
decoratePreventDefault(callback));
|
||||
}
|
||||
}
|
||||
|
||||
function resolveInternalDomView(viewRef: RenderViewRef): DefaultRenderView<Node> {
|
||||
return <DefaultRenderView<Node>>viewRef;
|
||||
}
|
||||
|
||||
function resolveInternalDomFragment(fragmentRef: RenderFragmentRef): Node[] {
|
||||
return (<DefaultRenderFragmentRef<Node>>fragmentRef).nodes;
|
||||
}
|
||||
|
||||
function moveNodesAfterSibling(sibling, nodes) {
|
||||
if (nodes.length > 0 && isPresent(DOM.parentElement(sibling))) {
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
DOM.insertBefore(sibling, nodes[i]);
|
||||
}
|
||||
DOM.insertBefore(nodes[0], sibling);
|
||||
}
|
||||
}
|
||||
|
||||
function decoratePreventDefault(eventHandler: Function): Function {
|
||||
return (event) => {
|
||||
var allowDefaultBehavior = eventHandler(event);
|
||||
if (!allowDefaultBehavior) {
|
||||
// TODO(tbosch): move preventDefault into event plugins...
|
||||
DOM.preventDefault(event);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import {OpaqueToken} from 'angular2/src/core/di';
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
|
||||
/**
|
||||
* A DI Token representing the main rendering context. In a browser this is the DOM Document.
|
||||
*
|
||||
* Note: Document might not be available in the Application Context when Application and Rendering
|
||||
* Contexts are not the same (e.g. when running the application into a Web Worker).
|
||||
*/
|
||||
export const DOCUMENT: OpaqueToken = CONST_EXPR(new OpaqueToken('DocumentToken'));
|
||||
@@ -1,50 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
library angular.events;
|
||||
|
||||
import 'dart:html';
|
||||
import './hammer_common.dart';
|
||||
import 'package:angular2/src/facade/exceptions.dart' show BaseException;
|
||||
import "package:angular2/src/core/di.dart" show Injectable;
|
||||
|
||||
import 'dart:js' as js;
|
||||
|
||||
@Injectable()
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import {HammerGesturesPluginCommon} from './hammer_common';
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
|
||||
@Injectable()
|
||||
export class HammerGesturesPlugin extends HammerGesturesPluginCommon {
|
||||
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: HTMLElement, 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); }); });
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
||||
import {
|
||||
isPresent,
|
||||
isBlank,
|
||||
StringWrapper,
|
||||
RegExpWrapper,
|
||||
NumberWrapper
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {EventManagerPlugin} from './event_manager';
|
||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
|
||||
var modifierKeys = ['alt', 'control', 'meta', 'shift'];
|
||||
var modifierKeyGetters: {[key: string]: (event: KeyboardEvent) => boolean} = {
|
||||
'alt': (event: KeyboardEvent) => event.altKey,
|
||||
'control': (event: KeyboardEvent) => event.ctrlKey,
|
||||
'meta': (event: KeyboardEvent) => event.metaKey,
|
||||
'shift': (event: KeyboardEvent) => event.shiftKey
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class KeyEventsPlugin extends EventManagerPlugin {
|
||||
constructor() { super(); }
|
||||
|
||||
supports(eventName: string): boolean {
|
||||
return isPresent(KeyEventsPlugin.parseEventName(eventName));
|
||||
}
|
||||
|
||||
addEventListener(element: HTMLElement, eventName: string, handler: (Event: any) => any) {
|
||||
var parsedEvent = KeyEventsPlugin.parseEventName(eventName);
|
||||
|
||||
var outsideHandler = KeyEventsPlugin.eventCallback(
|
||||
element, StringMapWrapper.get(parsedEvent, 'fullKey'), handler, this.manager.getZone());
|
||||
|
||||
this.manager.getZone().runOutsideAngular(() => {
|
||||
DOM.on(element, StringMapWrapper.get(parsedEvent, 'domEventName'), outsideHandler);
|
||||
});
|
||||
}
|
||||
|
||||
static parseEventName(eventName: string): {[key: string]: string} {
|
||||
var parts: string[] = eventName.toLowerCase().split('.');
|
||||
|
||||
var domEventName = parts.shift();
|
||||
if ((parts.length === 0) ||
|
||||
!(StringWrapper.equals(domEventName, 'keydown') ||
|
||||
StringWrapper.equals(domEventName, 'keyup'))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var key = KeyEventsPlugin._normalizeKey(parts.pop());
|
||||
|
||||
var fullKey = '';
|
||||
modifierKeys.forEach(modifierName => {
|
||||
if (ListWrapper.contains(parts, modifierName)) {
|
||||
ListWrapper.remove(parts, modifierName);
|
||||
fullKey += modifierName + '.';
|
||||
}
|
||||
});
|
||||
fullKey += key;
|
||||
|
||||
if (parts.length != 0 || key.length === 0) {
|
||||
// returning null instead of throwing to let another plugin process the event
|
||||
return null;
|
||||
}
|
||||
var result = StringMapWrapper.create();
|
||||
StringMapWrapper.set(result, 'domEventName', domEventName);
|
||||
StringMapWrapper.set(result, 'fullKey', fullKey);
|
||||
return result;
|
||||
}
|
||||
|
||||
static getEventFullKey(event: KeyboardEvent): string {
|
||||
var fullKey = '';
|
||||
var key = DOM.getEventKey(event);
|
||||
key = key.toLowerCase();
|
||||
if (StringWrapper.equals(key, ' ')) {
|
||||
key = 'space'; // for readability
|
||||
} else if (StringWrapper.equals(key, '.')) {
|
||||
key = 'dot'; // because '.' is used as a separator in event names
|
||||
}
|
||||
modifierKeys.forEach(modifierName => {
|
||||
if (modifierName != key) {
|
||||
var modifierGetter = StringMapWrapper.get(modifierKeyGetters, modifierName);
|
||||
if (modifierGetter(event)) {
|
||||
fullKey += modifierName + '.';
|
||||
}
|
||||
}
|
||||
});
|
||||
fullKey += key;
|
||||
return fullKey;
|
||||
}
|
||||
|
||||
static eventCallback(element: HTMLElement, fullKey: any, handler: (e: Event) => any,
|
||||
zone: NgZone): (event: KeyboardEvent) => void {
|
||||
return (event) => {
|
||||
if (StringWrapper.equals(KeyEventsPlugin.getEventFullKey(event), fullKey)) {
|
||||
zone.run(() => handler(event));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
static _normalizeKey(keyName: string): string {
|
||||
// TODO: switch to a StringMap if the mapping grows too much
|
||||
switch (keyName) {
|
||||
case 'esc':
|
||||
return 'escape';
|
||||
default:
|
||||
return keyName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
||||
import {Inject, Injectable} from 'angular2/src/core/di';
|
||||
import {SetWrapper} from 'angular2/src/facade/collection';
|
||||
import {DOCUMENT} from './dom_tokens';
|
||||
|
||||
@Injectable()
|
||||
export class SharedStylesHost {
|
||||
/** @internal */
|
||||
_styles: string[] = [];
|
||||
/** @internal */
|
||||
_stylesSet = new Set<string>();
|
||||
|
||||
constructor() {}
|
||||
|
||||
addStyles(styles: string[]) {
|
||||
var additions = [];
|
||||
styles.forEach(style => {
|
||||
if (!SetWrapper.has(this._stylesSet, style)) {
|
||||
this._stylesSet.add(style);
|
||||
this._styles.push(style);
|
||||
additions.push(style);
|
||||
}
|
||||
});
|
||||
this.onStylesAdded(additions);
|
||||
}
|
||||
|
||||
onStylesAdded(additions: string[]) {}
|
||||
|
||||
getAllStyles(): string[] { return this._styles; }
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DomSharedStylesHost extends SharedStylesHost {
|
||||
private _hostNodes = new Set<Node>();
|
||||
constructor(@Inject(DOCUMENT) doc: any) {
|
||||
super();
|
||||
this._hostNodes.add(doc.head);
|
||||
}
|
||||
/** @internal */
|
||||
_addStylesToHost(styles: string[], host: Node) {
|
||||
for (var i = 0; i < styles.length; i++) {
|
||||
var style = styles[i];
|
||||
DOM.appendChild(host, DOM.createStyleElement(style));
|
||||
}
|
||||
}
|
||||
addHost(hostNode: Node) {
|
||||
this._addStylesToHost(this._styles, hostNode);
|
||||
this._hostNodes.add(hostNode);
|
||||
}
|
||||
removeHost(hostNode: Node) { SetWrapper.delete(this._hostNodes, hostNode); }
|
||||
|
||||
onStylesAdded(additions: string[]) {
|
||||
this._hostNodes.forEach((hostNode) => { this._addStylesToHost(additions, hostNode); });
|
||||
}
|
||||
}
|
||||
+3
-27
@@ -1,9 +1,8 @@
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||
import {Injectable, Inject, OpaqueToken} from 'angular2/src/core/di';
|
||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
export const EVENT_MANAGER_PLUGINS: OpaqueToken =
|
||||
CONST_EXPR(new OpaqueToken("EventManagerPlugins"));
|
||||
@@ -55,27 +54,4 @@ export class EventManagerPlugin {
|
||||
addGlobalEventListener(element: string, eventName: string, handler: Function): Function {
|
||||
throw "not implemented";
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DomEventsPlugin extends EventManagerPlugin {
|
||||
manager: EventManager;
|
||||
|
||||
// This plugin should come last in the list of plugins, because it accepts all
|
||||
// events.
|
||||
supports(eventName: string): boolean { return true; }
|
||||
|
||||
addEventListener(element: HTMLElement, eventName: string, handler: Function) {
|
||||
var zone = this.manager.getZone();
|
||||
var outsideHandler = (event) => zone.run(() => handler(event));
|
||||
this.manager.getZone().runOutsideAngular(() => { DOM.on(element, eventName, outsideHandler); });
|
||||
}
|
||||
|
||||
addGlobalEventListener(target: string, eventName: string, handler: Function): Function {
|
||||
var element = DOM.getGlobalEventTarget(target);
|
||||
var zone = this.manager.getZone();
|
||||
var outsideHandler = (event) => zone.run(() => handler(event));
|
||||
return this.manager.getZone().runOutsideAngular(
|
||||
() => { return DOM.onAndCancel(element, eventName, outsideHandler); });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* @module
|
||||
* @description
|
||||
* This module provides advanced support for extending dom strategy.
|
||||
*/
|
||||
|
||||
export * from './dom/shared_styles_host';
|
||||
export * from './dom/dom_renderer';
|
||||
export * from './dom/dom_tokens';
|
||||
export * from './api';
|
||||
Reference in New Issue
Block a user