{"action":"created","comment":{"url":"https://api.github.com/repos/angular/angular/pulls/comments/39101677","id":39101677,"diff_hunk":"@@ -0,0 +1,473 @@\n+# WebWorkers in Angular 2: Documentation\n+\n+Angular 2 includes native support for writing applications which live in a\n+WebWorker. This document describes how to write applications that take advantage\n+of this feature.\n+It also provides a detailed description of the underlying messaging\n+infrastructure that angular uses to communicate between the main process and the\n+worker. This infrastructure can be modified by an application developer to\n+enable driving an angular 2 application from an iFrame, different window / tab,\n+server, etc..\n+\n+## Introduction\n+WebWorker support in Angular2 is designed to make it easy to leverage parallelization in your web application.\n+When you choose to run your application in a WebWorker angular runs both your application's logic and the \n+majority of the core angular framework in a WebWorker. \n+By offloading as much code as possible to the \n+\n+## Bootstrapping a WebWorker Application\n+Bootstrapping a WebWorker application is not much different than bootstrapping a normal application. \n+The primary difference is that you don't pass your root component directly to ```bootstrap```. \n+Instead you pass the name of a background script that calls ```bootstrapWebWorker``` with your root component.\n+\n+### Example\n+To bootstrap Hello World in a WebWorker we do the following in TypeScript\n+```HTML\n+<html>\n+  <head>\n+     <script src=\"https://github.jspm.io/jmcriffey/bower-traceur-runtime@0.0.87/traceur-runtime.js\"></script>\n+     <script src=\"https://jspm.io/system@0.16.js\"></script>\n+     <script src=\"angular2/web_worker/ui.js\"></script>\n+  </head>\n+  <body>\n+    <hello-world></hello-world>\n+    <script>System.import(\"index\")</script>\n+  </body>\n+</html>\n+```\n+```TypeScript\n+// index.js\n+import {bootstrap} from \"angular2/web_worker/ui\";\n+bootstrap(loader.js);\n+```\n+```JavaScript\n+// loader.js\n+importScripts(\"https://github.jspm.io/jmcriffey/bower-traceur-runtime@0.0.87/traceur-runtime.js\", \"https://jspm.io/system@0.16.js\", \"angular2/web_worker/worker.js\");\n+System.import(\"app\");\n+```\n+```TypeScript\n+// app.ts\n+import {Component, View, bootstrapWebWorker} from \"angular2/web_worker/worker\";\n+@Component({\n+  selector: \"hello-world\"\n+})\n+@View({\n+  template: \"<h1>Hello {{name}}</h1>\n+})\n+export class HelloWorld {\n+  name: string = \"Jane\";\n+}\n+\n+bootstrapWebWorker(HelloWorld);\n+```\n+There's a few important things to note here:\n+* On the UI side we import all angular types from `angular2/web_worker/ui` and on the worker side we import from\n+`angular2/web_worker/worker`. These modules include all the typings in the WebWorker bundle. By importing from\n+these URLs instead of `angular2/angular2` we can statically ensure that our app does not reference a type that\n+doesn't exist in the context it's mean to execute in. For example, if we tried to import DomRenderer in the Worker\n+or NgFor on the UI we would get a compiler error.\n+* The UI loads angular from the file `angular2/web_worker/ui.js` and the Worker loads angular from\n+`angular2/web_worker/worker.js`. These bundles are created specifically for using WebWorkers and should be used\n+instead of the normal angular2.js file. Both files contain subsets of the angular2 codebase that is designed to\n+run specifically on the UI or Worker. Additionally, they contain the core messaging infrastructure used to\n+communicate between the Worker and the UI. This messaging code is not in the standard angular2.js file.\n+* We pass `loader.js` to bootstrap and not `app.ts`. You can think of `loader.js` as the `index.html` of the\n+Worker. Since WebWorkers share no memory with the UI we need to reload the angular2 dependencies before\n+bootstrapping our application. We do this with importScripts. Additionally, we need to do this in a different \n+file than `app.ts` because our module loader (System.js in this example) has not been loaded yet, and `app.ts`\n+will be compiled with a System.define call at the top.\n+* The HelloWorld Component looks exactly like a normal Angular2 HelloWorld Component! The goal of WebWorker\n+support was to allow as much of Angular to live in the worker as possible. \n+As such, *most* angular2 components can be bootstrapped in a WebWorker with minimal to no changes required.\n+\n+For reference, here's the same HelloWorld example in Dart.\n+```HTML\n+<html>\n+  <body>\n+    <script type=\"application/dart\" src=\"index.dart\"></script>\n+    <script src=\"packages/browser.dart.js\"></script>\n+  </body>\n+</html>\n+```\n+```Dart\n+// index.dart\n+import \"package:angular2/web_worker/ui.dart\";\n+import \"package:angular2/src/core/reflection/reflection.dart\";\n+import \"package:angular2/src/core/reflection/reflection_capabilities.dart\";\n+\n+main() {\n+  reflector.reflectionCapabilities = new ReflectionCabilities();\n+  bootstrap(\"app.dart\");\n+}\n+```\n+```Dart\n+import \"package:angular2/web_worker/worker.dart\";\n+import \"package:angular2/src/core/reflection/reflection.dart\";\n+import \"package:angular2/src/core/reflection/reflection_capabilities.dart\";\n+\n+@Component({\n+  selector: \"hello-world\"\n+})\n+@View({\n+  template: \"<h1>Hello {{name}}</h1>\"\n+})\n+class HelloWorld {\n+  String name = \"Jane\";\n+}\n+\n+main(List<String> args, SendPort replyTo) {\n+  reflector.reflectionCapabilities = new ReflectionCapabilities();\n+  bootstrapWebWorker(replyTo, HelloWorld);\n+}\n+\n+```\n+This code is nearly the same as the TypeScript version with just a couple key differences:\n+* We don't have a `loader.js` file. Dart applications don't need this file because you don't need a module loader.\n+* We pass a `SendPort` to `bootstrapWebWorker`. Dart applications use the Isolate API, which communicates via\n+Dart's Port abstraction. When you call `bootstrap` from the UI thread, angular starts a new Isolate to run \n+your application logic. When Dart starts a new Isolate it passes a `SendPort` to that Isolate so that it \n+can communicate with the Isolate that spawned it. You need to pass this `SendPort` to `bootstrapWebWorker` \n+so that Angular can communicate with the UI.\n+* You need to set up `ReflectionCapabilities` on both the UI and Worker. Just like writing non-concurrent \n+Angular2 Dart applications you need to set up the reflector. You should not use Reflection in production, \n+but should use the angular 2 transformer to remove it in your final JS code. Note there's currently a bug \n+with running the transformer on your UI code (#3971). You can (and should) pass the file where you call\n+`bootstrapWebWorker` as an entry point to the transformer, but you should not pass your UI index file \n+to the transformer until that bug is fixed.\n+\n+## Writing WebWorker Compatible Components\n+You can do almost everything in a WebWorker component that you can do in a typical Angular 2 Component. \n+The main exception is that there is **no** DOM access from a WebWorker component. In Dart this means you can't \n+import anything from `dart:html` and in JavaScript it means you can't use `document` or `window`. Instead you \n+should use data bindings and if needed you can inject the `Renderer` along with your component's `ElementRef`\n+directly into your component and use methods such as `setElementProperty`, `setElementAttribute`,\n+`setElementClass`, `setElementStyle`, `invokeElementMethod`, and `setText`. Not that you **cannot** call\n+`getNativeElementSync`. Doing so will always return `null` when running in a WebWorker. \n+If you need DOM access see [Running Code on the UI](#running-code-on-the-ui).\n+\n+## WebWorker Design Overview\n+When running your application in a WebWorker, the majority of the angular core along with your application logic\n+runs on the worker. The two main components that run on the UI are the `Renderer` and the `RenderCompiler`. When\n+running angular in a WebWorker the bindings for these two components are replaced by the `WebWorkerRenderer` and \n+the `WebWorkerRenderCompiler`. When these components are used at runtime, they pass messages through the \n+[MessageBroker](#messagebroker) instructing the UI to run the actual method and return the result. The\n+[MessageBroker](#messagebroker) abstraction allows either side of the WebWorker boundary to schedule code to run\n+on the opposite side and receive the result. You can use the [MessageBroker](#messagebroker)\n+Additionally, the [MessageBroker](#messagebroker) sits on top of the [MessageBus](#messagebus). \n+MessageBus is a low level abstraction that provides a language agnostic API for communicating with angular components across any runtime boundary such as `WebWorker <--> UI` communication, `UI <--> Server` communication, \n+or `Window <--> Window` communication.\n+\n+See the diagram below for a high level overview of how this code is structured:\n+\n+![WebWorker Diagram](http://stanford.edu/~jteplitz/ng_2_worker.png)\n+\n+## Running Code on the UI\n+If your application needs to run code on the UI, there are a few options. The easiest way is to use a \n+CustomElement in your view. You can then register this custom element from your html file and run code in response\n+to the element's lifecycle hooks. Note, Custom Elements are still experimental. See \n+[MDN](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Custom_Elements) for the latest details on how\n+to use them.\n+\n+If you require more robust communication between the WebWorker and the UI you can use the [MessageBroker](#using-the-messagebroker-in-your-application) or\n+[MessageBus](#using-the-messagebus-in-your-application) directly.\n+\n+## MessageBus\n+The MessageBus is a low level abstraction that provides a language agnostic API for communicating with angular components across any runtime boundary. It supports multiplex communication through the use of a channel\n+abstraction.\n+\n+Angular currently includes two stable MessageBus implementations, which are used by default when you run your\n+application inside a WebWorker.\n+\n+1. The `PostMessageBus` is used by JavaScript applications to communicate between a WebWorker and the UI.\n+2. The `IsolateMessageBus` is used by Dart applications to communicate between a background Isolate and the UI.\n+\n+Angular also includes three experimental MessageBus implementations:\n+\n+1. The `WebSocketMessageBus` is a Dart MessageBus that lives on the UI and communicates with an angular\n+application running on a server. It's intended to be used with either the `SingleClientServerMessageBus` or the\n+`MultiClientServerMessageBus`.\n+2. The `SingleClientServerMessageBus` is a Dart MessageBus that lives on a Dart Server. It allows an angular\n+application to run on a server and communicate with a single browser that's running the `WebSocketMessageBus`.\n+3. The `MultiClientServerMessageBus` is like the `SingleClientServerMessageBus` except it allows an arbitrary\n+number of clients to connect to the server. It keeps all connected browsers in sync and if an event fires in\n+any connected browser it propagates the result to all connected clients. This can be especially useful as a \n+debugging tool, by allowing you to connect multiple browsers / devices to the same angular application, \n+change the state of that application, and ensure that all the clients render the view correctly. Using these tools\n+can make it easy to catch tricky browser compatibility issues.\n+\n+### Using the MessageBus in Your Application\n+**Note**: If you want to pass custom messages between the UI and WebWorker, it's recommended you use the\n+[MessageBroker](#using-the-messagebroker-in-your-application). However, if you want to control the messaging\n+protocol yourself you can use the MessageBus directly.\n+\n+To use the MessageBus you need to initialize a new channel on both the UI and WebWorker.\n+In TypeScript that would look like this:\n+```TypeScript\n+// index.ts, which is running on the UI.\n+var instance = bootstrap(\"loader.js\");\n+var bus = instance.bus;\n+bus.initChannel(\"My Custom Channel\");\n+```\n+```TypeScript\n+// background_index.ts, which is running on the WebWorker\n+import {MessageBus} from 'angular2/web_worker/worker';\n+@Component({...})\n+@View({...})\n+export class MyComponent {\n+  constructor (bus: MessageBus) {\n+    bus.initChannel(\"My CustomChannel\");\n+  }\n+}\n+```\n+\n+Once the channel has been initialized either side can use the `from` and `to` methods on the MessageBus to send\n+and receive messages. Both methods return EventEmitter. Expanding on the example from earlier:\n+```TypeScript\n+// index.ts, which is running on the UI.\n+var instance = bootstrap(\"loader.js\");\n+var bus = instance.bus;\n+bus.initChannel(\"My Custom Channel\");\n+bus.to(\"My Custom Channel\").next(\"hello from the UI\");\n+```\n+```TypeScript\n+// background_index.ts, which is running on the WebWorker\n+import {MessageBus} from 'angular2/web_worker/worker';\n+@Component({...})\n+@View({...})\n+export class MyComponent {\n+  constructor (bus: MessageBus) {\n+    bus.initChannel(\"My Custom Channel\");\n+    bus.from(\"My Custom Channel\").observer((message) => {\n+      console.log(message); // will print \"hello from the UI\"\n+    });\n+  }\n+}\n+```\n+\n+This example is nearly identical in Dart, and is included below for reference:\n+```Dart\n+// index.dart, which is running on the UI.\n+import 'package:angular2/web_workers/worker';\n+\n+main() {\n+  var instance = bootstrap(\"background_index.dart\");\n+  var bus = instance.bus;\n+  bus.initChannel(\"My Custom Channel\");\n+  bus.to(\"My Custom Channel\").add(\"hello from the UI\");\n+}\n+\n+```\n+```Dart\n+// background_index.dart, which is running on the WebWorker\n+import 'package:angular2/web_worker/worker';\n+@Component({...})\n+@View({...})\n+class MyComponent {\n+  MyComponent (MessageBus bus) {\n+    bus.initChannel(\"My Custom Channel\");\n+    bus.from(\"My Custom Channel\").listen((message) => {\n+      print(message); // will print \"hello from the UI\"\n+    });\n+  }\n+}\n+```\n+The only substantial difference between these APIs in Dart and TypeScript is the different APIs for the\n+`EventEmitter`.\n+\n+**Note** Because the messages passed through the MessageBus cross a WebWorker boundary, they must be serializable.\n+If you use the MessageBus directly, you are responsible for serializing your messages.\n+In JavaScript / TypeScript this means they must be serializable via JavaScript's \n+[structured cloning algorithim](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm).\n+\n+In Dart this means they must be valid messages that can be passed through a\n+[SendPort](https://api.dartlang.org/1.12.1/dart-isolate/SendPort/send.html).\n+\n+\n+### MessageBus and Zones\n+The MessageBus API includes support for [zones](http://www.github.com/angular/zone.js). \n+A MessageBus can be attached to a specific zone  (by calling `attachToZone). Then specific channels can be\n+specified to run in the zone when they are initialized.\n+If a channel is running in the zone, that means that any events emitted from that channel will be executed within\n+the given zone. For example, by default angular runs the EventDispatch channel inside the angular zone. That means\n+when an event is fired from the DOM and received on the WebWorker the event handler automatically runs inside the\n+angular zone. This is desired because after the event handler exits we want to exit the zone so that we trigger\n+change detection. Generally, you want your channels to run inside the zone unless you have a good reason for why \n+they need to run outside the zone.\n+\n+### Implementing and Using a Custom MessageBus\n+**Note** Implementing and using a Custom MesageBus is experimental and requires importing from private APIs.\n+\n+If you want to drive your application from something other than a WebWorker you can implement a custom message\n+bus. Implementing a custom message bus just means creating a class that fulfills the API specified by the\n+abstract MessageBus class.\n+\n+If you're implementing your MessageBus in Dart you can extend the `GenericMessageBus` class included in angular.\n+if you do this, you don't need to implement zone or channel support yourself. You only need to implement a \n+`MessageBusSink` that extends `GenericMessageBusSink` and a `MessageBusSource` that extends \n+`GenericMessageBusSource`. The `MessageBusSink` must override the `sendMessages` method. This method is\n+given a list of serialized messages that it is required to send through the sink.\n+The `MessageBusSource` needs to provide a [Stream](https://api.dartlang.org/1.12.1/dart-async/Stream-class.html)\n+of incoming messages (either by passing the stream to `GenericMessageBusSource's` constructor or by calling\n+attachTo() with the stream). It also needs to override the abstract `decodeMessages` method. This method is\n+given a List of serialized messages received by the source and should perform any decoding work that needs to be\n+done before the application can read the messages.\n+\n+For example, if your MessageBus sends and receives JSON data you would do the following:\n+```Dart\n+import 'package:angular2/src/web_workers/shared/generic_message_bus.dart';\n+import 'dart:convert';\n+\n+class JsonMessageBusSink extends GenericMessageBusSink {\n+  @override\n+  void sendMessages(List<dynamic> messages) {\n+    String encodedMessages = JSON.encode(messages);\n+    // Send encodedMessages here\n+  }\n+}\n+\n+class JsonMessageBusSource extends GenericMessageBuSource {\n+  JsonMessageBusSource(Stream incomingMessages) : super (incomingMessages);\n+  \n+  @override\n+  List<dynamic> decodeMessages(dynamic messages) {\n+    return JSON.decode(messages);\n+  }\n+}\n+```\n+\n+Once you've implemented your custom MessageBus in either TypeScript or Dart you can tell angular to use it like \n+so:\n+In TypeScript:\n+```TypeScript\n+// index.ts, running on the UI side\n+import {bootstrapUICommon} from 'angular2/src/web_workers/ui/impl';\n+var bus = new MyAwesomeMessageBus();\n+bootstrapUICommon(bus);\n+```\n+```TypeScript\n+// background_index.ts, running on the application side\n+import {bootstrapWebWorkerCommon} from 'angular2/src/web_workers/worker/application_common';\n+import {MyApp} from './app';\n+var bus = new MyAwesomeMessageBus();\n+bootstrapWebWorkerCommon(MyApp, bus);\n+```\n+In Dart:\n+```Dart\n+// index.dart, running on the UI side\n+import 'package:angular2/src/web_workers/ui/impl.dart' show bootstrapUICommon;\n+import \"package:angular2/src/core/reflection/reflection.dart\";\n+import \"package:angular2/src/core/reflection/reflection_capabilities.dart\";\n+\n+main() {\n+  reflector.reflectionCapabilities = new ReflectionCapabilities();\n+  var bus = new MyAwesomeMessageBus();\n+  bootstrapUiCommon(bus);\n+}\n+```\n+```Dart\n+// background_index.dart, running on the application side\n+import \"package:angular2/src/web_workers/worker/application_common.dart\" show bootstrapWebWorkerCommon;\n+import \"package:angular2/src/core/reflection/reflection.dart\";\n+import \"package:angular2/src/core/reflection/reflection_capabilities.dart\";\n+import \"./app.dart\" show MyApp;\n+\n+main() {\n+    reflector.reflectionCapabilities = new ReflectionCapabilities();\n+    var bus = new MyAwesomeMessageBus();\n+    bootstrapWebWorkerCommon(MyApp, bus);\n+}\n+```\n+Notice how we call `bootstrapUICommon` instead of `bootstrap` from the UI side. `bootstrap` spans a new WebWorker\n+/ Isolate and attaches the default angular MessageBus to it. If you're using a custom MessageBus you are \n+responsible for setting up the application side and initiating communication with it. `bootstrapUICommon` assumes\n+that the given MessageBus is already set up and can communicate with the application.\n+Similarly, we call `bootstrapWebWorkerCommon` instead of `boostrapWebWorker` from the application side. This is\n+because `bootstrapWebWorker` assumes you're using the default angular MessageBus and initializes a new one for you.\n+\n+## MessageBroker\n+The MessageBroker is a higher level messaging abstraction that sits on top of the MessageBus. It is used when you\n+want to execute code on the other side of a runtime boundary and may want to receive the result.\n+There are two types of MessageBrokers. The `ServiceMessageBroker` is used by the side that actually performs\n+an operation and may return a result. Conversely, the `ClientMessageBroker` is used by the side that requests that\n+an operation be performed and may want to receive the result.\n+\n+### Using the MessageBroker In Your Application\n+To use MessageBrokers in your application you must initialize both a `ClientMessageBroker` and a\n+`ServiceMessageBroker` on the same channel. You can then register methods that the `ServiceMessageBroker` should\n+listen the instruct the `ClientMessageBroker` to run those methods. Below is a lightweight example of using\n+MessageBrokers in an application. For a more complete example, check out the `WebWorkerRenderer` and\n+`MessageBasedRenderer` inside the Angular WebWorker code.\n+\n+#### Using the MessageBroker in TypeScript\n+```TypeScript\n+// index.ts, which is running on the UI with a method that we want to expose to a WebWorker\n+import {bootstrap} from 'angular2/web_worker/ui';\n+\n+var instance = bootstrap(\"loader.js\");\n+var broker = instance.app.createServiceMessageBroker(\"My Broker Channel\");\n+\n+// assume we have some function doCoolThings that takes a string argument and returns a Promise<string>\n+broker.registerMethod(\"awesomeMethod\", [PRIMITIVE], (arg1: string) => doCoolThing(arg1), PRIMITIVE);\n+```\n+```TypeScript\n+// background.ts, which is running on a WebWorker and wants to execute a method on the UI\n+import {Component, View, ClientMessageBrokerFactory, PRIMITIVE, UiArguments, FnArgs}\n+from 'angular2/web_worker/worker';\n+\n+@Component(...)\n+@View(...)\n+export class MyComponent {\n+  constructor(brokerFactory: ClientMessageBrokerFactory) {\n+    var broker = brokerFactory.createMessageBroker(\"My Broker Channel\");\n+    \n+    var arguments = [new FnArg(value, PRIMITIVE)];\n+    var methodInfo = new UiArguments(\"awesomeMethod\", arguments);\n+    broker.runOnService(methodInfo, PRIMTIVE).then((result: string) => {\n+      // result will be equal to the return value of doCoolThing(value) that ran on the UI.\n+    });\n+  }\n+}\n+```\n+#### Using the MessageBroker in Dart\n+```Dart\n+// index.dart, which is running on the UI with a method that we want to expose to a WebWorker\n+import 'package:angular2/web_worker/ui';\n+\n+main() {\n+  var instance = bootstrap(\"background.dart\");\n+  var broker = instance.app.createServiceMessageBroker(\"My Broker Channel\");\n+  \n+  // assume we have some function doCoolThings that takes a String argument and returns a Future<String>\n+  broker.registerMethod(\"awesomeMethod\", [PRIMITIVE], (String arg1) => doCoolThing(arg1), PRIMITIVE);\n+}\n+\n+```\n+```Dart\n+// background.dart, which is running on a WebWorker and wants to execute a method on the UI\n+import 'package:angular2/web_worker/worker';","path":"modules/angular2/docs/web_workers/web_workers.md","position":447,"original_position":447,"commit_id":"9a7fc6dff603643f955e4fc65c9969d41ba39204","original_commit_id":"9a7fc6dff603643f955e4fc65c9969d41ba39204","user":{"login":"yjbanov","id":211513,"avatar_url":"https://avatars.githubusercontent.com/u/211513?v=3","gravatar_id":"","url":"https://api.github.com/users/yjbanov","html_url":"https://github.com/yjbanov","followers_url":"https://api.github.com/users/yjbanov/followers","following_url":"https://api.github.com/users/yjbanov/following{/other_user}","gists_url":"https://api.github.com/users/yjbanov/gists{/gist_id}","starred_url":"https://api.github.com/users/yjbanov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yjbanov/subscriptions","organizations_url":"https://api.github.com/users/yjbanov/orgs","repos_url":"https://api.github.com/users/yjbanov/repos","events_url":"https://api.github.com/users/yjbanov/events{/privacy}","received_events_url":"https://api.github.com/users/yjbanov/received_events","type":"User","site_admin":false},"body":"`worker.dart`","created_at":"2015-09-09T21:53:11Z","updated_at":"2015-09-09T21:53:11Z","html_url":"https://github.com/angular/angular/pull/4103#discussion_r39101677","pull_request_url":"https://api.github.com/repos/angular/angular/pulls/4103","_links":{"self":{"href":"https://api.github.com/repos/angular/angular/pulls/comments/39101677"},"html":{"href":"https://github.com/angular/angular/pull/4103#discussion_r39101677"},"pull_request":{"href":"https://api.github.com/repos/angular/angular/pulls/4103"}}},"pull_request":{"url":"https://api.github.com/repos/angular/angular/pulls/4103","id":44603019,"html_url":"https://github.com/angular/angular/pull/4103","diff_url":"https://github.com/angular/angular/pull/4103.diff","patch_url":"https://github.com/angular/angular/pull/4103.patch","issue_url":"https://api.github.com/repos/angular/angular/issues/4103","number":4103,"state":"open","locked":false,"title":"docs(WebWorkers): Add overview of WebWorker design","user":{"login":"jteplitz602","id":193422,"avatar_url":"https://avatars.githubusercontent.com/u/193422?v=3","gravatar_id":"","url":"https://api.github.com/users/jteplitz602","html_url":"https://github.com/jteplitz602","followers_url":"https://api.github.com/users/jteplitz602/followers","following_url":"https://api.github.com/users/jteplitz602/following{/other_user}","gists_url":"https://api.github.com/users/jteplitz602/gists{/gist_id}","starred_url":"https://api.github.com/users/jteplitz602/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jteplitz602/subscriptions","organizations_url":"https://api.github.com/users/jteplitz602/orgs","repos_url":"https://api.github.com/users/jteplitz602/repos","events_url":"https://api.github.com/users/jteplitz602/events{/privacy}","received_events_url":"https://api.github.com/users/jteplitz602/received_events","type":"User","site_admin":false},"body":"","created_at":"2015-09-09T21:30:40Z","updated_at":"2015-09-09T21:53:11Z","closed_at":null,"merged_at":null,"merge_commit_sha":"5b5a78609b0de07386d2fbd0a75c84437eb458c8","assignee":{"login":"yjbanov","id":211513,"avatar_url":"https://avatars.githubusercontent.com/u/211513?v=3","gravatar_id":"","url":"https://api.github.com/users/yjbanov","html_url":"https://github.com/yjbanov","followers_url":"https://api.github.com/users/yjbanov/followers","following_url":"https://api.github.com/users/yjbanov/following{/other_user}","gists_url":"https://api.github.com/users/yjbanov/gists{/gist_id}","starred_url":"https://api.github.com/users/yjbanov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yjbanov/subscriptions","organizations_url":"https://api.github.com/users/yjbanov/orgs","repos_url":"https://api.github.com/users/yjbanov/repos","events_url":"https://api.github.com/users/yjbanov/events{/privacy}","received_events_url":"https://api.github.com/users/yjbanov/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/angular/angular/pulls/4103/commits","review_comments_url":"https://api.github.com/repos/angular/angular/pulls/4103/comments","review_comment_url":"https://api.github.com/repos/angular/angular/pulls/comments{/number}","comments_url":"https://api.github.com/repos/angular/angular/issues/4103/comments","statuses_url":"https://api.github.com/repos/angular/angular/statuses/9a7fc6dff603643f955e4fc65c9969d41ba39204","head":{"label":"jteplitz602:documentation","ref":"documentation","sha":"9a7fc6dff603643f955e4fc65c9969d41ba39204","user":{"login":"jteplitz602","id":193422,"avatar_url":"https://avatars.githubusercontent.com/u/193422?v=3","gravatar_id":"","url":"https://api.github.com/users/jteplitz602","html_url":"https://github.com/jteplitz602","followers_url":"https://api.github.com/users/jteplitz602/followers","following_url":"https://api.github.com/users/jteplitz602/following{/other_user}","gists_url":"https://api.github.com/users/jteplitz602/gists{/gist_id}","starred_url":"https://api.github.com/users/jteplitz602/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jteplitz602/subscriptions","organizations_url":"https://api.github.com/users/jteplitz602/orgs","repos_url":"https://api.github.com/users/jteplitz602/repos","events_url":"https://api.github.com/users/jteplitz602/events{/privacy}","received_events_url":"https://api.github.com/users/jteplitz602/received_events","type":"User","site_admin":false},"repo":{"id":37999840,"name":"angular","full_name":"jteplitz602/angular","owner":{"login":"jteplitz602","id":193422,"avatar_url":"https://avatars.githubusercontent.com/u/193422?v=3","gravatar_id":"","url":"https://api.github.com/users/jteplitz602","html_url":"https://github.com/jteplitz602","followers_url":"https://api.github.com/users/jteplitz602/followers","following_url":"https://api.github.com/users/jteplitz602/following{/other_user}","gists_url":"https://api.github.com/users/jteplitz602/gists{/gist_id}","starred_url":"https://api.github.com/users/jteplitz602/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jteplitz602/subscriptions","organizations_url":"https://api.github.com/users/jteplitz602/orgs","repos_url":"https://api.github.com/users/jteplitz602/repos","events_url":"https://api.github.com/users/jteplitz602/events{/privacy}","received_events_url":"https://api.github.com/users/jteplitz602/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/jteplitz602/angular","description":"","fork":true,"url":"https://api.github.com/repos/jteplitz602/angular","forks_url":"https://api.github.com/repos/jteplitz602/angular/forks","keys_url":"https://api.github.com/repos/jteplitz602/angular/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jteplitz602/angular/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jteplitz602/angular/teams","hooks_url":"https://api.github.com/repos/jteplitz602/angular/hooks","issue_events_url":"https://api.github.com/repos/jteplitz602/angular/issues/events{/number}","events_url":"https://api.github.com/repos/jteplitz602/angular/events","assignees_url":"https://api.github.com/repos/jteplitz602/angular/assignees{/user}","branches_url":"https://api.github.com/repos/jteplitz602/angular/branches{/branch}","tags_url":"https://api.github.com/repos/jteplitz602/angular/tags","blobs_url":"https://api.github.com/repos/jteplitz602/angular/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jteplitz602/angular/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jteplitz602/angular/git/refs{/sha}","trees_url":"https://api.github.com/repos/jteplitz602/angular/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jteplitz602/angular/statuses/{sha}","languages_url":"https://api.github.com/repos/jteplitz602/angular/languages","stargazers_url":"https://api.github.com/repos/jteplitz602/angular/stargazers","contributors_url":"https://api.github.com/repos/jteplitz602/angular/contributors","subscribers_url":"https://api.github.com/repos/jteplitz602/angular/subscribers","subscription_url":"https://api.github.com/repos/jteplitz602/angular/subscription","commits_url":"https://api.github.com/repos/jteplitz602/angular/commits{/sha}","git_commits_url":"https://api.github.com/repos/jteplitz602/angular/git/commits{/sha}","comments_url":"https://api.github.com/repos/jteplitz602/angular/comments{/number}","issue_comment_url":"https://api.github.com/repos/jteplitz602/angular/issues/comments{/number}","contents_url":"https://api.github.com/repos/jteplitz602/angular/contents/{+path}","compare_url":"https://api.github.com/repos/jteplitz602/angular/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jteplitz602/angular/merges","archive_url":"https://api.github.com/repos/jteplitz602/angular/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jteplitz602/angular/downloads","issues_url":"https://api.github.com/repos/jteplitz602/angular/issues{/number}","pulls_url":"https://api.github.com/repos/jteplitz602/angular/pulls{/number}","milestones_url":"https://api.github.com/repos/jteplitz602/angular/milestones{/number}","notifications_url":"https://api.github.com/repos/jteplitz602/angular/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jteplitz602/angular/labels{/name}","releases_url":"https://api.github.com/repos/jteplitz602/angular/releases{/id}","created_at":"2015-06-24T17:28:53Z","updated_at":"2015-06-24T17:28:55Z","pushed_at":"2015-09-09T21:38:48Z","git_url":"git://github.com/jteplitz602/angular.git","ssh_url":"git@github.com:jteplitz602/angular.git","clone_url":"https://github.com/jteplitz602/angular.git","svn_url":"https://github.com/jteplitz602/angular","homepage":null,"size":15800,"stargazers_count":0,"watchers_count":0,"language":"TypeScript","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"angular:master","ref":"master","sha":"77e8304fc4c5e7009aae4ea8a741b744b702a58a","user":{"login":"angular","id":139426,"avatar_url":"https://avatars.githubusercontent.com/u/139426?v=3","gravatar_id":"","url":"https://api.github.com/users/angular","html_url":"https://github.com/angular","followers_url":"https://api.github.com/users/angular/followers","following_url":"https://api.github.com/users/angular/following{/other_user}","gists_url":"https://api.github.com/users/angular/gists{/gist_id}","starred_url":"https://api.github.com/users/angular/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/angular/subscriptions","organizations_url":"https://api.github.com/users/angular/orgs","repos_url":"https://api.github.com/users/angular/repos","events_url":"https://api.github.com/users/angular/events{/privacy}","received_events_url":"https://api.github.com/users/angular/received_events","type":"Organization","site_admin":false},"repo":{"id":24195339,"name":"angular","full_name":"angular/angular","owner":{"login":"angular","id":139426,"avatar_url":"https://avatars.githubusercontent.com/u/139426?v=3","gravatar_id":"","url":"https://api.github.com/users/angular","html_url":"https://github.com/angular","followers_url":"https://api.github.com/users/angular/followers","following_url":"https://api.github.com/users/angular/following{/other_user}","gists_url":"https://api.github.com/users/angular/gists{/gist_id}","starred_url":"https://api.github.com/users/angular/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/angular/subscriptions","organizations_url":"https://api.github.com/users/angular/orgs","repos_url":"https://api.github.com/users/angular/repos","events_url":"https://api.github.com/users/angular/events{/privacy}","received_events_url":"https://api.github.com/users/angular/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/angular/angular","description":"","fork":false,"url":"https://api.github.com/repos/angular/angular","forks_url":"https://api.github.com/repos/angular/angular/forks","keys_url":"https://api.github.com/repos/angular/angular/keys{/key_id}","collaborators_url":"https://api.github.com/repos/angular/angular/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/angular/angular/teams","hooks_url":"https://api.github.com/repos/angular/angular/hooks","issue_events_url":"https://api.github.com/repos/angular/angular/issues/events{/number}","events_url":"https://api.github.com/repos/angular/angular/events","assignees_url":"https://api.github.com/repos/angular/angular/assignees{/user}","branches_url":"https://api.github.com/repos/angular/angular/branches{/branch}","tags_url":"https://api.github.com/repos/angular/angular/tags","blobs_url":"https://api.github.com/repos/angular/angular/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/angular/angular/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/angular/angular/git/refs{/sha}","trees_url":"https://api.github.com/repos/angular/angular/git/trees{/sha}","statuses_url":"https://api.github.com/repos/angular/angular/statuses/{sha}","languages_url":"https://api.github.com/repos/angular/angular/languages","stargazers_url":"https://api.github.com/repos/angular/angular/stargazers","contributors_url":"https://api.github.com/repos/angular/angular/contributors","subscribers_url":"https://api.github.com/repos/angular/angular/subscribers","subscription_url":"https://api.github.com/repos/angular/angular/subscription","commits_url":"https://api.github.com/repos/angular/angular/commits{/sha}","git_commits_url":"https://api.github.com/repos/angular/angular/git/commits{/sha}","comments_url":"https://api.github.com/repos/angular/angular/comments{/number}","issue_comment_url":"https://api.github.com/repos/angular/angular/issues/comments{/number}","contents_url":"https://api.github.com/repos/angular/angular/contents/{+path}","compare_url":"https://api.github.com/repos/angular/angular/compare/{base}...{head}","merges_url":"https://api.github.com/repos/angular/angular/merges","archive_url":"https://api.github.com/repos/angular/angular/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/angular/angular/downloads","issues_url":"https://api.github.com/repos/angular/angular/issues{/number}","pulls_url":"https://api.github.com/repos/angular/angular/pulls{/number}","milestones_url":"https://api.github.com/repos/angular/angular/milestones{/number}","notifications_url":"https://api.github.com/repos/angular/angular/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/angular/angular/labels{/name}","releases_url":"https://api.github.com/repos/angular/angular/releases{/id}","created_at":"2014-09-18T16:12:01Z","updated_at":"2015-09-09T20:48:27Z","pushed_at":"2015-09-09T21:38:49Z","git_url":"git://github.com/angular/angular.git","ssh_url":"git@github.com:angular/angular.git","clone_url":"https://github.com/angular/angular.git","svn_url":"https://github.com/angular/angular","homepage":"https://angular.io","size":49651,"stargazers_count":5206,"watchers_count":5206,"language":"TypeScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1414,"mirror_url":null,"open_issues_count":474,"forks":1414,"open_issues":474,"watchers":5206,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/angular/angular/pulls/4103"},"html":{"href":"https://github.com/angular/angular/pull/4103"},"issue":{"href":"https://api.github.com/repos/angular/angular/issues/4103"},"comments":{"href":"https://api.github.com/repos/angular/angular/issues/4103/comments"},"review_comments":{"href":"https://api.github.com/repos/angular/angular/pulls/4103/comments"},"review_comment":{"href":"https://api.github.com/repos/angular/angular/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/angular/angular/pulls/4103/commits"},"statuses":{"href":"https://api.github.com/repos/angular/angular/statuses/9a7fc6dff603643f955e4fc65c9969d41ba39204"}}}}