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});
|
||||
});
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user