feat(web-workers) Add WebWorker Renderer
Allows angular apps to be rendered from the webworker! Closes #3052, #3053, and #3097
This commit is contained in:
parent
21b988f554
commit
771c0170d9
|
@ -106,7 +106,7 @@ export class Parser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ParseAST {
|
export class _ParseAST {
|
||||||
index: int = 0;
|
index: int = 0;
|
||||||
constructor(public input: string, public location: any, public tokens: List<any>,
|
constructor(public input: string, public location: any, public tokens: List<any>,
|
||||||
public reflector: Reflector, public parseAction: boolean) {}
|
public reflector: Reflector, public parseAction: boolean) {}
|
||||||
|
|
|
@ -45,6 +45,7 @@ import {HammerGesturesPlugin} from 'angular2/src/render/dom/events/hammer_gestur
|
||||||
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
|
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
|
||||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||||
import {AppRootUrl} from 'angular2/src/services/app_root_url';
|
import {AppRootUrl} from 'angular2/src/services/app_root_url';
|
||||||
|
import {AnchorBasedAppRootUrl} from 'angular2/src/services/anchor_based_app_root_url';
|
||||||
import {
|
import {
|
||||||
ComponentRef,
|
ComponentRef,
|
||||||
DynamicComponentLoader
|
DynamicComponentLoader
|
||||||
|
@ -136,14 +137,14 @@ function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
|
||||||
StyleInliner,
|
StyleInliner,
|
||||||
DynamicComponentLoader,
|
DynamicComponentLoader,
|
||||||
Testability,
|
Testability,
|
||||||
AppRootUrl
|
AnchorBasedAppRootUrl,
|
||||||
|
bind(AppRootUrl).toAlias(AnchorBasedAppRootUrl)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function _createNgZone(): NgZone {
|
export function createNgZone(handler: ExceptionHandler): NgZone {
|
||||||
// bootstrapErrorReporter is needed because we cannot use custom exception handler
|
// bootstrapErrorReporter is needed because we cannot use custom exception handler
|
||||||
// configured via DI until the root Injector has been created.
|
// configured via DI until the root Injector has been created.
|
||||||
var handler = new ExceptionHandler();
|
|
||||||
var bootstrapErrorReporter = (exception, stackTrace) => handler.call(exception, stackTrace);
|
var bootstrapErrorReporter = (exception, stackTrace) => handler.call(exception, stackTrace);
|
||||||
var zone = new NgZone({enableLongStackTrace: assertionsEnabled()});
|
var zone = new NgZone({enableLongStackTrace: assertionsEnabled()});
|
||||||
zone.overrideOnErrorHandler(bootstrapErrorReporter);
|
zone.overrideOnErrorHandler(bootstrapErrorReporter);
|
||||||
|
@ -282,7 +283,7 @@ export function commonBootstrap(
|
||||||
BrowserDomAdapter.makeCurrent();
|
BrowserDomAdapter.makeCurrent();
|
||||||
var bootstrapProcess = PromiseWrapper.completer();
|
var bootstrapProcess = PromiseWrapper.completer();
|
||||||
|
|
||||||
var zone = _createNgZone();
|
var zone = createNgZone(new ExceptionHandler());
|
||||||
zone.run(() => {
|
zone.run(() => {
|
||||||
// TODO(rado): prepopulate template cache, so applications with only
|
// TODO(rado): prepopulate template cache, so applications with only
|
||||||
// index.html and main.js are possible.
|
// index.html and main.js are possible.
|
||||||
|
|
|
@ -39,6 +39,19 @@ bool isDate(obj) => obj is DateTime;
|
||||||
|
|
||||||
String stringify(obj) => obj.toString();
|
String stringify(obj) => obj.toString();
|
||||||
|
|
||||||
|
int serializeEnum(val) {
|
||||||
|
return val.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserializes an enum
|
||||||
|
* val should be the indexed value of the enum (sa returned from @Link{serializeEnum})
|
||||||
|
* values should be a map from indexes to values for the enum that you want to deserialize.
|
||||||
|
*/
|
||||||
|
dynamic deserializeEnum(int val, Map<int, dynamic> values) {
|
||||||
|
return values[val];
|
||||||
|
}
|
||||||
|
|
||||||
class StringWrapper {
|
class StringWrapper {
|
||||||
static String fromCharCode(int code) {
|
static String fromCharCode(int code) {
|
||||||
return new String.fromCharCode(code);
|
return new String.fromCharCode(code);
|
||||||
|
|
|
@ -130,6 +130,17 @@ export function stringify(token): string {
|
||||||
return (newLineIndex === -1) ? res : res.substring(0, newLineIndex);
|
return (newLineIndex === -1) ? res : res.substring(0, newLineIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// serialize / deserialize enum exist only for consistency with dart API
|
||||||
|
// enums in typescript don't need to be serialized
|
||||||
|
|
||||||
|
export function serializeEnum(val): int {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deserializeEnum(val, values: Map<int, any>): any {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
export class StringWrapper {
|
export class StringWrapper {
|
||||||
static fromCharCode(code: int): string { return String.fromCharCode(code); }
|
static fromCharCode(code: int): string { return String.fromCharCode(code); }
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import {AppRootUrl} from "angular2/src/services/app_root_url";
|
||||||
|
import {DOM} from "angular2/src/dom/dom_adapter";
|
||||||
|
import {Injectable} from "angular2/di";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension of {@link AppRootUrl} that uses a DOM anchor tag to set the root url to
|
||||||
|
* the current page's url.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class AnchorBasedAppRootUrl extends AppRootUrl {
|
||||||
|
constructor() {
|
||||||
|
super("");
|
||||||
|
// compute the root url to pass to AppRootUrl
|
||||||
|
var rootUrl: string;
|
||||||
|
var a = DOM.createElement('a');
|
||||||
|
DOM.resolveAndSetHref(a, './', null);
|
||||||
|
rootUrl = DOM.getHref(a);
|
||||||
|
|
||||||
|
this.value = rootUrl;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
import {Injectable} from 'angular2/di';
|
import {Injectable} from 'angular2/di';
|
||||||
import {isBlank} from 'angular2/src/facade/lang';
|
import {isBlank} from 'angular2/src/facade/lang';
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies app root url for the application.
|
* Specifies app root url for the application.
|
||||||
|
@ -15,16 +14,12 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
export class AppRootUrl {
|
export class AppRootUrl {
|
||||||
private _value: string;
|
private _value: string;
|
||||||
|
|
||||||
|
constructor(value: string) { this._value = value; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the base URL of the currently running application.
|
* Returns the base URL of the currently running application.
|
||||||
*/
|
*/
|
||||||
get value() {
|
get value() { return this._value; }
|
||||||
if (isBlank(this._value)) {
|
|
||||||
var a = DOM.createElement('a');
|
|
||||||
DOM.resolveAndSetHref(a, './', null);
|
|
||||||
this._value = DOM.getHref(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._value;
|
set value(value: string) { this._value = value; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import {XHR} from 'angular2/src/render/xhr';
|
||||||
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
|
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
|
||||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||||
import {AppRootUrl} from 'angular2/src/services/app_root_url';
|
import {AppRootUrl} from 'angular2/src/services/app_root_url';
|
||||||
|
import {AnchorBasedAppRootUrl} from 'angular2/src/services/anchor_based_app_root_url';
|
||||||
import {StyleUrlResolver} from 'angular2/src/render/dom/compiler/style_url_resolver';
|
import {StyleUrlResolver} from 'angular2/src/render/dom/compiler/style_url_resolver';
|
||||||
import {StyleInliner} from 'angular2/src/render/dom/compiler/style_inliner';
|
import {StyleInliner} from 'angular2/src/render/dom/compiler/style_inliner';
|
||||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||||
|
@ -56,6 +57,7 @@ import {
|
||||||
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES
|
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES
|
||||||
} from 'angular2/src/render/dom/dom_renderer';
|
} from 'angular2/src/render/dom/dom_renderer';
|
||||||
import {DefaultDomCompiler} from 'angular2/src/render/dom/compiler/compiler';
|
import {DefaultDomCompiler} from 'angular2/src/render/dom/compiler/compiler';
|
||||||
|
import {Serializer} from "angular2/src/web-workers/shared/serializer";
|
||||||
import {Log} from './utils';
|
import {Log} from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,6 +90,7 @@ function _getAppBindings() {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
appDoc = null;
|
appDoc = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
bind(DOCUMENT_TOKEN)
|
bind(DOCUMENT_TOKEN)
|
||||||
.toValue(appDoc),
|
.toValue(appDoc),
|
||||||
|
@ -102,6 +105,7 @@ function _getAppBindings() {
|
||||||
AppViewPool,
|
AppViewPool,
|
||||||
AppViewManager,
|
AppViewManager,
|
||||||
AppViewManagerUtils,
|
AppViewManagerUtils,
|
||||||
|
Serializer,
|
||||||
ELEMENT_PROBE_CONFIG,
|
ELEMENT_PROBE_CONFIG,
|
||||||
bind(APP_VIEW_POOL_CAPACITY).toValue(500),
|
bind(APP_VIEW_POOL_CAPACITY).toValue(500),
|
||||||
Compiler,
|
Compiler,
|
||||||
|
@ -120,7 +124,8 @@ function _getAppBindings() {
|
||||||
bind(XHR).toClass(MockXHR),
|
bind(XHR).toClass(MockXHR),
|
||||||
ComponentUrlMapper,
|
ComponentUrlMapper,
|
||||||
UrlResolver,
|
UrlResolver,
|
||||||
AppRootUrl,
|
AnchorBasedAppRootUrl,
|
||||||
|
bind(AppRootUrl).toAlias(AnchorBasedAppRootUrl),
|
||||||
StyleUrlResolver,
|
StyleUrlResolver,
|
||||||
StyleInliner,
|
StyleInliner,
|
||||||
TestComponentBuilder,
|
TestComponentBuilder,
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
import {CONST_EXPR} from "angular2/src/facade/lang";
|
||||||
|
import {OpaqueToken} from "angular2/di";
|
||||||
|
import {RenderElementRef, RenderViewRef} from "angular2/src/render/api";
|
||||||
|
|
||||||
|
export const ON_WEBWORKER = CONST_EXPR(new OpaqueToken('WebWorker.onWebWorker'));
|
||||||
|
|
||||||
|
export class WorkerElementRef implements RenderElementRef {
|
||||||
|
constructor(public renderView: RenderViewRef, public renderBoundElementIndex: number) {}
|
||||||
|
}
|
|
@ -13,6 +13,14 @@ export interface SourceListener {
|
||||||
(data: any): void; // TODO: Replace this any type with the type of a real messaging protocol
|
(data: any): void; // TODO: Replace this any type with the type of a real messaging protocol
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MessageBusSource { listen(fn: SourceListener): void; }
|
export interface MessageBusSource {
|
||||||
|
/**
|
||||||
|
* Attaches the SourceListener to this source.
|
||||||
|
* The SourceListener will get called whenever the bus receives a message
|
||||||
|
* Returns a listener id that can be passed to {@link removeListener}
|
||||||
|
*/
|
||||||
|
addListener(fn: SourceListener): number;
|
||||||
|
removeListener(index: number);
|
||||||
|
}
|
||||||
|
|
||||||
export interface MessageBusSink { send(message: Object): void; }
|
export interface MessageBusSink { send(message: Object): void; }
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
import {Injectable, Inject} from "angular2/di";
|
||||||
|
import {RenderProtoViewRef} from "angular2/src/render/api";
|
||||||
|
import {ON_WEBWORKER} from "angular2/src/web-workers/shared/api";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RenderProtoViewRefStore {
|
||||||
|
private _lookupByIndex: Map<number, RenderProtoViewRef> = new Map<number, RenderProtoViewRef>();
|
||||||
|
private _lookupByProtoView: Map<RenderProtoViewRef, number> =
|
||||||
|
new Map<RenderProtoViewRef, number>();
|
||||||
|
private _nextIndex: number = 0;
|
||||||
|
private _onWebworker: boolean;
|
||||||
|
|
||||||
|
constructor(@Inject(ON_WEBWORKER) onWebworker) { this._onWebworker = onWebworker; }
|
||||||
|
|
||||||
|
storeRenderProtoViewRef(ref: RenderProtoViewRef): number {
|
||||||
|
if (this._lookupByProtoView.has(ref)) {
|
||||||
|
return this._lookupByProtoView.get(ref);
|
||||||
|
} else {
|
||||||
|
this._lookupByIndex.set(this._nextIndex, ref);
|
||||||
|
this._lookupByProtoView.set(ref, this._nextIndex);
|
||||||
|
return this._nextIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
retreiveRenderProtoViewRef(index: number): RenderProtoViewRef {
|
||||||
|
return this._lookupByIndex.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
deserialize(index: number): RenderProtoViewRef {
|
||||||
|
if (index == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._onWebworker) {
|
||||||
|
return new WebworkerRenderProtoViewRef(index);
|
||||||
|
} else {
|
||||||
|
return this.retreiveRenderProtoViewRef(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serialize(ref: RenderProtoViewRef): number {
|
||||||
|
if (ref == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._onWebworker) {
|
||||||
|
return (<WebworkerRenderProtoViewRef>ref).refNumber;
|
||||||
|
} else {
|
||||||
|
return this.storeRenderProtoViewRef(ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WebworkerRenderProtoViewRef extends RenderProtoViewRef {
|
||||||
|
constructor(public refNumber: number) { super(); }
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
import {Injectable, Inject} from "angular2/di";
|
||||||
|
import {RenderViewRef, RenderFragmentRef, RenderViewWithFragments} from "angular2/src/render/api";
|
||||||
|
import {ON_WEBWORKER} from "angular2/src/web-workers/shared/api";
|
||||||
|
import {List, ListWrapper} from "angular2/src/facade/collection";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RenderViewWithFragmentsStore {
|
||||||
|
private _nextIndex: number = 0;
|
||||||
|
private _onWebWorker: boolean;
|
||||||
|
private _lookupByIndex: Map<number, RenderViewRef | RenderFragmentRef>;
|
||||||
|
private _lookupByView: Map<RenderViewRef | RenderFragmentRef, number>;
|
||||||
|
|
||||||
|
constructor(@Inject(ON_WEBWORKER) onWebWorker) {
|
||||||
|
this._onWebWorker = onWebWorker;
|
||||||
|
if (!onWebWorker) {
|
||||||
|
this._lookupByIndex = new Map<number, RenderViewRef | RenderFragmentRef>();
|
||||||
|
this._lookupByView = new Map<RenderViewRef | RenderFragmentRef, number>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allocate(fragmentCount: number): RenderViewWithFragments {
|
||||||
|
var viewRef = new WorkerRenderViewRef(this._nextIndex++);
|
||||||
|
var fragmentRefs = ListWrapper.createGrowableSize(fragmentCount);
|
||||||
|
|
||||||
|
for (var i = 0; i < fragmentCount; i++) {
|
||||||
|
fragmentRefs[i] = new WorkerRenderFragmentRef(this._nextIndex++);
|
||||||
|
}
|
||||||
|
return new RenderViewWithFragments(viewRef, fragmentRefs);
|
||||||
|
}
|
||||||
|
|
||||||
|
store(view: RenderViewWithFragments, startIndex: number) {
|
||||||
|
this._lookupByIndex.set(startIndex, view.viewRef);
|
||||||
|
this._lookupByView.set(view.viewRef, startIndex);
|
||||||
|
startIndex++;
|
||||||
|
|
||||||
|
ListWrapper.forEach(view.fragmentRefs, (ref) => {
|
||||||
|
this._lookupByIndex.set(startIndex, ref);
|
||||||
|
this._lookupByView.set(ref, startIndex);
|
||||||
|
startIndex++;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
retreive(ref: number): RenderViewRef | RenderFragmentRef {
|
||||||
|
if (ref == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this._lookupByIndex.get(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
serializeRenderViewRef(viewRef: RenderViewRef): number {
|
||||||
|
return this._serializeRenderFragmentOrViewRef(viewRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
serializeRenderFragmentRef(fragmentRef: RenderFragmentRef): number {
|
||||||
|
return this._serializeRenderFragmentOrViewRef(fragmentRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializeRenderViewRef(ref: number): RenderViewRef {
|
||||||
|
if (ref == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._onWebWorker) {
|
||||||
|
return WorkerRenderViewRef.deserialize(ref);
|
||||||
|
} else {
|
||||||
|
return this.retreive(ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializeRenderFragmentRef(ref: number): RenderFragmentRef {
|
||||||
|
if (ref == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._onWebWorker) {
|
||||||
|
return WorkerRenderFragmentRef.deserialize(ref);
|
||||||
|
} else {
|
||||||
|
return this.retreive(ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _serializeRenderFragmentOrViewRef(ref: RenderViewRef | RenderFragmentRef): number {
|
||||||
|
if (ref == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._onWebWorker) {
|
||||||
|
return (<WorkerRenderFragmentRef | WorkerRenderViewRef>ref).serialize();
|
||||||
|
} else {
|
||||||
|
return this._lookupByView.get(ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serializeViewWithFragments(view: RenderViewWithFragments): StringMap<string, any> {
|
||||||
|
if (view == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._onWebWorker) {
|
||||||
|
return {
|
||||||
|
'viewRef': (<WorkerRenderViewRef>view.viewRef).serialize(),
|
||||||
|
'fragmentRefs': ListWrapper.map(view.fragmentRefs, (val) => val.serialize())
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'viewRef': this._lookupByView.get(view.viewRef),
|
||||||
|
'fragmentRefs': ListWrapper.map(view.fragmentRefs, (val) => this._lookupByView.get(val))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializeViewWithFragments(obj: StringMap<string, any>): RenderViewWithFragments {
|
||||||
|
if (obj == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var viewRef: RenderViewRef | RenderFragmentRef;
|
||||||
|
var fragments: List<RenderViewRef | RenderFragmentRef>;
|
||||||
|
if (this._onWebWorker) {
|
||||||
|
viewRef = WorkerRenderViewRef.deserialize(obj['viewRef']);
|
||||||
|
fragments =
|
||||||
|
ListWrapper.map(obj['fragmentRefs'], (val) => WorkerRenderFragmentRef.deserialize(val));
|
||||||
|
|
||||||
|
return new RenderViewWithFragments(viewRef, fragments);
|
||||||
|
} else {
|
||||||
|
viewRef = this.retreive(obj['viewRef']);
|
||||||
|
fragments = ListWrapper.map(obj['fragmentRefs'], (val) => this.retreive(val));
|
||||||
|
|
||||||
|
return new RenderViewWithFragments(viewRef, fragments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WorkerRenderViewRef extends RenderViewRef {
|
||||||
|
constructor(public refNumber: number) { super(); }
|
||||||
|
serialize(): number { return this.refNumber; }
|
||||||
|
|
||||||
|
static deserialize(ref: number): WorkerRenderViewRef { return new WorkerRenderViewRef(ref); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WorkerRenderFragmentRef extends RenderFragmentRef {
|
||||||
|
constructor(public refNumber: number) { super(); }
|
||||||
|
|
||||||
|
serialize(): number { return this.refNumber; }
|
||||||
|
|
||||||
|
static deserialize(ref: number): WorkerRenderFragmentRef {
|
||||||
|
return new WorkerRenderFragmentRef(ref);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import {Type, isArray, isPresent} from "angular2/src/facade/lang";
|
import {Type, isArray, isPresent, serializeEnum, deserializeEnum} from "angular2/src/facade/lang";
|
||||||
import {List, ListWrapper, Map, StringMapWrapper, MapWrapper} from "angular2/src/facade/collection";
|
import {List, ListWrapper, Map, StringMapWrapper, MapWrapper} from "angular2/src/facade/collection";
|
||||||
import {
|
import {
|
||||||
ProtoViewDto,
|
ProtoViewDto,
|
||||||
|
@ -7,75 +7,122 @@ import {
|
||||||
DirectiveBinder,
|
DirectiveBinder,
|
||||||
ElementPropertyBinding,
|
ElementPropertyBinding,
|
||||||
EventBinding,
|
EventBinding,
|
||||||
ViewDefinition
|
ViewDefinition,
|
||||||
|
RenderProtoViewRef,
|
||||||
|
RenderProtoViewMergeMapping,
|
||||||
|
RenderViewRef,
|
||||||
|
RenderFragmentRef,
|
||||||
|
RenderElementRef,
|
||||||
|
ViewType
|
||||||
} from "angular2/src/render/api";
|
} from "angular2/src/render/api";
|
||||||
|
import {WorkerElementRef} from 'angular2/src/web-workers/shared/api';
|
||||||
import {AST, ASTWithSource} from "angular2/change_detection";
|
import {AST, ASTWithSource} from "angular2/change_detection";
|
||||||
import {Parser} from "angular2/src/change_detection/parser/parser";
|
import {Parser} from "angular2/src/change_detection/parser/parser";
|
||||||
|
import {Injectable} from "angular2/di";
|
||||||
|
import {RenderProtoViewRefStore} from 'angular2/src/web-workers/shared/render_proto_view_ref_store';
|
||||||
|
import {
|
||||||
|
RenderViewWithFragmentsStore
|
||||||
|
} from 'angular2/src/web-workers/shared/render_view_with_fragments_store';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class Serializer {
|
export class Serializer {
|
||||||
static parser: Parser = null;
|
private _enumRegistry: Map<any, Map<int, any>>;
|
||||||
|
constructor(private _parser: Parser, private _protoViewStore: RenderProtoViewRefStore,
|
||||||
|
private _renderViewStore: RenderViewWithFragmentsStore) {
|
||||||
|
this._enumRegistry = new Map<any, Map<int, any>>();
|
||||||
|
var viewTypeMap = new Map<int, any>();
|
||||||
|
viewTypeMap[0] = ViewType.HOST;
|
||||||
|
viewTypeMap[1] = ViewType.COMPONENT;
|
||||||
|
viewTypeMap[2] = ViewType.EMBEDDED;
|
||||||
|
this._enumRegistry.set(ViewType, viewTypeMap);
|
||||||
|
}
|
||||||
|
|
||||||
static serialize(obj: any, type: Type): Object {
|
serialize(obj: any, type: Type): Object {
|
||||||
if (!isPresent(obj)) {
|
if (!isPresent(obj)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (isArray(obj)) {
|
if (isArray(obj)) {
|
||||||
var serializedObj = [];
|
var serializedObj = [];
|
||||||
ListWrapper.forEach(obj, (val) => { serializedObj.push(Serializer.serialize(val, type)); });
|
ListWrapper.forEach(obj, (val) => { serializedObj.push(this.serialize(val, type)); });
|
||||||
return serializedObj;
|
return serializedObj;
|
||||||
}
|
}
|
||||||
|
if (type == String) {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
if (type == ViewDefinition) {
|
if (type == ViewDefinition) {
|
||||||
return ViewDefinitionSerializer.serialize(obj);
|
return this._serializeViewDefinition(obj);
|
||||||
} else if (type == DirectiveBinder) {
|
} else if (type == DirectiveBinder) {
|
||||||
return DirectiveBinderSerializer.serialize(obj);
|
return this._serializeDirectiveBinder(obj);
|
||||||
} else if (type == ProtoViewDto) {
|
} else if (type == ProtoViewDto) {
|
||||||
return ProtoViewDtoSerializer.serialize(obj);
|
return this._serializeProtoViewDto(obj);
|
||||||
} else if (type == ElementBinder) {
|
} else if (type == ElementBinder) {
|
||||||
return ElementBinderSerializer.serialize(obj);
|
return this._serializeElementBinder(obj);
|
||||||
} else if (type == DirectiveMetadata) {
|
} else if (type == DirectiveMetadata) {
|
||||||
return DirectiveMetadataSerializer.serialize(obj);
|
return this._serializeDirectiveMetadata(obj);
|
||||||
} else if (type == ASTWithSource) {
|
} else if (type == ASTWithSource) {
|
||||||
return ASTWithSourceSerializer.serialize(obj);
|
return this._serializeASTWithSource(obj);
|
||||||
|
} else if (type == RenderProtoViewRef) {
|
||||||
|
return this._protoViewStore.serialize(obj);
|
||||||
|
} else if (type == RenderProtoViewMergeMapping) {
|
||||||
|
return this._serializeRenderProtoViewMergeMapping(obj);
|
||||||
|
} else if (type == RenderViewRef) {
|
||||||
|
return this._renderViewStore.serializeRenderViewRef(obj);
|
||||||
|
} else if (type == RenderFragmentRef) {
|
||||||
|
return this._renderViewStore.serializeRenderFragmentRef(obj);
|
||||||
|
} else if (type == WorkerElementRef) {
|
||||||
|
return this._serializeWorkerElementRef(obj);
|
||||||
} else {
|
} else {
|
||||||
throw "No serializer for " + type.toString();
|
throw "No serializer for " + type.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: template this to return the type that is passed if possible
|
deserialize(map: any, type: Type, data?: any): any {
|
||||||
static deserialize(map: List<any>, type: Type, data?: any): any {
|
|
||||||
if (!isPresent(map)) {
|
if (!isPresent(map)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (isArray(map)) {
|
if (isArray(map)) {
|
||||||
var obj: List<any> = new List<any>();
|
var obj: List<any> = new List<any>();
|
||||||
ListWrapper.forEach(map, (val) => { obj.push(Serializer.deserialize(val, type, data)); });
|
ListWrapper.forEach(map, (val) => { obj.push(this.deserialize(val, type, data)); });
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
if (type == String) {
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
if (type == ViewDefinition) {
|
if (type == ViewDefinition) {
|
||||||
return ViewDefinitionSerializer.deserialize(map);
|
return this._deserializeViewDefinition(map);
|
||||||
} else if (type == DirectiveBinder) {
|
} else if (type == DirectiveBinder) {
|
||||||
return DirectiveBinderSerializer.deserialize(map);
|
return this._deserializeDirectiveBinder(map);
|
||||||
} else if (type == ProtoViewDto) {
|
} else if (type == ProtoViewDto) {
|
||||||
return ProtoViewDtoSerializer.deserialize(map);
|
return this._deserializeProtoViewDto(map);
|
||||||
} else if (type == DirectiveMetadata) {
|
} else if (type == DirectiveMetadata) {
|
||||||
return DirectiveMetadataSerializer.deserialize(map);
|
return this._deserializeDirectiveMetadata(map);
|
||||||
} else if (type == ElementBinder) {
|
} else if (type == ElementBinder) {
|
||||||
return ElementBinderSerializer.deserialize(map);
|
return this._deserializeElementBinder(map);
|
||||||
} else if (type == ASTWithSource) {
|
} else if (type == ASTWithSource) {
|
||||||
return ASTWithSourceSerializer.deserialize(map, data);
|
return this._deserializeASTWithSource(map, data);
|
||||||
|
} else if (type == RenderProtoViewRef) {
|
||||||
|
return this._protoViewStore.deserialize(map);
|
||||||
|
} else if (type == RenderProtoViewMergeMapping) {
|
||||||
|
return this._deserializeRenderProtoViewMergeMapping(map);
|
||||||
|
} else if (type == RenderViewRef) {
|
||||||
|
return this._renderViewStore.deserializeRenderViewRef(map);
|
||||||
|
} else if (type == RenderFragmentRef) {
|
||||||
|
return this._renderViewStore.deserializeRenderFragmentRef(map);
|
||||||
|
} else if (type == WorkerElementRef) {
|
||||||
|
return this._deserializeWorkerElementRef(map);
|
||||||
} else {
|
} else {
|
||||||
throw "No deserializer for " + type.toString();
|
throw "No deserializer for " + type.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static mapToObject(map: Map<any, any>, type?: Type): Object {
|
mapToObject(map: Map<string, any>, type?: Type): Object {
|
||||||
var object = {};
|
var object = {};
|
||||||
var serialize = isPresent(type);
|
var serialize = isPresent(type);
|
||||||
|
|
||||||
MapWrapper.forEach(map, (value, key) => {
|
MapWrapper.forEach(map, (value, key) => {
|
||||||
if (serialize) {
|
if (serialize) {
|
||||||
object[key] = Serializer.serialize(value, type);
|
object[key] = this.serialize(value, type);
|
||||||
} else {
|
} else {
|
||||||
object[key] = value;
|
object[key] = value;
|
||||||
}
|
}
|
||||||
|
@ -84,191 +131,213 @@ export class Serializer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Transforms a Javascript object into a Map<string, V>
|
* Transforms a Javascript object (StringMap) into a Map<string, V>
|
||||||
* If the values need to be deserialized pass in their type
|
* If the values need to be deserialized pass in their type
|
||||||
* and they will be deserialized before being placed in the map
|
* and they will be deserialized before being placed in the map
|
||||||
*/
|
*/
|
||||||
static objectToMap(obj: Object, type?: Type, data?: any): Map<string, any> {
|
objectToMap(obj: StringMap<string, any>, type?: Type, data?: any): Map<string, any> {
|
||||||
if (isPresent(type)) {
|
if (isPresent(type)) {
|
||||||
var map: Map<string, any> = new Map();
|
var map: Map<string, any> = new Map();
|
||||||
StringMapWrapper.forEach(
|
StringMapWrapper.forEach(obj,
|
||||||
obj, (key, val) => { map.set(key, Serializer.deserialize(val, type, data)); });
|
(key, val) => { map.set(key, this.deserialize(val, type, data)); });
|
||||||
return map;
|
return map;
|
||||||
} else {
|
} else {
|
||||||
return MapWrapper.createFromStringMap(obj);
|
return MapWrapper.createFromStringMap(obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class ASTWithSourceSerializer {
|
allocateRenderViews(fragmentCount: number) { this._renderViewStore.allocate(fragmentCount); }
|
||||||
static serialize(tree: ASTWithSource): Object {
|
|
||||||
|
private _serializeWorkerElementRef(elementRef: RenderElementRef): StringMap<string, any> {
|
||||||
|
return {
|
||||||
|
'renderView': this.serialize(elementRef.renderView, RenderViewRef),
|
||||||
|
'renderBoundElementIndex': elementRef.renderBoundElementIndex
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private _deserializeWorkerElementRef(map: StringMap<string, any>): RenderElementRef {
|
||||||
|
return new WorkerElementRef(this.deserialize(map['renderView'], RenderViewRef),
|
||||||
|
map['renderBoundElementIndex']);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _serializeRenderProtoViewMergeMapping(mapping: RenderProtoViewMergeMapping): Object {
|
||||||
|
return {
|
||||||
|
'mergedProtoViewRef': this._protoViewStore.serialize(mapping.mergedProtoViewRef),
|
||||||
|
'fragmentCount': mapping.fragmentCount,
|
||||||
|
'mappedElementIndices': mapping.mappedElementIndices,
|
||||||
|
'mappedElementCount': mapping.mappedElementCount,
|
||||||
|
'mappedTextIndices': mapping.mappedTextIndices,
|
||||||
|
'hostElementIndicesByViewIndex': mapping.hostElementIndicesByViewIndex,
|
||||||
|
'nestedViewCountByViewIndex': mapping.nestedViewCountByViewIndex
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private _deserializeRenderProtoViewMergeMapping(obj: StringMap<string, any>):
|
||||||
|
RenderProtoViewMergeMapping {
|
||||||
|
return new RenderProtoViewMergeMapping(
|
||||||
|
this._protoViewStore.deserialize(obj['mergedProtoViewRef']), obj['fragmentCount'],
|
||||||
|
obj['mappedElementIndices'], obj['mappedElementCount'], obj['mappedTextIndices'],
|
||||||
|
obj['hostElementIndicesByViewIndex'], obj['nestedViewCountByViewIndex']);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _serializeASTWithSource(tree: ASTWithSource): Object {
|
||||||
return {'input': tree.source, 'location': tree.location};
|
return {'input': tree.source, 'location': tree.location};
|
||||||
}
|
}
|
||||||
|
|
||||||
static deserialize(obj: any, data: string): AST {
|
private _deserializeASTWithSource(obj: StringMap<string, any>, data: string): AST {
|
||||||
// TODO: make ASTs serializable
|
// TODO: make ASTs serializable
|
||||||
var ast: AST;
|
var ast: AST;
|
||||||
switch (data) {
|
switch (data) {
|
||||||
case "interpolation":
|
case "interpolation":
|
||||||
ast = Serializer.parser.parseInterpolation(obj.input, obj.location);
|
ast = this._parser.parseInterpolation(obj['input'], obj['location']);
|
||||||
break;
|
break;
|
||||||
case "binding":
|
case "binding":
|
||||||
ast = Serializer.parser.parseBinding(obj.input, obj.location);
|
ast = this._parser.parseBinding(obj['input'], obj['location']);
|
||||||
break;
|
break;
|
||||||
case "simpleBinding":
|
case "simpleBinding":
|
||||||
ast = Serializer.parser.parseSimpleBinding(obj.input, obj.location);
|
ast = this._parser.parseSimpleBinding(obj['input'], obj['location']);
|
||||||
break;
|
break;
|
||||||
/*case "templateBindings":
|
|
||||||
ast = Serializer.parser.parseTemplateBindings(obj.input, obj.location);
|
|
||||||
break;*/
|
|
||||||
case "interpolation":
|
case "interpolation":
|
||||||
ast = Serializer.parser.parseInterpolation(obj.input, obj.location);
|
ast = this._parser.parseInterpolation(obj['input'], obj['location']);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw "No AST deserializer for " + data;
|
throw "No AST deserializer for " + data;
|
||||||
}
|
}
|
||||||
return ast;
|
return ast;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class ViewDefinitionSerializer {
|
private _serializeViewDefinition(view: ViewDefinition): Object {
|
||||||
static serialize(view: ViewDefinition): Object {
|
|
||||||
return {
|
return {
|
||||||
'componentId': view.componentId,
|
'componentId': view.componentId,
|
||||||
'templateAbsUrl': view.templateAbsUrl,
|
'templateAbsUrl': view.templateAbsUrl,
|
||||||
'template': view.template,
|
'template': view.template,
|
||||||
'directives': Serializer.serialize(view.directives, DirectiveMetadata),
|
'directives': this.serialize(view.directives, DirectiveMetadata),
|
||||||
'styleAbsUrls': view.styleAbsUrls,
|
'styleAbsUrls': view.styleAbsUrls,
|
||||||
'styles': view.styles
|
'styles': view.styles
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
static deserialize(obj: any): ViewDefinition {
|
|
||||||
|
private _deserializeViewDefinition(obj: StringMap<string, any>): ViewDefinition {
|
||||||
return new ViewDefinition({
|
return new ViewDefinition({
|
||||||
componentId: obj.componentId,
|
componentId: obj['componentId'],
|
||||||
templateAbsUrl: obj.templateAbsUrl, template: obj.template,
|
templateAbsUrl: obj['templateAbsUrl'], template: obj['template'],
|
||||||
directives: Serializer.deserialize(obj.directives, DirectiveMetadata),
|
directives: this.deserialize(obj['directives'], DirectiveMetadata),
|
||||||
styleAbsUrls: obj.styleAbsUrls,
|
styleAbsUrls: obj['styleAbsUrls'],
|
||||||
styles: obj.styles
|
styles: obj['styles']
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class DirectiveBinderSerializer {
|
private _serializeDirectiveBinder(binder: DirectiveBinder): Object {
|
||||||
static serialize(binder: DirectiveBinder): Object {
|
|
||||||
return {
|
return {
|
||||||
'directiveIndex': binder.directiveIndex,
|
'directiveIndex': binder.directiveIndex,
|
||||||
'propertyBindings': Serializer.mapToObject(binder.propertyBindings, ASTWithSource),
|
'propertyBindings': this.mapToObject(binder.propertyBindings, ASTWithSource),
|
||||||
'eventBindings': Serializer.serialize(binder.eventBindings, EventBinding),
|
'eventBindings': this.serialize(binder.eventBindings, EventBinding),
|
||||||
'hostPropertyBindings':
|
'hostPropertyBindings': this.serialize(binder.hostPropertyBindings, ElementPropertyBinding)
|
||||||
Serializer.serialize(binder.hostPropertyBindings, ElementPropertyBinding)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static deserialize(obj: any): DirectiveBinder {
|
private _deserializeDirectiveBinder(obj: StringMap<string, any>): DirectiveBinder {
|
||||||
return new DirectiveBinder({
|
return new DirectiveBinder({
|
||||||
directiveIndex: obj.directiveIndex,
|
directiveIndex: obj['directiveIndex'],
|
||||||
propertyBindings: Serializer.objectToMap(obj.propertyBindings, ASTWithSource, "binding"),
|
propertyBindings: this.objectToMap(obj['propertyBindings'], ASTWithSource, "binding"),
|
||||||
eventBindings: Serializer.deserialize(obj.eventBindings, EventBinding),
|
eventBindings: this.deserialize(obj['eventBindings'], EventBinding),
|
||||||
hostPropertyBindings: Serializer.deserialize(obj.hostPropertyBindings, ElementPropertyBinding)
|
hostPropertyBindings: this.deserialize(obj['hostPropertyBindings'], ElementPropertyBinding)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class ElementBinderSerializer {
|
private _serializeElementBinder(binder: ElementBinder): Object {
|
||||||
static serialize(binder: ElementBinder): Object {
|
|
||||||
return {
|
return {
|
||||||
'index': binder.index,
|
'index': binder.index,
|
||||||
'parentIndex': binder.parentIndex,
|
'parentIndex': binder.parentIndex,
|
||||||
'distanceToParent': binder.distanceToParent,
|
'distanceToParent': binder.distanceToParent,
|
||||||
'directives': Serializer.serialize(binder.directives, DirectiveBinder),
|
'directives': this.serialize(binder.directives, DirectiveBinder),
|
||||||
'nestedProtoView': Serializer.serialize(binder.nestedProtoView, ProtoViewDto),
|
'nestedProtoView': this.serialize(binder.nestedProtoView, ProtoViewDto),
|
||||||
'propertyBindings': Serializer.serialize(binder.propertyBindings, ElementPropertyBinding),
|
'propertyBindings': this.serialize(binder.propertyBindings, ElementPropertyBinding),
|
||||||
'variableBindings': Serializer.mapToObject(binder.variableBindings),
|
'variableBindings': this.mapToObject(binder.variableBindings),
|
||||||
'eventBindings': Serializer.serialize(binder.eventBindings, EventBinding),
|
'eventBindings': this.serialize(binder.eventBindings, EventBinding),
|
||||||
'readAttributes': Serializer.mapToObject(binder.readAttributes)
|
'readAttributes': this.mapToObject(binder.readAttributes)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static deserialize(obj: any): ElementBinder {
|
private _deserializeElementBinder(obj: StringMap<string, any>): ElementBinder {
|
||||||
return new ElementBinder({
|
return new ElementBinder({
|
||||||
index: obj.index,
|
index: obj['index'],
|
||||||
parentIndex: obj.parentIndex,
|
parentIndex: obj['parentIndex'],
|
||||||
distanceToParent: obj.distanceToParent,
|
distanceToParent: obj['distanceToParent'],
|
||||||
directives: Serializer.deserialize(obj.directives, DirectiveBinder),
|
directives: this.deserialize(obj['directives'], DirectiveBinder),
|
||||||
nestedProtoView: Serializer.deserialize(obj.nestedProtoView, ProtoViewDto),
|
nestedProtoView: this.deserialize(obj['nestedProtoView'], ProtoViewDto),
|
||||||
propertyBindings: Serializer.deserialize(obj.propertyBindings, ElementPropertyBinding),
|
propertyBindings: this.deserialize(obj['propertyBindings'], ElementPropertyBinding),
|
||||||
variableBindings: Serializer.objectToMap(obj.variableBindings),
|
variableBindings: this.objectToMap(obj['variableBindings']),
|
||||||
eventBindings: Serializer.deserialize(obj.eventBindings, EventBinding),
|
eventBindings: this.deserialize(obj['eventBindings'], EventBinding),
|
||||||
readAttributes: Serializer.objectToMap(obj.readAttributes)
|
readAttributes: this.objectToMap(obj['readAttributes'])
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class ProtoViewDtoSerializer {
|
private _serializeProtoViewDto(view: ProtoViewDto): Object {
|
||||||
static serialize(view: ProtoViewDto): Object {
|
|
||||||
// TODO: fix render refs and write a serializer for them
|
|
||||||
return {
|
return {
|
||||||
'render': null,
|
'render': this._protoViewStore.serialize(view.render),
|
||||||
'elementBinders': Serializer.serialize(view.elementBinders, ElementBinder),
|
'elementBinders': this.serialize(view.elementBinders, ElementBinder),
|
||||||
'variableBindings': Serializer.mapToObject(view.variableBindings),
|
'variableBindings': this.mapToObject(view.variableBindings),
|
||||||
'textBindings': Serializer.serialize(view.textBindings, ASTWithSource),
|
'type': serializeEnum(view.type),
|
||||||
'type': view.type
|
'textBindings': this.serialize(view.textBindings, ASTWithSource),
|
||||||
|
'transitiveNgContentCount': view.transitiveNgContentCount
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static deserialize(obj: any): ProtoViewDto {
|
private _deserializeProtoViewDto(obj: StringMap<string, any>): ProtoViewDto {
|
||||||
return new ProtoViewDto({
|
return new ProtoViewDto({
|
||||||
render: null, // TODO: fix render refs and write a serializer for them
|
render: this._protoViewStore.deserialize(obj["render"]),
|
||||||
elementBinders: Serializer.deserialize(obj.elementBinders, ElementBinder),
|
elementBinders: this.deserialize(obj['elementBinders'], ElementBinder),
|
||||||
variableBindings: Serializer.objectToMap(obj.variableBindings),
|
variableBindings: this.objectToMap(obj['variableBindings']),
|
||||||
textBindings: Serializer.deserialize(obj.textBindings, ASTWithSource, "interpolation"),
|
textBindings: this.deserialize(obj['textBindings'], ASTWithSource, "interpolation"),
|
||||||
type: obj.type
|
type: deserializeEnum(obj['type'], this._enumRegistry.get(ViewType)),
|
||||||
|
transitiveNgContentCount: obj['transitivengContentCount']
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class DirectiveMetadataSerializer {
|
private _serializeDirectiveMetadata(meta: DirectiveMetadata): Object {
|
||||||
static serialize(meta: DirectiveMetadata): Object {
|
|
||||||
var obj = {
|
var obj = {
|
||||||
'id': meta.id,
|
'id': meta.id,
|
||||||
'selector': meta.selector,
|
'selector': meta.selector,
|
||||||
'compileChildren': meta.compileChildren,
|
'compileChildren': meta.compileChildren,
|
||||||
'hostProperties': Serializer.mapToObject(meta.hostProperties),
|
'events': meta.events,
|
||||||
'hostListeners': Serializer.mapToObject(meta.hostListeners),
|
|
||||||
'hostActions': Serializer.mapToObject(meta.hostActions),
|
|
||||||
'hostAttributes': Serializer.mapToObject(meta.hostAttributes),
|
|
||||||
'properties': meta.properties,
|
'properties': meta.properties,
|
||||||
'readAttributes': meta.readAttributes,
|
'readAttributes': meta.readAttributes,
|
||||||
'type': meta.type,
|
'type': meta.type,
|
||||||
'exportAs': meta.exportAs,
|
|
||||||
'callOnDestroy': meta.callOnDestroy,
|
'callOnDestroy': meta.callOnDestroy,
|
||||||
|
'callOnChange': meta.callOnChange,
|
||||||
'callOnCheck': meta.callOnCheck,
|
'callOnCheck': meta.callOnCheck,
|
||||||
'callOnInit': meta.callOnInit,
|
'callOnInit': meta.callOnInit,
|
||||||
'callOnAllChangesDone': meta.callOnAllChangesDone,
|
'callOnAllChangesDone': meta.callOnAllChangesDone,
|
||||||
'changeDetection': meta.changeDetection,
|
'changeDetection': meta.changeDetection,
|
||||||
'events': meta.events
|
'exportAs': meta.exportAs,
|
||||||
|
'hostProperties': this.mapToObject(meta.hostProperties),
|
||||||
|
'hostListeners': this.mapToObject(meta.hostListeners),
|
||||||
|
'hostActions': this.mapToObject(meta.hostActions),
|
||||||
|
'hostAttributes': this.mapToObject(meta.hostAttributes)
|
||||||
};
|
};
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
static deserialize(obj: any): DirectiveMetadata {
|
private _deserializeDirectiveMetadata(obj: StringMap<string, any>): DirectiveMetadata {
|
||||||
return new DirectiveMetadata({
|
return new DirectiveMetadata({
|
||||||
id: obj.id,
|
id: obj['id'],
|
||||||
selector: obj.selector,
|
selector: obj['selector'],
|
||||||
compileChildren: obj.compileChildren,
|
compileChildren: obj['compileChildren'],
|
||||||
hostProperties: Serializer.objectToMap(obj.hostProperties),
|
hostProperties: this.objectToMap(obj['hostProperties']),
|
||||||
hostListeners: Serializer.objectToMap(obj.hostListeners),
|
hostListeners: this.objectToMap(obj['hostListeners']),
|
||||||
hostActions: Serializer.objectToMap(obj.hostActions),
|
hostActions: this.objectToMap(obj['hostActions']),
|
||||||
hostAttributes: Serializer.objectToMap(obj.hostAttributes),
|
hostAttributes: this.objectToMap(obj['hostAttributes']),
|
||||||
properties: obj.properties,
|
properties: obj['properties'],
|
||||||
readAttributes: obj.readAttributes,
|
readAttributes: obj['readAttributes'],
|
||||||
type: obj.type,
|
type: obj['type'],
|
||||||
exportAs: obj.exportAs,
|
exportAs: obj['exportAs'],
|
||||||
callOnDestroy: obj.callOnDestroy,
|
callOnDestroy: obj['callOnDestroy'],
|
||||||
callOnCheck: obj.callOnCheck,
|
callOnChange: obj['callOnChange'],
|
||||||
callOnInit: obj.callOnInit,
|
callOnCheck: obj['callOnCheck'],
|
||||||
callOnAllChangesDone: obj.callOnAllChangesDone,
|
callOnInit: obj['callOnInit'],
|
||||||
changeDetection: obj.changeDetection,
|
callOnAllChangesDone: obj['callOnAllChangesDone'],
|
||||||
events: obj.events
|
changeDetection: obj['changeDetection'],
|
||||||
|
events: obj['events']
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,275 +0,0 @@
|
||||||
import {Type, isArray, isPresent} from "angular2/src/facade/lang";
|
|
||||||
import {List, ListWrapper, Map, StringMapWrapper, MapWrapper} from "angular2/src/facade/collection";
|
|
||||||
import {
|
|
||||||
ProtoViewDto,
|
|
||||||
DirectiveMetadata,
|
|
||||||
ElementBinder,
|
|
||||||
DirectiveBinder,
|
|
||||||
ElementPropertyBinding,
|
|
||||||
EventBinding,
|
|
||||||
ViewDefinition
|
|
||||||
} from "angular2/src/render/api";
|
|
||||||
import {AST, ASTWithSource} from "angular2/change_detection";
|
|
||||||
import {Parser} from "angular2/src/change_detection/parser/parser";
|
|
||||||
|
|
||||||
export class Serializer {
|
|
||||||
static parser: Parser = null;
|
|
||||||
|
|
||||||
static serialize(obj: any, type: Type): Object {
|
|
||||||
if (!isPresent(obj)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (isArray(obj)) {
|
|
||||||
var serializedObj = [];
|
|
||||||
ListWrapper.forEach(obj, (val) => { serializedObj.push(Serializer.serialize(val, type)); });
|
|
||||||
return serializedObj;
|
|
||||||
}
|
|
||||||
if (type == ViewDefinition) {
|
|
||||||
return ViewDefinitionSerializer.serialize(obj);
|
|
||||||
} else if (type == DirectiveBinder) {
|
|
||||||
return DirectiveBinderSerializer.serialize(obj);
|
|
||||||
} else if (type == ProtoViewDto) {
|
|
||||||
return ProtoViewDtoSerializer.serialize(obj);
|
|
||||||
} else if (type == ElementBinder) {
|
|
||||||
return ElementBinderSerializer.serialize(obj);
|
|
||||||
} else if (type == DirectiveMetadata) {
|
|
||||||
return DirectiveMetadataSerializer.serialize(obj);
|
|
||||||
} else if (type == ASTWithSource) {
|
|
||||||
return ASTWithSourceSerializer.serialize(obj);
|
|
||||||
} else {
|
|
||||||
throw "No serializer for " + type.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: template this to return the type that is passed if possible
|
|
||||||
static deserialize(map, type: Type, data?): any {
|
|
||||||
if (!isPresent(map)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (isArray(map)) {
|
|
||||||
var obj: List<any> = new List<any>();
|
|
||||||
ListWrapper.forEach(map, (val) => { obj.push(Serializer.deserialize(val, type, data)); });
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == ViewDefinition) {
|
|
||||||
return ViewDefinitionSerializer.deserialize(map);
|
|
||||||
} else if (type == DirectiveBinder) {
|
|
||||||
return DirectiveBinderSerializer.deserialize(map);
|
|
||||||
} else if (type == ProtoViewDto) {
|
|
||||||
return ProtoViewDtoSerializer.deserialize(map);
|
|
||||||
} else if (type == DirectiveMetadata) {
|
|
||||||
return DirectiveMetadataSerializer.deserialize(map);
|
|
||||||
} else if (type == ElementBinder) {
|
|
||||||
return ElementBinderSerializer.deserialize(map);
|
|
||||||
} else if (type == ASTWithSource) {
|
|
||||||
return ASTWithSourceSerializer.deserialize(map, data);
|
|
||||||
} else {
|
|
||||||
throw "No deserializer for " + type.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static mapToObject(map, type?: Type): Object {
|
|
||||||
var object = {};
|
|
||||||
var serialize = isPresent(type);
|
|
||||||
|
|
||||||
MapWrapper.forEach(map, (value, key) => {
|
|
||||||
if (serialize) {
|
|
||||||
object[key] = Serializer.serialize(value, type);
|
|
||||||
} else {
|
|
||||||
object[key] = value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Transforms a Javascript object 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
|
|
||||||
*/
|
|
||||||
static objectToMap(obj, type?: Type, data?): Map<string, any> {
|
|
||||||
if (isPresent(type)) {
|
|
||||||
var map: Map<string, any> = new Map();
|
|
||||||
StringMapWrapper.forEach(
|
|
||||||
obj, (key, val) => { map.set(key, Serializer.deserialize(val, type, data)); });
|
|
||||||
return map;
|
|
||||||
} else {
|
|
||||||
return MapWrapper.createFromStringMap(obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ASTWithSourceSerializer {
|
|
||||||
static serialize(tree: ASTWithSource): Object {
|
|
||||||
return { 'input': tree.source, 'location': tree.location }
|
|
||||||
}
|
|
||||||
|
|
||||||
static deserialize(obj: any, data: string): AST {
|
|
||||||
// TODO: make ASTs serializable
|
|
||||||
var ast: AST;
|
|
||||||
switch (data) {
|
|
||||||
case "interpolation":
|
|
||||||
ast = Serializer.parser.parseInterpolation(obj.input, obj.location);
|
|
||||||
break;
|
|
||||||
case "binding":
|
|
||||||
ast = Serializer.parser.parseBinding(obj.input, obj.location);
|
|
||||||
break;
|
|
||||||
case "simpleBinding":
|
|
||||||
ast = Serializer.parser.parseSimpleBinding(obj.input, obj.location);
|
|
||||||
break;
|
|
||||||
/*case "templateBindings":
|
|
||||||
ast = Serializer.parser.parseTemplateBindings(obj.input, obj.location);
|
|
||||||
break;*/
|
|
||||||
case "interpolation":
|
|
||||||
ast = Serializer.parser.parseInterpolation(obj.input, obj.location);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw "No AST deserializer for " + data;
|
|
||||||
}
|
|
||||||
return ast;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ViewDefinitionSerializer {
|
|
||||||
static serialize(view: ViewDefinition): Object{
|
|
||||||
return {
|
|
||||||
'componentId': view.componentId,
|
|
||||||
'templateAbsUrl': view.templateAbsUrl,
|
|
||||||
'template': view.template,
|
|
||||||
'directives': Serializer.serialize(view.directives, DirectiveMetadata),
|
|
||||||
'styleAbsUrls': view.styleAbsUrls,
|
|
||||||
'styles': view.styles
|
|
||||||
};
|
|
||||||
}
|
|
||||||
static deserialize(obj): ViewDefinition {
|
|
||||||
return new ViewDefinition({
|
|
||||||
'componentId': obj.componentId,
|
|
||||||
'templateAbsUrl': obj.templateAbsUrl,
|
|
||||||
'template': obj.template,
|
|
||||||
'directives': Serializer.deserialize(obj.directives, DirectiveMetadata),
|
|
||||||
'styleAbsUrls': obj.styleAbsUrls,
|
|
||||||
'styles': obj.styles
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DirectiveBinderSerializer {
|
|
||||||
static serialize(binder: DirectiveBinder): Object {
|
|
||||||
return {
|
|
||||||
'directiveIndex': binder.directiveIndex,
|
|
||||||
'propertyBindings': Serializer.mapToObject(binder.propertyBindings, ASTWithSource),
|
|
||||||
'eventBindings': Serializer.serialize(binder.eventBindings, EventBinding),
|
|
||||||
'hostPropertyBindings':
|
|
||||||
Serializer.serialize(binder.hostPropertyBindings, ElementPropertyBinding)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static deserialize(obj): DirectiveBinder {
|
|
||||||
return new DirectiveBinder({
|
|
||||||
'directiveIndex': obj.directiveIndex,
|
|
||||||
'propertyBindings': Serializer.objectToMap(obj.propertyBindings, ASTWithSource, "binding"),
|
|
||||||
'eventBindings': Serializer.deserialize(obj.eventBindings, EventBinding),
|
|
||||||
'hostPropertyBindings':
|
|
||||||
Serializer.deserialize(obj.hostPropertyBindings, ElementPropertyBinding)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ElementBinderSerializer {
|
|
||||||
static serialize(binder: ElementBinder): Object {
|
|
||||||
return {
|
|
||||||
'index': binder.index,
|
|
||||||
'parentIndex': binder.parentIndex,
|
|
||||||
'distanceToParent': binder.distanceToParent,
|
|
||||||
'directives': Serializer.serialize(binder.directives, DirectiveBinder),
|
|
||||||
'nestedProtoView': Serializer.serialize(binder.nestedProtoView, ProtoViewDto),
|
|
||||||
'propertyBindings': Serializer.serialize(binder.propertyBindings, ElementPropertyBinding),
|
|
||||||
'variableBindings': Serializer.mapToObject(binder.variableBindings),
|
|
||||||
'eventBindings': Serializer.serialize(binder.eventBindings, EventBinding),
|
|
||||||
'textBindings': Serializer.serialize(binder.textBindings, ASTWithSource),
|
|
||||||
'readAttributes': Serializer.mapToObject(binder.readAttributes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static deserialize(obj): ElementBinder {
|
|
||||||
return new ElementBinder({
|
|
||||||
'index': obj.index,
|
|
||||||
'parentIndex': obj.parentIndex,
|
|
||||||
'distanceToParent': obj.distanceToParent,
|
|
||||||
'directives': Serializer.deserialize(obj.directives, DirectiveBinder),
|
|
||||||
'nestedProtoView': Serializer.deserialize(obj.nestedProtoView, ProtoViewDto),
|
|
||||||
'propertyBindings': Serializer.deserialize(obj.propertyBindings, ElementPropertyBinding),
|
|
||||||
'variableBindings': Serializer.objectToMap(obj.variableBindings),
|
|
||||||
'eventBindings': Serializer.deserialize(obj.eventBindings, EventBinding),
|
|
||||||
'textBindings': Serializer.deserialize(obj.textBindings, ASTWithSource, "interpolation"),
|
|
||||||
'readAttributes': Serializer.objectToMap(obj.readAttributes)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ProtoViewDtoSerializer {
|
|
||||||
static serialize(view: ProtoViewDto): Object {
|
|
||||||
return {
|
|
||||||
'render': null, // TODO: fix render refs and write a serializer for them
|
|
||||||
'elementBinders': Serializer.serialize(view.elementBinders, ElementBinder),
|
|
||||||
'variableBindings': Serializer.mapToObject(view.variableBindings),
|
|
||||||
'type': view.type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static deserialize(obj): ProtoViewDto {
|
|
||||||
return new ProtoViewDto({
|
|
||||||
'render': null, // TODO: fix render refs and write a serializer for them
|
|
||||||
'elementBinders': Serializer.deserialize(obj.elementBinders, ElementBinder),
|
|
||||||
'variableBindings': Serializer.objectToMap(obj.variableBindings),
|
|
||||||
'type': obj.type
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DirectiveMetadataSerializer {
|
|
||||||
static serialize(meta: DirectiveMetadata): Object {
|
|
||||||
var obj = {
|
|
||||||
'id': meta.id,
|
|
||||||
'selector': meta.selector,
|
|
||||||
'compileChildren': meta.compileChildren,
|
|
||||||
'hostProperties': Serializer.mapToObject(meta.hostProperties),
|
|
||||||
'hostListeners': Serializer.mapToObject(meta.hostListeners),
|
|
||||||
'hostActions': Serializer.mapToObject(meta.hostActions),
|
|
||||||
'hostAttributes': Serializer.mapToObject(meta.hostAttributes),
|
|
||||||
'properties': meta.properties,
|
|
||||||
'readAttributes': meta.readAttributes,
|
|
||||||
'type': meta.type,
|
|
||||||
'exportAs': meta.exportAs,
|
|
||||||
'callOnDestroy': meta.callOnDestroy,
|
|
||||||
'callOnCheck': meta.callOnCheck,
|
|
||||||
'callOnInit': meta.callOnInit,
|
|
||||||
'callOnAllChangesDone': meta.callOnAllChangesDone,
|
|
||||||
'changeDetection': meta.changeDetection,
|
|
||||||
'events': meta.events
|
|
||||||
};
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
static deserialize(obj): DirectiveMetadata {
|
|
||||||
return new DirectiveMetadata({
|
|
||||||
'id': obj.id,
|
|
||||||
'selector': obj.selector,
|
|
||||||
'compileChildren': obj.compileChildren,
|
|
||||||
'hostProperties': Serializer.objectToMap(obj.hostProperties),
|
|
||||||
'hostListeners': Serializer.objectToMap(obj.hostListeners),
|
|
||||||
'hostActions': Serializer.objectToMap(obj.hostActions),
|
|
||||||
'hostAttributes': Serializer.objectToMap(obj.hostAttributes),
|
|
||||||
'properties': obj.properties,
|
|
||||||
'readAttributes': obj.readAttributes,
|
|
||||||
'type': obj.type,
|
|
||||||
'exportAs': obj.exportAs,
|
|
||||||
'callOnDestroy': obj.callOnDestroy,
|
|
||||||
'callOnCheck': obj.callOnCheck,
|
|
||||||
'callOnInit': obj.callOnInit,
|
|
||||||
'callOnAllChangesDone': obj.callOnAllChangesDone,
|
|
||||||
'changeDetection': obj.changeDetection,
|
|
||||||
'events': obj.events
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,23 +2,28 @@ library angular2.src.web_workers.ui;
|
||||||
|
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import "package:angular2/src/web-workers/shared/message_bus.dart"
|
import 'dart:core';
|
||||||
|
import 'package:angular2/src/web-workers/shared/message_bus.dart'
|
||||||
show MessageBus, MessageBusSink, MessageBusSource;
|
show MessageBus, MessageBusSink, MessageBusSource;
|
||||||
|
import 'package:angular2/src/web-workers/ui/impl.dart'
|
||||||
|
show bootstrapUICommon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bootstrapping a WebWorker
|
* Bootstrapping a WebWorker
|
||||||
*
|
*
|
||||||
* You instantiate a WebWorker application by calling bootstrap with the URI of your worker's index script
|
* You instantiate a WebWorker application by calling bootstrap with the URI of your worker's index script
|
||||||
* Note: The WebWorker script must call bootstrapWebworker once it is set up to complete the bootstrapping process
|
* Note: The WebWorker script must call bootstrapWebworker once it is set up to complete the bootstrapping process
|
||||||
*/
|
*/
|
||||||
void bootstrap(String uri) {
|
void bootstrap(String uri) {
|
||||||
throw "Not Implemented";
|
spawnWorker(Uri.parse(uri)).then((bus) {
|
||||||
|
bootstrapUICommon(bus);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To be called from the main thread to spawn and communicate with the worker thread
|
* To be called from the main thread to spawn and communicate with the worker thread
|
||||||
*/
|
*/
|
||||||
Future<UIMessageBus> spawnWorker(Uri uri) {
|
Future<UIMessageBus> spawnWorker(Uri uri){
|
||||||
var receivePort = new ReceivePort();
|
var receivePort = new ReceivePort();
|
||||||
var isolateEndSendPort = receivePort.sendPort;
|
var isolateEndSendPort = receivePort.sendPort;
|
||||||
return Isolate.spawnUri(uri, const [], isolateEndSendPort).then((_) {
|
return Isolate.spawnUri(uri, const [], isolateEndSendPort).then((_) {
|
||||||
|
@ -52,6 +57,8 @@ class UIMessageBusSink extends MessageBusSink {
|
||||||
class UIMessageBusSource extends MessageBusSource {
|
class UIMessageBusSource extends MessageBusSource {
|
||||||
final ReceivePort _port;
|
final ReceivePort _port;
|
||||||
final Stream rawDataStream;
|
final Stream rawDataStream;
|
||||||
|
Map<int, StreamSubscription> _listenerStore = new Map<int, StreamSubscription>();
|
||||||
|
int _numListeners = 0;
|
||||||
|
|
||||||
UIMessageBusSource(ReceivePort port)
|
UIMessageBusSource(ReceivePort port)
|
||||||
: _port = port,
|
: _port = port,
|
||||||
|
@ -61,9 +68,17 @@ class UIMessageBusSource extends MessageBusSource {
|
||||||
return message is SendPort;
|
return message is SendPort;
|
||||||
});
|
});
|
||||||
|
|
||||||
void listen(Function fn) {
|
int addListener(Function fn){
|
||||||
rawDataStream.listen((message) {
|
var subscription = rawDataStream.listen((message){
|
||||||
fn({"data": message});
|
fn({"data": message});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_listenerStore[++_numListeners] = subscription;
|
||||||
|
return _numListeners;
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeListener(int index){
|
||||||
|
_listenerStore[index].cancel();
|
||||||
|
_listenerStore.remove(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
SourceListener
|
SourceListener
|
||||||
} from "angular2/src/web-workers/shared/message_bus";
|
} from "angular2/src/web-workers/shared/message_bus";
|
||||||
import {BaseException} from "angular2/src/facade/lang";
|
import {BaseException} from "angular2/src/facade/lang";
|
||||||
|
import {bootstrapUICommon} from "angular2/src/web-workers/ui/impl";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bootstrapping a WebWorker
|
* Bootstrapping a WebWorker
|
||||||
|
@ -15,7 +16,8 @@ import {BaseException} from "angular2/src/facade/lang";
|
||||||
* bootstrapping process
|
* bootstrapping process
|
||||||
*/
|
*/
|
||||||
export function bootstrap(uri: string): void {
|
export function bootstrap(uri: string): void {
|
||||||
throw new BaseException("Not Implemented");
|
var messageBus = spawnWorker(uri);
|
||||||
|
bootstrapUICommon(messageBus);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function spawnWorker(uri: string): MessageBus {
|
export function spawnWorker(uri: string): MessageBus {
|
||||||
|
@ -34,7 +36,19 @@ export class UIMessageBusSink implements MessageBusSink {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UIMessageBusSource implements MessageBusSource {
|
export class UIMessageBusSource implements MessageBusSource {
|
||||||
|
private _listenerStore: Map<int, SourceListener> = new Map<int, SourceListener>();
|
||||||
|
private _numListeners: int = 0;
|
||||||
|
|
||||||
constructor(private _worker: Worker) {}
|
constructor(private _worker: Worker) {}
|
||||||
|
|
||||||
listen(fn: SourceListener): void { this._worker.addEventListener("message", fn); }
|
public addListener(fn: SourceListener): int {
|
||||||
|
this._worker.addEventListener("message", fn);
|
||||||
|
this._listenerStore[++this._numListeners] = fn;
|
||||||
|
return this._numListeners;
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeListener(index: int): void {
|
||||||
|
removeEventListener("message", this._listenerStore[index]);
|
||||||
|
this._listenerStore.delete(index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
// TODO: This whole file is nearly identical to core/application.ts.
|
||||||
|
// There should be a way to refactor application so that this file is unnecessary
|
||||||
|
import {Injector, bind, Binding} from "angular2/di";
|
||||||
|
import {Type, isBlank, isPresent} from "angular2/src/facade/lang";
|
||||||
|
import {Reflector, reflector} from 'angular2/src/reflection/reflection';
|
||||||
|
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {
|
||||||
|
Parser,
|
||||||
|
Lexer,
|
||||||
|
ChangeDetection,
|
||||||
|
DynamicChangeDetection,
|
||||||
|
JitChangeDetection,
|
||||||
|
PreGeneratedChangeDetection,
|
||||||
|
Pipes,
|
||||||
|
defaultPipes
|
||||||
|
} from 'angular2/change_detection';
|
||||||
|
import {EventManager, DomEventsPlugin} from 'angular2/src/render/dom/events/event_manager';
|
||||||
|
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
|
||||||
|
import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
|
||||||
|
import {KeyEventsPlugin} from 'angular2/src/render/dom/events/key_events';
|
||||||
|
import {HammerGesturesPlugin} from 'angular2/src/render/dom/events/hammer_gestures';
|
||||||
|
import {AppViewPool, APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_pool';
|
||||||
|
import {Renderer, RenderCompiler} from 'angular2/src/render/api';
|
||||||
|
import {
|
||||||
|
DomRenderer,
|
||||||
|
DOCUMENT_TOKEN,
|
||||||
|
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES
|
||||||
|
} from 'angular2/src/render/dom/dom_renderer';
|
||||||
|
import {DefaultDomCompiler} from 'angular2/src/render/dom/compiler/compiler';
|
||||||
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
|
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||||
|
import {ShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/shadow_dom_strategy';
|
||||||
|
import {
|
||||||
|
EmulatedUnscopedShadowDomStrategy
|
||||||
|
} from 'angular2/src/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy';
|
||||||
|
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||||
|
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
||||||
|
import {AppViewListener} from 'angular2/src/core/compiler/view_listener';
|
||||||
|
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
|
||||||
|
import {ViewResolver} from 'angular2/src/core/compiler/view_resolver';
|
||||||
|
import {ViewLoader} from 'angular2/src/render/dom/compiler/view_loader';
|
||||||
|
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
|
import {ExceptionHandler} from 'angular2/src/core/exception_handler';
|
||||||
|
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
|
||||||
|
import {StyleInliner} from 'angular2/src/render/dom/compiler/style_inliner';
|
||||||
|
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
||||||
|
import {StyleUrlResolver} from 'angular2/src/render/dom/compiler/style_url_resolver';
|
||||||
|
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||||
|
import {Testability} from 'angular2/src/core/testability/testability';
|
||||||
|
import {XHR} from 'angular2/src/render/xhr';
|
||||||
|
import {XHRImpl} from 'angular2/src/render/xhr_impl';
|
||||||
|
import {Serializer} from 'angular2/src/web-workers/shared/serializer';
|
||||||
|
import {ON_WEBWORKER} from 'angular2/src/web-workers/shared/api';
|
||||||
|
import {RenderProtoViewRefStore} from 'angular2/src/web-workers/shared/render_proto_view_ref_store';
|
||||||
|
import {
|
||||||
|
RenderViewWithFragmentsStore
|
||||||
|
} from 'angular2/src/web-workers/shared/render_view_with_fragments_store';
|
||||||
|
import {AnchorBasedAppRootUrl} from 'angular2/src/services/anchor_based_app_root_url';
|
||||||
|
import {WebWorkerMain} from 'angular2/src/web-workers/ui/impl';
|
||||||
|
|
||||||
|
var _rootInjector: Injector;
|
||||||
|
|
||||||
|
// Contains everything that is safe to share between applications.
|
||||||
|
var _rootBindings = [bind(Reflector).toValue(reflector)];
|
||||||
|
|
||||||
|
// TODO: This code is nearly identitcal to core/application. There should be a way to only write it
|
||||||
|
// once
|
||||||
|
function _injectorBindings(): List<Type | Binding | List<any>> {
|
||||||
|
var bestChangeDetection: Type = DynamicChangeDetection;
|
||||||
|
if (PreGeneratedChangeDetection.isSupported()) {
|
||||||
|
bestChangeDetection = PreGeneratedChangeDetection;
|
||||||
|
} else if (JitChangeDetection.isSupported()) {
|
||||||
|
bestChangeDetection = JitChangeDetection;
|
||||||
|
}
|
||||||
|
// compute the root url to pass to AppRootUrl
|
||||||
|
/*var rootUrl: string;
|
||||||
|
var a = DOM.createElement('a');
|
||||||
|
DOM.resolveAndSetHref(a, './', null);
|
||||||
|
rootUrl = DOM.getHref(a);*/
|
||||||
|
|
||||||
|
return [
|
||||||
|
bind(DOCUMENT_TOKEN)
|
||||||
|
.toValue(DOM.defaultDoc()),
|
||||||
|
bind(EventManager)
|
||||||
|
.toFactory(
|
||||||
|
(ngZone) => {
|
||||||
|
var plugins =
|
||||||
|
[new HammerGesturesPlugin(), new KeyEventsPlugin(), new DomEventsPlugin()];
|
||||||
|
return new EventManager(plugins, ngZone);
|
||||||
|
},
|
||||||
|
[NgZone]),
|
||||||
|
bind(ShadowDomStrategy)
|
||||||
|
.toFactory((doc) => new EmulatedUnscopedShadowDomStrategy(doc.head), [DOCUMENT_TOKEN]),
|
||||||
|
bind(DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES).toValue(false),
|
||||||
|
DomRenderer,
|
||||||
|
DefaultDomCompiler,
|
||||||
|
Serializer,
|
||||||
|
bind(Renderer).toAlias(DomRenderer),
|
||||||
|
bind(RenderCompiler).toAlias(DefaultDomCompiler),
|
||||||
|
bind(ON_WEBWORKER).toValue(false),
|
||||||
|
RenderViewWithFragmentsStore,
|
||||||
|
RenderProtoViewRefStore,
|
||||||
|
ProtoViewFactory,
|
||||||
|
AppViewPool,
|
||||||
|
bind(APP_VIEW_POOL_CAPACITY).toValue(10000),
|
||||||
|
AppViewManager,
|
||||||
|
AppViewManagerUtils,
|
||||||
|
AppViewListener,
|
||||||
|
Compiler,
|
||||||
|
CompilerCache,
|
||||||
|
ViewResolver,
|
||||||
|
bind(Pipes).toValue(defaultPipes),
|
||||||
|
bind(ChangeDetection).toClass(bestChangeDetection),
|
||||||
|
ViewLoader,
|
||||||
|
DirectiveResolver,
|
||||||
|
Parser,
|
||||||
|
Lexer,
|
||||||
|
ExceptionHandler,
|
||||||
|
bind(XHR).toValue(new XHRImpl()),
|
||||||
|
ComponentUrlMapper,
|
||||||
|
UrlResolver,
|
||||||
|
StyleUrlResolver,
|
||||||
|
StyleInliner,
|
||||||
|
DynamicComponentLoader,
|
||||||
|
Testability,
|
||||||
|
AnchorBasedAppRootUrl,
|
||||||
|
WebWorkerMain
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createInjector(zone: NgZone): Injector {
|
||||||
|
BrowserDomAdapter.makeCurrent();
|
||||||
|
_rootBindings.push(bind(NgZone).toValue(zone));
|
||||||
|
var injector: Injector = Injector.resolveAndCreate(_rootBindings);
|
||||||
|
return injector.resolveAndCreateChild(_injectorBindings());
|
||||||
|
}
|
|
@ -0,0 +1,234 @@
|
||||||
|
/*
|
||||||
|
* This file is the entry point for the main thread
|
||||||
|
* It takes care of spawning the worker and sending it the initial init message
|
||||||
|
* It also acts and the messenger between the worker thread and the renderer running on the UI
|
||||||
|
* thread
|
||||||
|
* TODO: This class might need to be refactored to match application.ts...
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {createInjector} from "./di_bindings";
|
||||||
|
import {
|
||||||
|
Renderer,
|
||||||
|
RenderCompiler,
|
||||||
|
DirectiveMetadata,
|
||||||
|
ProtoViewDto,
|
||||||
|
ViewDefinition,
|
||||||
|
RenderProtoViewRef,
|
||||||
|
RenderProtoViewMergeMapping,
|
||||||
|
RenderViewRef,
|
||||||
|
RenderFragmentRef
|
||||||
|
} from "angular2/src/render/api";
|
||||||
|
import {Type, print, BaseException} from "angular2/src/facade/lang";
|
||||||
|
import {Promise, PromiseWrapper} from "angular2/src/facade/async";
|
||||||
|
import {Serializer} from "angular2/src/web-workers/shared/serializer";
|
||||||
|
import {MessageBus} from "angular2/src/web-workers/shared/message_bus";
|
||||||
|
import {
|
||||||
|
RenderViewWithFragmentsStore
|
||||||
|
} from 'angular2/src/web-workers/shared/render_view_with_fragments_store';
|
||||||
|
import {createNgZone} from 'angular2/src/core/application_common';
|
||||||
|
import {WorkerElementRef} from 'angular2/src/web-workers/shared/api';
|
||||||
|
import {AnchorBasedAppRootUrl} from 'angular2/src/services/anchor_based_app_root_url';
|
||||||
|
import {ExceptionHandler} from 'angular2/src/core/exception_handler';
|
||||||
|
import {Injectable} from 'angular2/di';
|
||||||
|
import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a zone, sets up the DI bindings
|
||||||
|
* And then creates a new WebWorkerMain object to handle messages from the worker
|
||||||
|
*/
|
||||||
|
export function bootstrapUICommon(bus: MessageBus) {
|
||||||
|
BrowserDomAdapter.makeCurrent();
|
||||||
|
var zone = createNgZone(new ExceptionHandler());
|
||||||
|
zone.run(() => {
|
||||||
|
var injector = createInjector(zone);
|
||||||
|
var webWorkerMain = injector.get(WebWorkerMain);
|
||||||
|
webWorkerMain.attachToWorker(bus);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class WebWorkerMain {
|
||||||
|
private _rootUrl: string;
|
||||||
|
private _bus: MessageBus;
|
||||||
|
|
||||||
|
constructor(private _renderCompiler: RenderCompiler, private _renderer: Renderer,
|
||||||
|
private _renderViewWithFragmentsStore: RenderViewWithFragmentsStore,
|
||||||
|
private _serializer: Serializer, rootUrl: AnchorBasedAppRootUrl) {
|
||||||
|
this._rootUrl = rootUrl.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attach's this WebWorkerMain instance to the given MessageBus
|
||||||
|
* This instance will now listen for all messages from the worker and handle them appropriately
|
||||||
|
* Note: Don't attach more than one WebWorkerMain instance to the same MessageBus.
|
||||||
|
*/
|
||||||
|
attachToWorker(bus: MessageBus) {
|
||||||
|
this._bus = bus;
|
||||||
|
this._bus.source.addListener((message) => { this._handleWorkerMessage(message); });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _sendInitMessage() { this._sendWorkerMessage("init", {"rootUrl": this._rootUrl}); }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sends an error back to the worker thread in response to an opeartion on the UI thread
|
||||||
|
*/
|
||||||
|
private _sendWorkerError(id: string, error: any) {
|
||||||
|
this._sendWorkerMessage("error", {"id": id, "error": error});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _sendWorkerMessage(type: string, data: StringMap<string, any>) {
|
||||||
|
this._bus.sink.send({'type': type, 'value': data});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Transfer the types with the serialized data so this can be automated?
|
||||||
|
private _handleCompilerMessage(data: ReceivedMessage) {
|
||||||
|
var promise: Promise<any>;
|
||||||
|
switch (data.method) {
|
||||||
|
case "compileHost":
|
||||||
|
var directiveMetadata = this._serializer.deserialize(data.args[0], DirectiveMetadata);
|
||||||
|
promise = this._renderCompiler.compileHost(directiveMetadata);
|
||||||
|
this._wrapWorkerPromise(data.id, promise, ProtoViewDto);
|
||||||
|
break;
|
||||||
|
case "compile":
|
||||||
|
var view = this._serializer.deserialize(data.args[0], ViewDefinition);
|
||||||
|
promise = this._renderCompiler.compile(view);
|
||||||
|
this._wrapWorkerPromise(data.id, promise, ProtoViewDto);
|
||||||
|
break;
|
||||||
|
case "mergeProtoViewsRecursively":
|
||||||
|
var views = this._serializer.deserialize(data.args[0], RenderProtoViewRef);
|
||||||
|
promise = this._renderCompiler.mergeProtoViewsRecursively(views);
|
||||||
|
this._wrapWorkerPromise(data.id, promise, RenderProtoViewMergeMapping);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new BaseException("not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _createViewHelper(args: List<any>, method) {
|
||||||
|
var hostProtoView = this._serializer.deserialize(args[0], RenderProtoViewRef);
|
||||||
|
var fragmentCount = args[1];
|
||||||
|
var startIndex, renderViewWithFragments;
|
||||||
|
if (method == "createView") {
|
||||||
|
startIndex = args[2];
|
||||||
|
renderViewWithFragments = this._renderer.createView(hostProtoView, fragmentCount);
|
||||||
|
} else {
|
||||||
|
var selector = args[2];
|
||||||
|
startIndex = args[3];
|
||||||
|
renderViewWithFragments =
|
||||||
|
this._renderer.createRootHostView(hostProtoView, fragmentCount, selector);
|
||||||
|
}
|
||||||
|
this._renderViewWithFragmentsStore.store(renderViewWithFragments, startIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleRendererMessage(data: ReceivedMessage) {
|
||||||
|
var args = data.args;
|
||||||
|
switch (data.method) {
|
||||||
|
case "createRootHostView":
|
||||||
|
case "createView":
|
||||||
|
this._createViewHelper(args, data.method);
|
||||||
|
break;
|
||||||
|
case "destroyView":
|
||||||
|
var viewRef = this._serializer.deserialize(args[0], RenderViewRef);
|
||||||
|
this._renderer.destroyView(viewRef);
|
||||||
|
break;
|
||||||
|
case "attachFragmentAfterFragment":
|
||||||
|
var previousFragment = this._serializer.deserialize(args[0], RenderFragmentRef);
|
||||||
|
var fragment = this._serializer.deserialize(args[1], RenderFragmentRef);
|
||||||
|
this._renderer.attachFragmentAfterFragment(previousFragment, fragment);
|
||||||
|
break;
|
||||||
|
case "attachFragmentAfterElement":
|
||||||
|
var element = this._serializer.deserialize(args[0], WorkerElementRef);
|
||||||
|
var fragment = this._serializer.deserialize(args[1], RenderFragmentRef);
|
||||||
|
this._renderer.attachFragmentAfterElement(element, fragment);
|
||||||
|
break;
|
||||||
|
case "detachFragment":
|
||||||
|
var fragment = this._serializer.deserialize(args[0], RenderFragmentRef);
|
||||||
|
this._renderer.detachFragment(fragment);
|
||||||
|
break;
|
||||||
|
case "hydrateView":
|
||||||
|
var viewRef = this._serializer.deserialize(args[0], RenderViewRef);
|
||||||
|
this._renderer.hydrateView(viewRef);
|
||||||
|
break;
|
||||||
|
case "dehydrateView":
|
||||||
|
var viewRef = this._serializer.deserialize(args[0], RenderViewRef);
|
||||||
|
this._renderer.dehydrateView(viewRef);
|
||||||
|
break;
|
||||||
|
case "setText":
|
||||||
|
var viewRef = this._serializer.deserialize(args[0], RenderViewRef);
|
||||||
|
var textNodeIndex = args[1];
|
||||||
|
var text = args[2];
|
||||||
|
this._renderer.setText(viewRef, textNodeIndex, text);
|
||||||
|
break;
|
||||||
|
case "setElementProperty":
|
||||||
|
var elementRef = this._serializer.deserialize(args[0], WorkerElementRef);
|
||||||
|
var propName = args[1];
|
||||||
|
var propValue = args[2];
|
||||||
|
this._renderer.setElementProperty(elementRef, propName, propValue);
|
||||||
|
break;
|
||||||
|
case "setElementAttribute":
|
||||||
|
var elementRef = this._serializer.deserialize(args[0], WorkerElementRef);
|
||||||
|
var attributeName = args[1];
|
||||||
|
var attributeValue = args[2];
|
||||||
|
this._renderer.setElementAttribute(elementRef, attributeName, attributeValue);
|
||||||
|
break;
|
||||||
|
case "setElementClass":
|
||||||
|
var elementRef = this._serializer.deserialize(args[0], WorkerElementRef);
|
||||||
|
var className = args[1];
|
||||||
|
var isAdd = args[2];
|
||||||
|
this._renderer.setElementClass(elementRef, className, isAdd);
|
||||||
|
break;
|
||||||
|
case "setElementStyle":
|
||||||
|
var elementRef = this._serializer.deserialize(args[0], WorkerElementRef);
|
||||||
|
var styleName = args[1];
|
||||||
|
var styleValue = args[2];
|
||||||
|
this._renderer.setElementStyle(elementRef, styleName, styleValue);
|
||||||
|
break;
|
||||||
|
case "invokeElementMethod":
|
||||||
|
var elementRef = this._serializer.deserialize(args[0], WorkerElementRef);
|
||||||
|
var methodName = args[1];
|
||||||
|
var methodArgs = args[2];
|
||||||
|
this._renderer.invokeElementMethod(elementRef, methodName, methodArgs);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new BaseException("Not Implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Create message type
|
||||||
|
private _handleWorkerMessage(message: StringMap<string, any>) {
|
||||||
|
var data: ReceivedMessage = new ReceivedMessage(message['data']);
|
||||||
|
switch (data.type) {
|
||||||
|
case "ready":
|
||||||
|
return this._sendInitMessage();
|
||||||
|
case "compiler":
|
||||||
|
return this._handleCompilerMessage(data);
|
||||||
|
case "renderer":
|
||||||
|
return this._handleRendererMessage(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _wrapWorkerPromise(id: string, promise: Promise<any>, type: Type): void {
|
||||||
|
PromiseWrapper.then(promise, (result: any) => {
|
||||||
|
try {
|
||||||
|
this._sendWorkerMessage("result",
|
||||||
|
{"id": id, "value": this._serializer.serialize(result, type)});
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
|
}, (error: any) => { this._sendWorkerError(id, error); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReceivedMessage {
|
||||||
|
method: string;
|
||||||
|
args: List<any>;
|
||||||
|
id: string;
|
||||||
|
type: string;
|
||||||
|
|
||||||
|
constructor(data: StringMap<string, any>) {
|
||||||
|
this.method = data['method'];
|
||||||
|
this.args = data['args'];
|
||||||
|
this.id = data['id'];
|
||||||
|
this.type = data['type'];
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,15 +2,18 @@ library angular2.src.web_workers.worker;
|
||||||
|
|
||||||
import "package:angular2/src/web-workers/shared/message_bus.dart"
|
import "package:angular2/src/web-workers/shared/message_bus.dart"
|
||||||
show MessageBus, MessageBusSource, MessageBusSink;
|
show MessageBus, MessageBusSource, MessageBusSink;
|
||||||
|
import "package:angular2/src/web-workers/worker/application_common.dart"
|
||||||
|
show bootstrapWebworkerCommon;
|
||||||
import "package:angular2/src/facade/async.dart" show Future;
|
import "package:angular2/src/facade/async.dart" show Future;
|
||||||
import "package:angular2/src/core/application.dart" show ApplicationRef;
|
import "package:angular2/src/core/application.dart" show ApplicationRef;
|
||||||
import "package:angular2/src/facade/lang.dart" show Type, BaseException;
|
import "package:angular2/src/facade/lang.dart" show Type, BaseException;
|
||||||
import "dart:isolate";
|
import "dart:isolate";
|
||||||
import "dart:async";
|
import "dart:async";
|
||||||
|
import 'dart:core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bootstrapping a Webworker Application
|
* Bootstrapping a Webworker Application
|
||||||
*
|
*
|
||||||
* You instantiate the application side by calling bootstrapWebworker from your webworker index
|
* You instantiate the application side by calling bootstrapWebworker from your webworker index
|
||||||
* script.
|
* script.
|
||||||
* You must supply a SendPort for communicating with the UI side in order to instantiate
|
* You must supply a SendPort for communicating with the UI side in order to instantiate
|
||||||
|
@ -21,9 +24,11 @@ import "dart:async";
|
||||||
*/
|
*/
|
||||||
Future<ApplicationRef> bootstrapWebworker(
|
Future<ApplicationRef> bootstrapWebworker(
|
||||||
SendPort replyTo, Type appComponentType,
|
SendPort replyTo, Type appComponentType,
|
||||||
[List<dynamic> componentInjectableBindings = null,
|
[List<dynamic> componentInjectableBindings = null]) {
|
||||||
Function errorReporter = null]) {
|
ReceivePort rPort = new ReceivePort();
|
||||||
throw new BaseException("Not implemented");
|
WorkerMessageBus bus = new WorkerMessageBus.fromPorts(replyTo, rPort);
|
||||||
|
return bootstrapWebworkerCommon(appComponentType, bus,
|
||||||
|
componentInjectableBindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
class WorkerMessageBus extends MessageBus {
|
class WorkerMessageBus extends MessageBus {
|
||||||
|
@ -52,14 +57,24 @@ class WorkerMessageBusSink extends MessageBusSink {
|
||||||
class WorkerMessageBusSource extends MessageBusSource {
|
class WorkerMessageBusSource extends MessageBusSource {
|
||||||
final ReceivePort _port;
|
final ReceivePort _port;
|
||||||
final Stream rawDataStream;
|
final Stream rawDataStream;
|
||||||
|
Map<int, StreamSubscription> _listenerStore = new Map<int, StreamSubscription>();
|
||||||
|
int _numListeners = 0;
|
||||||
|
|
||||||
WorkerMessageBusSource(ReceivePort rPort)
|
WorkerMessageBusSource(ReceivePort rPort)
|
||||||
: _port = rPort,
|
: _port = rPort,
|
||||||
rawDataStream = rPort.asBroadcastStream();
|
rawDataStream = rPort.asBroadcastStream();
|
||||||
|
|
||||||
void listen(Function fn) {
|
int addListener(Function fn){
|
||||||
rawDataStream.listen((message) {
|
var subscription = rawDataStream.listen((message){
|
||||||
fn({"data": message});
|
fn({"data": message});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_listenerStore[++_numListeners] = subscription;
|
||||||
|
return _numListeners;
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeListener(int index){
|
||||||
|
_listenerStore[index].cancel();
|
||||||
|
_listenerStore.remove(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,9 @@ import {
|
||||||
import {Type, BaseException} from "angular2/src/facade/lang";
|
import {Type, BaseException} from "angular2/src/facade/lang";
|
||||||
import {Binding} from "angular2/di";
|
import {Binding} from "angular2/di";
|
||||||
|
|
||||||
|
import {bootstrapWebworkerCommon} from "angular2/src/web-workers/worker/application_common";
|
||||||
import {ApplicationRef} from "angular2/src/core/application";
|
import {ApplicationRef} from "angular2/src/core/application";
|
||||||
|
import {Injectable} from "angular2/di";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bootstrapping a Webworker Application
|
* Bootstrapping a Webworker Application
|
||||||
|
@ -19,11 +21,15 @@ import {ApplicationRef} from "angular2/src/core/application";
|
||||||
* See the bootstrap() docs for more details.
|
* See the bootstrap() docs for more details.
|
||||||
*/
|
*/
|
||||||
export function bootstrapWebworker(
|
export function bootstrapWebworker(
|
||||||
appComponentType: Type, componentInjectableBindings: List<Type | Binding | List<any>> = null,
|
appComponentType: Type, componentInjectableBindings: List<Type | Binding | List<any>> = null):
|
||||||
errorReporter: Function = null): Promise<ApplicationRef> {
|
Promise<ApplicationRef> {
|
||||||
throw new BaseException("Not Implemented");
|
var bus: WorkerMessageBus =
|
||||||
|
new WorkerMessageBus(new WorkerMessageBusSink(), new WorkerMessageBusSource());
|
||||||
|
|
||||||
|
return bootstrapWebworkerCommon(appComponentType, bus, componentInjectableBindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class WorkerMessageBus implements MessageBus {
|
export class WorkerMessageBus implements MessageBus {
|
||||||
sink: WorkerMessageBusSink;
|
sink: WorkerMessageBusSink;
|
||||||
source: WorkerMessageBusSource;
|
source: WorkerMessageBusSource;
|
||||||
|
@ -39,5 +45,22 @@ export class WorkerMessageBusSink implements MessageBusSink {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WorkerMessageBusSource implements MessageBusSource {
|
export class WorkerMessageBusSource implements MessageBusSource {
|
||||||
public listen(fn: SourceListener) { addEventListener("message", fn); }
|
private listenerStore: Map<int, SourceListener>;
|
||||||
|
private numListeners: int;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.numListeners = 0;
|
||||||
|
this.listenerStore = new Map<int, SourceListener>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public addListener(fn: SourceListener): int {
|
||||||
|
addEventListener("message", fn);
|
||||||
|
this.listenerStore[++this.numListeners] = fn;
|
||||||
|
return this.numListeners;
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeListener(index: int): void {
|
||||||
|
removeEventListener("message", this.listenerStore[index]);
|
||||||
|
this.listenerStore.delete(index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,195 @@
|
||||||
|
import {Injector, bind, OpaqueToken, Binding} from 'angular2/di';
|
||||||
|
import {
|
||||||
|
NumberWrapper,
|
||||||
|
Type,
|
||||||
|
isBlank,
|
||||||
|
isPresent,
|
||||||
|
BaseException,
|
||||||
|
assertionsEnabled,
|
||||||
|
print,
|
||||||
|
stringify
|
||||||
|
} from 'angular2/src/facade/lang';
|
||||||
|
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
|
||||||
|
import {Reflector, reflector} from 'angular2/src/reflection/reflection';
|
||||||
|
import {
|
||||||
|
Parser,
|
||||||
|
Lexer,
|
||||||
|
ChangeDetection,
|
||||||
|
DynamicChangeDetection,
|
||||||
|
JitChangeDetection,
|
||||||
|
Pipes,
|
||||||
|
defaultPipes,
|
||||||
|
PreGeneratedChangeDetection
|
||||||
|
} from 'angular2/change_detection';
|
||||||
|
import {StyleUrlResolver} from 'angular2/src/render/dom/compiler/style_url_resolver';
|
||||||
|
import {ExceptionHandler} from 'angular2/src/core/exception_handler';
|
||||||
|
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
|
import {ViewResolver} from 'angular2/src/core/compiler/view_resolver';
|
||||||
|
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||||
|
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||||
|
import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle';
|
||||||
|
import {XHR} from 'angular2/src/render/xhr';
|
||||||
|
import {XHRImpl} from 'angular2/src/render/xhr_impl';
|
||||||
|
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
|
||||||
|
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||||
|
import {AppRootUrl} from 'angular2/src/services/app_root_url';
|
||||||
|
import {
|
||||||
|
ComponentRef,
|
||||||
|
DynamicComponentLoader
|
||||||
|
} from 'angular2/src/core/compiler/dynamic_component_loader';
|
||||||
|
import {Testability} from 'angular2/src/core/testability/testability';
|
||||||
|
import {AppViewPool, APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_pool';
|
||||||
|
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||||
|
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
||||||
|
import {AppViewListener} from 'angular2/src/core/compiler/view_listener';
|
||||||
|
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
|
||||||
|
import {WorkerRenderer, WorkerCompiler} from './renderer';
|
||||||
|
import {Renderer, RenderCompiler} from 'angular2/src/render/api';
|
||||||
|
import {internalView} from 'angular2/src/core/compiler/view_ref';
|
||||||
|
|
||||||
|
import {MessageBroker} from 'angular2/src/web-workers/worker/broker';
|
||||||
|
import {WorkerMessageBus} from 'angular2/src/web-workers/worker/application';
|
||||||
|
import {
|
||||||
|
appComponentRefPromiseToken,
|
||||||
|
appComponentTypeToken
|
||||||
|
} from 'angular2/src/core/application_tokens';
|
||||||
|
import {ApplicationRef} from 'angular2/src/core/application';
|
||||||
|
import {createNgZone} from 'angular2/src/core/application_common';
|
||||||
|
import {Serializer} from "angular2/src/web-workers/shared/serializer";
|
||||||
|
import {ON_WEBWORKER} from "angular2/src/web-workers/shared/api";
|
||||||
|
import {RenderProtoViewRefStore} from 'angular2/src/web-workers/shared/render_proto_view_ref_store';
|
||||||
|
import {
|
||||||
|
RenderViewWithFragmentsStore
|
||||||
|
} from 'angular2/src/web-workers/shared/render_view_with_fragments_store';
|
||||||
|
import {WorkerExceptionHandler} from 'angular2/src/web-workers/worker/exception_handler';
|
||||||
|
|
||||||
|
var _rootInjector: Injector;
|
||||||
|
|
||||||
|
// Contains everything that is safe to share between applications.
|
||||||
|
var _rootBindings = [bind(Reflector).toValue(reflector)];
|
||||||
|
|
||||||
|
function _injectorBindings(appComponentType, bus: WorkerMessageBus,
|
||||||
|
initData: StringMap<string, any>): List<Type | Binding | List<any>> {
|
||||||
|
var bestChangeDetection: Type = DynamicChangeDetection;
|
||||||
|
if (PreGeneratedChangeDetection.isSupported()) {
|
||||||
|
bestChangeDetection = PreGeneratedChangeDetection;
|
||||||
|
} else if (JitChangeDetection.isSupported()) {
|
||||||
|
bestChangeDetection = JitChangeDetection;
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
bind(appComponentTypeToken)
|
||||||
|
.toValue(appComponentType),
|
||||||
|
bind(appComponentRefPromiseToken)
|
||||||
|
.toFactory(
|
||||||
|
(dynamicComponentLoader, injector) => {
|
||||||
|
|
||||||
|
// TODO(rado): investigate whether to support bindings on root component.
|
||||||
|
return dynamicComponentLoader.loadAsRoot(appComponentType, null, injector)
|
||||||
|
.then((componentRef) => { return componentRef; });
|
||||||
|
},
|
||||||
|
[DynamicComponentLoader, Injector]),
|
||||||
|
|
||||||
|
bind(appComponentType).toFactory((ref) => ref.instance, [appComponentRefPromiseToken]),
|
||||||
|
bind(LifeCycle).toFactory((exceptionHandler) => new LifeCycle(null, assertionsEnabled()),
|
||||||
|
[ExceptionHandler]),
|
||||||
|
Serializer,
|
||||||
|
bind(WorkerMessageBus).toValue(bus),
|
||||||
|
bind(MessageBroker)
|
||||||
|
.toFactory((a, b) => new MessageBroker(a, b), [WorkerMessageBus, Serializer]),
|
||||||
|
WorkerRenderer,
|
||||||
|
bind(Renderer).toAlias(WorkerRenderer),
|
||||||
|
WorkerCompiler,
|
||||||
|
bind(RenderCompiler).toAlias(WorkerCompiler),
|
||||||
|
bind(ON_WEBWORKER).toValue(true),
|
||||||
|
RenderViewWithFragmentsStore,
|
||||||
|
RenderProtoViewRefStore,
|
||||||
|
ProtoViewFactory,
|
||||||
|
AppViewPool,
|
||||||
|
bind(APP_VIEW_POOL_CAPACITY).toValue(10000),
|
||||||
|
AppViewManager,
|
||||||
|
AppViewManagerUtils,
|
||||||
|
AppViewListener,
|
||||||
|
Compiler,
|
||||||
|
CompilerCache,
|
||||||
|
ViewResolver,
|
||||||
|
bind(Pipes).toValue(defaultPipes),
|
||||||
|
bind(ChangeDetection).toClass(bestChangeDetection),
|
||||||
|
DirectiveResolver,
|
||||||
|
Parser,
|
||||||
|
Lexer,
|
||||||
|
WorkerExceptionHandler,
|
||||||
|
bind(ExceptionHandler).toAlias(WorkerExceptionHandler),
|
||||||
|
bind(XHR).toValue(new XHRImpl()),
|
||||||
|
ComponentUrlMapper,
|
||||||
|
UrlResolver,
|
||||||
|
StyleUrlResolver,
|
||||||
|
DynamicComponentLoader,
|
||||||
|
Testability,
|
||||||
|
bind(AppRootUrl).toValue(new AppRootUrl(initData['rootUrl']))
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bootstrapWebworkerCommon(
|
||||||
|
appComponentType: Type, bus: WorkerMessageBus,
|
||||||
|
componentInjectableBindings: List<Type | Binding | List<any>> = null): Promise<ApplicationRef> {
|
||||||
|
var bootstrapProcess = PromiseWrapper.completer();
|
||||||
|
|
||||||
|
var zone = createNgZone(new WorkerExceptionHandler());
|
||||||
|
zone.run(() => {
|
||||||
|
// TODO(rado): prepopulate template cache, so applications with only
|
||||||
|
// index.html and main.js are possible.
|
||||||
|
//
|
||||||
|
|
||||||
|
var listenerId: int;
|
||||||
|
listenerId = bus.source.addListener((message: StringMap<string, any>) => {
|
||||||
|
if (message["data"]["type"] !== "init") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var appInjector = _createAppInjector(appComponentType, componentInjectableBindings, zone, bus,
|
||||||
|
message["data"]["value"]);
|
||||||
|
var compRefToken = PromiseWrapper.wrap(() => {
|
||||||
|
try {
|
||||||
|
return appInjector.get(appComponentRefPromiseToken);
|
||||||
|
} catch (e) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var tick = (componentRef) => {
|
||||||
|
var appChangeDetector = internalView(componentRef.hostView).changeDetector;
|
||||||
|
// retrieve life cycle: may have already been created if injected in root component
|
||||||
|
var lc = appInjector.get(LifeCycle);
|
||||||
|
lc.registerWith(zone, appChangeDetector);
|
||||||
|
lc.tick(); // the first tick that will bootstrap the app
|
||||||
|
|
||||||
|
bootstrapProcess.resolve(new ApplicationRef(componentRef, appComponentType, appInjector));
|
||||||
|
};
|
||||||
|
PromiseWrapper.then(compRefToken, tick,
|
||||||
|
(err, stackTrace) => { bootstrapProcess.reject(err, stackTrace); });
|
||||||
|
|
||||||
|
PromiseWrapper.catchError(compRefToken, (err) => {
|
||||||
|
print(err);
|
||||||
|
bootstrapProcess.reject(err, err.stack);
|
||||||
|
});
|
||||||
|
|
||||||
|
bus.source.removeListener(listenerId);
|
||||||
|
});
|
||||||
|
|
||||||
|
bus.sink.send({'type': "ready"});
|
||||||
|
});
|
||||||
|
|
||||||
|
return bootstrapProcess.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _createAppInjector(appComponentType: Type, bindings: List<Type | Binding | List<any>>,
|
||||||
|
zone: NgZone, bus: WorkerMessageBus, initData: StringMap<string, any>):
|
||||||
|
Injector {
|
||||||
|
if (isBlank(_rootInjector)) _rootInjector = Injector.resolveAndCreate(_rootBindings);
|
||||||
|
var mergedBindings: any[] =
|
||||||
|
isPresent(bindings) ?
|
||||||
|
ListWrapper.concat(_injectorBindings(appComponentType, bus, initData), bindings) :
|
||||||
|
_injectorBindings(appComponentType, bus, initData);
|
||||||
|
mergedBindings.push(bind(NgZone).toValue(zone));
|
||||||
|
return _rootInjector.resolveAndCreateChild(mergedBindings);
|
||||||
|
}
|
|
@ -4,12 +4,15 @@ import {print, isPresent, DateWrapper, stringify} from "../../facade/lang";
|
||||||
import {Promise, PromiseCompleter, PromiseWrapper} from "angular2/src/facade/async";
|
import {Promise, PromiseCompleter, PromiseWrapper} from "angular2/src/facade/async";
|
||||||
import {ListWrapper, StringMapWrapper, MapWrapper} from "../../facade/collection";
|
import {ListWrapper, StringMapWrapper, MapWrapper} from "../../facade/collection";
|
||||||
import {Serializer} from "angular2/src/web-workers/shared/serializer";
|
import {Serializer} from "angular2/src/web-workers/shared/serializer";
|
||||||
|
import {Injectable} from "angular2/di";
|
||||||
|
import {Type} from "angular2/src/facade/lang";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class MessageBroker {
|
export class MessageBroker {
|
||||||
private _pending: Map<string, Function> = new Map<string, Function>();
|
private _pending: Map<string, Function> = new Map<string, Function>();
|
||||||
|
|
||||||
constructor(private _messageBus: MessageBus) {
|
constructor(private _messageBus: MessageBus, protected _serializer: Serializer) {
|
||||||
this._messageBus.source.listen((data) => this._handleMessage(data['data']));
|
this._messageBus.source.addListener((data) => this._handleMessage(data['data']));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _generateMessageId(name: string): string {
|
private _generateMessageId(name: string): string {
|
||||||
|
@ -23,26 +26,48 @@ export class MessageBroker {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
runOnUiThread(args: UiArguments): Promise<any> {
|
runOnUiThread(args: UiArguments, returnType: Type): Promise<any> {
|
||||||
var completer = PromiseWrapper.completer();
|
|
||||||
var id: string = this._generateMessageId(args.type + args.method);
|
|
||||||
this._pending.set(id, completer.resolve);
|
|
||||||
PromiseWrapper.catchError(completer.promise, (err, stack?) => {
|
|
||||||
print(err);
|
|
||||||
completer.reject(err, stack);
|
|
||||||
});
|
|
||||||
|
|
||||||
var fnArgs = [];
|
var fnArgs = [];
|
||||||
if (isPresent(args.args)) {
|
if (isPresent(args.args)) {
|
||||||
ListWrapper.forEach(args.args, (argument) => {
|
ListWrapper.forEach(args.args, (argument) => {
|
||||||
fnArgs.push(Serializer.serialize(argument.value, argument.type));
|
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 = PromiseWrapper.completer();
|
||||||
|
id = this._generateMessageId(args.type + args.method);
|
||||||
|
this._pending.set(id, completer.resolve);
|
||||||
|
PromiseWrapper.catchError(completer.promise, (err, stack?) => {
|
||||||
|
print(err);
|
||||||
|
completer.reject(err, stack);
|
||||||
|
});
|
||||||
|
|
||||||
|
promise = PromiseWrapper.then(completer.promise, (data: MessageResult) => {
|
||||||
|
if (this._serializer == null) {
|
||||||
|
return data.value;
|
||||||
|
} else {
|
||||||
|
return this._serializer.deserialize(data.value, returnType);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
promise = null;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(jteplitz602): Create a class for these messages so we don't keep using StringMap
|
// TODO(jteplitz602): Create a class for these messages so we don't keep using StringMap
|
||||||
var message = {'type': args.type, 'method': args.method, 'args': fnArgs, 'id': id};
|
var message = {'type': args.type, 'method': args.method, 'args': fnArgs};
|
||||||
|
if (id != null) {
|
||||||
|
message['id'] = id;
|
||||||
|
}
|
||||||
this._messageBus.sink.send(message);
|
this._messageBus.sink.send(message);
|
||||||
return completer.promise;
|
|
||||||
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleMessage(message: StringMap<string, any>): void {
|
private _handleMessage(message: StringMap<string, any>): void {
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import {isPresent, print, BaseException} from 'angular2/src/facade/lang';
|
||||||
|
import {ListWrapper, isListLikeIterable} from 'angular2/src/facade/collection';
|
||||||
|
import {ExceptionHandler} from 'angular2/src/core/exception_handler';
|
||||||
|
import {Injectable} from 'angular2/di';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class WorkerExceptionHandler implements ExceptionHandler {
|
||||||
|
logError: Function = print;
|
||||||
|
|
||||||
|
call(exception: Object, stackTrace: any = null, reason: string = null) {
|
||||||
|
var longStackTrace = isListLikeIterable(stackTrace) ?
|
||||||
|
(<any>stackTrace).join("\n\n-----async gap-----\n") :
|
||||||
|
stackTrace;
|
||||||
|
|
||||||
|
this.logError(`${exception}\n\n${longStackTrace}`);
|
||||||
|
|
||||||
|
if (isPresent(reason)) {
|
||||||
|
this.logError(`Reason: ${reason}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
var context = this._findContext(exception);
|
||||||
|
if (isPresent(context)) {
|
||||||
|
this.logError("Error Context:");
|
||||||
|
this.logError(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
_findContext(exception: any): any {
|
||||||
|
if (!(exception instanceof BaseException)) return null;
|
||||||
|
return isPresent(exception.context) ? exception.context :
|
||||||
|
this._findContext(exception.originalException);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,260 @@
|
||||||
|
import {
|
||||||
|
Renderer,
|
||||||
|
RenderCompiler,
|
||||||
|
DirectiveMetadata,
|
||||||
|
ProtoViewDto,
|
||||||
|
ViewDefinition,
|
||||||
|
RenderProtoViewRef,
|
||||||
|
RenderViewRef,
|
||||||
|
RenderElementRef,
|
||||||
|
RenderEventDispatcher,
|
||||||
|
RenderProtoViewMergeMapping,
|
||||||
|
RenderViewWithFragments,
|
||||||
|
RenderFragmentRef
|
||||||
|
} from 'angular2/src/render/api';
|
||||||
|
import {Promise, PromiseWrapper} from "angular2/src/facade/async";
|
||||||
|
import {MessageBroker, FnArg, UiArguments} from "angular2/src/web-workers/worker/broker";
|
||||||
|
import {isPresent, print, BaseException} from "angular2/src/facade/lang";
|
||||||
|
import {Injectable} from "angular2/di";
|
||||||
|
import {
|
||||||
|
RenderViewWithFragmentsStore,
|
||||||
|
WorkerRenderViewRef
|
||||||
|
} from 'angular2/src/web-workers/shared/render_view_with_fragments_store';
|
||||||
|
import {WorkerElementRef} from 'angular2/src/web-workers/shared/api';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class WorkerCompiler implements RenderCompiler {
|
||||||
|
constructor(private _messageBroker: MessageBroker) {}
|
||||||
|
/**
|
||||||
|
* Creats a ProtoViewDto that contains a single nested component with the given componentId.
|
||||||
|
*/
|
||||||
|
compileHost(directiveMetadata: DirectiveMetadata): Promise<ProtoViewDto> {
|
||||||
|
var fnArgs: List<FnArg> = [new FnArg(directiveMetadata, DirectiveMetadata)];
|
||||||
|
var args: UiArguments = new UiArguments("compiler", "compileHost", fnArgs);
|
||||||
|
return this._messageBroker.runOnUiThread(args, ProtoViewDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compiles a single DomProtoView. Non recursive so that
|
||||||
|
* we don't need to serialize all possible components over the wire,
|
||||||
|
* but only the needed ones based on previous calls.
|
||||||
|
*/
|
||||||
|
compile(view: ViewDefinition): Promise<ProtoViewDto> {
|
||||||
|
var fnArgs: List<FnArg> = [new FnArg(view, ViewDefinition)];
|
||||||
|
var args: UiArguments = new UiArguments("compiler", "compile", fnArgs);
|
||||||
|
return this._messageBroker.runOnUiThread(args, ProtoViewDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges ProtoViews.
|
||||||
|
* The first entry of the array is the protoview into which all the other entries of the array
|
||||||
|
* should be merged.
|
||||||
|
* If the array contains other arrays, they will be merged before processing the parent array.
|
||||||
|
* The array must contain an entry for every component and embedded ProtoView of the first entry.
|
||||||
|
* @param protoViewRefs List of ProtoViewRefs or nested
|
||||||
|
* @return the merge result for every input array in depth first order.
|
||||||
|
*/
|
||||||
|
mergeProtoViewsRecursively(
|
||||||
|
protoViewRefs: List<RenderProtoViewRef | List<any>>): Promise<RenderProtoViewMergeMapping> {
|
||||||
|
var fnArgs: List<FnArg> = [new FnArg(protoViewRefs, RenderProtoViewRef)];
|
||||||
|
var args: UiArguments = new UiArguments("compiler", "mergeProtoViewsRecursively", fnArgs);
|
||||||
|
return this._messageBroker.runOnUiThread(args, RenderProtoViewMergeMapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class WorkerRenderer implements Renderer {
|
||||||
|
constructor(private _messageBroker: MessageBroker,
|
||||||
|
private _renderViewStore: RenderViewWithFragmentsStore) {}
|
||||||
|
/**
|
||||||
|
* Creates a root host view that includes the given element.
|
||||||
|
* Note that the fragmentCount needs to be passed in so that we can create a result
|
||||||
|
* synchronously even when dealing with webworkers!
|
||||||
|
*
|
||||||
|
* @param {RenderProtoViewRef} hostProtoViewRef a RenderProtoViewRef of type
|
||||||
|
* ProtoViewDto.HOST_VIEW_TYPE
|
||||||
|
* @param {any} hostElementSelector css selector for the host element (will be queried against the
|
||||||
|
* main document)
|
||||||
|
* @return {RenderViewRef} the created view
|
||||||
|
*/
|
||||||
|
createRootHostView(hostProtoViewRef: RenderProtoViewRef, fragmentCount: number,
|
||||||
|
hostElementSelector: string): RenderViewWithFragments {
|
||||||
|
return this._createViewHelper(hostProtoViewRef, fragmentCount, hostElementSelector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a regular view out of the given ProtoView
|
||||||
|
* Note that the fragmentCount needs to be passed in so that we can create a result
|
||||||
|
* synchronously even when dealing with webworkers!
|
||||||
|
*/
|
||||||
|
createView(protoViewRef: RenderProtoViewRef, fragmentCount: number): RenderViewWithFragments {
|
||||||
|
return this._createViewHelper(protoViewRef, fragmentCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _createViewHelper(protoViewRef: RenderProtoViewRef, fragmentCount: number,
|
||||||
|
hostElementSelector?: string): RenderViewWithFragments {
|
||||||
|
var renderViewWithFragments = this._renderViewStore.allocate(fragmentCount);
|
||||||
|
|
||||||
|
var startIndex = (<WorkerRenderViewRef>(renderViewWithFragments.viewRef)).refNumber;
|
||||||
|
var fnArgs: List<FnArg> = [
|
||||||
|
new FnArg(protoViewRef, RenderProtoViewRef),
|
||||||
|
new FnArg(fragmentCount, null),
|
||||||
|
];
|
||||||
|
var method = "createView";
|
||||||
|
if (isPresent(hostElementSelector) && hostElementSelector != null) {
|
||||||
|
fnArgs.push(new FnArg(hostElementSelector, null));
|
||||||
|
method = "createRootHostView";
|
||||||
|
}
|
||||||
|
fnArgs.push(new FnArg(startIndex, null));
|
||||||
|
|
||||||
|
var args = new UiArguments("renderer", method, fnArgs);
|
||||||
|
this._messageBroker.runOnUiThread(args, null);
|
||||||
|
|
||||||
|
return renderViewWithFragments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys the given view after it has been dehydrated and detached
|
||||||
|
*/
|
||||||
|
destroyView(viewRef: RenderViewRef) {
|
||||||
|
var fnArgs = [new FnArg(viewRef, RenderViewRef)];
|
||||||
|
var args = new UiArguments("renderer", "destroyView", fnArgs);
|
||||||
|
this._messageBroker.runOnUiThread(args, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches a fragment after another fragment.
|
||||||
|
*/
|
||||||
|
attachFragmentAfterFragment(previousFragmentRef: RenderFragmentRef,
|
||||||
|
fragmentRef: RenderFragmentRef) {
|
||||||
|
var fnArgs = [
|
||||||
|
new FnArg(previousFragmentRef, RenderFragmentRef),
|
||||||
|
new FnArg(fragmentRef, RenderFragmentRef)
|
||||||
|
];
|
||||||
|
var args = new UiArguments("renderer", "attachFragmentAfterFragment", fnArgs);
|
||||||
|
this._messageBroker.runOnUiThread(args, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches a fragment after an element.
|
||||||
|
*/
|
||||||
|
attachFragmentAfterElement(elementRef: RenderElementRef, fragmentRef: RenderFragmentRef) {
|
||||||
|
var fnArgs =
|
||||||
|
[new FnArg(elementRef, WorkerElementRef), new FnArg(fragmentRef, RenderFragmentRef)];
|
||||||
|
var args = new UiArguments("renderer", "attachFragmentAfterElement", fnArgs);
|
||||||
|
this._messageBroker.runOnUiThread(args, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detaches a fragment.
|
||||||
|
*/
|
||||||
|
detachFragment(fragmentRef: RenderFragmentRef) {
|
||||||
|
var fnArgs = [new FnArg(fragmentRef, RenderFragmentRef)];
|
||||||
|
var args = new UiArguments("renderer", "detachFragment", fnArgs);
|
||||||
|
this._messageBroker.runOnUiThread(args, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hydrates a view after it has been attached. Hydration/dehydration is used for reusing views
|
||||||
|
* inside of the view pool.
|
||||||
|
*/
|
||||||
|
hydrateView(viewRef: RenderViewRef) {
|
||||||
|
var fnArgs = [new FnArg(viewRef, RenderViewRef)];
|
||||||
|
var args = new UiArguments("renderer", "hydrateView", fnArgs);
|
||||||
|
this._messageBroker.runOnUiThread(args, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dehydrates a view after it has been attached. Hydration/dehydration is used for reusing views
|
||||||
|
* inside of the view pool.
|
||||||
|
*/
|
||||||
|
dehydrateView(viewRef: RenderViewRef) {
|
||||||
|
var fnArgs = [new FnArg(viewRef, RenderViewRef)];
|
||||||
|
var args = new UiArguments("renderer", "deyhdrateView", fnArgs);
|
||||||
|
this._messageBroker.runOnUiThread(args, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the native element at the given location.
|
||||||
|
* Attention: In a WebWorker scenario, this should always return null!
|
||||||
|
*/
|
||||||
|
getNativeElementSync(location: RenderElementRef): any { return null; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a property on an element.
|
||||||
|
*/
|
||||||
|
setElementProperty(location: RenderElementRef, propertyName: string, propertyValue: any) {
|
||||||
|
var fnArgs = [
|
||||||
|
new FnArg(location, WorkerElementRef),
|
||||||
|
new FnArg(propertyName, null),
|
||||||
|
new FnArg(propertyValue, null)
|
||||||
|
];
|
||||||
|
var args = new UiArguments("renderer", "setElementProperty", fnArgs);
|
||||||
|
this._messageBroker.runOnUiThread(args, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets an attribute on an element.
|
||||||
|
*/
|
||||||
|
setElementAttribute(location: RenderElementRef, attributeName: string, attributeValue: string) {
|
||||||
|
var fnArgs = [
|
||||||
|
new FnArg(location, WorkerElementRef),
|
||||||
|
new FnArg(attributeName, null),
|
||||||
|
new FnArg(attributeValue, null)
|
||||||
|
];
|
||||||
|
var args = new UiArguments("renderer", "setElementAttribute", fnArgs);
|
||||||
|
this._messageBroker.runOnUiThread(args, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a class on an element.
|
||||||
|
*/
|
||||||
|
setElementClass(location: RenderElementRef, className: string, isAdd: boolean) {
|
||||||
|
var fnArgs =
|
||||||
|
[new FnArg(location, WorkerElementRef), new FnArg(className, null), new FnArg(isAdd, null)];
|
||||||
|
var args = new UiArguments("renderer", "setElementClass", fnArgs);
|
||||||
|
this._messageBroker.runOnUiThread(args, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a style on an element.
|
||||||
|
*/
|
||||||
|
setElementStyle(location: RenderElementRef, styleName: string, styleValue: string) {
|
||||||
|
var fnArgs = [
|
||||||
|
new FnArg(location, WorkerElementRef),
|
||||||
|
new FnArg(styleName, null),
|
||||||
|
new FnArg(styleValue, null)
|
||||||
|
];
|
||||||
|
var args = new UiArguments("renderer", "setElementStyle", fnArgs);
|
||||||
|
this._messageBroker.runOnUiThread(args, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls a method on an element.
|
||||||
|
* Note: For now we're assuming that everything in the args list are primitive
|
||||||
|
*/
|
||||||
|
invokeElementMethod(location: RenderElementRef, methodName: string, args: List<any>) {
|
||||||
|
var fnArgs =
|
||||||
|
[new FnArg(location, WorkerElementRef), new FnArg(methodName, null), new FnArg(args, null)];
|
||||||
|
var uiArgs = new UiArguments("renderer", "invokeElementMethod", fnArgs);
|
||||||
|
this._messageBroker.runOnUiThread(uiArgs, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of a text node.
|
||||||
|
*/
|
||||||
|
setText(viewRef: RenderViewRef, textNodeIndex: number, text: string) {
|
||||||
|
var fnArgs =
|
||||||
|
[new FnArg(viewRef, RenderViewRef), new FnArg(textNodeIndex, null), new FnArg(text, null)];
|
||||||
|
var args = new UiArguments("renderer", "setText", fnArgs);
|
||||||
|
this._messageBroker.runOnUiThread(args, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the dispatcher for all events of the given view
|
||||||
|
*/
|
||||||
|
setEventDispatcher(viewRef: RenderViewRef, dispatcher: RenderEventDispatcher) {
|
||||||
|
// TODO(jteplitz602) support dom events in web worker. See #3046
|
||||||
|
}
|
||||||
|
}
|
|
@ -58,7 +58,8 @@ export function main() {
|
||||||
|
|
||||||
protoViewFactory = new FakeProtoViewFactory(protoViewFactoryResults);
|
protoViewFactory = new FakeProtoViewFactory(protoViewFactoryResults);
|
||||||
return new Compiler(directiveResolver, new CompilerCache(), tplResolver, cmpUrlMapper,
|
return new Compiler(directiveResolver, new CompilerCache(), tplResolver, cmpUrlMapper,
|
||||||
urlResolver, renderCompiler, protoViewFactory, new FakeAppRootUrl());
|
urlResolver, renderCompiler, protoViewFactory,
|
||||||
|
new AppRootUrl("http://www.app.com"));
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -398,8 +399,9 @@ export function main() {
|
||||||
var reader: any = new SpyDirectiveResolver();
|
var reader: any = new SpyDirectiveResolver();
|
||||||
|
|
||||||
// create the compiler
|
// create the compiler
|
||||||
var compiler = new Compiler(reader, cache, tplResolver, cmpUrlMapper, new UrlResolver(),
|
var compiler =
|
||||||
renderCompiler, protoViewFactory, new FakeAppRootUrl());
|
new Compiler(reader, cache, tplResolver, cmpUrlMapper, new UrlResolver(),
|
||||||
|
renderCompiler, protoViewFactory, new AppRootUrl("http://www.app.com"));
|
||||||
compiler.compileInHost(MainComponent)
|
compiler.compileInHost(MainComponent)
|
||||||
.then((protoViewRef) => {
|
.then((protoViewRef) => {
|
||||||
// the test should have failed if the resolver was called, so we're good
|
// the test should have failed if the resolver was called, so we're good
|
||||||
|
@ -669,10 +671,6 @@ class SpyDirectiveResolver extends SpyObject {
|
||||||
noSuchMethod(m) { return super.noSuchMethod(m) }
|
noSuchMethod(m) { return super.noSuchMethod(m) }
|
||||||
}
|
}
|
||||||
|
|
||||||
class FakeAppRootUrl extends AppRootUrl {
|
|
||||||
get value() { return 'http://www.app.com'; }
|
|
||||||
}
|
|
||||||
|
|
||||||
class FakeViewResolver extends ViewResolver {
|
class FakeViewResolver extends ViewResolver {
|
||||||
_cmpViews: Map<Type, viewAnn.View> = new Map();
|
_cmpViews: Map<Type, viewAnn.View> = new Map();
|
||||||
|
|
||||||
|
|
|
@ -276,5 +276,5 @@ export function main() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var someComponent = DirectiveMetadata.create(
|
export var someComponent = DirectiveMetadata.create(
|
||||||
{id: 'someComponent', type: DirectiveMetadata.COMPONENT_TYPE, selector: 'some-comp'});
|
{id: 'someComponent', type: DirectiveMetadata.COMPONENT_TYPE, selector: 'some-comp'});
|
||||||
|
|
|
@ -68,7 +68,7 @@ export class DomTestbed {
|
||||||
@Inject(DOCUMENT_TOKEN) document) {
|
@Inject(DOCUMENT_TOKEN) document) {
|
||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
this.compiler = compiler;
|
this.compiler = compiler;
|
||||||
this.rootEl = el('<div id="root"></div>');
|
this.rootEl = el('<div id="root" class="rootElem"></div>');
|
||||||
var oldRoots = DOM.querySelectorAll(document, '#root');
|
var oldRoots = DOM.querySelectorAll(document, '#root');
|
||||||
for (var i = 0; i < oldRoots.length; i++) {
|
for (var i = 0; i < oldRoots.length; i++) {
|
||||||
DOM.remove(oldRoots[i]);
|
DOM.remove(oldRoots[i]);
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
import {AsyncTestCompleter, inject, describe, it, expect} from "angular2/test_lib";
|
||||||
|
import {RenderProtoViewRef} from "angular2/src/render/api";
|
||||||
|
import {RenderProtoViewRefStore} from "angular2/src/web-workers/shared/render_proto_view_ref_store";
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe("RenderProtoViewRefStore", () => {
|
||||||
|
it("should store and return the correct reference", () => {
|
||||||
|
var store = new RenderProtoViewRefStore(true);
|
||||||
|
var ref1 = new RenderProtoViewRef();
|
||||||
|
var index1 = store.storeRenderProtoViewRef(ref1);
|
||||||
|
expect(store.retreiveRenderProtoViewRef(index1)).toBe(ref1);
|
||||||
|
var ref2 = new RenderProtoViewRef();
|
||||||
|
var index2 = store.storeRenderProtoViewRef(ref2);
|
||||||
|
expect(store.retreiveRenderProtoViewRef(index2)).toBe(ref2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should cache index numbers", () => {
|
||||||
|
var store = new RenderProtoViewRefStore(true);
|
||||||
|
var ref = new RenderProtoViewRef();
|
||||||
|
var index = store.storeRenderProtoViewRef(ref);
|
||||||
|
expect(store.storeRenderProtoViewRef(ref)).toEqual(index);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
import {AsyncTestCompleter, beforeEach, inject, describe, it, expect} from "angular2/test_lib";
|
||||||
|
import {RenderViewWithFragments, RenderViewRef, RenderFragmentRef} from "angular2/src/render/api";
|
||||||
|
import {
|
||||||
|
RenderViewWithFragmentsStore,
|
||||||
|
WorkerRenderViewRef,
|
||||||
|
WorkerRenderFragmentRef
|
||||||
|
} from "angular2/src/web-workers/shared/render_view_with_fragments_store";
|
||||||
|
import {List, ListWrapper} from "angular2/src/facade/collection";
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe("RenderViewWithFragmentsStore", () => {
|
||||||
|
describe("on WebWorker", () => {
|
||||||
|
var store;
|
||||||
|
beforeEach(() => { store = new RenderViewWithFragmentsStore(true); });
|
||||||
|
|
||||||
|
it("should allocate fragmentCount + 1 refs", () => {
|
||||||
|
var view: RenderViewWithFragments = store.allocate(10);
|
||||||
|
|
||||||
|
var viewRef: WorkerRenderViewRef = <WorkerRenderViewRef>view.viewRef;
|
||||||
|
expect(viewRef.refNumber).toEqual(0);
|
||||||
|
|
||||||
|
var fragmentRefs: List<WorkerRenderFragmentRef> =
|
||||||
|
<List<WorkerRenderFragmentRef>>view.fragmentRefs;
|
||||||
|
expect(fragmentRefs.length).toEqual(10);
|
||||||
|
|
||||||
|
for (var i = 0; i < fragmentRefs.length; i++) {
|
||||||
|
expect(fragmentRefs[i].refNumber).toEqual(i + 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not reuse a reference", () => {
|
||||||
|
store.allocate(10);
|
||||||
|
var view = store.allocate(0);
|
||||||
|
var viewRef = <WorkerRenderViewRef>view.viewRef;
|
||||||
|
expect(viewRef.refNumber).toEqual(11);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be serializable", () => {
|
||||||
|
var view = store.allocate(1);
|
||||||
|
expect(store.deserializeViewWithFragments(store.serializeViewWithFragments(view)))
|
||||||
|
.toEqual(view);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("on UI", () => {
|
||||||
|
var store;
|
||||||
|
beforeEach(() => { store = new RenderViewWithFragmentsStore(false); });
|
||||||
|
function createMockRenderViewWithFragments(): RenderViewWithFragments {
|
||||||
|
var view = new MockRenderViewRef();
|
||||||
|
var fragments = ListWrapper.createGrowableSize(20);
|
||||||
|
for (var i = 0; i < 20; i++) {
|
||||||
|
fragments[i] = new MockRenderFragmentRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RenderViewWithFragments(view, fragments);
|
||||||
|
}
|
||||||
|
it("should associate views with the correct references", () => {
|
||||||
|
var renderViewWithFragments = createMockRenderViewWithFragments();
|
||||||
|
|
||||||
|
store.store(renderViewWithFragments, 100);
|
||||||
|
expect(store.retreive(100)).toBe(renderViewWithFragments.viewRef);
|
||||||
|
|
||||||
|
for (var i = 0; i < renderViewWithFragments.fragmentRefs.length; i++) {
|
||||||
|
expect(store.retreive(101 + i)).toBe(renderViewWithFragments.fragmentRefs[i]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("RenderViewWithFragments", () => {
|
||||||
|
it("should be serializable", () => {
|
||||||
|
var renderViewWithFragments = createMockRenderViewWithFragments();
|
||||||
|
store.store(renderViewWithFragments, 0);
|
||||||
|
|
||||||
|
var deserialized = store.deserializeViewWithFragments(
|
||||||
|
store.serializeViewWithFragments(renderViewWithFragments));
|
||||||
|
expect(deserialized.viewRef).toBe(renderViewWithFragments.viewRef);
|
||||||
|
|
||||||
|
expect(deserialized.fragmentRefs.length)
|
||||||
|
.toEqual(renderViewWithFragments.fragmentRefs.length);
|
||||||
|
|
||||||
|
for (var i = 0; i < deserialized.fragmentRefs.length; i++) {
|
||||||
|
var val = deserialized.fragmentRefs[i];
|
||||||
|
expect(val).toBe(renderViewWithFragments.fragmentRefs[i]);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("RenderViewRef", () => {
|
||||||
|
it("should be serializable", () => {
|
||||||
|
var renderViewWithFragments = createMockRenderViewWithFragments();
|
||||||
|
store.store(renderViewWithFragments, 0);
|
||||||
|
|
||||||
|
var deserialized = store.deserializeRenderViewRef(
|
||||||
|
store.serializeRenderViewRef(renderViewWithFragments.viewRef));
|
||||||
|
expect(deserialized).toBe(renderViewWithFragments.viewRef);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("RenderFragmentRef", () => {
|
||||||
|
it("should be serializable", () => {
|
||||||
|
var renderViewWithFragments = createMockRenderViewWithFragments();
|
||||||
|
store.store(renderViewWithFragments, 0);
|
||||||
|
|
||||||
|
var serialized =
|
||||||
|
store.serializeRenderFragmentRef(renderViewWithFragments.fragmentRefs[0]);
|
||||||
|
var deserialized = store.deserializeRenderFragmentRef(serialized);
|
||||||
|
|
||||||
|
expect(deserialized).toBe(renderViewWithFragments.fragmentRefs[0]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockRenderViewRef extends RenderViewRef {
|
||||||
|
constructor() { super(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockRenderFragmentRef extends RenderFragmentRef {
|
||||||
|
constructor() { super(); }
|
||||||
|
}
|
|
@ -0,0 +1,343 @@
|
||||||
|
import {
|
||||||
|
AsyncTestCompleter,
|
||||||
|
inject,
|
||||||
|
describe,
|
||||||
|
it,
|
||||||
|
expect,
|
||||||
|
beforeEach,
|
||||||
|
createTestInjector,
|
||||||
|
beforeEachBindings
|
||||||
|
} from "angular2/test_lib";
|
||||||
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
|
import {DomTestbed, TestRootView, elRef} from '../../render/dom/dom_testbed';
|
||||||
|
import {bind} from 'angular2/di';
|
||||||
|
import {WorkerCompiler, WorkerRenderer} from "angular2/src/web-workers/worker/renderer";
|
||||||
|
import {MessageBroker, UiArguments, FnArg} from "angular2/src/web-workers/worker/broker";
|
||||||
|
import {Serializer} from "angular2/src/web-workers/shared/serializer";
|
||||||
|
import {isPresent, isBlank, BaseException, Type} from "angular2/src/facade/lang";
|
||||||
|
import {MapWrapper, ListWrapper} from "angular2/src/facade/collection";
|
||||||
|
import {
|
||||||
|
DirectiveMetadata,
|
||||||
|
ProtoViewDto,
|
||||||
|
RenderProtoViewRef,
|
||||||
|
RenderViewWithFragments,
|
||||||
|
ViewDefinition,
|
||||||
|
RenderProtoViewMergeMapping,
|
||||||
|
RenderViewRef,
|
||||||
|
RenderFragmentRef
|
||||||
|
} from "angular2/src/render/api";
|
||||||
|
import {
|
||||||
|
MessageBus,
|
||||||
|
MessageBusSource,
|
||||||
|
MessageBusSink,
|
||||||
|
SourceListener
|
||||||
|
} from "angular2/src/web-workers/shared/message_bus";
|
||||||
|
import {
|
||||||
|
RenderProtoViewRefStore,
|
||||||
|
WebworkerRenderProtoViewRef
|
||||||
|
} from "angular2/src/web-workers/shared/render_proto_view_ref_store";
|
||||||
|
import {
|
||||||
|
RenderViewWithFragmentsStore,
|
||||||
|
WorkerRenderViewRef
|
||||||
|
} from 'angular2/src/web-workers/shared/render_view_with_fragments_store';
|
||||||
|
import {resolveInternalDomProtoView} from 'angular2/src/render/dom/view/proto_view';
|
||||||
|
import {someComponent} from '../../render/dom/dom_renderer_integration_spec';
|
||||||
|
import {WebWorkerMain} from 'angular2/src/web-workers/ui/impl';
|
||||||
|
import {AnchorBasedAppRootUrl} from 'angular2/src/services/anchor_based_app_root_url';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
function createBroker(workerSerializer: Serializer, uiSerializer: Serializer, tb: DomTestbed,
|
||||||
|
uiRenderViewStore: RenderViewWithFragmentsStore,
|
||||||
|
workerRenderViewStore: RenderViewWithFragmentsStore): MessageBroker {
|
||||||
|
// set up the two message buses to pass messages to each other
|
||||||
|
var uiMessageBus = new MockMessageBus(new MockMessageBusSink(), new MockMessageBusSource());
|
||||||
|
var workerMessageBus = new MockMessageBus(new MockMessageBusSink(), new MockMessageBusSource());
|
||||||
|
uiMessageBus.attachToBus(workerMessageBus);
|
||||||
|
workerMessageBus.attachToBus(uiMessageBus);
|
||||||
|
|
||||||
|
// set up the worker side
|
||||||
|
var broker = new MessageBroker(workerMessageBus, workerSerializer);
|
||||||
|
|
||||||
|
// set up the ui side
|
||||||
|
var webWorkerMain = new WebWorkerMain(tb.compiler, tb.renderer, uiRenderViewStore, uiSerializer,
|
||||||
|
new AnchorBasedAppRootUrl());
|
||||||
|
webWorkerMain.attachToWorker(uiMessageBus);
|
||||||
|
return broker;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createWorkerRenderer(workerSerializer: Serializer, uiSerializer: Serializer,
|
||||||
|
tb: DomTestbed, uiRenderViewStore: RenderViewWithFragmentsStore,
|
||||||
|
workerRenderViewStore: RenderViewWithFragmentsStore):
|
||||||
|
WorkerRenderer {
|
||||||
|
var broker =
|
||||||
|
createBroker(workerSerializer, uiSerializer, tb, uiRenderViewStore, workerRenderViewStore);
|
||||||
|
return new WorkerRenderer(broker, workerRenderViewStore);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createWorkerCompiler(workerSerializer: Serializer, uiSerializer: Serializer,
|
||||||
|
tb: DomTestbed): WorkerCompiler {
|
||||||
|
var broker = createBroker(workerSerializer, uiSerializer, tb, null, null);
|
||||||
|
return new WorkerCompiler(broker);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Web Worker Compiler", function() {
|
||||||
|
var workerSerializer: Serializer;
|
||||||
|
var uiSerializer: Serializer;
|
||||||
|
var workerRenderProtoViewRefStore: RenderProtoViewRefStore;
|
||||||
|
var uiRenderProtoViewRefStore: RenderProtoViewRefStore;
|
||||||
|
var tb: DomTestbed;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
workerRenderProtoViewRefStore = new RenderProtoViewRefStore(true);
|
||||||
|
uiRenderProtoViewRefStore = new RenderProtoViewRefStore(false);
|
||||||
|
workerSerializer = createSerializer(workerRenderProtoViewRefStore, null);
|
||||||
|
uiSerializer = createSerializer(uiRenderProtoViewRefStore, null);
|
||||||
|
tb = createTestInjector([DomTestbed]).get(DomTestbed);
|
||||||
|
});
|
||||||
|
|
||||||
|
function resolveWebWorkerRef(ref: RenderProtoViewRef) {
|
||||||
|
var refNumber = (<WebworkerRenderProtoViewRef>ref).refNumber;
|
||||||
|
return resolveInternalDomProtoView(uiRenderProtoViewRefStore.deserialize(refNumber));
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should build the proto view', inject([AsyncTestCompleter], (async) => {
|
||||||
|
var compiler: WorkerCompiler = createWorkerCompiler(workerSerializer, uiSerializer, tb);
|
||||||
|
|
||||||
|
var dirMetadata = DirectiveMetadata.create(
|
||||||
|
{id: 'id', selector: 'CUSTOM', type: DirectiveMetadata.COMPONENT_TYPE});
|
||||||
|
compiler.compileHost(dirMetadata)
|
||||||
|
.then((protoView) => {
|
||||||
|
expect(DOM.tagName(DOM.firstChild(
|
||||||
|
DOM.content(resolveWebWorkerRef(protoView.render).rootElement))))
|
||||||
|
.toEqual('CUSTOM');
|
||||||
|
expect(protoView).not.toBeNull();
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Web Worker Renderer", () => {
|
||||||
|
beforeEachBindings(() => [DomTestbed]);
|
||||||
|
var renderer: WorkerRenderer;
|
||||||
|
var workerSerializer: Serializer;
|
||||||
|
var workerRenderViewStore: RenderViewWithFragmentsStore;
|
||||||
|
var uiRenderViewStore: RenderViewWithFragmentsStore;
|
||||||
|
var uiSerializer: Serializer;
|
||||||
|
var tb: DomTestbed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seriliazes the given obj with the uiSerializer and then returns the version that
|
||||||
|
* the worker would deserialize
|
||||||
|
*/
|
||||||
|
function serialize(obj: any, type: Type): any {
|
||||||
|
var serialized = uiSerializer.serialize(obj, type);
|
||||||
|
return workerSerializer.deserialize(serialized, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
workerRenderViewStore = new RenderViewWithFragmentsStore(true);
|
||||||
|
tb = createTestInjector([DomTestbed]).get(DomTestbed);
|
||||||
|
uiRenderViewStore = new RenderViewWithFragmentsStore(false);
|
||||||
|
workerSerializer = createSerializer(new RenderProtoViewRefStore(true), workerRenderViewStore);
|
||||||
|
uiSerializer = createSerializer(new RenderProtoViewRefStore(false), uiRenderViewStore);
|
||||||
|
renderer = createWorkerRenderer(workerSerializer, uiSerializer, tb, uiRenderViewStore,
|
||||||
|
workerRenderViewStore);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should create and destroy root host views while using the given elements in place',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
tb.compiler.compileHost(someComponent)
|
||||||
|
.then((hostProtoViewDto: any) => {
|
||||||
|
hostProtoViewDto = serialize(hostProtoViewDto, ProtoViewDto);
|
||||||
|
var viewWithFragments =
|
||||||
|
renderer.createRootHostView(hostProtoViewDto.render, 1, '#root');
|
||||||
|
var view = new WorkerTestRootView(viewWithFragments, uiRenderViewStore);
|
||||||
|
|
||||||
|
expect(tb.rootEl.parentNode).toBeTruthy();
|
||||||
|
expect(view.hostElement).toBe(tb.rootEl);
|
||||||
|
|
||||||
|
renderer.detachFragment(viewWithFragments.fragmentRefs[0]);
|
||||||
|
renderer.destroyView(viewWithFragments.viewRef);
|
||||||
|
expect(tb.rootEl.parentNode).toBeFalsy();
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should update text nodes', inject([AsyncTestCompleter], (async) => {
|
||||||
|
tb.compileAndMerge(
|
||||||
|
someComponent,
|
||||||
|
[
|
||||||
|
new ViewDefinition(
|
||||||
|
{componentId: 'someComponent', template: '{{a}}', directives: []})
|
||||||
|
])
|
||||||
|
.then((protoViewMergeMappings) => {
|
||||||
|
protoViewMergeMappings =
|
||||||
|
serialize(protoViewMergeMappings, RenderProtoViewMergeMapping);
|
||||||
|
var rootView = renderer.createView(protoViewMergeMappings.mergedProtoViewRef, 1);
|
||||||
|
renderer.hydrateView(rootView.viewRef);
|
||||||
|
|
||||||
|
renderer.setText(rootView.viewRef, 0, 'hello');
|
||||||
|
var view = new WorkerTestRootView(rootView, uiRenderViewStore);
|
||||||
|
expect(view.hostElement).toHaveText('hello');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should update any element property/attributes/class/style independent of the compilation on the root element and other elements',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
tb.compileAndMerge(someComponent,
|
||||||
|
[
|
||||||
|
new ViewDefinition({
|
||||||
|
componentId: 'someComponent',
|
||||||
|
template: '<input [title]="y" style="position:absolute">',
|
||||||
|
directives: []
|
||||||
|
})
|
||||||
|
])
|
||||||
|
.then((protoViewMergeMappings) => {
|
||||||
|
protoViewMergeMappings =
|
||||||
|
serialize(protoViewMergeMappings, RenderProtoViewMergeMapping);
|
||||||
|
|
||||||
|
var checkSetters = (elr, el) => {
|
||||||
|
renderer.setElementProperty(elr, 'tabIndex', 1);
|
||||||
|
expect((<HTMLInputElement>el).tabIndex).toEqual(1);
|
||||||
|
|
||||||
|
renderer.setElementClass(elr, 'a', true);
|
||||||
|
expect(DOM.hasClass(el, 'a')).toBe(true);
|
||||||
|
renderer.setElementClass(elr, 'a', false);
|
||||||
|
expect(DOM.hasClass(el, 'a')).toBe(false);
|
||||||
|
|
||||||
|
renderer.setElementStyle(elr, 'width', '10px');
|
||||||
|
expect(DOM.getStyle(el, 'width')).toEqual('10px');
|
||||||
|
renderer.setElementStyle(elr, 'width', null);
|
||||||
|
expect(DOM.getStyle(el, 'width')).toEqual('');
|
||||||
|
|
||||||
|
renderer.setElementAttribute(elr, 'someAttr', 'someValue');
|
||||||
|
expect(DOM.getAttribute(el, 'some-attr')).toEqual('someValue');
|
||||||
|
};
|
||||||
|
|
||||||
|
var rootViewWithFragments =
|
||||||
|
renderer.createView(protoViewMergeMappings.mergedProtoViewRef, 1);
|
||||||
|
renderer.hydrateView(rootViewWithFragments.viewRef);
|
||||||
|
|
||||||
|
var rootView = new WorkerTestRootView(rootViewWithFragments, uiRenderViewStore);
|
||||||
|
// root element
|
||||||
|
checkSetters(elRef(rootViewWithFragments.viewRef, 0), rootView.hostElement);
|
||||||
|
// nested elements
|
||||||
|
checkSetters(elRef(rootViewWithFragments.viewRef, 1),
|
||||||
|
DOM.firstChild(rootView.hostElement));
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should add and remove empty fragments', inject([AsyncTestCompleter], (async) => {
|
||||||
|
tb.compileAndMerge(someComponent,
|
||||||
|
[
|
||||||
|
new ViewDefinition({
|
||||||
|
componentId: 'someComponent',
|
||||||
|
template: '<template></template><template></template>',
|
||||||
|
directives: []
|
||||||
|
})
|
||||||
|
])
|
||||||
|
.then((protoViewMergeMappings) => {
|
||||||
|
protoViewMergeMappings =
|
||||||
|
serialize(protoViewMergeMappings, RenderProtoViewMergeMapping);
|
||||||
|
var rootViewWithFragments =
|
||||||
|
renderer.createView(protoViewMergeMappings.mergedProtoViewRef, 3);
|
||||||
|
|
||||||
|
var elr = elRef(rootViewWithFragments.viewRef, 1);
|
||||||
|
var rootView = new WorkerTestRootView(rootViewWithFragments, uiRenderViewStore);
|
||||||
|
expect(rootView.hostElement).toHaveText('');
|
||||||
|
var fragment = rootViewWithFragments.fragmentRefs[1];
|
||||||
|
var fragment2 = rootViewWithFragments.fragmentRefs[2];
|
||||||
|
renderer.attachFragmentAfterElement(elr, fragment);
|
||||||
|
renderer.attachFragmentAfterFragment(fragment, fragment2);
|
||||||
|
renderer.detachFragment(fragment);
|
||||||
|
renderer.detachFragment(fragment2);
|
||||||
|
expect(rootView.hostElement).toHaveText('');
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (DOM.supportsDOMEvents()) {
|
||||||
|
it('should call actions on the element independent of the compilation',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
tb.compileAndMerge(someComponent,
|
||||||
|
[
|
||||||
|
new ViewDefinition({
|
||||||
|
componentId: 'someComponent',
|
||||||
|
template: '<input [title]="y"></input>',
|
||||||
|
directives: []
|
||||||
|
})
|
||||||
|
])
|
||||||
|
.then((protoViewMergeMappings) => {
|
||||||
|
protoViewMergeMappings =
|
||||||
|
serialize(protoViewMergeMappings, RenderProtoViewMergeMapping);
|
||||||
|
var rootViewWithFragments =
|
||||||
|
renderer.createView(protoViewMergeMappings.mergedProtoViewRef, 1);
|
||||||
|
var rootView = new WorkerTestRootView(rootViewWithFragments, uiRenderViewStore);
|
||||||
|
|
||||||
|
renderer.invokeElementMethod(elRef(rootViewWithFragments.viewRef, 1),
|
||||||
|
'setAttribute', ['a', 'b']);
|
||||||
|
|
||||||
|
expect(DOM.getAttribute(DOM.childNodes(rootView.hostElement)[0], 'a'))
|
||||||
|
.toEqual('b');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class WorkerTestRootView extends TestRootView {
|
||||||
|
constructor(workerViewWithFragments: RenderViewWithFragments, uiRenderViewStore) {
|
||||||
|
super(new RenderViewWithFragments(
|
||||||
|
uiRenderViewStore.retreive(
|
||||||
|
(<WorkerRenderViewRef>workerViewWithFragments.viewRef).refNumber),
|
||||||
|
ListWrapper.map(workerViewWithFragments.fragmentRefs,
|
||||||
|
(val) => { return uiRenderViewStore.retreive(val.refNumber); })));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSerializer(protoViewRefStore: RenderProtoViewRefStore,
|
||||||
|
renderViewStore: RenderViewWithFragmentsStore): Serializer {
|
||||||
|
var injector = createTestInjector([
|
||||||
|
bind(RenderProtoViewRefStore)
|
||||||
|
.toValue(protoViewRefStore),
|
||||||
|
bind(RenderViewWithFragmentsStore).toValue(renderViewStore)
|
||||||
|
]);
|
||||||
|
return injector.get(Serializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockMessageBusSource implements MessageBusSource {
|
||||||
|
private _listenerStore: Map<int, SourceListener> = new Map<int, SourceListener>();
|
||||||
|
private _numListeners: number = 0;
|
||||||
|
|
||||||
|
addListener(fn: SourceListener): int {
|
||||||
|
this._listenerStore.set(++this._numListeners, fn);
|
||||||
|
return this._numListeners;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeListener(index: int): void { MapWrapper.delete(this._listenerStore, index); }
|
||||||
|
|
||||||
|
receive(message: Object): void {
|
||||||
|
MapWrapper.forEach(this._listenerStore, (fn: SourceListener, key: int) => { fn(message); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockMessageBusSink implements MessageBusSink {
|
||||||
|
private _sendTo: MockMessageBusSource;
|
||||||
|
|
||||||
|
send(message: Object): void { this._sendTo.receive({'data': message}); }
|
||||||
|
|
||||||
|
attachToSource(source: MockMessageBusSource) { this._sendTo = source; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockMessageBus implements MessageBus {
|
||||||
|
constructor(public sink: MockMessageBusSink, public source: MockMessageBusSource) {}
|
||||||
|
attachToBus(bus: MockMessageBus) { this.sink.attachToSource(bus.source); }
|
||||||
|
}
|
|
@ -41,7 +41,7 @@ export function main() {
|
||||||
new ViewLoader(null, null, null));
|
new ViewLoader(null, null, null));
|
||||||
var compiler = new Compiler(reader, cache, viewResolver, new ComponentUrlMapper(), urlResolver,
|
var compiler = new Compiler(reader, cache, viewResolver, new ComponentUrlMapper(), urlResolver,
|
||||||
renderCompiler, new ProtoViewFactory(new DynamicChangeDetection()),
|
renderCompiler, new ProtoViewFactory(new DynamicChangeDetection()),
|
||||||
new FakeAppRootUrl());
|
new AppRootUrl(""));
|
||||||
|
|
||||||
function measureWrapper(func, desc) {
|
function measureWrapper(func, desc) {
|
||||||
return function() {
|
return function() {
|
||||||
|
@ -161,7 +161,3 @@ class BenchmarkComponentNoBindings {
|
||||||
})
|
})
|
||||||
class BenchmarkComponentWithBindings {
|
class BenchmarkComponentWithBindings {
|
||||||
}
|
}
|
||||||
|
|
||||||
class FakeAppRootUrl extends AppRootUrl {
|
|
||||||
get value() { return ''; }
|
|
||||||
}
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -10,16 +10,16 @@ import "dart:isolate";
|
||||||
main(List<String> args, SendPort replyTo) {
|
main(List<String> args, SendPort replyTo) {
|
||||||
ReceivePort rPort = new ReceivePort();
|
ReceivePort rPort = new ReceivePort();
|
||||||
WorkerMessageBus bus = new WorkerMessageBus.fromPorts(replyTo, rPort);
|
WorkerMessageBus bus = new WorkerMessageBus.fromPorts(replyTo, rPort);
|
||||||
bus.source.listen((message) {
|
bus.source.addListener((message) {
|
||||||
if (identical(message['data']['type'], "echo")) {
|
if (identical(message['data']['type'], "echo")) {
|
||||||
bus.sink
|
bus.sink
|
||||||
.send({"type": "echo_response", "value": message['data']['value']});
|
.send({"type": "echo_response", "value": message['data']['value']});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
MessageBroker broker = new MessageBroker(bus);
|
MessageBroker broker = new MessageBroker(bus, null);
|
||||||
var args = new UiArguments("test", "tester");
|
var args = new UiArguments("test", "tester");
|
||||||
broker.runOnUiThread(args).then((data) {
|
broker.runOnUiThread(args, String).then((data) {
|
||||||
bus.sink.send({"type": "result", "value": data.value});
|
bus.sink.send({"type": "result", "value": data});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,17 +4,18 @@ import {
|
||||||
WorkerMessageBusSink
|
WorkerMessageBusSink
|
||||||
} from "angular2/src/web-workers/worker/application";
|
} from "angular2/src/web-workers/worker/application";
|
||||||
import {MessageBroker, UiArguments} from "angular2/src/web-workers/worker/broker";
|
import {MessageBroker, UiArguments} from "angular2/src/web-workers/worker/broker";
|
||||||
|
import {Serializer} from "angular2/src/web-workers/shared/serializer";
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
var bus = new WorkerMessageBus(new WorkerMessageBusSink(), new WorkerMessageBusSource());
|
var bus = new WorkerMessageBus(new WorkerMessageBusSink(), new WorkerMessageBusSource());
|
||||||
bus.source.listen((message) => {
|
bus.source.addListener((message) => {
|
||||||
if (message.data.type === "echo") {
|
if (message.data.type === "echo") {
|
||||||
bus.sink.send({type: "echo_response", 'value': message.data.value});
|
bus.sink.send({type: "echo_response", 'value': message.data.value});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var broker = new MessageBroker(bus);
|
var broker = new MessageBroker(bus, new Serializer(null));
|
||||||
var args = new UiArguments("test", "tester");
|
var args = new UiArguments("test", "tester");
|
||||||
broker.runOnUiThread(args)
|
broker.runOnUiThread(args, String)
|
||||||
.then((data) => { bus.sink.send({type: "result", value: data.value}); });
|
.then((data: string) => { bus.sink.send({type: "result", value: data}); });
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ main() {
|
||||||
var val = (querySelector("#echo_input") as InputElement).value;
|
var val = (querySelector("#echo_input") as InputElement).value;
|
||||||
bus.sink.send({'type': 'echo', 'value': val});
|
bus.sink.send({'type': 'echo', 'value': val});
|
||||||
});
|
});
|
||||||
bus.source.listen((message) {
|
bus.source.addListener((message) {
|
||||||
var data = message['data'];
|
var data = message['data'];
|
||||||
if (identical(data['type'], "echo_response")) {
|
if (identical(data['type'], "echo_response")) {
|
||||||
querySelector("#echo_result")
|
querySelector("#echo_result")
|
||||||
|
|
|
@ -13,7 +13,8 @@ document.getElementById("send_echo")
|
||||||
var val = (<HTMLInputElement>document.getElementById("echo_input")).value;
|
var val = (<HTMLInputElement>document.getElementById("echo_input")).value;
|
||||||
bus.sink.send({type: "echo", value: val});
|
bus.sink.send({type: "echo", value: val});
|
||||||
});
|
});
|
||||||
bus.source.listen((message) => {
|
|
||||||
|
bus.source.addListener((message) => {
|
||||||
if (message.data.type === "echo_response") {
|
if (message.data.type === "echo_response") {
|
||||||
document.getElementById("echo_result").innerHTML =
|
document.getElementById("echo_result").innerHTML =
|
||||||
`<span class='response'>${message.data.value}</span>`;
|
`<span class='response'>${message.data.value}</span>`;
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
library examples.src.web_workers.index;
|
||||||
|
|
||||||
|
import "index_common.dart" show HelloCmp;
|
||||||
|
import "dart:isolate";
|
||||||
|
import "package:angular2/src/web-workers/worker/application.dart" show bootstrapWebworker;
|
||||||
|
import "package:angular2/src/reflection/reflection_capabilities.dart";
|
||||||
|
import "package:angular2/src/reflection/reflection.dart";
|
||||||
|
|
||||||
|
main(List<String> args, SendPort replyTo){
|
||||||
|
reflector.reflectionCapabilities = new ReflectionCapabilities();
|
||||||
|
bootstrapWebworker(replyTo, HelloCmp);
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
import {HelloCmp} from "./index_common";
|
||||||
|
import {bootstrapWebworker} from "angular2/src/web-workers/worker/application";
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
bootstrapWebworker(HelloCmp);
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
library angular2.examples.web_workers;
|
||||||
|
|
||||||
|
import "package:angular2/src/web-workers/ui/application.dart" show bootstrap;
|
||||||
|
import "package:angular2/src/reflection/reflection_capabilities.dart";
|
||||||
|
import "package:angular2/src/reflection/reflection.dart";
|
||||||
|
|
||||||
|
main(){
|
||||||
|
reflector.reflectionCapabilities = new ReflectionCapabilities();
|
||||||
|
bootstrap("background_index.dart");
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<title>Hello Angular 2.0</title>
|
||||||
|
<body>
|
||||||
|
<hello-app>
|
||||||
|
Loading...
|
||||||
|
</hello-app>
|
||||||
|
|
||||||
|
$SCRIPTS$
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,2 @@
|
||||||
|
import {bootstrap} from "angular2/src/web-workers/ui/application";
|
||||||
|
bootstrap("loader.js");
|
|
@ -0,0 +1,53 @@
|
||||||
|
import {ElementRef, Component, Directive, View, Injectable, Renderer} from 'angular2/angular2';
|
||||||
|
|
||||||
|
// A service available to the Injector, used by the HelloCmp component.
|
||||||
|
@Injectable()
|
||||||
|
class GreetingService {
|
||||||
|
greeting: string = 'hello';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Directives are light-weight. They don't allow new
|
||||||
|
// expression contexts (use @Component for those needs).
|
||||||
|
@Directive({selector: '[red]'})
|
||||||
|
class RedDec {
|
||||||
|
// ElementRef is always injectable and it wraps the element on which the
|
||||||
|
// directive was found by the compiler.
|
||||||
|
constructor(el: ElementRef, renderer: Renderer) { renderer.setElementStyle(el, 'color', 'red'); }
|
||||||
|
// constructor(renderer: Renderer) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Angular 2.0 supports 2 basic types of directives:
|
||||||
|
// - Component - the basic building blocks of Angular 2.0 apps. Backed by
|
||||||
|
// ShadowDom.(http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom/)
|
||||||
|
// - Directive - add behavior to existing elements.
|
||||||
|
|
||||||
|
// @Component is AtScript syntax to annotate the HelloCmp class as an Angular
|
||||||
|
// 2.0 component.
|
||||||
|
@Component({
|
||||||
|
// The Selector prop tells Angular on which elements to instantiate this
|
||||||
|
// class. The syntax supported is a basic subset of CSS selectors, for example
|
||||||
|
// 'element', '[attr]', [attr=foo]', etc.
|
||||||
|
selector: 'hello-app',
|
||||||
|
// These are services that would be created if a class in the component's
|
||||||
|
// template tries to inject them.
|
||||||
|
viewInjector: [GreetingService]
|
||||||
|
})
|
||||||
|
// The template for the component.
|
||||||
|
@View({
|
||||||
|
// Expressions in the template (like {{greeting}}) are evaluated in the
|
||||||
|
// context of the HelloCmp class below.
|
||||||
|
template: `<div class="greeting">{{greeting}} <span red>world</span>!</div>
|
||||||
|
<button class="changeButton">change greeting</button>`,
|
||||||
|
// All directives used in the template need to be specified. This allows for
|
||||||
|
// modularity (RedDec can only be used in this template)
|
||||||
|
// and better tooling (the template can be invalidated if the attribute is
|
||||||
|
// misspelled).
|
||||||
|
directives: [RedDec]
|
||||||
|
})
|
||||||
|
export class HelloCmp {
|
||||||
|
greeting: string;
|
||||||
|
|
||||||
|
constructor(service: GreetingService) { this.greeting = service.greeting; }
|
||||||
|
|
||||||
|
changeGreeting(): void { this.greeting = 'howdy'; }
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
$SCRIPTS$
|
||||||
|
|
||||||
|
// TODO (jteplitz) Monkey patch this from within angular (#3207)
|
||||||
|
window = {
|
||||||
|
setTimeout: setTimeout,
|
||||||
|
Map: Map,
|
||||||
|
Set: Set,
|
||||||
|
Array: Array,
|
||||||
|
Reflect: Reflect,
|
||||||
|
RegExp: RegExp,
|
||||||
|
Promise: Promise,
|
||||||
|
Date: Date
|
||||||
|
};
|
||||||
|
assert = function() {}
|
||||||
|
|
||||||
|
|
||||||
|
System.import("examples/src/web_workers/background_index")
|
||||||
|
.then(
|
||||||
|
function(m) {
|
||||||
|
console.log("running main");
|
||||||
|
try {
|
||||||
|
m.main();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function(error) { console.error("error loading background", error); });
|
|
@ -1,7 +1,3 @@
|
||||||
importScripts("traceur-runtime.js",
|
importScripts("/examples/src/assets/zone-microtask-web-workers.js", "long-stack-trace-zone.js",
|
||||||
"es6-module-loader-sans-promises.src.js",
|
"traceur-runtime.js", "es6-module-loader-sans-promises.src.js", "system.src.js",
|
||||||
"system.src.js",
|
"extension-register.js", "extension-cjs.js", "Reflect.js", "runtime_paths.js");
|
||||||
"extension-register.js",
|
|
||||||
"extension-cjs.js",
|
|
||||||
"Reflect.js",
|
|
||||||
"runtime_paths.js");
|
|
||||||
|
|
|
@ -61,7 +61,8 @@ const kServedPaths = [
|
||||||
'examples/src/material/progress-linear',
|
'examples/src/material/progress-linear',
|
||||||
'examples/src/material/radio',
|
'examples/src/material/radio',
|
||||||
'examples/src/material/switcher',
|
'examples/src/material/switcher',
|
||||||
'examples/src/message_broker'
|
'examples/src/message_broker',
|
||||||
|
'examples/src/web_workers'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue