repackaging: all the file moves

This commit is contained in:
Igor Minar
2016-04-28 08:02:15 -07:00
committed by Misko Hevery
parent 4fe0f1fa65
commit 505da6c0a8
739 changed files with 0 additions and 52 deletions

View File

@ -0,0 +1,200 @@
import {
DateWrapper,
StringWrapper,
RegExpWrapper,
NumberWrapper,
isPresent
} from 'angular2/src/facade/lang';
import {Math} from 'angular2/src/facade/math';
import {camelCaseToDashCase} from 'angular2/src/platform/dom/util';
import {StringMapWrapper} from 'angular2/src/facade/collection';
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
import {BrowserDetails} from './browser_details';
import {CssAnimationOptions} from './css_animation_options';
export class Animation {
/** functions to be called upon completion */
callbacks: Function[] = [];
/** the duration (ms) of the animation (whether from CSS or manually set) */
computedDuration: number;
/** the animation delay (ms) (whether from CSS or manually set) */
computedDelay: number;
/** timestamp of when the animation started */
startTime: number;
/** functions for removing event listeners */
eventClearFunctions: Function[] = [];
/** flag used to track whether or not the animation has finished */
completed: boolean = false;
private _stringPrefix: string = '';
/** total amount of time that the animation should take including delay */
get totalTime(): number {
let delay = this.computedDelay != null ? this.computedDelay : 0;
let duration = this.computedDuration != null ? this.computedDuration : 0;
return delay + duration;
}
/**
* Stores the start time and starts the animation
* @param element
* @param data
* @param browserDetails
*/
constructor(public element: HTMLElement, public data: CssAnimationOptions,
public browserDetails: BrowserDetails) {
this.startTime = DateWrapper.toMillis(DateWrapper.now());
this._stringPrefix = DOM.getAnimationPrefix();
this.setup();
this.wait((timestamp: any) => this.start());
}
wait(callback: Function) {
// Firefox requires 2 frames for some reason
this.browserDetails.raf(callback, 2);
}
/**
* Sets up the initial styles before the animation is started
*/
setup(): void {
if (this.data.fromStyles != null) this.applyStyles(this.data.fromStyles);
if (this.data.duration != null)
this.applyStyles({'transitionDuration': this.data.duration.toString() + 'ms'});
if (this.data.delay != null)
this.applyStyles({'transitionDelay': this.data.delay.toString() + 'ms'});
}
/**
* After the initial setup has occurred, this method adds the animation styles
*/
start(): void {
this.addClasses(this.data.classesToAdd);
this.addClasses(this.data.animationClasses);
this.removeClasses(this.data.classesToRemove);
if (this.data.toStyles != null) this.applyStyles(this.data.toStyles);
var computedStyles = DOM.getComputedStyle(this.element);
this.computedDelay =
Math.max(this.parseDurationString(
computedStyles.getPropertyValue(this._stringPrefix + 'transition-delay')),
this.parseDurationString(
this.element.style.getPropertyValue(this._stringPrefix + 'transition-delay')));
this.computedDuration = Math.max(this.parseDurationString(computedStyles.getPropertyValue(
this._stringPrefix + 'transition-duration')),
this.parseDurationString(this.element.style.getPropertyValue(
this._stringPrefix + 'transition-duration')));
this.addEvents();
}
/**
* Applies the provided styles to the element
* @param styles
*/
applyStyles(styles: {[key: string]: any}): void {
StringMapWrapper.forEach(styles, (value: any, key: string) => {
var dashCaseKey = camelCaseToDashCase(key);
if (isPresent(DOM.getStyle(this.element, dashCaseKey))) {
DOM.setStyle(this.element, dashCaseKey, value.toString());
} else {
DOM.setStyle(this.element, this._stringPrefix + dashCaseKey, value.toString());
}
});
}
/**
* Adds the provided classes to the element
* @param classes
*/
addClasses(classes: string[]): void {
for (let i = 0, len = classes.length; i < len; i++) DOM.addClass(this.element, classes[i]);
}
/**
* Removes the provided classes from the element
* @param classes
*/
removeClasses(classes: string[]): void {
for (let i = 0, len = classes.length; i < len; i++) DOM.removeClass(this.element, classes[i]);
}
/**
* Adds events to track when animations have finished
*/
addEvents(): void {
if (this.totalTime > 0) {
this.eventClearFunctions.push(DOM.onAndCancel(
this.element, DOM.getTransitionEnd(), (event: any) => this.handleAnimationEvent(event)));
} else {
this.handleAnimationCompleted();
}
}
handleAnimationEvent(event: any): void {
let elapsedTime = Math.round(event.elapsedTime * 1000);
if (!this.browserDetails.elapsedTimeIncludesDelay) elapsedTime += this.computedDelay;
event.stopPropagation();
if (elapsedTime >= this.totalTime) this.handleAnimationCompleted();
}
/**
* Runs all animation callbacks and removes temporary classes
*/
handleAnimationCompleted(): void {
this.removeClasses(this.data.animationClasses);
this.callbacks.forEach(callback => callback());
this.callbacks = [];
this.eventClearFunctions.forEach(fn => fn());
this.eventClearFunctions = [];
this.completed = true;
}
/**
* Adds animation callbacks to be called upon completion
* @param callback
* @returns {Animation}
*/
onComplete(callback: Function): Animation {
if (this.completed) {
callback();
} else {
this.callbacks.push(callback);
}
return this;
}
/**
* Converts the duration string to the number of milliseconds
* @param duration
* @returns {number}
*/
parseDurationString(duration: string): number {
var maxValue = 0;
// duration must have at least 2 characters to be valid. (number + type)
if (duration == null || duration.length < 2) {
return maxValue;
} else if (duration.substring(duration.length - 2) == 'ms') {
let value = NumberWrapper.parseInt(this.stripLetters(duration), 10);
if (value > maxValue) maxValue = value;
} else if (duration.substring(duration.length - 1) == 's') {
let ms = NumberWrapper.parseFloat(this.stripLetters(duration)) * 1000;
let value = Math.floor(ms);
if (value > maxValue) maxValue = value;
}
return maxValue;
}
/**
* Strips the letters from the duration string
* @param str
* @returns {string}
*/
stripLetters(str: string): string {
return StringWrapper.replaceAll(str, RegExpWrapper.create('[^0-9]+$', ''), '');
}
}

View File

@ -0,0 +1,19 @@
import {Injectable} from 'angular2/src/core/di';
import {CssAnimationBuilder} from './css_animation_builder';
import {BrowserDetails} from './browser_details';
@Injectable()
export class AnimationBuilder {
/**
* Used for DI
* @param browserDetails
*/
constructor(public browserDetails: BrowserDetails) {}
/**
* Creates a new CSS Animation
* @returns {CssAnimationBuilder}
*/
css(): CssAnimationBuilder { return new CssAnimationBuilder(this.browserDetails); }
}

View File

@ -0,0 +1,55 @@
import {Injectable} from 'angular2/src/core/di';
import {Math} from 'angular2/src/facade/math';
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
@Injectable()
export class BrowserDetails {
elapsedTimeIncludesDelay = false;
constructor() { this.doesElapsedTimeIncludesDelay(); }
/**
* Determines if `event.elapsedTime` includes transition delay in the current browser. At this
* time, Chrome and Opera seem to be the only browsers that include this.
*/
doesElapsedTimeIncludesDelay(): void {
var div = DOM.createElement('div');
DOM.setAttribute(div, 'style', `position: absolute; top: -9999px; left: -9999px; width: 1px;
height: 1px; transition: all 1ms linear 1ms;`);
// Firefox requires that we wait for 2 frames for some reason
this.raf((timestamp: any) => {
DOM.on(div, 'transitionend', (event: any) => {
var elapsed = Math.round(event.elapsedTime * 1000);
this.elapsedTimeIncludesDelay = elapsed == 2;
DOM.remove(div);
});
DOM.setStyle(div, 'width', '2px');
}, 2);
}
raf(callback: Function, frames: number = 1): Function {
var queue: RafQueue = new RafQueue(callback, frames);
return () => queue.cancel();
}
}
class RafQueue {
currentFrameId: number;
constructor(public callback: Function, public frames: number) { this._raf(); }
private _raf() {
this.currentFrameId =
DOM.requestAnimationFrame((timestamp: number) => this._nextFrame(timestamp));
}
private _nextFrame(timestamp: number) {
this.frames--;
if (this.frames > 0) {
this._raf();
} else {
this.callback(timestamp);
}
}
cancel() {
DOM.cancelAnimationFrame(this.currentFrameId);
this.currentFrameId = null;
}
}

View File

@ -0,0 +1,93 @@
import {CssAnimationOptions} from './css_animation_options';
import {Animation} from './animation';
import {BrowserDetails} from './browser_details';
export class CssAnimationBuilder {
/** @type {CssAnimationOptions} */
data: CssAnimationOptions = new CssAnimationOptions();
/**
* Accepts public properties for CssAnimationBuilder
*/
constructor(public browserDetails: BrowserDetails) {}
/**
* Adds a temporary class that will be removed at the end of the animation
* @param className
*/
addAnimationClass(className: string): CssAnimationBuilder {
this.data.animationClasses.push(className);
return this;
}
/**
* Adds a class that will remain on the element after the animation has finished
* @param className
*/
addClass(className: string): CssAnimationBuilder {
this.data.classesToAdd.push(className);
return this;
}
/**
* Removes a class from the element
* @param className
*/
removeClass(className: string): CssAnimationBuilder {
this.data.classesToRemove.push(className);
return this;
}
/**
* Sets the animation duration (and overrides any defined through CSS)
* @param duration
*/
setDuration(duration: number): CssAnimationBuilder {
this.data.duration = duration;
return this;
}
/**
* Sets the animation delay (and overrides any defined through CSS)
* @param delay
*/
setDelay(delay: number): CssAnimationBuilder {
this.data.delay = delay;
return this;
}
/**
* Sets styles for both the initial state and the destination state
* @param from
* @param to
*/
setStyles(from: {[key: string]: any}, to: {[key: string]: any}): CssAnimationBuilder {
return this.setFromStyles(from).setToStyles(to);
}
/**
* Sets the initial styles for the animation
* @param from
*/
setFromStyles(from: {[key: string]: any}): CssAnimationBuilder {
this.data.fromStyles = from;
return this;
}
/**
* Sets the destination styles for the animation
* @param to
*/
setToStyles(to: {[key: string]: any}): CssAnimationBuilder {
this.data.toStyles = to;
return this;
}
/**
* Starts the animation and returns a promise
* @param element
*/
start(element: HTMLElement): Animation {
return new Animation(element, this.data, this.browserDetails);
}
}

View File

@ -0,0 +1,22 @@
export class CssAnimationOptions {
/** initial styles for the element */
fromStyles: {[key: string]: any};
/** destination styles for the element */
toStyles: {[key: string]: any};
/** classes to be added to the element */
classesToAdd: string[] = [];
/** classes to be removed from the element */
classesToRemove: string[] = [];
/** classes to be added for the duration of the animation */
animationClasses: string[] = [];
/** override the duration of the animation (in milliseconds) */
duration: number;
/** override the transition delay (in milliseconds) */
delay: number;
}

View File

@ -0,0 +1,536 @@
library angular.core.facade.dom;
import 'dart:html';
import 'package:angular2/platform/common_dom.dart' show setRootDomAdapter;
import 'generic_browser_adapter.dart' show GenericBrowserDomAdapter;
import 'package:angular2/src/facade/browser.dart';
import 'package:angular2/src/facade/lang.dart' show isBlank, isPresent;
import 'dart:js' as js;
// WARNING: Do not expose outside this class. Parsing HTML using this
// sanitizer is a security risk.
class _IdentitySanitizer implements NodeTreeSanitizer {
void sanitizeTree(Node node) {}
}
final _identitySanitizer = new _IdentitySanitizer();
final _keyCodeToKeyMap = const {
8: 'Backspace',
9: 'Tab',
12: 'Clear',
13: 'Enter',
16: 'Shift',
17: 'Control',
18: 'Alt',
19: 'Pause',
20: 'CapsLock',
27: 'Escape',
32: ' ',
33: 'PageUp',
34: 'PageDown',
35: 'End',
36: 'Home',
37: 'ArrowLeft',
38: 'ArrowUp',
39: 'ArrowRight',
40: 'ArrowDown',
45: 'Insert',
46: 'Delete',
65: 'a',
66: 'b',
67: 'c',
68: 'd',
69: 'e',
70: 'f',
71: 'g',
72: 'h',
73: 'i',
74: 'j',
75: 'k',
76: 'l',
77: 'm',
78: 'n',
79: 'o',
80: 'p',
81: 'q',
82: 'r',
83: 's',
84: 't',
85: 'u',
86: 'v',
87: 'w',
88: 'x',
89: 'y',
90: 'z',
91: 'OS',
93: 'ContextMenu',
96: '0',
97: '1',
98: '2',
99: '3',
100: '4',
101: '5',
102: '6',
103: '7',
104: '8',
105: '9',
106: '*',
107: '+',
109: '-',
110: '.',
111: '/',
112: 'F1',
113: 'F2',
114: 'F3',
115: 'F4',
116: 'F5',
117: 'F6',
118: 'F7',
119: 'F8',
120: 'F9',
121: 'F10',
122: 'F11',
123: 'F12',
144: 'NumLock',
145: 'ScrollLock'
};
final bool _supportsTemplateElement = () {
try {
return new TemplateElement().content != null;
} catch (_) {
return false;
}
}();
class BrowserDomAdapter extends GenericBrowserDomAdapter {
js.JsFunction _setProperty;
js.JsFunction _getProperty;
js.JsFunction _hasProperty;
Map<String, bool> _hasPropertyCache;
BrowserDomAdapter() {
_hasPropertyCache = new Map();
_setProperty = js.context.callMethod(
'eval', ['(function(el, prop, value) { el[prop] = value; })']);
_getProperty = js.context
.callMethod('eval', ['(function(el, prop) { return el[prop]; })']);
_hasProperty = js.context
.callMethod('eval', ['(function(el, prop) { return prop in el; })']);
}
static void makeCurrent() {
setRootDomAdapter(new BrowserDomAdapter());
}
bool hasProperty(Element element, String name) {
// Always return true as the serverside version html_adapter.dart does so.
// TODO: change this once we have schema support.
// Note: This nees to kept in sync with html_adapter.dart!
return true;
}
void setProperty(Element element, String name, Object value) {
var cacheKey = "${element.tagName}.${name}";
var hasProperty = this._hasPropertyCache[cacheKey];
if (hasProperty == null) {
hasProperty = this._hasProperty.apply([element, name]);
this._hasPropertyCache[cacheKey] = hasProperty;
}
if (hasProperty) {
_setProperty.apply([element, name, value]);
}
}
getProperty(Element element, String name) =>
_getProperty.apply([element, name]);
invoke(Element element, String methodName, List args) =>
this.getProperty(element, methodName).apply(args, thisArg: element);
// TODO(tbosch): move this into a separate environment class once we have it
logError(error) {
window.console.error(error);
}
log(error) {
window.console.log(error);
}
logGroup(error) {
window.console.group(error);
this.logError(error);
}
logGroupEnd() {
window.console.groupEnd();
}
@override
Map<String, String> get attrToPropMap => const <String, String>{
'class': 'className',
'innerHtml': 'innerHTML',
'readonly': 'readOnly',
'tabindex': 'tabIndex',
};
Element query(String selector) => document.querySelector(selector);
Element querySelector(el, String selector) => el.querySelector(selector);
ElementList querySelectorAll(el, String selector) =>
el.querySelectorAll(selector);
void on(EventTarget element, String event, callback(arg)) {
// due to https://code.google.com/p/dart/issues/detail?id=17406
// addEventListener misses zones so we use element.on.
element.on[event].listen(callback);
}
Function onAndCancel(EventTarget element, String event, callback(arg)) {
// due to https://code.google.com/p/dart/issues/detail?id=17406
// addEventListener misses zones so we use element.on.
var subscription = element.on[event].listen(callback);
return subscription.cancel;
}
void dispatchEvent(EventTarget el, Event evt) {
el.dispatchEvent(evt);
}
MouseEvent createMouseEvent(String eventType) =>
new MouseEvent(eventType, canBubble: true);
Event createEvent(String eventType) => new Event(eventType, canBubble: true);
void preventDefault(Event evt) {
evt.preventDefault();
}
bool isPrevented(Event evt) {
return evt.defaultPrevented;
}
String getInnerHTML(Element el) => el.innerHtml;
String getOuterHTML(Element el) => el.outerHtml;
void setInnerHTML(Element el, String value) {
el.innerHtml = value;
}
String nodeName(Node el) => el.nodeName;
String nodeValue(Node el) => el.nodeValue;
String type(InputElement el) => el.type;
Node content(TemplateElement el) =>
_supportsTemplateElement ? el.content : el;
Node firstChild(el) => el.firstChild;
Node nextSibling(Node el) => el.nextNode;
Element parentElement(Node el) => el.parentNode;
List<Node> childNodes(Node el) => el.childNodes;
List childNodesAsList(Node el) => childNodes(el).toList();
void clearNodes(Node el) {
el.nodes = const [];
}
void appendChild(Node el, Node node) {
el.append(node);
}
void removeChild(el, Node node) {
node.remove();
}
void replaceChild(Node el, Node newNode, Node oldNode) {
oldNode.replaceWith(newNode);
}
ChildNode remove(ChildNode el) {
return el..remove();
}
void insertBefore(Node el, node) {
el.parentNode.insertBefore(node, el);
}
void insertAllBefore(Node el, Iterable<Node> nodes) {
el.parentNode.insertAllBefore(nodes, el);
}
void insertAfter(Node el, Node node) {
el.parentNode.insertBefore(node, el.nextNode);
}
String getText(Node el) => el.text;
void setText(Node el, String value) {
el.text = value;
}
String getValue(el) => el.value;
void setValue(el, String value) {
el.value = value;
}
bool getChecked(InputElement el) => el.checked;
void setChecked(InputElement el, bool isChecked) {
el.checked = isChecked;
}
Comment createComment(String text) {
return new Comment(text);
}
TemplateElement createTemplate(String html) {
var t = new TemplateElement();
// We do not sanitize because templates are part of the application code
// not user code.
t.setInnerHtml(html, treeSanitizer: _identitySanitizer);
return t;
}
Element createElement(String tagName, [HtmlDocument doc = null]) {
if (doc == null) doc = document;
return doc.createElement(tagName);
}
Element createElementNS(String ns, String tagName, [HtmlDocument doc = null]) {
if (doc == null) doc = document;
return doc.createElementNS(ns, tagName);
}
Text createTextNode(String text, [HtmlDocument doc = null]) {
return new Text(text);
}
createScriptTag(String attrName, String attrValue,
[HtmlDocument doc = null]) {
if (doc == null) doc = document;
var el = doc.createElement('SCRIPT');
el.setAttribute(attrName, attrValue);
return el;
}
StyleElement createStyleElement(String css, [HtmlDocument doc = null]) {
if (doc == null) doc = document;
var el = doc.createElement('STYLE');
el.text = css;
return el;
}
ShadowRoot createShadowRoot(Element el) => el.createShadowRoot();
ShadowRoot getShadowRoot(Element el) => el.shadowRoot;
Element getHost(Element el) => (el as ShadowRoot).host;
clone(Node node) => node.clone(true);
List<Node> getElementsByClassName(Element element, String name) =>
element.getElementsByClassName(name);
List<Node> getElementsByTagName(Element element, String name) =>
element.querySelectorAll(name);
List<String> classList(Element element) => element.classes.toList();
void addClass(Element element, String className) {
element.classes.add(className);
}
void removeClass(Element element, String className) {
element.classes.remove(className);
}
bool hasClass(Element element, String className) =>
element.classes.contains(className);
void setStyle(Element element, String styleName, String styleValue) {
element.style.setProperty(styleName, styleValue);
}
bool hasStyle(Element element, String styleName, [String styleValue]) {
var value = this.getStyle(element, styleName);
return isPresent(styleValue) ? value == styleValue : value.length > 0;
}
void removeStyle(Element element, String styleName) {
element.style.removeProperty(styleName);
}
String getStyle(Element element, String styleName) {
return element.style.getPropertyValue(styleName);
}
String tagName(Element element) => element.tagName;
Map<String, String> attributeMap(Element element) {
var result = {};
result.addAll(element.attributes);
// TODO(tbosch): element.getNamespacedAttributes() somehow does not return the attribute value
var xlinkHref = element.getAttributeNS('http://www.w3.org/1999/xlink', 'href');
if (xlinkHref != null) {
result['xlink:href'] = xlinkHref;
}
return result;
}
bool hasAttribute(Element element, String attribute) =>
element.attributes.containsKey(attribute);
bool hasAttributeNS(Element element, String ns, String attribute) =>
element.getAttributeNS(ns, attribute) != null;
String getAttribute(Element element, String attribute) =>
element.getAttribute(attribute);
String getAttributeNS(Element element, String ns, String attribute) =>
element.getAttributeNS(ns, attribute);
void setAttribute(Element element, String name, String value) {
element.setAttribute(name, value);
}
void setAttributeNS(Element element, String ns, String name, String value) {
element.setAttributeNS(ns, name, value);
}
void removeAttribute(Element element, String name) {
//there is no removeAttribute method as of now in Dart:
//https://code.google.com/p/dart/issues/detail?id=19934
element.attributes.remove(name);
}
void removeAttributeNS(Element element, String ns, String name) {
element.getNamespacedAttributes(ns).remove(name);
}
Node templateAwareRoot(Element el) => el is TemplateElement ? el.content : el;
HtmlDocument createHtmlDocument() =>
document.implementation.createHtmlDocument('fakeTitle');
HtmlDocument defaultDoc() => document;
Rectangle getBoundingClientRect(el) => el.getBoundingClientRect();
String getTitle() => document.title;
void setTitle(String newTitle) {
document.title = newTitle;
}
bool elementMatches(n, String selector) =>
n is Element && n.matches(selector);
bool isTemplateElement(Element el) => el is TemplateElement;
bool isTextNode(Node node) => node.nodeType == Node.TEXT_NODE;
bool isCommentNode(Node node) => node.nodeType == Node.COMMENT_NODE;
bool isElementNode(Node node) => node.nodeType == Node.ELEMENT_NODE;
bool hasShadowRoot(Node node) {
return node is Element && node.shadowRoot != null;
}
bool isShadowRoot(Node node) {
return node is ShadowRoot;
}
Node importIntoDoc(Node node) {
return document.importNode(node, true);
}
Node adoptNode(Node node) {
return document.adoptNode(node);
}
String getHref(AnchorElement element) {
return element.href;
}
String getEventKey(KeyboardEvent event) {
int keyCode = event.keyCode;
return _keyCodeToKeyMap.containsKey(keyCode)
? _keyCodeToKeyMap[keyCode]
: 'Unidentified';
}
getGlobalEventTarget(String target) {
if (target == "window") {
return window;
} else if (target == "document") {
return document;
} else if (target == "body") {
return document.body;
}
}
getHistory() {
return window.history;
}
getLocation() {
return window.location;
}
String getBaseHref() {
var href = getBaseElementHref();
if (href == null) {
return null;
}
return _relativePath(href);
}
resetBaseElement() {
baseElement = null;
}
String getUserAgent() {
return window.navigator.userAgent;
}
void setData(Element element, String name, String value) {
element.dataset[name] = value;
}
String getData(Element element, String name) {
return element.dataset[name];
}
getComputedStyle(elem) => elem.getComputedStyle();
// TODO(tbosch): move this into a separate environment class once we have it
setGlobalVar(String path, value) {
var parts = path.split('.');
var obj = js.context;
while (parts.length > 1) {
var name = parts.removeAt(0);
if (obj.hasProperty(name)) {
obj = obj[name];
} else {
obj = obj[name] = new js.JsObject(js.context['Object']);
}
}
obj[parts.removeAt(0)] = value;
}
requestAnimationFrame(callback) {
return window.requestAnimationFrame(callback);
}
cancelAnimationFrame(id) {
window.cancelAnimationFrame(id);
}
num performanceNow() {
return window.performance.now();
}
parse(s) {
throw 'not implemented';
}
}
var baseElement = null;
String getBaseElementHref() {
if (baseElement == null) {
baseElement = document.querySelector('base');
if (baseElement == null) {
return null;
}
}
return baseElement.getAttribute('href');
}
// based on urlUtils.js in AngularJS 1
AnchorElement _urlParsingNode = null;
String _relativePath(String url) {
if (_urlParsingNode == null) {
_urlParsingNode = new AnchorElement();
}
_urlParsingNode.href = url;
var pathname = _urlParsingNode.pathname;
return (pathname[0] == '/') ? pathname : '/${pathname}';
}

