| 
									
										
										
										
											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
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-15 09:12:40 -08:00
										 |  |  | import {Component, Directive, HostBinding, HostListener, Input, Output, Query, Type, resolveForwardRef} from '@angular/core'; | 
					
						
							| 
									
										
										
										
											2016-08-30 18:07:40 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-18 15:17:44 -08:00
										 |  |  | import {ListWrapper, StringMapWrapper} from './facade/collection'; | 
					
						
							| 
									
										
										
										
											2016-09-27 15:41:37 -07:00
										 |  |  | import {stringify} from './facade/lang'; | 
					
						
							| 
									
										
										
										
											2016-12-15 09:12:40 -08:00
										 |  |  | import {CompilerInjectable} from './injectable'; | 
					
						
							| 
									
										
										
										
											2016-08-30 18:07:40 -07:00
										 |  |  | import {ReflectorReader, reflector} from './private_import_core'; | 
					
						
							| 
									
										
										
										
											2016-07-11 16:01:49 -07:00
										 |  |  | import {splitAtColon} from './util'; | 
					
						
							| 
									
										
										
										
											2014-11-11 17:33:47 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-19 18:39:35 -07:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2016-09-12 19:14:17 -07:00
										 |  |  |  * Resolve a `Type` for {@link Directive}. | 
					
						
							| 
									
										
										
										
											2015-07-07 08:15:58 +02:00
										 |  |  |  * | 
					
						
							|  |  |  |  * This interface can be overridden by the application developer to create custom behavior. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * See {@link Compiler} | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2016-12-15 09:12:40 -08:00
										 |  |  | @CompilerInjectable() | 
					
						
							| 
									
										
										
										
											2015-05-11 17:59:39 -07:00
										 |  |  | export class DirectiveResolver { | 
					
						
							| 
									
										
										
										
											2016-06-17 10:57:50 -07:00
										 |  |  |   constructor(private _reflector: ReflectorReader = reflector) {} | 
					
						
							| 
									
										
										
										
											2016-03-24 13:32:47 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-10 14:07:30 -08:00
										 |  |  |   isDirective(type: Type<any>) { | 
					
						
							|  |  |  |     const typeMetadata = this._reflector.annotations(resolveForwardRef(type)); | 
					
						
							|  |  |  |     return typeMetadata && typeMetadata.some(isDirectiveMetadata); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-07 08:15:58 +02:00
										 |  |  |   /** | 
					
						
							| 
									
										
										
										
											2016-09-12 19:14:17 -07:00
										 |  |  |    * Return {@link Directive} for a given `Type`. | 
					
						
							| 
									
										
										
										
											2015-07-07 08:15:58 +02:00
										 |  |  |    */ | 
					
						
							| 
									
										
										
										
											2016-09-12 19:14:17 -07:00
										 |  |  |   resolve(type: Type<any>, throwIfNotFound = true): Directive { | 
					
						
							| 
									
										
										
										
											2016-09-27 15:41:37 -07:00
										 |  |  |     const typeMetadata = this._reflector.annotations(resolveForwardRef(type)); | 
					
						
							|  |  |  |     if (typeMetadata) { | 
					
						
							| 
									
										
										
										
											2016-11-18 15:17:44 -08:00
										 |  |  |       const metadata = ListWrapper.findLast(typeMetadata, isDirectiveMetadata); | 
					
						
							| 
									
										
										
										
											2016-09-27 15:41:37 -07:00
										 |  |  |       if (metadata) { | 
					
						
							|  |  |  |         const propertyMetadata = this._reflector.propMetadata(type); | 
					
						
							| 
									
										
										
										
											2015-11-04 17:08:51 +08:00
										 |  |  |         return this._mergeWithPropertyMetadata(metadata, propertyMetadata, type); | 
					
						
							| 
									
										
										
										
											2014-11-11 17:33:47 -08:00
										 |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-09-27 15:41:37 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-18 03:50:31 -07:00
										 |  |  |     if (throwIfNotFound) { | 
					
						
							| 
									
										
										
										
											2016-08-25 00:50:16 -07:00
										 |  |  |       throw new Error(`No Directive annotation found on ${stringify(type)}`); | 
					
						
							| 
									
										
										
										
											2016-07-18 03:50:31 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-09-27 15:41:37 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-18 03:50:31 -07:00
										 |  |  |     return null; | 
					
						
							| 
									
										
										
										
											2014-11-11 17:33:47 -08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2015-09-03 15:10:48 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-08 16:38:52 -07:00
										 |  |  |   private _mergeWithPropertyMetadata( | 
					
						
							| 
									
										
										
										
											2016-09-12 19:14:17 -07:00
										 |  |  |       dm: Directive, propertyMetadata: {[key: string]: any[]}, | 
					
						
							|  |  |  |       directiveType: Type<any>): Directive { | 
					
						
							| 
									
										
										
										
											2016-09-27 15:41:37 -07:00
										 |  |  |     const inputs: string[] = []; | 
					
						
							|  |  |  |     const outputs: string[] = []; | 
					
						
							|  |  |  |     const host: {[key: string]: string} = {}; | 
					
						
							|  |  |  |     const queries: {[key: string]: any} = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Object.keys(propertyMetadata).forEach((propName: string) => { | 
					
						
							| 
									
										
										
										
											2016-11-18 15:17:44 -08:00
										 |  |  |       const input = ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof Input); | 
					
						
							|  |  |  |       if (input) { | 
					
						
							|  |  |  |         if (input.bindingPropertyName) { | 
					
						
							|  |  |  |           inputs.push(`${propName}: ${input.bindingPropertyName}`); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           inputs.push(propName); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       const output = ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof Output); | 
					
						
							|  |  |  |       if (output) { | 
					
						
							|  |  |  |         if (output.bindingPropertyName) { | 
					
						
							|  |  |  |           outputs.push(`${propName}: ${output.bindingPropertyName}`); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           outputs.push(propName); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2016-12-14 14:31:57 -08:00
										 |  |  |       const hostBindings = propertyMetadata[propName].filter(a => a && a instanceof HostBinding); | 
					
						
							|  |  |  |       hostBindings.forEach(hostBinding => { | 
					
						
							| 
									
										
										
										
											2016-11-18 15:17:44 -08:00
										 |  |  |         if (hostBinding.hostPropertyName) { | 
					
						
							|  |  |  |           const startWith = hostBinding.hostPropertyName[0]; | 
					
						
							|  |  |  |           if (startWith === '(') { | 
					
						
							|  |  |  |             throw new Error(`@HostBinding can not bind to events. Use @HostListener instead.`); | 
					
						
							|  |  |  |           } else if (startWith === '[') { | 
					
						
							|  |  |  |             throw new Error( | 
					
						
							|  |  |  |                 `@HostBinding parameter should be a property name, 'class.<name>', or 'attr.<name>'.`); | 
					
						
							| 
									
										
										
										
											2015-09-04 14:07:16 -07:00
										 |  |  |           } | 
					
						
							| 
									
										
										
										
											2016-11-18 15:17:44 -08:00
										 |  |  |           host[`[${hostBinding.hostPropertyName}]`] = propName; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           host[`[${propName}]`] = propName; | 
					
						
							| 
									
										
										
										
											2015-09-19 18:39:35 -07:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-12-14 14:31:57 -08:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2016-12-14 15:10:43 -08:00
										 |  |  |       const hostListeners = propertyMetadata[propName].filter(a => a && a instanceof HostListener); | 
					
						
							| 
									
										
										
										
											2016-12-14 14:31:57 -08:00
										 |  |  |       hostListeners.forEach(hostListener => { | 
					
						
							| 
									
										
										
										
											2016-11-18 15:17:44 -08:00
										 |  |  |         const args = hostListener.args || []; | 
					
						
							|  |  |  |         host[`(${hostListener.eventName})`] = `${propName}(${args.join(',')})`; | 
					
						
							| 
									
										
										
										
											2016-12-14 14:31:57 -08:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2016-11-18 15:17:44 -08:00
										 |  |  |       const query = ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof Query); | 
					
						
							|  |  |  |       if (query) { | 
					
						
							|  |  |  |         queries[propName] = query; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2015-09-03 15:10:48 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2015-11-04 17:08:51 +08:00
										 |  |  |     return this._merge(dm, inputs, outputs, host, queries, directiveType); | 
					
						
							| 
									
										
										
										
											2015-09-03 15:10:48 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-11 16:01:49 -07:00
										 |  |  |   private _extractPublicName(def: string) { return splitAtColon(def, [null, def])[1].trim(); } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-18 15:17:44 -08:00
										 |  |  |   private _dedupeBindings(bindings: string[]): string[] { | 
					
						
							|  |  |  |     const names = new Set<string>(); | 
					
						
							|  |  |  |     const reversedResult: string[] = []; | 
					
						
							|  |  |  |     // go last to first to allow later entries to overwrite previous entries
 | 
					
						
							|  |  |  |     for (let i = bindings.length - 1; i >= 0; i--) { | 
					
						
							|  |  |  |       const binding = bindings[i]; | 
					
						
							|  |  |  |       const name = this._extractPublicName(binding); | 
					
						
							|  |  |  |       if (!names.has(name)) { | 
					
						
							|  |  |  |         names.add(name); | 
					
						
							|  |  |  |         reversedResult.push(binding); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return reversedResult.reverse(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-08 16:38:52 -07:00
										 |  |  |   private _merge( | 
					
						
							| 
									
										
										
										
											2016-09-27 15:41:37 -07:00
										 |  |  |       directive: Directive, inputs: string[], outputs: string[], host: {[key: string]: string}, | 
					
						
							| 
									
										
										
										
											2016-09-12 19:14:17 -07:00
										 |  |  |       queries: {[key: string]: any}, directiveType: Type<any>): Directive { | 
					
						
							| 
									
										
										
										
											2016-11-18 15:17:44 -08:00
										 |  |  |     const mergedInputs = | 
					
						
							|  |  |  |         this._dedupeBindings(directive.inputs ? directive.inputs.concat(inputs) : inputs); | 
					
						
							|  |  |  |     const mergedOutputs = | 
					
						
							|  |  |  |         this._dedupeBindings(directive.outputs ? directive.outputs.concat(outputs) : outputs); | 
					
						
							| 
									
										
										
										
											2016-09-27 15:41:37 -07:00
										 |  |  |     const mergedHost = directive.host ? StringMapWrapper.merge(directive.host, host) : host; | 
					
						
							|  |  |  |     const mergedQueries = | 
					
						
							|  |  |  |         directive.queries ? StringMapWrapper.merge(directive.queries, queries) : queries; | 
					
						
							| 
									
										
										
										
											2015-09-03 15:10:48 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-27 15:41:37 -07:00
										 |  |  |     if (directive instanceof Component) { | 
					
						
							| 
									
										
										
										
											2016-09-12 19:14:17 -07:00
										 |  |  |       return new Component({ | 
					
						
							| 
									
										
										
										
											2016-09-27 15:41:37 -07:00
										 |  |  |         selector: directive.selector, | 
					
						
							| 
									
										
										
										
											2015-09-30 20:59:23 -07:00
										 |  |  |         inputs: mergedInputs, | 
					
						
							|  |  |  |         outputs: mergedOutputs, | 
					
						
							| 
									
										
										
										
											2015-09-04 14:07:16 -07:00
										 |  |  |         host: mergedHost, | 
					
						
							| 
									
										
										
										
											2016-09-27 15:41:37 -07:00
										 |  |  |         exportAs: directive.exportAs, | 
					
						
							|  |  |  |         moduleId: directive.moduleId, | 
					
						
							| 
									
										
										
										
											2015-09-17 18:45:49 -07:00
										 |  |  |         queries: mergedQueries, | 
					
						
							| 
									
										
										
										
											2016-09-27 15:41:37 -07:00
										 |  |  |         changeDetection: directive.changeDetection, | 
					
						
							|  |  |  |         providers: directive.providers, | 
					
						
							|  |  |  |         viewProviders: directive.viewProviders, | 
					
						
							|  |  |  |         entryComponents: directive.entryComponents, | 
					
						
							|  |  |  |         template: directive.template, | 
					
						
							|  |  |  |         templateUrl: directive.templateUrl, | 
					
						
							|  |  |  |         styles: directive.styles, | 
					
						
							|  |  |  |         styleUrls: directive.styleUrls, | 
					
						
							|  |  |  |         encapsulation: directive.encapsulation, | 
					
						
							|  |  |  |         animations: directive.animations, | 
					
						
							|  |  |  |         interpolation: directive.interpolation | 
					
						
							| 
									
										
										
										
											2015-09-03 15:10:48 -07:00
										 |  |  |       }); | 
					
						
							|  |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2016-09-12 19:14:17 -07:00
										 |  |  |       return new Directive({ | 
					
						
							| 
									
										
										
										
											2016-09-27 15:41:37 -07:00
										 |  |  |         selector: directive.selector, | 
					
						
							| 
									
										
										
										
											2015-09-30 20:59:23 -07:00
										 |  |  |         inputs: mergedInputs, | 
					
						
							|  |  |  |         outputs: mergedOutputs, | 
					
						
							| 
									
										
										
										
											2015-09-04 14:07:16 -07:00
										 |  |  |         host: mergedHost, | 
					
						
							| 
									
										
										
										
											2016-09-27 15:41:37 -07:00
										 |  |  |         exportAs: directive.exportAs, | 
					
						
							| 
									
										
										
										
											2015-10-10 22:11:13 -07:00
										 |  |  |         queries: mergedQueries, | 
					
						
							| 
									
										
										
										
											2016-09-27 15:41:37 -07:00
										 |  |  |         providers: directive.providers | 
					
						
							| 
									
										
										
										
											2015-09-03 15:10:48 -07:00
										 |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2014-11-11 17:33:47 -08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-09-27 15:41:37 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | function isDirectiveMetadata(type: any): type is Directive { | 
					
						
							|  |  |  |   return type instanceof Directive; | 
					
						
							|  |  |  | } |