| 
									
										
										
										
											2016-06-23 09:47:54 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							|  |  |  |  * Copyright Google Inc. All Rights Reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Use of this source code is governed by an MIT-style license that can be | 
					
						
							|  |  |  |  * found in the LICENSE file at https://angular.io/license
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-21 22:55:47 -07:00
										 |  |  | import {AnimationBuilder, animate, state, style, transition, trigger} from '@angular/animations'; | 
					
						
							| 
									
										
										
										
											2019-08-22 19:16:25 -07:00
										 |  |  | import {DOCUMENT, PlatformLocation, isPlatformServer, ɵgetDOM as getDOM} from '@angular/common'; | 
					
						
							| 
									
										
										
										
											2018-05-31 10:35:51 -07:00
										 |  |  | import {HTTP_INTERCEPTORS, HttpClient, HttpClientModule, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http'; | 
					
						
							| 
									
										
										
										
											2017-07-18 12:45:47 -07:00
										 |  |  | import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing'; | 
					
						
							| 
									
										
										
										
											2019-03-11 19:20:40 -05:00
										 |  |  | import {ApplicationRef, CompilerFactory, Component, HostListener, Inject, Injectable, Input, NgModule, NgZone, PLATFORM_ID, PlatformRef, ViewEncapsulation, destroyPlatform, getPlatform} from '@angular/core'; | 
					
						
							| 
									
										
										
										
											2018-11-16 16:14:36 +01:00
										 |  |  | import {async, inject} from '@angular/core/testing'; | 
					
						
							| 
									
										
										
										
											2019-03-11 19:20:40 -05:00
										 |  |  | import {BrowserModule, Title, TransferState, makeStateKey} from '@angular/platform-browser'; | 
					
						
							| 
									
										
										
										
											2017-09-11 00:18:55 -07:00
										 |  |  | import {BEFORE_APP_SERIALIZED, INITIAL_CONFIG, PlatformState, ServerModule, ServerTransferStateModule, platformDynamicServer, renderModule, renderModuleFactory} from '@angular/platform-server'; | 
					
						
							| 
									
										
										
										
											2019-03-11 19:20:40 -05:00
										 |  |  | import {ivyEnabled, modifiedInIvy} from '@angular/private/testing'; | 
					
						
							| 
									
										
										
										
											2018-05-31 10:35:51 -07:00
										 |  |  | import {Observable} from 'rxjs'; | 
					
						
							| 
									
										
										
										
											2018-02-27 17:06:06 -05:00
										 |  |  | import {first} from 'rxjs/operators'; | 
					
						
							| 
									
										
										
										
											2016-08-15 13:44:01 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | @Component({selector: 'app', template: `Works!`}) | 
					
						
							|  |  |  | class MyServerApp { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-10 17:00:27 -08:00
										 |  |  | @NgModule({ | 
					
						
							|  |  |  |   bootstrap: [MyServerApp], | 
					
						
							|  |  |  |   declarations: [MyServerApp], | 
					
						
							|  |  |  |   imports: [ServerModule], | 
					
						
							|  |  |  | }) | 
					
						
							| 
									
										
										
										
											2016-08-15 13:44:01 -07:00
										 |  |  | class ExampleModule { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-04 00:38:42 -07:00
										 |  |  | function getTitleRenderHook(doc: any) { | 
					
						
							|  |  |  |   return () => { | 
					
						
							|  |  |  |     // Set the title as part of the render hook.
 | 
					
						
							|  |  |  |     doc.title = 'RenderHook'; | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function exceptionRenderHook() { | 
					
						
							|  |  |  |   throw new Error('error'); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getMetaRenderHook(doc: any) { | 
					
						
							|  |  |  |   return () => { | 
					
						
							|  |  |  |     // Add a meta tag before rendering the document.
 | 
					
						
							|  |  |  |     const metaElement = doc.createElement('meta'); | 
					
						
							|  |  |  |     metaElement.setAttribute('name', 'description'); | 
					
						
							|  |  |  |     doc.head.appendChild(metaElement); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-05 16:30:45 -06:00
										 |  |  | function getAsyncTitleRenderHook(doc: any) { | 
					
						
							|  |  |  |   return () => { | 
					
						
							|  |  |  |     // Async set the title as part of the render hook.
 | 
					
						
							|  |  |  |     return new Promise(resolve => { | 
					
						
							|  |  |  |       setTimeout(() => { | 
					
						
							|  |  |  |         doc.title = 'AsyncRenderHook'; | 
					
						
							|  |  |  |         resolve(); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function asyncRejectRenderHook() { | 
					
						
							|  |  |  |   return () => { | 
					
						
							|  |  |  |     return new Promise((_resolve, reject) => { setTimeout(() => { reject('reject'); }); }); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-04 00:38:42 -07:00
										 |  |  | @NgModule({ | 
					
						
							|  |  |  |   bootstrap: [MyServerApp], | 
					
						
							|  |  |  |   declarations: [MyServerApp], | 
					
						
							|  |  |  |   imports: [BrowserModule.withServerTransition({appId: 'render-hook'}), ServerModule], | 
					
						
							|  |  |  |   providers: [ | 
					
						
							|  |  |  |     {provide: BEFORE_APP_SERIALIZED, useFactory: getTitleRenderHook, multi: true, deps: [DOCUMENT]}, | 
					
						
							|  |  |  |   ] | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | class RenderHookModule { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @NgModule({ | 
					
						
							|  |  |  |   bootstrap: [MyServerApp], | 
					
						
							|  |  |  |   declarations: [MyServerApp], | 
					
						
							|  |  |  |   imports: [BrowserModule.withServerTransition({appId: 'render-hook'}), ServerModule], | 
					
						
							|  |  |  |   providers: [ | 
					
						
							|  |  |  |     {provide: BEFORE_APP_SERIALIZED, useFactory: getTitleRenderHook, multi: true, deps: [DOCUMENT]}, | 
					
						
							|  |  |  |     {provide: BEFORE_APP_SERIALIZED, useValue: exceptionRenderHook, multi: true}, | 
					
						
							|  |  |  |     {provide: BEFORE_APP_SERIALIZED, useFactory: getMetaRenderHook, multi: true, deps: [DOCUMENT]}, | 
					
						
							|  |  |  |   ] | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | class MultiRenderHookModule { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-05 16:30:45 -06:00
										 |  |  | @NgModule({ | 
					
						
							|  |  |  |   bootstrap: [MyServerApp], | 
					
						
							|  |  |  |   declarations: [MyServerApp], | 
					
						
							|  |  |  |   imports: [BrowserModule.withServerTransition({appId: 'render-hook'}), ServerModule], | 
					
						
							|  |  |  |   providers: [ | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       provide: BEFORE_APP_SERIALIZED, | 
					
						
							|  |  |  |       useFactory: getAsyncTitleRenderHook, | 
					
						
							|  |  |  |       multi: true, | 
					
						
							|  |  |  |       deps: [DOCUMENT] | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   ] | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | class AsyncRenderHookModule { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | @NgModule({ | 
					
						
							|  |  |  |   bootstrap: [MyServerApp], | 
					
						
							|  |  |  |   declarations: [MyServerApp], | 
					
						
							|  |  |  |   imports: [BrowserModule.withServerTransition({appId: 'render-hook'}), ServerModule], | 
					
						
							|  |  |  |   providers: [ | 
					
						
							|  |  |  |     {provide: BEFORE_APP_SERIALIZED, useFactory: getMetaRenderHook, multi: true, deps: [DOCUMENT]}, | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       provide: BEFORE_APP_SERIALIZED, | 
					
						
							|  |  |  |       useFactory: getAsyncTitleRenderHook, | 
					
						
							|  |  |  |       multi: true, | 
					
						
							|  |  |  |       deps: [DOCUMENT] | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     {provide: BEFORE_APP_SERIALIZED, useFactory: asyncRejectRenderHook, multi: true}, | 
					
						
							|  |  |  |   ] | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | class AsyncMultiRenderHookModule { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  | @Component({selector: 'app', template: `Works too!`}) | 
					
						
							|  |  |  | class MyServerApp2 { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @NgModule({declarations: [MyServerApp2], imports: [ServerModule], bootstrap: [MyServerApp2]}) | 
					
						
							|  |  |  | class ExampleModule2 { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-13 16:22:03 -04:00
										 |  |  | @Component({selector: 'app', template: ``}) | 
					
						
							|  |  |  | class TitleApp { | 
					
						
							|  |  |  |   constructor(private title: Title) {} | 
					
						
							|  |  |  |   ngOnInit() { this.title.setTitle('Test App Title'); } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @NgModule({declarations: [TitleApp], imports: [ServerModule], bootstrap: [TitleApp]}) | 
					
						
							|  |  |  | class TitleAppModule { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 21:42:55 -08:00
										 |  |  | @Component({selector: 'app', template: '{{text}}<h1 [textContent]="h1"></h1>'}) | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  | class MyAsyncServerApp { | 
					
						
							|  |  |  |   text = ''; | 
					
						
							| 
									
										
										
										
											2017-04-13 20:54:57 +02:00
										 |  |  |   h1 = ''; | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-14 20:48:01 -07:00
										 |  |  |   @HostListener('window:scroll') | 
					
						
							|  |  |  |   track() { console.error('scroll'); } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  |   ngOnInit() { | 
					
						
							| 
									
										
										
										
											2017-04-13 20:54:57 +02:00
										 |  |  |     Promise.resolve(null).then(() => setTimeout(() => { | 
					
						
							|  |  |  |                                  this.text = 'Works!'; | 
					
						
							|  |  |  |                                  this.h1 = 'fine'; | 
					
						
							|  |  |  |                                }, 10)); | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-22 16:06:21 -08:00
										 |  |  | @NgModule({ | 
					
						
							|  |  |  |   declarations: [MyAsyncServerApp], | 
					
						
							|  |  |  |   imports: [BrowserModule.withServerTransition({appId: 'async-server'}), ServerModule], | 
					
						
							|  |  |  |   bootstrap: [MyAsyncServerApp] | 
					
						
							|  |  |  | }) | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  | class AsyncServerModule { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-14 15:40:55 -07:00
										 |  |  | @Component({selector: 'app', template: '<svg><use xlink:href="#clear"></use></svg>'}) | 
					
						
							|  |  |  | class SVGComponent { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @NgModule({ | 
					
						
							|  |  |  |   declarations: [SVGComponent], | 
					
						
							|  |  |  |   imports: [BrowserModule.withServerTransition({appId: 'svg-server'}), ServerModule], | 
					
						
							|  |  |  |   bootstrap: [SVGComponent] | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | class SVGServerModule { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-13 17:31:03 -07:00
										 |  |  | @Component({ | 
					
						
							|  |  |  |   selector: 'app', | 
					
						
							| 
									
										
										
										
											2018-06-21 22:55:47 -07:00
										 |  |  |   template: `<div [@myAnimation]="state">{{text}}</div>`, | 
					
						
							| 
									
										
										
										
											2017-03-13 17:31:03 -07:00
										 |  |  |   animations: [trigger( | 
					
						
							|  |  |  |       'myAnimation', | 
					
						
							| 
									
										
										
										
											2018-06-21 22:55:47 -07:00
										 |  |  |       [ | 
					
						
							|  |  |  |         state('void', style({'opacity': '0'})), | 
					
						
							|  |  |  |         state('active', style({ | 
					
						
							|  |  |  |                 'opacity': '1',                       // simple supported property
 | 
					
						
							|  |  |  |                 'font-weight': 'bold',                // property with dashed name
 | 
					
						
							|  |  |  |                 'transform': 'translate3d(0, 0, 0)',  // not natively supported by Domino
 | 
					
						
							|  |  |  |               })), | 
					
						
							|  |  |  |         transition('void => *', [animate('0ms')]), | 
					
						
							|  |  |  |       ], )] | 
					
						
							| 
									
										
										
										
											2017-03-13 17:31:03 -07:00
										 |  |  | }) | 
					
						
							|  |  |  | class MyAnimationApp { | 
					
						
							| 
									
										
										
										
											2018-06-21 22:55:47 -07:00
										 |  |  |   state = 'active'; | 
					
						
							| 
									
										
										
										
											2017-08-10 18:08:49 -07:00
										 |  |  |   constructor(private builder: AnimationBuilder) {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-13 17:31:03 -07:00
										 |  |  |   text = 'Works!'; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @NgModule({ | 
					
						
							|  |  |  |   declarations: [MyAnimationApp], | 
					
						
							|  |  |  |   imports: [BrowserModule.withServerTransition({appId: 'anim-server'}), ServerModule], | 
					
						
							|  |  |  |   bootstrap: [MyAnimationApp] | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | class AnimationServerModule { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-28 00:20:53 -07:00
										 |  |  | @Component({ | 
					
						
							|  |  |  |   selector: 'app', | 
					
						
							|  |  |  |   template: `<div>Works!</div>`, | 
					
						
							|  |  |  |   styles: ['div {color: blue; } :host { color: red; }'] | 
					
						
							|  |  |  | }) | 
					
						
							| 
									
										
										
										
											2017-02-14 11:34:05 -08:00
										 |  |  | class MyStylesApp { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-22 16:06:21 -08:00
										 |  |  | @NgModule({ | 
					
						
							|  |  |  |   declarations: [MyStylesApp], | 
					
						
							|  |  |  |   imports: [BrowserModule.withServerTransition({appId: 'example-styles'}), ServerModule], | 
					
						
							|  |  |  |   bootstrap: [MyStylesApp] | 
					
						
							|  |  |  | }) | 
					
						
							| 
									
										
										
										
											2017-02-14 11:34:05 -08:00
										 |  |  | class ExampleStylesModule { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-18 12:45:47 -07:00
										 |  |  | @NgModule({ | 
					
						
							|  |  |  |   bootstrap: [MyServerApp], | 
					
						
							|  |  |  |   declarations: [MyServerApp], | 
					
						
							|  |  |  |   imports: [ServerModule, HttpClientModule, HttpClientTestingModule], | 
					
						
							|  |  |  | }) | 
					
						
							| 
									
										
										
										
											2018-03-10 17:14:58 +00:00
										 |  |  | export class HttpClientExampleModule { | 
					
						
							| 
									
										
										
										
											2017-07-18 12:45:47 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-31 10:35:51 -07:00
										 |  |  | @Injectable() | 
					
						
							|  |  |  | export class MyHttpInterceptor implements HttpInterceptor { | 
					
						
							|  |  |  |   constructor(private http: HttpClient) {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { | 
					
						
							|  |  |  |     return next.handle(req); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @NgModule({ | 
					
						
							|  |  |  |   bootstrap: [MyServerApp], | 
					
						
							|  |  |  |   declarations: [MyServerApp], | 
					
						
							|  |  |  |   imports: [ServerModule, HttpClientModule, HttpClientTestingModule], | 
					
						
							|  |  |  |   providers: [ | 
					
						
							|  |  |  |     {provide: HTTP_INTERCEPTORS, multi: true, useClass: MyHttpInterceptor}, | 
					
						
							|  |  |  |   ], | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | export class HttpInterceptorExampleModule { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-13 15:17:40 -08:00
										 |  |  | @Component({selector: 'app', template: `<img [src]="'link'">`}) | 
					
						
							|  |  |  | class ImageApp { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @NgModule({declarations: [ImageApp], imports: [ServerModule], bootstrap: [ImageApp]}) | 
					
						
							|  |  |  | class ImageExampleModule { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-14 14:09:24 -07:00
										 |  |  | @Component({ | 
					
						
							|  |  |  |   selector: 'app', | 
					
						
							|  |  |  |   template: 'Native works', | 
					
						
							|  |  |  |   encapsulation: ViewEncapsulation.Native, | 
					
						
							|  |  |  |   styles: [':host { color: red; }'] | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | class NativeEncapsulationApp { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @NgModule({ | 
					
						
							|  |  |  |   declarations: [NativeEncapsulationApp], | 
					
						
							|  |  |  |   imports: [BrowserModule.withServerTransition({appId: 'test'}), ServerModule], | 
					
						
							|  |  |  |   bootstrap: [NativeEncapsulationApp] | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | class NativeExampleModule { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-28 14:33:07 -06:00
										 |  |  | @Component({selector: 'my-child', template: 'Works!'}) | 
					
						
							|  |  |  | class MyChildComponent { | 
					
						
							| 
									
										
										
										
											2018-06-18 16:38:33 -07:00
										 |  |  |   // TODO(issue/24571): remove '!'.
 | 
					
						
							|  |  |  |   @Input() public attr !: boolean; | 
					
						
							| 
									
										
										
										
											2017-03-28 14:33:07 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @Component({selector: 'app', template: '<my-child [attr]="false"></my-child>'}) | 
					
						
							|  |  |  | class MyHostComponent { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @NgModule({ | 
					
						
							|  |  |  |   declarations: [MyHostComponent, MyChildComponent], | 
					
						
							|  |  |  |   bootstrap: [MyHostComponent], | 
					
						
							|  |  |  |   imports: [ServerModule, BrowserModule.withServerTransition({appId: 'false-attributes'})] | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | class FalseAttributesModule { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 21:42:55 -08:00
										 |  |  | @Component({selector: 'app', template: '<div [innerText]="foo"></div>'}) | 
					
						
							|  |  |  | class InnerTextComponent { | 
					
						
							|  |  |  |   foo = 'Some text'; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @NgModule({ | 
					
						
							|  |  |  |   declarations: [InnerTextComponent], | 
					
						
							|  |  |  |   bootstrap: [InnerTextComponent], | 
					
						
							|  |  |  |   imports: [ServerModule, BrowserModule.withServerTransition({appId: 'inner-text'})] | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | class InnerTextModule { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-19 13:58:23 -07:00
										 |  |  | @Component({selector: 'app', template: '<input [name]="name">'}) | 
					
						
							|  |  |  | class MyInputComponent { | 
					
						
							|  |  |  |   @Input() | 
					
						
							|  |  |  |   name = ''; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @NgModule({ | 
					
						
							|  |  |  |   declarations: [MyInputComponent], | 
					
						
							|  |  |  |   bootstrap: [MyInputComponent], | 
					
						
							|  |  |  |   imports: [ServerModule, BrowserModule.withServerTransition({appId: 'name-attributes'})] | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | class NameModule { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-24 16:04:04 -07:00
										 |  |  | @Component({selector: 'app', template: '<div [innerHTML]="html"></div>'}) | 
					
						
							|  |  |  | class HTMLTypesApp { | 
					
						
							|  |  |  |   html = '<b>foo</b> bar'; | 
					
						
							|  |  |  |   constructor(@Inject(DOCUMENT) doc: Document) {} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @NgModule({ | 
					
						
							|  |  |  |   declarations: [HTMLTypesApp], | 
					
						
							|  |  |  |   imports: [BrowserModule.withServerTransition({appId: 'inner-html'}), ServerModule], | 
					
						
							|  |  |  |   bootstrap: [HTMLTypesApp] | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | class HTMLTypesModule { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-11 00:18:55 -07:00
										 |  |  | const TEST_KEY = makeStateKey<number>('test'); | 
					
						
							|  |  |  | const STRING_KEY = makeStateKey<string>('testString'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @Component({selector: 'app', template: 'Works!'}) | 
					
						
							|  |  |  | class TransferComponent { | 
					
						
							|  |  |  |   constructor(private transferStore: TransferState) {} | 
					
						
							|  |  |  |   ngOnInit() { this.transferStore.set(TEST_KEY, 10); } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @Component({selector: 'esc-app', template: 'Works!'}) | 
					
						
							|  |  |  | class EscapedComponent { | 
					
						
							|  |  |  |   constructor(private transferStore: TransferState) {} | 
					
						
							|  |  |  |   ngOnInit() { | 
					
						
							|  |  |  |     this.transferStore.set(STRING_KEY, '</script><script>alert(\'Hello&\' + "World");'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @NgModule({ | 
					
						
							|  |  |  |   bootstrap: [TransferComponent], | 
					
						
							|  |  |  |   declarations: [TransferComponent], | 
					
						
							|  |  |  |   imports: [ | 
					
						
							|  |  |  |     BrowserModule.withServerTransition({appId: 'transfer'}), | 
					
						
							|  |  |  |     ServerModule, | 
					
						
							|  |  |  |     ServerTransferStateModule, | 
					
						
							|  |  |  |   ] | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | class TransferStoreModule { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @NgModule({ | 
					
						
							|  |  |  |   bootstrap: [EscapedComponent], | 
					
						
							|  |  |  |   declarations: [EscapedComponent], | 
					
						
							|  |  |  |   imports: [ | 
					
						
							|  |  |  |     BrowserModule.withServerTransition({appId: 'transfer'}), | 
					
						
							|  |  |  |     ServerModule, | 
					
						
							|  |  |  |     ServerTransferStateModule, | 
					
						
							|  |  |  |   ] | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | class EscapedTransferStoreModule { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-31 19:29:02 -07:00
										 |  |  | @Component({selector: 'app', template: '<input [hidden]="true"><input [hidden]="false">'}) | 
					
						
							|  |  |  | class MyHiddenComponent { | 
					
						
							|  |  |  |   @Input() | 
					
						
							|  |  |  |   name = ''; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @NgModule({ | 
					
						
							|  |  |  |   declarations: [MyHiddenComponent], | 
					
						
							|  |  |  |   bootstrap: [MyHiddenComponent], | 
					
						
							|  |  |  |   imports: [ServerModule, BrowserModule.withServerTransition({appId: 'hidden-attributes'})] | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | class HiddenModule { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-17 15:10:54 -08:00
										 |  |  | (function() { | 
					
						
							| 
									
										
										
										
											2016-06-14 19:49:25 -07:00
										 |  |  |   if (getDOM().supportsDOMEvents()) return;  // NODE only
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-10 18:48:04 -06:00
										 |  |  |   describe('platform-server integration', () => { | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  |     beforeEach(() => { | 
					
						
							|  |  |  |       if (getPlatform()) destroyPlatform(); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2016-06-14 19:49:25 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it('should bootstrap', async(() => { | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  |          const platform = platformDynamicServer( | 
					
						
							|  |  |  |              [{provide: INITIAL_CONFIG, useValue: {document: '<app></app>'}}]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |          platform.bootstrapModule(ExampleModule).then((moduleRef) => { | 
					
						
							| 
									
										
										
										
											2017-02-22 16:49:46 -08:00
										 |  |  |            expect(isPlatformServer(moduleRef.injector.get(PLATFORM_ID))).toBe(true); | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  |            const doc = moduleRef.injector.get(DOCUMENT); | 
					
						
							| 
									
										
										
										
											2017-03-14 20:48:01 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-29 21:24:33 -07:00
										 |  |  |            expect(doc.head).toBe(doc.querySelector('head') !); | 
					
						
							|  |  |  |            expect(doc.body).toBe(doc.querySelector('body') !); | 
					
						
							| 
									
										
										
										
											2017-03-14 20:48:01 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-23 13:28:33 -07:00
										 |  |  |            expect(doc.documentElement.textContent).toEqual('Works!'); | 
					
						
							| 
									
										
										
										
											2017-03-14 20:48:01 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  |            platform.destroy(); | 
					
						
							|  |  |  |          }); | 
					
						
							| 
									
										
										
										
											2016-06-14 19:49:25 -07:00
										 |  |  |        })); | 
					
						
							| 
									
										
										
										
											2017-02-10 16:29:30 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  |     it('should allow multiple platform instances', async(() => { | 
					
						
							|  |  |  |          const platform = platformDynamicServer( | 
					
						
							|  |  |  |              [{provide: INITIAL_CONFIG, useValue: {document: '<app></app>'}}]); | 
					
						
							| 
									
										
										
										
											2017-02-14 16:14:40 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  |          const platform2 = platformDynamicServer( | 
					
						
							|  |  |  |              [{provide: INITIAL_CONFIG, useValue: {document: '<app></app>'}}]); | 
					
						
							| 
									
										
										
										
											2017-02-14 16:14:40 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  |          platform.bootstrapModule(ExampleModule).then((moduleRef) => { | 
					
						
							|  |  |  |            const doc = moduleRef.injector.get(DOCUMENT); | 
					
						
							| 
									
										
										
										
											2019-08-23 13:28:33 -07:00
										 |  |  |            expect(doc.documentElement.textContent).toEqual('Works!'); | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  |            platform.destroy(); | 
					
						
							|  |  |  |          }); | 
					
						
							| 
									
										
										
										
											2017-02-14 16:14:40 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  |          platform2.bootstrapModule(ExampleModule2).then((moduleRef) => { | 
					
						
							|  |  |  |            const doc = moduleRef.injector.get(DOCUMENT); | 
					
						
							| 
									
										
										
										
											2019-08-23 13:28:33 -07:00
										 |  |  |            expect(doc.documentElement.textContent).toEqual('Works too!'); | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  |            platform2.destroy(); | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |        })); | 
					
						
							| 
									
										
										
										
											2017-02-14 16:14:40 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-13 16:22:03 -04:00
										 |  |  |     it('adds title to the document using Title service', async(() => { | 
					
						
							|  |  |  |          const platform = platformDynamicServer([{ | 
					
						
							|  |  |  |            provide: INITIAL_CONFIG, | 
					
						
							|  |  |  |            useValue: | 
					
						
							|  |  |  |                {document: '<html><head><title></title></head><body><app></app></body></html>'} | 
					
						
							|  |  |  |          }]); | 
					
						
							|  |  |  |          platform.bootstrapModule(TitleAppModule).then(ref => { | 
					
						
							|  |  |  |            const state = ref.injector.get(PlatformState); | 
					
						
							|  |  |  |            const doc = ref.injector.get(DOCUMENT); | 
					
						
							| 
									
										
										
										
											2019-08-29 21:24:33 -07:00
										 |  |  |            const title = doc.querySelector('title') !; | 
					
						
							| 
									
										
										
										
											2019-08-23 13:28:33 -07:00
										 |  |  |            expect(title.textContent).toBe('Test App Title'); | 
					
						
							| 
									
										
										
										
											2017-03-13 16:22:03 -04:00
										 |  |  |            expect(state.renderToString()).toContain('<title>Test App Title</title>'); | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-14 18:38:24 -04:00
										 |  |  |     it('should get base href from document', async(() => { | 
					
						
							|  |  |  |          const platform = platformDynamicServer([{ | 
					
						
							|  |  |  |            provide: INITIAL_CONFIG, | 
					
						
							|  |  |  |            useValue: | 
					
						
							|  |  |  |                {document: '<html><head><base href="/"></head><body><app></app></body></html>'} | 
					
						
							|  |  |  |          }]); | 
					
						
							|  |  |  |          platform.bootstrapModule(ExampleModule).then((moduleRef) => { | 
					
						
							|  |  |  |            const location = moduleRef.injector.get(PlatformLocation); | 
					
						
							|  |  |  |            expect(location.getBaseHrefFromDOM()).toEqual('/'); | 
					
						
							|  |  |  |            platform.destroy(); | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-16 16:26:21 +01:00
										 |  |  |     it('adds styles with ng-transition attribute', async(() => { | 
					
						
							|  |  |  |          const platform = platformDynamicServer([{ | 
					
						
							|  |  |  |            provide: INITIAL_CONFIG, | 
					
						
							|  |  |  |            useValue: {document: '<html><head></head><body><app></app></body></html>'} | 
					
						
							|  |  |  |          }]); | 
					
						
							|  |  |  |          platform.bootstrapModule(ExampleStylesModule).then(ref => { | 
					
						
							|  |  |  |            const doc = ref.injector.get(DOCUMENT); | 
					
						
							| 
									
										
										
										
											2019-08-30 12:52:48 -07:00
										 |  |  |            const head = doc.getElementsByTagName('head')[0]; | 
					
						
							| 
									
										
										
										
											2018-11-16 16:26:21 +01:00
										 |  |  |            const styles: any[] = head.children as any; | 
					
						
							|  |  |  |            expect(styles.length).toBe(1); | 
					
						
							| 
									
										
										
										
											2019-08-23 13:28:33 -07:00
										 |  |  |            expect(styles[0].textContent).toContain('color: red'); | 
					
						
							| 
									
										
										
										
											2019-08-30 12:52:48 -07:00
										 |  |  |            expect(styles[0].getAttribute('ng-transition')).toBe('example-styles'); | 
					
						
							| 
									
										
										
										
											2018-11-16 16:26:21 +01:00
										 |  |  |          }); | 
					
						
							|  |  |  |        })); | 
					
						
							| 
									
										
										
										
											2017-02-14 11:34:05 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-13 15:17:40 -08:00
										 |  |  |     it('copies known properties to attributes', async(() => { | 
					
						
							|  |  |  |          const platform = platformDynamicServer( | 
					
						
							|  |  |  |              [{provide: INITIAL_CONFIG, useValue: {document: '<app></app>'}}]); | 
					
						
							|  |  |  |          platform.bootstrapModule(ImageExampleModule).then(ref => { | 
					
						
							|  |  |  |            const appRef: ApplicationRef = ref.injector.get(ApplicationRef); | 
					
						
							|  |  |  |            const app = appRef.components[0].location.nativeElement; | 
					
						
							| 
									
										
										
										
											2019-08-30 12:52:48 -07:00
										 |  |  |            const img = app.getElementsByTagName('img')[0] as any; | 
					
						
							| 
									
										
										
										
											2017-08-08 02:17:40 -07:00
										 |  |  |            expect(img.attributes['src'].value).toEqual('link'); | 
					
						
							| 
									
										
										
										
											2017-02-13 15:17:40 -08:00
										 |  |  |          }); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  |     describe('PlatformLocation', () => { | 
					
						
							|  |  |  |       it('is injectable', async(() => { | 
					
						
							|  |  |  |            const platform = platformDynamicServer( | 
					
						
							|  |  |  |                [{provide: INITIAL_CONFIG, useValue: {document: '<app></app>'}}]); | 
					
						
							|  |  |  |            platform.bootstrapModule(ExampleModule).then(appRef => { | 
					
						
							|  |  |  |              const location: PlatformLocation = appRef.injector.get(PlatformLocation); | 
					
						
							|  |  |  |              expect(location.pathname).toBe('/'); | 
					
						
							|  |  |  |              platform.destroy(); | 
					
						
							|  |  |  |            }); | 
					
						
							|  |  |  |          })); | 
					
						
							| 
									
										
										
										
											2017-02-14 19:48:48 -08:00
										 |  |  |       it('is configurable via INITIAL_CONFIG', () => { | 
					
						
							|  |  |  |         platformDynamicServer([{ | 
					
						
							|  |  |  |           provide: INITIAL_CONFIG, | 
					
						
							|  |  |  |           useValue: {document: '<app></app>', url: 'http://test.com/deep/path?query#hash'} | 
					
						
							|  |  |  |         }]) | 
					
						
							|  |  |  |             .bootstrapModule(ExampleModule) | 
					
						
							|  |  |  |             .then(appRef => { | 
					
						
							|  |  |  |               const location: PlatformLocation = appRef.injector.get(PlatformLocation); | 
					
						
							|  |  |  |               expect(location.pathname).toBe('/deep/path'); | 
					
						
							|  |  |  |               expect(location.search).toBe('?query'); | 
					
						
							|  |  |  |               expect(location.hash).toBe('#hash'); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-02-11 10:56:50 -08:00
										 |  |  |       it('parses component pieces of a URL', () => { | 
					
						
							|  |  |  |         platformDynamicServer([{ | 
					
						
							|  |  |  |           provide: INITIAL_CONFIG, | 
					
						
							|  |  |  |           useValue: {document: '<app></app>', url: 'http://test.com:80/deep/path?query#hash'} | 
					
						
							|  |  |  |         }]) | 
					
						
							|  |  |  |             .bootstrapModule(ExampleModule) | 
					
						
							|  |  |  |             .then(appRef => { | 
					
						
							|  |  |  |               const location: PlatformLocation = appRef.injector.get(PlatformLocation); | 
					
						
							|  |  |  |               expect(location.hostname).toBe('test.com'); | 
					
						
							|  |  |  |               expect(location.protocol).toBe('http:'); | 
					
						
							|  |  |  |               expect(location.port).toBe('80'); | 
					
						
							|  |  |  |               expect(location.pathname).toBe('/deep/path'); | 
					
						
							|  |  |  |               expect(location.search).toBe('?query'); | 
					
						
							|  |  |  |               expect(location.hash).toBe('#hash'); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-02-16 10:18:55 -08:00
										 |  |  |       it('handles empty search and hash portions of the url', () => { | 
					
						
							|  |  |  |         platformDynamicServer([{ | 
					
						
							|  |  |  |           provide: INITIAL_CONFIG, | 
					
						
							|  |  |  |           useValue: {document: '<app></app>', url: 'http://test.com/deep/path'} | 
					
						
							|  |  |  |         }]) | 
					
						
							|  |  |  |             .bootstrapModule(ExampleModule) | 
					
						
							|  |  |  |             .then(appRef => { | 
					
						
							|  |  |  |               const location: PlatformLocation = appRef.injector.get(PlatformLocation); | 
					
						
							|  |  |  |               expect(location.pathname).toBe('/deep/path'); | 
					
						
							|  |  |  |               expect(location.search).toBe(''); | 
					
						
							|  |  |  |               expect(location.hash).toBe(''); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  |       it('pushState causes the URL to update', async(() => { | 
					
						
							|  |  |  |            const platform = platformDynamicServer( | 
					
						
							|  |  |  |                [{provide: INITIAL_CONFIG, useValue: {document: '<app></app>'}}]); | 
					
						
							|  |  |  |            platform.bootstrapModule(ExampleModule).then(appRef => { | 
					
						
							|  |  |  |              const location: PlatformLocation = appRef.injector.get(PlatformLocation); | 
					
						
							|  |  |  |              location.pushState(null, 'Test', '/foo#bar'); | 
					
						
							|  |  |  |              expect(location.pathname).toBe('/foo'); | 
					
						
							|  |  |  |              expect(location.hash).toBe('#bar'); | 
					
						
							|  |  |  |              platform.destroy(); | 
					
						
							|  |  |  |            }); | 
					
						
							|  |  |  |          })); | 
					
						
							|  |  |  |       it('allows subscription to the hash state', done => { | 
					
						
							|  |  |  |         const platform = | 
					
						
							|  |  |  |             platformDynamicServer([{provide: INITIAL_CONFIG, useValue: {document: '<app></app>'}}]); | 
					
						
							|  |  |  |         platform.bootstrapModule(ExampleModule).then(appRef => { | 
					
						
							|  |  |  |           const location: PlatformLocation = appRef.injector.get(PlatformLocation); | 
					
						
							|  |  |  |           expect(location.pathname).toBe('/'); | 
					
						
							|  |  |  |           location.onHashChange((e: any) => { | 
					
						
							|  |  |  |             expect(e.type).toBe('hashchange'); | 
					
						
							|  |  |  |             expect(e.oldUrl).toBe('/'); | 
					
						
							|  |  |  |             expect(e.newUrl).toBe('/foo#bar'); | 
					
						
							|  |  |  |             platform.destroy(); | 
					
						
							|  |  |  |             done(); | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |           location.pushState(null, 'Test', '/foo#bar'); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-02-14 16:14:40 -08:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  |     describe('render', () => { | 
					
						
							|  |  |  |       let doc: string; | 
					
						
							|  |  |  |       let called: boolean; | 
					
						
							|  |  |  |       let expectedOutput = | 
					
						
							| 
									
										
										
										
											2019-02-04 21:42:55 -08:00
										 |  |  |           '<html><head></head><body><app ng-version="0.0.0-PLACEHOLDER">Works!<h1 textcontent="fine">fine</h1></app></body></html>'; | 
					
						
							| 
									
										
										
										
											2017-02-14 16:14:40 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  |       beforeEach(() => { | 
					
						
							|  |  |  |         // PlatformConfig takes in a parsed document so that it can be cached across requests.
 | 
					
						
							|  |  |  |         doc = '<html><head></head><body><app></app></body></html>'; | 
					
						
							|  |  |  |         called = false; | 
					
						
							| 
									
										
										
										
											2018-11-21 16:50:35 +01:00
										 |  |  |         // We use `window` and `document` directly in some parts of render3 for ivy
 | 
					
						
							|  |  |  |         // Only set it to undefined for legacy
 | 
					
						
							|  |  |  |         if (!ivyEnabled) { | 
					
						
							|  |  |  |           (global as any)['window'] = undefined; | 
					
						
							|  |  |  |           (global as any)['document'] = undefined; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  |       }); | 
					
						
							|  |  |  |       afterEach(() => { expect(called).toBe(true); }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-19 21:06:41 +01:00
										 |  |  |       it('using long form should work', async(() => { | 
					
						
							|  |  |  |            const platform = | 
					
						
							|  |  |  |                platformDynamicServer([{provide: INITIAL_CONFIG, useValue: {document: doc}}]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |            platform.bootstrapModule(AsyncServerModule) | 
					
						
							|  |  |  |                .then((moduleRef) => { | 
					
						
							|  |  |  |                  const applicationRef: ApplicationRef = moduleRef.injector.get(ApplicationRef); | 
					
						
							|  |  |  |                  return applicationRef.isStable.pipe(first((isStable: boolean) => isStable)) | 
					
						
							|  |  |  |                      .toPromise(); | 
					
						
							|  |  |  |                }) | 
					
						
							|  |  |  |                .then((b) => { | 
					
						
							|  |  |  |                  expect(platform.injector.get(PlatformState).renderToString()).toBe(expectedOutput); | 
					
						
							|  |  |  |                  platform.destroy(); | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  |                  called = true; | 
					
						
							|  |  |  |                }); | 
					
						
							| 
									
										
										
										
											2018-11-19 21:06:41 +01:00
										 |  |  |          })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('using renderModule should work', async(() => { | 
					
						
							|  |  |  |            renderModule(AsyncServerModule, {document: doc}).then(output => { | 
					
						
							|  |  |  |              expect(output).toBe(expectedOutput); | 
					
						
							|  |  |  |              called = true; | 
					
						
							|  |  |  |            }); | 
					
						
							|  |  |  |          })); | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 21:42:55 -08:00
										 |  |  |       modifiedInIvy('Will not support binding to innerText in Ivy since domino does not') | 
					
						
							|  |  |  |           .it('should support binding to innerText', async(() => { | 
					
						
							|  |  |  |                 renderModule(InnerTextModule, {document: doc}).then(output => { | 
					
						
							|  |  |  |                   expect(output).toBe( | 
					
						
							|  |  |  |                       '<html><head></head><body><app ng-version="0.0.0-PLACEHOLDER"><div innertext="Some text">Some text</div></app></body></html>'); | 
					
						
							|  |  |  |                   called = true; | 
					
						
							|  |  |  |                 }); | 
					
						
							|  |  |  |               })); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  |       it('using renderModuleFactory should work', | 
					
						
							|  |  |  |          async(inject([PlatformRef], (defaultPlatform: PlatformRef) => { | 
					
						
							|  |  |  |            const compilerFactory: CompilerFactory = | 
					
						
							|  |  |  |                defaultPlatform.injector.get(CompilerFactory, null); | 
					
						
							|  |  |  |            const moduleFactory = | 
					
						
							|  |  |  |                compilerFactory.createCompiler().compileModuleSync(AsyncServerModule); | 
					
						
							|  |  |  |            renderModuleFactory(moduleFactory, {document: doc}).then(output => { | 
					
						
							|  |  |  |              expect(output).toBe(expectedOutput); | 
					
						
							|  |  |  |              called = true; | 
					
						
							|  |  |  |            }); | 
					
						
							|  |  |  |          }))); | 
					
						
							| 
									
										
										
										
											2017-03-14 15:40:55 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-22 23:21:53 +01:00
										 |  |  |       it('works with SVG elements', async(() => { | 
					
						
							|  |  |  |            renderModule(SVGServerModule, {document: doc}).then(output => { | 
					
						
							|  |  |  |              expect(output).toBe( | 
					
						
							|  |  |  |                  '<html><head></head><body><app ng-version="0.0.0-PLACEHOLDER">' + | 
					
						
							|  |  |  |                  '<svg><use xlink:href="#clear"></use></svg></app></body></html>'); | 
					
						
							|  |  |  |              called = true; | 
					
						
							|  |  |  |            }); | 
					
						
							|  |  |  |          })); | 
					
						
							| 
									
										
										
										
											2018-11-16 16:14:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-12 21:21:52 -08:00
										 |  |  |       it('works with animation', async(() => { | 
					
						
							|  |  |  |            renderModule(AnimationServerModule, {document: doc}).then(output => { | 
					
						
							|  |  |  |              expect(output).toContain('Works!'); | 
					
						
							|  |  |  |              expect(output).toContain('ng-trigger-myAnimation'); | 
					
						
							|  |  |  |              expect(output).toContain('opacity:1;'); | 
					
						
							|  |  |  |              expect(output).toContain('transform:translate3d(0 , 0 , 0);'); | 
					
						
							|  |  |  |              expect(output).toContain('font-weight:bold;'); | 
					
						
							|  |  |  |              called = true; | 
					
						
							|  |  |  |            }); | 
					
						
							|  |  |  |          })); | 
					
						
							| 
									
										
										
										
											2017-03-14 14:09:24 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       it('should handle ViewEncapsulation.Native', async(() => { | 
					
						
							|  |  |  |            renderModule(NativeExampleModule, {document: doc}).then(output => { | 
					
						
							|  |  |  |              expect(output).not.toBe(''); | 
					
						
							|  |  |  |              expect(output).toContain('color: red'); | 
					
						
							|  |  |  |              called = true; | 
					
						
							|  |  |  |            }); | 
					
						
							|  |  |  |          })); | 
					
						
							| 
									
										
										
										
											2017-03-28 14:33:07 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-28 00:20:53 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-19 21:06:41 +01:00
										 |  |  |       it('sets a prefix for the _nghost and _ngcontent attributes', async(() => { | 
					
						
							|  |  |  |            renderModule(ExampleStylesModule, {document: doc}).then(output => { | 
					
						
							|  |  |  |              expect(output).toMatch( | 
					
						
							|  |  |  |                  /<html><head><style ng-transition="example-styles">div\[_ngcontent-sc\d+\] {color: blue; } \[_nghost-sc\d+\] { color: red; }<\/style><\/head><body><app _nghost-sc\d+="" ng-version="0.0.0-PLACEHOLDER"><div _ngcontent-sc\d+="">Works!<\/div><\/app><\/body><\/html>/); | 
					
						
							|  |  |  |              called = true; | 
					
						
							|  |  |  |            }); | 
					
						
							|  |  |  |          })); | 
					
						
							| 
									
										
										
										
											2018-11-16 16:14:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
											  
											
												fix(ivy): TestBed should not clobber compilation of global-scope modules (#28033)
When an @NgModule decorator executes, the module is added to a queue in
render3/jit/module.ts. Reading an ngComponentDef property causes this queue
to be flushed, ensuring that the component gets the correct module scope
applied.
In before_each.ts, a global beforeEach is added to all Angular tests which
calls TestBed.resetTestingModule() prior to running each test. This in turn
clears the module compilation queue (which is correct behavior, as modules
declared within the test should not leak outside of it via the queue).
So far this is okay. But before the first test runs, the module compilation
queue is full of modules declared in global scope. No definitions have been
read, so no flushes of the queue have been triggered. The global beforeEach
triggers a reset of the queue, aborting all of the in-progress global
compilation, breaking those classes when they're later used in tests.
This commit adds logic to TestBedRender3 to respect the state of the module
queue before the TestBed is first initialized or reset. The queue is flushed
prior to such an operation to ensure global compilation is allowed to finish
properly.
With this fix, a platform-server test now passes (previously the <my-child>
element was not detected as a component, because the encompassing module
never finished compilation.
FW-887 #resolve
PR Close #28033
											
										 
											2019-01-09 16:24:36 -08:00
										 |  |  |       it('should handle false values on attributes', async(() => { | 
					
						
							|  |  |  |            renderModule(FalseAttributesModule, {document: doc}).then(output => { | 
					
						
							|  |  |  |              expect(output).toBe( | 
					
						
							|  |  |  |                  '<html><head></head><body><app ng-version="0.0.0-PLACEHOLDER">' + | 
					
						
							|  |  |  |                  '<my-child ng-reflect-attr="false">Works!</my-child></app></body></html>'); | 
					
						
							|  |  |  |              called = true; | 
					
						
							|  |  |  |            }); | 
					
						
							|  |  |  |          })); | 
					
						
							| 
									
										
										
										
											2018-11-16 16:14:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-21 13:40:27 +01:00
										 |  |  |       it('should handle element property "name"', async(() => { | 
					
						
							|  |  |  |            renderModule(NameModule, {document: doc}).then(output => { | 
					
						
							|  |  |  |              expect(output).toBe( | 
					
						
							|  |  |  |                  '<html><head></head><body><app ng-version="0.0.0-PLACEHOLDER">' + | 
					
						
							|  |  |  |                  '<input name=""></app></body></html>'); | 
					
						
							|  |  |  |              called = true; | 
					
						
							|  |  |  |            }); | 
					
						
							|  |  |  |          })); | 
					
						
							| 
									
										
										
										
											2018-11-16 16:14:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-21 16:50:35 +01:00
										 |  |  |       it('should work with sanitizer to handle "innerHTML"', async(() => { | 
					
						
							|  |  |  |            // Clear out any global states. These should be set when platform-server
 | 
					
						
							|  |  |  |            // is initialized.
 | 
					
						
							|  |  |  |            (global as any).Node = undefined; | 
					
						
							|  |  |  |            (global as any).Document = undefined; | 
					
						
							|  |  |  |            renderModule(HTMLTypesModule, {document: doc}).then(output => { | 
					
						
							|  |  |  |              expect(output).toBe( | 
					
						
							|  |  |  |                  '<html><head></head><body><app ng-version="0.0.0-PLACEHOLDER">' + | 
					
						
							|  |  |  |                  '<div><b>foo</b> bar</div></app></body></html>'); | 
					
						
							|  |  |  |              called = true; | 
					
						
							|  |  |  |            }); | 
					
						
							|  |  |  |          })); | 
					
						
							| 
									
										
										
										
											2018-11-16 16:14:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-21 13:40:27 +01:00
										 |  |  |       it('should handle element property "hidden"', async(() => { | 
					
						
							|  |  |  |            renderModule(HiddenModule, {document: doc}).then(output => { | 
					
						
							|  |  |  |              expect(output).toBe( | 
					
						
							|  |  |  |                  '<html><head></head><body><app ng-version="0.0.0-PLACEHOLDER">' + | 
					
						
							|  |  |  |                  '<input hidden=""><input></app></body></html>'); | 
					
						
							|  |  |  |              called = true; | 
					
						
							|  |  |  |            }); | 
					
						
							|  |  |  |          })); | 
					
						
							| 
									
										
										
										
											2018-11-16 16:14:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-19 21:06:41 +01:00
										 |  |  |       it('should call render hook', async(() => { | 
					
						
							|  |  |  |            renderModule(RenderHookModule, {document: doc}).then(output => { | 
					
						
							|  |  |  |              // title should be added by the render hook.
 | 
					
						
							|  |  |  |              expect(output).toBe( | 
					
						
							|  |  |  |                  '<html><head><title>RenderHook</title></head><body>' + | 
					
						
							|  |  |  |                  '<app ng-version="0.0.0-PLACEHOLDER">Works!</app></body></html>'); | 
					
						
							|  |  |  |              called = true; | 
					
						
							|  |  |  |            }); | 
					
						
							|  |  |  |          })); | 
					
						
							| 
									
										
										
										
											2018-11-16 16:14:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-19 21:06:41 +01:00
										 |  |  |       it('should call multiple render hooks', async(() => { | 
					
						
							|  |  |  |            const consoleSpy = spyOn(console, 'warn'); | 
					
						
							|  |  |  |            renderModule(MultiRenderHookModule, {document: doc}).then(output => { | 
					
						
							|  |  |  |              // title should be added by the render hook.
 | 
					
						
							|  |  |  |              expect(output).toBe( | 
					
						
							|  |  |  |                  '<html><head><title>RenderHook</title><meta name="description"></head>' + | 
					
						
							|  |  |  |                  '<body><app ng-version="0.0.0-PLACEHOLDER">Works!</app></body></html>'); | 
					
						
							|  |  |  |              expect(consoleSpy).toHaveBeenCalled(); | 
					
						
							|  |  |  |              called = true; | 
					
						
							|  |  |  |            }); | 
					
						
							|  |  |  |          })); | 
					
						
							| 
									
										
										
										
											2019-03-05 16:30:45 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |       it('should call async render hooks', async(() => { | 
					
						
							|  |  |  |            renderModule(AsyncRenderHookModule, {document: doc}).then(output => { | 
					
						
							|  |  |  |              // title should be added by the render hook.
 | 
					
						
							|  |  |  |              expect(output).toBe( | 
					
						
							|  |  |  |                  '<html><head><title>AsyncRenderHook</title></head><body>' + | 
					
						
							|  |  |  |                  '<app ng-version="0.0.0-PLACEHOLDER">Works!</app></body></html>'); | 
					
						
							|  |  |  |              called = true; | 
					
						
							|  |  |  |            }); | 
					
						
							|  |  |  |          })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should call multiple async and sync render hooks', async(() => { | 
					
						
							|  |  |  |            const consoleSpy = spyOn(console, 'warn'); | 
					
						
							|  |  |  |            renderModule(AsyncMultiRenderHookModule, {document: doc}).then(output => { | 
					
						
							|  |  |  |              // title should be added by the render hook.
 | 
					
						
							|  |  |  |              expect(output).toBe( | 
					
						
							|  |  |  |                  '<html><head><meta name="description"><title>AsyncRenderHook</title></head>' + | 
					
						
							|  |  |  |                  '<body><app ng-version="0.0.0-PLACEHOLDER">Works!</app></body></html>'); | 
					
						
							|  |  |  |              expect(consoleSpy).toHaveBeenCalled(); | 
					
						
							|  |  |  |              called = true; | 
					
						
							|  |  |  |            }); | 
					
						
							|  |  |  |          })); | 
					
						
							| 
									
										
										
										
											2017-02-12 09:16:23 -08:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-02-10 17:00:27 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-18 12:45:47 -07:00
										 |  |  |     describe('HttpClient', () => { | 
					
						
							|  |  |  |       it('can inject HttpClient', async(() => { | 
					
						
							|  |  |  |            const platform = platformDynamicServer( | 
					
						
							|  |  |  |                [{provide: INITIAL_CONFIG, useValue: {document: '<app></app>'}}]); | 
					
						
							| 
									
										
										
										
											2018-03-10 17:14:58 +00:00
										 |  |  |            platform.bootstrapModule(HttpClientExampleModule).then(ref => { | 
					
						
							| 
									
										
										
										
											2017-07-18 12:45:47 -07:00
										 |  |  |              expect(ref.injector.get(HttpClient) instanceof HttpClient).toBeTruthy(); | 
					
						
							|  |  |  |            }); | 
					
						
							|  |  |  |          })); | 
					
						
							| 
									
										
										
										
											2018-11-16 16:14:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-18 12:45:47 -07:00
										 |  |  |       it('can make HttpClient requests', async(() => { | 
					
						
							|  |  |  |            const platform = platformDynamicServer( | 
					
						
							|  |  |  |                [{provide: INITIAL_CONFIG, useValue: {document: '<app></app>'}}]); | 
					
						
							| 
									
										
										
										
											2018-03-10 17:14:58 +00:00
										 |  |  |            platform.bootstrapModule(HttpClientExampleModule).then(ref => { | 
					
						
							| 
									
										
										
										
											2017-07-18 12:45:47 -07:00
										 |  |  |              const mock = ref.injector.get(HttpTestingController) as HttpTestingController; | 
					
						
							|  |  |  |              const http = ref.injector.get(HttpClient); | 
					
						
							| 
									
										
										
										
											2017-11-15 08:43:35 -08:00
										 |  |  |              ref.injector.get<NgZone>(NgZone).run(() => { | 
					
						
							| 
									
										
										
										
											2019-08-19 15:05:29 -07:00
										 |  |  |                http.get<string>('http://localhost/testing').subscribe((body: string) => { | 
					
						
							| 
									
										
										
										
											2017-07-18 12:45:47 -07:00
										 |  |  |                  NgZone.assertInAngularZone(); | 
					
						
							|  |  |  |                  expect(body).toEqual('success!'); | 
					
						
							|  |  |  |                }); | 
					
						
							|  |  |  |                mock.expectOne('http://localhost/testing').flush('success!'); | 
					
						
							|  |  |  |              }); | 
					
						
							|  |  |  |            }); | 
					
						
							|  |  |  |          })); | 
					
						
							| 
									
										
										
										
											2018-11-16 16:14:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-18 12:45:47 -07:00
										 |  |  |       it('requests are macrotasks', async(() => { | 
					
						
							|  |  |  |            const platform = platformDynamicServer( | 
					
						
							|  |  |  |                [{provide: INITIAL_CONFIG, useValue: {document: '<app></app>'}}]); | 
					
						
							| 
									
										
										
										
											2018-03-10 17:14:58 +00:00
										 |  |  |            platform.bootstrapModule(HttpClientExampleModule).then(ref => { | 
					
						
							| 
									
										
										
										
											2017-07-18 12:45:47 -07:00
										 |  |  |              const mock = ref.injector.get(HttpTestingController) as HttpTestingController; | 
					
						
							|  |  |  |              const http = ref.injector.get(HttpClient); | 
					
						
							| 
									
										
										
										
											2019-08-19 15:05:29 -07:00
										 |  |  |              ref.injector.get(NgZone).run(() => { | 
					
						
							|  |  |  |                http.get<string>('http://localhost/testing').subscribe((body: string) => { | 
					
						
							| 
									
										
										
										
											2017-07-18 12:45:47 -07:00
										 |  |  |                  expect(body).toEqual('success!'); | 
					
						
							|  |  |  |                }); | 
					
						
							| 
									
										
										
										
											2017-11-15 08:43:35 -08:00
										 |  |  |                expect(ref.injector.get<NgZone>(NgZone).hasPendingMacrotasks).toBeTruthy(); | 
					
						
							| 
									
										
										
										
											2017-07-18 12:45:47 -07:00
										 |  |  |                mock.expectOne('http://localhost/testing').flush('success!'); | 
					
						
							| 
									
										
										
										
											2017-11-15 08:43:35 -08:00
										 |  |  |                expect(ref.injector.get<NgZone>(NgZone).hasPendingMacrotasks).toBeFalsy(); | 
					
						
							| 
									
										
										
										
											2017-07-18 12:45:47 -07:00
										 |  |  |              }); | 
					
						
							|  |  |  |            }); | 
					
						
							|  |  |  |          })); | 
					
						
							| 
									
										
										
										
											2018-11-16 16:14:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-31 10:35:51 -07:00
										 |  |  |       it('can use HttpInterceptor that injects HttpClient', () => { | 
					
						
							|  |  |  |         const platform = | 
					
						
							|  |  |  |             platformDynamicServer([{provide: INITIAL_CONFIG, useValue: {document: '<app></app>'}}]); | 
					
						
							|  |  |  |         platform.bootstrapModule(HttpInterceptorExampleModule).then(ref => { | 
					
						
							|  |  |  |           const mock = ref.injector.get(HttpTestingController) as HttpTestingController; | 
					
						
							|  |  |  |           const http = ref.injector.get(HttpClient); | 
					
						
							| 
									
										
										
										
											2019-08-19 15:05:29 -07:00
										 |  |  |           ref.injector.get(NgZone).run(() => { | 
					
						
							|  |  |  |             http.get<string>('http://localhost/testing').subscribe((body: string) => { | 
					
						
							| 
									
										
										
										
											2018-05-31 10:35:51 -07:00
										 |  |  |               NgZone.assertInAngularZone(); | 
					
						
							|  |  |  |               expect(body).toEqual('success!'); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |             mock.expectOne('http://localhost/testing').flush('success!'); | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-07-18 12:45:47 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-09-11 00:18:55 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     describe('ServerTransferStoreModule', () => { | 
					
						
							|  |  |  |       let called = false; | 
					
						
							|  |  |  |       const defaultExpectedOutput = | 
					
						
							|  |  |  |           '<html><head></head><body><app ng-version="0.0.0-PLACEHOLDER">Works!</app><script id="transfer-state" type="application/json">{&q;test&q;:10}</script></body></html>'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       beforeEach(() => { called = false; }); | 
					
						
							|  |  |  |       afterEach(() => { expect(called).toBe(true); }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-19 21:06:41 +01:00
										 |  |  |       it('adds transfer script tag when using renderModule', async(() => { | 
					
						
							|  |  |  |            renderModule(TransferStoreModule, {document: '<app></app>'}).then(output => { | 
					
						
							|  |  |  |              expect(output).toBe(defaultExpectedOutput); | 
					
						
							|  |  |  |              called = true; | 
					
						
							|  |  |  |            }); | 
					
						
							|  |  |  |          })); | 
					
						
							| 
									
										
										
										
											2017-09-11 00:18:55 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       it('adds transfer script tag when using renderModuleFactory', | 
					
						
							|  |  |  |          async(inject([PlatformRef], (defaultPlatform: PlatformRef) => { | 
					
						
							|  |  |  |            const compilerFactory: CompilerFactory = | 
					
						
							|  |  |  |                defaultPlatform.injector.get(CompilerFactory, null); | 
					
						
							|  |  |  |            const moduleFactory = | 
					
						
							|  |  |  |                compilerFactory.createCompiler().compileModuleSync(TransferStoreModule); | 
					
						
							|  |  |  |            renderModuleFactory(moduleFactory, {document: '<app></app>'}).then(output => { | 
					
						
							|  |  |  |              expect(output).toBe(defaultExpectedOutput); | 
					
						
							|  |  |  |              called = true; | 
					
						
							|  |  |  |            }); | 
					
						
							|  |  |  |          }))); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-19 21:06:41 +01:00
										 |  |  |       it('cannot break out of <script> tag in serialized output', async(() => { | 
					
						
							|  |  |  |            renderModule(EscapedTransferStoreModule, { | 
					
						
							|  |  |  |              document: '<esc-app></esc-app>' | 
					
						
							|  |  |  |            }).then(output => { | 
					
						
							|  |  |  |              expect(output).toBe( | 
					
						
							|  |  |  |                  '<html><head></head><body><esc-app ng-version="0.0.0-PLACEHOLDER">Works!</esc-app>' + | 
					
						
							|  |  |  |                  '<script id="transfer-state" type="application/json">' + | 
					
						
							|  |  |  |                  '{&q;testString&q;:&q;&l;/script&g;&l;script&g;' + | 
					
						
							|  |  |  |                  'alert(&s;Hello&a;&s; + \\&q;World\\&q;);&q;}</script></body></html>'); | 
					
						
							|  |  |  |              called = true; | 
					
						
							|  |  |  |            }); | 
					
						
							|  |  |  |          })); | 
					
						
							| 
									
										
										
										
											2017-09-11 00:18:55 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-02-14 16:14:40 -08:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2017-12-16 14:42:55 -08:00
										 |  |  | })(); |