| 
									
										
										
										
											2016-04-07 17:17:50 -07:00
										 |  |  | import {provide, AppViewManager, ChangeDetectorRef, HostViewRef, Injector, OnChanges, HostViewFactoryRef, SimpleChange} from 'angular2/core'; | 
					
						
							| 
									
										
										
										
											2015-10-04 09:33:20 -07:00
										 |  |  | import {NG1_SCOPE} from './constants'; | 
					
						
							|  |  |  | import {ComponentInfo} from './metadata'; | 
					
						
							| 
									
										
										
										
											2015-10-09 14:53:04 -07:00
										 |  |  | import Element = protractor.Element; | 
					
						
							| 
									
										
										
										
											2015-10-26 20:17:46 -07:00
										 |  |  | import * as angular from './angular_js'; | 
					
						
							| 
									
										
										
										
											2015-10-04 09:33:20 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | const INITIAL_VALUE = { | 
					
						
							|  |  |  |   __UNINITIALIZED__: true | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-12 21:32:41 -07:00
										 |  |  | export class DowngradeNg2ComponentAdapter { | 
					
						
							| 
									
										
										
										
											2015-10-04 09:33:20 -07:00
										 |  |  |   component: any = null; | 
					
						
							|  |  |  |   inputChangeCount: number = 0; | 
					
						
							|  |  |  |   inputChanges: {[key: string]: SimpleChange} = null; | 
					
						
							|  |  |  |   hostViewRef: HostViewRef = null; | 
					
						
							|  |  |  |   changeDetector: ChangeDetectorRef = null; | 
					
						
							|  |  |  |   componentScope: angular.IScope; | 
					
						
							| 
									
										
										
										
											2015-10-09 14:53:04 -07:00
										 |  |  |   childNodes: Node[]; | 
					
						
							| 
									
										
										
										
											2015-12-02 10:35:51 -08:00
										 |  |  |   contentInsertionPoint: Node = null; | 
					
						
							| 
									
										
										
										
											2015-10-04 09:33:20 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-07 17:17:50 -07:00
										 |  |  |   constructor( | 
					
						
							|  |  |  |       private id: string, private info: ComponentInfo, private element: angular.IAugmentedJQuery, | 
					
						
							|  |  |  |       private attrs: angular.IAttributes, private scope: angular.IScope, | 
					
						
							|  |  |  |       private parentInjector: Injector, private parse: angular.IParseService, | 
					
						
							|  |  |  |       private viewManager: AppViewManager, private hostViewFactory: HostViewFactoryRef) { | 
					
						
							| 
									
										
										
										
											2015-10-09 14:53:04 -07:00
										 |  |  |     (<any>this.element[0]).id = id; | 
					
						
							| 
									
										
										
										
											2015-10-04 09:33:20 -07:00
										 |  |  |     this.componentScope = scope.$new(); | 
					
						
							| 
									
										
										
										
											2015-10-09 14:53:04 -07:00
										 |  |  |     this.childNodes = <Node[]><any>element.contents(); | 
					
						
							| 
									
										
										
										
											2015-10-04 09:33:20 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   bootstrapNg2() { | 
					
						
							| 
									
										
										
										
											2015-10-10 22:11:13 -07:00
										 |  |  |     var childInjector = this.parentInjector.resolveAndCreateChild( | 
					
						
							| 
									
										
										
										
											2015-10-12 11:30:34 -07:00
										 |  |  |         [provide(NG1_SCOPE, {useValue: this.componentScope})]); | 
					
						
							| 
									
										
										
										
											2015-12-02 10:35:51 -08:00
										 |  |  |     this.contentInsertionPoint = document.createComment('ng1 insertion point'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     this.hostViewRef = this.viewManager.createRootHostView( | 
					
						
							|  |  |  |         this.hostViewFactory, '#' + this.id, childInjector, [[this.contentInsertionPoint]]); | 
					
						
							| 
									
										
										
										
											2015-10-04 09:33:20 -07:00
										 |  |  |     var hostElement = this.viewManager.getHostElement(this.hostViewRef); | 
					
						
							|  |  |  |     this.changeDetector = this.hostViewRef.changeDetectorRef; | 
					
						
							|  |  |  |     this.component = this.viewManager.getComponent(hostElement); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-26 20:17:46 -07:00
										 |  |  |   setupInputs(): void { | 
					
						
							| 
									
										
										
										
											2015-10-04 09:33:20 -07:00
										 |  |  |     var attrs = this.attrs; | 
					
						
							|  |  |  |     var inputs = this.info.inputs; | 
					
						
							|  |  |  |     for (var i = 0; i < inputs.length; i++) { | 
					
						
							|  |  |  |       var input = inputs[i]; | 
					
						
							|  |  |  |       var expr = null; | 
					
						
							|  |  |  |       if (attrs.hasOwnProperty(input.attr)) { | 
					
						
							|  |  |  |         var observeFn = ((prop) => { | 
					
						
							|  |  |  |           var prevValue = INITIAL_VALUE; | 
					
						
							|  |  |  |           return (value) => { | 
					
						
							|  |  |  |             if (this.inputChanges !== null) { | 
					
						
							|  |  |  |               this.inputChangeCount++; | 
					
						
							|  |  |  |               this.inputChanges[prop] = | 
					
						
							|  |  |  |                   new Ng1Change(value, prevValue === INITIAL_VALUE ? value : prevValue); | 
					
						
							|  |  |  |               prevValue = value; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             this.component[prop] = value; | 
					
						
							| 
									
										
										
										
											2015-10-26 20:17:46 -07:00
										 |  |  |           }; | 
					
						
							| 
									
										
										
										
											2015-10-04 09:33:20 -07:00
										 |  |  |         })(input.prop); | 
					
						
							|  |  |  |         attrs.$observe(input.attr, observeFn); | 
					
						
							|  |  |  |       } else if (attrs.hasOwnProperty(input.bindAttr)) { | 
					
						
							|  |  |  |         expr = attrs[input.bindAttr]; | 
					
						
							|  |  |  |       } else if (attrs.hasOwnProperty(input.bracketAttr)) { | 
					
						
							|  |  |  |         expr = attrs[input.bracketAttr]; | 
					
						
							|  |  |  |       } else if (attrs.hasOwnProperty(input.bindonAttr)) { | 
					
						
							|  |  |  |         expr = attrs[input.bindonAttr]; | 
					
						
							|  |  |  |       } else if (attrs.hasOwnProperty(input.bracketParenAttr)) { | 
					
						
							|  |  |  |         expr = attrs[input.bracketParenAttr]; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (expr != null) { | 
					
						
							|  |  |  |         var watchFn = ((prop) => (value, prevValue) => { | 
					
						
							|  |  |  |           if (this.inputChanges != null) { | 
					
						
							|  |  |  |             this.inputChangeCount++; | 
					
						
							|  |  |  |             this.inputChanges[prop] = new Ng1Change(prevValue, value); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           this.component[prop] = value; | 
					
						
							|  |  |  |         })(input.prop); | 
					
						
							|  |  |  |         this.componentScope.$watch(expr, watchFn); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     var prototype = this.info.type.prototype; | 
					
						
							| 
									
										
											  
											
												refactor(lifecycle): prefix lifecycle methods with "ng"
BREAKING CHANGE:
Previously, components that would implement lifecycle interfaces would include methods
like "onChanges" or "afterViewInit." Given that components were at risk of using such
names without realizing that Angular would call the methods at different points of
the component lifecycle. This change adds an "ng" prefix to all lifecycle hook methods,
far reducing the risk of an accidental name collision.
To fix, just rename these methods:
 * onInit
 * onDestroy
 * doCheck
 * onChanges
 * afterContentInit
 * afterContentChecked
 * afterViewInit
 * afterViewChecked
 * _Router Hooks_
 * onActivate
 * onReuse
 * onDeactivate
 * canReuse
 * canDeactivate
To:
 * ngOnInit,
 * ngOnDestroy,
 * ngDoCheck,
 * ngOnChanges,
 * ngAfterContentInit,
 * ngAfterContentChecked,
 * ngAfterViewInit,
 * ngAfterViewChecked
 * _Router Hooks_
 * routerOnActivate
 * routerOnReuse
 * routerOnDeactivate
 * routerCanReuse
 * routerCanDeactivate
The names of lifecycle interfaces and enums have not changed, though interfaces
have been updated to reflect the new method names.
Closes #5036
											
										 
											2015-11-16 17:04:36 -08:00
										 |  |  |     if (prototype && (<OnChanges>prototype).ngOnChanges) { | 
					
						
							| 
									
										
										
										
											2015-10-04 09:33:20 -07:00
										 |  |  |       // Detect: OnChanges interface
 | 
					
						
							|  |  |  |       this.inputChanges = {}; | 
					
						
							|  |  |  |       this.componentScope.$watch(() => this.inputChangeCount, () => { | 
					
						
							|  |  |  |         var inputChanges = this.inputChanges; | 
					
						
							|  |  |  |         this.inputChanges = {}; | 
					
						
							| 
									
										
											  
											
												refactor(lifecycle): prefix lifecycle methods with "ng"
BREAKING CHANGE:
Previously, components that would implement lifecycle interfaces would include methods
like "onChanges" or "afterViewInit." Given that components were at risk of using such
names without realizing that Angular would call the methods at different points of
the component lifecycle. This change adds an "ng" prefix to all lifecycle hook methods,
far reducing the risk of an accidental name collision.
To fix, just rename these methods:
 * onInit
 * onDestroy
 * doCheck
 * onChanges
 * afterContentInit
 * afterContentChecked
 * afterViewInit
 * afterViewChecked
 * _Router Hooks_
 * onActivate
 * onReuse
 * onDeactivate
 * canReuse
 * canDeactivate
To:
 * ngOnInit,
 * ngOnDestroy,
 * ngDoCheck,
 * ngOnChanges,
 * ngAfterContentInit,
 * ngAfterContentChecked,
 * ngAfterViewInit,
 * ngAfterViewChecked
 * _Router Hooks_
 * routerOnActivate
 * routerOnReuse
 * routerOnDeactivate
 * routerCanReuse
 * routerCanDeactivate
The names of lifecycle interfaces and enums have not changed, though interfaces
have been updated to reflect the new method names.
Closes #5036
											
										 
											2015-11-16 17:04:36 -08:00
										 |  |  |         (<OnChanges>this.component).ngOnChanges(inputChanges); | 
					
						
							| 
									
										
										
										
											2015-10-04 09:33:20 -07:00
										 |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-10-11 11:18:11 -07:00
										 |  |  |     this.componentScope.$watch(() => this.changeDetector && this.changeDetector.detectChanges()); | 
					
						
							| 
									
										
										
										
											2015-10-04 09:33:20 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-09 14:53:04 -07:00
										 |  |  |   projectContent() { | 
					
						
							|  |  |  |     var childNodes = this.childNodes; | 
					
						
							| 
									
										
										
										
											2015-12-02 10:35:51 -08:00
										 |  |  |     var parent = this.contentInsertionPoint.parentNode; | 
					
						
							|  |  |  |     if (parent) { | 
					
						
							| 
									
										
										
										
											2015-10-09 14:53:04 -07:00
										 |  |  |       for (var i = 0, ii = childNodes.length; i < ii; i++) { | 
					
						
							| 
									
										
										
										
											2015-12-02 10:35:51 -08:00
										 |  |  |         parent.insertBefore(childNodes[i], this.contentInsertionPoint); | 
					
						
							| 
									
										
										
										
											2015-10-09 14:53:04 -07:00
										 |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-04 09:33:20 -07:00
										 |  |  |   setupOutputs() { | 
					
						
							|  |  |  |     var attrs = this.attrs; | 
					
						
							|  |  |  |     var outputs = this.info.outputs; | 
					
						
							|  |  |  |     for (var j = 0; j < outputs.length; j++) { | 
					
						
							|  |  |  |       var output = outputs[j]; | 
					
						
							|  |  |  |       var expr = null; | 
					
						
							|  |  |  |       var assignExpr = false; | 
					
						
							| 
									
										
										
										
											2015-10-10 19:56:22 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       var bindonAttr = | 
					
						
							|  |  |  |           output.bindonAttr ? output.bindonAttr.substring(0, output.bindonAttr.length - 6) : null; | 
					
						
							| 
									
										
										
										
											2016-04-07 17:17:50 -07:00
										 |  |  |       var bracketParenAttr = output.bracketParenAttr ? | 
					
						
							|  |  |  |           `[(${output.bracketParenAttr.substring(2, output.bracketParenAttr.length - 8)})]` : | 
					
						
							|  |  |  |           null; | 
					
						
							| 
									
										
										
										
											2015-10-10 19:56:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-04 09:33:20 -07:00
										 |  |  |       if (attrs.hasOwnProperty(output.onAttr)) { | 
					
						
							|  |  |  |         expr = attrs[output.onAttr]; | 
					
						
							|  |  |  |       } else if (attrs.hasOwnProperty(output.parenAttr)) { | 
					
						
							|  |  |  |         expr = attrs[output.parenAttr]; | 
					
						
							| 
									
										
										
										
											2015-10-10 19:56:22 -07:00
										 |  |  |       } else if (attrs.hasOwnProperty(bindonAttr)) { | 
					
						
							|  |  |  |         expr = attrs[bindonAttr]; | 
					
						
							| 
									
										
										
										
											2015-10-04 09:33:20 -07:00
										 |  |  |         assignExpr = true; | 
					
						
							| 
									
										
										
										
											2015-10-10 19:56:22 -07:00
										 |  |  |       } else if (attrs.hasOwnProperty(bracketParenAttr)) { | 
					
						
							|  |  |  |         expr = attrs[bracketParenAttr]; | 
					
						
							| 
									
										
										
										
											2015-10-04 09:33:20 -07:00
										 |  |  |         assignExpr = true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (expr != null && assignExpr != null) { | 
					
						
							|  |  |  |         var getter = this.parse(expr); | 
					
						
							|  |  |  |         var setter = getter.assign; | 
					
						
							|  |  |  |         if (assignExpr && !setter) { | 
					
						
							|  |  |  |           throw new Error(`Expression '${expr}' is not assignable!`); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         var emitter = this.component[output.prop]; | 
					
						
							|  |  |  |         if (emitter) { | 
					
						
							| 
									
										
										
										
											2015-10-24 18:48:43 -07:00
										 |  |  |           emitter.subscribe({ | 
					
						
							| 
									
										
										
										
											2015-10-04 09:33:20 -07:00
										 |  |  |             next: assignExpr ? ((setter) => (value) => setter(this.scope, value))(setter) : | 
					
						
							|  |  |  |                                ((getter) => (value) => getter(this.scope, {$event: value}))(getter) | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           throw new Error(`Missing emitter '${output.prop}' on component '${this.info.selector}'!`); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   registerCleanup() { | 
					
						
							| 
									
										
										
										
											2016-01-13 16:35:48 +02:00
										 |  |  |     this.element.bind('$destroy', () => this.viewManager.destroyRootHostView(this.hostViewRef)); | 
					
						
							| 
									
										
										
										
											2015-10-04 09:33:20 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Ng1Change implements SimpleChange { | 
					
						
							|  |  |  |   constructor(public previousValue: any, public currentValue: any) {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   isFirstChange(): boolean { return this.previousValue === this.currentValue; } | 
					
						
							|  |  |  | } |