From c5cb7009ca5d06830345cf0ce8982d2ff5ef7ee2 Mon Sep 17 00:00:00 2001 From: Jason Teplitz Date: Tue, 28 Jul 2015 16:38:55 -0700 Subject: [PATCH] feat(WebWorkers): Add WebWorker Todo Example. Add support for more DOM events. Fixed breakage caused by previous DI commit in WebWorker Todo example --- .../src/web-workers/shared/serializer.ts | 41 +- .../src/web-workers/ui/event_serializer.dart | 35 +- .../src/web-workers/ui/event_serializer.ts | 29 +- modules/angular2/src/web-workers/ui/impl.ts | 61 ++- .../angular2/src/web-workers/worker/broker.ts | 2 + .../worker/event_deserializer.dart | 69 +++ .../web-workers/worker/event_deserializer.ts | 8 + .../src/web-workers/worker/renderer.ts | 2 +- .../test/web-workers/worker/broker_spec.ts | 3 +- .../kitchen_sink/kitchen_sink_spec.dart | 5 + .../kitchen_sink_spec.ts} | 4 +- .../e2e_test/web_workers/todo/todo_spec.dart | 5 + .../e2e_test/web_workers/todo/todo_spec.ts | 20 + .../web_workers/web_workers_spec.dart | 5 - .../{ => kitchen_sink}/background_index.dart | 2 +- .../{ => kitchen_sink}/background_index.ts | 0 .../src/web_workers/kitchen_sink/index.dart | 10 + .../web_workers/{ => kitchen_sink}/index.html | 0 .../web_workers/{ => kitchen_sink}/index.ts | 0 .../{ => kitchen_sink}/index_common.ts | 2 +- .../src/web_workers/kitchen_sink/loader.js | 24 ++ .../web_workers/todo/background_index.dart | 13 + .../src/web_workers/todo/background_index.ts | 6 + .../examples/src/web_workers/todo/css/bg.png | Bin 0 -> 2126 bytes .../src/web_workers/todo/css/main.css | 399 ++++++++++++++++++ .../src/web_workers/{ => todo}/index.dart | 2 +- .../examples/src/web_workers/todo/index.html | 13 + .../examples/src/web_workers/todo/index.ts | 2 + .../src/web_workers/todo/index_common.ts | 60 +++ .../src/web_workers/{ => todo}/loader.js | 2 +- .../web_workers/todo/services/TodoStore.ts | 51 +++ .../examples/src/web_workers/todo/todo.html | 68 +++ tools/broccoli/trees/browser_tree.ts | 3 +- 33 files changed, 919 insertions(+), 27 deletions(-) create mode 100644 modules/angular2/src/web-workers/worker/event_deserializer.dart create mode 100644 modules/angular2/src/web-workers/worker/event_deserializer.ts create mode 100644 modules/examples/e2e_test/web_workers/kitchen_sink/kitchen_sink_spec.dart rename modules/examples/e2e_test/web_workers/{web_workers_spec.ts => kitchen_sink/kitchen_sink_spec.ts} (91%) create mode 100644 modules/examples/e2e_test/web_workers/todo/todo_spec.dart create mode 100644 modules/examples/e2e_test/web_workers/todo/todo_spec.ts delete mode 100644 modules/examples/e2e_test/web_workers/web_workers_spec.dart rename modules/examples/src/web_workers/{ => kitchen_sink}/background_index.dart (88%) rename modules/examples/src/web_workers/{ => kitchen_sink}/background_index.ts (100%) create mode 100644 modules/examples/src/web_workers/kitchen_sink/index.dart rename modules/examples/src/web_workers/{ => kitchen_sink}/index.html (100%) rename modules/examples/src/web_workers/{ => kitchen_sink}/index.ts (100%) rename modules/examples/src/web_workers/{ => kitchen_sink}/index_common.ts (98%) create mode 100644 modules/examples/src/web_workers/kitchen_sink/loader.js create mode 100644 modules/examples/src/web_workers/todo/background_index.dart create mode 100644 modules/examples/src/web_workers/todo/background_index.ts create mode 100644 modules/examples/src/web_workers/todo/css/bg.png create mode 100644 modules/examples/src/web_workers/todo/css/main.css rename modules/examples/src/web_workers/{ => todo}/index.dart (86%) create mode 100644 modules/examples/src/web_workers/todo/index.html create mode 100644 modules/examples/src/web_workers/todo/index.ts create mode 100644 modules/examples/src/web_workers/todo/index_common.ts rename modules/examples/src/web_workers/{ => todo}/loader.js (87%) create mode 100644 modules/examples/src/web_workers/todo/services/TodoStore.ts create mode 100644 modules/examples/src/web_workers/todo/todo.html diff --git a/modules/angular2/src/web-workers/shared/serializer.ts b/modules/angular2/src/web-workers/shared/serializer.ts index 62fd98b382..5767649f4f 100644 --- a/modules/angular2/src/web-workers/shared/serializer.ts +++ b/modules/angular2/src/web-workers/shared/serializer.ts @@ -28,7 +28,8 @@ import { RenderFragmentRef, RenderElementRef, ViewType, - ViewEncapsulation + ViewEncapsulation, + PropertyBindingType } from "angular2/src/render/api"; import {WorkerElementRef} from 'angular2/src/web-workers/shared/api'; import {AST, ASTWithSource} from 'angular2/src/change_detection/change_detection'; @@ -57,6 +58,13 @@ export class Serializer { viewEncapsulationMap[1] = ViewEncapsulation.NATIVE; viewEncapsulationMap[2] = ViewEncapsulation.NONE; this._enumRegistry.set(ViewEncapsulation, viewEncapsulationMap); + + var propertyBindingTypeMap = new Map(); + propertyBindingTypeMap[0] = PropertyBindingType.PROPERTY; + propertyBindingTypeMap[1] = PropertyBindingType.ATTRIBUTE; + propertyBindingTypeMap[2] = PropertyBindingType.CLASS; + propertyBindingTypeMap[3] = PropertyBindingType.STYLE; + this._enumRegistry.set(PropertyBindingType, propertyBindingTypeMap); } serialize(obj: any, type: Type): Object { @@ -93,6 +101,8 @@ export class Serializer { return this._renderViewStore.serializeRenderFragmentRef(obj); } else if (type == WorkerElementRef) { return this._serializeWorkerElementRef(obj); + } else if (type == ElementPropertyBinding) { + return this._serializeElementPropertyBinding(obj); } else if (type == EventBinding) { return this._serializeEventBinding(obj); } else { @@ -137,6 +147,8 @@ export class Serializer { return this._deserializeWorkerElementRef(map); } else if (type == EventBinding) { return this._deserializeEventBinding(map); + } else if (type == ElementPropertyBinding) { + return this._deserializeElementPropertyBinding(map); } else { throw new BaseException("No deserializer for " + type.toString()); } @@ -165,7 +177,7 @@ export class Serializer { if (isPresent(type)) { var map: Map = new Map(); StringMapWrapper.forEach(obj, - (key, val) => { map.set(key, this.deserialize(val, type, data)); }); + (val, key) => { map.set(key, this.deserialize(val, type, data)); }); return map; } else { return MapWrapper.createFromStringMap(obj); @@ -174,13 +186,29 @@ export class Serializer { allocateRenderViews(fragmentCount: number) { this._renderViewStore.allocate(fragmentCount); } + private _serializeElementPropertyBinding(binding: + ElementPropertyBinding): StringMap { + return { + 'type': serializeEnum(binding.type), + 'astWithSource': this.serialize(binding.astWithSource, ASTWithSource), + 'property': binding.property, + 'unit': binding.unit + }; + } + + private _deserializeElementPropertyBinding(map: StringMap): ElementPropertyBinding { + var type = deserializeEnum(map['type'], this._enumRegistry.get(PropertyBindingType)); + var ast = this.deserialize(map['astWithSource'], ASTWithSource, "binding"); + return new ElementPropertyBinding(type, ast, map['property'], map['unit']); + } + private _serializeEventBinding(binding: EventBinding): StringMap { return {'fullName': binding.fullName, 'source': this.serialize(binding.source, ASTWithSource)}; } private _deserializeEventBinding(map: StringMap): EventBinding { return new EventBinding(map['fullName'], - this.deserialize(map['source'], ASTWithSource, "binding")); + this.deserialize(map['source'], ASTWithSource, "action")); } private _serializeWorkerElementRef(elementRef: RenderElementRef): StringMap { @@ -223,15 +251,12 @@ export class Serializer { // TODO: make ASTs serializable var ast: AST; switch (data) { - case "interpolation": - ast = this._parser.parseInterpolation(obj['input'], obj['location']); + case "action": + ast = this._parser.parseAction(obj['input'], obj['location']); break; case "binding": ast = this._parser.parseBinding(obj['input'], obj['location']); break; - case "simpleBinding": - ast = this._parser.parseSimpleBinding(obj['input'], obj['location']); - break; case "interpolation": ast = this._parser.parseInterpolation(obj['input'], obj['location']); break; diff --git a/modules/angular2/src/web-workers/ui/event_serializer.dart b/modules/angular2/src/web-workers/ui/event_serializer.dart index 97b0708735..ef06212e64 100644 --- a/modules/angular2/src/web-workers/ui/event_serializer.dart +++ b/modules/angular2/src/web-workers/ui/event_serializer.dart @@ -52,12 +52,45 @@ final Map KEYBOARD_EVENT_PROPERTIES = { #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 NODES_WITH_VALUE = + new Set.from(["input", "select", "option", "button", "li", "meter", "progress", "param"]); + +Map 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 serializeEventWithValue(dynamic e) { + var serializedEvent = serializeEvent(e, EVENT_PROPERTIES); + return addValue(e, serializedEvent); +} Map serializeMouseEvent(dynamic e) { return serializeEvent(e, MOUSE_EVENT_PROPERTIES); } Map serializeKeyboardEvent(dynamic e) { - return serializeEvent(e, KEYBOARD_EVENT_PROPERTIES); + var serializedEvent = serializeEvent(e, KEYBOARD_EVENT_PROPERTIES); + return addValue(e, serializedEvent); +} + +// TODO(jteplitz602): #3374. See above. +Map addValue(dynamic e, Map serializedEvent) { + if (NODES_WITH_VALUE.contains(e.target.tagName.toLowerCase())){ + serializedEvent['target'] = {'value': e.target.value}; + } + return serializedEvent; } Map serializeEvent(dynamic e, Map PROPERTIES) { diff --git a/modules/angular2/src/web-workers/ui/event_serializer.ts b/modules/angular2/src/web-workers/ui/event_serializer.ts index e830706e76..5ee913e901 100644 --- a/modules/angular2/src/web-workers/ui/event_serializer.ts +++ b/modules/angular2/src/web-workers/ui/event_serializer.ts @@ -1,4 +1,4 @@ -import {StringMap} from 'angular2/src/facade/collection'; +import {StringMap, Set} from 'angular2/src/facade/collection'; const MOUSE_EVENT_PROPERTIES = [ "altKey", @@ -31,12 +31,37 @@ const KEYBOARD_EVENT_PROPERTIES = [ '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 { + return serializeEvent(e, EVENT_PROPERTIES); +} + +// TODO(jteplitz602): Allow users to specify the properties they need rather than always +// adding value #3374 +export function serializeEventWithValue(e: Event): StringMap { + var serializedEvent = serializeEvent(e, EVENT_PROPERTIES); + return addValue(e, serializedEvent); +} + export function serializeMouseEvent(e: MouseEvent): StringMap { return serializeEvent(e, MOUSE_EVENT_PROPERTIES); } export function serializeKeyboardEvent(e: KeyboardEvent): StringMap { - return serializeEvent(e, KEYBOARD_EVENT_PROPERTIES); + var serializedEvent = serializeEvent(e, KEYBOARD_EVENT_PROPERTIES); + return addValue(e, serializedEvent); +} + +// TODO(jteplitz602): #3374. See above. +function addValue(e: Event, serializedEvent: StringMap): StringMap { + if (NODES_WITH_VALUE.has((e.target).tagName.toLowerCase())) { + serializedEvent['target'] = {'value': (e.target).value}; + } + return serializedEvent; } function serializeEvent(e: any, properties: List): StringMap { diff --git a/modules/angular2/src/web-workers/ui/impl.ts b/modules/angular2/src/web-workers/ui/impl.ts index fe61be2e03..bd08337028 100644 --- a/modules/angular2/src/web-workers/ui/impl.ts +++ b/modules/angular2/src/web-workers/ui/impl.ts @@ -35,7 +35,9 @@ import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter'; import {DOM} from 'angular2/src/dom/dom_adapter'; import { serializeMouseEvent, - serializeKeyboardEvent + serializeKeyboardEvent, + serializeGenericEvent, + serializeEventWithValue } from 'angular2/src/web-workers/ui/event_serializer'; /** @@ -236,7 +238,8 @@ class EventDispatcher implements RenderEventDispatcher { dispatchRenderEvent(elementIndex: number, eventName: string, locals: Map) { var e = locals.get('$event'); var serializedEvent; - switch (eventName) { + // TODO (jteplitz602): support custom events #3350 + switch (e.type) { case "click": case "mouseup": case "mousedown": @@ -255,6 +258,60 @@ class EventDispatcher implements RenderEventDispatcher { case "keyup": serializedEvent = serializeKeyboardEvent(e); break; + case "input": + case "change": + case "blur": + serializedEvent = serializeEventWithValue(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"); } diff --git a/modules/angular2/src/web-workers/worker/broker.ts b/modules/angular2/src/web-workers/worker/broker.ts index 098b60e216..2946d36b85 100644 --- a/modules/angular2/src/web-workers/worker/broker.ts +++ b/modules/angular2/src/web-workers/worker/broker.ts @@ -8,6 +8,7 @@ import {Injectable} from "angular2/di"; import {Type} from "angular2/src/facade/lang"; import {RenderViewRef, RenderEventDispatcher} from 'angular2/src/render/api'; import {NgZone} from 'angular2/src/core/zone/ng_zone'; +import {deserializeGenericEvent} from './event_deserializer'; @Injectable() export class MessageBroker { @@ -96,6 +97,7 @@ export class MessageBroker { private _dispatchEvent(eventData: RenderEventData): void { var dispatcher = this._eventDispatchRegistry.get(eventData.viewRef); this._zone.run(() => { + eventData.locals['$event'] = deserializeGenericEvent(eventData.locals['$event']); dispatcher.dispatchRenderEvent(eventData.elementIndex, eventData.eventName, eventData.locals); }); } diff --git a/modules/angular2/src/web-workers/worker/event_deserializer.dart b/modules/angular2/src/web-workers/worker/event_deserializer.dart new file mode 100644 index 0000000000..170179437b --- /dev/null +++ b/modules/angular2/src/web-workers/worker/event_deserializer.dart @@ -0,0 +1,69 @@ +library angular2.src.web_workers.worker.event_deserializer; + +class GenericEvent { + Map properties; + EventTarget _target = null; + + GenericEvent(this.properties); + + bool get bubbles => properties['bubbles']; + bool get cancelable => properties['cancelable']; + bool get defaultPrevented => properties['defaultPrevented']; + int get eventPhase => properties['eventPhase']; + int get timeStamp => properties['timeStamp']; + String get type => properties['type']; + bool get altKey => properties['altKey']; + + int get charCode => properties['charCode']; + bool get ctrlKey => properties['ctrlKey']; + int get detail => properties['detail']; + int get keyCode => properties['keyCode']; + int get keyLocation => properties['keyLocation']; + Point get layer => _getPoint('layer'); + int get location => properties['location']; + bool get repeat => properties['repeat']; + bool get shiftKey => properties['shiftKey']; + + int get button => properties['button']; + Point get client => _getPoint('client'); + bool get metaKey => properties['metaKey']; + Point get offset => _getPoint('offset'); + Point get page => _getPoint('page'); + Point get screen => _getPoint('screen'); + + EventTarget get target{ + if (_target != null){ + return _target; + } else if (properties.containsKey("target")){ + _target = new EventTarget(properties['target']); + return _target; + } else { + return null; + } + } + + dynamic _getPoint(name) { + Map point = properties[name]; + return new Point(point['x'], point['y'], point['magnitude']); + } +} + +class EventTarget { + dynamic value; + + EventTarget(Map properties) { + value = properties['value']; + } +} + +class Point { + int x; + int y; + double magnitude; + + Point(this.x, this.y, this.magnitude); +} + +GenericEvent deserializeGenericEvent(Map serializedEvent) { + return new GenericEvent(serializedEvent); +} diff --git a/modules/angular2/src/web-workers/worker/event_deserializer.ts b/modules/angular2/src/web-workers/worker/event_deserializer.ts new file mode 100644 index 0000000000..3db097918a --- /dev/null +++ b/modules/angular2/src/web-workers/worker/event_deserializer.ts @@ -0,0 +1,8 @@ +import {StringMap} from "angular2/src/facade/collection"; + +// no deserialization is necessary in TS. +// This is only here to match dart interface +export function deserializeGenericEvent(serializedEvent: StringMap): + StringMap { + return serializedEvent; +} diff --git a/modules/angular2/src/web-workers/worker/renderer.ts b/modules/angular2/src/web-workers/worker/renderer.ts index 93898bc2ff..8ced7e802e 100644 --- a/modules/angular2/src/web-workers/worker/renderer.ts +++ b/modules/angular2/src/web-workers/worker/renderer.ts @@ -171,7 +171,7 @@ export class WorkerRenderer implements Renderer { */ dehydrateView(viewRef: RenderViewRef) { var fnArgs = [new FnArg(viewRef, RenderViewRef)]; - var args = new UiArguments("renderer", "deyhdrateView", fnArgs); + var args = new UiArguments("renderer", "dehydrateView", fnArgs); this._messageBroker.runOnUiThread(args, null); } diff --git a/modules/angular2/test/web-workers/worker/broker_spec.ts b/modules/angular2/test/web-workers/worker/broker_spec.ts index 54610d230e..9b68ebd5c3 100644 --- a/modules/angular2/test/web-workers/worker/broker_spec.ts +++ b/modules/angular2/test/web-workers/worker/broker_spec.ts @@ -53,7 +53,8 @@ export function main() { 'value': { 'viewRef': viewRef.serialize(), 'elementIndex': elementIndex, - 'eventName': eventName + 'eventName': eventName, + 'locals': {'$event': {'target': {value: null}}} } } }); diff --git a/modules/examples/e2e_test/web_workers/kitchen_sink/kitchen_sink_spec.dart b/modules/examples/e2e_test/web_workers/kitchen_sink/kitchen_sink_spec.dart new file mode 100644 index 0000000000..e64cb5819f --- /dev/null +++ b/modules/examples/e2e_test/web_workers/kitchen_sink/kitchen_sink_spec.dart @@ -0,0 +1,5 @@ +library examples.e2e_test.web_workers.kitchen_sink_spec; + +main() { + +} diff --git a/modules/examples/e2e_test/web_workers/web_workers_spec.ts b/modules/examples/e2e_test/web_workers/kitchen_sink/kitchen_sink_spec.ts similarity index 91% rename from modules/examples/e2e_test/web_workers/web_workers_spec.ts rename to modules/examples/e2e_test/web_workers/kitchen_sink/kitchen_sink_spec.ts index 7732baa745..b1ef9d41e2 100644 --- a/modules/examples/e2e_test/web_workers/web_workers_spec.ts +++ b/modules/examples/e2e_test/web_workers/kitchen_sink/kitchen_sink_spec.ts @@ -1,10 +1,10 @@ import {verifyNoBrowserErrors} from 'angular2/src/test_lib/e2e_util'; import {Promise} from 'angular2/src/facade/async'; -describe('WebWorkers', function() { +describe('WebWorkers Kitchen Sink', function() { afterEach(verifyNoBrowserErrors); var selector = "hello-app .greeting"; - var URL = "examples/src/web_workers/index.html"; + var URL = "examples/src/web_workers/kitchen_sink/index.html"; it('should greet', () => { browser.get(URL); diff --git a/modules/examples/e2e_test/web_workers/todo/todo_spec.dart b/modules/examples/e2e_test/web_workers/todo/todo_spec.dart new file mode 100644 index 0000000000..ef47ade78b --- /dev/null +++ b/modules/examples/e2e_test/web_workers/todo/todo_spec.dart @@ -0,0 +1,5 @@ +library examples.e2e_test.web_workers.todo_spec; + +main() { + +} diff --git a/modules/examples/e2e_test/web_workers/todo/todo_spec.ts b/modules/examples/e2e_test/web_workers/todo/todo_spec.ts new file mode 100644 index 0000000000..0cb839b089 --- /dev/null +++ b/modules/examples/e2e_test/web_workers/todo/todo_spec.ts @@ -0,0 +1,20 @@ +import {verifyNoBrowserErrors} from 'angular2/src/test_lib/e2e_util'; +import {Promise} from 'angular2/src/facade/async'; + +describe('WebWorkers Todo', function() { + afterEach(verifyNoBrowserErrors); + + var URL = "examples/src/web_workers/todo/index.html"; + + it('should bootstrap', () => { + browser.get(URL); + + waitForBootstrap(); + expect(element(by.css("#todoapp header")).getText()).toEqual("todos"); + }); + +}); + +function waitForBootstrap(): void { + browser.wait(protractor.until.elementLocated(by.css("todo-app #todoapp")), 5000); +} diff --git a/modules/examples/e2e_test/web_workers/web_workers_spec.dart b/modules/examples/e2e_test/web_workers/web_workers_spec.dart deleted file mode 100644 index 8873540d4e..0000000000 --- a/modules/examples/e2e_test/web_workers/web_workers_spec.dart +++ /dev/null @@ -1,5 +0,0 @@ -library examples.e2e_test.web_workers.web_workers_spec; - -main() { - -} diff --git a/modules/examples/src/web_workers/background_index.dart b/modules/examples/src/web_workers/kitchen_sink/background_index.dart similarity index 88% rename from modules/examples/src/web_workers/background_index.dart rename to modules/examples/src/web_workers/kitchen_sink/background_index.dart index 95d9ad5a49..0efb21fe26 100644 --- a/modules/examples/src/web_workers/background_index.dart +++ b/modules/examples/src/web_workers/kitchen_sink/background_index.dart @@ -1,4 +1,4 @@ -library examples.src.web_workers.index; +library examples.src.web_workers.kitchen_sink.background_index; import "index_common.dart" show HelloCmp; import "dart:isolate"; diff --git a/modules/examples/src/web_workers/background_index.ts b/modules/examples/src/web_workers/kitchen_sink/background_index.ts similarity index 100% rename from modules/examples/src/web_workers/background_index.ts rename to modules/examples/src/web_workers/kitchen_sink/background_index.ts diff --git a/modules/examples/src/web_workers/kitchen_sink/index.dart b/modules/examples/src/web_workers/kitchen_sink/index.dart new file mode 100644 index 0000000000..5e91566986 --- /dev/null +++ b/modules/examples/src/web_workers/kitchen_sink/index.dart @@ -0,0 +1,10 @@ +library angular2.examples.web_workers.kitchen_sink.index; + +import "package:angular2/src/web-workers/ui/application.dart" show bootstrap; +import "package:angular2/src/reflection/reflection_capabilities.dart"; +import "package:angular2/src/reflection/reflection.dart"; + +main() { + reflector.reflectionCapabilities = new ReflectionCapabilities(); + bootstrap("background_index.dart"); +} diff --git a/modules/examples/src/web_workers/index.html b/modules/examples/src/web_workers/kitchen_sink/index.html similarity index 100% rename from modules/examples/src/web_workers/index.html rename to modules/examples/src/web_workers/kitchen_sink/index.html diff --git a/modules/examples/src/web_workers/index.ts b/modules/examples/src/web_workers/kitchen_sink/index.ts similarity index 100% rename from modules/examples/src/web_workers/index.ts rename to modules/examples/src/web_workers/kitchen_sink/index.ts diff --git a/modules/examples/src/web_workers/index_common.ts b/modules/examples/src/web_workers/kitchen_sink/index_common.ts similarity index 98% rename from modules/examples/src/web_workers/index_common.ts rename to modules/examples/src/web_workers/kitchen_sink/index_common.ts index fa5bee41f1..aa0ff95adc 100644 --- a/modules/examples/src/web_workers/index_common.ts +++ b/modules/examples/src/web_workers/kitchen_sink/index_common.ts @@ -54,5 +54,5 @@ export class HelloCmp { changeGreeting(): void { this.greeting = 'howdy'; } - onKeyDown(event): void { this.lastKey = StringWrapper.fromCharCode(event['keyCode']); } + onKeyDown(event): void { this.lastKey = StringWrapper.fromCharCode(event.keyCode); } } diff --git a/modules/examples/src/web_workers/kitchen_sink/loader.js b/modules/examples/src/web_workers/kitchen_sink/loader.js new file mode 100644 index 0000000000..09eabf9538 --- /dev/null +++ b/modules/examples/src/web_workers/kitchen_sink/loader.js @@ -0,0 +1,24 @@ +$SCRIPTS$ window = { + setTimeout: setTimeout, + Map: Map, + Set: Set, + Array: Array, + Reflect: Reflect, + RegExp: RegExp, + Promise: Promise, + Date: Date, + zone: zone +}; +assert = function() {}; + +System.import("examples/src/web_workers/kitchen_sink/background_index") + .then( + function(m) { + console.log("running main"); + try { + m.main(); + } catch (e) { + console.error(e); + } + }, + function(error) { console.error("error loading background", error); }); diff --git a/modules/examples/src/web_workers/todo/background_index.dart b/modules/examples/src/web_workers/todo/background_index.dart new file mode 100644 index 0000000000..6a4814d1db --- /dev/null +++ b/modules/examples/src/web_workers/todo/background_index.dart @@ -0,0 +1,13 @@ +library examples.src.web_workers.todo.background_index; + +import "index_common.dart" show TodoApp; +import "dart:isolate"; +import "package:angular2/src/web-workers/worker/application.dart" + show bootstrapWebworker; +import "package:angular2/src/reflection/reflection_capabilities.dart"; +import "package:angular2/src/reflection/reflection.dart"; + +main(List args, SendPort replyTo) { + reflector.reflectionCapabilities = new ReflectionCapabilities(); + bootstrapWebworker(replyTo, TodoApp).catchError((error) => throw error); +} diff --git a/modules/examples/src/web_workers/todo/background_index.ts b/modules/examples/src/web_workers/todo/background_index.ts new file mode 100644 index 0000000000..7237799977 --- /dev/null +++ b/modules/examples/src/web_workers/todo/background_index.ts @@ -0,0 +1,6 @@ +import {bootstrapWebworker} from "angular2/src/web-workers/worker/application"; +import {TodoApp} from "./index_common"; + +export function main() { + bootstrapWebworker(TodoApp); +} diff --git a/modules/examples/src/web_workers/todo/css/bg.png b/modules/examples/src/web_workers/todo/css/bg.png new file mode 100644 index 0000000000000000000000000000000000000000..b2a7600825ee11f21849ed475499c7d1ecbcc870 GIT binary patch literal 2126 zcmV-U2(kBxP)+9y`=HK7nt=~3t000O5Nklm=04hVa_lXl_bm=+M;=eI<%$rO2ta`suh*Sr#POw*?adq!O!CV z*0&b)rkCCEV)1nIuiLBntUH-MJI;&qYgz|d@Nhnn_abi2g`pu4NAMVid3hS%?quz~ zjlJf(x8cj{VS zUVEP1msg9`^OFUhSO5rtXHhRlyU2i>6$BT=glG`{JlctGHrwRIm)6Buu65jLQn^H8 zvl9lZqUBA+%qvM5XC+yY@mfA(YB~XP=e|6<{%$^eU8nrK?v2ccb@Jom5CG>rKAL7J zplHEIavVp|0p1&m!G`Ti?<7iZ;}@G7#Z)}Km%2!FHnQ0*&?PLR)G!HAaHgU#c|$dz zpj#0HmeadIe>`#)=Jjkb{B=FKnU3pc-&Y@j_j6(h4Y~+Uq!^J=LHunx1xi4QXK-Y{&MoT8ky6Vb9c|WhnOwF~c=3zOy8agktliS6# z`#8F`9H)D=bmk9B5MnW&_r#)f$c+;$LSr-@^An8dhc~Iquuv>jOK7pw7LJ;&X0i1C zGMsHdP1Os@ny$$j!>XAii%7bp5k>`pyNA!~epb)()p9zR4Yl6z=U}{CIdh1z(FpAo zQIW!;0zpCyC4*7YLkZCO@cul0R_&zghsA8Ek)z%jNpKa92{@NH+SstX%@}xB*Jk!l}PZ1cClIns~}5^!RncUk*rmA z%SIVgt58EQxLJ+OiFqKkBuwquyZLUp|gr zPUbUbFbBrPd1xO`^C*r-i3p9*F#(OBh!4Wy@aC&*!|O|GXcjDA&YhF{;cD7*o(GS^ z@pQSE7)x_81=Qyje6*LQ#TXu-7(o;S8u3tpDA0_ddj%hmCspeXdhYR}-i9A=C`EoChUYuH~^x!9+|&(Pgb*>Ck<=9j|)*@xyfT zpP#+Kt<}39b|3Wl4fxT(+G?aH>gG^d@MEaUOJRfy55TtFI7^Y)VuMU=7Pp0y55jS~ z+TJ)igMyrqkX;wU8j68iIDtqJYhS_D3_Oem-@g6me_RfiQ}uT_K-A-9qG%}gk3E9c z!8`KgsMNXm)beloPfMql*|&$M>1q`i!cY)RFYnjNYu*gSv@8XebV7%}xYL>6)GJPJ zJpA7K31lcJuIFKSw-{x4FX0K2Az)~Xg<`sOu$4|^-(^XJX4YzoiNRvL zyuY7~26w-P^Zw%6F}shBwV2$02c8RsSUy6dM92diCAxiX3IUqsq5ig6>U_!Vy*q5$ zjm}16tJr+QZ&T?HkkpeIwVX-r1EI=@%Zlt;6g*mj&E!A))%gXJ=x6u#l zcKEP>bx4rqV*k`BBrZ$Mqjt{TsHcxUH>;)iAK}B(Kila&VD%b?6m&^0WK4^&EZNAI z8AMYs_%$D%|M+@+cQqL-hi4yG>h*jwdii!W1{}p>ZhwFYt_4Su1kjY9<-5`!$VNOI1y(EDSH4WpCijE_>al!Nt=^eVER#uJ1=b z;BWF2IgyF5LexV+yec$Kin;Ai@myo;G>zJ&jrdW#ecXhIZph5OYqw_PEfcF56Z5Zu_ZZE#q3Mc+N&1O^7hoh9QR7`+L$cBP5pqG=fqA0uh zUBukaxTFmH)<|Xbvp2c=HSbNkpXw73a5lv7V$jb92#yZ141@$X?F}Jt8gIU7Wm6m9 z?e_;;zsnm6`LZeP>T=$)Kr>Z?kr*UmFqR7zx0C6^bmcsc@1AGtw_rNH>-Xm$d*|Q< zn&1Ln0u7=l&ILs>%CkJp`DiG9F18x4Ne+lg<#i?e7jL%x;4ZnRkN^Mx07*qoM6N<$ Ef(>0N!2kdN literal 0 HcmV?d00001 diff --git a/modules/examples/src/web_workers/todo/css/main.css b/modules/examples/src/web_workers/todo/css/main.css new file mode 100644 index 0000000000..a87b28d934 --- /dev/null +++ b/modules/examples/src/web_workers/todo/css/main.css @@ -0,0 +1,399 @@ +@charset "utf-8"; + +html, +body { + margin: 0; + padding: 0; +} + +body { + font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; + line-height: 1.4em; + background: #eaeaea; + color: #4d4d4d; + width: 550px; + margin: 0 auto; + -webkit-font-smoothing: antialiased; + -moz-font-smoothing: antialiased; + -ms-font-smoothing: antialiased; + -o-font-smoothing: antialiased; + font-smoothing: antialiased; +} + +button { + margin: 0; + padding: 0; + border: 0; + background: none; + font-size: 100%; + vertical-align: baseline; + font-family: inherit; + font-weight: inherit; + color: inherit; + -webkit-appearance: none; + -ms-appearance: none; + appearance: none; + -webkit-font-smoothing: antialiased; + -moz-font-smoothing: antialiased; + -ms-font-smoothing: antialiased; + font-smoothing: antialiased; +} + +button, +input[type="checkbox"] { + outline: none; +} + +.hidden { + display: none; +} + +.visible { + display: block !important; +} + +#todoapp { + background: #fff; + margin: 130px 0 40px 0; + position: relative; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), + 0 25px 50px 0 rgba(0, 0, 0, 0.1); +} + +#todoapp input::-webkit-input-placeholder { + font-style: italic; + font-weight: 300; + color: #e6e6e6; +} + +#todoapp input::-moz-placeholder { + font-style: italic; + font-weight: 300; + color: #e6e6e6; +} + +#todoapp input::input-placeholder { + font-style: italic; + font-weight: 300; + color: #e6e6e6; +} + +#todoapp h1 { + position: absolute; + top: -155px; + width: 100%; + font-size: 100px; + font-weight: 100; + text-align: center; + color: rgba(175, 47, 47, 0.15); + -webkit-text-rendering: optimizeLegibility; + -moz-text-rendering: optimizeLegibility; + -ms-text-rendering: optimizeLegibility; + text-rendering: optimizeLegibility; +} + +#new-todo, +.edit { + position: relative; + margin: 0; + width: 100%; + font-size: 24px; + font-family: inherit; + font-weight: inherit; + line-height: 1.4em; + border: 0; + outline: none; + color: inherit; + padding: 6px; + border: 1px solid #999; + box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); + -ms-box-sizing: border-box; + box-sizing: border-box; + -webkit-font-smoothing: antialiased; + -moz-font-smoothing: antialiased; + -ms-font-smoothing: antialiased; + font-smoothing: antialiased; +} + +#new-todo { + padding: 16px 16px 16px 60px; + border: none; + background: rgba(0, 0, 0, 0.003); + box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); +} + +#main { + position: relative; + z-index: 2; + border-top: 1px solid #e6e6e6; +} + +label[for='toggle-all'] { + display: none; +} + +#toggle-all { + position: absolute; + top: -55px; + left: -12px; + width: 60px; + height: 34px; + text-align: center; + border: none; /* Mobile Safari */ +} + +#toggle-all:before { + content: '❯'; + font-size: 22px; + color: #e6e6e6; + padding: 10px 27px 10px 27px; +} + +#toggle-all:checked:before { + color: #737373; +} + +#todo-list { + margin: 0; + padding: 0; + list-style: none; +} + +#todo-list li { + position: relative; + font-size: 24px; + border-bottom: 1px solid #ededed; +} + +#todo-list li:last-child { + border-bottom: none; +} + +#todo-list li.editing { + border-bottom: none; + padding: 0; +} + +#todo-list li.editing .edit { + display: block; + width: 506px; + padding: 13px 17px 12px 17px; + margin: 0 0 0 43px; +} + +#todo-list li.editing .view { + display: none; +} + +#todo-list li .toggle { + text-align: center; + width: 40px; + /* auto, since non-WebKit browsers doesn't support input styling */ + height: auto; + position: absolute; + top: 0; + bottom: 0; + margin: auto 0; + border: none; /* Mobile Safari */ + -webkit-appearance: none; + -ms-appearance: none; + appearance: none; +} + +#todo-list li .toggle:after { + content: url('data:image/svg+xml;charset=utf8,'); +} + +#todo-list li .toggle:checked:after { + content: url('data:image/svg+xml;charset=utf8,'); +} + +#todo-list li label { + white-space: pre; + word-break: break-word; + padding: 15px 60px 15px 15px; + margin-left: 45px; + display: block; + line-height: 1.2; + transition: color 0.4s; +} + +#todo-list li.completed label { + color: #d9d9d9; + text-decoration: line-through; +} + +#todo-list li .destroy { + display: none; + position: absolute; + top: 0; + right: 10px; + bottom: 0; + width: 40px; + height: 40px; + margin: auto 0; + font-size: 30px; + color: #cc9a9a; + margin-bottom: 11px; + transition: color 0.2s ease-out; +} + +#todo-list li .destroy:hover { + color: #af5b5e; +} + +#todo-list li .destroy:after { + content: '×'; +} + +#todo-list li:hover .destroy { + display: block; +} + +#todo-list li .edit { + display: none; +} + +#todo-list li.editing:last-child { + margin-bottom: -1px; +} + +#footer { + color: #777; + padding: 10px 15px; + height: 20px; + text-align: center; + border-top: 1px solid #e6e6e6; +} + +#footer:before { + content: ''; + position: absolute; + right: 0; + bottom: 0; + left: 0; + height: 50px; + overflow: hidden; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), + 0 8px 0 -3px #f6f6f6, + 0 9px 1px -3px rgba(0, 0, 0, 0.2), + 0 16px 0 -6px #f6f6f6, + 0 17px 2px -6px rgba(0, 0, 0, 0.2); +} + +#todo-count { + float: left; + text-align: left; +} + +#todo-count strong { + font-weight: 300; +} + +#filters { + margin: 0; + padding: 0; + list-style: none; + position: absolute; + right: 0; + left: 0; +} + +#filters li { + display: inline; +} + +#filters li a { + color: inherit; + margin: 3px; + padding: 3px 7px; + text-decoration: none; + border: 1px solid transparent; + border-radius: 3px; +} + +#filters li a.selected, +#filters li a:hover { + border-color: rgba(175, 47, 47, 0.1); +} + +#filters li a.selected { + border-color: rgba(175, 47, 47, 0.2); +} + +#clear-completed, +html #clear-completed:active { + float: right; + position: relative; + line-height: 20px; + text-decoration: none; + cursor: pointer; + visibility: hidden; + position: relative; +} + +#clear-completed::after { + visibility: visible; + content: 'Clear completed'; + position: absolute; + right: 0; + white-space: nowrap; +} + +#clear-completed:hover::after { + text-decoration: underline; +} + +#info { + margin: 65px auto 0; + color: #bfbfbf; + font-size: 10px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + text-align: center; +} + +#info p { + line-height: 1; +} + +#info a { + color: inherit; + text-decoration: none; + font-weight: 400; +} + +#info a:hover { + text-decoration: underline; +} + +/* +Hack to remove background from Mobile Safari. +Can't use it globally since it destroys checkboxes in Firefox +*/ +@media screen and (-webkit-min-device-pixel-ratio:0) { + #toggle-all, + #todo-list li .toggle { + background: none; + } + + #todo-list li .toggle { + height: 40px; + } + + #toggle-all { + -webkit-transform: rotate(90deg); + transform: rotate(90deg); + -webkit-appearance: none; + appearance: none; + } +} + +@media (max-width: 430px) { + #footer { + height: 50px; + } + + #filters { + bottom: 10px; + } +} diff --git a/modules/examples/src/web_workers/index.dart b/modules/examples/src/web_workers/todo/index.dart similarity index 86% rename from modules/examples/src/web_workers/index.dart rename to modules/examples/src/web_workers/todo/index.dart index 15d0822e4c..43ff3371ab 100644 --- a/modules/examples/src/web_workers/index.dart +++ b/modules/examples/src/web_workers/todo/index.dart @@ -1,4 +1,4 @@ -library angular2.examples.web_workers; +library angular2.examples.web_workers.todo.index; import "package:angular2/src/web-workers/ui/application.dart" show bootstrap; import "package:angular2/src/reflection/reflection_capabilities.dart"; diff --git a/modules/examples/src/web_workers/todo/index.html b/modules/examples/src/web_workers/todo/index.html new file mode 100644 index 0000000000..a52efc9238 --- /dev/null +++ b/modules/examples/src/web_workers/todo/index.html @@ -0,0 +1,13 @@ + + + Todo Angular 2 - WebWorker + + + + Loading... + + + + $SCRIPTS$ + + diff --git a/modules/examples/src/web_workers/todo/index.ts b/modules/examples/src/web_workers/todo/index.ts new file mode 100644 index 0000000000..170f78bba0 --- /dev/null +++ b/modules/examples/src/web_workers/todo/index.ts @@ -0,0 +1,2 @@ +import {bootstrap} from "angular2/src/web-workers/ui/application"; +bootstrap("loader.js"); diff --git a/modules/examples/src/web_workers/todo/index_common.ts b/modules/examples/src/web_workers/todo/index_common.ts new file mode 100644 index 0000000000..7ae55dd299 --- /dev/null +++ b/modules/examples/src/web_workers/todo/index_common.ts @@ -0,0 +1,60 @@ +import {NgFor, Component, View, formDirectives} from 'angular2/angular2'; +import {Store, Todo, TodoFactory} from './services/TodoStore'; + +@Component({selector: 'todo-app', viewBindings: [Store, TodoFactory]}) +@View({templateUrl: 'todo.html', directives: [NgFor, formDirectives]}) +export class TodoApp { + todoEdit: Todo = null; + inputValue: string; + hideActive: boolean = false; + hideCompleted: boolean = false; + isComplete: boolean = false; + + constructor(public todoStore: Store, public factory: TodoFactory) {} + + enterTodo(): void { + this.addTodo(this.inputValue); + this.inputValue = ""; + } + + doneEditing($event, todo: Todo): void { + var which = $event.keyCode; + if (which === 13) { + todo.title = todo.editTitle; + this.todoEdit = null; + } else if (which === 27) { + this.todoEdit = null; + todo.editTitle = todo.title; + } + } + + editTodo(todo: Todo): void { this.todoEdit = todo; } + + addTodo(newTitle: string): void { this.todoStore.add(this.factory.create(newTitle, false)); } + + completeMe(todo: Todo): void { todo.completed = !todo.completed; } + + toggleCompleted(): void { + this.hideActive = !this.hideActive; + this.hideCompleted = false; + } + + toggleActive(): void { + this.hideCompleted = !this.hideCompleted; + this.hideActive = false; + } + + showAll(): void { + this.hideCompleted = false; + this.hideActive = false; + } + + deleteMe(todo: Todo): void { this.todoStore.remove(todo); } + + toggleAll($event): void { + this.isComplete = !this.isComplete; + this.todoStore.list.forEach((todo: Todo) => { todo.completed = this.isComplete; }); + } + + clearCompleted(): void { this.todoStore.removeBy((todo) => todo.completed); } +} diff --git a/modules/examples/src/web_workers/loader.js b/modules/examples/src/web_workers/todo/loader.js similarity index 87% rename from modules/examples/src/web_workers/loader.js rename to modules/examples/src/web_workers/todo/loader.js index 992289a184..47bc93fe4a 100644 --- a/modules/examples/src/web_workers/loader.js +++ b/modules/examples/src/web_workers/todo/loader.js @@ -11,7 +11,7 @@ $SCRIPTS$ window = { }; assert = function() {}; -System.import("examples/src/web_workers/background_index") +System.import("examples/src/web_workers/todo/background_index") .then( function(m) { console.log("running main"); diff --git a/modules/examples/src/web_workers/todo/services/TodoStore.ts b/modules/examples/src/web_workers/todo/services/TodoStore.ts new file mode 100644 index 0000000000..11b8b566e1 --- /dev/null +++ b/modules/examples/src/web_workers/todo/services/TodoStore.ts @@ -0,0 +1,51 @@ +import {Injectable} from 'angular2/angular2'; +import {ListWrapper} from 'angular2/src/facade/collection'; + +// base model for RecordStore +export class KeyModel { + constructor(public key: number) {} +} + +export class Todo extends KeyModel { + editTitle: string; + constructor(key: number, public title: string, public completed: boolean) { + super(key); + this.editTitle = title; + } +} + +@Injectable() +export class TodoFactory { + _uid: number = 0; + + nextUid(): number { return ++this._uid; } + + create(title: string, isCompleted: boolean): Todo { + return new Todo(this.nextUid(), title, isCompleted); + } +} + +// Store manages any generic item that inherits from KeyModel +@Injectable() +export class Store { + list: List = []; + + add(record: KeyModel): void { this.list.push(record); } + + remove(record: KeyModel): void { this._spliceOut(record); } + + removeBy(callback: Function): void { + var records = ListWrapper.filter(this.list, callback); + ListWrapper.removeAll(this.list, records); + } + + private _spliceOut(record: KeyModel) { + var i = this._indexFor(record); + if (i > -1) { + return ListWrapper.splice(this.list, i, 1)[0]; + } + return null; + } + + private _indexFor(record: KeyModel) { return this.list.indexOf(record); } +} diff --git a/modules/examples/src/web_workers/todo/todo.html b/modules/examples/src/web_workers/todo/todo.html new file mode 100644 index 0000000000..dd1220cdaa --- /dev/null +++ b/modules/examples/src/web_workers/todo/todo.html @@ -0,0 +1,68 @@ +
+ + + +
+ + + +
    + +
  • +
    + + + + + + +
    + +
    + + + +
    +
  • +
+ +
+ + + +
+ + diff --git a/tools/broccoli/trees/browser_tree.ts b/tools/broccoli/trees/browser_tree.ts index 5b662cd464..6681dea4e2 100644 --- a/tools/broccoli/trees/browser_tree.ts +++ b/tools/broccoli/trees/browser_tree.ts @@ -64,7 +64,8 @@ const kServedPaths = [ 'examples/src/material/radio', 'examples/src/material/switcher', 'examples/src/message_broker', - 'examples/src/web_workers' + 'examples/src/web_workers/kitchen_sink', + 'examples/src/web_workers/todo' ];