View File

@ -0,0 +1,372 @@
import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
import {isBlank, isPresent, global, setValueOnPath, DateWrapper} from 'angular2/src/facade/lang';
import {setRootDomAdapter} from 'angular2/src/platform/dom/dom_adapter';
import {GenericBrowserDomAdapter} from './generic_browser_adapter';
var _attrToPropMap = {
'class': 'className',
'innerHtml': 'innerHTML',
'readonly': 'readOnly',
'tabindex': 'tabIndex'
};
const DOM_KEY_LOCATION_NUMPAD = 3;
// Map to convert some key or keyIdentifier values to what will be returned by getEventKey
var _keyMap = {
// The following values are here for cross-browser compatibility and to match the W3C standard
// cf http://www.w3.org/TR/DOM-Level-3-Events-key/
'\b': 'Backspace',
'\t': 'Tab',
'\x7F': 'Delete',
'\x1B': 'Escape',
'Del': 'Delete',
'Esc': 'Escape',
'Left': 'ArrowLeft',
'Right': 'ArrowRight',
'Up': 'ArrowUp',
'Down': 'ArrowDown',
'Menu': 'ContextMenu',
'Scroll': 'ScrollLock',
'Win': 'OS'
};
// There is a bug in Chrome for numeric keypad keys:
// https://code.google.com/p/chromium/issues/detail?id=155654
// 1, 2, 3 ... are reported as A, B, C ...
var _chromeNumKeyPadMap = {
'A': '1',
'B': '2',
'C': '3',
'D': '4',
'E': '5',
'F': '6',
'G': '7',
'H': '8',
'I': '9',
'J': '*',
'K': '+',
'M': '-',
'N': '.',
'O': '/',
'\x60': '0',
'\x90': 'NumLock'
};
/**
* A `DomAdapter` powered by full browser DOM APIs.
*/
/* tslint:disable:requireParameterType */
export class BrowserDomAdapter extends GenericBrowserDomAdapter {
parse(templateHtml: string) { throw new Error("parse not implemented"); }
static makeCurrent() { setRootDomAdapter(new BrowserDomAdapter()); }
hasProperty(element, name: string): boolean { return name in element; }
setProperty(el: /*element*/ any, name: string, value: any) { el[name] = value; }
getProperty(el: /*element*/ any, name: string): any { return el[name]; }
invoke(el: /*element*/ any, methodName: string, args: any[]): any {
el[methodName].apply(el, args);
}
// TODO(tbosch): move this into a separate environment class once we have it
logError(error) {
if (window.console.error) {
window.console.error(error);
} else {
window.console.log(error);
}
}
log(error) { window.console.log(error); }
logGroup(error) {
if (window.console.group) {
window.console.group(error);
this.logError(error);
} else {
window.console.log(error);
}
}
logGroupEnd() {
if (window.console.groupEnd) {
window.console.groupEnd();
}
}
get attrToPropMap(): any { return _attrToPropMap; }
query(selector: string): any { return document.querySelector(selector); }
querySelector(el, selector: string): HTMLElement { return el.querySelector(selector); }
querySelectorAll(el, selector: string): any[] { return el.querySelectorAll(selector); }
on(el, evt, listener) { el.addEventListener(evt, listener, false); }
onAndCancel(el, evt, listener): Function {
el.addEventListener(evt, listener, false);
// Needed to follow Dart's subscription semantic, until fix of
// https://code.google.com/p/dart/issues/detail?id=17406
return () => { el.removeEventListener(evt, listener, false); };
}
dispatchEvent(el, evt) { el.dispatchEvent(evt); }
createMouseEvent(eventType: string): MouseEvent {
var evt: MouseEvent = document.createEvent('MouseEvent');
evt.initEvent(eventType, true, true);
return evt;
}
createEvent(eventType): Event {
var evt: Event = document.createEvent('Event');
evt.initEvent(eventType, true, true);
return evt;
}
preventDefault(evt: Event) {
evt.preventDefault();
evt.returnValue = false;
}
isPrevented(evt: Event): boolean {
return evt.defaultPrevented || isPresent(evt.returnValue) && !evt.returnValue;
}
getInnerHTML(el): string { return el.innerHTML; }
getOuterHTML(el): string { return el.outerHTML; }
nodeName(node: Node): string { return node.nodeName; }
nodeValue(node: Node): string { return node.nodeValue; }
type(node: HTMLInputElement): string { return node.type; }
content(node: Node): Node {
if (this.hasProperty(node, "content")) {
return (<any>node).content;
} else {
return node;
}
}
firstChild(el): Node { return el.firstChild; }
nextSibling(el): Node { return el.nextSibling; }
parentElement(el): Node { return el.parentNode; }
childNodes(el): Node[] { return el.childNodes; }
childNodesAsList(el): any[] {
var childNodes = el.childNodes;
var res = ListWrapper.createFixedSize(childNodes.length);
for (var i = 0; i < childNodes.length; i++) {
res[i] = childNodes[i];
}
return res;
}
clearNodes(el) {
while (el.firstChild) {
el.removeChild(el.firstChild);
}
}
appendChild(el, node) { el.appendChild(node); }
removeChild(el, node) { el.removeChild(node); }
replaceChild(el: Node, newChild, oldChild) { el.replaceChild(newChild, oldChild); }
remove(node): Node {
if (node.parentNode) {
node.parentNode.removeChild(node);
}
return node;
}
insertBefore(el, node) { el.parentNode.insertBefore(node, el); }
insertAllBefore(el, nodes) { nodes.forEach(n => el.parentNode.insertBefore(n, el)); }
insertAfter(el, node) { el.parentNode.insertBefore(node, el.nextSibling); }
setInnerHTML(el, value) { el.innerHTML = value; }
getText(el): string { return el.textContent; }
// TODO(vicb): removed Element type because it does not support StyleElement
setText(el, value: string) { el.textContent = value; }
getValue(el): string { return el.value; }
setValue(el, value: string) { el.value = value; }
getChecked(el): boolean { return el.checked; }
setChecked(el, value: boolean) { el.checked = value; }
createComment(text: string): Comment { return document.createComment(text); }
createTemplate(html): HTMLElement {
var t = document.createElement('template');
t.innerHTML = html;
return t;
}
createElement(tagName, doc = document): HTMLElement { return doc.createElement(tagName); }
createElementNS(ns, tagName, doc = document): Element { return doc.createElementNS(ns, tagName); }
createTextNode(text: string, doc = document): Text { return doc.createTextNode(text); }
createScriptTag(attrName: string, attrValue: string, doc = document): HTMLScriptElement {
var el = <HTMLScriptElement>doc.createElement('SCRIPT');
el.setAttribute(attrName, attrValue);
return el;
}
createStyleElement(css: string, doc = document): HTMLStyleElement {
var style = <HTMLStyleElement>doc.createElement('style');
this.appendChild(style, this.createTextNode(css));
return style;
}
createShadowRoot(el: HTMLElement): DocumentFragment { return (<any>el).createShadowRoot(); }
getShadowRoot(el: HTMLElement): DocumentFragment { return (<any>el).shadowRoot; }
getHost(el: HTMLElement): HTMLElement { return (<any>el).host; }
clone(node: Node): Node { return node.cloneNode(true); }
getElementsByClassName(element, name: string): HTMLElement[] {
return element.getElementsByClassName(name);
}
getElementsByTagName(element, name: string): HTMLElement[] {
return element.getElementsByTagName(name);
}
classList(element): any[] { return <any[]>Array.prototype.slice.call(element.classList, 0); }
addClass(element, className: string) { element.classList.add(className); }
removeClass(element, className: string) { element.classList.remove(className); }
hasClass(element, className: string): boolean { return element.classList.contains(className); }
setStyle(element, styleName: string, styleValue: string) {
element.style[styleName] = styleValue;
}
removeStyle(element, stylename: string) { element.style[stylename] = null; }
getStyle(element, stylename: string): string { return element.style[stylename]; }
hasStyle(element, styleName: string, styleValue: string = null): boolean {
var value = this.getStyle(element, styleName) || '';
return styleValue ? value == styleValue : value.length > 0;
}
tagName(element): string { return element.tagName; }
attributeMap(element): Map<string, string> {
var res = new Map<string, string>();
var elAttrs = element.attributes;
for (var i = 0; i < elAttrs.length; i++) {
var attrib = elAttrs[i];
res.set(attrib.name, attrib.value);
}
return res;
}
hasAttribute(element, attribute: string): boolean { return element.hasAttribute(attribute); }
hasAttributeNS(element, ns: string, attribute: string): boolean {
return element.hasAttributeNS(ns, attribute);
}
getAttribute(element, attribute: string): string { return element.getAttribute(attribute); }
getAttributeNS(element, ns: string, name: string): string {
return element.getAttributeNS(ns, name);
}
setAttribute(element, name: string, value: string) { element.setAttribute(name, value); }
setAttributeNS(element, ns: string, name: string, value: string) {
element.setAttributeNS(ns, name, value);
}
removeAttribute(element, attribute: string) { element.removeAttribute(attribute); }
removeAttributeNS(element, ns: string, name: string) { element.removeAttributeNS(ns, name); }
templateAwareRoot(el): any { return this.isTemplateElement(el) ? this.content(el) : el; }
createHtmlDocument(): HTMLDocument {
return document.implementation.createHTMLDocument('fakeTitle');
}
defaultDoc(): HTMLDocument { return document; }
getBoundingClientRect(el): any {
try {
return el.getBoundingClientRect();
} catch (e) {
return {top: 0, bottom: 0, left: 0, right: 0, width: 0, height: 0};
}
}
getTitle(): string { return document.title; }
setTitle(newTitle: string) { document.title = newTitle || ''; }
elementMatches(n, selector: string): boolean {
var matches = false;
if (n instanceof HTMLElement) {
if (n.matches) {
matches = n.matches(selector);
} else if (n.msMatchesSelector) {
matches = n.msMatchesSelector(selector);
} else if (n.webkitMatchesSelector) {
matches = n.webkitMatchesSelector(selector);
}
}
return matches;
}
isTemplateElement(el: any): boolean {
return el instanceof HTMLElement && el.nodeName == "TEMPLATE";
}
isTextNode(node: Node): boolean { return node.nodeType === Node.TEXT_NODE; }
isCommentNode(node: Node): boolean { return node.nodeType === Node.COMMENT_NODE; }
isElementNode(node: Node): boolean { return node.nodeType === Node.ELEMENT_NODE; }
hasShadowRoot(node): boolean { return node instanceof HTMLElement && isPresent(node.shadowRoot); }
isShadowRoot(node): boolean { return node instanceof DocumentFragment; }
importIntoDoc(node: Node): any {
var toImport = node;
if (this.isTemplateElement(node)) {
toImport = this.content(node);
}
return document.importNode(toImport, true);
}
adoptNode(node: Node): any { return document.adoptNode(node); }
getHref(el: Element): string { return (<any>el).href; }
getEventKey(event): string {
var key = event.key;
if (isBlank(key)) {
key = event.keyIdentifier;
// keyIdentifier is defined in the old draft of DOM Level 3 Events implemented by Chrome and
// Safari
// cf
// http://www.w3.org/TR/2007/WD-DOM-Level-3-Events-20071221/events.html#Events-KeyboardEvents-Interfaces
if (isBlank(key)) {
return 'Unidentified';
}
if (key.startsWith('U+')) {
key = String.fromCharCode(parseInt(key.substring(2), 16));
if (event.location === DOM_KEY_LOCATION_NUMPAD && _chromeNumKeyPadMap.hasOwnProperty(key)) {
// There is a bug in Chrome for numeric keypad keys:
// https://code.google.com/p/chromium/issues/detail?id=155654
// 1, 2, 3 ... are reported as A, B, C ...
key = _chromeNumKeyPadMap[key];
}
}
}
if (_keyMap.hasOwnProperty(key)) {
key = _keyMap[key];
}
return key;
}
getGlobalEventTarget(target: string): EventTarget {
if (target == "window") {
return window;
} else if (target == "document") {
return document;
} else if (target == "body") {
return document.body;
}
}
getHistory(): History { return window.history; }
getLocation(): Location { return window.location; }
getBaseHref(): string {
var href = getBaseElementHref();
if (isBlank(href)) {
return null;
}
return relativePath(href);
}
resetBaseElement(): void { baseElement = null; }
getUserAgent(): string { return window.navigator.userAgent; }
setData(element, name: string, value: string) {
this.setAttribute(element, 'data-' + name, value);
}
getData(element, name: string): string { return this.getAttribute(element, 'data-' + name); }
getComputedStyle(element): any { return getComputedStyle(element); }
// TODO(tbosch): move this into a separate environment class once we have it
setGlobalVar(path: string, value: any) { setValueOnPath(global, path, value); }
requestAnimationFrame(callback): number { return window.requestAnimationFrame(callback); }
cancelAnimationFrame(id: number) { window.cancelAnimationFrame(id); }
performanceNow(): number {
// performance.now() is not available in all browsers, see
// http://caniuse.com/#search=performance.now
if (isPresent(window.performance) && isPresent(window.performance.now)) {
return window.performance.now();
} else {
return DateWrapper.toMillis(DateWrapper.now());
}
}
}
var baseElement = null;
function getBaseElementHref(): string {
if (isBlank(baseElement)) {
baseElement = document.querySelector('base');
if (isBlank(baseElement)) {
return null;
}
}
return baseElement.getAttribute('href');
}
// based on urlUtils.js in AngularJS 1
var urlParsingNode = null;
function relativePath(url): string {
if (isBlank(urlParsingNode)) {
urlParsingNode = document.createElement("a");
}
urlParsingNode.setAttribute('href', url);
return (urlParsingNode.pathname.charAt(0) === '/') ? urlParsingNode.pathname :
'/' + urlParsingNode.pathname;
}

View File

@ -0,0 +1,61 @@
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {isPresent, isFunction, Type} from 'angular2/src/facade/lang';
import {DomAdapter} from 'angular2/src/platform/dom/dom_adapter';
import {XHRImpl} from 'angular2/src/platform/browser/xhr_impl';
/**
* Provides DOM operations in any browser environment.
*/
export abstract class GenericBrowserDomAdapter extends DomAdapter {
private _animationPrefix: string = null;
private _transitionEnd: string = null;
constructor() {
super();
try {
var element = this.createElement('div', this.defaultDoc());
if (isPresent(this.getStyle(element, 'animationName'))) {
this._animationPrefix = '';
} else {
var domPrefixes = ['Webkit', 'Moz', 'O', 'ms'];
for (var i = 0; i < domPrefixes.length; i++) {
if (isPresent(this.getStyle(element, domPrefixes[i] + 'AnimationName'))) {
this._animationPrefix = '-' + domPrefixes[i].toLowerCase() + '-';
break;
}
}
}
var transEndEventNames: {[key: string]: string} = {
WebkitTransition: 'webkitTransitionEnd',
MozTransition: 'transitionend',
OTransition: 'oTransitionEnd otransitionend',
transition: 'transitionend'
};
StringMapWrapper.forEach(transEndEventNames, (value: string, key: string) => {
if (isPresent(this.getStyle(element, key))) {
this._transitionEnd = value;
}
});
} catch (e) {
this._animationPrefix = null;
this._transitionEnd = null;
}
}
getXHR(): Type { return XHRImpl; }
getDistributedNodes(el: HTMLElement): Node[] { return (<any>el).getDistributedNodes(); }
resolveAndSetHref(el: HTMLAnchorElement, baseUrl: string, href: string) {
el.href = href == null ? baseUrl : baseUrl + '/../' + href;
}
supportsDOMEvents(): boolean { return true; }
supportsNativeShadowDOM(): boolean {
return isFunction((<any>this.defaultDoc().body).createShadowRoot);
}
getAnimationPrefix(): string {
return isPresent(this._animationPrefix) ? this._animationPrefix : "";
}
getTransitionEnd(): string { return isPresent(this._transitionEnd) ? this._transitionEnd : ""; }
supportsAnimation(): boolean {
return isPresent(this._animationPrefix) && isPresent(this._transitionEnd);
}
}

View File

@ -0,0 +1,57 @@
import {Injectable} from 'angular2/src/core/di/decorators';
import {UrlChangeListener, PlatformLocation} from './platform_location';
import {History, Location} from 'angular2/src/facade/browser';
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
/**
* `PlatformLocation` encapsulates all of the direct calls to platform APIs.
* This class should not be used directly by an application developer. Instead, use
* {@link Location}.
*/
@Injectable()
export class BrowserPlatformLocation extends PlatformLocation {
private _location: Location;
private _history: History;
constructor() {
super();
this._init();
}
// This is moved to its own method so that `MockPlatformLocationStrategy` can overwrite it
/** @internal */
_init() {
this._location = DOM.getLocation();
this._history = DOM.getHistory();
}
/** @internal */
get location(): Location { return this._location; }
getBaseHrefFromDOM(): string { return DOM.getBaseHref(); }
onPopState(fn: UrlChangeListener): void {
DOM.getGlobalEventTarget('window').addEventListener('popstate', fn, false);
}
onHashChange(fn: UrlChangeListener): void {
DOM.getGlobalEventTarget('window').addEventListener('hashchange', fn, false);
}
get pathname(): string { return this._location.pathname; }
get search(): string { return this._location.search; }
get hash(): string { return this._location.hash; }
set pathname(newPath: string) { this._location.pathname = newPath; }
pushState(state: any, title: string, url: string): void {
this._history.pushState(state, title, url);
}
replaceState(state: any, title: string, url: string): void {
this._history.replaceState(state, title, url);
}
forward(): void { this._history.forward(); }
back(): void { this._history.back(); }
}

View File

@ -0,0 +1,34 @@
import {PromiseWrapper} from 'angular2/src/facade/async';
import {DomAdapter} from 'angular2/src/platform/dom/dom_adapter';
import {ElementRef} from 'angular2/src/core/linker/element_ref';
export class Rectangle {
left;
right;
top;
bottom;
height;
width;
constructor(left, top, width, height) {
this.left = left;
this.right = left + width;
this.top = top;
this.bottom = top + height;
this.height = height;
this.width = width;
}
}
export class Ruler {
domAdapter: DomAdapter;
constructor(domAdapter: DomAdapter) { this.domAdapter = domAdapter; }
measure(el: ElementRef): Promise<Rectangle> {
var clntRect = <any>this.domAdapter.getBoundingClientRect(el.nativeElement);
// even if getBoundingClientRect is synchronous we use async API in preparation for further
// changes
return PromiseWrapper.resolve(
new Rectangle(clntRect.left, clntRect.top, clntRect.width, clntRect.height));
}
}

View File

@ -0,0 +1,188 @@
library testability.browser_testability;
import 'package:angular2/core.dart';
import 'package:angular2/platform/common_dom.dart';
import 'dart:html';
import 'dart:js' as js;
// Work around http://dartbug.com/17752, copied from
// https://github.com/angular/angular.dart/blob/master/lib/introspection.dart
// Proxies a Dart function that accepts up to 10 parameters.
js.JsFunction _jsFunction(Function fn) {
const Object X = __varargSentinel;
return new js.JsFunction.withThis((thisArg,
[o1 = X,
o2 = X,
o3 = X,
o4 = X,
o5 = X,
o6 = X,
o7 = X,
o8 = X,
o9 = X,
o10 = X]) {
return __invokeFn(fn, o1, o2, o3, o4, o5, o6, o7, o8, o9, o10);
});
}
const Object __varargSentinel = const Object();
__invokeFn(fn, o1, o2, o3, o4, o5, o6, o7, o8, o9, o10) {
var args = [o1, o2, o3, o4, o5, o6, o7, o8, o9, o10];
while (args.length > 0 && identical(args.last, __varargSentinel)) {
args.removeLast();
}
return _jsify(Function.apply(fn, args));
}
// Helper function to JSify a Dart object. While this is *required* to JSify
// the result of a scope.eval(), other uses are not required and are used to
// work around http://dartbug.com/17752 in a convenient way (that bug affects
// dart2js in checked mode.)
_jsify(var obj) {
if (obj == null || obj is js.JsObject) {
return obj;
}
if (obj is _JsObjectProxyable) {
return obj._toJsObject();
}
if (obj is Function) {
return _jsFunction(obj);
}
if ((obj is Map) || (obj is Iterable)) {
var mappedObj = (obj is Map)
? new Map.fromIterables(obj.keys, obj.values.map(_jsify))
: obj.map(_jsify);
if (obj is List) {
return new js.JsArray.from(mappedObj);
} else {
return new js.JsObject.jsify(mappedObj);
}
}
return obj;
}
abstract class _JsObjectProxyable {
js.JsObject _toJsObject();
}
class PublicTestability implements _JsObjectProxyable {
Testability _testability;
PublicTestability(Testability testability) {
this._testability = testability;
}
bool isStable() {
return this._testability.isStable();
}
whenStable(Function callback) {
return this._testability.whenStable(callback);
}
findBindings(Element elem, String binding, bool exactMatch) {
return this._testability.findBindings(elem, binding, exactMatch);
}
js.JsObject _toJsObject() {
return _jsify({
'findBindings': (bindingString, [exactMatch, allowNonElementNodes]) =>
findBindings(bindingString, exactMatch, allowNonElementNodes),
'isStable': () => isStable(),
'whenStable': (callback) => whenStable((didWork) => callback.apply([didWork]))
})..['_dart_'] = this;
}
}
class BrowserGetTestability implements GetTestability {
const BrowserGetTestability();
static init() {
setTestabilityGetter(const BrowserGetTestability());
}
void addToWindow(TestabilityRegistry registry) {
var jsRegistry = js.context['ngTestabilityRegistries'];
if (jsRegistry == null) {
js.context['ngTestabilityRegistries'] = jsRegistry = new js.JsArray();
js.context['getAngularTestability'] =
_jsify((Element elem, [bool findInAncestors = true]) {
var registry = js.context['ngTestabilityRegistries'];
for (int i = 0; i < registry.length; i++) {
var result = registry[i]
.callMethod('getAngularTestability', [elem, findInAncestors]);
if (result != null) return result;
}
throw 'Could not find testability for element.';
});
var getAllAngularTestabilities = () {
var registry = js.context['ngTestabilityRegistries'];
var result = [];
for (int i = 0; i < registry.length; i++) {
var testabilities =
registry[i].callMethod('getAllAngularTestabilities');
if (testabilities != null) result.addAll(testabilities);
}
return _jsify(result);
};
js.context['getAllAngularTestabilities'] =
_jsify(getAllAngularTestabilities);
var whenAllStable = _jsify((callback) {
var testabilities = getAllAngularTestabilities();
var count = testabilities.length;
var didWork = false;
var decrement = _jsify((bool didWork_) {
didWork = didWork || didWork_;
count--;
if (count == 0) {
callback.apply([didWork]);
}
});
testabilities.forEach((testability) {
testability.callMethod('whenStable', [decrement]);
});
});
if (js.context['frameworkStabilizers'] == null) {
js.context['frameworkStabilizers'] = new js.JsArray();
}
js.context['frameworkStabilizers'].add(whenAllStable);
}
jsRegistry.add(this._createRegistry(registry));
}
findTestabilityInTree(TestabilityRegistry registry, dynamic elem, bool findInAncestors) {
if (elem == null) {
return null;
}
var t = registry.getTestability(elem);
if (t != null) {
return t;
} else if (!findInAncestors) {
return null;
}
if (DOM.isShadowRoot(elem)) {
return this.findTestabilityInTree(registry, DOM.getHost(elem), true);
}
return this.findTestabilityInTree(registry, DOM.parentElement(elem), true);
}
js.JsObject _createRegistry(TestabilityRegistry registry) {
var object = new js.JsObject(js.context['Object']);
object['getAngularTestability'] =
_jsify((Element elem, bool findInAncestors) {
var testability = registry.findTestabilityInTree(elem, findInAncestors);
return testability == null
? null
: _jsify(new PublicTestability(testability));
});
object['getAllAngularTestabilities'] = _jsify(() {
var publicTestabilities = registry
.getAllTestabilities()
.map((testability) => new PublicTestability(testability));
return _jsify(publicTestabilities);
});
return object;
}
}

View File

