| 
									
										
										
										
											2016-07-15 09:42:33 -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-08-01 12:19:09 -07:00
										 |  |  | import {HtmlParser} from '../ml_parser/html_parser'; | 
					
						
							|  |  |  | import {InterpolationConfig} from '../ml_parser/interpolation_config'; | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  | import {ParseError} from '../parse_util'; | 
					
						
							| 
									
										
										
										
											2016-07-15 09:42:33 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-04 17:46:31 -07:00
										 |  |  | import * as extractor from './extractor_merger'; | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  | import * as i18n from './i18n_ast'; | 
					
						
							| 
									
										
										
										
											2016-07-15 09:42:33 -07:00
										 |  |  | import {Serializer} from './serializers/serializer'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * A container for message extracted from the templates. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export class MessageBundle { | 
					
						
							|  |  |  |   private _messageMap: {[id: string]: i18n.Message} = {}; | 
					
						
							| 
									
										
										
										
											2016-07-15 09:42:33 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   constructor( | 
					
						
							|  |  |  |       private _htmlParser: HtmlParser, private _implicitTags: string[], | 
					
						
							|  |  |  |       private _implicitAttrs: {[k: string]: string[]}) {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |   updateFromTemplate(html: string, url: string, interpolationConfig: InterpolationConfig): | 
					
						
							|  |  |  |       ParseError[] { | 
					
						
							| 
									
										
										
										
											2016-07-15 09:42:33 -07:00
										 |  |  |     const htmlParserResult = this._htmlParser.parse(html, url, true, interpolationConfig); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (htmlParserResult.errors.length) { | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |       return htmlParserResult.errors; | 
					
						
							| 
									
										
										
										
											2016-07-15 09:42:33 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-04 17:46:31 -07:00
										 |  |  |     const i18nParserResult = extractor.extractMessages( | 
					
						
							| 
									
										
										
										
											2016-07-15 09:42:33 -07:00
										 |  |  |         htmlParserResult.rootNodes, interpolationConfig, this._implicitTags, this._implicitAttrs); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-04 17:46:31 -07:00
										 |  |  |     if (i18nParserResult.errors.length) { | 
					
						
							|  |  |  |       return i18nParserResult.errors; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-05 11:53:37 -07:00
										 |  |  |     i18nParserResult.messages.forEach( | 
					
						
							|  |  |  |         (message) => { this._messageMap[digestMessage(message)] = message; }); | 
					
						
							| 
									
										
										
										
											2016-07-15 09:42:33 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |   write(serializer: Serializer): string { return serializer.write(this._messageMap); } | 
					
						
							| 
									
										
										
										
											2016-07-15 09:42:33 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-05 11:53:37 -07:00
										 |  |  | export function digestMessage(message: i18n.Message): string { | 
					
						
							|  |  |  |   return strHash(serializeNodes(message.nodes).join('') + `[${message.meaning}]`); | 
					
						
							| 
									
										
										
										
											2016-08-01 14:43:20 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-15 09:42:33 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * String hash function similar to java.lang.String.hashCode(). | 
					
						
							|  |  |  |  * The hash code for a string is computed as | 
					
						
							|  |  |  |  * s[0] * 31 ^ (n - 1) + s[1] * 31 ^ (n - 2) + ... + s[n - 1], | 
					
						
							|  |  |  |  * where s[i] is the ith character of the string and n is the length of | 
					
						
							|  |  |  |  * the string. We mod the result to make it between 0 (inclusive) and 2^32 (exclusive). | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Based on goog.string.hashCode from the Google Closure library | 
					
						
							|  |  |  |  * https://github.com/google/closure-library/
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @internal | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | // TODO(vicb): better algo (less collisions) ?
 | 
					
						
							|  |  |  | export function strHash(str: string): string { | 
					
						
							|  |  |  |   let result: number = 0; | 
					
						
							|  |  |  |   for (var i = 0; i < str.length; ++i) { | 
					
						
							|  |  |  |     // Normalize to 4 byte range, 0 ... 2^32.
 | 
					
						
							|  |  |  |     result = (31 * result + str.charCodeAt(i)) >>> 0; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return result.toString(16); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Serialize the i18n ast to something xml-like in order to generate an UID. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The visitor is also used in the i18n parser tests | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @internal | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  | class _SerializerVisitor implements i18n.Visitor { | 
					
						
							|  |  |  |   visitText(text: i18n.Text, context: any): any { return text.value; } | 
					
						
							| 
									
										
										
										
											2016-07-15 09:42:33 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |   visitContainer(container: i18n.Container, context: any): any { | 
					
						
							| 
									
										
										
										
											2016-07-15 09:42:33 -07:00
										 |  |  |     return `[${container.children.map(child => child.visit(this)).join(', ')}]`; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |   visitIcu(icu: i18n.Icu, context: any): any { | 
					
						
							| 
									
										
										
										
											2016-07-15 09:42:33 -07:00
										 |  |  |     let strCases = Object.keys(icu.cases).map((k: string) => `${k} {${icu.cases[k].visit(this)}}`); | 
					
						
							|  |  |  |     return `{${icu.expression}, ${icu.type}, ${strCases.join(', ')}}`; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |   visitTagPlaceholder(ph: i18n.TagPlaceholder, context: any): any { | 
					
						
							| 
									
										
										
										
											2016-07-15 09:42:33 -07:00
										 |  |  |     return ph.isVoid ? | 
					
						
							|  |  |  |         `<ph tag name="${ph.startName}"/>` : | 
					
						
							|  |  |  |         `<ph tag name="${ph.startName}">${ph.children.map(child => child.visit(this)).join(', ')}</ph name="${ph.closeName}">`; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |   visitPlaceholder(ph: i18n.Placeholder, context: any): any { | 
					
						
							| 
									
										
										
										
											2016-07-15 09:42:33 -07:00
										 |  |  |     return `<ph name="${ph.name}">${ph.value}</ph>`; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |   visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): any { | 
					
						
							| 
									
										
										
										
											2016-07-15 09:42:33 -07:00
										 |  |  |     return `<ph icu name="${ph.name}">${ph.value.visit(this)}</ph>`; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const serializerVisitor = new _SerializerVisitor(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 14:43:20 -07:00
										 |  |  | export function serializeNodes(nodes: i18n.Node[]): string[] { | 
					
						
							|  |  |  |   return nodes.map(a => a.visit(serializerVisitor, null)); | 
					
						
							| 
									
										
										
										
											2016-07-15 09:42:33 -07:00
										 |  |  | } |