@angular/core
Version:
Angular - the core framework
670 lines • 102 kB
JavaScript
/**
* @license
* Copyright Google LLC 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
*/
import '../../util/ng_dev_mode';
import '../../util/ng_i18n_closure_mode';
import { XSS_SECURITY_URL } from '../../error_details_base_url';
import { getTemplateContent, URI_ATTRS, VALID_ATTRS, VALID_ELEMENTS, } from '../../sanitization/html_sanitizer';
import { getInertBodyHelper } from '../../sanitization/inert_body';
import { _sanitizeUrl } from '../../sanitization/url_sanitizer';
import { assertDefined, assertEqual, assertGreaterThanOrEqual, assertOneOf, assertString, } from '../../util/assert';
import { loadIcuContainerVisitor } from '../instructions/i18n_icu_container_visitor';
import { allocExpando, createTNodeAtIndex } from '../instructions/shared';
import { getDocument } from '../interfaces/document';
import { ELEMENT_MARKER, I18nCreateOpCode, ICU_MARKER, } from '../interfaces/i18n';
import { HEADER_OFFSET } from '../interfaces/view';
import { getCurrentParentTNode, getCurrentTNode, setCurrentTNode } from '../state';
import { i18nCreateOpCodesToString, i18nRemoveOpCodesToString, i18nUpdateOpCodesToString, icuCreateOpCodesToString, } from './i18n_debug';
import { addTNodeAndUpdateInsertBeforeIndex } from './i18n_insert_before_index';
import { ensureIcuContainerVisitorLoaded } from './i18n_tree_shaking';
import { createTNodePlaceholder, icuCreateOpCode, isRootTemplateMessage, setTIcu, setTNodeInsertBeforeIndex, } from './i18n_util';
const BINDING_REGEXP = /�(\d+):?\d*�/gi;
const ICU_REGEXP = /({\s*�\d+:?\d*�\s*,\s*\S{6}\s*,[\s\S]*})/gi;
const NESTED_ICU = /�(\d+)�/;
const ICU_BLOCK_REGEXP = /^\s*(�\d+:?\d*�)\s*,\s*(select|plural)\s*,/;
const MARKER = `�`;
const SUBTEMPLATE_REGEXP = /�\/?\*(\d+:\d+)�/gi;
const PH_REGEXP = /�(\/?[#*]\d+):?\d*�/gi;
/**
* Angular uses the special entity &ngsp; as a placeholder for non-removable space.
* It's replaced by the 0xE500 PUA (Private Use Areas) unicode character and later on replaced by a
* space.
* We are re-implementing the same idea since translations might contain this special character.
*/
const NGSP_UNICODE_REGEXP = /\uE500/g;
function replaceNgsp(value) {
return value.replace(NGSP_UNICODE_REGEXP, ' ');
}
/**
* Patch a `debug` property getter on top of the existing object.
*
* NOTE: always call this method with `ngDevMode && attachDebugObject(...)`
*
* @param obj Object to patch
* @param debugGetter Getter returning a value to patch
*/
function attachDebugGetter(obj, debugGetter) {
if (ngDevMode) {
Object.defineProperty(obj, 'debug', { get: debugGetter, enumerable: false });
}
else {
throw new Error('This method should be guarded with `ngDevMode` so that it can be tree shaken in production!');
}
}
/**
* Create dynamic nodes from i18n translation block.
*
* - Text nodes are created synchronously
* - TNodes are linked into tree lazily
*
* @param tView Current `TView`
* @parentTNodeIndex index to the parent TNode of this i18n block
* @param lView Current `LView`
* @param index Index of `ɵɵi18nStart` instruction.
* @param message Message to translate.
* @param subTemplateIndex Index into the sub template of message translation. (ie in case of
* `ngIf`) (-1 otherwise)
*/
export function i18nStartFirstCreatePass(tView, parentTNodeIndex, lView, index, message, subTemplateIndex) {
const rootTNode = getCurrentParentTNode();
const createOpCodes = [];
const updateOpCodes = [];
const existingTNodeStack = [[]];
const astStack = [[]];
if (ngDevMode) {
attachDebugGetter(createOpCodes, i18nCreateOpCodesToString);
attachDebugGetter(updateOpCodes, i18nUpdateOpCodesToString);
}
message = getTranslationForTemplate(message, subTemplateIndex);
const msgParts = replaceNgsp(message).split(PH_REGEXP);
for (let i = 0; i < msgParts.length; i++) {
let value = msgParts[i];
if ((i & 1) === 0) {
// Even indexes are text (including bindings & ICU expressions)
const parts = i18nParseTextIntoPartsAndICU(value);
for (let j = 0; j < parts.length; j++) {
let part = parts[j];
if ((j & 1) === 0) {
// `j` is odd therefore `part` is string
const text = part;
ngDevMode && assertString(text, 'Parsed ICU part should be string');
if (text !== '') {
i18nStartFirstCreatePassProcessTextNode(astStack[0], tView, rootTNode, existingTNodeStack[0], createOpCodes, updateOpCodes, lView, text);
}
}
else {
// `j` is Even therefor `part` is an `ICUExpression`
const icuExpression = part;
// Verify that ICU expression has the right shape. Translations might contain invalid
// constructions (while original messages were correct), so ICU parsing at runtime may
// not succeed (thus `icuExpression` remains a string).
// Note: we intentionally retain the error here by not using `ngDevMode`, because
// the value can change based on the locale and users aren't guaranteed to hit
// an invalid string while they're developing.
if (typeof icuExpression !== 'object') {
throw new Error(`Unable to parse ICU expression in "${message}" message.`);
}
const icuContainerTNode = createTNodeAndAddOpCode(tView, rootTNode, existingTNodeStack[0], lView, createOpCodes, ngDevMode ? `ICU ${index}:${icuExpression.mainBinding}` : '', true);
const icuNodeIndex = icuContainerTNode.index;
ngDevMode &&
assertGreaterThanOrEqual(icuNodeIndex, HEADER_OFFSET, 'Index must be in absolute LView offset');
icuStart(astStack[0], tView, lView, updateOpCodes, parentTNodeIndex, icuExpression, icuNodeIndex);
}
}
}
else {
// Odd indexes are placeholders (elements and sub-templates)
// At this point value is something like: '/#1:2' (originally coming from '�/#1:2�')
const isClosing = value.charCodeAt(0) === 47 /* CharCode.SLASH */;
const type = value.charCodeAt(isClosing ? 1 : 0);
ngDevMode && assertOneOf(type, 42 /* CharCode.STAR */, 35 /* CharCode.HASH */);
const index = HEADER_OFFSET + Number.parseInt(value.substring(isClosing ? 2 : 1));
if (isClosing) {
existingTNodeStack.shift();
astStack.shift();
setCurrentTNode(getCurrentParentTNode(), false);
}
else {
const tNode = createTNodePlaceholder(tView, existingTNodeStack[0], index);
existingTNodeStack.unshift([]);
setCurrentTNode(tNode, true);
const placeholderNode = {
kind: 2 /* I18nNodeKind.PLACEHOLDER */,
index,
children: [],
type: type === 35 /* CharCode.HASH */ ? 0 /* I18nPlaceholderType.ELEMENT */ : 1 /* I18nPlaceholderType.SUBTEMPLATE */,
};
astStack[0].push(placeholderNode);
astStack.unshift(placeholderNode.children);
}
}
}
tView.data[index] = {
create: createOpCodes,
update: updateOpCodes,
ast: astStack[0],
};
}
/**
* Allocate space in i18n Range add create OpCode instruction to create a text or comment node.
*
* @param tView Current `TView` needed to allocate space in i18n range.
* @param rootTNode Root `TNode` of the i18n block. This node determines if the new TNode will be
* added as part of the `i18nStart` instruction or as part of the `TNode.insertBeforeIndex`.
* @param existingTNodes internal state for `addTNodeAndUpdateInsertBeforeIndex`.
* @param lView Current `LView` needed to allocate space in i18n range.
* @param createOpCodes Array storing `I18nCreateOpCodes` where new opCodes will be added.
* @param text Text to be added when the `Text` or `Comment` node will be created.
* @param isICU true if a `Comment` node for ICU (instead of `Text`) node should be created.
*/
function createTNodeAndAddOpCode(tView, rootTNode, existingTNodes, lView, createOpCodes, text, isICU) {
const i18nNodeIdx = allocExpando(tView, lView, 1, null);
let opCode = i18nNodeIdx << I18nCreateOpCode.SHIFT;
let parentTNode = getCurrentParentTNode();
if (rootTNode === parentTNode) {
// FIXME(misko): A null `parentTNode` should represent when we fall of the `LView` boundary.
// (there is no parent), but in some circumstances (because we are inconsistent about how we set
// `previousOrParentTNode`) it could point to `rootTNode` So this is a work around.
parentTNode = null;
}
if (parentTNode === null) {
// If we don't have a parent that means that we can eagerly add nodes.
// If we have a parent than these nodes can't be added now (as the parent has not been created
// yet) and instead the `parentTNode` is responsible for adding it. See
// `TNode.insertBeforeIndex`
opCode |= I18nCreateOpCode.APPEND_EAGERLY;
}
if (isICU) {
opCode |= I18nCreateOpCode.COMMENT;
ensureIcuContainerVisitorLoaded(loadIcuContainerVisitor);
}
createOpCodes.push(opCode, text === null ? '' : text);
// We store `{{?}}` so that when looking at debug `TNodeType.template` we can see where the
// bindings are.
const tNode = createTNodeAtIndex(tView, i18nNodeIdx, isICU ? 32 /* TNodeType.Icu */ : 1 /* TNodeType.Text */, text === null ? (ngDevMode ? '{{?}}' : '') : text, null);
addTNodeAndUpdateInsertBeforeIndex(existingTNodes, tNode);
const tNodeIdx = tNode.index;
setCurrentTNode(tNode, false /* Text nodes are self closing */);
if (parentTNode !== null && rootTNode !== parentTNode) {
// We are a child of deeper node (rather than a direct child of `i18nStart` instruction.)
// We have to make sure to add ourselves to the parent.
setTNodeInsertBeforeIndex(parentTNode, tNodeIdx);
}
return tNode;
}
/**
* Processes text node in i18n block.
*
* Text nodes can have:
* - Create instruction in `createOpCodes` for creating the text node.
* - Allocate spec for text node in i18n range of `LView`
* - If contains binding:
* - bindings => allocate space in i18n range of `LView` to store the binding value.
* - populate `updateOpCodes` with update instructions.
*
* @param tView Current `TView`
* @param rootTNode Root `TNode` of the i18n block. This node determines if the new TNode will
* be added as part of the `i18nStart` instruction or as part of the
* `TNode.insertBeforeIndex`.
* @param existingTNodes internal state for `addTNodeAndUpdateInsertBeforeIndex`.
* @param createOpCodes Location where the creation OpCodes will be stored.
* @param lView Current `LView`
* @param text The translated text (which may contain binding)
*/
function i18nStartFirstCreatePassProcessTextNode(ast, tView, rootTNode, existingTNodes, createOpCodes, updateOpCodes, lView, text) {
const hasBinding = text.match(BINDING_REGEXP);
const tNode = createTNodeAndAddOpCode(tView, rootTNode, existingTNodes, lView, createOpCodes, hasBinding ? null : text, false);
const index = tNode.index;
if (hasBinding) {
generateBindingUpdateOpCodes(updateOpCodes, text, index, null, 0, null);
}
ast.push({ kind: 0 /* I18nNodeKind.TEXT */, index });
}
/**
* See `i18nAttributes` above.
*/
export function i18nAttributesFirstPass(tView, index, values) {
const previousElement = getCurrentTNode();
const previousElementIndex = previousElement.index;
const updateOpCodes = [];
if (ngDevMode) {
attachDebugGetter(updateOpCodes, i18nUpdateOpCodesToString);
}
if (tView.firstCreatePass && tView.data[index] === null) {
for (let i = 0; i < values.length; i += 2) {
const attrName = values[i];
const message = values[i + 1];
if (message !== '') {
// Check if attribute value contains an ICU and throw an error if that's the case.
// ICUs in element attributes are not supported.
// Note: we intentionally retain the error here by not using `ngDevMode`, because
// the `value` can change based on the locale and users aren't guaranteed to hit
// an invalid string while they're developing.
if (ICU_REGEXP.test(message)) {
throw new Error(`ICU expressions are not supported in attributes. Message: "${message}".`);
}
// i18n attributes that hit this code path are guaranteed to have bindings, because
// the compiler treats static i18n attributes as regular attribute bindings.
// Since this may not be the first i18n attribute on this element we need to pass in how
// many previous bindings there have already been.
generateBindingUpdateOpCodes(updateOpCodes, message, previousElementIndex, attrName, countBindings(updateOpCodes), null);
}
}
tView.data[index] = updateOpCodes;
}
}
/**
* Generate the OpCodes to update the bindings of a string.
*
* @param updateOpCodes Place where the update opcodes will be stored.
* @param str The string containing the bindings.
* @param destinationNode Index of the destination node which will receive the binding.
* @param attrName Name of the attribute, if the string belongs to an attribute.
* @param sanitizeFn Sanitization function used to sanitize the string after update, if necessary.
* @param bindingStart The lView index of the next expression that can be bound via an opCode.
* @returns The mask value for these bindings
*/
function generateBindingUpdateOpCodes(updateOpCodes, str, destinationNode, attrName, bindingStart, sanitizeFn) {
ngDevMode &&
assertGreaterThanOrEqual(destinationNode, HEADER_OFFSET, 'Index must be in absolute LView offset');
const maskIndex = updateOpCodes.length; // Location of mask
const sizeIndex = maskIndex + 1; // location of size for skipping
updateOpCodes.push(null, null); // Alloc space for mask and size
const startIndex = maskIndex + 2; // location of first allocation.
if (ngDevMode) {
attachDebugGetter(updateOpCodes, i18nUpdateOpCodesToString);
}
const textParts = str.split(BINDING_REGEXP);
let mask = 0;
for (let j = 0; j < textParts.length; j++) {
const textValue = textParts[j];
if (j & 1) {
// Odd indexes are bindings
const bindingIndex = bindingStart + parseInt(textValue, 10);
updateOpCodes.push(-1 - bindingIndex);
mask = mask | toMaskBit(bindingIndex);
}
else if (textValue !== '') {
// Even indexes are text
updateOpCodes.push(textValue);
}
}
updateOpCodes.push((destinationNode << 2 /* I18nUpdateOpCode.SHIFT_REF */) |
(attrName ? 1 /* I18nUpdateOpCode.Attr */ : 0 /* I18nUpdateOpCode.Text */));
if (attrName) {
updateOpCodes.push(attrName, sanitizeFn);
}
updateOpCodes[maskIndex] = mask;
updateOpCodes[sizeIndex] = updateOpCodes.length - startIndex;
return mask;
}
/**
* Count the number of bindings in the given `opCodes`.
*
* It could be possible to speed this up, by passing the number of bindings found back from
* `generateBindingUpdateOpCodes()` to `i18nAttributesFirstPass()` but this would then require more
* complexity in the code and/or transient objects to be created.
*
* Since this function is only called once when the template is instantiated, is trivial in the
* first instance (since `opCodes` will be an empty array), and it is not common for elements to
* contain multiple i18n bound attributes, it seems like this is a reasonable compromise.
*/
function countBindings(opCodes) {
let count = 0;
for (let i = 0; i < opCodes.length; i++) {
const opCode = opCodes[i];
// Bindings are negative numbers.
if (typeof opCode === 'number' && opCode < 0) {
count++;
}
}
return count;
}
/**
* Convert binding index to mask bit.
*
* Each index represents a single bit on the bit-mask. Because bit-mask only has 32 bits, we make
* the 32nd bit share all masks for all bindings higher than 32. Since it is extremely rare to
* have more than 32 bindings this will be hit very rarely. The downside of hitting this corner
* case is that we will execute binding code more often than necessary. (penalty of performance)
*/
function toMaskBit(bindingIndex) {
return 1 << Math.min(bindingIndex, 31);
}
/**
* Removes everything inside the sub-templates of a message.
*/
function removeInnerTemplateTranslation(message) {
let match;
let res = '';
let index = 0;
let inTemplate = false;
let tagMatched;
while ((match = SUBTEMPLATE_REGEXP.exec(message)) !== null) {
if (!inTemplate) {
res += message.substring(index, match.index + match[0].length);
tagMatched = match[1];
inTemplate = true;
}
else {
if (match[0] === `${MARKER}/*${tagMatched}${MARKER}`) {
index = match.index;
inTemplate = false;
}
}
}
ngDevMode &&
assertEqual(inTemplate, false, `Tag mismatch: unable to find the end of the sub-template in the translation "${message}"`);
res += message.slice(index);
return res;
}
/**
* Extracts a part of a message and removes the rest.
*
* This method is used for extracting a part of the message associated with a template. A
* translated message can span multiple templates.
*
* Example:
* ```
* <div i18n>Translate <span *ngIf>me</span>!</div>
* ```
*
* @param message The message to crop
* @param subTemplateIndex Index of the sub-template to extract. If undefined it returns the
* external template and removes all sub-templates.
*/
export function getTranslationForTemplate(message, subTemplateIndex) {
if (isRootTemplateMessage(subTemplateIndex)) {
// We want the root template message, ignore all sub-templates
return removeInnerTemplateTranslation(message);
}
else {
// We want a specific sub-template
const start = message.indexOf(`:${subTemplateIndex}${MARKER}`) + 2 + subTemplateIndex.toString().length;
const end = message.search(new RegExp(`${MARKER}\\/\\*\\d+:${subTemplateIndex}${MARKER}`));
return removeInnerTemplateTranslation(message.substring(start, end));
}
}
/**
* Generate the OpCodes for ICU expressions.
*
* @param icuExpression
* @param index Index where the anchor is stored and an optional `TIcuContainerNode`
* - `lView[anchorIdx]` points to a `Comment` node representing the anchor for the ICU.
* - `tView.data[anchorIdx]` points to the `TIcuContainerNode` if ICU is root (`null` otherwise)
*/
function icuStart(ast, tView, lView, updateOpCodes, parentIdx, icuExpression, anchorIdx) {
ngDevMode && assertDefined(icuExpression, 'ICU expression must be defined');
let bindingMask = 0;
const tIcu = {
type: icuExpression.type,
currentCaseLViewIndex: allocExpando(tView, lView, 1, null),
anchorIdx,
cases: [],
create: [],
remove: [],
update: [],
};
addUpdateIcuSwitch(updateOpCodes, icuExpression, anchorIdx);
setTIcu(tView, anchorIdx, tIcu);
const values = icuExpression.values;
const cases = [];
for (let i = 0; i < values.length; i++) {
// Each value is an array of strings & other ICU expressions
const valueArr = values[i];
const nestedIcus = [];
for (let j = 0; j < valueArr.length; j++) {
const value = valueArr[j];
if (typeof value !== 'string') {
// It is an nested ICU expression
const icuIndex = nestedIcus.push(value) - 1;
// Replace nested ICU expression by a comment node
valueArr[j] = `<!--�${icuIndex}�-->`;
}
}
const caseAst = [];
cases.push(caseAst);
bindingMask =
parseIcuCase(caseAst, tView, tIcu, lView, updateOpCodes, parentIdx, icuExpression.cases[i], valueArr.join(''), nestedIcus) | bindingMask;
}
if (bindingMask) {
addUpdateIcuUpdate(updateOpCodes, bindingMask, anchorIdx);
}
ast.push({
kind: 3 /* I18nNodeKind.ICU */,
index: anchorIdx,
cases,
currentCaseLViewIndex: tIcu.currentCaseLViewIndex,
});
}
/**
* Parses text containing an ICU expression and produces a JSON object for it.
* Original code from closure library, modified for Angular.
*
* @param pattern Text containing an ICU expression that needs to be parsed.
*
*/
function parseICUBlock(pattern) {
const cases = [];
const values = [];
let icuType = 1 /* IcuType.plural */;
let mainBinding = 0;
pattern = pattern.replace(ICU_BLOCK_REGEXP, function (str, binding, type) {
if (type === 'select') {
icuType = 0 /* IcuType.select */;
}
else {
icuType = 1 /* IcuType.plural */;
}
mainBinding = parseInt(binding.slice(1), 10);
return '';
});
const parts = i18nParseTextIntoPartsAndICU(pattern);
// Looking for (key block)+ sequence. One of the keys has to be "other".
for (let pos = 0; pos < parts.length;) {
let key = parts[pos++].trim();
if (icuType === 1 /* IcuType.plural */) {
// Key can be "=x", we just want "x"
key = key.replace(/\s*(?:=)?(\w+)\s*/, '$1');
}
if (key.length) {
cases.push(key);
}
const blocks = i18nParseTextIntoPartsAndICU(parts[pos++]);
if (cases.length > values.length) {
values.push(blocks);
}
}
// TODO(ocombe): support ICU expressions in attributes, see #21615
return { type: icuType, mainBinding: mainBinding, cases, values };
}
/**
* Breaks pattern into strings and top level {...} blocks.
* Can be used to break a message into text and ICU expressions, or to break an ICU expression
* into keys and cases. Original code from closure library, modified for Angular.
*
* @param pattern (sub)Pattern to be broken.
* @returns An `Array<string|IcuExpression>` where:
* - odd positions: `string` => text between ICU expressions
* - even positions: `ICUExpression` => ICU expression parsed into `ICUExpression` record.
*/
function i18nParseTextIntoPartsAndICU(pattern) {
if (!pattern) {
return [];
}
let prevPos = 0;
const braceStack = [];
const results = [];
const braces = /[{}]/g;
// lastIndex doesn't get set to 0 so we have to.
braces.lastIndex = 0;
let match;
while ((match = braces.exec(pattern))) {
const pos = match.index;
if (match[0] == '}') {
braceStack.pop();
if (braceStack.length == 0) {
// End of the block.
const block = pattern.substring(prevPos, pos);
if (ICU_BLOCK_REGEXP.test(block)) {
results.push(parseICUBlock(block));
}
else {
results.push(block);
}
prevPos = pos + 1;
}
}
else {
if (braceStack.length == 0) {
const substring = pattern.substring(prevPos, pos);
results.push(substring);
prevPos = pos + 1;
}
braceStack.push('{');
}
}
const substring = pattern.substring(prevPos);
results.push(substring);
return results;
}
/**
* Parses a node, its children and its siblings, and generates the mutate & update OpCodes.
*
*/
function parseIcuCase(ast, tView, tIcu, lView, updateOpCodes, parentIdx, caseName, unsafeCaseHtml, nestedIcus) {
const create = [];
const remove = [];
const update = [];
if (ngDevMode) {
attachDebugGetter(create, icuCreateOpCodesToString);
attachDebugGetter(remove, i18nRemoveOpCodesToString);
attachDebugGetter(update, i18nUpdateOpCodesToString);
}
tIcu.cases.push(caseName);
tIcu.create.push(create);
tIcu.remove.push(remove);
tIcu.update.push(update);
const inertBodyHelper = getInertBodyHelper(getDocument());
const inertBodyElement = inertBodyHelper.getInertBodyElement(unsafeCaseHtml);
ngDevMode && assertDefined(inertBodyElement, 'Unable to generate inert body element');
const inertRootNode = getTemplateContent(inertBodyElement) || inertBodyElement;
if (inertRootNode) {
return walkIcuTree(ast, tView, tIcu, lView, updateOpCodes, create, remove, update, inertRootNode, parentIdx, nestedIcus, 0);
}
else {
return 0;
}
}
function walkIcuTree(ast, tView, tIcu, lView, sharedUpdateOpCodes, create, remove, update, parentNode, parentIdx, nestedIcus, depth) {
let bindingMask = 0;
let currentNode = parentNode.firstChild;
while (currentNode) {
const newIndex = allocExpando(tView, lView, 1, null);
switch (currentNode.nodeType) {
case Node.ELEMENT_NODE:
const element = currentNode;
const tagName = element.tagName.toLowerCase();
if (VALID_ELEMENTS.hasOwnProperty(tagName)) {
addCreateNodeAndAppend(create, ELEMENT_MARKER, tagName, parentIdx, newIndex);
tView.data[newIndex] = tagName;
const elAttrs = element.attributes;
for (let i = 0; i < elAttrs.length; i++) {
const attr = elAttrs.item(i);
const lowerAttrName = attr.name.toLowerCase();
const hasBinding = !!attr.value.match(BINDING_REGEXP);
// we assume the input string is safe, unless it's using a binding
if (hasBinding) {
if (VALID_ATTRS.hasOwnProperty(lowerAttrName)) {
if (URI_ATTRS[lowerAttrName]) {
generateBindingUpdateOpCodes(update, attr.value, newIndex, attr.name, 0, _sanitizeUrl);
}
else {
generateBindingUpdateOpCodes(update, attr.value, newIndex, attr.name, 0, null);
}
}
else {
ngDevMode &&
console.warn(`WARNING: ignoring unsafe attribute value ` +
`${lowerAttrName} on element ${tagName} ` +
`(see ${XSS_SECURITY_URL})`);
}
}
else {
addCreateAttribute(create, newIndex, attr);
}
}
const elementNode = {
kind: 1 /* I18nNodeKind.ELEMENT */,
index: newIndex,
children: [],
};
ast.push(elementNode);
// Parse the children of this node (if any)
bindingMask =
walkIcuTree(elementNode.children, tView, tIcu, lView, sharedUpdateOpCodes, create, remove, update, currentNode, newIndex, nestedIcus, depth + 1) | bindingMask;
addRemoveNode(remove, newIndex, depth);
}
break;
case Node.TEXT_NODE:
const value = currentNode.textContent || '';
const hasBinding = value.match(BINDING_REGEXP);
addCreateNodeAndAppend(create, null, hasBinding ? '' : value, parentIdx, newIndex);
addRemoveNode(remove, newIndex, depth);
if (hasBinding) {
bindingMask =
generateBindingUpdateOpCodes(update, value, newIndex, null, 0, null) | bindingMask;
}
ast.push({
kind: 0 /* I18nNodeKind.TEXT */,
index: newIndex,
});
break;
case Node.COMMENT_NODE:
// Check if the comment node is a placeholder for a nested ICU
const isNestedIcu = NESTED_ICU.exec(currentNode.textContent || '');
if (isNestedIcu) {
const nestedIcuIndex = parseInt(isNestedIcu[1], 10);
const icuExpression = nestedIcus[nestedIcuIndex];
// Create the comment node that will anchor the ICU expression
addCreateNodeAndAppend(create, ICU_MARKER, ngDevMode ? `nested ICU ${nestedIcuIndex}` : '', parentIdx, newIndex);
icuStart(ast, tView, lView, sharedUpdateOpCodes, parentIdx, icuExpression, newIndex);
addRemoveNestedIcu(remove, newIndex, depth);
}
break;
}
currentNode = currentNode.nextSibling;
}
return bindingMask;
}
function addRemoveNode(remove, index, depth) {
if (depth === 0) {
remove.push(index);
}
}
function addRemoveNestedIcu(remove, index, depth) {
if (depth === 0) {
remove.push(~index); // remove ICU at `index`
remove.push(index); // remove ICU comment at `index`
}
}
function addUpdateIcuSwitch(update, icuExpression, index) {
update.push(toMaskBit(icuExpression.mainBinding), 2, -1 - icuExpression.mainBinding, (index << 2 /* I18nUpdateOpCode.SHIFT_REF */) | 2 /* I18nUpdateOpCode.IcuSwitch */);
}
function addUpdateIcuUpdate(update, bindingMask, index) {
update.push(bindingMask, 1, (index << 2 /* I18nUpdateOpCode.SHIFT_REF */) | 3 /* I18nUpdateOpCode.IcuUpdate */);
}
function addCreateNodeAndAppend(create, marker, text, appendToParentIdx, createAtIdx) {
if (marker !== null) {
create.push(marker);
}
create.push(text, createAtIdx, icuCreateOpCode(0 /* IcuCreateOpCode.AppendChild */, appendToParentIdx, createAtIdx));
}
function addCreateAttribute(create, newIndex, attr) {
create.push((newIndex << 1 /* IcuCreateOpCode.SHIFT_REF */) | 1 /* IcuCreateOpCode.Attr */, attr.name, attr.value);
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaTE4bl9wYXJzZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL3BhY2thZ2VzL2NvcmUvc3JjL3JlbmRlcjMvaTE4bi9pMThuX3BhcnNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7R0FNRztBQUNILE9BQU8sd0JBQXdCLENBQUM7QUFDaEMsT0FBTyxpQ0FBaUMsQ0FBQztBQUV6QyxPQUFPLEVBQUMsZ0JBQWdCLEVBQUMsTUFBTSw4QkFBOEIsQ0FBQztBQUM5RCxPQUFPLEVBQ0wsa0JBQWtCLEVBQ2xCLFNBQVMsRUFDVCxXQUFXLEVBQ1gsY0FBYyxHQUNmLE1BQU0sbUNBQW1DLENBQUM7QUFDM0MsT0FBTyxFQUFDLGtCQUFrQixFQUFDLE1BQU0sK0JBQStCLENBQUM7QUFDakUsT0FBTyxFQUFDLFlBQVksRUFBQyxNQUFNLGtDQUFrQyxDQUFDO0FBQzlELE9BQU8sRUFDTCxhQUFhLEVBQ2IsV0FBVyxFQUNYLHdCQUF3QixFQUN4QixXQUFXLEVBQ1gsWUFBWSxHQUNiLE1BQU0sbUJBQW1CLENBQUM7QUFFM0IsT0FBTyxFQUFDLHVCQUF1QixFQUFDLE1BQU0sNENBQTRDLENBQUM7QUFDbkYsT0FBTyxFQUFDLFlBQVksRUFBRSxrQkFBa0IsRUFBQyxNQUFNLHdCQUF3QixDQUFDO0FBQ3hFLE9BQU8sRUFBQyxXQUFXLEVBQUMsTUFBTSx3QkFBd0IsQ0FBQztBQUNuRCxPQUFPLEVBQ0wsY0FBYyxFQUNkLGdCQUFnQixFQVVoQixVQUFVLEdBT1gsTUFBTSxvQkFBb0IsQ0FBQztBQUc1QixPQUFPLEVBQUMsYUFBYSxFQUFlLE1BQU0sb0JBQW9CLENBQUM7QUFDL0QsT0FBTyxFQUFDLHFCQUFxQixFQUFFLGVBQWUsRUFBRSxlQUFlLEVBQUMsTUFBTSxVQUFVLENBQUM7QUFFakYsT0FBTyxFQUNMLHlCQUF5QixFQUN6Qix5QkFBeUIsRUFDekIseUJBQXlCLEVBQ3pCLHdCQUF3QixHQUN6QixNQUFN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