@ -0,0 +1,90 @@
import {Map, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
import {global, isPresent} from 'angular2/src/facade/lang';
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
import {PromiseWrapper, ObservableWrapper} from 'angular2/src/facade/async';
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
import {
Injectable,
TestabilityRegistry,
Testability,
GetTestability,
setTestabilityGetter
} from 'angular2/core';
class PublicTestability {
/** @internal */
_testability: Testability;
constructor(testability: Testability) { this._testability = testability; }
isStable(): boolean { return this._testability.isStable(); }
whenStable(callback: Function) { this._testability.whenStable(callback); }
findBindings(using: any, provider: string, exactMatch: boolean): any[] {
return this.findProviders(using, provider, exactMatch);
}
findProviders(using: any, provider: string, exactMatch: boolean): any[] {
return this._testability.findBindings(using, provider, exactMatch);
}
}
export class BrowserGetTestability implements GetTestability {
static init() { setTestabilityGetter(new BrowserGetTestability()); }
addToWindow(registry: TestabilityRegistry): void {
global.getAngularTestability = (elem: any, findInAncestors: boolean = true) => {
var testability = registry.findTestabilityInTree(elem, findInAncestors);
if (testability == null) {
throw new Error('Could not find testability for element.');
}
return new PublicTestability(testability);
};
global.getAllAngularTestabilities = () => {
var testabilities = registry.getAllTestabilities();
return testabilities.map((testability) => { return new PublicTestability(testability); });
};
global.getAllAngularRootElements = () => registry.getAllRootElements();
var whenAllStable = (callback) => {
var testabilities = global.getAllAngularTestabilities();
var count = testabilities.length;
var didWork = false;
var decrement = function(didWork_) {
didWork = didWork || didWork_;
count--;
if (count == 0) {
callback(didWork);
}
};
testabilities.forEach(function(testability) { testability.whenStable(decrement); });
};
if (!global.frameworkStabilizers) {
global.frameworkStabilizers = ListWrapper.createGrowableSize(0);
}
global.frameworkStabilizers.push(whenAllStable);
}
findTestabilityInTree(registry: TestabilityRegistry, elem: any,
findInAncestors: boolean): Testability {
if (elem == null) {
return null;
}
var t = registry.getTestability(elem);
if (isPresent(t)) {
return t;
} else if (!findInAncestors) {
return null;
}
if (DOM.isShadowRoot(elem)) {
return this.findTestabilityInTree(registry, DOM.getHost(elem), true);
}
return this.findTestabilityInTree(registry, DOM.parentElement(elem), true);
}
}

View File

@ -0,0 +1,23 @@
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
/**
* A service that can be used to get and set the title of a current HTML document.
*
* Since an Angular 2 application can't be bootstrapped on the entire HTML document (`<html>` tag)
* it is not possible to bind to the `text` property of the `HTMLTitleElement` elements
* (representing the `<title>` tag). Instead, this service can be used to set and get the current
* title value.
*/
export class Title {
/**
* Get the title of the current HTML document.
* @returns {string}
*/
getTitle(): string { return DOM.getTitle(); }
/**
* Set the title of the current HTML document.
* @param newTitle
*/
setTitle(newTitle: string) { DOM.setTitle(newTitle); }
}

View File

@ -0,0 +1,74 @@
import {ApplicationRef} from 'angular2/src/core/application_ref';
import {ComponentRef} from 'angular2/src/core/linker/component_factory';
import {isPresent, NumberWrapper} from 'angular2/src/facade/lang';
import {window} from 'angular2/src/facade/browser';
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
export class ChangeDetectionPerfRecord {
constructor(public msPerTick: number, public numTicks: number) {}
}
/**
* Entry point for all Angular debug tools. This object corresponds to the `ng`
* global variable accessible in the dev console.
*/
export class AngularTools {
profiler: AngularProfiler;
constructor(ref: ComponentRef<any>) { this.profiler = new AngularProfiler(ref); }
}
/**
* Entry point for all Angular profiling-related debug tools. This object
* corresponds to the `ng.profiler` in the dev console.
*/
export class AngularProfiler {
appRef: ApplicationRef;
constructor(ref: ComponentRef<any>) { this.appRef = ref.injector.get(ApplicationRef); }
/**
* Exercises change detection in a loop and then prints the average amount of
* time in milliseconds how long a single round of change detection takes for
* the current state of the UI. It runs a minimum of 5 rounds for a minimum
* of 500 milliseconds.
*
* Optionally, a user may pass a `config` parameter containing a map of
* options. Supported options are:
*
* `record` (boolean) - causes the profiler to record a CPU profile while
* it exercises the change detector. Example:
*
* ```
* ng.profiler.timeChangeDetection({record: true})
* ```
*/
timeChangeDetection(config: any): ChangeDetectionPerfRecord {
var record = isPresent(config) && config['record'];
var profileName = 'Change Detection';
// Profiler is not available in Android browsers, nor in IE 9 without dev tools opened
var isProfilerAvailable = isPresent(window.console.profile);
if (record && isProfilerAvailable) {
window.console.profile(profileName);
}
var start = DOM.performanceNow();
var numTicks = 0;
while (numTicks < 5 || (DOM.performanceNow() - start) < 500) {
this.appRef.tick();
numTicks++;
}
var end = DOM.performanceNow();
if (record && isProfilerAvailable) {
// need to cast to <any> because type checker thinks there's no argument
// while in fact there is:
//
// https://developer.mozilla.org/en-US/docs/Web/API/Console/profileEnd
(<any>window.console.profileEnd)(profileName);
}
var msPerTick = (end - start) / numTicks;
window.console.log(`ran ${numTicks} change detection cycles`);
window.console.log(`${NumberWrapper.toFixed(msPerTick, 2)} ms per check`);
return new ChangeDetectionPerfRecord(msPerTick, numTicks);
}
}

View File

@ -0,0 +1,35 @@
library angular2.src.tools.tools;
import 'dart:js';
import 'package:angular2/src/core/linker/component_factory.dart'
show ComponentRef;
import 'common_tools.dart' show AngularTools;
/**
* Enabled Angular 2 debug tools that are accessible via your browser's
* developer console.
*
* Usage:
*
* 1. Open developer console (e.g. in Chrome Ctrl + Shift + j)
* 1. Type `ng.` (usually the console will show auto-complete suggestion)
* 1. Try the change detection profiler `ng.profiler.timeChangeDetection()`
* then hit Enter.
*/
void enableDebugTools(ComponentRef<dynamic> ref) {
final tools = new AngularTools(ref);
context['ng'] = new JsObject.jsify({
'profiler': {
'timeChangeDetection': ([config]) {
tools.profiler.timeChangeDetection(config);
}
}
});
}
/**
* Disables Angular 2 tools.
*/
void disableDebugTools() {
context.deleteProperty('ng');
}

View File

@ -0,0 +1,27 @@
import {global} from 'angular2/src/facade/lang';
import {ComponentRef} from 'angular2/src/core/linker/component_factory';
import {AngularTools} from './common_tools';
var context = <any>global;
/**
* Enabled Angular 2 debug tools that are accessible via your browser's
* developer console.
*
* Usage:
*
* 1. Open developer console (e.g. in Chrome Ctrl + Shift + j)
* 1. Type `ng.` (usually the console will show auto-complete suggestion)
* 1. Try the change detection profiler `ng.profiler.timeChangeDetection()`
* then hit Enter.
*/
export function enableDebugTools(ref: ComponentRef<any>): void {
context.ng = new AngularTools(ref);
}
/**
* Disables Angular 2 tools.
*/
export function disableDebugTools(): void {
delete context.ng;
}

View File

@ -0,0 +1,108 @@
import {IS_DART} from 'angular2/src/facade/lang';
import {provide, Injector, OpaqueToken} from 'angular2/src/core/di';
import {XHR} from 'angular2/src/compiler/xhr';
import {
PLATFORM_INITIALIZER,
PLATFORM_DIRECTIVES,
PLATFORM_PIPES,
ComponentRef,
ExceptionHandler,
Reflector,
RootRenderer,
reflector,
APPLICATION_COMMON_PROVIDERS,
PLATFORM_COMMON_PROVIDERS
} from "angular2/core";
import {COMMON_DIRECTIVES, COMMON_PIPES, FORM_PROVIDERS} from "angular2/common";
import {Testability} from 'angular2/src/core/testability/testability';
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
import {DomEventsPlugin} from 'angular2/src/platform/dom/events/dom_events';
import {KeyEventsPlugin} from 'angular2/src/platform/dom/events/key_events';
import {DOCUMENT} from 'angular2/src/platform/dom/dom_tokens';
import {DomRootRenderer, DomRootRenderer_} from 'angular2/src/platform/dom/dom_renderer';
import {DomSharedStylesHost, SharedStylesHost} from 'angular2/src/platform/dom/shared_styles_host';
import {BrowserDetails} from "angular2/src/animate/browser_details";
import {AnimationBuilder} from "angular2/src/animate/animation_builder";
import {BrowserDomAdapter} from './browser/browser_adapter';
import {BrowserGetTestability} from 'angular2/src/platform/browser/testability';
import {CachedXHR} from 'angular2/src/platform/browser/xhr_cache';
import {wtfInit} from 'angular2/src/core/profile/wtf_init';
import {EventManager, EVENT_MANAGER_PLUGINS} from "angular2/src/platform/dom/events/event_manager";
import {
HAMMER_GESTURE_CONFIG,
HammerGestureConfig,
HammerGesturesPlugin
} from 'angular2/src/platform/dom/events/hammer_gestures';
import {ELEMENT_PROBE_PROVIDERS} from 'angular2/platform/common_dom';
export {DOCUMENT} from 'angular2/src/platform/dom/dom_tokens';
export {Title} from 'angular2/src/platform/browser/title';
export {
ELEMENT_PROBE_PROVIDERS,
ELEMENT_PROBE_PROVIDERS_PROD_MODE,
inspectNativeElement,
By
} from 'angular2/platform/common_dom';
export {BrowserDomAdapter} from './browser/browser_adapter';
export {enableDebugTools, disableDebugTools} from 'angular2/src/platform/browser/tools/tools';
export {HAMMER_GESTURE_CONFIG, HammerGestureConfig} from './dom/events/hammer_gestures';
export const BROWSER_PLATFORM_MARKER =
/*@ts2dart_const*/ new OpaqueToken('BrowserPlatformMarker');
/**
* A set of providers to initialize the Angular platform in a web browser.
*
* Used automatically by `bootstrap`, or can be passed to {@link platform}.
*/
export const BROWSER_PROVIDERS: Array<any /*Type | Provider | any[]*/> = /*@ts2dart_const*/[
/*@ts2dart_Provider*/ {provide: BROWSER_PLATFORM_MARKER, useValue: true},
PLATFORM_COMMON_PROVIDERS,
/*@ts2dart_Provider*/ {provide: PLATFORM_INITIALIZER, useValue: initDomAdapter, multi: true},
];
function _exceptionHandler(): ExceptionHandler {
// !IS_DART is required because we must rethrow exceptions in JS,
// but must not rethrow exceptions in Dart
return new ExceptionHandler(DOM, !IS_DART);
}
function _document(): any {
return DOM.defaultDoc();
}
/**
* A set of providers to initialize an Angular application in a web browser.
*
* Used automatically by `bootstrap`, or can be passed to {@link PlatformRef.application}.
*/
export const BROWSER_APP_COMMON_PROVIDERS: Array<any /*Type | Provider | any[]*/> =
/*@ts2dart_const*/[
APPLICATION_COMMON_PROVIDERS,
FORM_PROVIDERS,
/* @ts2dart_Provider */ {provide: PLATFORM_PIPES, useValue: COMMON_PIPES, multi: true},
/* @ts2dart_Provider */ {provide: PLATFORM_DIRECTIVES, useValue: COMMON_DIRECTIVES, multi: true},
/* @ts2dart_Provider */ {provide: ExceptionHandler, useFactory: _exceptionHandler, deps: []},
/* @ts2dart_Provider */ {provide: DOCUMENT, useFactory: _document, deps: []},
/* @ts2dart_Provider */ {provide: EVENT_MANAGER_PLUGINS, useClass: DomEventsPlugin, multi: true},
/* @ts2dart_Provider */ {provide: EVENT_MANAGER_PLUGINS, useClass: KeyEventsPlugin, multi: true},
/* @ts2dart_Provider */ {provide: EVENT_MANAGER_PLUGINS, useClass: HammerGesturesPlugin, multi: true},
/* @ts2dart_Provider */ {provide: HAMMER_GESTURE_CONFIG, useClass: HammerGestureConfig},
/* @ts2dart_Provider */ {provide: DomRootRenderer, useClass: DomRootRenderer_},
/* @ts2dart_Provider */ {provide: RootRenderer, useExisting: DomRootRenderer},
/* @ts2dart_Provider */ {provide: SharedStylesHost, useExisting: DomSharedStylesHost},
DomSharedStylesHost,
Testability,
BrowserDetails,
AnimationBuilder,
EventManager,
ELEMENT_PROBE_PROVIDERS
];
export const CACHED_TEMPLATE_PROVIDER: Array<any /*Type | Provider | any[]*/> =
/*@ts2dart_const*/[/*@ts2dart_Provider*/ {provide: XHR, useClass: CachedXHR}];
export function initDomAdapter() {
BrowserDomAdapter.makeCurrent();
wtfInit();
BrowserGetTestability.init();
}

View File

@ -0,0 +1,44 @@
import {Type, isPresent, isBlank} from 'angular2/src/facade/lang';
import {Predicate} from 'angular2/src/facade/collection';
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
import {DebugElement} from 'angular2/core';
/**
* Predicates for use with {@link DebugElement}'s query functions.
*/
export class By {
/**
* Match all elements.
*
* ## Example
*
* {@example platform/dom/debug/ts/by/by.ts region='by_all'}
*/
static all(): Predicate<DebugElement> { return (debugElement) => true; }
/**
* Match elements by the given CSS selector.
*
* ## Example
*
* {@example platform/dom/debug/ts/by/by.ts region='by_css'}
*/
static css(selector: string): Predicate<DebugElement> {
return (debugElement) => {
return isPresent(debugElement.nativeElement) ?
DOM.elementMatches(debugElement.nativeElement, selector) :
false;
};
}
/**
* Match elements that have the given directive present.
*
* ## Example
*
* {@example platform/dom/debug/ts/by/by.ts region='by_directive'}
*/
static directive(type: Type): Predicate<DebugElement> {
return (debugElement) => { return debugElement.providerTokens.indexOf(type) !== -1; };
}
}

View File

@ -0,0 +1,52 @@
import {assertionsEnabled} from 'angular2/src/facade/lang';
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
import {DebugNode, getDebugNode} from 'angular2/src/core/debug/debug_node';
import {DomRootRenderer} from 'angular2/src/platform/dom/dom_renderer';
import {RootRenderer, NgZone, ApplicationRef} from 'angular2/core';
import {DebugDomRootRenderer} from 'angular2/src/core/debug/debug_renderer';
const CORE_TOKENS = /*@ts2dart_const*/ {'ApplicationRef': ApplicationRef, 'NgZone': NgZone};
const INSPECT_GLOBAL_NAME = 'ng.probe';
const CORE_TOKENS_GLOBAL_NAME = 'ng.coreTokens';
/**
* Returns a {@link DebugElement} for the given native DOM element, or
* null if the given native element does not have an Angular view associated
* with it.
*/
export function inspectNativeElement(element): DebugNode {
return getDebugNode(element);
}
function _createConditionalRootRenderer(rootRenderer) {
if (assertionsEnabled()) {
return _createRootRenderer(rootRenderer);
}
return rootRenderer;
}
function _createRootRenderer(rootRenderer) {
DOM.setGlobalVar(INSPECT_GLOBAL_NAME, inspectNativeElement);
DOM.setGlobalVar(CORE_TOKENS_GLOBAL_NAME, CORE_TOKENS);
return new DebugDomRootRenderer(rootRenderer);
}
/**
* Providers which support debugging Angular applications (e.g. via `ng.probe`).
*/
export const ELEMENT_PROBE_PROVIDERS: any[] = /*@ts2dart_const*/[
/*@ts2dart_Provider*/ {
provide: RootRenderer,
useFactory: _createConditionalRootRenderer,
deps: [DomRootRenderer]
}
];
export const ELEMENT_PROBE_PROVIDERS_PROD_MODE: any[] = /*@ts2dart_const*/[
/*@ts2dart_Provider*/ {
provide: RootRenderer,
useFactory: _createRootRenderer,
deps: [DomRootRenderer]
}
];

View File

@ -0,0 +1,143 @@
import {isBlank, Type} from 'angular2/src/facade/lang';
export var DOM: DomAdapter = null;
export function setRootDomAdapter(adapter: DomAdapter) {
if (isBlank(DOM)) {
DOM = adapter;
}
}
/* tslint:disable:requireParameterType */
/**
* Provides DOM operations in an environment-agnostic way.
*/
export abstract class DomAdapter {
abstract hasProperty(element, name: string): boolean;
abstract setProperty(el: Element, name: string, value: any);
abstract getProperty(el: Element, name: string): any;
abstract invoke(el: Element, methodName: string, args: any[]): any;
abstract logError(error);
abstract log(error);
abstract logGroup(error);
abstract logGroupEnd();
/** @deprecated */
abstract getXHR(): Type;
/**
* Maps attribute names to their corresponding property names for cases
* where attribute name doesn't match property name.
*/
get attrToPropMap(): {[key: string]: string} { return this._attrToPropMap; };
set attrToPropMap(value: {[key: string]: string}) { this._attrToPropMap = value; };
/** @internal */
_attrToPropMap: {[key: string]: string};
abstract parse(templateHtml: string);
abstract query(selector: string): any;
abstract querySelector(el, selector: string): HTMLElement;
abstract querySelectorAll(el, selector: string): any[];
abstract on(el, evt, listener);
abstract onAndCancel(el, evt, listener): Function;
abstract dispatchEvent(el, evt);
abstract createMouseEvent(eventType): any;
abstract createEvent(eventType: string): any;
abstract preventDefault(evt);
abstract isPrevented(evt): boolean;
abstract getInnerHTML(el): string;
abstract getOuterHTML(el): string;
abstract nodeName(node): string;
abstract nodeValue(node): string;
abstract type(node): string;
abstract content(node): any;
abstract firstChild(el): Node;
abstract nextSibling(el): Node;
abstract parentElement(el): Node;
abstract childNodes(el): Node[];
abstract childNodesAsList(el): Node[];
abstract clearNodes(el);
abstract appendChild(el, node);
abstract removeChild(el, node);
abstract replaceChild(el, newNode, oldNode);
abstract remove(el): Node;
abstract insertBefore(el, node);
abstract insertAllBefore(el, nodes);
abstract insertAfter(el, node);
abstract setInnerHTML(el, value);
abstract getText(el): string;
abstract setText(el, value: string);
abstract getValue(el): string;
abstract setValue(el, value: string);
abstract getChecked(el): boolean;
abstract setChecked(el, value: boolean);
abstract createComment(text: string): any;
abstract createTemplate(html): HTMLElement;
abstract createElement(tagName, doc?): HTMLElement;
abstract createElementNS(ns: string, tagName: string, doc?): Element;
abstract createTextNode(text: string, doc?): Text;
abstract createScriptTag(attrName: string, attrValue: string, doc?): HTMLElement;
abstract createStyleElement(css: string, doc?): HTMLStyleElement;
abstract createShadowRoot(el): any;
abstract getShadowRoot(el): any;
abstract getHost(el): any;
abstract getDistributedNodes(el): Node[];
abstract clone /*<T extends Node>*/ (node: Node /*T*/): Node /*T*/;
abstract getElementsByClassName(element, name: string): HTMLElement[];
abstract getElementsByTagName(element, name: string): HTMLElement[];
abstract classList(element): any[];
abstract addClass(element, className: string);
abstract removeClass(element, className: string);
abstract hasClass(element, className: string): boolean;
abstract setStyle(element, styleName: string, styleValue: string);
abstract removeStyle(element, styleName: string);
abstract getStyle(element, styleName: string): string;
abstract hasStyle(element, styleName: string, styleValue?: string): boolean;
abstract tagName(element): string;
abstract attributeMap(element): Map<string, string>;
abstract hasAttribute(element, attribute: string): boolean;
abstract hasAttributeNS(element, ns: string, attribute: string): boolean;
abstract getAttribute(element, attribute: string): string;
abstract getAttributeNS(element, ns: string, attribute: string): string;
abstract setAttribute(element, name: string, value: string);
abstract setAttributeNS(element, ns: string, name: string, value: string);
abstract removeAttribute(element, attribute: string);
abstract removeAttributeNS(element, ns: string, attribute: string);
abstract templateAwareRoot(el);
abstract createHtmlDocument(): HTMLDocument;
abstract defaultDoc(): HTMLDocument;
abstract getBoundingClientRect(el);
abstract getTitle(): string;
abstract setTitle(newTitle: string);
abstract elementMatches(n, selector: string): boolean;
abstract isTemplateElement(el: any): boolean;
abstract isTextNode(node): boolean;
abstract isCommentNode(node): boolean;
abstract isElementNode(node): boolean;
abstract hasShadowRoot(node): boolean;
abstract isShadowRoot(node): boolean;
abstract importIntoDoc /*<T extends Node>*/ (node: Node /*T*/): Node /*T*/;
abstract adoptNode /*<T extends Node>*/ (node: Node /*T*/): Node /*T*/;
abstract getHref(element): string;
abstract getEventKey(event): string;
abstract resolveAndSetHref(element, baseUrl: string, href: string);
abstract supportsDOMEvents(): boolean;
abstract supportsNativeShadowDOM(): boolean;
abstract getGlobalEventTarget(target: string): any;
abstract getHistory(): History;
abstract getLocation(): Location;
abstract getBaseHref(): string;
abstract resetBaseElement(): void;
abstract getUserAgent(): string;
abstract setData(element, name: string, value: string);
abstract getComputedStyle(element): any;
abstract getData(element, name: string): string;
abstract setGlobalVar(name: string, value: any);
abstract requestAnimationFrame(callback): number;
abstract cancelAnimationFrame(id);
abstract performanceNow(): number;
abstract getAnimationPrefix(): string;
abstract getTransitionEnd(): string;
abstract supportsAnimation(): boolean;
}

View File

@ -0,0 +1,339 @@
import {Inject, Injectable, OpaqueToken} from 'angular2/src/core/di';
import {AnimationBuilder} from 'angular2/src/animate/animation_builder';
import {
isPresent,
isBlank,
Json,
RegExpWrapper,
stringify,
StringWrapper,
isArray,
isString
} from 'angular2/src/facade/lang';
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
import {DomSharedStylesHost} from './shared_styles_host';
import {
Renderer,
RootRenderer,
RenderComponentType,
RenderDebugInfo
} from 'angular2/src/core/render/api';
import {EventManager} from './events/event_manager';
import {DOCUMENT} from './dom_tokens';
import {ViewEncapsulation} from 'angular2/src/core/metadata';
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
import {camelCaseToDashCase} from './util';
const NAMESPACE_URIS =
/*@ts2dart_const*/
{'xlink': 'http://www.w3.org/1999/xlink', 'svg': 'http://www.w3.org/2000/svg'};
const TEMPLATE_COMMENT_TEXT = 'template bindings={}';
var TEMPLATE_BINDINGS_EXP = /^template bindings=(.*)$/g;
export abstract class DomRootRenderer implements RootRenderer {
private _registeredComponents: Map<string, DomRenderer> = new Map<string, DomRenderer>();
constructor(public document: any, public eventManager: EventManager,
public sharedStylesHost: DomSharedStylesHost, public animate: AnimationBuilder) {}
renderComponent(componentProto: RenderComponentType): Renderer {
var renderer = this._registeredComponents.get(componentProto.id);
if (isBlank(renderer)) {
renderer = new DomRenderer(this, componentProto);
this._registeredComponents.set(componentProto.id, renderer);
}
return renderer;
}
}
@Injectable()
export class DomRootRenderer_ extends DomRootRenderer {
constructor(@Inject(DOCUMENT) _document: any, _eventManager: EventManager,
sharedStylesHost: DomSharedStylesHost, animate: AnimationBuilder) {
super(_document, _eventManager, sharedStylesHost, animate);
}
}
export class DomRenderer implements Renderer {
private _contentAttr: string;
private _hostAttr: string;
private _styles: string[];
constructor(private _rootRenderer: DomRootRenderer, private componentProto: RenderComponentType) {
this._styles = _flattenStyles(componentProto.id, componentProto.styles, []);
if (componentProto.encapsulation !== ViewEncapsulation.Native) {
this._rootRenderer.sharedStylesHost.addStyles(this._styles);
}
if (this.componentProto.encapsulation === ViewEncapsulation.Emulated) {
this._contentAttr = _shimContentAttribute(componentProto.id);
this._hostAttr = _shimHostAttribute(componentProto.id);
} else {
this._contentAttr = null;
this._hostAttr = null;
}
}
selectRootElement(selectorOrNode: string | any, debugInfo: RenderDebugInfo): Element {
var el;
if (isString(selectorOrNode)) {
el = DOM.querySelector(this._rootRenderer.document, selectorOrNode);
if (isBlank(el)) {
throw new BaseException(`The selector "${selectorOrNode}" did not match any elements`);
}
} else {
el = selectorOrNode;
}
DOM.clearNodes(el);
return el;
}
createElement(parent: Element, name: string, debugInfo: RenderDebugInfo): Node {
var nsAndName = splitNamespace(name);
var el = isPresent(nsAndName[0]) ?
DOM.createElementNS(NAMESPACE_URIS[nsAndName[0]], nsAndName[1]) :
DOM.createElement(nsAndName[1]);
if (isPresent(this._contentAttr)) {
DOM.setAttribute(el, this._contentAttr, '');
}
if (isPresent(parent)) {
DOM.appendChild(parent, el);
}
return el;
}
createViewRoot(hostElement: any): any {
var nodesParent;
if (this.componentProto.encapsulation === ViewEncapsulation.Native) {
nodesParent = DOM.createShadowRoot(hostElement);
this._rootRenderer.sharedStylesHost.addHost(nodesParent);
for (var i = 0; i < this._styles.length; i++) {
DOM.appendChild(nodesParent, DOM.createStyleElement(this._styles[i]));
}
} else {
if (isPresent(this._hostAttr)) {
DOM.setAttribute(hostElement, this._hostAttr, '');
}
nodesParent = hostElement;
}
return nodesParent;
}
createTemplateAnchor(parentElement: any, debugInfo: RenderDebugInfo): any {
var comment = DOM.createComment(TEMPLATE_COMMENT_TEXT);
if (isPresent(parentElement)) {
DOM.appendChild(parentElement, comment);
}
return comment;
}
createText(parentElement: any, value: string, debugInfo: RenderDebugInfo): any {
var node = DOM.createTextNode(value);
if (isPresent(parentElement)) {
DOM.appendChild(parentElement, node);
}
return node;
}
projectNodes(parentElement: any, nodes: any[]) {
if (isBlank(parentElement)) return;
appendNodes(parentElement, nodes);
}
attachViewAfter(node: any, viewRootNodes: any[]) {
moveNodesAfterSibling(node, viewRootNodes);
for (let i = 0; i < viewRootNodes.length; i++) this.animateNodeEnter(viewRootNodes[i]);
}
detachView(viewRootNodes: any[]) {
for (var i = 0; i < viewRootNodes.length; i++) {
var node = viewRootNodes[i];
DOM.remove(node);
this.animateNodeLeave(node);
}
}
destroyView(hostElement: any, viewAllNodes: any[]) {
if (this.componentProto.encapsulation === ViewEncapsulation.Native && isPresent(hostElement)) {
this._rootRenderer.sharedStylesHost.removeHost(DOM.getShadowRoot(hostElement));
}
}
listen(renderElement: any, name: string, callback: Function): Function {
return this._rootRenderer.eventManager.addEventListener(renderElement, name,
decoratePreventDefault(callback));
}
listenGlobal(target: string, name: string, callback: Function): Function {
return this._rootRenderer.eventManager.addGlobalEventListener(target, name,
decoratePreventDefault(callback));
}
setElementProperty(renderElement: any, propertyName: string, propertyValue: any): void {
DOM.setProperty(renderElement, propertyName, propertyValue);
}
setElementAttribute(renderElement: any, attributeName: string, attributeValue: string): void {
var attrNs;
var nsAndName = splitNamespace(attributeName);
if (isPresent(nsAndName[0])) {
attributeName = nsAndName[0] + ':' + nsAndName[1];
attrNs = NAMESPACE_URIS[nsAndName[0]];
}
if (isPresent(attributeValue)) {
if (isPresent(attrNs)) {
DOM.setAttributeNS(renderElement, attrNs, attributeName, attributeValue);
} else {
DOM.setAttribute(renderElement, attributeName, attributeValue);
}
} else {
if (isPresent(attrNs)) {
DOM.removeAttributeNS(renderElement, attrNs, nsAndName[1]);
} else {
DOM.removeAttribute(renderElement, attributeName);
}
}
}
setBindingDebugInfo(renderElement: any, propertyName: string, propertyValue: string): void {
var dashCasedPropertyName = camelCaseToDashCase(propertyName);
if (DOM.isCommentNode(renderElement)) {
var existingBindings = RegExpWrapper.firstMatch(
TEMPLATE_BINDINGS_EXP, StringWrapper.replaceAll(DOM.getText(renderElement), /\n/g, ''));
var parsedBindings = Json.parse(existingBindings[1]);
parsedBindings[dashCasedPropertyName] = propertyValue;
DOM.setText(renderElement, StringWrapper.replace(TEMPLATE_COMMENT_TEXT, '{}',
Json.stringify(parsedBindings)));
} else {
this.setElementAttribute(renderElement, propertyName, propertyValue);
}
}
setElementClass(renderElement: any, className: string, isAdd: boolean): void {
if (isAdd) {
DOM.addClass(renderElement, className);
} else {
DOM.removeClass(renderElement, className);
}
}
setElementStyle(renderElement: any, styleName: string, styleValue: string): void {
if (isPresent(styleValue)) {
DOM.setStyle(renderElement, styleName, stringify(styleValue));
} else {
DOM.removeStyle(renderElement, styleName);
}
}
invokeElementMethod(renderElement: any, methodName: string, args: any[]): void {
DOM.invoke(renderElement, methodName, args);
}
setText(renderNode: any, text: string): void { DOM.setText(renderNode, text); }
/**
* Performs animations if necessary
* @param node
*/
animateNodeEnter(node: Node) {
if (DOM.isElementNode(node) && DOM.hasClass(node, 'ng-animate')) {
DOM.addClass(node, 'ng-enter');
this._rootRenderer.animate.css()
.addAnimationClass('ng-enter-active')
.start(<HTMLElement>node)
.onComplete(() => { DOM.removeClass(node, 'ng-enter'); });
}
}
/**
* If animations are necessary, performs animations then removes the element; otherwise, it just
* removes the element.
* @param node
*/
animateNodeLeave(node: Node) {
if (DOM.isElementNode(node) && DOM.hasClass(node, 'ng-animate')) {
DOM.addClass(node, 'ng-leave');
this._rootRenderer.animate.css()
.addAnimationClass('ng-leave-active')
.start(<HTMLElement>node)
.onComplete(() => {
DOM.removeClass(node, 'ng-leave');
DOM.remove(node);
});
} else {
DOM.remove(node);
}
}
}
function moveNodesAfterSibling(sibling, nodes) {
var parent = DOM.parentElement(sibling);
if (nodes.length > 0 && isPresent(parent)) {
var nextSibling = DOM.nextSibling(sibling);
if (isPresent(nextSibling)) {
for (var i = 0; i < nodes.length; i++) {
DOM.insertBefore(nextSibling, nodes[i]);
}
} else {
for (var i = 0; i < nodes.length; i++) {
DOM.appendChild(parent, nodes[i]);
}
}
}
}
function appendNodes(parent, nodes) {
for (var i = 0; i < nodes.length; i++) {
DOM.appendChild(parent, nodes[i]);
}
}
function decoratePreventDefault(eventHandler: Function): Function {
return (event) => {
var allowDefaultBehavior = eventHandler(event);
if (allowDefaultBehavior === false) {
// TODO(tbosch): move preventDefault into event plugins...
DOM.preventDefault(event);
}
};
}
var COMPONENT_REGEX = /%COMP%/g;
export const COMPONENT_VARIABLE = '%COMP%';
export const HOST_ATTR = /*@ts2dart_const*/ `_nghost-${COMPONENT_VARIABLE}`;
export const CONTENT_ATTR = /*@ts2dart_const*/ `_ngcontent-${COMPONENT_VARIABLE}`;
function _shimContentAttribute(componentShortId: string): string {
return StringWrapper.replaceAll(CONTENT_ATTR, COMPONENT_REGEX, componentShortId);
}
function _shimHostAttribute(componentShortId: string): string {
return StringWrapper.replaceAll(HOST_ATTR, COMPONENT_REGEX, componentShortId);
}
function _flattenStyles(compId: string, styles: Array<any | any[]>, target: string[]): string[] {
for (var i = 0; i < styles.length; i++) {
var style = styles[i];
if (isArray(style)) {
_flattenStyles(compId, style, target);
} else {
style = StringWrapper.replaceAll(style, COMPONENT_REGEX, compId);
target.push(style);
}
}
return target;
}
var NS_PREFIX_RE = /^@([^:]+):(.+)/g;
function splitNamespace(name: string): string[] {
if (name[0] != '@') {
return [null, name];
}
let match = RegExpWrapper.firstMatch(NS_PREFIX_RE, name);
return [match[1], match[2]];
}

View File

@ -0,0 +1,9 @@
import {OpaqueToken} from 'angular2/src/core/di';
/**
* 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 = /*@ts2dart_const*/ new OpaqueToken('DocumentToken');

View File

@ -0,0 +1,25 @@
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
import {Injectable} from 'angular2/core';
import {EventManagerPlugin, EventManager} from './event_manager';
@Injectable()
export class DomEventsPlugin extends EventManagerPlugin {
// 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): Function {
var zone = this.manager.getZone();
var outsideHandler = (event) => zone.runGuarded(() => handler(event));
return this.manager.getZone().runOutsideAngular(
() => DOM.onAndCancel(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.runGuarded(() => handler(event));
return this.manager.getZone().runOutsideAngular(
() => DOM.onAndCancel(element, eventName, outsideHandler));
}
}

View File

@ -0,0 +1,56 @@
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
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 =
/*@ts2dart_const*/ new OpaqueToken("EventManagerPlugins");
@Injectable()
export class EventManager {
private _plugins: EventManagerPlugin[];
constructor(@Inject(EVENT_MANAGER_PLUGINS) plugins: EventManagerPlugin[], private _zone: NgZone) {
plugins.forEach(p => p.manager = this);
this._plugins = ListWrapper.reversed(plugins);
}
addEventListener(element: HTMLElement, eventName: string, handler: Function): Function {
var plugin = this._findPluginFor(eventName);
return plugin.addEventListener(element, eventName, handler);
}
addGlobalEventListener(target: string, eventName: string, handler: Function): Function {
var plugin = this._findPluginFor(eventName);
return plugin.addGlobalEventListener(target, eventName, handler);
}
getZone(): NgZone { return this._zone; }
/** @internal */
_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;
}
}
throw new BaseException(`No event manager plugin found for event ${eventName}`);
}
}
export class EventManagerPlugin {
manager: EventManager;
// That is equivalent to having supporting $event.target
supports(eventName: string): boolean { return false; }
addEventListener(element: HTMLElement, eventName: string, handler: Function): Function {
throw "not implemented";
}
addGlobalEventListener(element: string, eventName: string, handler: Function): Function {
throw "not implemented";
}
}

