@angular/core
Version:
Angular - the core framework
641 lines • 96 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, 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 = [[]];
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(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(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();
setCurrentTNode(getCurrentParentTNode(), false);
}
else {
const tNode = createTNodePlaceholder(tView, existingTNodeStack[0], index);
existingTNodeStack.unshift([]);
setCurrentTNode(tNode, true);
}
}
}
tView.data[index] = {
create: createOpCodes,
update: updateOpCodes,
};
}
/**
* 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(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);
if (hasBinding) {
generateBindingUpdateOpCodes(updateOpCodes, text, tNode.index, null, 0, null);
}
}
/**
* 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);
}
export function isRootTemplateMessage(subTemplateIndex) {
return subTemplateIndex === -1;
}
/**
* 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)
*/
export function icuStart(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;
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}�-->`;
}
}
bindingMask = parseIcuCase(tView, tIcu, lView, updateOpCodes, parentIdx, icuExpression.cases[i], valueArr.join(''), nestedIcus) |
bindingMask;
}
if (bindingMask) {
addUpdateIcuUpdate(updateOpCodes, bindingMask, anchorIdx);
}
}
/**
* 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.
*
*/
export 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.
*/
export 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.
*
*/
export function parseIcuCase(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(tView, tIcu, lView, updateOpCodes, create, remove, update, inertRootNode, parentIdx, nestedIcus, 0);
}
else {
return 0;
}
}
function walkIcuTree(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);
}
}
// Parse the children of this node (if any)
bindingMask = walkIcuTree(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;
}
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(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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaTE4bl9wYXJzZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL3BhY2thZ2VzL2NvcmUvc3JjL3JlbmRlcjMvaTE4bi9pMThuX3BhcnNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7R0FNRztBQUNILE9BQU8sd0JBQXdCLENBQUM7QUFDaEMsT0FBTyxpQ0FBaUMsQ0FBQztBQUV6QyxPQUFPLEVBQUMsZ0JBQWdCLEVBQUMsTUFBTSw4QkFBOEIsQ0FBQztBQUM5RCxPQUFPLEVBQUMsa0JBQWtCLEVBQUUsU0FBUyxFQUFFLFdBQVcsRUFBRSxjQUFjLEVBQUMsTUFBTSxtQ0FBbUMsQ0FBQztBQUM3RyxPQUFPLEVBQUMsa0JBQWtCLEVBQUMsTUFBTSwrQkFBK0IsQ0FBQztBQUNqRSxPQUFPLEVBQUMsWUFBWSxFQUFDLE1BQU0sa0NBQWtDLENBQUM7QUFDOUQsT0FBTyxFQUFDLGFBQWEsRUFBRSxXQUFXLEVBQUUsd0JBQXdCLEVBQUUsV0FBVyxFQUFFLFlBQVksRUFBQyxNQUFNLG1CQUFtQixDQUFDO0FBRWxILE9BQU8sRUFBQyx1QkFBdUIsRUFBQyxNQUFNLDRDQUE0QyxDQUFDO0FBQ25GLE9BQU8sRUFBQyxZQUFZLEVBQUUsa0JBQWtCLEVBQUMsTUFBTSx3QkFBd0IsQ0FBQztBQUN4RSxPQUFPLEVBQUMsV0FBVyxFQUFDLE1BQU0sd0JBQXdCLENBQUM7QUFDbkQsT0FBTyxFQUFDLGNBQWMsRUFBRSxnQkFBZ0IsRUFBNkUsVUFBVSxFQUF5RSxNQUFNLG9CQUFvQixDQUFDO0FBR25PLE9BQU8sRUFBQyxhQUFhLEVBQWUsTUFBTSxvQkFBb0IsQ0FBQztBQUMvRCxPQUFPLEVBQUMscUJBQXFCLEVBQUUsZUFBZSxFQUFFLGVBQWUsRUFBQyxNQUFNLFVBQVUsQ0FBQztBQUVqRixPQUFPLEVBQUMseUJBQXlCLEVBQUUseUJBQXlCLEVBQUUseUJBQXlCLEVBQUUsd0JBQXdCLEVBQUMsTUFBTSxjQUFj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