repackaging: all the file moves
This commit is contained in:
@ -0,0 +1,223 @@
|
||||
library angular2.src.web_workers.debug_tools.multi_client_server_message_bus;
|
||||
|
||||
import 'dart:io';
|
||||
import 'dart:convert' show JSON;
|
||||
import 'dart:async';
|
||||
import 'package:angular2/src/web_workers/shared/messaging_api.dart';
|
||||
import 'package:angular2/src/web_workers/shared/generic_message_bus.dart';
|
||||
|
||||
// TODO(jteplitz602): Remove hard coded result type and
|
||||
// clear messageHistory once app is done with it #3859
|
||||
class MultiClientServerMessageBus extends GenericMessageBus {
|
||||
bool hasPrimary = false;
|
||||
|
||||
@override
|
||||
MultiClientServerMessageBusSink get sink => super.sink;
|
||||
@override
|
||||
MultiClientServerMessageBusSource get source => super.source;
|
||||
|
||||
MultiClientServerMessageBus(MultiClientServerMessageBusSink sink,
|
||||
MultiClientServerMessageBusSource source)
|
||||
: super(sink, source);
|
||||
|
||||
MultiClientServerMessageBus.fromHttpServer(HttpServer server)
|
||||
: super(new MultiClientServerMessageBusSink(),
|
||||
new MultiClientServerMessageBusSource()) {
|
||||
source.onResult.listen(_resultReceived);
|
||||
server.listen((HttpRequest request) {
|
||||
if (request.uri.path == "/ws") {
|
||||
WebSocketTransformer.upgrade(request).then((WebSocket socket) {
|
||||
var wrapper = new WebSocketWrapper(
|
||||
sink.messageHistory, sink.resultMarkers, socket);
|
||||
if (!hasPrimary) {
|
||||
wrapper.setPrimary(true);
|
||||
hasPrimary = true;
|
||||
}
|
||||
sink.addConnection(wrapper);
|
||||
source.addConnection(wrapper);
|
||||
|
||||
wrapper.stream.listen(null, onDone: _handleDisconnect(wrapper));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _resultReceived(_) {
|
||||
sink.resultReceived();
|
||||
}
|
||||
|
||||
Function _handleDisconnect(WebSocketWrapper wrapper) {
|
||||
return () {
|
||||
sink.removeConnection(wrapper);
|
||||
if (wrapper.isPrimary) {
|
||||
hasPrimary = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class WebSocketWrapper {
|
||||
WebSocket _socket;
|
||||
Stream stream;
|
||||
int _numResultsReceived = 0;
|
||||
bool _isPrimary = false;
|
||||
bool caughtUp = false;
|
||||
List<String> _messageHistory;
|
||||
List<int> _resultMarkers;
|
||||
StreamController<String> _sendStream;
|
||||
|
||||
WebSocketWrapper(this._messageHistory, this._resultMarkers, this._socket) {
|
||||
stream = _socket.asBroadcastStream();
|
||||
stream.listen((encodedMessage) {
|
||||
var messages = JSON.decode(encodedMessage);
|
||||
messages.forEach((data) {
|
||||
var message = data['message'];
|
||||
if (message is Map && message.containsKey("type")) {
|
||||
if (message['type'] == 'result') {
|
||||
resultReceived();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
_sendStream = new StreamController<String>();
|
||||
_socket.addStream(_sendStream.stream);
|
||||
}
|
||||
|
||||
void send(String data) {
|
||||
_sendStream.add(data);
|
||||
}
|
||||
|
||||
bool get isPrimary => _isPrimary;
|
||||
|
||||
void resultReceived() {
|
||||
if (!isPrimary && !caughtUp) {
|
||||
_numResultsReceived++;
|
||||
sendToMarker(_numResultsReceived);
|
||||
}
|
||||
}
|
||||
|
||||
void setPrimary(bool primary) {
|
||||
_isPrimary = primary;
|
||||
if (primary) {
|
||||
caughtUp = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Sends up to the given result marker
|
||||
void sendToMarker(int markerIndex) {
|
||||
int numMessages;
|
||||
int curr;
|
||||
if (markerIndex >= _resultMarkers.length) {
|
||||
// we're past the final result marker so send all messages in history
|
||||
curr = (_resultMarkers.length > 0)
|
||||
? _resultMarkers[_resultMarkers.length - 1]
|
||||
: 0;
|
||||
numMessages = _messageHistory.length - curr;
|
||||
caughtUp = true;
|
||||
} else {
|
||||
curr = (markerIndex == 0) ? 0 : _resultMarkers[markerIndex - 1];
|
||||
var end = _resultMarkers[markerIndex];
|
||||
numMessages = end - curr;
|
||||
}
|
||||
while (numMessages > 0) {
|
||||
send(_messageHistory[curr]);
|
||||
curr++;
|
||||
numMessages--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MultiClientServerMessageBusSink extends GenericMessageBusSink {
|
||||
final List<String> messageHistory = new List<String>();
|
||||
final Set<WebSocketWrapper> openConnections = new Set<WebSocketWrapper>();
|
||||
final List<int> resultMarkers = new List<int>();
|
||||
|
||||
void resultReceived() {
|
||||
resultMarkers.add(messageHistory.length);
|
||||
}
|
||||
|
||||
void addConnection(WebSocketWrapper webSocket) {
|
||||
openConnections.add(webSocket);
|
||||
// send messages up to the first result marker to this socket
|
||||
webSocket.sendToMarker(0);
|
||||
}
|
||||
|
||||
void removeConnection(WebSocketWrapper webSocket) {
|
||||
openConnections.remove(webSocket);
|
||||
}
|
||||
|
||||
@override
|
||||
void sendMessages(List<dynamic> messages) {
|
||||
String encodedMessages = JSON.encode(messages);
|
||||
openConnections.forEach((WebSocketWrapper webSocket) {
|
||||
if (webSocket.caughtUp) {
|
||||
webSocket.send(encodedMessages);
|
||||
}
|
||||
});
|
||||
messageHistory.add(encodedMessages);
|
||||
}
|
||||
}
|
||||
|
||||
class MultiClientServerMessageBusSource extends GenericMessageBusSource {
|
||||
Function onResultReceived;
|
||||
final StreamController mainController;
|
||||
final StreamController resultController = new StreamController();
|
||||
|
||||
MultiClientServerMessageBusSource._(controller)
|
||||
: mainController = controller,
|
||||
super(controller.stream);
|
||||
|
||||
factory MultiClientServerMessageBusSource() {
|
||||
return new MultiClientServerMessageBusSource._(
|
||||
new StreamController.broadcast());
|
||||
}
|
||||
|
||||
Stream get onResult => resultController.stream;
|
||||
|
||||
void addConnection(WebSocketWrapper webSocket) {
|
||||
if (webSocket.isPrimary) {
|
||||
webSocket.stream.listen((encodedMessages) {
|
||||
var decodedMessages = _decodeMessages(encodedMessages);
|
||||
decodedMessages.forEach((decodedMessage) {
|
||||
var message = decodedMessage['message'];
|
||||
if (message is Map && message.containsKey("type")) {
|
||||
if (message['type'] == 'result') {
|
||||
// tell the bus that a result was received on the primary
|
||||
resultController.add(message);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mainController.add(decodedMessages);
|
||||
});
|
||||
} else {
|
||||
webSocket.stream.listen((encodedMessages) {
|
||||
// handle events from non-primary connection.
|
||||
var decodedMessages = _decodeMessages(encodedMessages);
|
||||
var eventMessages = new List<Map<String, dynamic>>();
|
||||
decodedMessages.forEach((decodedMessage) {
|
||||
var channel = decodedMessage['channel'];
|
||||
if (channel == EVENT_CHANNEL) {
|
||||
eventMessages.add(decodedMessage);
|
||||
}
|
||||
});
|
||||
if (eventMessages.length > 0) {
|
||||
mainController.add(eventMessages);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
List<dynamic> _decodeMessages(dynamic messages) {
|
||||
return JSON.decode(messages);
|
||||
}
|
||||
|
||||
// This is a noop for the MultiClientBus because it has to decode the JSON messages before
|
||||
// the generic bus receives them in order to check for results and forward events
|
||||
// from the non-primary connection.
|
||||
@override
|
||||
List<dynamic> decodeMessages(dynamic messages) {
|
||||
return messages;
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
library angular2.src.web_workers.debug_tools.single_client_server_message_bus;
|
||||
|
||||
import 'dart:io';
|
||||
import 'dart:convert' show JSON;
|
||||
import 'package:angular2/src/web_workers/shared/generic_message_bus.dart';
|
||||
|
||||
class SingleClientServerMessageBus extends GenericMessageBus {
|
||||
bool connected = false;
|
||||
|
||||
@override
|
||||
SingleClientServerMessageBusSink get sink => super.sink;
|
||||
@override
|
||||
SingleClientServerMessageBusSource get source => super.source;
|
||||
|
||||
SingleClientServerMessageBus(SingleClientServerMessageBusSink sink,
|
||||
SingleClientServerMessageBusSource source)
|
||||
: super(sink, source);
|
||||
|
||||
SingleClientServerMessageBus.fromHttpServer(HttpServer server)
|
||||
: super(new SingleClientServerMessageBusSink(),
|
||||
new SingleClientServerMessageBusSource()) {
|
||||
server.listen((HttpRequest request) {
|
||||
if (request.uri.path == "/ws") {
|
||||
if (!connected) {
|
||||
WebSocketTransformer.upgrade(request).then((WebSocket socket) {
|
||||
sink.setConnection(socket);
|
||||
|
||||
var stream = socket.asBroadcastStream();
|
||||
source.attachTo(stream);
|
||||
stream.listen(null, onDone: _handleDisconnect);
|
||||
}).catchError((error) {
|
||||
throw error;
|
||||
connected = false;
|
||||
});
|
||||
connected = true;
|
||||
} else {
|
||||
// refuse additional clients
|
||||
request.response.statusCode = HttpStatus.SERVICE_UNAVAILABLE;
|
||||
request.response.write("Maximum number of clients connected.");
|
||||
request.response.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _handleDisconnect() {
|
||||
sink.removeConnection();
|
||||
connected = false;
|
||||
}
|
||||
}
|
||||
|
||||
class SingleClientServerMessageBusSink extends GenericMessageBusSink {
|
||||
final List<String> _messageBuffer = new List<String>();
|
||||
WebSocket _socket = null;
|
||||
|
||||
void setConnection(WebSocket webSocket) {
|
||||
_socket = webSocket;
|
||||
_sendBufferedMessages();
|
||||
}
|
||||
|
||||
void removeConnection() {
|
||||
_socket = null;
|
||||
}
|
||||
|
||||
@override
|
||||
void sendMessages(List<dynamic> message) {
|
||||
String encodedMessages = JSON.encode(message);
|
||||
if (_socket != null) {
|
||||
_socket.add(encodedMessages);
|
||||
} else {
|
||||
_messageBuffer.add(encodedMessages);
|
||||
}
|
||||
}
|
||||
|
||||
void _sendBufferedMessages() {
|
||||
_messageBuffer.forEach((message) => _socket.add(message));
|
||||
_messageBuffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
class SingleClientServerMessageBusSource extends GenericMessageBusSource {
|
||||
SingleClientServerMessageBusSource() : super(null);
|
||||
|
||||
@override
|
||||
List<dynamic> decodeMessages(dynamic messages) {
|
||||
return JSON.decode(messages);
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
library angular2.src.web_workers.worker.web_socket_message_bus;
|
||||
|
||||
import 'dart:html';
|
||||
import 'dart:convert' show JSON;
|
||||
import 'package:angular2/src/web_workers/shared/generic_message_bus.dart';
|
||||
|
||||
class WebSocketMessageBus extends GenericMessageBus {
|
||||
WebSocketMessageBus(
|
||||
WebSocketMessageBusSink sink, WebSocketMessageBusSource source)
|
||||
: super(sink, source);
|
||||
|
||||
WebSocketMessageBus.fromWebSocket(WebSocket webSocket)
|
||||
: super(new WebSocketMessageBusSink(webSocket),
|
||||
new WebSocketMessageBusSource(webSocket));
|
||||
}
|
||||
|
||||
class WebSocketMessageBusSink extends GenericMessageBusSink {
|
||||
final WebSocket _webSocket;
|
||||
|
||||
WebSocketMessageBusSink(this._webSocket);
|
||||
|
||||
void sendMessages(List<dynamic> messages) {
|
||||
_webSocket.send(JSON.encode(messages));
|
||||
}
|
||||
}
|
||||
|
||||
class WebSocketMessageBusSource extends GenericMessageBusSource {
|
||||
WebSocketMessageBusSource(WebSocket webSocket) : super(webSocket.onMessage);
|
||||
|
||||
List<dynamic> decodeMessages(MessageEvent event) {
|
||||
var messages = event.data;
|
||||
return JSON.decode(messages);
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
import {OpaqueToken} from "angular2/src/core/di";
|
||||
|
||||
export const ON_WEB_WORKER = /*@ts2dart_const*/ new OpaqueToken('WebWorker.onWebWorker');
|
@ -0,0 +1,167 @@
|
||||
import {MessageBus} from "angular2/src/web_workers/shared/message_bus";
|
||||
import {
|
||||
print,
|
||||
isPresent,
|
||||
DateWrapper,
|
||||
stringify,
|
||||
Type,
|
||||
StringWrapper
|
||||
} from "angular2/src/facade/lang";
|
||||
import {
|
||||
PromiseCompleter,
|
||||
PromiseWrapper,
|
||||
ObservableWrapper,
|
||||
EventEmitter
|
||||
} from "angular2/src/facade/async";
|
||||
import {StringMapWrapper, MapWrapper} from "angular2/src/facade/collection";
|
||||
import {Serializer} from "angular2/src/web_workers/shared/serializer";
|
||||
import {Injectable} from "angular2/src/core/di";
|
||||
export {Type} from "angular2/src/facade/lang";
|
||||
|
||||
export abstract class ClientMessageBrokerFactory {
|
||||
/**
|
||||
* Initializes the given channel and attaches a new {@link ClientMessageBroker} to it.
|
||||
*/
|
||||
abstract createMessageBroker(channel: string, runInZone?: boolean): ClientMessageBroker;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ClientMessageBrokerFactory_ extends ClientMessageBrokerFactory {
|
||||
/** @internal */
|
||||
public _serializer: Serializer;
|
||||
constructor(private _messageBus: MessageBus, _serializer: Serializer) {
|
||||
super();
|
||||
this._serializer = _serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the given channel and attaches a new {@link ClientMessageBroker} to it.
|
||||
*/
|
||||
createMessageBroker(channel: string, runInZone: boolean = true): ClientMessageBroker {
|
||||
this._messageBus.initChannel(channel, runInZone);
|
||||
return new ClientMessageBroker_(this._messageBus, this._serializer, channel);
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class ClientMessageBroker {
|
||||
abstract runOnService(args: UiArguments, returnType: Type): Promise<any>;
|
||||
}
|
||||
|
||||
export class ClientMessageBroker_ extends ClientMessageBroker {
|
||||
private _pending: Map<string, PromiseCompleter<any>> = new Map<string, PromiseCompleter<any>>();
|
||||
private _sink: EventEmitter<any>;
|
||||
/** @internal */
|
||||
public _serializer: Serializer;
|
||||
|
||||
constructor(messageBus: MessageBus, _serializer: Serializer, public channel) {
|
||||
super();
|
||||
this._sink = messageBus.to(channel);
|
||||
this._serializer = _serializer;
|
||||
var source = messageBus.from(channel);
|
||||
ObservableWrapper.subscribe(source,
|
||||
(message: {[key: string]: any}) => this._handleMessage(message));
|
||||
}
|
||||
|
||||
private _generateMessageId(name: string): string {
|
||||
var time: string = stringify(DateWrapper.toMillis(DateWrapper.now()));
|
||||
var iteration: number = 0;
|
||||
var id: string = name + time + stringify(iteration);
|
||||
while (isPresent(this._pending[id])) {
|
||||
id = `${name}${time}${iteration}`;
|
||||
iteration++;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
runOnService(args: UiArguments, returnType: Type): Promise<any> {
|
||||
var fnArgs = [];
|
||||
if (isPresent(args.args)) {
|
||||
args.args.forEach(argument => {
|
||||
if (argument.type != null) {
|
||||
fnArgs.push(this._serializer.serialize(argument.value, argument.type));
|
||||
} else {
|
||||
fnArgs.push(argument.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var promise: Promise<any>;
|
||||
var id: string = null;
|
||||
if (returnType != null) {
|
||||
var completer: PromiseCompleter<any> = PromiseWrapper.completer();
|
||||
id = this._generateMessageId(args.method);
|
||||
this._pending.set(id, completer);
|
||||
PromiseWrapper.catchError(completer.promise, (err, stack?) => {
|
||||
print(err);
|
||||
completer.reject(err, stack);
|
||||
});
|
||||
|
||||
promise = PromiseWrapper.then(completer.promise, (value: any) => {
|
||||
if (this._serializer == null) {
|
||||
return value;
|
||||
} else {
|
||||
return this._serializer.deserialize(value, returnType);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
promise = null;
|
||||
}
|
||||
|
||||
// TODO(jteplitz602): Create a class for these messages so we don't keep using StringMap #3685
|
||||
var message = {'method': args.method, 'args': fnArgs};
|
||||
if (id != null) {
|
||||
message['id'] = id;
|
||||
}
|
||||
ObservableWrapper.callEmit(this._sink, message);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
private _handleMessage(message: {[key: string]: any}): void {
|
||||
var data = new MessageData(message);
|
||||
// TODO(jteplitz602): replace these strings with messaging constants #3685
|
||||
if (StringWrapper.equals(data.type, "result") || StringWrapper.equals(data.type, "error")) {
|
||||
var id = data.id;
|
||||
if (this._pending.has(id)) {
|
||||
if (StringWrapper.equals(data.type, "result")) {
|
||||
this._pending.get(id).resolve(data.value);
|
||||
} else {
|
||||
this._pending.get(id).reject(data.value, null);
|
||||
}
|
||||
this._pending.delete(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MessageData {
|
||||
type: string;
|
||||
value: any;
|
||||
id: string;
|
||||
|
||||
constructor(data: {[key: string]: any}) {
|
||||
this.type = StringMapWrapper.get(data, "type");
|
||||
this.id = this._getValueIfPresent(data, "id");
|
||||
this.value = this._getValueIfPresent(data, "value");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value from the StringMap if present. Otherwise returns null
|
||||
* @internal
|
||||
*/
|
||||
_getValueIfPresent(data: {[key: string]: any}, key: string) {
|
||||
if (StringMapWrapper.contains(data, key)) {
|
||||
return StringMapWrapper.get(data, key);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class FnArg {
|
||||
constructor(public value, public type: Type) {}
|
||||
}
|
||||
|
||||
export class UiArguments {
|
||||
constructor(public method: string, public args?: FnArg[]) {}
|
||||
}
|
@ -0,0 +1,156 @@
|
||||
library angular2.src.web_workers.shared.generic_message_bus;
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:angular2/src/facade/async.dart' show EventEmitter;
|
||||
import 'package:angular2/src/web_workers/shared/message_bus.dart'
|
||||
show MessageBus, MessageBusSink, MessageBusSource;
|
||||
import 'package:angular2/src/core/zone/ng_zone.dart';
|
||||
import 'package:angular2/src/facade/lang.dart';
|
||||
import 'package:angular2/src/facade/exceptions.dart';
|
||||
|
||||
class GenericMessageBus implements MessageBus {
|
||||
final MessageBusSink _sink;
|
||||
final MessageBusSource _source;
|
||||
|
||||
MessageBusSink get sink => _sink;
|
||||
MessageBusSource get source => _source;
|
||||
|
||||
GenericMessageBus(MessageBusSink sink, MessageBusSource source)
|
||||
: _sink = sink,
|
||||
_source = source;
|
||||
|
||||
void attachToZone(NgZone zone) {
|
||||
_sink.attachToZone(zone);
|
||||
_source.attachToZone(zone);
|
||||
}
|
||||
|
||||
void initChannel(String channel, [bool runInZone = true]) {
|
||||
_sink.initChannel(channel, runInZone);
|
||||
_source.initChannel(channel, runInZone);
|
||||
}
|
||||
|
||||
EventEmitter from(String channel) {
|
||||
return _source.from(channel);
|
||||
}
|
||||
|
||||
EventEmitter to(String channel) {
|
||||
return _sink.to(channel);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class GenericMessageBusSink implements MessageBusSink {
|
||||
NgZone _zone;
|
||||
final _channels = new Map<String, _Channel>();
|
||||
final _messageBuffer = new List<dynamic>();
|
||||
|
||||
void attachToZone(NgZone zone) {
|
||||
_zone = zone;
|
||||
_zone.runOutsideAngular(() {
|
||||
_zone.onStable.listen((_) {
|
||||
if (_messageBuffer.length > 0) {
|
||||
sendMessages(_messageBuffer);
|
||||
_messageBuffer.clear();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void initChannel(String channelName, [bool runInZone = true]) {
|
||||
if (_channels.containsKey(channelName)) {
|
||||
throw new BaseException("${channelName} has already been initialized.");
|
||||
}
|
||||
|
||||
var emitter = new EventEmitter();
|
||||
var channel = new _Channel(emitter, runInZone);
|
||||
|
||||
emitter.listen((data) {
|
||||
var message = {'channel': channelName, 'message': data};
|
||||
if (runInZone) {
|
||||
_messageBuffer.add(message);
|
||||
} else {
|
||||
sendMessages([message]);
|
||||
}
|
||||
});
|
||||
|
||||
_channels[channelName] = channel;
|
||||
}
|
||||
|
||||
EventEmitter to(String channelName) {
|
||||
if (_channels.containsKey(channelName)) {
|
||||
return _channels[channelName].emitter;
|
||||
} else {
|
||||
throw new BaseException(
|
||||
"${channelName} is not set up. Did you forget to call initChannel?");
|
||||
}
|
||||
}
|
||||
|
||||
void sendMessages(List<dynamic> messages);
|
||||
}
|
||||
|
||||
abstract class GenericMessageBusSource implements MessageBusSource {
|
||||
Stream _stream;
|
||||
final _channels = new Map<String, _Channel>();
|
||||
NgZone _zone;
|
||||
|
||||
Stream get stream => _stream;
|
||||
|
||||
GenericMessageBusSource(Stream stream) {
|
||||
attachTo(stream);
|
||||
}
|
||||
|
||||
void attachTo(Stream stream) {
|
||||
_stream = stream;
|
||||
if (stream != null) {
|
||||
stream.listen((messages) {
|
||||
List<dynamic> decodedMessages = decodeMessages(messages);
|
||||
if (decodedMessages != null) {
|
||||
decodedMessages.forEach((message) => _handleMessage(message));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void attachToZone(NgZone zone) {
|
||||
_zone = zone;
|
||||
}
|
||||
|
||||
void initChannel(String channelName, [bool runInZone = true]) {
|
||||
if (_channels.containsKey(channelName)) {
|
||||
throw new BaseException("${channelName} has already been initialized.");
|
||||
}
|
||||
|
||||
var emitter = new EventEmitter();
|
||||
var channelInfo = new _Channel(emitter, runInZone);
|
||||
_channels[channelName] = channelInfo;
|
||||
}
|
||||
|
||||
EventEmitter from(String channelName) {
|
||||
if (_channels.containsKey(channelName)) {
|
||||
return _channels[channelName].emitter;
|
||||
} else {
|
||||
throw new BaseException(
|
||||
"${channelName} is not set up. Did you forget to call initChannel?");
|
||||
}
|
||||
}
|
||||
|
||||
void _handleMessage(dynamic data) {
|
||||
var channelName = data['channel'];
|
||||
if (_channels.containsKey(channelName)) {
|
||||
var channelInfo = _channels[channelName];
|
||||
if (channelInfo.runInZone) {
|
||||
_zone.run(() => channelInfo.emitter.add(data['message']));
|
||||
} else {
|
||||
channelInfo.emitter.add(data['message']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<dynamic> decodeMessages(dynamic message);
|
||||
}
|
||||
|
||||
class _Channel {
|
||||
EventEmitter emitter;
|
||||
bool runInZone;
|
||||
|
||||
_Channel(this.emitter, this.runInZone);
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
library angular2.src.web_workers.shared.isolate_message_bus;
|
||||
|
||||
import 'dart:isolate';
|
||||
import 'package:angular2/src/web_workers/shared/generic_message_bus.dart';
|
||||
|
||||
class IsolateMessageBus extends GenericMessageBus {
|
||||
IsolateMessageBus(IsolateMessageBusSink sink, IsolateMessageBusSource source)
|
||||
: super(sink, source);
|
||||
}
|
||||
|
||||
class IsolateMessageBusSink extends GenericMessageBusSink {
|
||||
final SendPort _port;
|
||||
|
||||
IsolateMessageBusSink(SendPort port) : _port = port;
|
||||
|
||||
@override
|
||||
void sendMessages(List<dynamic> messages) {
|
||||
_port.send(messages);
|
||||
}
|
||||
}
|
||||
|
||||
class IsolateMessageBusSource extends GenericMessageBusSource {
|
||||
IsolateMessageBusSource(ReceivePort port) : super(port.asBroadcastStream());
|
||||
|
||||
@override
|
||||
List<dynamic> decodeMessages(dynamic messages) {
|
||||
if (messages is SendPort) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
import {EventEmitter} from 'angular2/src/facade/async';
|
||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||
export {EventEmitter, Observable} from 'angular2/src/facade/async';
|
||||
|
||||
/**
|
||||
* Message Bus is a low level API used to communicate between the UI and the background.
|
||||
* Communication is based on a channel abstraction. Messages published in a
|
||||
* given channel to one MessageBusSink are received on the same channel
|
||||
* by the corresponding MessageBusSource.
|
||||
*/
|
||||
export abstract class MessageBus implements MessageBusSource, MessageBusSink {
|
||||
/**
|
||||
* Sets up a new channel on the MessageBus.
|
||||
* MUST be called before calling from or to on the channel.
|
||||
* If runInZone is true then the source will emit events inside the angular zone
|
||||
* and the sink will buffer messages and send only once the zone exits.
|
||||
* if runInZone is false then the source will emit events inside the global zone
|
||||
* and the sink will send messages immediately.
|
||||
*/
|
||||
abstract initChannel(channel: string, runInZone?: boolean): void;
|
||||
|
||||
/**
|
||||
* Assigns this bus to the given zone.
|
||||
* Any callbacks attached to channels where runInZone was set to true on initialization
|
||||
* will be executed in the given zone.
|
||||
*/
|
||||
abstract attachToZone(zone: NgZone): void;
|
||||
|
||||
/**
|
||||
* Returns an {@link EventEmitter} that emits every time a message
|
||||
* is received on the given channel.
|
||||
*/
|
||||
abstract from(channel: string): EventEmitter<any>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns an {@link EventEmitter} for the given channel
|
||||
* To publish methods to that channel just call next (or add in dart) on the returned emitter
|
||||
*/
|
||||
abstract to(channel: string): EventEmitter<any>;
|
||||
}
|
||||
|
||||
export interface MessageBusSource {
|
||||
/**
|
||||
* Sets up a new channel on the MessageBusSource.
|
||||
* MUST be called before calling from on the channel.
|
||||
* If runInZone is true then the source will emit events inside the angular zone.
|
||||
* if runInZone is false then the source will emit events inside the global zone.
|
||||
*/
|
||||
initChannel(channel: string, runInZone: boolean): void;
|
||||
|
||||
/**
|
||||
* Assigns this source to the given zone.
|
||||
* Any channels which are initialized with runInZone set to true will emit events that will be
|
||||
* executed within the given zone.
|
||||
*/
|
||||
attachToZone(zone: NgZone): void;
|
||||
|
||||
/**
|
||||
* Returns an {@link EventEmitter} that emits every time a message
|
||||
* is received on the given channel.
|
||||
*/
|
||||
from(channel: string): EventEmitter<any>;
|
||||
}
|
||||
|
||||
export interface MessageBusSink {
|
||||
/**
|
||||
* Sets up a new channel on the MessageBusSink.
|
||||
* MUST be called before calling to on the channel.
|
||||
* If runInZone is true the sink will buffer messages and send only once the zone exits.
|
||||
* if runInZone is false the sink will send messages immediatly.
|
||||
*/
|
||||
initChannel(channel: string, runInZone: boolean): void;
|
||||
|
||||
/**
|
||||
* Assigns this sink to the given zone.
|
||||
* Any channels which are initialized with runInZone set to true will wait for the given zone
|
||||
* to exit before sending messages.
|
||||
*/
|
||||
attachToZone(zone: NgZone): void;
|
||||
|
||||
/**
|
||||
* Returns an {@link EventEmitter} for the given channel
|
||||
* To publish methods to that channel just call next (or add in dart) on the returned emitter
|
||||
*/
|
||||
to(channel: string): EventEmitter<any>;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* All channels used by angular's WebWorker components are listed here.
|
||||
* You should not use these channels in your application code.
|
||||
*/
|
||||
export const RENDERER_CHANNEL = "ng-Renderer";
|
||||
export const XHR_CHANNEL = "ng-XHR";
|
||||
export const EVENT_CHANNEL = "ng-Events";
|
||||
export const ROUTER_CHANNEL = "ng-Router";
|
@ -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;
|
@ -0,0 +1,147 @@
|
||||
import {
|
||||
MessageBus,
|
||||
MessageBusSource,
|
||||
MessageBusSink
|
||||
} from "angular2/src/web_workers/shared/message_bus";
|
||||
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {Injectable} from "angular2/src/core/di";
|
||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||
|
||||
// TODO(jteplitz602) Replace this with the definition in lib.webworker.d.ts(#3492)
|
||||
export interface PostMessageTarget { postMessage: (message: any, transfer?:[ArrayBuffer]) => void; }
|
||||
|
||||
export class PostMessageBusSink implements MessageBusSink {
|
||||
private _zone: NgZone;
|
||||
private _channels: {[key: string]: _Channel} = StringMapWrapper.create();
|
||||
private _messageBuffer: Array<Object> = [];
|
||||
|
||||
constructor(private _postMessageTarget: PostMessageTarget) {}
|
||||
|
||||
attachToZone(zone: NgZone): void {
|
||||
this._zone = zone;
|
||||
this._zone.runOutsideAngular(() => {
|
||||
ObservableWrapper.subscribe(this._zone.onStable, (_) => { this._handleOnEventDone(); });
|
||||
});
|
||||
}
|
||||
|
||||
initChannel(channel: string, runInZone: boolean = true): void {
|
||||
if (StringMapWrapper.contains(this._channels, channel)) {
|
||||
throw new BaseException(`${channel} has already been initialized`);
|
||||
}
|
||||
|
||||
var emitter = new EventEmitter(false);
|
||||
var channelInfo = new _Channel(emitter, runInZone);
|
||||
this._channels[channel] = channelInfo;
|
||||
emitter.subscribe((data: Object) => {
|
||||
var message = {channel: channel, message: data};
|
||||
if (runInZone) {
|
||||
this._messageBuffer.push(message);
|
||||
} else {
|
||||
this._sendMessages([message]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
to(channel: string): EventEmitter<any> {
|
||||
if (StringMapWrapper.contains(this._channels, channel)) {
|
||||
return this._channels[channel].emitter;
|
||||
} else {
|
||||
throw new BaseException(`${channel} is not set up. Did you forget to call initChannel?`);
|
||||
}
|
||||
}
|
||||
|
||||
private _handleOnEventDone() {
|
||||
if (this._messageBuffer.length > 0) {
|
||||
this._sendMessages(this._messageBuffer);
|
||||
this._messageBuffer = [];
|
||||
}
|
||||
}
|
||||
|
||||
private _sendMessages(messages: Array<Object>) { this._postMessageTarget.postMessage(messages); }
|
||||
}
|
||||
|
||||
export class PostMessageBusSource implements MessageBusSource {
|
||||
private _zone: NgZone;
|
||||
private _channels: {[key: string]: _Channel} = StringMapWrapper.create();
|
||||
|
||||
constructor(eventTarget?: EventTarget) {
|
||||
if (eventTarget) {
|
||||
eventTarget.addEventListener("message", (ev: MessageEvent) => this._handleMessages(ev));
|
||||
} else {
|
||||
// if no eventTarget is given we assume we're in a WebWorker and listen on the global scope
|
||||
addEventListener("message", (ev: MessageEvent) => this._handleMessages(ev));
|
||||
}
|
||||
}
|
||||
|
||||
attachToZone(zone: NgZone) { this._zone = zone; }
|
||||
|
||||
initChannel(channel: string, runInZone: boolean = true) {
|
||||
if (StringMapWrapper.contains(this._channels, channel)) {
|
||||
throw new BaseException(`${channel} has already been initialized`);
|
||||
}
|
||||
|
||||
var emitter = new EventEmitter(false);
|
||||
var channelInfo = new _Channel(emitter, runInZone);
|
||||
this._channels[channel] = channelInfo;
|
||||
}
|
||||
|
||||
from(channel: string): EventEmitter<any> {
|
||||
if (StringMapWrapper.contains(this._channels, channel)) {
|
||||
return this._channels[channel].emitter;
|
||||
} else {
|
||||
throw new BaseException(`${channel} is not set up. Did you forget to call initChannel?`);
|
||||
}
|
||||
}
|
||||
|
||||
private _handleMessages(ev: MessageEvent): void {
|
||||
var messages = ev.data;
|
||||
for (var i = 0; i < messages.length; i++) {
|
||||
this._handleMessage(messages[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private _handleMessage(data: any): void {
|
||||
var channel = data.channel;
|
||||
if (StringMapWrapper.contains(this._channels, channel)) {
|
||||
var channelInfo = this._channels[channel];
|
||||
if (channelInfo.runInZone) {
|
||||
this._zone.run(() => { channelInfo.emitter.emit(data.message); });
|
||||
} else {
|
||||
channelInfo.emitter.emit(data.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A TypeScript implementation of {@link MessageBus} for communicating via JavaScript's
|
||||
* postMessage API.
|
||||
*/
|
||||
@Injectable()
|
||||
export class PostMessageBus implements MessageBus {
|
||||
constructor(public sink: PostMessageBusSink, public source: PostMessageBusSource) {}
|
||||
|
||||
attachToZone(zone: NgZone): void {
|
||||
this.source.attachToZone(zone);
|
||||
this.sink.attachToZone(zone);
|
||||
}
|
||||
|
||||
initChannel(channel: string, runInZone: boolean = true): void {
|
||||
this.source.initChannel(channel, runInZone);
|
||||
this.sink.initChannel(channel, runInZone);
|
||||
}
|
||||
|
||||
from(channel: string): EventEmitter<any> { return this.source.from(channel); }
|
||||
|
||||
to(channel: string): EventEmitter<any> { return this.sink.to(channel); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class that wraps a channel's {@link EventEmitter} and
|
||||
* keeps track of if it should run in the zone.
|
||||
*/
|
||||
class _Channel {
|
||||
constructor(public emitter: EventEmitter<any>, public runInZone: boolean) {}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
import {Injectable} from "angular2/src/core/di";
|
||||
|
||||
@Injectable()
|
||||
export class RenderStore {
|
||||
private _nextIndex: number = 0;
|
||||
private _lookupById: Map<number, any>;
|
||||
private _lookupByObject: Map<any, number>;
|
||||
|
||||
constructor() {
|
||||
this._lookupById = new Map<number, any>();
|
||||
this._lookupByObject = new Map<any, number>();
|
||||
}
|
||||
|
||||
allocateId(): number { return this._nextIndex++; }
|
||||
|
||||
store(obj: any, id: number): void {
|
||||
this._lookupById.set(id, obj);
|
||||
this._lookupByObject.set(obj, id);
|
||||
}
|
||||
|
||||
remove(obj: any): void {
|
||||
var index = this._lookupByObject.get(obj);
|
||||
this._lookupByObject.delete(obj);
|
||||
this._lookupById.delete(index);
|
||||
}
|
||||
|
||||
deserialize(id: number): any {
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!this._lookupById.has(id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this._lookupById.get(id);
|
||||
}
|
||||
|
||||
serialize(obj: any): number {
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
return this._lookupByObject.get(obj);
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
// This file contains interface versions of browser types that can be serialized to Plain Old
|
||||
// JavaScript Objects
|
||||
export class LocationType {
|
||||
constructor(public href: string, public protocol: string, public host: string,
|
||||
public hostname: string, public port: string, public pathname: string,
|
||||
public search: string, public hash: string, public origin: string) {}
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
import {Type, isArray, isPresent, serializeEnum, deserializeEnum} from "angular2/src/facade/lang";
|
||||
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||
|
||||
import {Map, StringMapWrapper, MapWrapper} from "angular2/src/facade/collection";
|
||||
import {RenderComponentType} from "angular2/src/core/render/api";
|
||||
import {Injectable} from "angular2/src/core/di";
|
||||
import {RenderStore} from 'angular2/src/web_workers/shared/render_store';
|
||||
import {ViewEncapsulation, VIEW_ENCAPSULATION_VALUES} from 'angular2/src/core/metadata/view';
|
||||
import {LocationType} from './serialized_types';
|
||||
|
||||
// PRIMITIVE is any type that does not need to be serialized (string, number, boolean)
|
||||
// We set it to String so that it is considered a Type.
|
||||
export const PRIMITIVE: Type = /*@ts2dart_const*/ String;
|
||||
|
||||
@Injectable()
|
||||
export class Serializer {
|
||||
constructor(private _renderStore: RenderStore) {}
|
||||
|
||||
serialize(obj: any, type: any): Object {
|
||||
if (!isPresent(obj)) {
|
||||
return null;
|
||||
}
|
||||
if (isArray(obj)) {
|
||||
return (<any[]>obj).map(v => this.serialize(v, type));
|
||||
}
|
||||
if (type == PRIMITIVE) {
|
||||
return obj;
|
||||
}
|
||||
if (type == RenderStoreObject) {
|
||||
return this._renderStore.serialize(obj);
|
||||
} else if (type === RenderComponentType) {
|
||||
return this._serializeRenderComponentType(obj);
|
||||
} else if (type === ViewEncapsulation) {
|
||||
return serializeEnum(obj);
|
||||
} else if (type === LocationType) {
|
||||
return this._serializeLocation(obj);
|
||||
} else {
|
||||
throw new BaseException("No serializer for " + type.toString());
|
||||
}
|
||||
}
|
||||
|
||||
deserialize(map: any, type: any, data?: any): any {
|
||||
if (!isPresent(map)) {
|
||||
return null;
|
||||
}
|
||||
if (isArray(map)) {
|
||||
var obj: any[] = [];
|
||||
(<any[]>map).forEach(val => obj.push(this.deserialize(val, type, data)));
|
||||
return obj;
|
||||
}
|
||||
if (type == PRIMITIVE) {
|
||||
return map;
|
||||
}
|
||||
|
||||
if (type == RenderStoreObject) {
|
||||
return this._renderStore.deserialize(map);
|
||||
} else if (type === RenderComponentType) {
|
||||
return this._deserializeRenderComponentType(map);
|
||||
} else if (type === ViewEncapsulation) {
|
||||
return VIEW_ENCAPSULATION_VALUES[map];
|
||||
} else if (type === LocationType) {
|
||||
return this._deserializeLocation(map);
|
||||
} else {
|
||||
throw new BaseException("No deserializer for " + type.toString());
|
||||
}
|
||||
}
|
||||
|
||||
mapToObject(map: Map<string, any>, type?: Type): Object {
|
||||
var object = {};
|
||||
var serialize = isPresent(type);
|
||||
|
||||
map.forEach((value, key) => {
|
||||
if (serialize) {
|
||||
object[key] = this.serialize(value, type);
|
||||
} else {
|
||||
object[key] = value;
|
||||
}
|
||||
});
|
||||
return object;
|
||||
}
|
||||
|
||||
/*
|
||||
* Transforms a Javascript object (StringMap) into a Map<string, V>
|
||||
* If the values need to be deserialized pass in their type
|
||||
* and they will be deserialized before being placed in the map
|
||||
*/
|
||||
objectToMap(obj: {[key: string]: any}, type?: Type, data?: any): Map<string, any> {
|
||||
if (isPresent(type)) {
|
||||
var map = new Map<string, any>();
|
||||
StringMapWrapper.forEach(obj,
|
||||
(val, key) => { map.set(key, this.deserialize(val, type, data)); });
|
||||
return map;
|
||||
} else {
|
||||
return MapWrapper.createFromStringMap(obj);
|
||||
}
|
||||
}
|
||||
|
||||
private _serializeLocation(loc: LocationType): Object {
|
||||
return {
|
||||
'href': loc.href,
|
||||
'protocol': loc.protocol,
|
||||
'host': loc.host,
|
||||
'hostname': loc.hostname,
|
||||
'port': loc.port,
|
||||
'pathname': loc.pathname,
|
||||
'search': loc.search,
|
||||
'hash': loc.hash,
|
||||
'origin': loc.origin
|
||||
};
|
||||
}
|
||||
|
||||
private _deserializeLocation(loc: {[key: string]: any}): LocationType {
|
||||
return new LocationType(loc['href'], loc['protocol'], loc['host'], loc['hostname'], loc['port'],
|
||||
loc['pathname'], loc['search'], loc['hash'], loc['origin']);
|
||||
}
|
||||
|
||||
private _serializeRenderComponentType(obj: RenderComponentType): Object {
|
||||
return {
|
||||
'id': obj.id,
|
||||
'templateUrl': obj.templateUrl,
|
||||
'slotCount': obj.slotCount,
|
||||
'encapsulation': this.serialize(obj.encapsulation, ViewEncapsulation),
|
||||
'styles': this.serialize(obj.styles, PRIMITIVE)
|
||||
};
|
||||
}
|
||||
|
||||
private _deserializeRenderComponentType(map: {[key: string]: any}): RenderComponentType {
|
||||
return new RenderComponentType(map['id'], map['templateUrl'], map['slotCount'],
|
||||
this.deserialize(map['encapsulation'], ViewEncapsulation),
|
||||
this.deserialize(map['styles'], PRIMITIVE));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class RenderStoreObject {}
|
@ -0,0 +1,99 @@
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
import {ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {Serializer} from "angular2/src/web_workers/shared/serializer";
|
||||
import {isPresent, Type, FunctionWrapper} from "angular2/src/facade/lang";
|
||||
import {MessageBus} from "angular2/src/web_workers/shared/message_bus";
|
||||
import {EventEmitter, PromiseWrapper, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
export abstract class ServiceMessageBrokerFactory {
|
||||
/**
|
||||
* Initializes the given channel and attaches a new {@link ServiceMessageBroker} to it.
|
||||
*/
|
||||
abstract createMessageBroker(channel: string, runInZone?: boolean): ServiceMessageBroker;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ServiceMessageBrokerFactory_ extends ServiceMessageBrokerFactory {
|
||||
/** @internal */
|
||||
public _serializer: Serializer;
|
||||
|
||||
constructor(private _messageBus: MessageBus, _serializer: Serializer) {
|
||||
super();
|
||||
this._serializer = _serializer;
|
||||
}
|
||||
|
||||
createMessageBroker(channel: string, runInZone: boolean = true): ServiceMessageBroker {
|
||||
this._messageBus.initChannel(channel, runInZone);
|
||||
return new ServiceMessageBroker_(this._messageBus, this._serializer, channel);
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class ServiceMessageBroker {
|
||||
abstract registerMethod(methodName: string, signature: Type[], method: Function,
|
||||
returnType?: Type): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class for UIComponents that allows components to register methods.
|
||||
* If a registered method message is received from the broker on the worker,
|
||||
* the UIMessageBroker deserializes its arguments and calls the registered method.
|
||||
* If that method returns a promise, the UIMessageBroker returns the result to the worker.
|
||||
*/
|
||||
export class ServiceMessageBroker_ extends ServiceMessageBroker {
|
||||
private _sink: EventEmitter<any>;
|
||||
private _methods: Map<string, Function> = new Map<string, Function>();
|
||||
|
||||
constructor(messageBus: MessageBus, private _serializer: Serializer, public channel) {
|
||||
super();
|
||||
this._sink = messageBus.to(channel);
|
||||
var source = messageBus.from(channel);
|
||||
ObservableWrapper.subscribe(source, (message) => this._handleMessage(message));
|
||||
}
|
||||
|
||||
registerMethod(methodName: string, signature: Type[], method: (..._: any[]) => Promise<any>| void,
|
||||
returnType?: Type): void {
|
||||
this._methods.set(methodName, (message: ReceivedMessage) => {
|
||||
var serializedArgs = message.args;
|
||||
let numArgs = signature === null ? 0 : signature.length;
|
||||
var deserializedArgs: any[] = ListWrapper.createFixedSize(numArgs);
|
||||
for (var i = 0; i < numArgs; i++) {
|
||||
var serializedArg = serializedArgs[i];
|
||||
deserializedArgs[i] = this._serializer.deserialize(serializedArg, signature[i]);
|
||||
}
|
||||
|
||||
var promise = FunctionWrapper.apply(method, deserializedArgs);
|
||||
if (isPresent(returnType) && isPresent(promise)) {
|
||||
this._wrapWebWorkerPromise(message.id, promise, returnType);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _handleMessage(map: {[key: string]: any}): void {
|
||||
var message = new ReceivedMessage(map);
|
||||
if (this._methods.has(message.method)) {
|
||||
this._methods.get(message.method)(message);
|
||||
}
|
||||
}
|
||||
|
||||
private _wrapWebWorkerPromise(id: string, promise: Promise<any>, type: Type): void {
|
||||
PromiseWrapper.then(promise, (result: any) => {
|
||||
ObservableWrapper.callEmit(
|
||||
this._sink,
|
||||
{'type': 'result', 'value': this._serializer.serialize(result, type), 'id': id});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class ReceivedMessage {
|
||||
method: string;
|
||||
args: any[];
|
||||
id: string;
|
||||
type: string;
|
||||
|
||||
constructor(data: {[key: string]: any}) {
|
||||
this.method = data['method'];
|
||||
this.args = data['args'];
|
||||
this.id = data['id'];
|
||||
this.type = data['type'];
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
library angular2.src.web_workers.ui.bind;
|
||||
|
||||
/**
|
||||
* Binding is not necessary in dart.
|
||||
* This method just returns the passed function regardless of scope.
|
||||
* It's only here to match the TypeScript implementation.
|
||||
* TODO(jteplitz602) Have ts2dart remove calls to bind(#3820)
|
||||
*/
|
||||
Function bind(Function fn, dynamic scope) {
|
||||
return fn;
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
export function bind(fn: Function, scope: any): Function {
|
||||
return fn.bind(scope);
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
import {Serializer, RenderStoreObject} from 'angular2/src/web_workers/shared/serializer';
|
||||
import {
|
||||
serializeMouseEvent,
|
||||
serializeKeyboardEvent,
|
||||
serializeGenericEvent,
|
||||
serializeEventWithTarget,
|
||||
serializeTransitionEvent
|
||||
} from 'angular2/src/web_workers/ui/event_serializer';
|
||||
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
export class EventDispatcher {
|
||||
constructor(private _sink: EventEmitter<any>, private _serializer: Serializer) {}
|
||||
|
||||
dispatchRenderEvent(element: any, eventTarget: string, eventName: string, event: any): boolean {
|
||||
var serializedEvent;
|
||||
// TODO (jteplitz602): support custom events #3350
|
||||
switch (event.type) {
|
||||
case "click":
|
||||
case "mouseup":
|
||||
case "mousedown":
|
||||
case "dblclick":
|
||||
case "contextmenu":
|
||||
case "mouseenter":
|
||||
case "mouseleave":
|
||||
case "mousemove":
|
||||
case "mouseout":
|
||||
case "mouseover":
|
||||
case "show":
|
||||
serializedEvent = serializeMouseEvent(event);
|
||||
break;
|
||||
case "keydown":
|
||||
case "keypress":
|
||||
case "keyup":
|
||||
serializedEvent = serializeKeyboardEvent(event);
|
||||
break;
|
||||
case "input":
|
||||
case "change":
|
||||
case "blur":
|
||||
serializedEvent = serializeEventWithTarget(event);
|
||||
break;
|
||||
case "abort":
|
||||
case "afterprint":
|
||||
case "beforeprint":
|
||||
case "cached":
|
||||
case "canplay":
|
||||
case "canplaythrough":
|
||||
case "chargingchange":
|
||||
case "chargingtimechange":
|
||||
case "close":
|
||||
case "dischargingtimechange":
|
||||
case "DOMContentLoaded":
|
||||
case "downloading":
|
||||
case "durationchange":
|
||||
case "emptied":
|
||||
case "ended":
|
||||
case "error":
|
||||
case "fullscreenchange":
|
||||
case "fullscreenerror":
|
||||
case "invalid":
|
||||
case "languagechange":
|
||||
case "levelfchange":
|
||||
case "loadeddata":
|
||||
case "loadedmetadata":
|
||||
case "obsolete":
|
||||
case "offline":
|
||||
case "online":
|
||||
case "open":
|
||||
case "orientatoinchange":
|
||||
case "pause":
|
||||
case "pointerlockchange":
|
||||
case "pointerlockerror":
|
||||
case "play":
|
||||
case "playing":
|
||||
case "ratechange":
|
||||
case "readystatechange":
|
||||
case "reset":
|
||||
case "scroll":
|
||||
case "seeked":
|
||||
case "seeking":
|
||||
case "stalled":
|
||||
case "submit":
|
||||
case "success":
|
||||
case "suspend":
|
||||
case "timeupdate":
|
||||
case "updateready":
|
||||
case "visibilitychange":
|
||||
case "volumechange":
|
||||
case "waiting":
|
||||
serializedEvent = serializeGenericEvent(event);
|
||||
break;
|
||||
case "transitionend":
|
||||
serializedEvent = serializeTransitionEvent(event);
|
||||
break;
|
||||
default:
|
||||
throw new BaseException(eventName + " not supported on WebWorkers");
|
||||
}
|
||||
ObservableWrapper.callEmit(this._sink, {
|
||||
"element": this._serializer.serialize(element, RenderStoreObject),
|
||||
"eventName": eventName,
|
||||
"eventTarget": eventTarget,
|
||||
"event": serializedEvent
|
||||
});
|
||||
|
||||
// TODO(kegluneq): Eventually, we want the user to indicate from the UI side whether the event
|
||||
// should be canceled, but for now just call `preventDefault` on the original DOM event.
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
library angular2.src.web_workers.event_serializer;
|
||||
|
||||
import 'dart:core';
|
||||
import 'dart:html';
|
||||
|
||||
// List of all elements with HTML value attribute.
|
||||
// Taken from: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes
|
||||
final Set<String> NODES_WITH_VALUE = new Set<String>.from([
|
||||
"input",
|
||||
"select",
|
||||
"option",
|
||||
"button",
|
||||
"li",
|
||||
"meter",
|
||||
"progress",
|
||||
"param",
|
||||
"textarea"
|
||||
]);
|
||||
|
||||
Map<String, dynamic> serializeGenericEvent(dynamic e) {
|
||||
var serialized = new Map<String, dynamic>();
|
||||
serialized['bubbles'] = e.bubbles;
|
||||
serialized['cancelable'] = e.cancelable;
|
||||
serialized['defaultPrevented'] = e.defaultPrevented;
|
||||
serialized['eventPhase'] = e.eventPhase;
|
||||
serialized['timeStamp'] = e.timeStamp;
|
||||
serialized['type'] = e.type;
|
||||
return serialized;
|
||||
}
|
||||
|
||||
// TODO(jteplitz602): Allow users to specify the properties they need rather than always
|
||||
// adding value #3374
|
||||
Map<String, dynamic> serializeEventWithTarget(dynamic e) {
|
||||
var serializedEvent = serializeGenericEvent(e);
|
||||
return addTarget(e, serializedEvent);
|
||||
}
|
||||
|
||||
Map<String, dynamic> serializeMouseEvent(dynamic e) {
|
||||
var serialized = new Map<String, dynamic>();
|
||||
serialized['altKey'] = e.altKey;
|
||||
serialized['bubbles'] = e.bubbles;
|
||||
serialized['button'] = e.button;
|
||||
serialized['cancelable'] = e.cancelable;
|
||||
serialized['client'] = serializePoint(e.client);
|
||||
serialized['ctrlKey'] = e.ctrlKey;
|
||||
serialized['defaultPrevented'] = e.defaultPrevented;
|
||||
serialized['detail'] = e.detail;
|
||||
serialized['eventPhase'] = e.eventPhase;
|
||||
serialized['layer'] = serializePoint(e.layer);
|
||||
serialized['metaKey'] = e.metaKey;
|
||||
serialized['offset'] = serializePoint(e.offset);
|
||||
serialized['page'] = serializePoint(e.page);
|
||||
serialized['region'] = e.region;
|
||||
serialized['screen'] = serializePoint(e.screen);
|
||||
serialized['shiftKey'] = e.shiftKey;
|
||||
serialized['timeStamp'] = e.timeStamp;
|
||||
serialized['type'] = e.type;
|
||||
return serialized;
|
||||
}
|
||||
|
||||
Map<String, dynamic> serializePoint(Point point) {
|
||||
var serialized = new Map<String, dynamic>();
|
||||
serialized['magnitude'] = point.magnitude;
|
||||
serialized['x'] = point.x;
|
||||
serialized['y'] = point.y;
|
||||
return serialized;
|
||||
}
|
||||
|
||||
Map<String, dynamic> serializeKeyboardEvent(dynamic e) {
|
||||
var serialized = new Map<String, dynamic>();
|
||||
serialized['altKey'] = e.altKey;
|
||||
serialized['bubbles'] = e.bubbles;
|
||||
serialized['cancelable'] = e.cancelable;
|
||||
serialized['charCode'] = e.charCode;
|
||||
serialized['ctrlKey'] = e.ctrlKey;
|
||||
serialized['defaultPrevented'] = e.defaultPrevented;
|
||||
serialized['detail'] = e.detail;
|
||||
serialized['eventPhase'] = e.eventPhase;
|
||||
serialized['keyCode'] = e.keyCode;
|
||||
serialized['keyLocation'] = e.keyLocation;
|
||||
serialized['location'] = e.location;
|
||||
serialized['repeat'] = e.repeat;
|
||||
serialized['shiftKey'] = e.shiftKey;
|
||||
serialized['timeStamp'] = e.timeStamp;
|
||||
serialized['type'] = e.type;
|
||||
//return addTarget(e, serialized);
|
||||
return serialized;
|
||||
}
|
||||
|
||||
Map<String, dynamic> serializeTransitionEvent(dynamic e) {
|
||||
var serialized = serializeGenericEvent(e);
|
||||
serialized['propertyName'] = e.propertyName;
|
||||
serialized['elapsedTime'] = e.elapsedTime;
|
||||
serialized['pseudoElement'] = e.pseudoElement;
|
||||
return addTarget(e, serialized);
|
||||
}
|
||||
|
||||
// TODO(jteplitz602): #3374. See above.
|
||||
Map<String, dynamic> addTarget(
|
||||
dynamic e, Map<String, dynamic> serializedEvent) {
|
||||
if (NODES_WITH_VALUE.contains(e.target.tagName.toLowerCase())) {
|
||||
serializedEvent['target'] = {'value': e.target.value};
|
||||
if (e.target is InputElement) {
|
||||
serializedEvent['target']['files'] = e.target.files;
|
||||
}
|
||||
}
|
||||
return serializedEvent;
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
import {Set} from 'angular2/src/facade/collection';
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
|
||||
const MOUSE_EVENT_PROPERTIES = [
|
||||
"altKey",
|
||||
"button",
|
||||
"clientX",
|
||||
"clientY",
|
||||
"metaKey",
|
||||
"movementX",
|
||||
"movementY",
|
||||
"offsetX",
|
||||
"offsetY",
|
||||
"region",
|
||||
"screenX",
|
||||
"screenY",
|
||||
"shiftKey"
|
||||
];
|
||||
|
||||
const KEYBOARD_EVENT_PROPERTIES = [
|
||||
'altkey',
|
||||
'charCode',
|
||||
'code',
|
||||
'ctrlKey',
|
||||
'isComposing',
|
||||
'key',
|
||||
'keyCode',
|
||||
'location',
|
||||
'metaKey',
|
||||
'repeat',
|
||||
'shiftKey',
|
||||
'which'
|
||||
];
|
||||
|
||||
const TRANSITION_EVENT_PROPERTIES = ['propertyName', 'elapsedTime', 'pseudoElement'];
|
||||
|
||||
const EVENT_PROPERTIES = ['type', 'bubbles', 'cancelable'];
|
||||
|
||||
const NODES_WITH_VALUE = new Set(
|
||||
["input", "select", "option", "button", "li", "meter", "progress", "param", "textarea"]);
|
||||
|
||||
export function serializeGenericEvent(e: Event): {[key: string]: any} {
|
||||
return serializeEvent(e, EVENT_PROPERTIES);
|
||||
}
|
||||
|
||||
// TODO(jteplitz602): Allow users to specify the properties they need rather than always
|
||||
// adding value and files #3374
|
||||
export function serializeEventWithTarget(e: Event): {[key: string]: any} {
|
||||
var serializedEvent = serializeEvent(e, EVENT_PROPERTIES);
|
||||
return addTarget(e, serializedEvent);
|
||||
}
|
||||
|
||||
export function serializeMouseEvent(e: MouseEvent): {[key: string]: any} {
|
||||
return serializeEvent(e, MOUSE_EVENT_PROPERTIES);
|
||||
}
|
||||
|
||||
export function serializeKeyboardEvent(e: KeyboardEvent): {[key: string]: any} {
|
||||
var serializedEvent = serializeEvent(e, KEYBOARD_EVENT_PROPERTIES);
|
||||
return addTarget(e, serializedEvent);
|
||||
}
|
||||
|
||||
export function serializeTransitionEvent(e: TransitionEvent): {[key: string]: any} {
|
||||
var serializedEvent = serializeEvent(e, TRANSITION_EVENT_PROPERTIES);
|
||||
return addTarget(e, serializedEvent);
|
||||
}
|
||||
|
||||
// TODO(jteplitz602): #3374. See above.
|
||||
function addTarget(e: Event, serializedEvent: {[key: string]: any}): {[key: string]: any} {
|
||||
if (NODES_WITH_VALUE.has((<HTMLElement>e.target).tagName.toLowerCase())) {
|
||||
var target = <HTMLInputElement>e.target;
|
||||
serializedEvent['target'] = {'value': target.value};
|
||||
if (isPresent(target.files)) {
|
||||
serializedEvent['target']['files'] = target.files;
|
||||
}
|
||||
}
|
||||
return serializedEvent;
|
||||
}
|
||||
|
||||
function serializeEvent(e: any, properties: string[]): {[key: string]: any} {
|
||||
var serialized = {};
|
||||
for (var i = 0; i < properties.length; i++) {
|
||||
var prop = properties[i];
|
||||
serialized[prop] = e[prop];
|
||||
}
|
||||
return serialized;
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
import {
|
||||
BrowserPlatformLocation
|
||||
} from 'angular2/src/platform/browser/location/browser_platform_location';
|
||||
import {UrlChangeListener} from 'angular2/platform/common';
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
import {ROUTER_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
|
||||
import {
|
||||
ServiceMessageBrokerFactory,
|
||||
ServiceMessageBroker
|
||||
} from 'angular2/src/web_workers/shared/service_message_broker';
|
||||
import {PRIMITIVE, Serializer} from 'angular2/src/web_workers/shared/serializer';
|
||||
import {bind} from './bind';
|
||||
import {LocationType} from 'angular2/src/web_workers/shared/serialized_types';
|
||||
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
|
||||
import {EventEmitter, ObservableWrapper, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
@Injectable()
|
||||
export class MessageBasedPlatformLocation {
|
||||
private _channelSink: EventEmitter<Object>;
|
||||
private _broker: ServiceMessageBroker;
|
||||
|
||||
constructor(private _brokerFactory: ServiceMessageBrokerFactory,
|
||||
private _platformLocation: BrowserPlatformLocation, bus: MessageBus,
|
||||
private _serializer: Serializer) {
|
||||
this._platformLocation.onPopState(<UrlChangeListener>bind(this._sendUrlChangeEvent, this));
|
||||
this._platformLocation.onHashChange(<UrlChangeListener>bind(this._sendUrlChangeEvent, this));
|
||||
this._broker = this._brokerFactory.createMessageBroker(ROUTER_CHANNEL);
|
||||
this._channelSink = bus.to(ROUTER_CHANNEL);
|
||||
}
|
||||
|
||||
start(): void {
|
||||
this._broker.registerMethod("getLocation", null, bind(this._getLocation, this), LocationType);
|
||||
this._broker.registerMethod("setPathname", [PRIMITIVE], bind(this._setPathname, this));
|
||||
this._broker.registerMethod("pushState", [PRIMITIVE, PRIMITIVE, PRIMITIVE],
|
||||
bind(this._platformLocation.pushState, this._platformLocation));
|
||||
this._broker.registerMethod("replaceState", [PRIMITIVE, PRIMITIVE, PRIMITIVE],
|
||||
bind(this._platformLocation.replaceState, this._platformLocation));
|
||||
this._broker.registerMethod("forward", null,
|
||||
bind(this._platformLocation.forward, this._platformLocation));
|
||||
this._broker.registerMethod("back", null,
|
||||
bind(this._platformLocation.back, this._platformLocation));
|
||||
}
|
||||
|
||||
private _getLocation(): Promise<Location> {
|
||||
return PromiseWrapper.resolve(this._platformLocation.location);
|
||||
}
|
||||
|
||||
|
||||
private _sendUrlChangeEvent(e: Event): void {
|
||||
let loc = this._serializer.serialize(this._platformLocation.location, LocationType);
|
||||
let serializedEvent = {'type': e.type};
|
||||
ObservableWrapper.callEmit(this._channelSink, {'event': serializedEvent, 'location': loc});
|
||||
}
|
||||
|
||||
private _setPathname(pathname: string): void { this._platformLocation.pathname = pathname; }
|
||||
}
|
174
modules/@angular/platform-browser/src/web_workers/ui/renderer.ts
Normal file
174
modules/@angular/platform-browser/src/web_workers/ui/renderer.ts
Normal file
@ -0,0 +1,174 @@
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
|
||||
import {Serializer, PRIMITIVE, RenderStoreObject} from 'angular2/src/web_workers/shared/serializer';
|
||||
import {RootRenderer, Renderer, RenderComponentType} from 'angular2/src/core/render/api';
|
||||
import {EVENT_CHANNEL, RENDERER_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
|
||||
import {Type} from 'angular2/src/facade/lang';
|
||||
import {bind} from './bind';
|
||||
import {EventDispatcher} from 'angular2/src/web_workers/ui/event_dispatcher';
|
||||
import {RenderStore} from 'angular2/src/web_workers/shared/render_store';
|
||||
import {ServiceMessageBrokerFactory} from 'angular2/src/web_workers/shared/service_message_broker';
|
||||
|
||||
@Injectable()
|
||||
export class MessageBasedRenderer {
|
||||
private _eventDispatcher: EventDispatcher;
|
||||
|
||||
constructor(private _brokerFactory: ServiceMessageBrokerFactory, private _bus: MessageBus,
|
||||
private _serializer: Serializer, private _renderStore: RenderStore,
|
||||
private _rootRenderer: RootRenderer) {}
|
||||
|
||||
start(): void {
|
||||
var broker = this._brokerFactory.createMessageBroker(RENDERER_CHANNEL);
|
||||
this._bus.initChannel(EVENT_CHANNEL);
|
||||
this._eventDispatcher = new EventDispatcher(this._bus.to(EVENT_CHANNEL), this._serializer);
|
||||
|
||||
broker.registerMethod("renderComponent", [RenderComponentType, PRIMITIVE],
|
||||
bind(this._renderComponent, this));
|
||||
|
||||
broker.registerMethod("selectRootElement", [RenderStoreObject, PRIMITIVE, PRIMITIVE],
|
||||
bind(this._selectRootElement, this));
|
||||
broker.registerMethod("createElement",
|
||||
[RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
|
||||
bind(this._createElement, this));
|
||||
broker.registerMethod("createViewRoot", [RenderStoreObject, RenderStoreObject, PRIMITIVE],
|
||||
bind(this._createViewRoot, this));
|
||||
broker.registerMethod("createTemplateAnchor", [RenderStoreObject, RenderStoreObject, PRIMITIVE],
|
||||
bind(this._createTemplateAnchor, this));
|
||||
broker.registerMethod("createText",
|
||||
[RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
|
||||
bind(this._createText, this));
|
||||
broker.registerMethod("projectNodes", [RenderStoreObject, RenderStoreObject, RenderStoreObject],
|
||||
bind(this._projectNodes, this));
|
||||
broker.registerMethod("attachViewAfter",
|
||||
[RenderStoreObject, RenderStoreObject, RenderStoreObject],
|
||||
bind(this._attachViewAfter, this));
|
||||
broker.registerMethod("detachView", [RenderStoreObject, RenderStoreObject],
|
||||
bind(this._detachView, this));
|
||||
broker.registerMethod("destroyView", [RenderStoreObject, RenderStoreObject, RenderStoreObject],
|
||||
bind(this._destroyView, this));
|
||||
broker.registerMethod("setElementProperty",
|
||||
[RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
|
||||
bind(this._setElementProperty, this));
|
||||
broker.registerMethod("setElementAttribute",
|
||||
[RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
|
||||
bind(this._setElementAttribute, this));
|
||||
broker.registerMethod("setBindingDebugInfo",
|
||||
[RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
|
||||
bind(this._setBindingDebugInfo, this));
|
||||
broker.registerMethod("setElementClass",
|
||||
[RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
|
||||
bind(this._setElementClass, this));
|
||||
broker.registerMethod("setElementStyle",
|
||||
[RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
|
||||
bind(this._setElementStyle, this));
|
||||
broker.registerMethod("invokeElementMethod",
|
||||
[RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
|
||||
bind(this._invokeElementMethod, this));
|
||||
broker.registerMethod("setText", [RenderStoreObject, RenderStoreObject, PRIMITIVE],
|
||||
bind(this._setText, this));
|
||||
broker.registerMethod("listen", [RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
|
||||
bind(this._listen, this));
|
||||
broker.registerMethod("listenGlobal", [RenderStoreObject, PRIMITIVE, PRIMITIVE, PRIMITIVE],
|
||||
bind(this._listenGlobal, this));
|
||||
broker.registerMethod("listenDone", [RenderStoreObject, RenderStoreObject],
|
||||
bind(this._listenDone, this));
|
||||
}
|
||||
|
||||
private _renderComponent(renderComponentType: RenderComponentType, rendererId: number) {
|
||||
var renderer = this._rootRenderer.renderComponent(renderComponentType);
|
||||
this._renderStore.store(renderer, rendererId);
|
||||
}
|
||||
|
||||
private _selectRootElement(renderer: Renderer, selector: string, elId: number) {
|
||||
this._renderStore.store(renderer.selectRootElement(selector, null), elId);
|
||||
}
|
||||
|
||||
private _createElement(renderer: Renderer, parentElement: any, name: string, elId: number) {
|
||||
this._renderStore.store(renderer.createElement(parentElement, name, null), elId);
|
||||
}
|
||||
|
||||
private _createViewRoot(renderer: Renderer, hostElement: any, elId: number) {
|
||||
var viewRoot = renderer.createViewRoot(hostElement);
|
||||
if (this._renderStore.serialize(hostElement) !== elId) {
|
||||
this._renderStore.store(viewRoot, elId);
|
||||
}
|
||||
}
|
||||
|
||||
private _createTemplateAnchor(renderer: Renderer, parentElement: any, elId: number) {
|
||||
this._renderStore.store(renderer.createTemplateAnchor(parentElement, null), elId);
|
||||
}
|
||||
|
||||
private _createText(renderer: Renderer, parentElement: any, value: string, elId: number) {
|
||||
this._renderStore.store(renderer.createText(parentElement, value, null), elId);
|
||||
}
|
||||
|
||||
private _projectNodes(renderer: Renderer, parentElement: any, nodes: any[]) {
|
||||
renderer.projectNodes(parentElement, nodes);
|
||||
}
|
||||
|
||||
private _attachViewAfter(renderer: Renderer, node: any, viewRootNodes: any[]) {
|
||||
renderer.attachViewAfter(node, viewRootNodes);
|
||||
}
|
||||
|
||||
private _detachView(renderer: Renderer, viewRootNodes: any[]) {
|
||||
renderer.detachView(viewRootNodes);
|
||||
}
|
||||
|
||||
private _destroyView(renderer: Renderer, hostElement: any, viewAllNodes: any[]) {
|
||||
renderer.destroyView(hostElement, viewAllNodes);
|
||||
for (var i = 0; i < viewAllNodes.length; i++) {
|
||||
this._renderStore.remove(viewAllNodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private _setElementProperty(renderer: Renderer, renderElement: any, propertyName: string,
|
||||
propertyValue: any) {
|
||||
renderer.setElementProperty(renderElement, propertyName, propertyValue);
|
||||
}
|
||||
|
||||
private _setElementAttribute(renderer: Renderer, renderElement: any, attributeName: string,
|
||||
attributeValue: string) {
|
||||
renderer.setElementAttribute(renderElement, attributeName, attributeValue);
|
||||
}
|
||||
|
||||
private _setBindingDebugInfo(renderer: Renderer, renderElement: any, propertyName: string,
|
||||
propertyValue: string) {
|
||||
renderer.setBindingDebugInfo(renderElement, propertyName, propertyValue);
|
||||
}
|
||||
|
||||
private _setElementClass(renderer: Renderer, renderElement: any, className: string,
|
||||
isAdd: boolean) {
|
||||
renderer.setElementClass(renderElement, className, isAdd);
|
||||
}
|
||||
|
||||
private _setElementStyle(renderer: Renderer, renderElement: any, styleName: string,
|
||||
styleValue: string) {
|
||||
renderer.setElementStyle(renderElement, styleName, styleValue);
|
||||
}
|
||||
|
||||
private _invokeElementMethod(renderer: Renderer, renderElement: any, methodName: string,
|
||||
args: any[]) {
|
||||
renderer.invokeElementMethod(renderElement, methodName, args);
|
||||
}
|
||||
|
||||
private _setText(renderer: Renderer, renderNode: any, text: string) {
|
||||
renderer.setText(renderNode, text);
|
||||
}
|
||||
|
||||
private _listen(renderer: Renderer, renderElement: any, eventName: string, unlistenId: number) {
|
||||
var unregisterCallback = renderer.listen(renderElement, eventName,
|
||||
(event) => this._eventDispatcher.dispatchRenderEvent(
|
||||
renderElement, null, eventName, event));
|
||||
this._renderStore.store(unregisterCallback, unlistenId);
|
||||
}
|
||||
|
||||
private _listenGlobal(renderer: Renderer, eventTarget: string, eventName: string,
|
||||
unlistenId: number) {
|
||||
var unregisterCallback = renderer.listenGlobal(
|
||||
eventTarget, eventName,
|
||||
(event) => this._eventDispatcher.dispatchRenderEvent(null, eventTarget, eventName, event));
|
||||
this._renderStore.store(unregisterCallback, unlistenId);
|
||||
}
|
||||
|
||||
private _listenDone(renderer: Renderer, unlistenCallback: Function) { unlistenCallback(); }
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
import {MessageBasedPlatformLocation} from './platform_location';
|
||||
import {
|
||||
BrowserPlatformLocation
|
||||
} from 'angular2/src/platform/browser/location/browser_platform_location';
|
||||
import {APP_INITIALIZER, Provider, Injector, NgZone} from 'angular2/core';
|
||||
|
||||
export const WORKER_RENDER_ROUTER = /*@ts2dart_const*/[
|
||||
MessageBasedPlatformLocation,
|
||||
BrowserPlatformLocation,
|
||||
/* @ts2dart_Provider */ {provide: APP_INITIALIZER, useFactory: initRouterListeners, multi: true, deps: [Injector]}
|
||||
];
|
||||
|
||||
function initRouterListeners(injector: Injector): () => void {
|
||||
return () => {
|
||||
let zone = injector.get(NgZone);
|
||||
|
||||
zone.runGuarded(() => injector.get(MessageBasedPlatformLocation).start());
|
||||
};
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
import {PRIMITIVE} from 'angular2/src/web_workers/shared/serializer';
|
||||
import {XHR_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
|
||||
import {XHR} from 'angular2/src/compiler/xhr';
|
||||
import {ServiceMessageBrokerFactory} from 'angular2/src/web_workers/shared/service_message_broker';
|
||||
import {bind} from './bind';
|
||||
|
||||
@Injectable()
|
||||
export class MessageBasedXHRImpl {
|
||||
constructor(private _brokerFactory: ServiceMessageBrokerFactory, private _xhr: XHR) {}
|
||||
|
||||
start(): void {
|
||||
var broker = this._brokerFactory.createMessageBroker(XHR_CHANNEL);
|
||||
broker.registerMethod("get", [PRIMITIVE], bind(this._xhr.get, this._xhr), PRIMITIVE);
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
library angular2.src.web_workers.worker.event_deserializer;
|
||||
|
||||
class GenericEvent {
|
||||
Map<String, dynamic> properties;
|
||||
EventTarget _target = null;
|
||||
|
||||
GenericEvent(this.properties);
|
||||
|
||||
bool get bubbles => properties['bubbles'];
|
||||
bool get cancelable => properties['cancelable'];
|
||||
bool get defaultPrevented => properties['defaultPrevented'];
|
||||
int get eventPhase => properties['eventPhase'];
|
||||
int get timeStamp => properties['timeStamp'];
|
||||
String get type => properties['type'];
|
||||
bool get altKey => properties['altKey'];
|
||||
|
||||
int get charCode => properties['charCode'];
|
||||
bool get ctrlKey => properties['ctrlKey'];
|
||||
int get detail => properties['detail'];
|
||||
int get keyCode => properties['keyCode'];
|
||||
int get keyLocation => properties['keyLocation'];
|
||||
Point get layer => _getPoint('layer');
|
||||
int get location => properties['location'];
|
||||
bool get repeat => properties['repeat'];
|
||||
bool get shiftKey => properties['shiftKey'];
|
||||
|
||||
int get button => properties['button'];
|
||||
Point get client => _getPoint('client');
|
||||
bool get metaKey => properties['metaKey'];
|
||||
Point get offset => _getPoint('offset');
|
||||
Point get page => _getPoint('page');
|
||||
Point get screen => _getPoint('screen');
|
||||
|
||||
String get propertyName => properties['propertyName'];
|
||||
num get elapsedTime => properties['elapsedTime'];
|
||||
String get pseudoElement => properties['pseudoElement'];
|
||||
|
||||
EventTarget get target {
|
||||
if (_target != null) {
|
||||
return _target;
|
||||
} else if (properties.containsKey("target")) {
|
||||
_target = new EventTarget(properties['target']);
|
||||
return _target;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
dynamic _getPoint(name) {
|
||||
Map<String, dynamic> point = properties[name];
|
||||
return new Point(point['x'], point['y'], point['magnitude']);
|
||||
}
|
||||
}
|
||||
|
||||
class EventTarget {
|
||||
dynamic value;
|
||||
|
||||
EventTarget(Map<String, dynamic> properties) {
|
||||
value = properties['value'];
|
||||
}
|
||||
}
|
||||
|
||||
class Point {
|
||||
int x;
|
||||
int y;
|
||||
double magnitude;
|
||||
|
||||
Point(this.x, this.y, this.magnitude);
|
||||
}
|
||||
|
||||
GenericEvent deserializeGenericEvent(Map<String, dynamic> serializedEvent) {
|
||||
return new GenericEvent(serializedEvent);
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
// no deserialization is necessary in TS.
|
||||
// This is only here to match dart interface
|
||||
export function deserializeGenericEvent(
|
||||
serializedEvent: {[key: string]: any}): {[key: string]: any} {
|
||||
return serializedEvent;
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
import {
|
||||
FnArg,
|
||||
UiArguments,
|
||||
ClientMessageBroker,
|
||||
ClientMessageBrokerFactory
|
||||
} from 'angular2/src/web_workers/shared/client_message_broker';
|
||||
import {PlatformLocation, UrlChangeEvent, UrlChangeListener} from 'angular2/platform/common';
|
||||
import {ROUTER_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
|
||||
import {LocationType} from 'angular2/src/web_workers/shared/serialized_types';
|
||||
import {PromiseWrapper, EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {PRIMITIVE, Serializer} from 'angular2/src/web_workers/shared/serializer';
|
||||
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
|
||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {StringWrapper} from 'angular2/src/facade/lang';
|
||||
import {deserializeGenericEvent} from './event_deserializer';
|
||||
|
||||
@Injectable()
|
||||
export class WebWorkerPlatformLocation extends PlatformLocation {
|
||||
private _broker: ClientMessageBroker;
|
||||
private _popStateListeners: Array<Function> = [];
|
||||
private _hashChangeListeners: Array<Function> = [];
|
||||
private _location: LocationType = null;
|
||||
private _channelSource: EventEmitter<Object>;
|
||||
|
||||
constructor(brokerFactory: ClientMessageBrokerFactory, bus: MessageBus,
|
||||
private _serializer: Serializer) {
|
||||
super();
|
||||
this._broker = brokerFactory.createMessageBroker(ROUTER_CHANNEL);
|
||||
|
||||
this._channelSource = bus.from(ROUTER_CHANNEL);
|
||||
ObservableWrapper.subscribe(this._channelSource, (msg: {[key: string]: any}) => {
|
||||
var listeners: Array<Function> = null;
|
||||
if (StringMapWrapper.contains(msg, 'event')) {
|
||||
let type: string = msg['event']['type'];
|
||||
if (StringWrapper.equals(type, "popstate")) {
|
||||
listeners = this._popStateListeners;
|
||||
} else if (StringWrapper.equals(type, "hashchange")) {
|
||||
listeners = this._hashChangeListeners;
|
||||
}
|
||||
|
||||
if (listeners !== null) {
|
||||
let e = deserializeGenericEvent(msg['event']);
|
||||
// There was a popState or hashChange event, so the location object thas been updated
|
||||
this._location = this._serializer.deserialize(msg['location'], LocationType);
|
||||
listeners.forEach((fn: Function) => fn(e));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** @internal **/
|
||||
init(): Promise<boolean> {
|
||||
var args: UiArguments = new UiArguments("getLocation");
|
||||
|
||||
var locationPromise: Promise<LocationType> = this._broker.runOnService(args, LocationType);
|
||||
return PromiseWrapper.then(locationPromise, (val: LocationType): boolean => {
|
||||
this._location = val;
|
||||
return true;
|
||||
}, (err): boolean => { throw new BaseException(err); });
|
||||
}
|
||||
|
||||
getBaseHrefFromDOM(): string {
|
||||
throw new BaseException(
|
||||
"Attempt to get base href from DOM from WebWorker. You must either provide a value for the APP_BASE_HREF token through DI or use the hash location strategy.");
|
||||
}
|
||||
|
||||
onPopState(fn: UrlChangeListener): void { this._popStateListeners.push(fn); }
|
||||
|
||||
onHashChange(fn: UrlChangeListener): void { this._hashChangeListeners.push(fn); }
|
||||
|
||||
get pathname(): string {
|
||||
if (this._location === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this._location.pathname;
|
||||
}
|
||||
|
||||
get search(): string {
|
||||
if (this._location === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this._location.search;
|
||||
}
|
||||
|
||||
get hash(): string {
|
||||
if (this._location === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this._location.hash;
|
||||
}
|
||||
|
||||
set pathname(newPath: string) {
|
||||
if (this._location === null) {
|
||||
throw new BaseException("Attempt to set pathname before value is obtained from UI");
|
||||
}
|
||||
|
||||
this._location.pathname = newPath;
|
||||
|
||||
var fnArgs = [new FnArg(newPath, PRIMITIVE)];
|
||||
var args = new UiArguments("setPathname", fnArgs);
|
||||
this._broker.runOnService(args, null);
|
||||
}
|
||||
|
||||
pushState(state: any, title: string, url: string): void {
|
||||
var fnArgs =
|
||||
[new FnArg(state, PRIMITIVE), new FnArg(title, PRIMITIVE), new FnArg(url, PRIMITIVE)];
|
||||
var args = new UiArguments("pushState", fnArgs);
|
||||
this._broker.runOnService(args, null);
|
||||
}
|
||||
|
||||
replaceState(state: any, title: string, url: string): void {
|
||||
var fnArgs =
|
||||
[new FnArg(state, PRIMITIVE), new FnArg(title, PRIMITIVE), new FnArg(url, PRIMITIVE)];
|
||||
var args = new UiArguments("replaceState", fnArgs);
|
||||
this._broker.runOnService(args, null);
|
||||
}
|
||||
|
||||
forward(): void {
|
||||
var args = new UiArguments("forward");
|
||||
this._broker.runOnService(args, null);
|
||||
}
|
||||
|
||||
back(): void {
|
||||
var args = new UiArguments("back");
|
||||
this._broker.runOnService(args, null);
|
||||
}
|
||||
}
|
@ -0,0 +1,279 @@
|
||||
import {
|
||||
Renderer,
|
||||
RootRenderer,
|
||||
RenderComponentType,
|
||||
RenderDebugInfo
|
||||
} from 'angular2/src/core/render/api';
|
||||
import {
|
||||
ClientMessageBroker,
|
||||
ClientMessageBrokerFactory,
|
||||
FnArg,
|
||||
UiArguments
|
||||
} from "angular2/src/web_workers/shared/client_message_broker";
|
||||
import {isPresent, isBlank, print} from "angular2/src/facade/lang";
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {Injectable} from "angular2/src/core/di";
|
||||
import {RenderStore} from 'angular2/src/web_workers/shared/render_store';
|
||||
import {RENDERER_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
|
||||
import {Serializer, RenderStoreObject} from 'angular2/src/web_workers/shared/serializer';
|
||||
import {EVENT_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
|
||||
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
|
||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
||||
import {deserializeGenericEvent} from './event_deserializer';
|
||||
|
||||
@Injectable()
|
||||
export class WebWorkerRootRenderer implements RootRenderer {
|
||||
private _messageBroker;
|
||||
public globalEvents: NamedEventEmitter = new NamedEventEmitter();
|
||||
private _componentRenderers: Map<string, WebWorkerRenderer> =
|
||||
new Map<string, WebWorkerRenderer>();
|
||||
|
||||
constructor(messageBrokerFactory: ClientMessageBrokerFactory, bus: MessageBus,
|
||||
private _serializer: Serializer, private _renderStore: RenderStore) {
|
||||
this._messageBroker = messageBrokerFactory.createMessageBroker(RENDERER_CHANNEL);
|
||||
bus.initChannel(EVENT_CHANNEL);
|
||||
var source = bus.from(EVENT_CHANNEL);
|
||||
ObservableWrapper.subscribe(source, (message) => this._dispatchEvent(message));
|
||||
}
|
||||
|
||||
private _dispatchEvent(message: {[key: string]: any}): void {
|
||||
var eventName = message['eventName'];
|
||||
var target = message['eventTarget'];
|
||||
var event = deserializeGenericEvent(message['event']);
|
||||
if (isPresent(target)) {
|
||||
this.globalEvents.dispatchEvent(eventNameWithTarget(target, eventName), event);
|
||||
} else {
|
||||
var element =
|
||||
<WebWorkerRenderNode>this._serializer.deserialize(message['element'], RenderStoreObject);
|
||||
element.events.dispatchEvent(eventName, event);
|
||||
}
|
||||
}
|
||||
|
||||
renderComponent(componentType: RenderComponentType): Renderer {
|
||||
var result = this._componentRenderers.get(componentType.id);
|
||||
if (isBlank(result)) {
|
||||
result = new WebWorkerRenderer(this, componentType);
|
||||
this._componentRenderers.set(componentType.id, result);
|
||||
var id = this._renderStore.allocateId();
|
||||
this._renderStore.store(result, id);
|
||||
this.runOnService('renderComponent', [
|
||||
new FnArg(componentType, RenderComponentType),
|
||||
new FnArg(result, RenderStoreObject),
|
||||
]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
runOnService(fnName: string, fnArgs: FnArg[]) {
|
||||
var args = new UiArguments(fnName, fnArgs);
|
||||
this._messageBroker.runOnService(args, null);
|
||||
}
|
||||
|
||||
allocateNode(): WebWorkerRenderNode {
|
||||
var result = new WebWorkerRenderNode();
|
||||
var id = this._renderStore.allocateId();
|
||||
this._renderStore.store(result, id);
|
||||
return result;
|
||||
}
|
||||
|
||||
allocateId(): number { return this._renderStore.allocateId(); }
|
||||
|
||||
destroyNodes(nodes: any[]) {
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
this._renderStore.remove(nodes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class WebWorkerRenderer implements Renderer, RenderStoreObject {
|
||||
constructor(private _rootRenderer: WebWorkerRootRenderer,
|
||||
private _componentType: RenderComponentType) {}
|
||||
|
||||
private _runOnService(fnName: string, fnArgs: FnArg[]) {
|
||||
var fnArgsWithRenderer = [new FnArg(this, RenderStoreObject)].concat(fnArgs);
|
||||
this._rootRenderer.runOnService(fnName, fnArgsWithRenderer);
|
||||
}
|
||||
|
||||
selectRootElement(selectorOrNode: string, debugInfo: RenderDebugInfo): any {
|
||||
var node = this._rootRenderer.allocateNode();
|
||||
this._runOnService('selectRootElement',
|
||||
[new FnArg(selectorOrNode, null), new FnArg(node, RenderStoreObject)]);
|
||||
return node;
|
||||
}
|
||||
|
||||
createElement(parentElement: any, name: string, debugInfo: RenderDebugInfo): any {
|
||||
var node = this._rootRenderer.allocateNode();
|
||||
this._runOnService('createElement', [
|
||||
new FnArg(parentElement, RenderStoreObject),
|
||||
new FnArg(name, null),
|
||||
new FnArg(node, RenderStoreObject)
|
||||
]);
|
||||
return node;
|
||||
}
|
||||
|
||||
createViewRoot(hostElement: any): any {
|
||||
var viewRoot = this._componentType.encapsulation === ViewEncapsulation.Native ?
|
||||
this._rootRenderer.allocateNode() :
|
||||
hostElement;
|
||||
this._runOnService(
|
||||
'createViewRoot',
|
||||
[new FnArg(hostElement, RenderStoreObject), new FnArg(viewRoot, RenderStoreObject)]);
|
||||
return viewRoot;
|
||||
}
|
||||
|
||||
createTemplateAnchor(parentElement: any, debugInfo: RenderDebugInfo): any {
|
||||
var node = this._rootRenderer.allocateNode();
|
||||
this._runOnService(
|
||||
'createTemplateAnchor',
|
||||
[new FnArg(parentElement, RenderStoreObject), new FnArg(node, RenderStoreObject)]);
|
||||
return node;
|
||||
}
|
||||
|
||||
createText(parentElement: any, value: string, debugInfo: RenderDebugInfo): any {
|
||||
var node = this._rootRenderer.allocateNode();
|
||||
this._runOnService('createText', [
|
||||
new FnArg(parentElement, RenderStoreObject),
|
||||
new FnArg(value, null),
|
||||
new FnArg(node, RenderStoreObject)
|
||||
]);
|
||||
return node;
|
||||
}
|
||||
|
||||
projectNodes(parentElement: any, nodes: any[]) {
|
||||
this._runOnService(
|
||||
'projectNodes',
|
||||
[new FnArg(parentElement, RenderStoreObject), new FnArg(nodes, RenderStoreObject)]);
|
||||
}
|
||||
|
||||
attachViewAfter(node: any, viewRootNodes: any[]) {
|
||||
this._runOnService(
|
||||
'attachViewAfter',
|
||||
[new FnArg(node, RenderStoreObject), new FnArg(viewRootNodes, RenderStoreObject)]);
|
||||
}
|
||||
|
||||
detachView(viewRootNodes: any[]) {
|
||||
this._runOnService('detachView', [new FnArg(viewRootNodes, RenderStoreObject)]);
|
||||
}
|
||||
|
||||
destroyView(hostElement: any, viewAllNodes: any[]) {
|
||||
this._runOnService(
|
||||
'destroyView',
|
||||
[new FnArg(hostElement, RenderStoreObject), new FnArg(viewAllNodes, RenderStoreObject)]);
|
||||
this._rootRenderer.destroyNodes(viewAllNodes);
|
||||
}
|
||||
|
||||
setElementProperty(renderElement: any, propertyName: string, propertyValue: any) {
|
||||
this._runOnService('setElementProperty', [
|
||||
new FnArg(renderElement, RenderStoreObject),
|
||||
new FnArg(propertyName, null),
|
||||
new FnArg(propertyValue, null)
|
||||
]);
|
||||
}
|
||||
|
||||
setElementAttribute(renderElement: any, attributeName: string, attributeValue: string) {
|
||||
this._runOnService('setElementAttribute', [
|
||||
new FnArg(renderElement, RenderStoreObject),
|
||||
new FnArg(attributeName, null),
|
||||
new FnArg(attributeValue, null)
|
||||
]);
|
||||
}
|
||||
|
||||
setBindingDebugInfo(renderElement: any, propertyName: string, propertyValue: string) {
|
||||
this._runOnService('setBindingDebugInfo', [
|
||||
new FnArg(renderElement, RenderStoreObject),
|
||||
new FnArg(propertyName, null),
|
||||
new FnArg(propertyValue, null)
|
||||
]);
|
||||
}
|
||||
|
||||
setElementClass(renderElement: any, className: string, isAdd: boolean) {
|
||||
this._runOnService('setElementClass', [
|
||||
new FnArg(renderElement, RenderStoreObject),
|
||||
new FnArg(className, null),
|
||||
new FnArg(isAdd, null)
|
||||
]);
|
||||
}
|
||||
|
||||
setElementStyle(renderElement: any, styleName: string, styleValue: string) {
|
||||
this._runOnService('setElementStyle', [
|
||||
new FnArg(renderElement, RenderStoreObject),
|
||||
new FnArg(styleName, null),
|
||||
new FnArg(styleValue, null)
|
||||
]);
|
||||
}
|
||||
|
||||
invokeElementMethod(renderElement: any, methodName: string, args: any[]) {
|
||||
this._runOnService('invokeElementMethod', [
|
||||
new FnArg(renderElement, RenderStoreObject),
|
||||
new FnArg(methodName, null),
|
||||
new FnArg(args, null)
|
||||
]);
|
||||
}
|
||||
|
||||
setText(renderNode: any, text: string) {
|
||||
this._runOnService('setText',
|
||||
[new FnArg(renderNode, RenderStoreObject), new FnArg(text, null)]);
|
||||
}
|
||||
|
||||
listen(renderElement: WebWorkerRenderNode, name: string, callback: Function): Function {
|
||||
renderElement.events.listen(name, callback);
|
||||
var unlistenCallbackId = this._rootRenderer.allocateId();
|
||||
this._runOnService('listen', [
|
||||
new FnArg(renderElement, RenderStoreObject),
|
||||
new FnArg(name, null),
|
||||
new FnArg(unlistenCallbackId, null)
|
||||
]);
|
||||
return () => {
|
||||
renderElement.events.unlisten(name, callback);
|
||||
this._runOnService('listenDone', [new FnArg(unlistenCallbackId, null)]);
|
||||
};
|
||||
}
|
||||
|
||||
listenGlobal(target: string, name: string, callback: Function): Function {
|
||||
this._rootRenderer.globalEvents.listen(eventNameWithTarget(target, name), callback);
|
||||
var unlistenCallbackId = this._rootRenderer.allocateId();
|
||||
this._runOnService(
|
||||
'listenGlobal',
|
||||
[new FnArg(target, null), new FnArg(name, null), new FnArg(unlistenCallbackId, null)]);
|
||||
return () => {
|
||||
this._rootRenderer.globalEvents.unlisten(eventNameWithTarget(target, name), callback);
|
||||
this._runOnService('listenDone', [new FnArg(unlistenCallbackId, null)]);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class NamedEventEmitter {
|
||||
private _listeners: Map<string, Function[]>;
|
||||
|
||||
private _getListeners(eventName: string): Function[] {
|
||||
if (isBlank(this._listeners)) {
|
||||
this._listeners = new Map<string, Function[]>();
|
||||
}
|
||||
var listeners = this._listeners.get(eventName);
|
||||
if (isBlank(listeners)) {
|
||||
listeners = [];
|
||||
this._listeners.set(eventName, listeners);
|
||||
}
|
||||
return listeners;
|
||||
}
|
||||
|
||||
listen(eventName: string, callback: Function) { this._getListeners(eventName).push(callback); }
|
||||
|
||||
unlisten(eventName: string, callback: Function) {
|
||||
ListWrapper.remove(this._getListeners(eventName), callback);
|
||||
}
|
||||
|
||||
dispatchEvent(eventName: string, event: any) {
|
||||
var listeners = this._getListeners(eventName);
|
||||
for (var i = 0; i < listeners.length; i++) {
|
||||
listeners[i](event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function eventNameWithTarget(target: string, eventName: string): string {
|
||||
return `${target}:${eventName}`;
|
||||
}
|
||||
|
||||
export class WebWorkerRenderNode { events: NamedEventEmitter = new NamedEventEmitter(); }
|
@ -0,0 +1,20 @@
|
||||
import {ApplicationRef, Provider, NgZone, APP_INITIALIZER} from 'angular2/core';
|
||||
import {PlatformLocation} from 'angular2/platform/common';
|
||||
import {WebWorkerPlatformLocation} from './platform_location';
|
||||
import {ROUTER_PROVIDERS_COMMON} from 'angular2/src/router/router_providers_common';
|
||||
|
||||
export var WORKER_APP_ROUTER = [
|
||||
ROUTER_PROVIDERS_COMMON,
|
||||
/* @ts2dart_Provider */ {provide: PlatformLocation, useClass: WebWorkerPlatformLocation},
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: (platformLocation: WebWorkerPlatformLocation, zone: NgZone) => () =>
|
||||
initRouter(platformLocation, zone),
|
||||
multi: true,
|
||||
deps: [PlatformLocation, NgZone]
|
||||
}
|
||||
];
|
||||
|
||||
function initRouter(platformLocation: WebWorkerPlatformLocation, zone: NgZone): Promise<boolean> {
|
||||
return zone.runGuarded(() => { return platformLocation.init(); });
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
import {XHR} from 'angular2/src/compiler/xhr';
|
||||
import {
|
||||
FnArg,
|
||||
UiArguments,
|
||||
ClientMessageBroker,
|
||||
ClientMessageBrokerFactory
|
||||
} from 'angular2/src/web_workers/shared/client_message_broker';
|
||||
import {XHR_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
|
||||
|
||||
/**
|
||||
* Implementation of compiler/xhr that relays XHR requests to the UI side where they are sent
|
||||
* and the result is proxied back to the worker
|
||||
*/
|
||||
@Injectable()
|
||||
export class WebWorkerXHRImpl extends XHR {
|
||||
private _messageBroker: ClientMessageBroker;
|
||||
|
||||
constructor(messageBrokerFactory: ClientMessageBrokerFactory) {
|
||||
super();
|
||||
this._messageBroker = messageBrokerFactory.createMessageBroker(XHR_CHANNEL);
|
||||
}
|
||||
|
||||
get(url: string): Promise<string> {
|
||||
var fnArgs: FnArg[] = [new FnArg(url, null)];
|
||||
var args: UiArguments = new UiArguments("get", fnArgs);
|
||||
return this._messageBroker.runOnService(args, String);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user