9
modules/angular2/src/web_workers/shared/api.ts
Normal file
9
modules/angular2/src/web_workers/shared/api.ts
Normal file
@ -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_WEB_WORKER = CONST_EXPR(new OpaqueToken('WebWorker.onWebWorker'));
|
||||
|
||||
export class WebWorkerElementRef implements RenderElementRef {
|
||||
constructor(public renderView: RenderViewRef, public renderBoundElementIndex: number) {}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
library angular2.src.web_workers.shared.isolate_message_bus;
|
||||
|
||||
import 'dart:isolate';
|
||||
import 'dart:async';
|
||||
import 'dart:core';
|
||||
import 'package:angular2/src/web_workers/shared/message_bus.dart'
|
||||
show MessageBus, MessageBusSink, MessageBusSource;
|
||||
import 'package:angular2/src/facade/async.dart';
|
||||
|
||||
class IsolateMessageBus implements MessageBus {
|
||||
final IsolateMessageBusSink sink;
|
||||
final IsolateMessageBusSource source;
|
||||
|
||||
IsolateMessageBus(IsolateMessageBusSink sink, IsolateMessageBusSource source)
|
||||
: sink = sink,
|
||||
source = source;
|
||||
|
||||
EventEmitter from(String channel) {
|
||||
return source.from(channel);
|
||||
}
|
||||
|
||||
EventEmitter to(String channel) {
|
||||
return sink.to(channel);
|
||||
}
|
||||
}
|
||||
|
||||
class IsolateMessageBusSink implements MessageBusSink {
|
||||
final SendPort _port;
|
||||
final Map<String, EventEmitter> _channels = new Map<String, EventEmitter>();
|
||||
|
||||
IsolateMessageBusSink(SendPort port) : _port = port;
|
||||
|
||||
EventEmitter to(String channel) {
|
||||
if (_channels.containsKey(channel)) {
|
||||
return _channels[channel];
|
||||
} else {
|
||||
var emitter = new EventEmitter();
|
||||
emitter.listen((message) {
|
||||
_port.send({'channel': channel, 'message': message});
|
||||
});
|
||||
_channels[channel] = emitter;
|
||||
return emitter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class IsolateMessageBusSource extends MessageBusSource {
|
||||
final Stream rawDataStream;
|
||||
final Map<String, EventEmitter> _channels = new Map<String, EventEmitter>();
|
||||
|
||||
IsolateMessageBusSource(ReceivePort port)
|
||||
: rawDataStream = port.asBroadcastStream() {
|
||||
rawDataStream.listen((message) {
|
||||
if (message is SendPort){
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.containsKey("channel")) {
|
||||
var channel = message['channel'];
|
||||
if (_channels.containsKey(channel)) {
|
||||
_channels[channel].add(message['message']);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
EventEmitter from(String channel) {
|
||||
if (_channels.containsKey(channel)) {
|
||||
return _channels[channel];
|
||||
} else {
|
||||
var emitter = new EventEmitter();
|
||||
_channels[channel] = emitter;
|
||||
return emitter;
|
||||
}
|
||||
}
|
||||
}
|
43
modules/angular2/src/web_workers/shared/message_bus.ts
Normal file
43
modules/angular2/src/web_workers/shared/message_bus.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import {EventEmitter} from 'angular2/src/facade/async';
|
||||
import {BaseException} from 'angular2/src/facade/lang';
|
||||
|
||||
function _abstract() {
|
||||
throw new BaseException("This method is abstract");
|
||||
}
|
||||
|
||||
/**
|
||||
* Message Bus is a low level API used to communicate between the UI and the background.
|
||||
* Communication is based on a channel abstraction. Messages published in a
|
||||
* given channel to one MessageBusSink are received on the same channel
|
||||
* by the corresponding MessageBusSource.
|
||||
*/
|
||||
export /* abstract (with TS 1.6) */ class MessageBus implements MessageBusSource, MessageBusSink {
|
||||
/**
|
||||
* Returns an {@link EventEmitter} that emits every time a messsage
|
||||
* is received on the given channel.
|
||||
*/
|
||||
from(channel: string): EventEmitter { throw _abstract(); }
|
||||
|
||||
|
||||
/**
|
||||
* Returns an {@link EventEmitter} for the given channel
|
||||
* To publish methods to that channel just call next (or add in dart) on the returned emitter
|
||||
*/
|
||||
to(channel: string): EventEmitter { throw _abstract(); }
|
||||
}
|
||||
|
||||
export interface MessageBusSource {
|
||||
/**
|
||||
* Returns an {@link EventEmitter} that emits every time a messsage
|
||||
* is received on the given channel.
|
||||
*/
|
||||
from(channel: string): EventEmitter;
|
||||
}
|
||||
|
||||
export interface MessageBusSink {
|
||||
/**
|
||||
* Returns an {@link EventEmitter} for the given channel
|
||||
* To publish methods to that channel just call next (or add in dart) on the returned emitter
|
||||
*/
|
||||
to(channel: string): EventEmitter;
|
||||
}
|
9
modules/angular2/src/web_workers/shared/messaging_api.ts
Normal file
9
modules/angular2/src/web_workers/shared/messaging_api.ts
Normal file
@ -0,0 +1,9 @@
|
||||
/**
|
||||
* All channels used by angular's WebWorker components are listed here.
|
||||
* You should not use these channels in your application code.
|
||||
*/
|
||||
export const SETUP_CHANNEL = "ng-WebWorkerSetup";
|
||||
export const RENDER_COMPILER_CHANNEL = "ng-RenderCompiler";
|
||||
export const RENDERER_CHANNEL = "ng-Renderer";
|
||||
export const XHR_CHANNEL = "ng-XHR";
|
||||
export const EVENT_CHANNEL = "ng-events";
|
@ -0,0 +1,3 @@
|
||||
// PostMessageBus can't be implemented in dart since dart doesn't use postMessage
|
||||
// This file is only here to prevent ts2dart from trying to transpile the PostMessageBus
|
||||
library angular2.src.web_workers.shared.post_message_bus;
|
75
modules/angular2/src/web_workers/shared/post_message_bus.ts
Normal file
75
modules/angular2/src/web_workers/shared/post_message_bus.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import {
|
||||
MessageBus,
|
||||
MessageBusSource,
|
||||
MessageBusSink
|
||||
} from "angular2/src/web_workers/shared/message_bus";
|
||||
import {EventEmitter} from 'angular2/src/facade/async';
|
||||
import {StringMap, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {Injectable} from "angular2/di";
|
||||
|
||||
/**
|
||||
* A TypeScript implementation of {@link MessageBus} for communicating via JavaScript's
|
||||
* postMessage API.
|
||||
*/
|
||||
@Injectable()
|
||||
export class PostMessageBus implements MessageBus {
|
||||
constructor(private _sink: PostMessageBusSink, private _source: PostMessageBusSource) {}
|
||||
|
||||
from(channel: string): EventEmitter { return this._source.from(channel); }
|
||||
|
||||
to(channel: string): EventEmitter { return this._sink.to(channel); }
|
||||
}
|
||||
|
||||
export class PostMessageBusSink implements MessageBusSink {
|
||||
private _channels: StringMap<string, EventEmitter> = StringMapWrapper.create();
|
||||
|
||||
constructor(private _postMessageTarget: PostMessageTarget) {}
|
||||
|
||||
public to(channel: string): EventEmitter {
|
||||
if (StringMapWrapper.contains(this._channels, channel)) {
|
||||
return this._channels[channel];
|
||||
} else {
|
||||
var emitter = new EventEmitter();
|
||||
emitter.observer({
|
||||
next: (message: Object) => {
|
||||
this._postMessageTarget.postMessage({channel: channel, message: message});
|
||||
}
|
||||
});
|
||||
return emitter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class PostMessageBusSource implements MessageBusSource {
|
||||
private _channels: StringMap<string, EventEmitter> = StringMapWrapper.create();
|
||||
|
||||
constructor(eventTarget?: EventTarget) {
|
||||
if (eventTarget) {
|
||||
eventTarget.addEventListener("message", (ev: MessageEvent) => this._handleMessage(ev));
|
||||
} else {
|
||||
// if no eventTarget is given we assume we're in a WebWorker and listen on the global scope
|
||||
addEventListener("message", (ev: MessageEvent) => this._handleMessage(ev));
|
||||
}
|
||||
}
|
||||
|
||||
private _handleMessage(ev: MessageEvent) {
|
||||
var data = ev.data;
|
||||
var channel = data.channel;
|
||||
if (StringMapWrapper.contains(this._channels, channel)) {
|
||||
this._channels[channel].next(data.message);
|
||||
}
|
||||
}
|
||||
|
||||
public from(channel: string): EventEmitter {
|
||||
if (StringMapWrapper.contains(this._channels, channel)) {
|
||||
return this._channels[channel];
|
||||
} else {
|
||||
var emitter = new EventEmitter();
|
||||
this._channels[channel] = emitter;
|
||||
return emitter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(jteplitz602) Replace this with the definition in lib.webworker.d.ts(#3492)
|
||||
export interface PostMessageTarget { postMessage: (message: any, transfer?:[ArrayBuffer]) => void; }
|
@ -0,0 +1,56 @@
|
||||
import {Injectable, Inject} from "angular2/di";
|
||||
import {RenderProtoViewRef} from "angular2/src/render/api";
|
||||
import {ON_WEB_WORKER} 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_WEB_WORKER) 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,136 @@
|
||||
import {Injectable, Inject} from "angular2/di";
|
||||
import {RenderViewRef, RenderFragmentRef, RenderViewWithFragments} from "angular2/src/render/api";
|
||||
import {ON_WEB_WORKER} 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_WEB_WORKER) onWebWorker) {
|
||||
this._onWebWorker = onWebWorker;
|
||||
this._lookupByIndex = new Map<number, RenderViewRef | RenderFragmentRef>();
|
||||
this._lookupByView = new Map<RenderViewRef | RenderFragmentRef, number>();
|
||||
}
|
||||
|
||||
allocate(fragmentCount: number): RenderViewWithFragments {
|
||||
var initialIndex = this._nextIndex;
|
||||
|
||||
var viewRef = new WebWorkerRenderViewRef(this._nextIndex++);
|
||||
var fragmentRefs = ListWrapper.createGrowableSize(fragmentCount);
|
||||
|
||||
for (var i = 0; i < fragmentCount; i++) {
|
||||
fragmentRefs[i] = new WebWorkerRenderFragmentRef(this._nextIndex++);
|
||||
}
|
||||
var renderViewWithFragments = new RenderViewWithFragments(viewRef, fragmentRefs);
|
||||
this.store(renderViewWithFragments, initialIndex);
|
||||
return renderViewWithFragments;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return this.retreive(ref);
|
||||
}
|
||||
|
||||
deserializeRenderFragmentRef(ref: number): RenderFragmentRef {
|
||||
if (ref == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.retreive(ref);
|
||||
}
|
||||
|
||||
private _serializeRenderFragmentOrViewRef(ref: RenderViewRef | RenderFragmentRef): number {
|
||||
if (ref == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this._onWebWorker) {
|
||||
return (<WebWorkerRenderFragmentRef | WebWorkerRenderViewRef>ref).serialize();
|
||||
} else {
|
||||
return this._lookupByView.get(ref);
|
||||
}
|
||||
}
|
||||
|
||||
serializeViewWithFragments(view: RenderViewWithFragments): StringMap<string, any> {
|
||||
if (view == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this._onWebWorker) {
|
||||
return {
|
||||
'viewRef': (<WebWorkerRenderViewRef>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 = this.deserializeRenderViewRef(obj['viewRef']);
|
||||
var fragments =
|
||||
ListWrapper.map(obj['fragmentRefs'], (val) => this.deserializeRenderFragmentRef(val));
|
||||
|
||||
return new RenderViewWithFragments(viewRef, fragments);
|
||||
}
|
||||
}
|
||||
|
||||
export class WebWorkerRenderViewRef extends RenderViewRef {
|
||||
constructor(public refNumber: number) { super(); }
|
||||
serialize(): number { return this.refNumber; }
|
||||
|
||||
static deserialize(ref: number): WebWorkerRenderViewRef {
|
||||
return new WebWorkerRenderViewRef(ref);
|
||||
}
|
||||
}
|
||||
|
||||
export class WebWorkerRenderFragmentRef extends RenderFragmentRef {
|
||||
constructor(public refNumber: number) { super(); }
|
||||
|
||||
serialize(): number { return this.refNumber; }
|
||||
|
||||
static deserialize(ref: number): WebWorkerRenderFragmentRef {
|
||||
return new WebWorkerRenderFragmentRef(ref);
|
||||
}
|
||||
}
|
406
modules/angular2/src/web_workers/shared/serializer.ts
Normal file
406
modules/angular2/src/web_workers/shared/serializer.ts
Normal file
@ -0,0 +1,406 @@
|
||||
import {
|
||||
Type,
|
||||
isArray,
|
||||
isPresent,
|
||||
serializeEnum,
|
||||
deserializeEnum,
|
||||
BaseException
|
||||
} from "angular2/src/facade/lang";
|
||||
import {
|
||||
List,
|
||||
ListWrapper,
|
||||
Map,
|
||||
StringMap,
|
||||
StringMapWrapper,
|
||||
MapWrapper
|
||||
} from "angular2/src/facade/collection";
|
||||
import {
|
||||
ProtoViewDto,
|
||||
RenderDirectiveMetadata,
|
||||
RenderElementBinder,
|
||||
DirectiveBinder,
|
||||
ElementPropertyBinding,
|
||||
EventBinding,
|
||||
ViewDefinition,
|
||||
RenderProtoViewRef,
|
||||
RenderProtoViewMergeMapping,
|
||||
RenderViewRef,
|
||||
RenderFragmentRef,
|
||||
RenderElementRef,
|
||||
ViewType,
|
||||
ViewEncapsulation,
|
||||
PropertyBindingType
|
||||
} from "angular2/src/render/api";
|
||||
import {WebWorkerElementRef} from 'angular2/src/web_workers/shared/api';
|
||||
import {AST, ASTWithSource} from 'angular2/src/change_detection/change_detection';
|
||||
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 {
|
||||
private _enumRegistry: Map<any, Map<number, any>>;
|
||||
constructor(private _parser: Parser, private _protoViewStore: RenderProtoViewRefStore,
|
||||
private _renderViewStore: RenderViewWithFragmentsStore) {
|
||||
this._enumRegistry = new Map<any, Map<number, any>>();
|
||||
|
||||
var viewTypeMap = new Map<number, any>();
|
||||
viewTypeMap[0] = ViewType.HOST;
|
||||
viewTypeMap[1] = ViewType.COMPONENT;
|
||||
viewTypeMap[2] = ViewType.EMBEDDED;
|
||||
this._enumRegistry.set(ViewType, viewTypeMap);
|
||||
|
||||
var viewEncapsulationMap = new Map<number, any>();
|
||||
viewEncapsulationMap[0] = ViewEncapsulation.EMULATED;
|
||||
viewEncapsulationMap[1] = ViewEncapsulation.NATIVE;
|
||||
viewEncapsulationMap[2] = ViewEncapsulation.NONE;
|
||||
this._enumRegistry.set(ViewEncapsulation, viewEncapsulationMap);
|
||||
|
||||
var propertyBindingTypeMap = new Map<number, any>();
|
||||
propertyBindingTypeMap[0] = PropertyBindingType.PROPERTY;
|
||||
propertyBindingTypeMap[1] = PropertyBindingType.ATTRIBUTE;
|
||||
propertyBindingTypeMap[2] = PropertyBindingType.CLASS;
|
||||
propertyBindingTypeMap[3] = PropertyBindingType.STYLE;
|
||||
this._enumRegistry.set(PropertyBindingType, propertyBindingTypeMap);
|
||||
}
|
||||
|
||||
serialize(obj: any, type: Type): Object {
|
||||
if (!isPresent(obj)) {
|
||||
return null;
|
||||
}
|
||||
if (isArray(obj)) {
|
||||
var serializedObj = [];
|
||||
ListWrapper.forEach(obj, (val) => { serializedObj.push(this.serialize(val, type)); });
|
||||
return serializedObj;
|
||||
}
|
||||
if (type == String) {
|
||||
return obj;
|
||||
}
|
||||
if (type == ViewDefinition) {
|
||||
return this._serializeViewDefinition(obj);
|
||||
} else if (type == DirectiveBinder) {
|
||||
return this._serializeDirectiveBinder(obj);
|
||||
} else if (type == ProtoViewDto) {
|
||||
return this._serializeProtoViewDto(obj);
|
||||
} else if (type == RenderElementBinder) {
|
||||
return this._serializeElementBinder(obj);
|
||||
} else if (type == RenderDirectiveMetadata) {
|
||||
return this._serializeDirectiveMetadata(obj);
|
||||
} else if (type == ASTWithSource) {
|
||||
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 == WebWorkerElementRef) {
|
||||
return this._serializeWorkerElementRef(obj);
|
||||
} else if (type == ElementPropertyBinding) {
|
||||
return this._serializeElementPropertyBinding(obj);
|
||||
} else if (type == EventBinding) {
|
||||
return this._serializeEventBinding(obj);
|
||||
} else {
|
||||
throw new BaseException("No serializer for " + type.toString());
|
||||
}
|
||||
}
|
||||
|
||||
deserialize(map: any, type: Type, data?: any): any {
|
||||
if (!isPresent(map)) {
|
||||
return null;
|
||||
}
|
||||
if (isArray(map)) {
|
||||
var obj: List<any> = new List<any>();
|
||||
ListWrapper.forEach(map, (val) => { obj.push(this.deserialize(val, type, data)); });
|
||||
return obj;
|
||||
}
|
||||
if (type == String) {
|
||||
return map;
|
||||
}
|
||||
|
||||
if (type == ViewDefinition) {
|
||||
return this._deserializeViewDefinition(map);
|
||||
} else if (type == DirectiveBinder) {
|
||||
return this._deserializeDirectiveBinder(map);
|
||||
} else if (type == ProtoViewDto) {
|
||||
return this._deserializeProtoViewDto(map);
|
||||
} else if (type == RenderDirectiveMetadata) {
|
||||
return this._deserializeDirectiveMetadata(map);
|
||||
} else if (type == RenderElementBinder) {
|
||||
return this._deserializeElementBinder(map);
|
||||
} else if (type == ASTWithSource) {
|
||||
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 == WebWorkerElementRef) {
|
||||
return this._deserializeWorkerElementRef(map);
|
||||
} else if (type == EventBinding) {
|
||||
return this._deserializeEventBinding(map);
|
||||
} else if (type == ElementPropertyBinding) {
|
||||
return this._deserializeElementPropertyBinding(map);
|
||||
} else {
|
||||
throw new BaseException("No deserializer for " + type.toString());
|
||||
}
|
||||
}
|
||||
|
||||
mapToObject(map: Map<string, any>, type?: Type): Object {
|
||||
var object = {};
|
||||
var serialize = isPresent(type);
|
||||
|
||||
MapWrapper.forEach(map, (value, key) => {
|
||||
if (serialize) {
|
||||
object[key] = this.serialize(value, type);
|
||||
} else {
|
||||
object[key] = value;
|
||||
}
|
||||
});
|
||||
return object;
|
||||
}
|
||||
|
||||
/*
|
||||
* Transforms a Javascript object (StringMap) into a Map<string, V>
|
||||
* If the values need to be deserialized pass in their type
|
||||
* and they will be deserialized before being placed in the map
|
||||
*/
|
||||
objectToMap(obj: StringMap<string, any>, type?: Type, data?: any): Map<string, any> {
|
||||
if (isPresent(type)) {
|
||||
var map: Map<string, any> = new Map();
|
||||
StringMapWrapper.forEach(obj,
|
||||
(val, key) => { map.set(key, this.deserialize(val, type, data)); });
|
||||
return map;
|
||||
} else {
|
||||
return MapWrapper.createFromStringMap(obj);
|
||||
}
|
||||
}
|
||||
|
||||
allocateRenderViews(fragmentCount: number) { this._renderViewStore.allocate(fragmentCount); }
|
||||
|
||||
private _serializeElementPropertyBinding(binding:
|
||||
ElementPropertyBinding): StringMap<string, any> {
|
||||
return {
|
||||
'type': serializeEnum(binding.type),
|
||||
'astWithSource': this.serialize(binding.astWithSource, ASTWithSource),
|
||||
'property': binding.property,
|
||||
'unit': binding.unit
|
||||
};
|
||||
}
|
||||
|
||||
private _deserializeElementPropertyBinding(map: StringMap<string, any>): ElementPropertyBinding {
|
||||
var type = deserializeEnum(map['type'], this._enumRegistry.get(PropertyBindingType));
|
||||
var ast = this.deserialize(map['astWithSource'], ASTWithSource, "binding");
|
||||
return new ElementPropertyBinding(type, ast, map['property'], map['unit']);
|
||||
}
|
||||
|
||||
private _serializeEventBinding(binding: EventBinding): StringMap<string, any> {
|
||||
return {'fullName': binding.fullName, 'source': this.serialize(binding.source, ASTWithSource)};
|
||||
}
|
||||
|
||||
private _deserializeEventBinding(map: StringMap<string, any>): EventBinding {
|
||||
return new EventBinding(map['fullName'],
|
||||
this.deserialize(map['source'], ASTWithSource, "action"));
|
||||
}
|
||||
|
||||
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 WebWorkerElementRef(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};
|
||||
}
|
||||
|
||||
private _deserializeASTWithSource(obj: StringMap<string, any>, data: string): AST {
|
||||
// TODO: make ASTs serializable
|
||||
var ast: AST;
|
||||
switch (data) {
|
||||
case "action":
|
||||
ast = this._parser.parseAction(obj['input'], obj['location']);
|
||||
break;
|
||||
case "binding":
|
||||
ast = this._parser.parseBinding(obj['input'], obj['location']);
|
||||
break;
|
||||
case "interpolation":
|
||||
ast = this._parser.parseInterpolation(obj['input'], obj['location']);
|
||||
break;
|
||||
default:
|
||||
throw "No AST deserializer for " + data;
|
||||
}
|
||||
return ast;
|
||||
}
|
||||
|
||||
private _serializeViewDefinition(view: ViewDefinition): Object {
|
||||
return {
|
||||
'componentId': view.componentId,
|
||||
'templateAbsUrl': view.templateAbsUrl,
|
||||
'template': view.template,
|
||||
'directives': this.serialize(view.directives, RenderDirectiveMetadata),
|
||||
'styleAbsUrls': view.styleAbsUrls,
|
||||
'styles': view.styles,
|
||||
'encapsulation': serializeEnum(view.encapsulation)
|
||||
};
|
||||
}
|
||||
|
||||
private _deserializeViewDefinition(obj: StringMap<string, any>): ViewDefinition {
|
||||
return new ViewDefinition({
|
||||
componentId: obj['componentId'],
|
||||
templateAbsUrl: obj['templateAbsUrl'], template: obj['template'],
|
||||
directives: this.deserialize(obj['directives'], RenderDirectiveMetadata),
|
||||
styleAbsUrls: obj['styleAbsUrls'],
|
||||
styles: obj['styles'],
|
||||
encapsulation:
|
||||
deserializeEnum(obj['encapsulation'], this._enumRegistry.get(ViewEncapsulation))
|
||||
});
|
||||
}
|
||||
|
||||
private _serializeDirectiveBinder(binder: DirectiveBinder): Object {
|
||||
return {
|
||||
'directiveIndex': binder.directiveIndex,
|
||||
'propertyBindings': this.mapToObject(binder.propertyBindings, ASTWithSource),
|
||||
'eventBindings': this.serialize(binder.eventBindings, EventBinding),
|
||||
'hostPropertyBindings': this.serialize(binder.hostPropertyBindings, ElementPropertyBinding)
|
||||
};
|
||||
}
|
||||
|
||||
private _deserializeDirectiveBinder(obj: StringMap<string, any>): DirectiveBinder {
|
||||
return new DirectiveBinder({
|
||||
directiveIndex: obj['directiveIndex'],
|
||||
propertyBindings: this.objectToMap(obj['propertyBindings'], ASTWithSource, "binding"),
|
||||
eventBindings: this.deserialize(obj['eventBindings'], EventBinding),
|
||||
hostPropertyBindings: this.deserialize(obj['hostPropertyBindings'], ElementPropertyBinding)
|
||||
});
|
||||
}
|
||||
|
||||
private _serializeElementBinder(binder: RenderElementBinder): Object {
|
||||
return {
|
||||
'index': binder.index,
|
||||
'parentIndex': binder.parentIndex,
|
||||
'distanceToParent': binder.distanceToParent,
|
||||
'directives': this.serialize(binder.directives, DirectiveBinder),
|
||||
'nestedProtoView': this.serialize(binder.nestedProtoView, ProtoViewDto),
|
||||
'propertyBindings': this.serialize(binder.propertyBindings, ElementPropertyBinding),
|
||||
'variableBindings': this.mapToObject(binder.variableBindings),
|
||||
'eventBindings': this.serialize(binder.eventBindings, EventBinding),
|
||||
'readAttributes': this.mapToObject(binder.readAttributes)
|
||||
};
|
||||
}
|
||||
|
||||
private _deserializeElementBinder(obj: StringMap<string, any>): RenderElementBinder {
|
||||
return new RenderElementBinder({
|
||||
index: obj['index'],
|
||||
parentIndex: obj['parentIndex'],
|
||||
distanceToParent: obj['distanceToParent'],
|
||||
directives: this.deserialize(obj['directives'], DirectiveBinder),
|
||||
nestedProtoView: this.deserialize(obj['nestedProtoView'], ProtoViewDto),
|
||||
propertyBindings: this.deserialize(obj['propertyBindings'], ElementPropertyBinding),
|
||||
variableBindings: this.objectToMap(obj['variableBindings']),
|
||||
eventBindings: this.deserialize(obj['eventBindings'], EventBinding),
|
||||
readAttributes: this.objectToMap(obj['readAttributes'])
|
||||
});
|
||||
}
|
||||
|
||||
private _serializeProtoViewDto(view: ProtoViewDto): Object {
|
||||
return {
|
||||
'render': this._protoViewStore.serialize(view.render),
|
||||
'elementBinders': this.serialize(view.elementBinders, RenderElementBinder),
|
||||
'variableBindings': this.mapToObject(view.variableBindings),
|
||||
'type': serializeEnum(view.type),
|
||||
'textBindings': this.serialize(view.textBindings, ASTWithSource),
|
||||
'transitiveNgContentCount': view.transitiveNgContentCount
|
||||
};
|
||||
}
|
||||
|
||||
private _deserializeProtoViewDto(obj: StringMap<string, any>): ProtoViewDto {
|
||||
return new ProtoViewDto({
|
||||
render: this._protoViewStore.deserialize(obj["render"]),
|
||||
elementBinders: this.deserialize(obj['elementBinders'], RenderElementBinder),
|
||||
variableBindings: this.objectToMap(obj['variableBindings']),
|
||||
textBindings: this.deserialize(obj['textBindings'], ASTWithSource, "interpolation"),
|
||||
type: deserializeEnum(obj['type'], this._enumRegistry.get(ViewType)),
|
||||
transitiveNgContentCount: obj['transitiveNgContentCount']
|
||||
});
|
||||
}
|
||||
|
||||
private _serializeDirectiveMetadata(meta: RenderDirectiveMetadata): Object {
|
||||
var obj = {
|
||||
'id': meta.id,
|
||||
'selector': meta.selector,
|
||||
'compileChildren': meta.compileChildren,
|
||||
'events': meta.events,
|
||||
'properties': meta.properties,
|
||||
'readAttributes': meta.readAttributes,
|
||||
'type': meta.type,
|
||||
'callOnDestroy': meta.callOnDestroy,
|
||||
'callOnChange': meta.callOnChange,
|
||||
'callOnCheck': meta.callOnCheck,
|
||||
'callOnInit': meta.callOnInit,
|
||||
'callOnAllChangesDone': meta.callOnAllChangesDone,
|
||||
'changeDetection': meta.changeDetection,
|
||||
'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;
|
||||
}
|
||||
private _deserializeDirectiveMetadata(obj: StringMap<string, any>): RenderDirectiveMetadata {
|
||||
return new RenderDirectiveMetadata({
|
||||
id: obj['id'],
|
||||
selector: obj['selector'],
|
||||
compileChildren: obj['compileChildren'],
|
||||
hostProperties: this.objectToMap(obj['hostProperties']),
|
||||
hostListeners: this.objectToMap(obj['hostListeners']),
|
||||
hostActions: this.objectToMap(obj['hostActions']),
|
||||
hostAttributes: this.objectToMap(obj['hostAttributes']),
|
||||
properties: obj['properties'],
|
||||
readAttributes: obj['readAttributes'],
|
||||
type: obj['type'],
|
||||
exportAs: obj['exportAs'],
|
||||
callOnDestroy: obj['callOnDestroy'],
|
||||
callOnChange: obj['callOnChange'],
|
||||
callOnCheck: obj['callOnCheck'],
|
||||
callOnInit: obj['callOnInit'],
|
||||
callOnAllChangesDone: obj['callOnAllChangesDone'],
|
||||
changeDetection: obj['changeDetection'],
|
||||
events: obj['events']
|
||||
});
|
||||
}
|
||||
}
|
45
modules/angular2/src/web_workers/ui/application.dart
Normal file
45
modules/angular2/src/web_workers/ui/application.dart
Normal file
@ -0,0 +1,45 @@
|
||||
library angular2.src.web_workers.ui;
|
||||
|
||||
import 'dart:isolate';
|
||||
import 'dart:async';
|
||||
import 'dart:core';
|
||||
import 'package:angular2/src/web_workers/shared/message_bus.dart'
|
||||
show MessageBus;
|
||||
import 'package:angular2/src/web_workers/ui/impl.dart' show bootstrapUICommon;
|
||||
import 'package:angular2/src/web_workers/shared/isolate_message_bus.dart';
|
||||
|
||||
/**
|
||||
* Bootstrapping a WebWorker
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
Future<MessageBus> bootstrap(String uri) {
|
||||
return spawnWebWorker(Uri.parse(uri)).then((bus) {
|
||||
bootstrapUICommon(bus);
|
||||
return bus;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* To be called from the main thread to spawn and communicate with the worker thread
|
||||
*/
|
||||
Future<MessageBus> spawnWebWorker(Uri uri) {
|
||||
var receivePort = new ReceivePort();
|
||||
var isolateEndSendPort = receivePort.sendPort;
|
||||
return Isolate.spawnUri(uri, const [], isolateEndSendPort).then((_) {
|
||||
var source = new UIMessageBusSource(receivePort);
|
||||
return source.sink.then((sendPort) {
|
||||
var sink = new IsolateMessageBusSink(sendPort);
|
||||
return new IsolateMessageBus(sink, source);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class UIMessageBusSource extends IsolateMessageBusSource {
|
||||
UIMessageBusSource(ReceivePort port) : super(port);
|
||||
|
||||
Future<SendPort> get sink => rawDataStream.firstWhere((message) {
|
||||
return message is SendPort;
|
||||
});
|
||||
}
|
29
modules/angular2/src/web_workers/ui/application.ts
Normal file
29
modules/angular2/src/web_workers/ui/application.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import {
|
||||
PostMessageBus,
|
||||
PostMessageBusSink,
|
||||
PostMessageBusSource
|
||||
} from 'angular2/src/web_workers/shared/post_message_bus';
|
||||
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
|
||||
import {BaseException} from "angular2/src/facade/lang";
|
||||
import {bootstrapUICommon} from "angular2/src/web_workers/ui/impl";
|
||||
|
||||
/**
|
||||
* Bootstrapping a WebWorker
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
export function bootstrap(uri: string): MessageBus {
|
||||
var messageBus = spawnWebWorker(uri);
|
||||
bootstrapUICommon(messageBus);
|
||||
return messageBus;
|
||||
}
|
||||
|
||||
export function spawnWebWorker(uri: string): MessageBus {
|
||||
var webWorker: Worker = new Worker(uri);
|
||||
var sink = new PostMessageBusSink(webWorker);
|
||||
var source = new PostMessageBusSource(webWorker);
|
||||
return new PostMessageBus(sink, source);
|
||||
}
|
148
modules/angular2/src/web_workers/ui/di_bindings.ts
Normal file
148
modules/angular2/src/web_workers/ui/di_bindings.ts
Normal file
@ -0,0 +1,148 @@
|
||||
// TODO (jteplitz602): This whole file is nearly identical to core/application.ts.
|
||||
// There should be a way to refactor application so that this file is unnecessary. See #3277
|
||||
import {Injector, bind, Binding} from "angular2/di";
|
||||
import {Reflector, reflector} from 'angular2/src/reflection/reflection';
|
||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {
|
||||
Parser,
|
||||
Lexer,
|
||||
ChangeDetection,
|
||||
DynamicChangeDetection,
|
||||
JitChangeDetection,
|
||||
PreGeneratedChangeDetection
|
||||
} from 'angular2/src/change_detection/change_detection';
|
||||
import {DEFAULT_PIPES} from 'angular2/pipes';
|
||||
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 {AppRootUrl} from 'angular2/src/services/app_root_url';
|
||||
import {
|
||||
DomRenderer,
|
||||
DOCUMENT,
|
||||
DefaultDomCompiler,
|
||||
APP_ID_RANDOM_BINDING,
|
||||
MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE,
|
||||
TemplateCloner
|
||||
} from 'angular2/src/render/render';
|
||||
import {ElementSchemaRegistry} from 'angular2/src/render/dom/schema/element_schema_registry';
|
||||
import {DomElementSchemaRegistry} from 'angular2/src/render/dom/schema/dom_element_schema_registry';
|
||||
import {
|
||||
SharedStylesHost,
|
||||
DomSharedStylesHost
|
||||
} from 'angular2/src/render/dom/view/shared_styles_host';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||
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_WEB_WORKER} 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';
|
||||
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
|
||||
import {MessageBasedRenderCompiler} from 'angular2/src/web_workers/ui/render_compiler';
|
||||
import {MessageBasedRenderer} from 'angular2/src/web_workers/ui/renderer';
|
||||
import {MessageBasedXHRImpl} from 'angular2/src/web_workers/ui/xhr_impl';
|
||||
import {WebWorkerSetup} from 'angular2/src/web_workers/ui/setup';
|
||||
|
||||
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<any> {
|
||||
var bestChangeDetection = new DynamicChangeDetection();
|
||||
if (PreGeneratedChangeDetection.isSupported()) {
|
||||
bestChangeDetection = new PreGeneratedChangeDetection();
|
||||
} else if (JitChangeDetection.isSupported()) {
|
||||
bestChangeDetection = new JitChangeDetection();
|
||||
}
|
||||
|
||||
return [
|
||||
bind(DOCUMENT)
|
||||
.toValue(DOM.defaultDoc()),
|
||||
bind(EventManager)
|
||||
.toFactory(
|
||||
(ngZone) => {
|
||||
var plugins =
|
||||
[new HammerGesturesPlugin(), new KeyEventsPlugin(), new DomEventsPlugin()];
|
||||
return new EventManager(plugins, ngZone);
|
||||
},
|
||||
[NgZone]),
|
||||
DomRenderer,
|
||||
bind(Renderer).toAlias(DomRenderer),
|
||||
APP_ID_RANDOM_BINDING,
|
||||
TemplateCloner,
|
||||
bind(MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE).toValue(20),
|
||||
DefaultDomCompiler,
|
||||
bind(RenderCompiler).toAlias(DefaultDomCompiler),
|
||||
DomSharedStylesHost,
|
||||
bind(SharedStylesHost).toAlias(DomSharedStylesHost),
|
||||
Serializer,
|
||||
bind(ON_WEB_WORKER).toValue(false),
|
||||
bind(ElementSchemaRegistry).toValue(new DomElementSchemaRegistry()),
|
||||
RenderViewWithFragmentsStore,
|
||||
RenderProtoViewRefStore,
|
||||
ProtoViewFactory,
|
||||
AppViewPool,
|
||||
bind(APP_VIEW_POOL_CAPACITY).toValue(10000),
|
||||
AppViewManager,
|
||||
AppViewManagerUtils,
|
||||
AppViewListener,
|
||||
Compiler,
|
||||
CompilerCache,
|
||||
ViewResolver,
|
||||
DEFAULT_PIPES,
|
||||
bind(ChangeDetection).toValue(bestChangeDetection),
|
||||
ViewLoader,
|
||||
DirectiveResolver,
|
||||
Parser,
|
||||
Lexer,
|
||||
bind(ExceptionHandler).toFactory(() => new ExceptionHandler(DOM), []),
|
||||
bind(XHR).toValue(new XHRImpl()),
|
||||
ComponentUrlMapper,
|
||||
UrlResolver,
|
||||
StyleUrlResolver,
|
||||
StyleInliner,
|
||||
DynamicComponentLoader,
|
||||
Testability,
|
||||
AnchorBasedAppRootUrl,
|
||||
bind(AppRootUrl).toAlias(AnchorBasedAppRootUrl),
|
||||
WebWorkerMain,
|
||||
WebWorkerSetup,
|
||||
MessageBasedRenderCompiler,
|
||||
MessageBasedXHRImpl,
|
||||
MessageBasedRenderer
|
||||
];
|
||||
}
|
||||
|
||||
export function createInjector(zone: NgZone, bus: MessageBus): Injector {
|
||||
BrowserDomAdapter.makeCurrent();
|
||||
_rootBindings.push(bind(NgZone).toValue(zone));
|
||||
_rootBindings.push(bind(MessageBus).toValue(bus));
|
||||
var injector: Injector = Injector.resolveAndCreate(_rootBindings);
|
||||
return injector.resolveAndCreateChild(_injectorBindings());
|
||||
}
|
110
modules/angular2/src/web_workers/ui/event_dispatcher.ts
Normal file
110
modules/angular2/src/web_workers/ui/event_dispatcher.ts
Normal file
@ -0,0 +1,110 @@
|
||||
import {
|
||||
RenderViewRef,
|
||||
RenderEventDispatcher,
|
||||
} from 'angular2/src/render/api';
|
||||
import {Serializer} from 'angular2/src/web_workers/shared/serializer';
|
||||
import {
|
||||
serializeMouseEvent,
|
||||
serializeKeyboardEvent,
|
||||
serializeGenericEvent,
|
||||
serializeEventWithTarget
|
||||
} from 'angular2/src/web_workers/ui/event_serializer';
|
||||
import {BaseException} from "angular2/src/facade/lang";
|
||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
export class EventDispatcher implements RenderEventDispatcher {
|
||||
constructor(private _viewRef: RenderViewRef, private _sink: EventEmitter,
|
||||
private _serializer: Serializer) {}
|
||||
|
||||
dispatchRenderEvent(elementIndex: number, eventName: string, locals: Map<string, any>) {
|
||||
var e = locals.get('$event');
|
||||
var serializedEvent;
|
||||
// TODO (jteplitz602): support custom events #3350
|
||||
switch (e.type) {
|
||||
case "click":
|
||||
case "mouseup":
|
||||
case "mousedown":
|
||||
case "dblclick":
|
||||
case "contextmenu":
|
||||
case "mouseenter":
|
||||
case "mouseleave":
|
||||
case "mousemove":
|
||||
case "mouseout":
|
||||
case "mouseover":
|
||||
case "show":
|
||||
serializedEvent = serializeMouseEvent(e);
|
||||
break;
|
||||
case "keydown":
|
||||
case "keypress":
|
||||
case "keyup":
|
||||
serializedEvent = serializeKeyboardEvent(e);
|
||||
break;
|
||||
case "input":
|
||||
case "change":
|
||||
case "blur":
|
||||
serializedEvent = serializeEventWithTarget(e);
|
||||
break;
|
||||
case "abort":
|
||||
case "afterprint":
|
||||
case "beforeprint":
|
||||
case "cached":
|
||||
case "canplay":
|
||||
case "canplaythrough":
|
||||
case "chargingchange":
|
||||
case "chargingtimechange":
|
||||
case "close":
|
||||
case "dischargingtimechange":
|
||||
case "DOMContentLoaded":
|
||||
case "downloading":
|
||||
case "durationchange":
|
||||
case "emptied":
|
||||
case "ended":
|
||||
case "error":
|
||||
case "fullscreenchange":
|
||||
case "fullscreenerror":
|
||||
case "invalid":
|
||||
case "languagechange":
|
||||
case "levelfchange":
|
||||
case "loadeddata":
|
||||
case "loadedmetadata":
|
||||
case "obsolete":
|
||||
case "offline":
|
||||
case "online":
|
||||
case "open":
|
||||
case "orientatoinchange":
|
||||
case "pause":
|
||||
case "pointerlockchange":
|
||||
case "pointerlockerror":
|
||||
case "play":
|
||||
case "playing":
|
||||
case "ratechange":
|
||||
case "readystatechange":
|
||||
case "reset":
|
||||
case "seeked":
|
||||
case "seeking":
|
||||
case "stalled":
|
||||
case "submit":
|
||||
case "success":
|
||||
case "suspend":
|
||||
case "timeupdate":
|
||||
case "updateready":
|
||||
case "visibilitychange":
|
||||
case "volumechange":
|
||||
case "waiting":
|
||||
serializedEvent = serializeGenericEvent(e);
|
||||
break;
|
||||
default:
|
||||
throw new BaseException(eventName + " not supported on WebWorkers");
|
||||
}
|
||||
var serializedLocals = StringMapWrapper.create();
|
||||
StringMapWrapper.set(serializedLocals, '$event', serializedEvent);
|
||||
|
||||
ObservableWrapper.callNext(this._sink, {
|
||||
"viewRef": this._serializer.serialize(this._viewRef, RenderViewRef),
|
||||
"elementIndex": elementIndex,
|
||||
"eventName": eventName,
|
||||
"locals": serializedLocals
|
||||
});
|
||||
}
|
||||
}
|
128
modules/angular2/src/web_workers/ui/event_serializer.dart
Normal file
128
modules/angular2/src/web_workers/ui/event_serializer.dart
Normal file
@ -0,0 +1,128 @@
|
||||
library angular2.src.web_workers.event_serializer;
|
||||
|
||||
import 'package:angular2/src/facade/collection.dart';
|
||||
// TODO(jteplitz602): Remove Mirrors from serialization #3348
|
||||
@MirrorsUsed(
|
||||
symbols: "altKey, bubbles, button, cancelable, client, ctrlKey, " +
|
||||
"defaultPrevented, detail, eventPhase, layer, metaKey, offset, page, region, screen, " +
|
||||
"shiftKey, timeStamp, type, magnitude, x, y, charCode, keyCode, keyLocation, location, repeat")
|
||||
import 'dart:mirrors';
|
||||
import 'dart:core';
|
||||
import 'dart:html';
|
||||
|
||||
// These Maps can't be const due to a dartj2 bug (see http://github.com/dart-lang/sdk/issues/21825)
|
||||
// Once that bug is fixed these should be const
|
||||
final Map MOUSE_EVENT_PROPERTIES = {
|
||||
#altKey: bool,
|
||||
#bubbles: bool,
|
||||
#button: int,
|
||||
#cancelable: bool,
|
||||
#client: Point,
|
||||
#ctrlKey: bool,
|
||||
#defaultPrevented: bool,
|
||||
#detail: int,
|
||||
#eventPhase: int,
|
||||
#layer: Point,
|
||||
#metaKey: bool,
|
||||
#offset: Point,
|
||||
#page: Point,
|
||||
#region: String,
|
||||
#screen: Point,
|
||||
#shiftKey: bool,
|
||||
#timeStamp: int,
|
||||
#type: String
|
||||
};
|
||||
|
||||
final Map KEYBOARD_EVENT_PROPERTIES = {
|
||||
#altKey: bool,
|
||||
#bubbles: bool,
|
||||
#cancelable: bool,
|
||||
#charCode: int,
|
||||
#ctrlKey: bool,
|
||||
#defaultPrevented: bool,
|
||||
#detail: int,
|
||||
#eventPhase: int,
|
||||
#keyCode: int,
|
||||
#keyLocation: int,
|
||||
#layer: Point,
|
||||
#location: int,
|
||||
#repeat: bool,
|
||||
#shiftKey: bool,
|
||||
#timeStamp: int,
|
||||
#type: String
|
||||
};
|
||||
|
||||
final Map EVENT_PROPERTIES = {
|
||||
#bubbles: bool,
|
||||
#cancelable: bool,
|
||||
#defaultPrevented: bool,
|
||||
#eventPhase: int,
|
||||
#timeStamp: int,
|
||||
#type: String
|
||||
};
|
||||
|
||||
// List of all elements with HTML value attribute.
|
||||
// Taken from: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes
|
||||
final Set<String> NODES_WITH_VALUE = new Set<String>.from([
|
||||
"input",
|
||||
"select",
|
||||
"option",
|
||||
"button",
|
||||
"li",
|
||||
"meter",
|
||||
"progress",
|
||||
"param"
|
||||
]);
|
||||
|
||||
Map<String, dynamic> serializeGenericEvent(dynamic e) {
|
||||
return serializeEvent(e, EVENT_PROPERTIES);
|
||||
}
|
||||
|
||||
// TODO(jteplitz602): Allow users to specify the properties they need rather than always
|
||||
// adding value #3374
|
||||
Map<String, dynamic> serializeEventWithTarget(dynamic e) {
|
||||
var serializedEvent = serializeEvent(e, EVENT_PROPERTIES);
|
||||
return addTarget(e, serializedEvent);
|
||||
}
|
||||
|
||||
Map<String, dynamic> serializeMouseEvent(dynamic e) {
|
||||
return serializeEvent(e, MOUSE_EVENT_PROPERTIES);
|
||||
}
|
||||
|
||||
Map<String, dynamic> serializeKeyboardEvent(dynamic e) {
|
||||
var serializedEvent = serializeEvent(e, KEYBOARD_EVENT_PROPERTIES);
|
||||
return addTarget(e, serializedEvent);
|
||||
}
|
||||
|
||||
// TODO(jteplitz602): #3374. See above.
|
||||
Map<String, dynamic> addTarget(
|
||||
dynamic e, Map<String, dynamic> serializedEvent) {
|
||||
if (NODES_WITH_VALUE.contains(e.target.tagName.toLowerCase())) {
|
||||
serializedEvent['target'] = {'value': e.target.value};
|
||||
if (e.target is InputElement) {
|
||||
serializedEvent['target']['files'] = e.target.files;
|
||||
}
|
||||
}
|
||||
return serializedEvent;
|
||||
}
|
||||
|
||||
Map<String, dynamic> serializeEvent(dynamic e, Map<Symbol, Type> PROPERTIES) {
|
||||
var serialized = StringMapWrapper.create();
|
||||
var mirror = reflect(e);
|
||||
PROPERTIES.forEach((property, type) {
|
||||
var value = mirror.getField(property).reflectee;
|
||||
var propertyName = MirrorSystem.getName(property);
|
||||
if (type == int || type == bool || type == String) {
|
||||
serialized[propertyName] = value;
|
||||
} else if (type == Point) {
|
||||
var point = reflect(value);
|
||||
serialized[propertyName] = {
|
||||
'magnitude': point.getField(#magnitude).reflectee,
|
||||
'x': point.getField(#x).reflectee,
|
||||
'y': point.getField(#y).reflectee
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
return serialized;
|
||||
}
|
79
modules/angular2/src/web_workers/ui/event_serializer.ts
Normal file
79
modules/angular2/src/web_workers/ui/event_serializer.ts
Normal file
@ -0,0 +1,79 @@
|
||||
import {StringMap, Set} from 'angular2/src/facade/collection';
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
|
||||
const MOUSE_EVENT_PROPERTIES = [
|
||||
"altKey",
|
||||
"button",
|
||||
"clientX",
|
||||
"clientY",
|
||||
"metaKey",
|
||||
"movementX",
|
||||
"movementY",
|
||||
"offsetX",
|
||||
"offsetY",
|
||||
"region",
|
||||
"screenX",
|
||||
"screenY",
|
||||
"shiftKey"
|
||||
];
|
||||
|
||||
const KEYBOARD_EVENT_PROPERTIES = [
|
||||
'altkey',
|
||||
'charCode',
|
||||
'code',
|
||||
'ctrlKey',
|
||||
'isComposing',
|
||||
'key',
|
||||
'keyCode',
|
||||
'location',
|
||||
'metaKey',
|
||||
'repeat',
|
||||
'shiftKey',
|
||||
'which'
|
||||
];
|
||||
|
||||
const EVENT_PROPERTIES = ['type', 'bubbles', 'cancelable'];
|
||||
|
||||
const NODES_WITH_VALUE =
|
||||
new Set(["input", "select", "option", "button", "li", "meter", "progress", "param"]);
|
||||
|
||||
export function serializeGenericEvent(e: Event): StringMap<string, any> {
|
||||
return serializeEvent(e, EVENT_PROPERTIES);
|
||||
}
|
||||
|
||||
// TODO(jteplitz602): Allow users to specify the properties they need rather than always
|
||||
// adding value and files #3374
|
||||
export function serializeEventWithTarget(e: Event): StringMap<string, any> {
|
||||
var serializedEvent = serializeEvent(e, EVENT_PROPERTIES);
|
||||
return addTarget(e, serializedEvent);
|
||||
}
|
||||
|
||||
export function serializeMouseEvent(e: MouseEvent): StringMap<string, any> {
|
||||
return serializeEvent(e, MOUSE_EVENT_PROPERTIES);
|
||||
}
|
||||
|
||||
export function serializeKeyboardEvent(e: KeyboardEvent): StringMap<string, any> {
|
||||
var serializedEvent = serializeEvent(e, KEYBOARD_EVENT_PROPERTIES);
|
||||
return addTarget(e, serializedEvent);
|
||||
}
|
||||
|
||||
// TODO(jteplitz602): #3374. See above.
|
||||
function addTarget(e: Event, serializedEvent: StringMap<string, any>): StringMap<string, any> {
|
||||
if (NODES_WITH_VALUE.has((<HTMLElement>e.target).tagName.toLowerCase())) {
|
||||
var target = <HTMLInputElement>e.target;
|
||||
serializedEvent['target'] = {'value': target.value};
|
||||
if (isPresent(target.files)) {
|
||||
serializedEvent['target']['files'] = target.files;
|
||||
}
|
||||
}
|
||||
return serializedEvent;
|
||||
}
|
||||
|
||||
function serializeEvent(e: any, properties: List<string>): StringMap<string, any> {
|
||||
var serialized = {};
|
||||
for (var i = 0; i < properties.length; i++) {
|
||||
var prop = properties[i];
|
||||
serialized[prop] = e[prop];
|
||||
}
|
||||
return serialized;
|
||||
}
|
53
modules/angular2/src/web_workers/ui/impl.ts
Normal file
53
modules/angular2/src/web_workers/ui/impl.ts
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
import {createInjector} from "./di_bindings";
|
||||
import {MessageBus, MessageBusSink} from "angular2/src/web_workers/shared/message_bus";
|
||||
import {createNgZone} from 'angular2/src/core/application_common';
|
||||
import {Injectable} from 'angular2/di';
|
||||
import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
|
||||
import {wtfInit} from 'angular2/src/profile/wtf_init';
|
||||
import {WebWorkerSetup} from 'angular2/src/web_workers/ui/setup';
|
||||
import {MessageBasedRenderCompiler} from 'angular2/src/web_workers/ui/render_compiler';
|
||||
import {MessageBasedRenderer} from 'angular2/src/web_workers/ui/renderer';
|
||||
import {MessageBasedXHRImpl} from 'angular2/src/web_workers/ui/xhr_impl';
|
||||
|
||||
/**
|
||||
* 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();
|
||||
wtfInit();
|
||||
zone.run(() => {
|
||||
var injector = createInjector(zone, bus);
|
||||
// necessary to kick off all the message based components
|
||||
injector.get(WebWorkerMain);
|
||||
});
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class WebWorkerMain {
|
||||
constructor(public renderCompiler: MessageBasedRenderCompiler,
|
||||
public renderer: MessageBasedRenderer, public xhr: MessageBasedXHRImpl,
|
||||
public setup: WebWorkerSetup) {}
|
||||
}
|
||||
|
||||
export 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'];
|
||||
}
|
||||
}
|
63
modules/angular2/src/web_workers/ui/render_compiler.ts
Normal file
63
modules/angular2/src/web_workers/ui/render_compiler.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import {Injectable} from 'angular2/di';
|
||||
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
|
||||
import {Serializer} from 'angular2/src/web_workers/shared/serializer';
|
||||
import {
|
||||
RenderDirectiveMetadata,
|
||||
ProtoViewDto,
|
||||
ViewDefinition,
|
||||
RenderProtoViewRef,
|
||||
RenderProtoViewMergeMapping,
|
||||
RenderCompiler
|
||||
} from 'angular2/src/render/api';
|
||||
import {EventEmitter, ObservableWrapper, PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {RENDER_COMPILER_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
|
||||
import {ReceivedMessage} from 'angular2/src/web_workers/ui/impl';
|
||||
import {BaseException, Type} from 'angular2/src/facade/lang';
|
||||
|
||||
// TODO(jteplitz602): Create parent UIComponent class #3703
|
||||
@Injectable()
|
||||
export class MessageBasedRenderCompiler {
|
||||
private _sink: EventEmitter;
|
||||
private _source: EventEmitter;
|
||||
|
||||
constructor(bus: MessageBus, private _serializer: Serializer,
|
||||
private _renderCompiler: RenderCompiler) {
|
||||
this._sink = bus.to(RENDER_COMPILER_CHANNEL);
|
||||
this._source = bus.from(RENDER_COMPILER_CHANNEL);
|
||||
ObservableWrapper.subscribe(this._source,
|
||||
(message: StringMap<string, any>) => this._handleMessage(message));
|
||||
}
|
||||
|
||||
private _handleMessage(map: StringMap<string, any>): void {
|
||||
var message = new ReceivedMessage(map);
|
||||
var args = message.args;
|
||||
var promise: Promise<any>;
|
||||
switch (message.method) {
|
||||
case "compileHost":
|
||||
var directiveMetadata = this._serializer.deserialize(args[0], RenderDirectiveMetadata);
|
||||
promise = this._renderCompiler.compileHost(directiveMetadata);
|
||||
this._wrapWebWorkerPromise(message.id, promise, ProtoViewDto);
|
||||
break;
|
||||
case "compile":
|
||||
var view = this._serializer.deserialize(args[0], ViewDefinition);
|
||||
promise = this._renderCompiler.compile(view);
|
||||
this._wrapWebWorkerPromise(message.id, promise, ProtoViewDto);
|
||||
break;
|
||||
case "mergeProtoViewsRecursively":
|
||||
var views = this._serializer.deserialize(args[0], RenderProtoViewRef);
|
||||
promise = this._renderCompiler.mergeProtoViewsRecursively(views);
|
||||
this._wrapWebWorkerPromise(message.id, promise, RenderProtoViewMergeMapping);
|
||||
break;
|
||||
default:
|
||||
throw new BaseException("not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
private _wrapWebWorkerPromise(id: string, promise: Promise<any>, type: Type): void {
|
||||
PromiseWrapper.then(promise, (result: any) => {
|
||||
ObservableWrapper.callNext(
|
||||
this._sink,
|
||||
{'type': 'result', 'value': this._serializer.serialize(result, type), 'id': id});
|
||||
});
|
||||
}
|
||||
}
|
127
modules/angular2/src/web_workers/ui/renderer.ts
Normal file
127
modules/angular2/src/web_workers/ui/renderer.ts
Normal file
@ -0,0 +1,127 @@
|
||||
import {Injectable} from 'angular2/di';
|
||||
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
|
||||
import {Serializer} from 'angular2/src/web_workers/shared/serializer';
|
||||
import {
|
||||
RenderViewRef,
|
||||
RenderFragmentRef,
|
||||
RenderProtoViewRef,
|
||||
Renderer
|
||||
} from 'angular2/src/render/api';
|
||||
import {WebWorkerElementRef} from 'angular2/src/web_workers/shared/api';
|
||||
import {EventEmitter, ObservableWrapper, PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {EVENT_CHANNEL, RENDERER_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
|
||||
import {ReceivedMessage} from 'angular2/src/web_workers/ui/impl';
|
||||
import {BaseException, Type} from 'angular2/src/facade/lang';
|
||||
import {EventDispatcher} from 'angular2/src/web_workers/ui/event_dispatcher';
|
||||
import {
|
||||
RenderViewWithFragmentsStore
|
||||
} from 'angular2/src/web_workers/shared/render_view_with_fragments_store';
|
||||
|
||||
@Injectable()
|
||||
export class MessageBasedRenderer {
|
||||
constructor(private _bus: MessageBus, private _serializer: Serializer,
|
||||
private _renderViewWithFragmentsStore: RenderViewWithFragmentsStore,
|
||||
private _renderer: Renderer) {
|
||||
var source = _bus.from(RENDERER_CHANNEL);
|
||||
ObservableWrapper.subscribe(source,
|
||||
(message: StringMap<string, any>) => this._handleMessage(message));
|
||||
}
|
||||
|
||||
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 _handleMessage(map: StringMap<string, any>): void {
|
||||
var data = new ReceivedMessage(map);
|
||||
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], WebWorkerElementRef);
|
||||
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], WebWorkerElementRef);
|
||||
var propName = args[1];
|
||||
var propValue = args[2];
|
||||
this._renderer.setElementProperty(elementRef, propName, propValue);
|
||||
break;
|
||||
case "setElementAttribute":
|
||||
var elementRef = this._serializer.deserialize(args[0], WebWorkerElementRef);
|
||||
var attributeName = args[1];
|
||||
var attributeValue = args[2];
|
||||
this._renderer.setElementAttribute(elementRef, attributeName, attributeValue);
|
||||
break;
|
||||
case "setElementClass":
|
||||
var elementRef = this._serializer.deserialize(args[0], WebWorkerElementRef);
|
||||
var className = args[1];
|
||||
var isAdd = args[2];
|
||||
this._renderer.setElementClass(elementRef, className, isAdd);
|
||||
break;
|
||||
case "setElementStyle":
|
||||
var elementRef = this._serializer.deserialize(args[0], WebWorkerElementRef);
|
||||
var styleName = args[1];
|
||||
var styleValue = args[2];
|
||||
this._renderer.setElementStyle(elementRef, styleName, styleValue);
|
||||
break;
|
||||
case "invokeElementMethod":
|
||||
var elementRef = this._serializer.deserialize(args[0], WebWorkerElementRef);
|
||||
var methodName = args[1];
|
||||
var methodArgs = args[2];
|
||||
this._renderer.invokeElementMethod(elementRef, methodName, methodArgs);
|
||||
break;
|
||||
case "setEventDispatcher":
|
||||
var viewRef = this._serializer.deserialize(args[0], RenderViewRef);
|
||||
var dispatcher =
|
||||
new EventDispatcher(viewRef, this._bus.to(EVENT_CHANNEL), this._serializer);
|
||||
this._renderer.setEventDispatcher(viewRef, dispatcher);
|
||||
break;
|
||||
default:
|
||||
throw new BaseException("Not Implemented");
|
||||
}
|
||||
}
|
||||
}
|
20
modules/angular2/src/web_workers/ui/setup.ts
Normal file
20
modules/angular2/src/web_workers/ui/setup.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import {SETUP_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
|
||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
|
||||
import {AnchorBasedAppRootUrl} from 'angular2/src/services/anchor_based_app_root_url';
|
||||
import {Injectable} from 'angular2/di';
|
||||
|
||||
@Injectable()
|
||||
export class WebWorkerSetup {
|
||||
constructor(bus: MessageBus, anchorBasedAppRootUrl: AnchorBasedAppRootUrl) {
|
||||
var rootUrl = anchorBasedAppRootUrl.value;
|
||||
var sink = bus.to(SETUP_CHANNEL);
|
||||
var source = bus.from(SETUP_CHANNEL);
|
||||
|
||||
ObservableWrapper.subscribe(source, (message: string) => {
|
||||
if (message === "ready") {
|
||||
ObservableWrapper.callNext(sink, {"rootUrl": rootUrl});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
44
modules/angular2/src/web_workers/ui/xhr_impl.ts
Normal file
44
modules/angular2/src/web_workers/ui/xhr_impl.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import {Injectable} from 'angular2/di';
|
||||
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
|
||||
import {Serializer} from 'angular2/src/web_workers/shared/serializer';
|
||||
import {EventEmitter, ObservableWrapper, PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {XHR_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
|
||||
import {ReceivedMessage} from 'angular2/src/web_workers/ui/impl';
|
||||
import {BaseException, Type} from 'angular2/src/facade/lang';
|
||||
import {XHR} from 'angular2/src/render/xhr';
|
||||
|
||||
// TODO(jteplitz602): Create parent UIComponent class #3703
|
||||
@Injectable()
|
||||
export class MessageBasedXHRImpl {
|
||||
private _sink: EventEmitter;
|
||||
private _source: EventEmitter;
|
||||
|
||||
constructor(bus: MessageBus, private _serializer: Serializer, private _xhr: XHR) {
|
||||
this._sink = bus.to(XHR_CHANNEL);
|
||||
this._source = bus.from(XHR_CHANNEL);
|
||||
ObservableWrapper.subscribe(this._source,
|
||||
(message: StringMap<string, any>) => this._handleMessage(message));
|
||||
}
|
||||
|
||||
private _handleMessage(map: StringMap<string, any>) {
|
||||
var message = new ReceivedMessage(map);
|
||||
var args = message.args;
|
||||
switch (message.method) {
|
||||
case "get":
|
||||
var url = args[0];
|
||||
var promise = this._xhr.get(url);
|
||||
this._wrapWebWorkerPromise(message.id, promise, String);
|
||||
break;
|
||||
default:
|
||||
throw new BaseException(message.method + " Not Implemented");
|
||||
}
|
||||
}
|
||||
|
||||
private _wrapWebWorkerPromise(id: string, promise: Promise<any>, type: Type): void {
|
||||
PromiseWrapper.then(promise, (result: any) => {
|
||||
ObservableWrapper.callNext(
|
||||
this._sink,
|
||||
{'type': 'result', 'value': this._serializer.serialize(result, type), 'id': id});
|
||||
});
|
||||
}
|
||||
}
|
39
modules/angular2/src/web_workers/worker/application.dart
Normal file
39
modules/angular2/src/web_workers/worker/application.dart
Normal file
@ -0,0 +1,39 @@
|
||||
library angular2.src.web_workers.worker;
|
||||
|
||||
import "package:angular2/src/web_workers/shared/isolate_message_bus.dart";
|
||||
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/core/application_ref.dart" show ApplicationRef;
|
||||
import "package:angular2/src/facade/lang.dart" show Type, BaseException;
|
||||
import "dart:isolate";
|
||||
import "dart:async";
|
||||
import 'dart:core';
|
||||
|
||||
/**
|
||||
* Bootstrapping a Webworker Application
|
||||
*
|
||||
* You instantiate the application side by calling bootstrapWebworker from your webworker index
|
||||
* script.
|
||||
* You must supply a SendPort for communicating with the UI side in order to instantiate
|
||||
* the application.
|
||||
* Other than the SendPort you can call bootstrapWebworker() exactly as you would call
|
||||
* bootstrap() in a regular Angular application
|
||||
* See the bootstrap() docs for more details.
|
||||
*/
|
||||
Future<ApplicationRef> bootstrapWebWorker(
|
||||
SendPort replyTo, Type appComponentType,
|
||||
[List<dynamic> componentInjectableBindings = null]) {
|
||||
ReceivePort rPort = new ReceivePort();
|
||||
var sink = new WebWorkerMessageBusSink(replyTo, rPort);
|
||||
var source = new IsolateMessageBusSource(rPort);
|
||||
IsolateMessageBus bus = new IsolateMessageBus(sink, source);
|
||||
return bootstrapWebWorkerCommon(
|
||||
appComponentType, bus, componentInjectableBindings);
|
||||
}
|
||||
|
||||
class WebWorkerMessageBusSink extends IsolateMessageBusSink {
|
||||
WebWorkerMessageBusSink(SendPort sPort, ReceivePort rPort) : super(sPort) {
|
||||
sPort.send(rPort.sendPort);
|
||||
}
|
||||
}
|
40
modules/angular2/src/web_workers/worker/application.ts
Normal file
40
modules/angular2/src/web_workers/worker/application.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import {
|
||||
PostMessageBus,
|
||||
PostMessageBusSink,
|
||||
PostMessageBusSource
|
||||
} from 'angular2/src/web_workers/shared/post_message_bus';
|
||||
import {Type, BaseException} from "angular2/src/facade/lang";
|
||||
import {Binding} from "angular2/di";
|
||||
import {Map} from 'angular2/src/facade/collection';
|
||||
import {Promise} from 'angular2/src/facade/async';
|
||||
import {bootstrapWebWorkerCommon} from "angular2/src/web_workers/worker/application_common";
|
||||
import {ApplicationRef} from "angular2/src/core/application_ref";
|
||||
import {Injectable} from "angular2/di";
|
||||
|
||||
// TODO(jteplitz602) remove this and compile with lib.webworker.d.ts (#3492)
|
||||
interface PostMessageInterface {
|
||||
(message: any, transferrables?:[ArrayBuffer]): void;
|
||||
}
|
||||
var _postMessage: PostMessageInterface = <any>postMessage;
|
||||
|
||||
/**
|
||||
* Bootstrapping a Webworker Application
|
||||
*
|
||||
* You instantiate the application side by calling bootstrapWebworker from your webworker index
|
||||
* script.
|
||||
* You can call bootstrapWebworker() exactly as you would call bootstrap() in a regular Angular
|
||||
* application
|
||||
* See the bootstrap() docs for more details.
|
||||
*/
|
||||
export function bootstrapWebWorker(
|
||||
appComponentType: Type, componentInjectableBindings: List<Type | Binding | List<any>> = null):
|
||||
Promise<ApplicationRef> {
|
||||
var sink = new PostMessageBusSink({
|
||||
postMessage:
|
||||
(message: any, transferrables?:[ArrayBuffer]) => { _postMessage(message, transferrables); }
|
||||
});
|
||||
var source = new PostMessageBusSource();
|
||||
var bus = new PostMessageBus(sink, source);
|
||||
|
||||
return bootstrapWebWorkerCommon(appComponentType, bus, componentInjectableBindings);
|
||||
}
|
199
modules/angular2/src/web_workers/worker/application_common.ts
Normal file
199
modules/angular2/src/web_workers/worker/application_common.ts
Normal file
@ -0,0 +1,199 @@
|
||||
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,
|
||||
PreGeneratedChangeDetection,
|
||||
IterableDiffers,
|
||||
defaultIterableDiffers,
|
||||
KeyValueDiffers,
|
||||
defaultKeyValueDiffers
|
||||
} from 'angular2/src/change_detection/change_detection';
|
||||
import {DEFAULT_PIPES} from 'angular2/pipes';
|
||||
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 {PipeResolver} from 'angular2/src/core/compiler/pipe_resolver';
|
||||
import {ViewResolver} from 'angular2/src/core/compiler/view_resolver';
|
||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {Promise, PromiseWrapper, PromiseCompleter} 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 {WebWorkerXHRImpl} from 'angular2/src/web_workers/worker/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 {WebWorkerRenderer, WebWorkerCompiler} from './renderer';
|
||||
import {Renderer, RenderCompiler} from 'angular2/src/render/api';
|
||||
import {internalView} from 'angular2/src/core/compiler/view_ref';
|
||||
|
||||
import {MessageBrokerFactory} from 'angular2/src/web_workers/worker/broker';
|
||||
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
|
||||
import {APP_COMPONENT_REF_PROMISE, APP_COMPONENT} from 'angular2/src/core/application_tokens';
|
||||
import {ApplicationRef} from 'angular2/src/core/application_ref';
|
||||
import {Serializer} from "angular2/src/web_workers/shared/serializer";
|
||||
import {ON_WEB_WORKER} 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 {ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {SETUP_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
|
||||
import {WebWorkerEventDispatcher} from 'angular2/src/web_workers/worker/event_dispatcher';
|
||||
|
||||
var _rootInjector: Injector;
|
||||
|
||||
// Contains everything that is safe to share between applications.
|
||||
var _rootBindings = [bind(Reflector).toValue(reflector)];
|
||||
|
||||
class PrintLogger {
|
||||
log = print;
|
||||
logGroup = print;
|
||||
logGroupEnd() {}
|
||||
}
|
||||
|
||||
function _injectorBindings(appComponentType, bus: MessageBus, initData: StringMap<string, any>):
|
||||
List<Type | Binding | List<any>> {
|
||||
var bestChangeDetection = new DynamicChangeDetection();
|
||||
if (PreGeneratedChangeDetection.isSupported()) {
|
||||
bestChangeDetection = new PreGeneratedChangeDetection();
|
||||
} else if (JitChangeDetection.isSupported()) {
|
||||
bestChangeDetection = new JitChangeDetection();
|
||||
}
|
||||
return [
|
||||
bind(APP_COMPONENT)
|
||||
.toValue(appComponentType),
|
||||
bind(APP_COMPONENT_REF_PROMISE)
|
||||
.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, [APP_COMPONENT_REF_PROMISE]),
|
||||
bind(LifeCycle).toFactory((exceptionHandler) => new LifeCycle(null, assertionsEnabled()),
|
||||
[ExceptionHandler]),
|
||||
Serializer,
|
||||
bind(MessageBus).toValue(bus),
|
||||
MessageBrokerFactory,
|
||||
WebWorkerRenderer,
|
||||
bind(Renderer).toAlias(WebWorkerRenderer),
|
||||
WebWorkerCompiler,
|
||||
bind(RenderCompiler).toAlias(WebWorkerCompiler),
|
||||
bind(ON_WEB_WORKER).toValue(true),
|
||||
RenderViewWithFragmentsStore,
|
||||
RenderProtoViewRefStore,
|
||||
ProtoViewFactory,
|
||||
AppViewPool,
|
||||
bind(APP_VIEW_POOL_CAPACITY).toValue(10000),
|
||||
AppViewManager,
|
||||
AppViewManagerUtils,
|
||||
AppViewListener,
|
||||
Compiler,
|
||||
CompilerCache,
|
||||
ViewResolver,
|
||||
DEFAULT_PIPES,
|
||||
bind(IterableDiffers).toValue(defaultIterableDiffers),
|
||||
bind(KeyValueDiffers).toValue(defaultKeyValueDiffers),
|
||||
bind(ChangeDetection).toValue(bestChangeDetection),
|
||||
DirectiveResolver,
|
||||
PipeResolver,
|
||||
Parser,
|
||||
Lexer,
|
||||
bind(ExceptionHandler).toFactory(() => new ExceptionHandler(new PrintLogger()), []),
|
||||
WebWorkerXHRImpl,
|
||||
bind(XHR).toAlias(WebWorkerXHRImpl),
|
||||
ComponentUrlMapper,
|
||||
UrlResolver,
|
||||
StyleUrlResolver,
|
||||
DynamicComponentLoader,
|
||||
Testability,
|
||||
bind(AppRootUrl).toValue(new AppRootUrl(initData['rootUrl'])),
|
||||
WebWorkerEventDispatcher
|
||||
];
|
||||
}
|
||||
|
||||
export function bootstrapWebWorkerCommon(
|
||||
appComponentType: Type, bus: MessageBus,
|
||||
componentInjectableBindings: List<Type | Binding | List<any>> = null): Promise<ApplicationRef> {
|
||||
var bootstrapProcess: PromiseCompleter<any> = PromiseWrapper.completer();
|
||||
|
||||
var zone = new NgZone({enableLongStackTrace: assertionsEnabled()});
|
||||
zone.run(() => {
|
||||
// TODO(rado): prepopulate template cache, so applications with only
|
||||
// index.html and main.js are possible.
|
||||
//
|
||||
|
||||
|
||||
var subscription: any;
|
||||
var emitter = bus.from(SETUP_CHANNEL);
|
||||
subscription = ObservableWrapper.subscribe(emitter, (message: StringMap<string, any>) => {
|
||||
var appInjector =
|
||||
_createAppInjector(appComponentType, componentInjectableBindings, zone, bus, message);
|
||||
var compRefToken = PromiseWrapper.wrap(() => {
|
||||
try {
|
||||
return appInjector.get(APP_COMPONENT_REF_PROMISE);
|
||||
} 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); });
|
||||
|
||||
ObservableWrapper.dispose(subscription);
|
||||
});
|
||||
|
||||
ObservableWrapper.callNext(bus.to(SETUP_CHANNEL), "ready");
|
||||
});
|
||||
|
||||
return bootstrapProcess.promise;
|
||||
}
|
||||
|
||||
function _createAppInjector(appComponentType: Type, bindings: List<Type | Binding | List<any>>,
|
||||
zone: NgZone, bus: MessageBus, 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);
|
||||
}
|
136
modules/angular2/src/web_workers/worker/broker.ts
Normal file
136
modules/angular2/src/web_workers/worker/broker.ts
Normal file
@ -0,0 +1,136 @@
|
||||
/// <reference path="../../../globals.d.ts" />
|
||||
import {MessageBus} from "angular2/src/web_workers/shared/message_bus";
|
||||
import {print, isPresent, DateWrapper, stringify} from "../../facade/lang";
|
||||
import {
|
||||
Promise,
|
||||
PromiseCompleter,
|
||||
PromiseWrapper,
|
||||
ObservableWrapper,
|
||||
EventEmitter
|
||||
} from "angular2/src/facade/async";
|
||||
import {ListWrapper, StringMapWrapper, MapWrapper} from "../../facade/collection";
|
||||
import {Serializer} from "angular2/src/web_workers/shared/serializer";
|
||||
import {Injectable} from "angular2/di";
|
||||
import {Type} from "angular2/src/facade/lang";
|
||||
|
||||
@Injectable()
|
||||
export class MessageBrokerFactory {
|
||||
constructor(private _messageBus: MessageBus, protected _serializer: Serializer) {}
|
||||
|
||||
createMessageBroker(channel: string): MessageBroker {
|
||||
return new MessageBroker(this._messageBus, this._serializer, channel);
|
||||
}
|
||||
}
|
||||
|
||||
export class MessageBroker {
|
||||
private _pending: Map<string, PromiseCompleter<any>> = new Map<string, PromiseCompleter<any>>();
|
||||
private _sink: EventEmitter;
|
||||
|
||||
constructor(messageBus: MessageBus, protected _serializer: Serializer, public channel) {
|
||||
this._sink = messageBus.to(channel);
|
||||
var source = messageBus.from(channel);
|
||||
ObservableWrapper.subscribe(source, (message) => this._handleMessage(message));
|
||||
}
|
||||
|
||||
private _generateMessageId(name: string): string {
|
||||
var time: string = stringify(DateWrapper.toMillis(DateWrapper.now()));
|
||||
var iteration: number = 0;
|
||||
var id: string = name + time + stringify(iteration);
|
||||
while (isPresent(this._pending[id])) {
|
||||
id = `${name}${time}${iteration}`;
|
||||
iteration++;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
runOnUiThread(args: UiArguments, returnType: Type): Promise<any> {
|
||||
var fnArgs = [];
|
||||
if (isPresent(args.args)) {
|
||||
ListWrapper.forEach(args.args, (argument) => {
|
||||
if (argument.type != null) {
|
||||
fnArgs.push(this._serializer.serialize(argument.value, argument.type));
|
||||
} else {
|
||||
fnArgs.push(argument.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var promise: Promise<any>;
|
||||
var id: string = null;
|
||||
if (returnType != null) {
|
||||
var completer: PromiseCompleter<any> = PromiseWrapper.completer();
|
||||
id = this._generateMessageId(args.method);
|
||||
this._pending.set(id, completer);
|
||||
PromiseWrapper.catchError(completer.promise, (err, stack?) => {
|
||||
print(err);
|
||||
completer.reject(err, stack);
|
||||
});
|
||||
|
||||
promise = PromiseWrapper.then(completer.promise, (value: any) => {
|
||||
if (this._serializer == null) {
|
||||
return value;
|
||||
} else {
|
||||
return this._serializer.deserialize(value, returnType);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
promise = null;
|
||||
}
|
||||
|
||||
// TODO(jteplitz602): Create a class for these messages so we don't keep using StringMap #3685
|
||||
var message = {'method': args.method, 'args': fnArgs};
|
||||
if (id != null) {
|
||||
message['id'] = id;
|
||||
}
|
||||
ObservableWrapper.callNext(this._sink, message);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
private _handleMessage(message: StringMap<string, any>): void {
|
||||
var data = new MessageData(message);
|
||||
// TODO(jteplitz602): replace these strings with messaging constants #3685
|
||||
if (data.type === "result" || data.type === "error") {
|
||||
var id = data.id;
|
||||
if (this._pending.has(id)) {
|
||||
if (data.type === "result") {
|
||||
this._pending.get(id).resolve(data.value);
|
||||
} else {
|
||||
this._pending.get(id).reject(data.value, null);
|
||||
}
|
||||
this._pending.delete(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MessageData {
|
||||
type: string;
|
||||
value: any;
|
||||
id: string;
|
||||
|
||||
constructor(data: StringMap<string, any>) {
|
||||
this.type = StringMapWrapper.get(data, "type");
|
||||
this.id = this._getValueIfPresent(data, "id");
|
||||
this.value = this._getValueIfPresent(data, "value");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value from the StringMap if present. Otherwise returns null
|
||||
*/
|
||||
_getValueIfPresent(data: StringMap<string, any>, key: string) {
|
||||
if (StringMapWrapper.contains(data, key)) {
|
||||
return StringMapWrapper.get(data, key);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class FnArg {
|
||||
constructor(public value, public type) {}
|
||||
}
|
||||
|
||||
export class UiArguments {
|
||||
constructor(public method: string, public args?: List<FnArg>) {}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
library angular2.src.web_workers.worker.event_deserializer;
|
||||
|
||||
class GenericEvent {
|
||||
Map<String, dynamic> properties;
|
||||
EventTarget _target = null;
|
||||
|
||||
GenericEvent(this.properties);
|
||||
|
||||
bool get bubbles => properties['bubbles'];
|
||||
bool get cancelable => properties['cancelable'];
|
||||
bool get defaultPrevented => properties['defaultPrevented'];
|
||||
int get eventPhase => properties['eventPhase'];
|
||||
int get timeStamp => properties['timeStamp'];
|
||||
String get type => properties['type'];
|
||||
bool get altKey => properties['altKey'];
|
||||
|
||||
int get charCode => properties['charCode'];
|
||||
bool get ctrlKey => properties['ctrlKey'];
|
||||
int get detail => properties['detail'];
|
||||
int get keyCode => properties['keyCode'];
|
||||
int get keyLocation => properties['keyLocation'];
|
||||
Point get layer => _getPoint('layer');
|
||||
int get location => properties['location'];
|
||||
bool get repeat => properties['repeat'];
|
||||
bool get shiftKey => properties['shiftKey'];
|
||||
|
||||
int get button => properties['button'];
|
||||
Point get client => _getPoint('client');
|
||||
bool get metaKey => properties['metaKey'];
|
||||
Point get offset => _getPoint('offset');
|
||||
Point get page => _getPoint('page');
|
||||
Point get screen => _getPoint('screen');
|
||||
|
||||
EventTarget get target {
|
||||
if (_target != null) {
|
||||
return _target;
|
||||
} else if (properties.containsKey("target")) {
|
||||
_target = new EventTarget(properties['target']);
|
||||
return _target;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
dynamic _getPoint(name) {
|
||||
Map<String, dynamic> point = properties[name];
|
||||
return new Point(point['x'], point['y'], point['magnitude']);
|
||||
}
|
||||
}
|
||||
|
||||
class EventTarget {
|
||||
dynamic value;
|
||||
|
||||
EventTarget(Map<String, dynamic> properties) {
|
||||
value = properties['value'];
|
||||
}
|
||||
}
|
||||
|
||||
class Point {
|
||||
int x;
|
||||
int y;
|
||||
double magnitude;
|
||||
|
||||
Point(this.x, this.y, this.magnitude);
|
||||
}
|
||||
|
||||
GenericEvent deserializeGenericEvent(Map<String, dynamic> serializedEvent) {
|
||||
return new GenericEvent(serializedEvent);
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
import {StringMap} from "angular2/src/facade/collection";
|
||||
|
||||
// no deserialization is necessary in TS.
|
||||
// This is only here to match dart interface
|
||||
export function deserializeGenericEvent(serializedEvent: StringMap<string, any>):
|
||||
StringMap<string, any> {
|
||||
return serializedEvent;
|
||||
}
|
49
modules/angular2/src/web_workers/worker/event_dispatcher.ts
Normal file
49
modules/angular2/src/web_workers/worker/event_dispatcher.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import {Injectable} from 'angular2/di';
|
||||
import {Map, MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {RenderViewRef, RenderEventDispatcher} from 'angular2/src/render/api';
|
||||
import {Serializer} from 'angular2/src/web_workers/shared/serializer';
|
||||
import {EVENT_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
|
||||
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
|
||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {deserializeGenericEvent} from './event_deserializer';
|
||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||
|
||||
@Injectable()
|
||||
export class WebWorkerEventDispatcher {
|
||||
private _eventDispatchRegistry: Map<RenderViewRef, RenderEventDispatcher> =
|
||||
new Map<RenderViewRef, RenderEventDispatcher>();
|
||||
|
||||
constructor(bus: MessageBus, private _serializer: Serializer, private _zone: NgZone) {
|
||||
var source = bus.from(EVENT_CHANNEL);
|
||||
ObservableWrapper.subscribe(
|
||||
source, (message) => this._dispatchEvent(new RenderEventData(message, _serializer)));
|
||||
}
|
||||
|
||||
|
||||
private _dispatchEvent(eventData: RenderEventData): void {
|
||||
var dispatcher = this._eventDispatchRegistry.get(eventData.viewRef);
|
||||
this._zone.run(() => {
|
||||
eventData.locals['$event'] = deserializeGenericEvent(eventData.locals['$event']);
|
||||
dispatcher.dispatchRenderEvent(eventData.elementIndex, eventData.eventName, eventData.locals);
|
||||
});
|
||||
}
|
||||
|
||||
registerEventDispatcher(viewRef: RenderViewRef, dispatcher: RenderEventDispatcher): void {
|
||||
this._eventDispatchRegistry.set(viewRef, dispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class RenderEventData {
|
||||
viewRef: RenderViewRef;
|
||||
elementIndex: number;
|
||||
eventName: string;
|
||||
locals: Map<string, any>;
|
||||
|
||||
constructor(message: StringMap<string, any>, serializer: Serializer) {
|
||||
this.viewRef = serializer.deserialize(message['viewRef'], RenderViewRef);
|
||||
this.elementIndex = message['elementIndex'];
|
||||
this.eventName = message['eventName'];
|
||||
this.locals = MapWrapper.createFromStringMap(message['locals']);
|
||||
}
|
||||
}
|
286
modules/angular2/src/web_workers/worker/renderer.ts
Normal file
286
modules/angular2/src/web_workers/worker/renderer.ts
Normal file
@ -0,0 +1,286 @@
|
||||
import {
|
||||
Renderer,
|
||||
RenderCompiler,
|
||||
RenderDirectiveMetadata,
|
||||
ProtoViewDto,
|
||||
ViewDefinition,
|
||||
RenderProtoViewRef,
|
||||
RenderViewRef,
|
||||
RenderElementRef,
|
||||
RenderEventDispatcher,
|
||||
RenderProtoViewMergeMapping,
|
||||
RenderViewWithFragments,
|
||||
RenderFragmentRef
|
||||
} from 'angular2/src/render/api';
|
||||
import {Promise, PromiseWrapper} from "angular2/src/facade/async";
|
||||
import {
|
||||
MessageBroker,
|
||||
MessageBrokerFactory,
|
||||
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,
|
||||
WebWorkerRenderViewRef
|
||||
} from 'angular2/src/web_workers/shared/render_view_with_fragments_store';
|
||||
import {WebWorkerElementRef} from 'angular2/src/web_workers/shared/api';
|
||||
import {
|
||||
RENDER_COMPILER_CHANNEL,
|
||||
RENDERER_CHANNEL
|
||||
} from 'angular2/src/web_workers/shared/messaging_api';
|
||||
import {WebWorkerEventDispatcher} from 'angular2/src/web_workers/worker/event_dispatcher';
|
||||
|
||||
@Injectable()
|
||||
export class WebWorkerCompiler implements RenderCompiler {
|
||||
private _messageBroker;
|
||||
constructor(messageBrokerFactory: MessageBrokerFactory) {
|
||||
this._messageBroker = messageBrokerFactory.createMessageBroker(RENDER_COMPILER_CHANNEL);
|
||||
}
|
||||
/**
|
||||
* Creats a ProtoViewDto that contains a single nested component with the given componentId.
|
||||
*/
|
||||
compileHost(directiveMetadata: RenderDirectiveMetadata): Promise<ProtoViewDto> {
|
||||
var fnArgs: List<FnArg> = [new FnArg(directiveMetadata, RenderDirectiveMetadata)];
|
||||
var args: UiArguments = new UiArguments("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("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("mergeProtoViewsRecursively", fnArgs);
|
||||
return this._messageBroker.runOnUiThread(args, RenderProtoViewMergeMapping);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class WebWorkerRenderer implements Renderer {
|
||||
private _messageBroker;
|
||||
constructor(messageBrokerFactory: MessageBrokerFactory,
|
||||
private _renderViewStore: RenderViewWithFragmentsStore,
|
||||
private _eventDispatcher: WebWorkerEventDispatcher) {
|
||||
this._messageBroker = messageBrokerFactory.createMessageBroker(RENDERER_CHANNEL);
|
||||
}
|
||||
/**
|
||||
* 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 = (<WebWorkerRenderViewRef>(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(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("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("attachFragmentAfterFragment", fnArgs);
|
||||
this._messageBroker.runOnUiThread(args, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches a fragment after an element.
|
||||
*/
|
||||
attachFragmentAfterElement(elementRef: RenderElementRef, fragmentRef: RenderFragmentRef) {
|
||||
var fnArgs =
|
||||
[new FnArg(elementRef, WebWorkerElementRef), new FnArg(fragmentRef, RenderFragmentRef)];
|
||||
var args = new UiArguments("attachFragmentAfterElement", fnArgs);
|
||||
this._messageBroker.runOnUiThread(args, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches a fragment.
|
||||
*/
|
||||
detachFragment(fragmentRef: RenderFragmentRef) {
|
||||
var fnArgs = [new FnArg(fragmentRef, RenderFragmentRef)];
|
||||
var args = new UiArguments("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("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("dehydrateView", 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, WebWorkerElementRef),
|
||||
new FnArg(propertyName, null),
|
||||
new FnArg(propertyValue, null)
|
||||
];
|
||||
var args = new UiArguments("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, WebWorkerElementRef),
|
||||
new FnArg(attributeName, null),
|
||||
new FnArg(attributeValue, null)
|
||||
];
|
||||
var args = new UiArguments("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, WebWorkerElementRef),
|
||||
new FnArg(className, null),
|
||||
new FnArg(isAdd, null)
|
||||
];
|
||||
var args = new UiArguments("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, WebWorkerElementRef),
|
||||
new FnArg(styleName, null),
|
||||
new FnArg(styleValue, null)
|
||||
];
|
||||
var args = new UiArguments("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, WebWorkerElementRef),
|
||||
new FnArg(methodName, null),
|
||||
new FnArg(args, null)
|
||||
];
|
||||
var uiArgs = new UiArguments("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("setText", fnArgs);
|
||||
this._messageBroker.runOnUiThread(args, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the dispatcher for all events of the given view
|
||||
*/
|
||||
setEventDispatcher(viewRef: RenderViewRef, dispatcher: RenderEventDispatcher) {
|
||||
var fnArgs = [new FnArg(viewRef, RenderViewRef)];
|
||||
var args = new UiArguments("setEventDispatcher", fnArgs);
|
||||
this._eventDispatcher.registerEventDispatcher(viewRef, dispatcher);
|
||||
this._messageBroker.runOnUiThread(args, null);
|
||||
}
|
||||
}
|
30
modules/angular2/src/web_workers/worker/xhr_impl.ts
Normal file
30
modules/angular2/src/web_workers/worker/xhr_impl.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import {Injectable} from 'angular2/di';
|
||||
import {Promise} from 'angular2/src/facade/async';
|
||||
import {XHR} from 'angular2/src/render/xhr';
|
||||
import {
|
||||
FnArg,
|
||||
UiArguments,
|
||||
MessageBroker,
|
||||
MessageBrokerFactory
|
||||
} from 'angular2/src/web_workers/worker/broker';
|
||||
import {XHR_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
|
||||
|
||||
/**
|
||||
* Implementation of render/xhr that relays XHR requests to the UI side where they are sent
|
||||
* and the result is proxied back to the worker
|
||||
*/
|
||||
@Injectable()
|
||||
export class WebWorkerXHRImpl extends XHR {
|
||||
private _messageBroker: MessageBroker;
|
||||
|
||||
constructor(messageBrokerFactory: MessageBrokerFactory) {
|
||||
super();
|
||||
this._messageBroker = messageBrokerFactory.createMessageBroker(XHR_CHANNEL);
|
||||
}
|
||||
|
||||
get(url: string): Promise<string> {
|
||||
var fnArgs: List<FnArg> = [new FnArg(url, null)];
|
||||
var args: UiArguments = new UiArguments("get", fnArgs);
|
||||
return this._messageBroker.runOnUiThread(args, String);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user