View File

@ -0,0 +1,50 @@
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);
}
}

View File

@ -0,0 +1,114 @@
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, Inject, OpaqueToken;
import 'dart:js' as js;
const OpaqueToken HAMMER_GESTURE_CONFIG = const OpaqueToken("HammerGestureConfig");
overrideDefault(js.JsObject mc, String eventName, Object config) {
var jsObj = mc.callMethod('get', [eventName]);
jsObj.callMethod('set', [
new js.JsObject.jsify(config)
]);
}
@Injectable()
class HammerGestureConfig {
List<String> events = [];
Map overrides = {};
buildHammer(Element element) {
var mc = new js.JsObject(js.context['Hammer'], [element]);
overrideDefault(mc, 'pinch', {'enable': true});
overrideDefault(mc, 'rotate', {'enable': true});
this.overrides.forEach((Object config, String eventName) => overrideDefault(mc, eventName, config));
return mc;
}
}
@Injectable()
class HammerGesturesPlugin extends HammerGesturesPluginCommon {
HammerGestureConfig _config;
HammerGesturesPlugin(@Inject(HAMMER_GESTURE_CONFIG) this._config) {}
bool supports(String eventName) {
if (!super.supports(eventName) && !this.isCustomEvent(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 = this._config.buildHammer(element);
mc.callMethod('on', [
eventName,
(eventObj) {
zone.runGuarded(() {
var dartEvent = new HammerEvent._fromJsEvent(eventObj);
handler(dartEvent);
});
}
]);
});
}
isCustomEvent(String eventName) { return this._config.events.indexOf(eventName) > -1; }
}
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;
}
}

View File

@ -0,0 +1,62 @@
import {HammerGesturesPluginCommon} from './hammer_common';
import {isPresent} from 'angular2/src/facade/lang';
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
import {Injectable, Inject, OpaqueToken} from 'angular2/core';
export const HAMMER_GESTURE_CONFIG: OpaqueToken =
/*@ts2dart_const*/ new OpaqueToken("HammerGestureConfig");
export interface HammerInstance {
on(eventName: string, callback: Function): void;
off(eventName: string, callback: Function): void;
}
@Injectable()
export class HammerGestureConfig {
events: string[] = [];
overrides: {[key: string]: Object} = {};
buildHammer(element: HTMLElement): HammerInstance {
var mc = new Hammer(element);
mc.get('pinch').set({enable: true});
mc.get('rotate').set({enable: true});
for (let eventName in this.overrides) {
mc.get(eventName).set(this.overrides[eventName]);
}
return mc;
}
}
@Injectable()
export class HammerGesturesPlugin extends HammerGesturesPluginCommon {
constructor(@Inject(HAMMER_GESTURE_CONFIG) private _config: HammerGestureConfig) { super(); }
supports(eventName: string): boolean {
if (!super.supports(eventName) && !this.isCustomEvent(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): Function {
var zone = this.manager.getZone();
eventName = eventName.toLowerCase();
return zone.runOutsideAngular(() => {
// Creating the manager bind events, must be done outside of angular
var mc = this._config.buildHammer(element);
var callback = function(eventObj) { zone.runGuarded(function() { handler(eventObj); }); };
mc.on(eventName, callback);
return () => { mc.off(eventName, callback); };
});
}
isCustomEvent(eventName: string): boolean { return this._config.events.indexOf(eventName) > -1; }
}

View File

@ -0,0 +1,113 @@
import {DOM} from 'angular2/src/platform/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: Function): Function {
var parsedEvent = KeyEventsPlugin.parseEventName(eventName);
var outsideHandler = KeyEventsPlugin.eventCallback(
element, StringMapWrapper.get(parsedEvent, 'fullKey'), handler, this.manager.getZone());
return this.manager.getZone().runOutsideAngular(() => {
return DOM.onAndCancel(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: Function,
zone: NgZone): Function {
return (event) => {
if (StringWrapper.equals(KeyEventsPlugin.getEventFullKey(event), fullKey)) {
zone.runGuarded(() => 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;
}
}
}

View File

@ -0,0 +1,55 @@
import {DOM} from 'angular2/src/platform/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); });
}
}

View File

@ -0,0 +1,15 @@
import {StringWrapper} from 'angular2/src/facade/lang';
var CAMEL_CASE_REGEXP = /([A-Z])/g;
var DASH_CASE_REGEXP = /-([a-z])/g;
export function camelCaseToDashCase(input: string): string {
return StringWrapper.replaceAllMapped(input, CAMEL_CASE_REGEXP,
(m) => { return '-' + m[1].toLowerCase(); });
}
export function dashCaseToCamelCase(input: string): string {
return StringWrapper.replaceAllMapped(input, DASH_CASE_REGEXP,
(m) => { return m[1].toUpperCase(); });
}

View File

@ -0,0 +1,130 @@
export * from 'angular2/src/core/angular_entrypoint';
export {
BROWSER_PROVIDERS,
CACHED_TEMPLATE_PROVIDER,
ELEMENT_PROBE_PROVIDERS,
ELEMENT_PROBE_PROVIDERS_PROD_MODE,
inspectNativeElement,
BrowserDomAdapter,
By,
Title,
DOCUMENT,
enableDebugTools,
disableDebugTools
} from 'angular2/src/platform/browser_common';
import {Type, isPresent, isBlank} from 'angular2/src/facade/lang';
import {
BROWSER_PROVIDERS,
BROWSER_APP_COMMON_PROVIDERS,
BROWSER_PLATFORM_MARKER
} from 'angular2/src/platform/browser_common';
import {COMPILER_PROVIDERS} from 'angular2/compiler';
import {
ComponentRef,
coreLoadAndBootstrap,
reflector,
ReflectiveInjector,
PlatformRef,
OpaqueToken,
getPlatform,
createPlatform,
assertPlatform
} from 'angular2/core';
import {ReflectionCapabilities} from 'angular2/src/core/reflection/reflection_capabilities';
import {XHRImpl} from "angular2/src/platform/browser/xhr_impl";
import {XHR} from 'angular2/compiler';
/**
* An array of providers that should be passed into `application()` when bootstrapping a component.
*/
export const BROWSER_APP_PROVIDERS: Array<any /*Type | Provider | any[]*/> = /*@ts2dart_const*/[
BROWSER_APP_COMMON_PROVIDERS,
COMPILER_PROVIDERS,
/*@ts2dart_Provider*/ {provide: XHR, useClass: XHRImpl},
];
export function browserPlatform(): PlatformRef {
if (isBlank(getPlatform())) {
createPlatform(ReflectiveInjector.resolveAndCreate(BROWSER_PROVIDERS));
}
return assertPlatform(BROWSER_PLATFORM_MARKER);
}
/**
* Bootstrapping for Angular applications.
*
* You instantiate an Angular application by explicitly specifying a component to use
* as the root component for your application via the `bootstrap()` method.
*
* ## Simple Example
*
* Assuming this `index.html`:
*
* ```html
* <html>
* <!-- load Angular script tags here. -->
* <body>
* <my-app>loading...</my-app>
* </body>
* </html>
* ```
*
* An application is bootstrapped inside an existing browser DOM, typically `index.html`.
* Unlike Angular 1, Angular 2 does not compile/process providers in `index.html`. This is
* mainly for security reasons, as well as architectural changes in Angular 2. This means
* that `index.html` can safely be processed using server-side technologies such as
* providers. Bindings can thus use double-curly `{{ syntax }}` without collision from
* Angular 2 component double-curly `{{ syntax }}`.
*
* We can use this script code:
*
* {@example core/ts/bootstrap/bootstrap.ts region='bootstrap'}
*
* When the app developer invokes `bootstrap()` with the root component `MyApp` as its
* argument, Angular performs the following tasks:
*
* 1. It uses the component's `selector` property to locate the DOM element which needs
* to be upgraded into the angular component.
* 2. It creates a new child injector (from the platform injector). Optionally, you can
* also override the injector configuration for an app by invoking `bootstrap` with the
* `componentInjectableBindings` argument.
* 3. It creates a new `Zone` and connects it to the angular application's change detection
* domain instance.
* 4. It creates an emulated or shadow DOM on the selected component's host element and loads the
* template into it.
* 5. It instantiates the specified component.
* 6. Finally, Angular performs change detection to apply the initial data providers for the
* application.
*
*
* ## Bootstrapping Multiple Applications
*
* When working within a browser window, there are many singleton resources: cookies, title,
* location, and others. Angular services that represent these resources must likewise be
* shared across all Angular applications that occupy the same browser window. For this
* reason, Angular creates exactly one global platform object which stores all shared
* services, and each angular application injector has the platform injector as its parent.
*
* Each application has its own private injector as well. When there are multiple
* applications on a page, Angular treats each application injector's services as private
* to that application.
*
* ## API
*
* - `appComponentType`: The root component which should act as the application. This is
* a reference to a `Type` which is annotated with `@Component(...)`.
* - `customProviders`: An additional set of providers that can be added to the
* app injector to override default injection behavior.
*
* Returns a `Promise` of {@link ComponentRef}.
*/
export function bootstrap(
appComponentType: Type,
customProviders?: Array<any /*Type | Provider | any[]*/>): Promise<ComponentRef<any>> {
reflector.reflectionCapabilities = new ReflectionCapabilities();
var appInjector = ReflectiveInjector.resolveAndCreate(
[BROWSER_APP_PROVIDERS, isPresent(customProviders) ? customProviders : []],
browserPlatform().injector);
return coreLoadAndBootstrap(appInjector, appComponentType);
}

View File

@ -0,0 +1,60 @@
export * from 'angular2/src/core/angular_entrypoint';
export {
BROWSER_PROVIDERS,
ELEMENT_PROBE_PROVIDERS,
ELEMENT_PROBE_PROVIDERS_PROD_MODE,
inspectNativeElement,
BrowserDomAdapter,
By,
Title,
enableDebugTools,
disableDebugTools
} from 'angular2/src/platform/browser_common';
import {Type, isPresent, isBlank} from 'angular2/src/facade/lang';
import {
BROWSER_PROVIDERS,
BROWSER_APP_COMMON_PROVIDERS,
BROWSER_PLATFORM_MARKER
} from 'angular2/src/platform/browser_common';
import {
ComponentRef,
coreLoadAndBootstrap,
ReflectiveInjector,
PlatformRef,
getPlatform,
createPlatform,
assertPlatform
} from 'angular2/core';
/**
* An array of providers that should be passed into `application()` when bootstrapping a component
* when all templates
* have been precompiled offline.
*/
export const BROWSER_APP_PROVIDERS: Array<any /*Type | Provider | any[]*/> =
/*@ts2dart_const*/ BROWSER_APP_COMMON_PROVIDERS;
export function browserStaticPlatform(): PlatformRef {
if (isBlank(getPlatform())) {
createPlatform(ReflectiveInjector.resolveAndCreate(BROWSER_PROVIDERS));
}
return assertPlatform(BROWSER_PLATFORM_MARKER);
}
/**
* See {@link bootstrap} for more information.
*/
export function bootstrapStatic(appComponentType: Type,
customProviders?: Array<any /*Type | Provider | any[]*/>,
initReflector?: Function): Promise<ComponentRef<any>> {
if (isPresent(initReflector)) {
initReflector();
}
let appProviders =
isPresent(customProviders) ? [BROWSER_APP_PROVIDERS, customProviders] : BROWSER_APP_PROVIDERS;
var appInjector =
ReflectiveInjector.resolveAndCreate(appProviders, browserStaticPlatform().injector);
return coreLoadAndBootstrap(appInjector, appComponentType);
}

View File

@ -0,0 +1,223 @@
library angular2.src.web_workers.debug_tools.multi_client_server_message_bus;
import 'dart:io';
import 'dart:convert' show JSON;
import 'dart:async';
import 'package:angular2/src/web_workers/shared/messaging_api.dart';
import 'package:angular2/src/web_workers/shared/generic_message_bus.dart';
// TODO(jteplitz602): Remove hard coded result type and
// clear messageHistory once app is done with it #3859
class MultiClientServerMessageBus extends GenericMessageBus {
bool hasPrimary = false;
@override
MultiClientServerMessageBusSink get sink => super.sink;
@override
MultiClientServerMessageBusSource get source => super.source;
MultiClientServerMessageBus(MultiClientServerMessageBusSink sink,
MultiClientServerMessageBusSource source)
: super(sink, source);
MultiClientServerMessageBus.fromHttpServer(HttpServer server)
: super(new MultiClientServerMessageBusSink(),
new MultiClientServerMessageBusSource()) {
source.onResult.listen(_resultReceived);
server.listen((HttpRequest request) {
if (request.uri.path == "/ws") {
WebSocketTransformer.upgrade(request).then((WebSocket socket) {
var wrapper = new WebSocketWrapper(
sink.messageHistory, sink.resultMarkers, socket);
if (!hasPrimary) {
wrapper.setPrimary(true);
hasPrimary = true;
}
sink.addConnection(wrapper);
source.addConnection(wrapper);
wrapper.stream.listen(null, onDone: _handleDisconnect(wrapper));
});
}
});
}
void _resultReceived(_) {
sink.resultReceived();
}
Function _handleDisconnect(WebSocketWrapper wrapper) {
return () {
sink.removeConnection(wrapper);
if (wrapper.isPrimary) {
hasPrimary = false;
}
};
}
}
class WebSocketWrapper {
WebSocket _socket;
Stream stream;
int _numResultsReceived = 0;
bool _isPrimary = false;
bool caughtUp = false;
List<String> _messageHistory;
List<int> _resultMarkers;
StreamController<String> _sendStream;
WebSocketWrapper(this._messageHistory, this._resultMarkers, this._socket) {
stream = _socket.asBroadcastStream();
stream.listen((encodedMessage) {
var messages = JSON.decode(encodedMessage);
messages.forEach((data) {
var message = data['message'];
if (message is Map && message.containsKey("type")) {
if (message['type'] == 'result') {
resultReceived();
}
}
});
});
_sendStream = new StreamController<String>();
_socket.addStream(_sendStream.stream);
}
void send(String data) {
_sendStream.add(data);
}
bool get isPrimary => _isPrimary;
void resultReceived() {
if (!isPrimary && !caughtUp) {
_numResultsReceived++;
sendToMarker(_numResultsReceived);
}
}
void setPrimary(bool primary) {
_isPrimary = primary;
if (primary) {
caughtUp = true;
}
}
// Sends up to the given result marker
void sendToMarker(int markerIndex) {
int numMessages;
int curr;
if (markerIndex >= _resultMarkers.length) {
// we're past the final result marker so send all messages in history
curr = (_resultMarkers.length > 0)
? _resultMarkers[_resultMarkers.length - 1]
: 0;
numMessages = _messageHistory.length - curr;
caughtUp = true;
} else {
curr = (markerIndex == 0) ? 0 : _resultMarkers[markerIndex - 1];
var end = _resultMarkers[markerIndex];
numMessages = end - curr;
}
while (numMessages > 0) {
send(_messageHistory[curr]);
curr++;
numMessages--;
}
}
}
class MultiClientServerMessageBusSink extends GenericMessageBusSink {
final List<String> messageHistory = new List<String>();
final Set<WebSocketWrapper> openConnections = new Set<WebSocketWrapper>();
final List<int> resultMarkers = new List<int>();
void resultReceived() {
resultMarkers.add(messageHistory.length);
}
void addConnection(WebSocketWrapper webSocket) {
openConnections.add(webSocket);
// send messages up to the first result marker to this socket
webSocket.sendToMarker(0);
}
void removeConnection(WebSocketWrapper webSocket) {
openConnections.remove(webSocket);
}
@override
void sendMessages(List<dynamic> messages) {
String encodedMessages = JSON.encode(messages);
openConnections.forEach((WebSocketWrapper webSocket) {
if (webSocket.caughtUp) {
webSocket.send(encodedMessages);
}
});
messageHistory.add(encodedMessages);
}
}
class MultiClientServerMessageBusSource extends GenericMessageBusSource {
Function onResultReceived;
final StreamController mainController;
final StreamController resultController = new StreamController();
MultiClientServerMessageBusSource._(controller)
: mainController = controller,
super(controller.stream);
factory MultiClientServerMessageBusSource() {
return new MultiClientServerMessageBusSource._(
new StreamController.broadcast());
}
Stream get onResult => resultController.stream;
void addConnection(WebSocketWrapper webSocket) {
if (webSocket.isPrimary) {
webSocket.stream.listen((encodedMessages) {
var decodedMessages = _decodeMessages(encodedMessages);
decodedMessages.forEach((decodedMessage) {
var message = decodedMessage['message'];
if (message is Map && message.containsKey("type")) {
if (message['type'] == 'result') {
// tell the bus that a result was received on the primary
resultController.add(message);
}
}
});
mainController.add(decodedMessages);
});
} else {
webSocket.stream.listen((encodedMessages) {
// handle events from non-primary connection.
var decodedMessages = _decodeMessages(encodedMessages);
var eventMessages = new List<Map<String, dynamic>>();
decodedMessages.forEach((decodedMessage) {
var channel = decodedMessage['channel'];
if (channel == EVENT_CHANNEL) {
eventMessages.add(decodedMessage);
}
});
if (eventMessages.length > 0) {
mainController.add(eventMessages);
}
});
}
}
List<dynamic> _decodeMessages(dynamic messages) {
return JSON.decode(messages);
}
// This is a noop for the MultiClientBus because it has to decode the JSON messages before
// the generic bus receives them in order to check for results and forward events
// from the non-primary connection.
@override
List<dynamic> decodeMessages(dynamic messages) {
return messages;
}
}

View File

@ -0,0 +1,88 @@
library angular2.src.web_workers.debug_tools.single_client_server_message_bus;
import 'dart:io';
import 'dart:convert' show JSON;
import 'package:angular2/src/web_workers/shared/generic_message_bus.dart';
class SingleClientServerMessageBus extends GenericMessageBus {
bool connected = false;
@override
SingleClientServerMessageBusSink get sink => super.sink;
@override
SingleClientServerMessageBusSource get source => super.source;
SingleClientServerMessageBus(SingleClientServerMessageBusSink sink,
SingleClientServerMessageBusSource source)
: super(sink, source);
SingleClientServerMessageBus.fromHttpServer(HttpServer server)
: super(new SingleClientServerMessageBusSink(),
new SingleClientServerMessageBusSource()) {
server.listen((HttpRequest request) {
if (request.uri.path == "/ws") {
if (!connected) {
WebSocketTransformer.upgrade(request).then((WebSocket socket) {
sink.setConnection(socket);
var stream = socket.asBroadcastStream();
source.attachTo(stream);
stream.listen(null, onDone: _handleDisconnect);
}).catchError((error) {
throw error;
connected = false;
});
connected = true;
} else {
// refuse additional clients
request.response.statusCode = HttpStatus.SERVICE_UNAVAILABLE;
request.response.write("Maximum number of clients connected.");
request.response.close();
}
}
});
}
void _handleDisconnect() {
sink.removeConnection();
connected = false;
}
}
class SingleClientServerMessageBusSink extends GenericMessageBusSink {
final List<String> _messageBuffer = new List<String>();
WebSocket _socket = null;
void setConnection(WebSocket webSocket) {
_socket = webSocket;
_sendBufferedMessages();
}
void removeConnection() {
_socket = null;
}
@override
void sendMessages(List<dynamic> message) {
String encodedMessages = JSON.encode(message);
if (_socket != null) {
_socket.add(encodedMessages);
} else {
_messageBuffer.add(encodedMessages);
}
}
void _sendBufferedMessages() {
_messageBuffer.forEach((message) => _socket.add(message));
_messageBuffer.clear();
}
}
class SingleClientServerMessageBusSource extends GenericMessageBusSource {
SingleClientServerMessageBusSource() : super(null);
@override
List<dynamic> decodeMessages(dynamic messages) {
return JSON.decode(messages);
}
}

View File

@ -0,0 +1,34 @@
library angular2.src.web_workers.worker.web_socket_message_bus;
import 'dart:html';
import 'dart:convert' show JSON;
import 'package:angular2/src/web_workers/shared/generic_message_bus.dart';
class WebSocketMessageBus extends GenericMessageBus {
WebSocketMessageBus(
WebSocketMessageBusSink sink, WebSocketMessageBusSource source)
: super(sink, source);
WebSocketMessageBus.fromWebSocket(WebSocket webSocket)
: super(new WebSocketMessageBusSink(webSocket),
new WebSocketMessageBusSource(webSocket));
}
class WebSocketMessageBusSink extends GenericMessageBusSink {
final WebSocket _webSocket;
WebSocketMessageBusSink(this._webSocket);
void sendMessages(List<dynamic> messages) {
_webSocket.send(JSON.encode(messages));
}
}
class WebSocketMessageBusSource extends GenericMessageBusSource {
WebSocketMessageBusSource(WebSocket webSocket) : super(webSocket.onMessage);
List<dynamic> decodeMessages(MessageEvent event) {
var messages = event.data;
return JSON.decode(messages);
}
}

View File

@ -0,0 +1,3 @@
import {OpaqueToken} from "angular2/src/core/di";
export const ON_WEB_WORKER = /*@ts2dart_const*/ new OpaqueToken('WebWorker.onWebWorker');

View File

@ -0,0 +1,167 @@
import {MessageBus} from "angular2/src/web_workers/shared/message_bus";
import {
print,
isPresent,
DateWrapper,
stringify,
Type,
StringWrapper
} from "angular2/src/facade/lang";
import {
PromiseCompleter,
PromiseWrapper,
ObservableWrapper,
EventEmitter
} from "angular2/src/facade/async";
import {StringMapWrapper, MapWrapper} from "angular2/src/facade/collection";
import {Serializer} from "angular2/src/web_workers/shared/serializer";
import {Injectable} from "angular2/src/core/di";
export {Type} from "angular2/src/facade/lang";
export abstract class ClientMessageBrokerFactory {
/**
* Initializes the given channel and attaches a new {@link ClientMessageBroker} to it.
*/
abstract createMessageBroker(channel: string, runInZone?: boolean): ClientMessageBroker;
}
@Injectable()
export class ClientMessageBrokerFactory_ extends ClientMessageBrokerFactory {
/** @internal */
public _serializer: Serializer;
constructor(private _messageBus: MessageBus, _serializer: Serializer) {
super();
this._serializer = _serializer;
}
/**
* Initializes the given channel and attaches a new {@link ClientMessageBroker} to it.
*/
createMessageBroker(channel: string, runInZone: boolean = true): ClientMessageBroker {
this._messageBus.initChannel(channel, runInZone);
return new ClientMessageBroker_(this._messageBus, this._serializer, channel);
}
}
export abstract class ClientMessageBroker {
abstract runOnService(args: UiArguments, returnType: Type): Promise<any>;
}
export class ClientMessageBroker_ extends ClientMessageBroker {
private _pending: Map<string, PromiseCompleter<any>> = new Map<string, PromiseCompleter<any>>();
private _sink: EventEmitter<any>;
/** @internal */
public _serializer: Serializer;
constructor(messageBus: MessageBus, _serializer: Serializer, public channel) {
super();
this._sink = messageBus.to(channel);
this._serializer = _serializer;
var source = messageBus.from(channel);
ObservableWrapper.subscribe(source,
(message: {[key: string]: any}) => this._handleMessage(message));
}
private _generateMessageId(name: string): string {
var time: string = stringify(DateWrapper.toMillis(DateWrapper.now()));
var iteration: number = 0;
var id: string = name + time + stringify(iteration);
while (isPresent(this._pending[id])) {
id = `${name}${time}${iteration}`;
iteration++;
}
return id;
}
runOnService(args: UiArguments, returnType: Type): Promise<any> {
var fnArgs = [];
if (isPresent(args.args)) {
args.args.forEach(argument => {
if (argument.type != null) {
fnArgs.push(this._serializer.serialize(argument.value, argument.type));
} else {
fnArgs.push(argument.value);
}
});
}
var promise: Promise<any>;
var id: string = null;
if (returnType != null) {
var completer: PromiseCompleter<any> = PromiseWrapper.completer();
id = this._generateMessageId(args.method);
this._pending.set(id, completer);
PromiseWrapper.catchError(completer.promise, (err, stack?) => {
print(err);
completer.reject(err, stack);
});
promise = PromiseWrapper.then(completer.promise, (value: any) => {
if (this._serializer == null) {
return value;
} else {
return this._serializer.deserialize(value, returnType);
}
});
} else {
promise = null;
}
// TODO(jteplitz602): Create a class for these messages so we don't keep using StringMap #3685
var message = {'method': args.method, 'args': fnArgs};
if (id != null) {
message['id'] = id;
}
ObservableWrapper.callEmit(this._sink, message);
return promise;
}
private _handleMessage(message: {[key: string]: any}): void {
var data = new MessageData(message);
// TODO(jteplitz602): replace these strings with messaging constants #3685
if (StringWrapper.equals(data.type, "result") || StringWrapper.equals(data.type, "error")) {
var id = data.id;
if (this._pending.has(id)) {
if (StringWrapper.equals(data.type, "result")) {
this._pending.get(id).resolve(data.value);
} else {
this._pending.get(id).reject(data.value, null);
}
this._pending.delete(id);
}
}
}
}
class MessageData {
type: string;
value: any;
id: string;
constructor(data: {[key: string]: any}) {
this.type = StringMapWrapper.get(data, "type");
this.id = this._getValueIfPresent(data, "id");
this.value = this._getValueIfPresent(data, "value");
}
/**
* Returns the value from the StringMap if present. Otherwise returns null
* @internal
*/
_getValueIfPresent(data: {[key: string]: any}, key: string) {
if (StringMapWrapper.contains(data, key)) {
return StringMapWrapper.get(data, key);
} else {
return null;
}
}
}
export class FnArg {
constructor(public value, public type: Type) {}
}
export class UiArguments {
constructor(public method: string, public args?: FnArg[]) {}
}

View File

@ -0,0 +1,156 @@
library angular2.src.web_workers.shared.generic_message_bus;
import 'dart:async';
import 'package:angular2/src/facade/async.dart' show EventEmitter;
import 'package:angular2/src/web_workers/shared/message_bus.dart'
show MessageBus, MessageBusSink, MessageBusSource;
import 'package:angular2/src/core/zone/ng_zone.dart';
import 'package:angular2/src/facade/lang.dart';
import 'package:angular2/src/facade/exceptions.dart';
class GenericMessageBus implements MessageBus {
final MessageBusSink _sink;
final MessageBusSource _source;
MessageBusSink get sink => _sink;
MessageBusSource get source => _source;
GenericMessageBus(MessageBusSink sink, MessageBusSource source)
: _sink = sink,
_source = source;
void attachToZone(NgZone zone) {
_sink.attachToZone(zone);
_source.attachToZone(zone);
}
void initChannel(String channel, [bool runInZone = true]) {
_sink.initChannel(channel, runInZone);
_source.initChannel(channel, runInZone);
}
EventEmitter from(String channel) {
return _source.from(channel);
}
EventEmitter to(String channel) {
return _sink.to(channel);
}
}
abstract class GenericMessageBusSink implements MessageBusSink {
NgZone _zone;
final _channels = new Map<String, _Channel>();
final _messageBuffer = new List<dynamic>();
void attachToZone(NgZone zone) {
_zone = zone;
_zone.runOutsideAngular(() {
_zone.onStable.listen((_) {
if (_messageBuffer.length > 0) {
sendMessages(_messageBuffer);
_messageBuffer.clear();
}
});
});
}
void initChannel(String channelName, [bool runInZone = true]) {
if (_channels.containsKey(channelName)) {
throw new BaseException("${channelName} has already been initialized.");
}
var emitter = new EventEmitter();
var channel = new _Channel(emitter, runInZone);
emitter.listen((data) {
var message = {'channel': channelName, 'message': data};
if (runInZone) {
_messageBuffer.add(message);
} else {
sendMessages([message]);
}
});
_channels[channelName] = channel;
}
EventEmitter to(String channelName) {
if (_channels.containsKey(channelName)) {
return _channels[channelName].emitter;
} else {
throw new BaseException(
"${channelName} is not set up. Did you forget to call initChannel?");
}
}
void sendMessages(List<dynamic> messages);
}
abstract class GenericMessageBusSource implements MessageBusSource {
Stream _stream;
final _channels = new Map<String, _Channel>();
NgZone _zone;
Stream get stream => _stream;
GenericMessageBusSource(Stream stream) {
attachTo(stream);
}
void attachTo(Stream stream) {
_stream = stream;
if (stream != null) {
stream.listen((messages) {
List<dynamic> decodedMessages = decodeMessages(messages);
if (decodedMessages != null) {
decodedMessages.forEach((message) => _handleMessage(message));
}
});
}
}
void attachToZone(NgZone zone) {
_zone = zone;
}
void initChannel(String channelName, [bool runInZone = true]) {
if (_channels.containsKey(channelName)) {
throw new BaseException("${channelName} has already been initialized.");
}
var emitter = new EventEmitter();
var channelInfo = new _Channel(emitter, runInZone);
_channels[channelName] = channelInfo;
}
EventEmitter from(String channelName) {
if (_channels.containsKey(channelName)) {
return _channels[channelName].emitter;
} else {
throw new BaseException(
"${channelName} is not set up. Did you forget to call initChannel?");
}
}
void _handleMessage(dynamic data) {
var channelName = data['channel'];
if (_channels.containsKey(channelName)) {
var channelInfo = _channels[channelName];
if (channelInfo.runInZone) {
_zone.run(() => channelInfo.emitter.add(data['message']));
} else {
channelInfo.emitter.add(data['message']);
}
}
}
List<dynamic> decodeMessages(dynamic message);
}
class _Channel {
EventEmitter emitter;
bool runInZone;
_Channel(this.emitter, this.runInZone);
}

View File

@ -0,0 +1,33 @@
library angular2.src.web_workers.shared.isolate_message_bus;
import 'dart:isolate';
import 'package:angular2/src/web_workers/shared/generic_message_bus.dart';
class IsolateMessageBus extends GenericMessageBus {
IsolateMessageBus(IsolateMessageBusSink sink, IsolateMessageBusSource source)
: super(sink, source);
}
class IsolateMessageBusSink extends GenericMessageBusSink {
final SendPort _port;
IsolateMessageBusSink(SendPort port) : _port = port;
@override
void sendMessages(List<dynamic> messages) {
_port.send(messages);
}
}
class IsolateMessageBusSource extends GenericMessageBusSource {
IsolateMessageBusSource(ReceivePort port) : super(port.asBroadcastStream());
@override
List<dynamic> decodeMessages(dynamic messages) {
if (messages is SendPort) {
return null;
}
return messages;
}
}

View File

@ -0,0 +1,87 @@
import {EventEmitter} from 'angular2/src/facade/async';
import {NgZone} from 'angular2/src/core/zone/ng_zone';
export {EventEmitter, Observable} from 'angular2/src/facade/async';
/**
* Message Bus is a low level API used to communicate between the UI and the background.
* Communication is based on a channel abstraction. Messages published in a
* given channel to one MessageBusSink are received on the same channel
* by the corresponding MessageBusSource.
*/
export abstract class MessageBus implements MessageBusSource, MessageBusSink {
/**
* Sets up a new channel on the MessageBus.
* MUST be called before calling from or to on the channel.
* If runInZone is true then the source will emit events inside the angular zone
* and the sink will buffer messages and send only once the zone exits.
* if runInZone is false then the source will emit events inside the global zone
* and the sink will send messages immediately.
*/
abstract initChannel(channel: string, runInZone?: boolean): void;
/**
* Assigns this bus to the given zone.
* Any callbacks attached to channels where runInZone was set to true on initialization
* will be executed in the given zone.
*/
abstract attachToZone(zone: NgZone): void;
/**
* Returns an {@link EventEmitter} that emits every time a message
* is received on the given channel.
*/
abstract from(channel: string): EventEmitter<any>;
/**
* Returns an {@link EventEmitter} for the given channel
* To publish methods to that channel just call next (or add in dart) on the returned emitter
*/
abstract to(channel: string): EventEmitter<any>;
}
export interface MessageBusSource {
/**
* Sets up a new channel on the MessageBusSource.
* MUST be called before calling from on the channel.
* If runInZone is true then the source will emit events inside the angular zone.
* if runInZone is false then the source will emit events inside the global zone.
*/
initChannel(channel: string, runInZone: boolean): void;
/**
* Assigns this source to the given zone.
* Any channels which are initialized with runInZone set to true will emit events that will be
* executed within the given zone.
*/
attachToZone(zone: NgZone): void;
/**
* Returns an {@link EventEmitter} that emits every time a message
* is received on the given channel.
*/
from(channel: string): EventEmitter<any>;
}
export interface MessageBusSink {
/**
* Sets up a new channel on the MessageBusSink.
* MUST be called before calling to on the channel.
* If runInZone is true the sink will buffer messages and send only once the zone exits.
* if runInZone is false the sink will send messages immediatly.
*/
initChannel(channel: string, runInZone: boolean): void;
/**
* Assigns this sink to the given zone.
* Any channels which are initialized with runInZone set to true will wait for the given zone
* to exit before sending messages.
*/
attachToZone(zone: NgZone): void;
/**
* Returns an {@link EventEmitter} for the given channel
* To publish methods to that channel just call next (or add in dart) on the returned emitter
*/
to(channel: string): EventEmitter<any>;
}

View File

@ -0,0 +1,8 @@
/**
* All channels used by angular's WebWorker components are listed here.
* You should not use these channels in your application code.
*/
export const RENDERER_CHANNEL = "ng-Renderer";
export const XHR_CHANNEL = "ng-XHR";
export const EVENT_CHANNEL = "ng-Events";
export const ROUTER_CHANNEL = "ng-Router";

View File

@ -0,0 +1,3 @@
// PostMessageBus can't be implemented in dart since dart doesn't use postMessage
// This file is only here to prevent ts2dart from trying to transpile the PostMessageBus
library angular2.src.web_workers.shared.post_message_bus;

View File

@ -0,0 +1,147 @@
import {
MessageBus,
MessageBusSource,
MessageBusSink
} from "angular2/src/web_workers/shared/message_bus";
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
import {StringMapWrapper} from 'angular2/src/facade/collection';
import {Injectable} from "angular2/src/core/di";
import {NgZone} from 'angular2/src/core/zone/ng_zone';
// TODO(jteplitz602) Replace this with the definition in lib.webworker.d.ts(#3492)
export interface PostMessageTarget { postMessage: (message: any, transfer?:[ArrayBuffer]) => void; }
export class PostMessageBusSink implements MessageBusSink {
private _zone: NgZone;
private _channels: {[key: string]: _Channel} = StringMapWrapper.create();
private _messageBuffer: Array<Object> = [];
constructor(private _postMessageTarget: PostMessageTarget) {}
attachToZone(zone: NgZone): void {
this._zone = zone;
this._zone.runOutsideAngular(() => {
ObservableWrapper.subscribe(this._zone.onStable, (_) => { this._handleOnEventDone(); });
});
}
initChannel(channel: string, runInZone: boolean = true): void {
if (StringMapWrapper.contains(this._channels, channel)) {
throw new BaseException(`${channel} has already been initialized`);
}
var emitter = new EventEmitter(false);
var channelInfo = new _Channel(emitter, runInZone);
this._channels[channel] = channelInfo;
emitter.subscribe((data: Object) => {
var message = {channel: channel, message: data};
if (runInZone) {
this._messageBuffer.push(message);
} else {
this._sendMessages([message]);
}
});
}
to(channel: string): EventEmitter<any> {
if (StringMapWrapper.contains(this._channels, channel)) {
return this._channels[channel].emitter;
} else {
throw new BaseException(`${channel} is not set up. Did you forget to call initChannel?`);
}
}
private _handleOnEventDone() {
if (this._messageBuffer.length > 0) {
this._sendMessages(this._messageBuffer);
this._messageBuffer = [];
}
}
private _sendMessages(messages: Array<Object>) { this._postMessageTarget.postMessage(messages); }
}
export class PostMessageBusSource implements MessageBusSource {
private _zone: NgZone;
private _channels: {[key: string]: _Channel} = StringMapWrapper.create();
constructor(eventTarget?: EventTarget) {
if (eventTarget) {
eventTarget.addEventListener("message", (ev: MessageEvent) => this._handleMessages(ev));
} else {
// if no eventTarget is given we assume we're in a WebWorker and listen on the global scope
addEventListener("message", (ev: MessageEvent) => this._handleMessages(ev));
}
}
attachToZone(zone: NgZone) { this._zone = zone; }
initChannel(channel: string, runInZone: boolean = true) {
if (StringMapWrapper.contains(this._channels, channel)) {
throw new BaseException(`${channel} has already been initialized`);
}
var emitter = new EventEmitter(false);
var channelInfo = new _Channel(emitter, runInZone);
this._channels[channel] = channelInfo;
}
from(channel: string): EventEmitter<any> {
if (StringMapWrapper.contains(this._channels, channel)) {
return this._channels[channel].emitter;
} else {
throw new BaseException(`${channel} is not set up. Did you forget to call initChannel?`);
}
}
private _handleMessages(ev: MessageEvent): void {
var messages = ev.data;
for (var i = 0; i < messages.length; i++) {
this._handleMessage(messages[i]);
}
}
private _handleMessage(data: any): void {
var channel = data.channel;
if (StringMapWrapper.contains(this._channels, channel)) {
var channelInfo = this._channels[channel];
if (channelInfo.runInZone) {
this._zone.run(() => { channelInfo.emitter.emit(data.message); });
} else {
channelInfo.emitter.emit(data.message);
}
}
}
}
/**
* A TypeScript implementation of {@link MessageBus} for communicating via JavaScript's
* postMessage API.
*/
@Injectable()
export class PostMessageBus implements MessageBus {
constructor(public sink: PostMessageBusSink, public source: PostMessageBusSource) {}
attachToZone(zone: NgZone): void {
this.source.attachToZone(zone);
this.sink.attachToZone(zone);
}
initChannel(channel: string, runInZone: boolean = true): void {
this.source.initChannel(channel, runInZone);
this.sink.initChannel(channel, runInZone);
}
from(channel: string): EventEmitter<any> { return this.source.from(channel); }
to(channel: string): EventEmitter<any> { return this.sink.to(channel); }
}
/**
* Helper class that wraps a channel's {@link EventEmitter} and
* keeps track of if it should run in the zone.
*/
class _Channel {
constructor(public emitter: EventEmitter<any>, public runInZone: boolean) {}
}

View File

@ -0,0 +1,45 @@
import {Injectable} from "angular2/src/core/di";
@Injectable()
export class RenderStore {
private _nextIndex: number = 0;
private _lookupById: Map<number, any>;
private _lookupByObject: Map<any, number>;
constructor() {
this._lookupById = new Map<number, any>();
this._lookupByObject = new Map<any, number>();
}
allocateId(): number { return this._nextIndex++; }
store(obj: any, id: number): void {
this._lookupById.set(id, obj);
this._lookupByObject.set(obj, id);
}
remove(obj: any): void {
var index = this._lookupByObject.get(obj);
this._lookupByObject.delete(obj);
this._lookupById.delete(index);
}
deserialize(id: number): any {
if (id == null) {
return null;
}
if (!this._lookupById.has(id)) {
return null;
}
return this._lookupById.get(id);
}
serialize(obj: any): number {
if (obj == null) {
return null;
}
return this._lookupByObject.get(obj);
}
}

View File

@ -0,0 +1,7 @@
// This file contains interface versions of browser types that can be serialized to Plain Old
// JavaScript Objects
export class LocationType {
constructor(public href: string, public protocol: string, public host: string,
public hostname: string, public port: string, public pathname: string,
public search: string, public hash: string, public origin: string) {}
}

View File

@ -0,0 +1,135 @@
import {Type, isArray, isPresent, serializeEnum, deserializeEnum} from "angular2/src/facade/lang";
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
import {Map, StringMapWrapper, MapWrapper} from "angular2/src/facade/collection";
import {RenderComponentType} from "angular2/src/core/render/api";
import {Injectable} from "angular2/src/core/di";
import {RenderStore} from 'angular2/src/web_workers/shared/render_store';
import {ViewEncapsulation, VIEW_ENCAPSULATION_VALUES} from 'angular2/src/core/metadata/view';
import {LocationType} from './serialized_types';
// PRIMITIVE is any type that does not need to be serialized (string, number, boolean)
// We set it to String so that it is considered a Type.
export const PRIMITIVE: Type = /*@ts2dart_const*/ String;
@Injectable()
export class Serializer {
constructor(private _renderStore: RenderStore) {}
serialize(obj: any, type: any): Object {
if (!isPresent(obj)) {
return null;
}
if (isArray(obj)) {
return (<any[]>obj).map(v => this.serialize(v, type));
}
if (type == PRIMITIVE) {
return obj;
}
if (type == RenderStoreObject) {
return this._renderStore.serialize(obj);
} else if (type === RenderComponentType) {
return this._serializeRenderComponentType(obj);
} else if (type === ViewEncapsulation) {
return serializeEnum(obj);
} else if (type === LocationType) {
return this._serializeLocation(obj);
} else {
throw new BaseException("No serializer for " + type.toString());
}
}
deserialize(map: any, type: any, data?: any): any {
if (!isPresent(map)) {
return null;
}
if (isArray(map)) {
var obj: any[] = [];
(<any[]>map).forEach(val => obj.push(this.deserialize(val, type, data)));
return obj;
}
if (type == PRIMITIVE) {
return map;
}
if (type == RenderStoreObject) {
return this._renderStore.deserialize(map);
} else if (type === RenderComponentType) {
return this._deserializeRenderComponentType(map);
} else if (type === ViewEncapsulation) {
return VIEW_ENCAPSULATION_VALUES[map];
} else if (type === LocationType) {
return this._deserializeLocation(map);
} else {
throw new BaseException("No deserializer for " + type.toString());
}
}
mapToObject(map: Map<string, any>, type?: Type): Object {
var object = {};
var serialize = isPresent(type);
map.forEach((value, key) => {
if (serialize) {
object[key] = this.serialize(value, type);
} else {
object[key] = value;
}
});
return object;
}
/*
* Transforms a Javascript object (StringMap) into a Map<string, V>
* If the values need to be deserialized pass in their type
* and they will be deserialized before being placed in the map
*/
objectToMap(obj: {[key: string]: any}, type?: Type, data?: any): Map<string, any> {
if (isPresent(type)) {
var map = new Map<string, any>();
StringMapWrapper.forEach(obj,
(val, key) => { map.set(key, this.deserialize(val, type, data)); });
return map;
} else {
return MapWrapper.createFromStringMap(obj);
}
}
private _serializeLocation(loc: LocationType): Object {
return {
'href': loc.href,
'protocol': loc.protocol,
'host': loc.host,
'hostname': loc.hostname,
'port': loc.port,
'pathname': loc.pathname,
'search': loc.search,
'hash': loc.hash,
'origin': loc.origin
};
}
private _deserializeLocation(loc: {[key: string]: any}): LocationType {
return new LocationType(loc['href'], loc['protocol'], loc['host'], loc['hostname'], loc['port'],
loc['pathname'], loc['search'], loc['hash'], loc['origin']);
}
private _serializeRenderComponentType(obj: RenderComponentType): Object {
return {
'id': obj.id,
'templateUrl': obj.templateUrl,
'slotCount': obj.slotCount,
'encapsulation': this.serialize(obj.encapsulation, ViewEncapsulation),
'styles': this.serialize(obj.styles, PRIMITIVE)
};
}
private _deserializeRenderComponentType(map: {[key: string]: any}): RenderComponentType {
return new RenderComponentType(map['id'], map['templateUrl'], map['slotCount'],
this.deserialize(map['encapsulation'], ViewEncapsulation),
this.deserialize(map['styles'], PRIMITIVE));
}
}
export class RenderStoreObject {}

View File

@ -0,0 +1,99 @@
import {Injectable} from 'angular2/src/core/di';
import {ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection';
import {Serializer} from "angular2/src/web_workers/shared/serializer";
import {isPresent, Type, FunctionWrapper} from "angular2/src/facade/lang";
import {MessageBus} from "angular2/src/web_workers/shared/message_bus";
import {EventEmitter, PromiseWrapper, ObservableWrapper} from 'angular2/src/facade/async';
export abstract class ServiceMessageBrokerFactory {
/**
* Initializes the given channel and attaches a new {@link ServiceMessageBroker} to it.
*/
abstract createMessageBroker(channel: string, runInZone?: boolean): ServiceMessageBroker;
}
@Injectable()
export class ServiceMessageBrokerFactory_ extends ServiceMessageBrokerFactory {
/** @internal */
public _serializer: Serializer;
constructor(private _messageBus: MessageBus, _serializer: Serializer) {
super();
this._serializer = _serializer;
}
createMessageBroker(channel: string, runInZone: boolean = true): ServiceMessageBroker {
this._messageBus.initChannel(channel, runInZone);
return new ServiceMessageBroker_(this._messageBus, this._serializer, channel);
}
}
export abstract class ServiceMessageBroker {
abstract registerMethod(methodName: string, signature: Type[], method: Function,
returnType?: Type): void;
}
/**
* Helper class for UIComponents that allows components to register methods.
* If a registered method message is received from the broker on the worker,
* the UIMessageBroker deserializes its arguments and calls the registered method.
* If that method returns a promise, the UIMessageBroker returns the result to the worker.
*/
export class ServiceMessageBroker_ extends ServiceMessageBroker {
private _sink: EventEmitter<any>;
private _methods: Map<string, Function> = new Map<string, Function>();
constructor(messageBus: MessageBus, private _serializer: Serializer, public channel) {
super();
this._sink = messageBus.to(channel);
var source = messageBus.from(channel);
ObservableWrapper.subscribe(source, (message) => this._handleMessage(message));
}
registerMethod(methodName: string, signature: Type[], method: (..._: any[]) => Promise<any>| void,
returnType?: Type): void {
this._methods.set(methodName, (message: ReceivedMessage) => {
var serializedArgs = message.args;
let numArgs = signature === null ? 0 : signature.length;
var deserializedArgs: any[] = ListWrapper.createFixedSize(numArgs);
for (var i = 0; i < numArgs; i++) {
var serializedArg = serializedArgs[i];
deserializedArgs[i] = this._serializer.deserialize(serializedArg, signature[i]);
}
var promise = FunctionWrapper.apply(method, deserializedArgs);
if (isPresent(returnType) && isPresent(promise)) {
this._wrapWebWorkerPromise(message.id, promise, returnType);
}
});
}
private _handleMessage(map: {[key: string]: any}): void {
var message = new ReceivedMessage(map);
if (this._methods.has(message.method)) {
this._methods.get(message.method)(message);
}
}
private _wrapWebWorkerPromise(id: string, promise: Promise<any>, type: Type): void {
PromiseWrapper.then(promise, (result: any) => {
ObservableWrapper.callEmit(
this._sink,
{'type': 'result', 'value': this._serializer.serialize(result, type), 'id': id});
});
}
}
export class ReceivedMessage {
method: string;
args: any[];
id: string;
type: string;
constructor(data: {[key: string]: any}) {
this.method = data['method'];
this.args = data['args'];
this.id = data['id'];
this.type = data['type'];
}
}

View File

@ -0,0 +1,11 @@
library angular2.src.web_workers.ui.bind;
/**
* Binding is not necessary in dart.
* This method just returns the passed function regardless of scope.
* It's only here to match the TypeScript implementation.
* TODO(jteplitz602) Have ts2dart remove calls to bind(#3820)
*/
Function bind(Function fn, dynamic scope) {
return fn;
}

View File

@ -0,0 +1,3 @@
export function bind(fn: Function, scope: any): Function {
return fn.bind(scope);
}

View File

@ -0,0 +1,110 @@
import {Serializer, RenderStoreObject} from 'angular2/src/web_workers/shared/serializer';
import {
serializeMouseEvent,
serializeKeyboardEvent,
serializeGenericEvent,
serializeEventWithTarget,
serializeTransitionEvent
} from 'angular2/src/web_workers/ui/event_serializer';
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
import {StringMapWrapper} from 'angular2/src/facade/collection';
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
export class EventDispatcher {
constructor(private _sink: EventEmitter<any>, private _serializer: Serializer) {}
dispatchRenderEvent(element: any, eventTarget: string, eventName: string, event: any): boolean {
var serializedEvent;
// TODO (jteplitz602): support custom events #3350
switch (event.type) {
case "click":
case "mouseup":
case "mousedown":
case "dblclick":
case "contextmenu":
case "mouseenter":
case "mouseleave":
case "mousemove":
case "mouseout":
case "mouseover":
case "show":
serializedEvent = serializeMouseEvent(event);
break;
case "keydown":
case "keypress":
case "keyup":
serializedEvent = serializeKeyboardEvent(event);
break;
case "input":
case "change":
case "blur":
serializedEvent = serializeEventWithTarget(event);
break;
case "abort":
case "afterprint":
case "beforeprint":
case "cached":
case "canplay":
case "canplaythrough":
case "chargingchange":
case "chargingtimechange":
case "close":
case "dischargingtimechange":
case "DOMContentLoaded":
case "downloading":
case "durationchange":
case "emptied":
case "ended":
case "error":
case "fullscreenchange":
case "fullscreenerror":
case "invalid":
case "languagechange":
case "levelfchange":
case "loadeddata":
case "loadedmetadata":
case "obsolete":
case "offline":
case "online":
case "open":
case "orientatoinchange":
case "pause":
case "pointerlockchange":
case "pointerlockerror":
case "play":
case "playing":
case "ratechange":
case "readystatechange":
case "reset":
case "scroll":
case "seeked":
case "seeking":
case "stalled":
case "submit":
case "success":
case "suspend":
case "timeupdate":
case "updateready":
case "visibilitychange":
case "volumechange":
case "waiting":
serializedEvent = serializeGenericEvent(event);
break;
case "transitionend":
serializedEvent = serializeTransitionEvent(event);
break;
default:
throw new BaseException(eventName + " not supported on WebWorkers");
}
ObservableWrapper.callEmit(this._sink, {
"element": this._serializer.serialize(element, RenderStoreObject),
"eventName": eventName,
"eventTarget": eventTarget,
"event": serializedEvent
});
// TODO(kegluneq): Eventually, we want the user to indicate from the UI side whether the event
// should be canceled, but for now just call `preventDefault` on the original DOM event.
return false;
}
}

View File

@ -0,0 +1,108 @@
library angular2.src.web_workers.event_serializer;
import 'dart:core';
import 'dart:html';
// List of all elements with HTML value attribute.
// Taken from: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes
final Set<String> NODES_WITH_VALUE = new Set<String>.from([
"input",
"select",
"option",
"button",
"li",
"meter",
"progress",
"param",
"textarea"
]);
Map<String, dynamic> serializeGenericEvent(dynamic e) {
var serialized = new Map<String, dynamic>();
serialized['bubbles'] = e.bubbles;
serialized['cancelable'] = e.cancelable;
serialized['defaultPrevented'] = e.defaultPrevented;
serialized['eventPhase'] = e.eventPhase;
serialized['timeStamp'] = e.timeStamp;
serialized['type'] = e.type;
return serialized;
}
// TODO(jteplitz602): Allow users to specify the properties they need rather than always
// adding value #3374
Map<String, dynamic> serializeEventWithTarget(dynamic e) {
var serializedEvent = serializeGenericEvent(e);
return addTarget(e, serializedEvent);
}
Map<String, dynamic> serializeMouseEvent(dynamic e) {
var serialized = new Map<String, dynamic>();
serialized['altKey'] = e.altKey;
serialized['bubbles'] = e.bubbles;
serialized['button'] = e.button;
serialized['cancelable'] = e.cancelable;
serialized['client'] = serializePoint(e.client);
serialized['ctrlKey'] = e.ctrlKey;
serialized['defaultPrevented'] = e.defaultPrevented;
serialized['detail'] = e.detail;
serialized['eventPhase'] = e.eventPhase;
serialized['layer'] = serializePoint(e.layer);
serialized['metaKey'] = e.metaKey;
serialized['offset'] = serializePoint(e.offset);
serialized['page'] = serializePoint(e.page);
serialized['region'] = e.region;
serialized['screen'] = serializePoint(e.screen);
serialized['shiftKey'] = e.shiftKey;
serialized['timeStamp'] = e.timeStamp;
serialized['type'] = e.type;
return serialized;
}
Map<String, dynamic> serializePoint(Point point) {
var serialized = new Map<String, dynamic>();
serialized['magnitude'] = point.magnitude;
serialized['x'] = point.x;
serialized['y'] = point.y;
return serialized;
}
Map<String, dynamic> serializeKeyboardEvent(dynamic e) {
var serialized = new Map<String, dynamic>();
serialized['altKey'] = e.altKey;
serialized['bubbles'] = e.bubbles;
serialized['cancelable'] = e.cancelable;
serialized['charCode'] = e.charCode;
serialized['ctrlKey'] = e.ctrlKey;
serialized['defaultPrevented'] = e.defaultPrevented;
serialized['detail'] = e.detail;
serialized['eventPhase'] = e.eventPhase;
serialized['keyCode'] = e.keyCode;
serialized['keyLocation'] = e.keyLocation;
serialized['location'] = e.location;
serialized['repeat'] = e.repeat;
serialized['shiftKey'] = e.shiftKey;
serialized['timeStamp'] = e.timeStamp;
serialized['type'] = e.type;
//return addTarget(e, serialized);
return serialized;
}
Map<String, dynamic> serializeTransitionEvent(dynamic e) {
var serialized = serializeGenericEvent(e);
serialized['propertyName'] = e.propertyName;
serialized['elapsedTime'] = e.elapsedTime;
serialized['pseudoElement'] = e.pseudoElement;
return addTarget(e, serialized);
}
// TODO(jteplitz602): #3374. See above.
Map<String, dynamic> addTarget(
dynamic e, Map<String, dynamic> serializedEvent) {
if (NODES_WITH_VALUE.contains(e.target.tagName.toLowerCase())) {
serializedEvent['target'] = {'value': e.target.value};
if (e.target is InputElement) {
serializedEvent['target']['files'] = e.target.files;
}
}
return serializedEvent;
}

View File

@ -0,0 +1,86 @@
import {Set} from 'angular2/src/facade/collection';
import {isPresent} from 'angular2/src/facade/lang';
const MOUSE_EVENT_PROPERTIES = [
"altKey",
"button",
"clientX",
"clientY",
"metaKey",
"movementX",
"movementY",
"offsetX",
"offsetY",
"region",
"screenX",
"screenY",
"shiftKey"
];
const KEYBOARD_EVENT_PROPERTIES = [
'altkey',
'charCode',
'code',
'ctrlKey',
'isComposing',
'key',
'keyCode',
'location',
'metaKey',
'repeat',
'shiftKey',
'which'
];
const TRANSITION_EVENT_PROPERTIES = ['propertyName', 'elapsedTime', 'pseudoElement'];
const EVENT_PROPERTIES = ['type', 'bubbles', 'cancelable'];
const NODES_WITH_VALUE = new Set(
["input", "select", "option", "button", "li", "meter", "progress", "param", "textarea"]);
export function serializeGenericEvent(e: Event): {[key: string]: any} {
return serializeEvent(e, EVENT_PROPERTIES);
}
// TODO(jteplitz602): Allow users to specify the properties they need rather than always
// adding value and files #3374
export function serializeEventWithTarget(e: Event): {[key: string]: any} {
var serializedEvent = serializeEvent(e, EVENT_PROPERTIES);
return addTarget(e, serializedEvent);
}
export function serializeMouseEvent(e: MouseEvent): {[key: string]: any} {
return serializeEvent(e, MOUSE_EVENT_PROPERTIES);
}
export function serializeKeyboardEvent(e: KeyboardEvent): {[key: string]: any} {
var serializedEvent = serializeEvent(e, KEYBOARD_EVENT_PROPERTIES);
return addTarget(e, serializedEvent);
}
export function serializeTransitionEvent(e: TransitionEvent): {[key: string]: any} {
var serializedEvent = serializeEvent(e, TRANSITION_EVENT_PROPERTIES);
return addTarget(e, serializedEvent);
}
// TODO(jteplitz602): #3374. See above.
function addTarget(e: Event, serializedEvent: {[key: string]: any}): {[key: string]: any} {
if (NODES_WITH_VALUE.has((<HTMLElement>e.target).tagName.toLowerCase())) {
var target = <HTMLInputElement>e.target;
serializedEvent['target'] = {'value': target.value};
if (isPresent(target.files)) {
serializedEvent['target']['files'] = target.files;
}
}
return serializedEvent;
}
function serializeEvent(e: any, properties: string[]): {[key: string]: any} {
var serialized = {};
for (var i = 0; i < properties.length; i++) {
var prop = properties[i];
serialized[prop] = e[prop];
}
return serialized;
}

View File

@ -0,0 +1,56 @@
import {
BrowserPlatformLocation
} from 'angular2/src/platform/browser/location/browser_platform_location';
import {UrlChangeListener} from 'angular2/platform/common';
import {Injectable} from 'angular2/src/core/di';
import {ROUTER_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
import {
ServiceMessageBrokerFactory,
ServiceMessageBroker
} from 'angular2/src/web_workers/shared/service_message_broker';
import {PRIMITIVE, Serializer} from 'angular2/src/web_workers/shared/serializer';
import {bind} from './bind';
import {LocationType} from 'angular2/src/web_workers/shared/serialized_types';
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
import {EventEmitter, ObservableWrapper, PromiseWrapper} from 'angular2/src/facade/async';
@Injectable()
export class MessageBasedPlatformLocation {
private _channelSink: EventEmitter<Object>;
private _broker: ServiceMessageBroker;
constructor(private _brokerFactory: ServiceMessageBrokerFactory,
private _platformLocation: BrowserPlatformLocation, bus: MessageBus,
private _serializer: Serializer) {
this._platformLocation.onPopState(<UrlChangeListener>bind(this._sendUrlChangeEvent, this));
this._platformLocation.onHashChange(<UrlChangeListener>bind(this._sendUrlChangeEvent, this));
this._broker = this._brokerFactory.createMessageBroker(ROUTER_CHANNEL);
this._channelSink = bus.to(ROUTER_CHANNEL);
}
start(): void {
this._broker.registerMethod("getLocation", null, bind(this._getLocation, this), LocationType);
this._broker.registerMethod("setPathname", [PRIMITIVE], bind(this._setPathname, this));
this._broker.registerMethod("pushState", [PRIMITIVE, PRIMITIVE, PRIMITIVE],
bind(this._platformLocation.pushState, this._platformLocation));
this._broker.registerMethod("replaceState", [PRIMITIVE, PRIMITIVE, PRIMITIVE],
bind(this._platformLocation.replaceState, this._platformLocation));
this._broker.registerMethod("forward", null,
bind(this._platformLocation.forward, this._platformLocation));
this._broker.registerMethod("back", null,
bind(this._platformLocation.back, this._platformLocation));
}
private _getLocation(): Promise<Location> {
return PromiseWrapper.resolve(this._platformLocation.location);
}
private _sendUrlChangeEvent(e: Event): void {
let loc = this._serializer.serialize(this._platformLocation.location, LocationType);
let serializedEvent = {'type': e.type};
ObservableWrapper.callEmit(this._channelSink, {'event': serializedEvent, 'location': loc});
}
private _setPathname(pathname: string): void { this._platformLocation.pathname = pathname; }
}

View File

@ -0,0 +1,174 @@
import {Injectable} from 'angular2/src/core/di';
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
import {Serializer, PRIMITIVE, RenderStoreObject} from 'angular2/src/web_workers/shared/serializer';
import {RootRenderer, Renderer, RenderComponentType} from 'angular2/src/core/render/api';
import {EVENT_CHANNEL, RENDERER_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
import {Type} from 'angular2/src/facade/lang';
import {bind} from './bind';
import {EventDispatcher} from 'angular2/src/web_workers/ui/event_dispatcher';
import {RenderStore} from 'angular2/src/web_workers/shared/render_store';
import {ServiceMessageBrokerFactory} from 'angular2/src/web_workers/shared/service_message_broker';
@Injectable()
export class MessageBasedRenderer {
private _eventDispatcher: EventDispatcher;
constructor(private _brokerFactory: ServiceMessageBrokerFactory, private _bus: MessageBus,
private _serializer: Serializer, private _renderStore: RenderStore,
private _rootRenderer: RootRenderer) {}
start(): void {
var broker = this._brokerFactory.createMessageBroker(RENDERER_CHANNEL);
this._bus.initChannel(EVENT_CHANNEL);
this._eventDispatcher = new EventDispatcher(this._bus.to(EVENT_CHANNEL), this._serializer);
broker.registerMethod("renderComponent", [RenderComponentType, PRIMITIVE],
bind(this._renderComponent, this));
broker.registerMethod("selectRootElement", [RenderStoreObject, PRIMITIVE, PRIMITIVE],
bind(this._selectRootElement, this));
broker.registerMethod("createElement",
[RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
bind(this._createElement, this));
broker.registerMethod("createViewRoot", [RenderStoreObject, RenderStoreObject, PRIMITIVE],
bind(this._createViewRoot, this));
broker.registerMethod("createTemplateAnchor", [RenderStoreObject, RenderStoreObject, PRIMITIVE],
bind(this._createTemplateAnchor, this));
broker.registerMethod("createText",
[RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
bind(this._createText, this));
broker.registerMethod("projectNodes", [RenderStoreObject, RenderStoreObject, RenderStoreObject],
bind(this._projectNodes, this));
broker.registerMethod("attachViewAfter",
[RenderStoreObject, RenderStoreObject, RenderStoreObject],
bind(this._attachViewAfter, this));
broker.registerMethod("detachView", [RenderStoreObject, RenderStoreObject],
bind(this._detachView, this));
broker.registerMethod("destroyView", [RenderStoreObject, RenderStoreObject, RenderStoreObject],
bind(this._destroyView, this));
broker.registerMethod("setElementProperty",
[RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
bind(this._setElementProperty, this));
broker.registerMethod("setElementAttribute",
[RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
bind(this._setElementAttribute, this));
broker.registerMethod("setBindingDebugInfo",
[RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
bind(this._setBindingDebugInfo, this));
broker.registerMethod("setElementClass",
[RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
bind(this._setElementClass, this));
broker.registerMethod("setElementStyle",
[RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
bind(this._setElementStyle, this));
broker.registerMethod("invokeElementMethod",
[RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
bind(this._invokeElementMethod, this));
broker.registerMethod("setText", [RenderStoreObject, RenderStoreObject, PRIMITIVE],
bind(this._setText, this));
broker.registerMethod("listen", [RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
bind(this._listen, this));
broker.registerMethod("listenGlobal", [RenderStoreObject, PRIMITIVE, PRIMITIVE, PRIMITIVE],
bind(this._listenGlobal, this));
broker.registerMethod("listenDone", [RenderStoreObject, RenderStoreObject],
bind(this._listenDone, this));
}
private _renderComponent(renderComponentType: RenderComponentType, rendererId: number) {
var renderer = this._rootRenderer.renderComponent(renderComponentType);
this._renderStore.store(renderer, rendererId);
}
private _selectRootElement(renderer: Renderer, selector: string, elId: number) {
this._renderStore.store(renderer.selectRootElement(selector, null), elId);
}
private _createElement(renderer: Renderer, parentElement: any, name: string, elId: number) {
this._renderStore.store(renderer.createElement(parentElement, name, null), elId);
}
private _createViewRoot(renderer: Renderer, hostElement: any, elId: number) {
var viewRoot = renderer.createViewRoot(hostElement);
if (this._renderStore.serialize(hostElement) !== elId) {
this._renderStore.store(viewRoot, elId);
}
}
private _createTemplateAnchor(renderer: Renderer, parentElement: any, elId: number) {
this._renderStore.store(renderer.createTemplateAnchor(parentElement, null), elId);
}
private _createText(renderer: Renderer, parentElement: any, value: string, elId: number) {
this._renderStore.store(renderer.createText(parentElement, value, null), elId);
}
private _projectNodes(renderer: Renderer, parentElement: any, nodes: any[]) {
renderer.projectNodes(parentElement, nodes);
}
private _attachViewAfter(renderer: Renderer, node: any, viewRootNodes: any[]) {
renderer.attachViewAfter(node, viewRootNodes);
}
private _detachView(renderer: Renderer, viewRootNodes: any[]) {
renderer.detachView(viewRootNodes);
}
private _destroyView(renderer: Renderer, hostElement: any, viewAllNodes: any[]) {
renderer.destroyView(hostElement, viewAllNodes);
for (var i = 0; i < viewAllNodes.length; i++) {
this._renderStore.remove(viewAllNodes[i]);
}
}
private _setElementProperty(renderer: Renderer, renderElement: any, propertyName: string,
propertyValue: any) {
renderer.setElementProperty(renderElement, propertyName, propertyValue);
}
private _setElementAttribute(renderer: Renderer, renderElement: any, attributeName: string,
attributeValue: string) {
renderer.setElementAttribute(renderElement, attributeName, attributeValue);
}
private _setBindingDebugInfo(renderer: Renderer, renderElement: any, propertyName: string,
propertyValue: string) {
renderer.setBindingDebugInfo(renderElement, propertyName, propertyValue);
}
private _setElementClass(renderer: Renderer, renderElement: any, className: string,
isAdd: boolean) {
renderer.setElementClass(renderElement, className, isAdd);
}
private _setElementStyle(renderer: Renderer, renderElement: any, styleName: string,
styleValue: string) {
renderer.setElementStyle(renderElement, styleName, styleValue);
}
private _invokeElementMethod(renderer: Renderer, renderElement: any, methodName: string,
args: any[]) {
renderer.invokeElementMethod(renderElement, methodName, args);
}
private _setText(renderer: Renderer, renderNode: any, text: string) {
renderer.setText(renderNode, text);
}
private _listen(renderer: Renderer, renderElement: any, eventName: string, unlistenId: number) {
var unregisterCallback = renderer.listen(renderElement, eventName,
(event) => this._eventDispatcher.dispatchRenderEvent(
renderElement, null, eventName, event));
this._renderStore.store(unregisterCallback, unlistenId);
}
private _listenGlobal(renderer: Renderer, eventTarget: string, eventName: string,
unlistenId: number) {
var unregisterCallback = renderer.listenGlobal(
eventTarget, eventName,
(event) => this._eventDispatcher.dispatchRenderEvent(null, eventTarget, eventName, event));
this._renderStore.store(unregisterCallback, unlistenId);
}
private _listenDone(renderer: Renderer, unlistenCallback: Function) { unlistenCallback(); }
}

View File

@ -0,0 +1,19 @@
import {MessageBasedPlatformLocation} from './platform_location';
import {
BrowserPlatformLocation
} from 'angular2/src/platform/browser/location/browser_platform_location';
import {APP_INITIALIZER, Provider, Injector, NgZone} from 'angular2/core';
export const WORKER_RENDER_ROUTER = /*@ts2dart_const*/[
MessageBasedPlatformLocation,
BrowserPlatformLocation,
/* @ts2dart_Provider */ {provide: APP_INITIALIZER, useFactory: initRouterListeners, multi: true, deps: [Injector]}
];
function initRouterListeners(injector: Injector): () => void {
return () => {
let zone = injector.get(NgZone);
zone.runGuarded(() => injector.get(MessageBasedPlatformLocation).start());
};
}

View File

@ -0,0 +1,16 @@
import {Injectable} from 'angular2/src/core/di';
import {PRIMITIVE} from 'angular2/src/web_workers/shared/serializer';
import {XHR_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
import {XHR} from 'angular2/src/compiler/xhr';
import {ServiceMessageBrokerFactory} from 'angular2/src/web_workers/shared/service_message_broker';
import {bind} from './bind';
@Injectable()
export class MessageBasedXHRImpl {
constructor(private _brokerFactory: ServiceMessageBrokerFactory, private _xhr: XHR) {}
start(): void {
var broker = this._brokerFactory.createMessageBroker(XHR_CHANNEL);
broker.registerMethod("get", [PRIMITIVE], bind(this._xhr.get, this._xhr), PRIMITIVE);
}
}

View File

@ -0,0 +1,73 @@
library angular2.src.web_workers.worker.event_deserializer;
class GenericEvent {
Map<String, dynamic> properties;
EventTarget _target = null;
GenericEvent(this.properties);
bool get bubbles => properties['bubbles'];
bool get cancelable => properties['cancelable'];
bool get defaultPrevented => properties['defaultPrevented'];
int get eventPhase => properties['eventPhase'];
int get timeStamp => properties['timeStamp'];
String get type => properties['type'];
bool get altKey => properties['altKey'];
int get charCode => properties['charCode'];
bool get ctrlKey => properties['ctrlKey'];
int get detail => properties['detail'];
int get keyCode => properties['keyCode'];
int get keyLocation => properties['keyLocation'];
Point get layer => _getPoint('layer');
int get location => properties['location'];
bool get repeat => properties['repeat'];
bool get shiftKey => properties['shiftKey'];
int get button => properties['button'];
Point get client => _getPoint('client');
bool get metaKey => properties['metaKey'];
Point get offset => _getPoint('offset');
Point get page => _getPoint('page');
Point get screen => _getPoint('screen');
String get propertyName => properties['propertyName'];
num get elapsedTime => properties['elapsedTime'];
String get pseudoElement => properties['pseudoElement'];
EventTarget get target {
if (_target != null) {
return _target;
} else if (properties.containsKey("target")) {
_target = new EventTarget(properties['target']);
return _target;
} else {
return null;
}
}
dynamic _getPoint(name) {
Map<String, dynamic> point = properties[name];
return new Point(point['x'], point['y'], point['magnitude']);
}
}
class EventTarget {
dynamic value;
EventTarget(Map<String, dynamic> properties) {
value = properties['value'];
}
}
class Point {
int x;
int y;
double magnitude;
Point(this.x, this.y, this.magnitude);
}
GenericEvent deserializeGenericEvent(Map<String, dynamic> serializedEvent) {
return new GenericEvent(serializedEvent);
}

View File

@ -0,0 +1,6 @@
// no deserialization is necessary in TS.
// This is only here to match dart interface
export function deserializeGenericEvent(
serializedEvent: {[key: string]: any}): {[key: string]: any} {
return serializedEvent;
}

View File

@ -0,0 +1,132 @@
import {Injectable} from 'angular2/src/core/di';
import {
FnArg,
UiArguments,
ClientMessageBroker,
ClientMessageBrokerFactory
} from 'angular2/src/web_workers/shared/client_message_broker';
import {PlatformLocation, UrlChangeEvent, UrlChangeListener} from 'angular2/platform/common';
import {ROUTER_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
import {LocationType} from 'angular2/src/web_workers/shared/serialized_types';
import {PromiseWrapper, EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
import {BaseException} from 'angular2/src/facade/exceptions';
import {PRIMITIVE, Serializer} from 'angular2/src/web_workers/shared/serializer';
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
import {StringMapWrapper} from 'angular2/src/facade/collection';
import {StringWrapper} from 'angular2/src/facade/lang';
import {deserializeGenericEvent} from './event_deserializer';
@Injectable()
export class WebWorkerPlatformLocation extends PlatformLocation {
private _broker: ClientMessageBroker;
private _popStateListeners: Array<Function> = [];
private _hashChangeListeners: Array<Function> = [];
private _location: LocationType = null;
private _channelSource: EventEmitter<Object>;
constructor(brokerFactory: ClientMessageBrokerFactory, bus: MessageBus,
private _serializer: Serializer) {
super();
this._broker = brokerFactory.createMessageBroker(ROUTER_CHANNEL);
this._channelSource = bus.from(ROUTER_CHANNEL);
ObservableWrapper.subscribe(this._channelSource, (msg: {[key: string]: any}) => {
var listeners: Array<Function> = null;
if (StringMapWrapper.contains(msg, 'event')) {
let type: string = msg['event']['type'];
if (StringWrapper.equals(type, "popstate")) {
listeners = this._popStateListeners;
} else if (StringWrapper.equals(type, "hashchange")) {
listeners = this._hashChangeListeners;
}
if (listeners !== null) {
let e = deserializeGenericEvent(msg['event']);
// There was a popState or hashChange event, so the location object thas been updated
this._location = this._serializer.deserialize(msg['location'], LocationType);
listeners.forEach((fn: Function) => fn(e));
}
}
});
}
/** @internal **/
init(): Promise<boolean> {
var args: UiArguments = new UiArguments("getLocation");
var locationPromise: Promise<LocationType> = this._broker.runOnService(args, LocationType);
return PromiseWrapper.then(locationPromise, (val: LocationType): boolean => {
this._location = val;
return true;
}, (err): boolean => { throw new BaseException(err); });
}
getBaseHrefFromDOM(): string {
throw new BaseException(
"Attempt to get base href from DOM from WebWorker. You must either provide a value for the APP_BASE_HREF token through DI or use the hash location strategy.");
}
onPopState(fn: UrlChangeListener): void { this._popStateListeners.push(fn); }
onHashChange(fn: UrlChangeListener): void { this._hashChangeListeners.push(fn); }
get pathname(): string {
if (this._location === null) {
return null;
}
return this._location.pathname;
}
get search(): string {
if (this._location === null) {
return null;
}
return this._location.search;
}
get hash(): string {
if (this._location === null) {
return null;
}
return this._location.hash;
}
set pathname(newPath: string) {
if (this._location === null) {
throw new BaseException("Attempt to set pathname before value is obtained from UI");
}
this._location.pathname = newPath;
var fnArgs = [new FnArg(newPath, PRIMITIVE)];
var args = new UiArguments("setPathname", fnArgs);
this._broker.runOnService(args, null);
}
pushState(state: any, title: string, url: string): void {
var fnArgs =
[new FnArg(state, PRIMITIVE), new FnArg(title, PRIMITIVE), new FnArg(url, PRIMITIVE)];
var args = new UiArguments("pushState", fnArgs);
this._broker.runOnService(args, null);
}
replaceState(state: any, title: string, url: string): void {
var fnArgs =
[new FnArg(state, PRIMITIVE), new FnArg(title, PRIMITIVE), new FnArg(url, PRIMITIVE)];
var args = new UiArguments("replaceState", fnArgs);
this._broker.runOnService(args, null);
}
forward(): void {
var args = new UiArguments("forward");
this._broker.runOnService(args, null);
}
back(): void {
var args = new UiArguments("back");
this._broker.runOnService(args, null);
}
}

View File

@ -0,0 +1,279 @@
import {
Renderer,
RootRenderer,
RenderComponentType,
RenderDebugInfo
} from 'angular2/src/core/render/api';
import {
ClientMessageBroker,
ClientMessageBrokerFactory,
FnArg,
UiArguments
} from "angular2/src/web_workers/shared/client_message_broker";
import {isPresent, isBlank, print} from "angular2/src/facade/lang";
import {ListWrapper} from 'angular2/src/facade/collection';
import {Injectable} from "angular2/src/core/di";
import {RenderStore} from 'angular2/src/web_workers/shared/render_store';
import {RENDERER_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
import {Serializer, RenderStoreObject} from 'angular2/src/web_workers/shared/serializer';
import {EVENT_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
import {deserializeGenericEvent} from './event_deserializer';
@Injectable()
export class WebWorkerRootRenderer implements RootRenderer {
private _messageBroker;
public globalEvents: NamedEventEmitter = new NamedEventEmitter();
private _componentRenderers: Map<string, WebWorkerRenderer> =
new Map<string, WebWorkerRenderer>();
constructor(messageBrokerFactory: ClientMessageBrokerFactory, bus: MessageBus,
private _serializer: Serializer, private _renderStore: RenderStore) {
this._messageBroker = messageBrokerFactory.createMessageBroker(RENDERER_CHANNEL);
bus.initChannel(EVENT_CHANNEL);
var source = bus.from(EVENT_CHANNEL);
ObservableWrapper.subscribe(source, (message) => this._dispatchEvent(message));
}
private _dispatchEvent(message: {[key: string]: any}): void {
var eventName = message['eventName'];
var target = message['eventTarget'];
var event = deserializeGenericEvent(message['event']);
if (isPresent(target)) {
this.globalEvents.dispatchEvent(eventNameWithTarget(target, eventName), event);
} else {
var element =
<WebWorkerRenderNode>this._serializer.deserialize(message['element'], RenderStoreObject);
element.events.dispatchEvent(eventName, event);
}
}
renderComponent(componentType: RenderComponentType): Renderer {
var result = this._componentRenderers.get(componentType.id);
if (isBlank(result)) {
result = new WebWorkerRenderer(this, componentType);
this._componentRenderers.set(componentType.id, result);
var id = this._renderStore.allocateId();
this._renderStore.store(result, id);
this.runOnService('renderComponent', [
new FnArg(componentType, RenderComponentType),
new FnArg(result, RenderStoreObject),
]);
}
return result;
}
runOnService(fnName: string, fnArgs: FnArg[]) {
var args = new UiArguments(fnName, fnArgs);
this._messageBroker.runOnService(args, null);
}
allocateNode(): WebWorkerRenderNode {
var result = new WebWorkerRenderNode();
var id = this._renderStore.allocateId();
this._renderStore.store(result, id);
return result;
}
allocateId(): number { return this._renderStore.allocateId(); }
destroyNodes(nodes: any[]) {
for (var i = 0; i < nodes.length; i++) {
this._renderStore.remove(nodes[i]);
}
}
}
export class WebWorkerRenderer implements Renderer, RenderStoreObject {
constructor(private _rootRenderer: WebWorkerRootRenderer,
private _componentType: RenderComponentType) {}
private _runOnService(fnName: string, fnArgs: FnArg[]) {
var fnArgsWithRenderer = [new FnArg(this, RenderStoreObject)].concat(fnArgs);
this._rootRenderer.runOnService(fnName, fnArgsWithRenderer);
}
selectRootElement(selectorOrNode: string, debugInfo: RenderDebugInfo): any {
var node = this._rootRenderer.allocateNode();
this._runOnService('selectRootElement',
[new FnArg(selectorOrNode, null), new FnArg(node, RenderStoreObject)]);
return node;
}
createElement(parentElement: any, name: string, debugInfo: RenderDebugInfo): any {
var node = this._rootRenderer.allocateNode();
this._runOnService('createElement', [
new FnArg(parentElement, RenderStoreObject),
new FnArg(name, null),
new FnArg(node, RenderStoreObject)
]);
return node;
}
createViewRoot(hostElement: any): any {
var viewRoot = this._componentType.encapsulation === ViewEncapsulation.Native ?
this._rootRenderer.allocateNode() :
hostElement;
this._runOnService(
'createViewRoot',
[new FnArg(hostElement, RenderStoreObject), new FnArg(viewRoot, RenderStoreObject)]);
return viewRoot;
}
createTemplateAnchor(parentElement: any, debugInfo: RenderDebugInfo): any {
var node = this._rootRenderer.allocateNode();
this._runOnService(
'createTemplateAnchor',
[new FnArg(parentElement, RenderStoreObject), new FnArg(node, RenderStoreObject)]);
return node;
}
createText(parentElement: any, value: string, debugInfo: RenderDebugInfo): any {
var node = this._rootRenderer.allocateNode();
this._runOnService('createText', [
new FnArg(parentElement, RenderStoreObject),
new FnArg(value, null),
new FnArg(node, RenderStoreObject)
]);
return node;
}
projectNodes(parentElement: any, nodes: any[]) {
this._runOnService(
'projectNodes',
[new FnArg(parentElement, RenderStoreObject), new FnArg(nodes, RenderStoreObject)]);
}
attachViewAfter(node: any, viewRootNodes: any[]) {
this._runOnService(
'attachViewAfter',
[new FnArg(node, RenderStoreObject), new FnArg(viewRootNodes, RenderStoreObject)]);
}
detachView(viewRootNodes: any[]) {
this._runOnService('detachView', [new FnArg(viewRootNodes, RenderStoreObject)]);
}
destroyView(hostElement: any, viewAllNodes: any[]) {
this._runOnService(
'destroyView',
[new FnArg(hostElement, RenderStoreObject), new FnArg(viewAllNodes, RenderStoreObject)]);
this._rootRenderer.destroyNodes(viewAllNodes);
}
setElementProperty(renderElement: any, propertyName: string, propertyValue: any) {
this._runOnService('setElementProperty', [
new FnArg(renderElement, RenderStoreObject),
new FnArg(propertyName, null),
new FnArg(propertyValue, null)
]);
}
setElementAttribute(renderElement: any, attributeName: string, attributeValue: string) {
this._runOnService('setElementAttribute', [
new FnArg(renderElement, RenderStoreObject),
new FnArg(attributeName, null),
new FnArg(attributeValue, null)
]);
}
setBindingDebugInfo(renderElement: any, propertyName: string, propertyValue: string) {
this._runOnService('setBindingDebugInfo', [
new FnArg(renderElement, RenderStoreObject),
new FnArg(propertyName, null),
new FnArg(propertyValue, null)
]);
}
setElementClass(renderElement: any, className: string, isAdd: boolean) {
this._runOnService('setElementClass', [
new FnArg(renderElement, RenderStoreObject),
new FnArg(className, null),
new FnArg(isAdd, null)
]);
}
setElementStyle(renderElement: any, styleName: string, styleValue: string) {
this._runOnService('setElementStyle', [
new FnArg(renderElement, RenderStoreObject),
new FnArg(styleName, null),
new FnArg(styleValue, null)
]);
}
invokeElementMethod(renderElement: any, methodName: string, args: any[]) {
this._runOnService('invokeElementMethod', [
new FnArg(renderElement, RenderStoreObject),
new FnArg(methodName, null),
new FnArg(args, null)
]);
}
setText(renderNode: any, text: string) {
this._runOnService('setText',
[new FnArg(renderNode, RenderStoreObject), new FnArg(text, null)]);
}
listen(renderElement: WebWorkerRenderNode, name: string, callback: Function): Function {
renderElement.events.listen(name, callback);
var unlistenCallbackId = this._rootRenderer.allocateId();
this._runOnService('listen', [
new FnArg(renderElement, RenderStoreObject),
new FnArg(name, null),
new FnArg(unlistenCallbackId, null)
]);
return () => {
renderElement.events.unlisten(name, callback);
this._runOnService('listenDone', [new FnArg(unlistenCallbackId, null)]);
};
}
listenGlobal(target: string, name: string, callback: Function): Function {
this._rootRenderer.globalEvents.listen(eventNameWithTarget(target, name), callback);
var unlistenCallbackId = this._rootRenderer.allocateId();
this._runOnService(
'listenGlobal',
[new FnArg(target, null), new FnArg(name, null), new FnArg(unlistenCallbackId, null)]);
return () => {
this._rootRenderer.globalEvents.unlisten(eventNameWithTarget(target, name), callback);
this._runOnService('listenDone', [new FnArg(unlistenCallbackId, null)]);
};
}
}
export class NamedEventEmitter {
private _listeners: Map<string, Function[]>;
private _getListeners(eventName: string): Function[] {
if (isBlank(this._listeners)) {
this._listeners = new Map<string, Function[]>();
}
var listeners = this._listeners.get(eventName);
if (isBlank(listeners)) {
listeners = [];
this._listeners.set(eventName, listeners);
}
return listeners;
}
listen(eventName: string, callback: Function) { this._getListeners(eventName).push(callback); }
unlisten(eventName: string, callback: Function) {
ListWrapper.remove(this._getListeners(eventName), callback);
}
dispatchEvent(eventName: string, event: any) {
var listeners = this._getListeners(eventName);
for (var i = 0; i < listeners.length; i++) {
listeners[i](event);
}
}
}
function eventNameWithTarget(target: string, eventName: string): string {
return `${target}:${eventName}`;
}
export class WebWorkerRenderNode { events: NamedEventEmitter = new NamedEventEmitter(); }

View File

@ -0,0 +1,20 @@
import {ApplicationRef, Provider, NgZone, APP_INITIALIZER} from 'angular2/core';
import {PlatformLocation} from 'angular2/platform/common';
import {WebWorkerPlatformLocation} from './platform_location';
import {ROUTER_PROVIDERS_COMMON} from 'angular2/src/router/router_providers_common';
export var WORKER_APP_ROUTER = [
ROUTER_PROVIDERS_COMMON,
/* @ts2dart_Provider */ {provide: PlatformLocation, useClass: WebWorkerPlatformLocation},
{
provide: APP_INITIALIZER,
useFactory: (platformLocation: WebWorkerPlatformLocation, zone: NgZone) => () =>
initRouter(platformLocation, zone),
multi: true,
deps: [PlatformLocation, NgZone]
}
];
function initRouter(platformLocation: WebWorkerPlatformLocation, zone: NgZone): Promise<boolean> {
return zone.runGuarded(() => { return platformLocation.init(); });
}

View File

@ -0,0 +1,29 @@
import {Injectable} from 'angular2/src/core/di';
import {XHR} from 'angular2/src/compiler/xhr';
import {
FnArg,
UiArguments,
ClientMessageBroker,
ClientMessageBrokerFactory
} from 'angular2/src/web_workers/shared/client_message_broker';
import {XHR_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
/**
* Implementation of compiler/xhr that relays XHR requests to the UI side where they are sent
* and the result is proxied back to the worker
*/
@Injectable()
export class WebWorkerXHRImpl extends XHR {
private _messageBroker: ClientMessageBroker;
constructor(messageBrokerFactory: ClientMessageBrokerFactory) {
super();
this._messageBroker = messageBrokerFactory.createMessageBroker(XHR_CHANNEL);
}
get(url: string): Promise<string> {
var fnArgs: FnArg[] = [new FnArg(url, null)];
var args: UiArguments = new UiArguments("get", fnArgs);
return this._messageBroker.runOnService(args, String);
}
}

View File

@ -0,0 +1,37 @@
library angular2.src.platform.worker_app;
import 'package:angular2/src/core/zone/ng_zone.dart';
import 'package:angular2/src/platform/server/webworker_adapter.dart';
import 'package:angular2/src/platform/worker_app_common.dart';
import 'package:angular2/core.dart';
import 'package:angular2/src/web_workers/shared/isolate_message_bus.dart';
import 'package:angular2/src/web_workers/shared/message_bus.dart';
import 'dart:isolate';
const OpaqueToken RENDER_SEND_PORT = const OpaqueToken("RenderSendPort");
const List<dynamic> WORKER_APP_APPLICATION = const [
WORKER_APP_APPLICATION_COMMON,
const Provider(MessageBus,
useFactory: createMessageBus, deps: const [NgZone, RENDER_SEND_PORT]),
const Provider(APP_INITIALIZER, useValue: setupIsolate, multi: true)
];
MessageBus createMessageBus(NgZone zone, SendPort replyTo) {
ReceivePort rPort = new ReceivePort();
var sink = new WebWorkerMessageBusSink(replyTo, rPort);
var source = new IsolateMessageBusSource(rPort);
var bus = new IsolateMessageBus(sink, source);
bus.attachToZone(zone);
return bus;
}
setupIsolate() {
WebWorkerDomAdapter.makeCurrent();
}
class WebWorkerMessageBusSink extends IsolateMessageBusSink {
WebWorkerMessageBusSink(SendPort sPort, ReceivePort rPort) : super(sPort) {
sPort.send(rPort.sendPort);
}
}

View File

@ -0,0 +1,39 @@
import {NgZone} from 'angular2/src/core/zone/ng_zone';
import {Type, isPresent} from 'angular2/src/facade/lang';
import {Provider} from 'angular2/src/core/di';
import {Parse5DomAdapter} from 'angular2/src/platform/server/parse5_adapter';
import {
PostMessageBus,
PostMessageBusSink,
PostMessageBusSource
} from 'angular2/src/web_workers/shared/post_message_bus';
import {WORKER_APP_APPLICATION_COMMON} from './worker_app_common';
import {APP_INITIALIZER} from 'angular2/core';
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
import {COMPILER_PROVIDERS} from 'angular2/src/compiler/compiler';
// TODO(jteplitz602) remove this and compile with lib.webworker.d.ts (#3492)
let _postMessage = {
postMessage: (message: any, transferrables?:[ArrayBuffer]) => {
(<any>postMessage)(message, transferrables);
}
};
export const WORKER_APP_APPLICATION: Array<any /*Type | Provider | any[]*/> = [
WORKER_APP_APPLICATION_COMMON,
COMPILER_PROVIDERS,
/* @ts2dart_Provider */ {provide: MessageBus, useFactory: createMessageBus, deps: [NgZone]},
/* @ts2dart_Provider */ {provide: APP_INITIALIZER, useValue: setupWebWorker, multi: true}
];
function createMessageBus(zone: NgZone): MessageBus {
let sink = new PostMessageBusSink(_postMessage);
let source = new PostMessageBusSource();
let bus = new PostMessageBus(sink, source);
bus.attachToZone(zone);
return bus;
}
function setupWebWorker(): void {
Parse5DomAdapter.makeCurrent();
}

View File

@ -0,0 +1,64 @@
import {XHR} from 'angular2/src/compiler/xhr';
import {WebWorkerXHRImpl} from 'angular2/src/web_workers/worker/xhr_impl';
import {WebWorkerRootRenderer} from 'angular2/src/web_workers/worker/renderer';
import {print} from 'angular2/src/facade/lang';
import {RootRenderer} from 'angular2/src/core/render/api';
import {
PLATFORM_DIRECTIVES,
PLATFORM_PIPES,
ExceptionHandler,
APPLICATION_COMMON_PROVIDERS,
PLATFORM_COMMON_PROVIDERS,
OpaqueToken
} from 'angular2/core';
import {COMMON_DIRECTIVES, COMMON_PIPES, FORM_PROVIDERS} from "angular2/common";
import {
ClientMessageBrokerFactory,
ClientMessageBrokerFactory_
} from 'angular2/src/web_workers/shared/client_message_broker';
import {
ServiceMessageBrokerFactory,
ServiceMessageBrokerFactory_
} from 'angular2/src/web_workers/shared/service_message_broker';
import {Serializer} from "angular2/src/web_workers/shared/serializer";
import {ON_WEB_WORKER} from "angular2/src/web_workers/shared/api";
import {RenderStore} from 'angular2/src/web_workers/shared/render_store';
class PrintLogger {
log = print;
logError = print;
logGroup = print;
logGroupEnd() {}
}
export const WORKER_APP_PLATFORM_MARKER =
/*@ts2dart_const*/ new OpaqueToken('WorkerAppPlatformMarker');
export const WORKER_APP_PLATFORM: Array<any /*Type | Provider | any[]*/> =
/*@ts2dart_const*/[
PLATFORM_COMMON_PROVIDERS,
/*@ts2dart_const*/ (
/* @ts2dart_Provider */ {provide: WORKER_APP_PLATFORM_MARKER, useValue: true})
];
export const WORKER_APP_APPLICATION_COMMON: Array<any /*Type | Provider | any[]*/> =
/*@ts2dart_const*/[
APPLICATION_COMMON_PROVIDERS,
FORM_PROVIDERS,
Serializer,
/* @ts2dart_Provider */ {provide: PLATFORM_PIPES, useValue: COMMON_PIPES, multi: true},
/* @ts2dart_Provider */ {provide: PLATFORM_DIRECTIVES, useValue: COMMON_DIRECTIVES, multi: true},
/* @ts2dart_Provider */ {provide: ClientMessageBrokerFactory, useClass: ClientMessageBrokerFactory_},
/* @ts2dart_Provider */ {provide: ServiceMessageBrokerFactory, useClass: ServiceMessageBrokerFactory_},
WebWorkerRootRenderer,
/* @ts2dart_Provider */ {provide: RootRenderer, useExisting: WebWorkerRootRenderer},
/* @ts2dart_Provider */ {provide: ON_WEB_WORKER, useValue: true},
RenderStore,
/* @ts2dart_Provider */ {provide: ExceptionHandler, useFactory: _exceptionHandler, deps: []},
WebWorkerXHRImpl,
/* @ts2dart_Provider */ {provide: XHR, useExisting: WebWorkerXHRImpl}
];
function _exceptionHandler(): ExceptionHandler {
return new ExceptionHandler(new PrintLogger());
}

View File

@ -0,0 +1,64 @@
library angular2.src.platform.worker_render;
import 'package:angular2/src/platform/worker_render_common.dart'
show
WORKER_RENDER_APPLICATION_COMMON,
WORKER_RENDER_MESSAGING_PROVIDERS,
WORKER_SCRIPT,
initializeGenericWorkerRenderer;
import 'package:angular2/src/web_workers/shared/isolate_message_bus.dart';
import 'package:angular2/src/web_workers/shared/message_bus.dart';
import 'package:angular2/core.dart';
import 'package:angular2/src/core/di.dart';
import 'dart:isolate';
import 'dart:async';
const WORKER_RENDER_APP = WORKER_RENDER_APPLICATION_COMMON;
Future<List> initIsolate(String scriptUri) async {
var instance = await spawnIsolate(Uri.parse(scriptUri));
return [
WORKER_RENDER_APPLICATION_COMMON,
new Provider(WebWorkerInstance, useValue: instance),
new Provider(APP_INITIALIZER,
useFactory: (injector) => () => initializeGenericWorkerRenderer(injector),
multi: true,
deps: [Injector]),
new Provider(MessageBus, useValue: instance.bus)
];
}
/**
* Spawns a new class and initializes the WebWorkerInstance
*/
Future<WebWorkerInstance> spawnIsolate(Uri uri) async {
var receivePort = new ReceivePort();
var isolateEndSendPort = receivePort.sendPort;
var isolate = await Isolate.spawnUri(uri, const [], isolateEndSendPort);
var source = new UIMessageBusSource(receivePort);
var sendPort = await source.sink;
var sink = new IsolateMessageBusSink(sendPort);
var bus = new IsolateMessageBus(sink, source);
return new WebWorkerInstance(isolate, bus);
}
class UIMessageBusSource extends IsolateMessageBusSource {
UIMessageBusSource(ReceivePort port) : super(port);
Future<SendPort> get sink => stream.firstWhere((message) {
return message is SendPort;
});
}
/**
* Wrapper class that exposes the Isolate
* and underlying {@link MessageBus} for lower level message passing.
*/
class WebWorkerInstance {
Isolate worker;
MessageBus bus;
WebWorkerInstance(this.worker, this.bus);
}

View File

@ -0,0 +1,78 @@
import {
PostMessageBus,
PostMessageBusSink,
PostMessageBusSource
} from 'angular2/src/web_workers/shared/post_message_bus';
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
import {APP_INITIALIZER} from 'angular2/core';
import {Injector, Injectable, Provider} from 'angular2/src/core/di';
import {MessageBasedRenderer} from 'angular2/src/web_workers/ui/renderer';
import {MessageBasedXHRImpl} from 'angular2/src/web_workers/ui/xhr_impl';
import {
WORKER_RENDER_APPLICATION_COMMON,
WORKER_RENDER_MESSAGING_PROVIDERS,
WORKER_SCRIPT,
initializeGenericWorkerRenderer
} from 'angular2/src/platform/worker_render_common';
import {BaseException} from 'angular2/src/facade/exceptions';
/**
* Wrapper class that exposes the Worker
* and underlying {@link MessageBus} for lower level message passing.
*/
@Injectable()
export class WebWorkerInstance {
public worker: Worker;
public bus: MessageBus;
/** @internal */
public init(worker: Worker, bus: MessageBus) {
this.worker = worker;
this.bus = bus;
}
}
/**
* An array of providers that should be passed into `application()` when initializing a new Worker.
*/
export const WORKER_RENDER_APPLICATION: Array<any /*Type | Provider | any[]*/> = /*@ts2dart_const*/[
WORKER_RENDER_APPLICATION_COMMON, WebWorkerInstance,
/*@ts2dart_Provider*/ {
provide: APP_INITIALIZER,
useFactory: (injector => () => initWebWorkerApplication(injector)),
multi: true,
deps: [Injector]
},
/*@ts2dart_Provider*/ {
provide: MessageBus,
useFactory: (instance) => instance.bus,
deps: [WebWorkerInstance]
}
];
function initWebWorkerApplication(injector: Injector): void {
var scriptUri: string;
try {
scriptUri = injector.get(WORKER_SCRIPT);
} catch (e) {
throw new BaseException(
"You must provide your WebWorker's initialization script with the WORKER_SCRIPT token");
}
let instance = injector.get(WebWorkerInstance);
spawnWebWorker(scriptUri, instance);
initializeGenericWorkerRenderer(injector);
}
/**
* Spawns a new class and initializes the WebWorkerInstance
*/
function spawnWebWorker(uri: string, instance: WebWorkerInstance): void {
var webWorker: Worker = new Worker(uri);
var sink = new PostMessageBusSink(webWorker);
var source = new PostMessageBusSource(webWorker);
var bus = new PostMessageBus(sink, source);
instance.init(webWorker, bus);
}

View File

@ -0,0 +1,129 @@
import {IS_DART} from 'angular2/src/facade/lang';
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
import {NgZone} from 'angular2/src/core/zone/ng_zone';
import {
PLATFORM_DIRECTIVES,
PLATFORM_PIPES,
ComponentRef,
ExceptionHandler,
Reflector,
reflector,
APPLICATION_COMMON_PROVIDERS,
PLATFORM_COMMON_PROVIDERS,
RootRenderer,
PLATFORM_INITIALIZER,
APP_INITIALIZER
} from 'angular2/core';
import {EVENT_MANAGER_PLUGINS, EventManager} from 'angular2/platform/common_dom';
import {provide, Provider, Injector, OpaqueToken} from 'angular2/src/core/di';
// TODO change these imports once dom_adapter is moved out of core
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
import {DomEventsPlugin} from 'angular2/src/platform/dom/events/dom_events';
import {KeyEventsPlugin} from 'angular2/src/platform/dom/events/key_events';
import {DOCUMENT} from 'angular2/src/platform/dom/dom_tokens';
import {DomRootRenderer, DomRootRenderer_} from 'angular2/src/platform/dom/dom_renderer';
import {DomSharedStylesHost, SharedStylesHost} from 'angular2/src/platform/dom/shared_styles_host';
import {BrowserDetails} from 'angular2/src/animate/browser_details';
import {AnimationBuilder} from 'angular2/src/animate/animation_builder';
import {XHR} from 'angular2/compiler';
import {XHRImpl} from 'angular2/src/platform/browser/xhr_impl';
import {Testability} from 'angular2/src/core/testability/testability';
import {BrowserGetTestability} from 'angular2/src/platform/browser/testability';
import {BrowserDomAdapter} from './browser/browser_adapter';
import {wtfInit} from 'angular2/src/core/profile/wtf_init';
import {MessageBasedRenderer} from 'angular2/src/web_workers/ui/renderer';
import {MessageBasedXHRImpl} from 'angular2/src/web_workers/ui/xhr_impl';
import {
ServiceMessageBrokerFactory,
ServiceMessageBrokerFactory_
} from 'angular2/src/web_workers/shared/service_message_broker';
import {
ClientMessageBrokerFactory,
ClientMessageBrokerFactory_
} from 'angular2/src/web_workers/shared/client_message_broker';
import {
BrowserPlatformLocation
} from 'angular2/src/platform/browser/location/browser_platform_location';
import {Serializer} from 'angular2/src/web_workers/shared/serializer';
import {ON_WEB_WORKER} from 'angular2/src/web_workers/shared/api';
import {RenderStore} from 'angular2/src/web_workers/shared/render_store';
import {
HAMMER_GESTURE_CONFIG,
HammerGestureConfig,
HammerGesturesPlugin
} from 'angular2/src/platform/dom/events/hammer_gestures';
export const WORKER_SCRIPT: OpaqueToken = /*@ts2dart_const*/ new OpaqueToken("WebWorkerScript");
// Message based Worker classes that listen on the MessageBus
export const WORKER_RENDER_MESSAGING_PROVIDERS: Array<any /*Type | Provider | any[]*/> =
/*@ts2dart_const*/[MessageBasedRenderer, MessageBasedXHRImpl];
export const WORKER_RENDER_PLATFORM_MARKER =
/*@ts2dart_const*/ new OpaqueToken('WorkerRenderPlatformMarker');
export const WORKER_RENDER_PLATFORM: Array<any /*Type | Provider | any[]*/> = /*@ts2dart_const*/[
PLATFORM_COMMON_PROVIDERS,
/*@ts2dart_const*/ (/* @ts2dart_Provider */ {provide: WORKER_RENDER_PLATFORM_MARKER, useValue: true}),
/* @ts2dart_Provider */ {provide: PLATFORM_INITIALIZER, useValue: initWebWorkerRenderPlatform, multi: true}
];
/**
* A list of {@link Provider}s. To use the router in a Worker enabled application you must
* include these providers when setting up the render thread.
*/
export const WORKER_RENDER_ROUTER: Array<any /*Type | Provider | any[]*/> =
/*@ts2dart_const*/[BrowserPlatformLocation];
export const WORKER_RENDER_APPLICATION_COMMON: Array<any /*Type | Provider | any[]*/> =
/*@ts2dart_const*/[
APPLICATION_COMMON_PROVIDERS,
WORKER_RENDER_MESSAGING_PROVIDERS,
/* @ts2dart_Provider */ {provide: ExceptionHandler, useFactory: _exceptionHandler, deps: []},
/* @ts2dart_Provider */ {provide: DOCUMENT, useFactory: _document, deps: []},
// TODO(jteplitz602): Investigate if we definitely need EVENT_MANAGER on the render thread
// #5298
/* @ts2dart_Provider */ {provide: EVENT_MANAGER_PLUGINS, useClass: DomEventsPlugin, multi: true},
/* @ts2dart_Provider */ {provide: EVENT_MANAGER_PLUGINS, useClass: KeyEventsPlugin, multi: true},
/* @ts2dart_Provider */ {provide: EVENT_MANAGER_PLUGINS, useClass: HammerGesturesPlugin, multi: true},
/* @ts2dart_Provider */ {provide: HAMMER_GESTURE_CONFIG, useClass: HammerGestureConfig},
/* @ts2dart_Provider */ {provide: DomRootRenderer, useClass: DomRootRenderer_},
/* @ts2dart_Provider */ {provide: RootRenderer, useExisting: DomRootRenderer},
/* @ts2dart_Provider */ {provide: SharedStylesHost, useExisting: DomSharedStylesHost},
/* @ts2dart_Provider */ {provide: XHR, useClass: XHRImpl},
MessageBasedXHRImpl,
/* @ts2dart_Provider */ {provide: ServiceMessageBrokerFactory, useClass: ServiceMessageBrokerFactory_},
/* @ts2dart_Provider */ {provide: ClientMessageBrokerFactory, useClass: ClientMessageBrokerFactory_},
Serializer,
/* @ts2dart_Provider */ {provide: ON_WEB_WORKER, useValue: false},
RenderStore,
DomSharedStylesHost,
Testability,
BrowserDetails,
AnimationBuilder,
EventManager
];
export function initializeGenericWorkerRenderer(injector: Injector) {
var bus = injector.get(MessageBus);
let zone = injector.get(NgZone);
bus.attachToZone(zone);
zone.runGuarded(() => {
WORKER_RENDER_MESSAGING_PROVIDERS.forEach((token) => { injector.get(token).start(); });
});
}
export function initWebWorkerRenderPlatform(): void {
BrowserDomAdapter.makeCurrent();
wtfInit();
BrowserGetTestability.init();
}
function _exceptionHandler(): ExceptionHandler {
return new ExceptionHandler(DOM, !IS_DART);
}
function _document(): any {
return DOM.defaultDoc();
}

View File

@ -0,0 +1,51 @@
library angular2.platform.worker_app;
import "package:angular2/src/platform/worker_app_common.dart";
import "package:angular2/src/platform/worker_app.dart";
import 'package:angular2/core.dart';
import 'package:angular2/src/facade/lang.dart';
import 'dart:isolate';
import 'dart:async';
export "package:angular2/src/platform/worker_app_common.dart"
show WORKER_APP_PLATFORM, WORKER_APP_APPLICATION_COMMON;
export "package:angular2/src/core/angular_entrypoint.dart"
show AngularEntrypoint;
export "package:angular2/src/platform/worker_app.dart"
show WORKER_APP_APPLICATION, RENDER_SEND_PORT;
export 'package:angular2/src/web_workers/shared/client_message_broker.dart'
show ClientMessageBroker, ClientMessageBrokerFactory, FnArg, UiArguments;
export 'package:angular2/src/web_workers/shared/service_message_broker.dart'
show ReceivedMessage, ServiceMessageBroker, ServiceMessageBrokerFactory;
export 'package:angular2/src/web_workers/shared/serializer.dart' show PRIMITIVE;
export 'package:angular2/src/web_workers/shared/message_bus.dart';
export 'package:angular2/src/web_workers/worker/router_providers.dart'
show WORKER_APP_ROUTER;
PlatformRef _platform = null;
SendPort _renderSendPort = null;
PlatformRef workerAppPlatform(SendPort renderSendPort) {
if (isBlank(getPlatform())) {
createPlatform(ReflectiveInjector.resolveAndCreate([
WORKER_APP_PLATFORM,
new Provider(RENDER_SEND_PORT, useValue: renderSendPort)
]));
}
var platform = assertPlatform(WORKER_APP_PLATFORM_MARKER);
if (platform.injector.get(RENDER_SEND_PORT, null) != renderSendPort) {
throw 'Platform has already been created with a different SendPort. Please distroy it first.';
}
return platform;
}
Future<ComponentRef<dynamic>> bootstrapApp(
SendPort renderSendPort,
Type appComponentType,
[List<dynamic /*Type | Provider | any[]*/> customProviders]) {
var appInjector = ReflectiveInjector.resolveAndCreate([
WORKER_APP_APPLICATION,
isPresent(customProviders) ? customProviders : []
], workerAppPlatform(renderSendPort).injector);
return coreLoadAndBootstrap(appInjector, appComponentType);
}

View File

@ -0,0 +1,52 @@
import {isPresent, isBlank} from 'angular2/src/facade/lang';
import {
WORKER_APP_PLATFORM,
WORKER_APP_PLATFORM_MARKER
} from 'angular2/src/platform/worker_app_common';
import {WORKER_APP_APPLICATION} from 'angular2/src/platform/worker_app';
import {
PlatformRef,
Type,
ComponentRef,
ReflectiveInjector,
coreLoadAndBootstrap,
getPlatform,
createPlatform,
assertPlatform
} from 'angular2/core';
export {
WORKER_APP_PLATFORM,
WORKER_APP_APPLICATION_COMMON
} from 'angular2/src/platform/worker_app_common';
export {WORKER_APP_APPLICATION} from 'angular2/src/platform/worker_app';
export {
ClientMessageBroker,
ClientMessageBrokerFactory,
FnArg,
UiArguments
} from 'angular2/src/web_workers/shared/client_message_broker';
export {
ReceivedMessage,
ServiceMessageBroker,
ServiceMessageBrokerFactory
} from 'angular2/src/web_workers/shared/service_message_broker';
export {PRIMITIVE} from 'angular2/src/web_workers/shared/serializer';
export * from 'angular2/src/web_workers/shared/message_bus';
export {WORKER_APP_ROUTER} from 'angular2/src/web_workers/worker/router_providers';
export function workerAppPlatform(): PlatformRef {
if (isBlank(getPlatform())) {
createPlatform(ReflectiveInjector.resolveAndCreate(WORKER_APP_PLATFORM));
}
return assertPlatform(WORKER_APP_PLATFORM_MARKER);
}
export function bootstrapApp(
appComponentType: Type,
customProviders?: Array<any /*Type | Provider | any[]*/>): Promise<ComponentRef<any>> {
var appInjector = ReflectiveInjector.resolveAndCreate(
[WORKER_APP_APPLICATION, isPresent(customProviders) ? customProviders : []],
workerAppPlatform().injector);
return coreLoadAndBootstrap(appInjector, appComponentType);
}

View File

@ -0,0 +1,49 @@
library angular2.platform.worker_render;
import 'package:angular2/src/platform/worker_render.dart';
import 'package:angular2/src/platform/worker_render_common.dart';
import 'package:angular2/core.dart';
import 'package:angular2/src/facade/lang.dart';
import 'dart:async';
export 'package:angular2/src/platform/worker_render_common.dart'
show
WORKER_SCRIPT,
WORKER_RENDER_PLATFORM,
WORKER_RENDER_APPLICATION_COMMON,
initializeGenericWorkerRenderer;
export 'package:angular2/src/platform/worker_render.dart'
show WebWorkerInstance;
export '../src/web_workers/shared/client_message_broker.dart'
show ClientMessageBroker, ClientMessageBrokerFactory, FnArg, UiArguments;
export '../src/web_workers/shared/service_message_broker.dart'
show ReceivedMessage, ServiceMessageBroker, ServiceMessageBrokerFactory;
export '../src/web_workers/shared/serializer.dart' show PRIMITIVE;
export '../src/web_workers/shared/message_bus.dart';
export '../src/web_workers/ui/router_providers.dart' show WORKER_RENDER_ROUTER;
const WORKER_RENDER_APP = WORKER_RENDER_APPLICATION_COMMON;
PlatformRef workerRenderPlatform() {
if (isBlank(getPlatform())) {
createPlatform(ReflectiveInjector.resolveAndCreate(WORKER_RENDER_PLATFORM));
}
return assertPlatform(WORKER_RENDER_PLATFORM_MARKER);
}
Future<ApplicationRef> bootstrapRender(
String workerScriptUri,
[List<dynamic /*Type | Provider | any[]*/> customProviders]) {
return initIsolate(workerScriptUri).then( (appProviders) {
var appInjector = ReflectiveInjector.resolveAndCreate([
appProviders,
isPresent(customProviders) ? customProviders : []
], workerRenderPlatform().injector);
return appInjector.get(ApplicationRef);
});
}

View File

@ -0,0 +1,68 @@
import {isPresent, isBlank} from 'angular2/src/facade/lang';
import {PromiseWrapper} from 'angular2/src/facade/async';
import {
ApplicationRef,
PlatformRef,
ReflectiveInjector,
Provider,
getPlatform,
createPlatform,
assertPlatform
} from 'angular2/core';
import {WORKER_RENDER_APPLICATION} from 'angular2/src/platform/worker_render';
import {
WORKER_SCRIPT,
WORKER_RENDER_PLATFORM,
WORKER_RENDER_PLATFORM_MARKER
} from 'angular2/src/platform/worker_render_common';
export {
WORKER_SCRIPT,
WORKER_RENDER_PLATFORM,
initializeGenericWorkerRenderer,
WORKER_RENDER_APPLICATION_COMMON
} from 'angular2/src/platform/worker_render_common';
export {WORKER_RENDER_APPLICATION, WebWorkerInstance} from 'angular2/src/platform/worker_render';
export {
ClientMessageBroker,
ClientMessageBrokerFactory,
FnArg,
UiArguments
} from '../src/web_workers/shared/client_message_broker';
export {
ReceivedMessage,
ServiceMessageBroker,
ServiceMessageBrokerFactory
} from '../src/web_workers/shared/service_message_broker';
export {PRIMITIVE} from '../src/web_workers/shared/serializer';
export * from '../src/web_workers/shared/message_bus';
/**
* @deprecated Use WORKER_RENDER_APPLICATION
*/
export const WORKER_RENDER_APP = WORKER_RENDER_APPLICATION;
export {WORKER_RENDER_ROUTER} from 'angular2/src/web_workers/ui/router_providers';
export function workerRenderPlatform(): PlatformRef {
if (isBlank(getPlatform())) {
createPlatform(ReflectiveInjector.resolveAndCreate(WORKER_RENDER_PLATFORM));
}
return assertPlatform(WORKER_RENDER_PLATFORM_MARKER);
}
export function bootstrapRender(
workerScriptUri: string,
customProviders?: Array<any /*Type | Provider | any[]*/>): Promise<ApplicationRef> {
var pf = ReflectiveInjector.resolveAndCreate(WORKER_RENDER_PLATFORM);
var app = ReflectiveInjector.resolveAndCreate(
[
WORKER_RENDER_APPLICATION,
/* @ts2dart_Provider */ {provide: WORKER_SCRIPT, useValue: workerScriptUri},
isPresent(customProviders) ? customProviders : []
],
workerRenderPlatform().injector);
// Return a promise so that we keep the same semantics as Dart,
// and we might want to wait for the app side to come up
// in the future...
return PromiseWrapper.resolve(app.get(ApplicationRef));
}