UNPKG

@angular/core

Version:

Angular - the core framework

916 lines 123 kB
/** * @fileoverview added by tsickle * Generated from: packages/core/src/render3/instructions/styling.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @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 */ import { unwrapSafeValue } from '../../sanitization/bypass'; import { stylePropNeedsSanitization, ɵɵsanitizeStyle } from '../../sanitization/sanitization'; import { keyValueArrayGet, keyValueArraySet } from '../../util/array_utils'; import { assertDefined, assertEqual, assertLessThan, assertNotEqual, throwError } from '../../util/assert'; import { EMPTY_ARRAY } from '../../util/empty'; import { concatStringsWithSpace, stringify } from '../../util/stringify'; import { assertFirstUpdatePass } from '../assert'; import { bindingUpdated } from '../bindings'; import { getTStylingRangeNext, getTStylingRangeNextDuplicate, getTStylingRangePrev, getTStylingRangePrevDuplicate } from '../interfaces/styling'; import { HEADER_OFFSET, RENDERER } from '../interfaces/view'; import { applyStyling } from '../node_manipulation'; import { getCurrentDirectiveDef, getCurrentStyleSanitizer, getLView, getSelectedIndex, getTView, incrementBindingIndex, setCurrentStyleSanitizer } from '../state'; import { insertTStylingBinding } from '../styling/style_binding_list'; import { getLastParsedKey, getLastParsedValue, parseClassName, parseClassNameNext, parseStyle, parseStyleNext } from '../styling/styling_parser'; import { NO_CHANGE } from '../tokens'; import { getNativeByIndex } from '../util/view_utils'; import { setDirectiveInputsWhichShadowsStyling } from './property'; /** * Sets the current style sanitizer function which will then be used * within all follow-up prop and map-based style binding instructions * for the given element. * * Note that once styling has been applied to the element (i.e. once * `advance(n)` is executed or the hostBindings/template function exits) * then the active `sanitizerFn` will be set to `null`. This means that * once styling is applied to another element then a another call to * `styleSanitizer` will need to be made. * * \@codeGenApi * @param {?} sanitizer * @return {?} */ export function ɵɵstyleSanitizer(sanitizer) { setCurrentStyleSanitizer(sanitizer); } /** * Update a style binding on an element with the provided value. * * If the style value is falsy then it will be removed from the element * (or assigned a different value depending if there are any styles placed * on the element with `styleMap` or any static styles that are * present from when the element was created with `styling`). * * Note that the styling element is updated as part of `stylingApply`. * * \@codeGenApi * @param {?} prop A valid CSS property. * @param {?} value New value to write (`null` or an empty string to remove). * @param {?=} suffix Optional suffix. Used with scalar values to add unit such as `px`. * Note that when a suffix is provided then the underlying sanitizer will * be ignored. * * Note that this will apply the provided style value to the host element if this function is called * within a host binding function. * * @return {?} */ export function ɵɵstyleProp(prop, value, suffix) { checkStylingProperty(prop, value, suffix, false); return ɵɵstyleProp; } /** * Update a class binding on an element with the provided value. * * This instruction is meant to handle the `[class.foo]="exp"` case and, * therefore, the class binding itself must already be allocated using * `styling` within the creation block. * * \@codeGenApi * @param {?} className * @param {?} value A true/false value which will turn the class on or off. * * Note that this will apply the provided class value to the host element if this function * is called within a host binding function. * * @return {?} */ export function ɵɵclassProp(className, value) { checkStylingProperty(className, value, null, true); return ɵɵclassProp; } /** * Update style bindings using an object literal on an element. * * This instruction is meant to apply styling via the `[style]="exp"` template bindings. * When styles are applied to the element they will then be updated with respect to * any styles/classes set via `styleProp`. If any styles are set to falsy * then they will be removed from the element. * * Note that the styling instruction will not be applied until `stylingApply` is called. * * \@codeGenApi * @param {?} styles A key/value style map of the styles that will be applied to the given element. * Any missing styles (that have already been applied to the element beforehand) will be * removed (unset) from the element's styling. * * Note that this will apply the provided styleMap value to the host element if this function * is called within a host binding. * * @return {?} */ export function ɵɵstyleMap(styles) { checkStylingMap(styleKeyValueArraySet, styleStringParser, styles, false); } /** * Parse text as style and add values to KeyValueArray. * * This code is pulled out to a separate function so that it can be tree shaken away if it is not * needed. It is only referenced from `ɵɵstyleMap`. * * @param {?} keyValueArray KeyValueArray to add parsed values to. * @param {?} text text to parse. * @return {?} */ export function styleStringParser(keyValueArray, text) { for (let i = parseStyle(text); i >= 0; i = parseStyleNext(text, i)) { styleKeyValueArraySet(keyValueArray, getLastParsedKey(text), getLastParsedValue(text)); } } /** * Update class bindings using an object literal or class-string on an element. * * This instruction is meant to apply styling via the `[class]="exp"` template bindings. * When classes are applied to the element they will then be updated with * respect to any styles/classes set via `classProp`. If any * classes are set to falsy then they will be removed from the element. * * Note that the styling instruction will not be applied until `stylingApply` is called. * Note that this will the provided classMap value to the host element if this function is called * within a host binding. * * \@codeGenApi * @param {?} classes A key/value map or string of CSS classes that will be added to the * given element. Any missing classes (that have already been applied to the element * beforehand) will be removed (unset) from the element's list of CSS classes. * * @return {?} */ export function ɵɵclassMap(classes) { checkStylingMap(keyValueArraySet, classStringParser, classes, true); } /** * Parse text as class and add values to KeyValueArray. * * This code is pulled out to a separate function so that it can be tree shaken away if it is not * needed. It is only referenced from `ɵɵclassMap`. * * @param {?} keyValueArray KeyValueArray to add parsed values to. * @param {?} text text to parse. * @return {?} */ export function classStringParser(keyValueArray, text) { for (let i = parseClassName(text); i >= 0; i = parseClassNameNext(text, i)) { keyValueArraySet(keyValueArray, getLastParsedKey(text), true); } } /** * Common code between `ɵɵclassProp` and `ɵɵstyleProp`. * * @param {?} prop property name. * @param {?} value binding value. * @param {?} suffixOrSanitizer suffix or sanitization function * @param {?} isClassBased `true` if `class` change (`false` if `style`) * @return {?} */ export function checkStylingProperty(prop, value, suffixOrSanitizer, isClassBased) { /** @type {?} */ const lView = getLView(); /** @type {?} */ const tView = getTView(); // Styling instructions use 2 slots per binding. // 1. one for the value / TStylingKey // 2. one for the intermittent-value / TStylingRange /** @type {?} */ const bindingIndex = incrementBindingIndex(2); if (tView.firstUpdatePass) { stylingFirstUpdatePass(tView, prop, bindingIndex, isClassBased); } if (value !== NO_CHANGE && bindingUpdated(lView, bindingIndex, value)) { // This is a work around. Once PR#34480 lands the sanitizer is passed explicitly and this line // can be removed. /** @type {?} */ let styleSanitizer; if (suffixOrSanitizer == null) { if (styleSanitizer = getCurrentStyleSanitizer()) { suffixOrSanitizer = (/** @type {?} */ (styleSanitizer)); } } /** @type {?} */ const tNode = (/** @type {?} */ (tView.data[getSelectedIndex() + HEADER_OFFSET])); updateStyling(tView, tNode, lView, lView[RENDERER], prop, lView[bindingIndex + 1] = normalizeAndApplySuffixOrSanitizer(value, suffixOrSanitizer), isClassBased, bindingIndex); } } /** * Common code between `ɵɵclassMap` and `ɵɵstyleMap`. * * @param {?} keyValueArraySet (See `keyValueArraySet` in "util/array_utils") Gets passed in as a * function so that * `style` can pass in version which does sanitization. This is done for tree shaking * purposes. * @param {?} stringParser Parser used to parse `value` if `string`. (Passed in as `style` and `class` * have different parsers.) * @param {?} value bound value from application * @param {?} isClassBased `true` if `class` change (`false` if `style`) * @return {?} */ export function checkStylingMap(keyValueArraySet, stringParser, value, isClassBased) { /** @type {?} */ const tView = getTView(); /** @type {?} */ const bindingIndex = incrementBindingIndex(2); if (tView.firstUpdatePass) { stylingFirstUpdatePass(tView, null, bindingIndex, isClassBased); } /** @type {?} */ const lView = getLView(); if (value !== NO_CHANGE && bindingUpdated(lView, bindingIndex, value)) { // `getSelectedIndex()` should be here (rather than in instruction) so that it is guarded by the // if so as not to read unnecessarily. /** @type {?} */ const tNode = (/** @type {?} */ (tView.data[getSelectedIndex() + HEADER_OFFSET])); if (hasStylingInputShadow(tNode, isClassBased) && !isInHostBindings(tView, bindingIndex)) { if (ngDevMode) { // verify that if we are shadowing then `TData` is appropriately marked so that we skip // processing this binding in styling resolution. /** @type {?} */ const tStylingKey = tView.data[bindingIndex]; assertEqual(Array.isArray(tStylingKey) ? tStylingKey[1] : tStylingKey, false, 'Styling linked list shadow input should be marked as \'false\''); } // VE does not concatenate the static portion like we are doing here. // Instead VE just ignores the static completely if dynamic binding is present. // Because of locality we have already set the static portion because we don't know if there // is a dynamic portion until later. If we would ignore the static portion it would look like // the binding has removed it. This would confuse `[ngStyle]`/`[ngClass]` to do the wrong // thing as it would think that the static portion was removed. For this reason we // concatenate it so that `[ngStyle]`/`[ngClass]` can continue to work on changed. /** @type {?} */ let staticPrefix = isClassBased ? tNode.classesWithoutHost : tNode.stylesWithoutHost; ngDevMode && isClassBased === false && staticPrefix !== null && assertEqual(staticPrefix.endsWith(';'), true, 'Expecting static portion to end with \';\''); if (staticPrefix !== null) { // We want to make sure that falsy values of `value` become empty strings. value = concatStringsWithSpace(staticPrefix, value ? value : ''); } // Given `<div [style] my-dir>` such that `my-dir` has `@Input('style')`. // This takes over the `[style]` binding. (Same for `[class]`) setDirectiveInputsWhichShadowsStyling(tView, tNode, lView, value, isClassBased); } else { updateStylingMap(tView, tNode, lView, lView[RENDERER], lView[bindingIndex + 1], lView[bindingIndex + 1] = toStylingKeyValueArray(keyValueArraySet, stringParser, value), isClassBased, bindingIndex); } } } /** * Determines when the binding is in `hostBindings` section * * @param {?} tView Current `TView` * @param {?} bindingIndex index of binding which we would like if it is in `hostBindings` * @return {?} */ function isInHostBindings(tView, bindingIndex) { // All host bindings are placed after the expando section. return bindingIndex >= tView.expandoStartIndex; } /** * Collects the necessary information to insert the binding into a linked list of style bindings * using `insertTStylingBinding`. * * @param {?} tView `TView` where the binding linked list will be stored. * @param {?} tStylingKey Property/key of the binding. * @param {?} bindingIndex Index of binding associated with the `prop` * @param {?} isClassBased `true` if `class` change (`false` if `style`) * @return {?} */ function stylingFirstUpdatePass(tView, tStylingKey, bindingIndex, isClassBased) { ngDevMode && assertFirstUpdatePass(tView); /** @type {?} */ const tData = tView.data; if (tData[bindingIndex + 1] === null) { // The above check is necessary because we don't clear first update pass until first successful // (no exception) template execution. This prevents the styling instruction from double adding // itself to the list. // `getSelectedIndex()` should be here (rather than in instruction) so that it is guarded by the // if so as not to read unnecessarily. /** @type {?} */ const tNode = (/** @type {?} */ (tData[getSelectedIndex() + HEADER_OFFSET])); /** @type {?} */ const isHostBindings = isInHostBindings(tView, bindingIndex); if (hasStylingInputShadow(tNode, isClassBased) && tStylingKey === null && !isHostBindings) { // `tStylingKey === null` implies that we are either `[style]` or `[class]` binding. // If there is a directive which uses `@Input('style')` or `@Input('class')` than // we need to neutralize this binding since that directive is shadowing it. // We turn this into a noop by setting the key to `false` tStylingKey = false; } tStylingKey = wrapInStaticStylingKey(tData, tNode, tStylingKey, isClassBased); insertTStylingBinding(tData, tNode, tStylingKey, bindingIndex, isHostBindings, isClassBased); } } /** * Adds static styling information to the binding if applicable. * * The linked list of styles not only stores the list and keys, but also stores static styling * information on some of the keys. This function determines if the key should contain the styling * information and computes it. * * See `TStylingStatic` for more details. * * @param {?} tData `TData` where the linked list is stored. * @param {?} tNode `TNode` for which the styling is being computed. * @param {?} stylingKey `TStylingKeyPrimitive` which may need to be wrapped into `TStylingKey` * @param {?} isClassBased `true` if `class` (`false` if `style`) * @return {?} */ export function wrapInStaticStylingKey(tData, tNode, stylingKey, isClassBased) { /** @type {?} */ const hostDirectiveDef = getCurrentDirectiveDef(tData); /** @type {?} */ let residual = isClassBased ? tNode.residualClasses : tNode.residualStyles; if (hostDirectiveDef === null) { // We are in template node. // If template node already had styling instruction then it has already collected the static // styling and there is no need to collect them again. We know that we are the first styling // instruction because the `TNode.*Bindings` points to 0 (nothing has been inserted yet). /** @type {?} */ const isFirstStylingInstructionInTemplate = (/** @type {?} */ ((/** @type {?} */ ((isClassBased ? tNode.classBindings : tNode.styleBindings))))) === 0; if (isFirstStylingInstructionInTemplate) { // It would be nice to be able to get the statics from `mergeAttrs`, however, at this point // they are already merged and it would not be possible to figure which property belongs where // in the priority. stylingKey = collectStylingFromDirectives(null, tData, tNode, stylingKey, isClassBased); stylingKey = collectStylingFromTAttrs(stylingKey, tNode.attrs, isClassBased); // We know that if we have styling binding in template we can't have residual. residual = null; } } else { // We are in host binding node and there was no binding instruction in template node. // This means that we need to compute the residual. /** @type {?} */ const directiveStylingLast = tNode.directiveStylingLast; /** @type {?} */ const isFirstStylingInstructionInHostBinding = directiveStylingLast === -1 || tData[directiveStylingLast] !== hostDirectiveDef; if (isFirstStylingInstructionInHostBinding) { stylingKey = collectStylingFromDirectives(hostDirectiveDef, tData, tNode, stylingKey, isClassBased); if (residual === null) { // - If `null` than either: // - Template styling instruction already ran and it has consumed the static // styling into its `TStylingKey` and so there is no need to update residual. Instead // we need to update the `TStylingKey` associated with the first template node // instruction. OR // - Some other styling instruction ran and determined that there are no residuals /** @type {?} */ let templateStylingKey = getTemplateHeadTStylingKey(tData, tNode, isClassBased); if (templateStylingKey !== undefined && Array.isArray(templateStylingKey)) { // Only recompute if `templateStylingKey` had static values. (If no static value found // then there is nothing to do since this operation can only produce less static keys, not // more.) templateStylingKey = collectStylingFromDirectives(null, tData, tNode, templateStylingKey[1] /* unwrap previous statics */, isClassBased); templateStylingKey = collectStylingFromTAttrs(templateStylingKey, tNode.attrs, isClassBased); setTemplateHeadTStylingKey(tData, tNode, isClassBased, templateStylingKey); } } else { // We only need to recompute residual if it is not `null`. // - If existing residual (implies there was no template styling). This means that some of // the statics may have moved from the residual to the `stylingKey` and so we have to // recompute. // - If `undefined` this is the first time we are running. residual = collectResidual(tData, tNode, isClassBased); } } } if (residual !== undefined) { isClassBased ? (tNode.residualClasses = residual) : (tNode.residualStyles = residual); } return stylingKey; } /** * Retrieve the `TStylingKey` for the template styling instruction. * * This is needed since `hostBinding` styling instructions are inserted after the template * instruction. While the template instruction needs to update the residual in `TNode` the * `hostBinding` instructions need to update the `TStylingKey` of the template instruction because * the template instruction is downstream from the `hostBindings` instructions. * * @param {?} tData `TData` where the linked list is stored. * @param {?} tNode `TNode` for which the styling is being computed. * @param {?} isClassBased `true` if `class` (`false` if `style`) * @return {?} `TStylingKey` if found or `undefined` if not found. */ function getTemplateHeadTStylingKey(tData, tNode, isClassBased) { /** @type {?} */ const bindings = isClassBased ? tNode.classBindings : tNode.styleBindings; if (getTStylingRangeNext(bindings) === 0) { // There does not seem to be a styling instruction in the `template`. return undefined; } return (/** @type {?} */ (tData[getTStylingRangePrev(bindings)])); } /** * Update the `TStylingKey` of the first template instruction in `TNode`. * * Logically `hostBindings` styling instructions are of lower priority than that of the template. * However, they execute after the template styling instructions. This means that they get inserted * in front of the template styling instructions. * * If we have a template styling instruction and a new `hostBindings` styling instruction is * executed it means that it may need to steal static fields from the template instruction. This * method allows us to update the first template instruction `TStylingKey` with a new value. * * Assume: * ``` * <div my-dir style="color: red" [style.color]="tmplExp"></div> * * \@Directive({ * host: { * 'style': 'width: 100px', * '[style.color]': 'dirExp', * } * }) * class MyDir {} * ``` * * when `[style.color]="tmplExp"` executes it creates this data structure. * ``` * ['', 'color', 'color', 'red', 'width', '100px'], * ``` * * The reason for this is that the template instruction does not know if there are styling * instructions and must assume that there are none and must collect all of the static styling. * (both * `color' and 'width`) * * When `'[style.color]': 'dirExp',` executes we need to insert a new data into the linked list. * ``` * ['', 'color', 'width', '100px'], // newly inserted * ['', 'color', 'color', 'red', 'width', '100px'], // this is wrong * ``` * * Notice that the template statics is now wrong as it incorrectly contains `width` so we need to * update it like so: * ``` * ['', 'color', 'width', '100px'], * ['', 'color', 'color', 'red'], // UPDATE * ``` * * @param {?} tData `TData` where the linked list is stored. * @param {?} tNode `TNode` for which the styling is being computed. * @param {?} isClassBased `true` if `class` (`false` if `style`) * @param {?} tStylingKey New `TStylingKey` which is replacing the old one. * @return {?} */ function setTemplateHeadTStylingKey(tData, tNode, isClassBased, tStylingKey) { /** @type {?} */ const bindings = isClassBased ? tNode.classBindings : tNode.styleBindings; ngDevMode && assertNotEqual(getTStylingRangeNext(bindings), 0, 'Expecting to have at least one template styling binding.'); tData[getTStylingRangePrev(bindings)] = tStylingKey; } /** * Collect all static values after the current `TNode.directiveStylingLast` index. * * Collect the remaining styling information which has not yet been collected by an existing * styling instruction. * * @param {?} tData `TData` where the `DirectiveDefs` are stored. * @param {?} tNode `TNode` which contains the directive range. * @param {?} isClassBased `true` if `class` (`false` if `style`) * @return {?} */ function collectResidual(tData, tNode, isClassBased) { /** @type {?} */ let residual = undefined; /** @type {?} */ const directiveEnd = tNode.directiveEnd; ngDevMode && assertNotEqual(tNode.directiveStylingLast, -1, 'By the time this function gets called at least one hostBindings-node styling instruction must have executed.'); // We add `1 + tNode.directiveStart` because we need to skip the current directive (as we are // collecting things after the last `hostBindings` directive which had a styling instruction.) for (let i = 1 + tNode.directiveStylingLast; i < directiveEnd; i++) { /** @type {?} */ const attrs = ((/** @type {?} */ (tData[i]))).hostAttrs; residual = (/** @type {?} */ (collectStylingFromTAttrs(residual, attrs, isClassBased))); } return (/** @type {?} */ (collectStylingFromTAttrs(residual, tNode.attrs, isClassBased))); } /** * Collect the static styling information with lower priority than `hostDirectiveDef`. * * (This is opposite of residual styling.) * * @param {?} hostDirectiveDef `DirectiveDef` for which we want to collect lower priority static * styling. (Or `null` if template styling) * @param {?} tData `TData` where the linked list is stored. * @param {?} tNode `TNode` for which the styling is being computed. * @param {?} stylingKey Existing `TStylingKey` to update or wrap. * @param {?} isClassBased `true` if `class` (`false` if `style`) * @return {?} */ function collectStylingFromDirectives(hostDirectiveDef, tData, tNode, stylingKey, isClassBased) { // We need to loop because there can be directives which have `hostAttrs` but don't have // `hostBindings` so this loop catches up to the current directive.. /** @type {?} */ let currentDirective = null; /** @type {?} */ const directiveEnd = tNode.directiveEnd; /** @type {?} */ let directiveStylingLast = tNode.directiveStylingLast; if (directiveStylingLast === -1) { directiveStylingLast = tNode.directiveStart; } else { directiveStylingLast++; } while (directiveStylingLast < directiveEnd) { currentDirective = (/** @type {?} */ (tData[directiveStylingLast])); ngDevMode && assertDefined(currentDirective, 'expected to be defined'); stylingKey = collectStylingFromTAttrs(stylingKey, currentDirective.hostAttrs, isClassBased); if (currentDirective === hostDirectiveDef) break; directiveStylingLast++; } if (hostDirectiveDef !== null) { // we only advance the styling cursor if we are collecting data from host bindings. // Template executes before host bindings and so if we would update the index, // host bindings would not get their statics. tNode.directiveStylingLast = directiveStylingLast; } return stylingKey; } /** * Convert `TAttrs` into `TStylingStatic`. * * @param {?} stylingKey existing `TStylingKey` to update or wrap. * @param {?} attrs `TAttributes` to process. * @param {?} isClassBased `true` if `class` (`false` if `style`) * @return {?} */ function collectStylingFromTAttrs(stylingKey, attrs, isClassBased) { /** @type {?} */ const desiredMarker = isClassBased ? 1 /* Classes */ : 2 /* Styles */; /** @type {?} */ let currentMarker = -1 /* ImplicitAttributes */; if (attrs !== null) { for (let i = 0; i < attrs.length; i++) { /** @type {?} */ const item = (/** @type {?} */ (attrs[i])); if (typeof item === 'number') { currentMarker = item; } else { if (currentMarker === desiredMarker) { if (!Array.isArray(stylingKey)) { stylingKey = stylingKey === undefined ? [] : (/** @type {?} */ (['', stylingKey])); } keyValueArraySet((/** @type {?} */ (stylingKey)), item, isClassBased ? true : attrs[++i]); } } } } return stylingKey === undefined ? null : stylingKey; } /** * Convert user input to `KeyValueArray`. * * This function takes user input which could be `string`, Object literal, or iterable and converts * it into a consistent representation. The output of this is `KeyValueArray` (which is an array * where * even indexes contain keys and odd indexes contain values for those keys). * * The advantage of converting to `KeyValueArray` is that we can perform diff in an input * independent * way. * (ie we can compare `foo bar` to `['bar', 'baz'] and determine a set of changes which need to be * applied) * * The fact that `KeyValueArray` is sorted is very important because it allows us to compute the * difference in linear fashion without the need to allocate any additional data. * * For example if we kept this as a `Map` we would have to iterate over previous `Map` to determine * which values need to be deleted, over the new `Map` to determine additions, and we would have to * keep additional `Map` to keep track of duplicates or items which have not yet been visited. * * @param {?} keyValueArraySet (See `keyValueArraySet` in "util/array_utils") Gets passed in as a * function so that * `style` can pass in version which does sanitization. This is done for tree shaking * purposes. * @param {?} stringParser The parser is passed in so that it will be tree shakable. See * `styleStringParser` and `classStringParser` * @param {?} value The value to parse/convert to `KeyValueArray` * @return {?} */ export function toStylingKeyValueArray(keyValueArraySet, stringParser, value) { if (value == null /*|| value === undefined */ || value === '') return (/** @type {?} */ (EMPTY_ARRAY)); /** @type {?} */ const styleKeyValueArray = (/** @type {?} */ ([])); /** @type {?} */ const unwrappedValue = (/** @type {?} */ (unwrapSafeValue(value))); if (Array.isArray(unwrappedValue)) { for (let i = 0; i < unwrappedValue.length; i++) { keyValueArraySet(styleKeyValueArray, unwrappedValue[i], true); } } else if (typeof unwrappedValue === 'object') { for (const key in unwrappedValue) { if (unwrappedValue.hasOwnProperty(key)) { keyValueArraySet(styleKeyValueArray, key, unwrappedValue[key]); } } } else if (typeof unwrappedValue === 'string') { stringParser(styleKeyValueArray, unwrappedValue); } else { ngDevMode && throwError('Unsupported styling type ' + typeof unwrappedValue + ': ' + unwrappedValue); } return styleKeyValueArray; } /** * Set a `value` for a `key` taking style sanitization into account. * * See: `keyValueArraySet` for details * * @param {?} keyValueArray KeyValueArray to add to. * @param {?} key Style key to add. (This key will be checked if it needs sanitization) * @param {?} value The value to set (If key needs sanitization it will be sanitized) * @return {?} */ export function styleKeyValueArraySet(keyValueArray, key, value) { if (stylePropNeedsSanitization(key)) { value = ɵɵsanitizeStyle(value); } keyValueArraySet(keyValueArray, key, value); } /** * Update map based styling. * * Map based styling could be anything which contains more than one binding. For example `string`, * or object literal. Dealing with all of these types would complicate the logic so * instead this function expects that the complex input is first converted into normalized * `KeyValueArray`. The advantage of normalization is that we get the values sorted, which makes it * very cheap to compute deltas between the previous and current value. * * @param {?} tView Associated `TView.data` contains the linked list of binding priorities. * @param {?} tNode `TNode` where the binding is located. * @param {?} lView `LView` contains the values associated with other styling binding at this `TNode`. * @param {?} renderer Renderer to use if any updates. * @param {?} oldKeyValueArray Previous value represented as `KeyValueArray` * @param {?} newKeyValueArray Current value represented as `KeyValueArray` * @param {?} isClassBased `true` if `class` (`false` if `style`) * @param {?} bindingIndex Binding index of the binding. * @return {?} */ function updateStylingMap(tView, tNode, lView, renderer, oldKeyValueArray, newKeyValueArray, isClassBased, bindingIndex) { if ((/** @type {?} */ (oldKeyValueArray)) === NO_CHANGE) { // On first execution the oldKeyValueArray is NO_CHANGE => treat it as empty KeyValueArray. oldKeyValueArray = (/** @type {?} */ (EMPTY_ARRAY)); } /** @type {?} */ let oldIndex = 0; /** @type {?} */ let newIndex = 0; /** @type {?} */ let oldKey = 0 < oldKeyValueArray.length ? oldKeyValueArray[0] : null; /** @type {?} */ let newKey = 0 < newKeyValueArray.length ? newKeyValueArray[0] : null; while (oldKey !== null || newKey !== null) { ngDevMode && assertLessThan(oldIndex, 999, 'Are we stuck in infinite loop?'); ngDevMode && assertLessThan(newIndex, 999, 'Are we stuck in infinite loop?'); /** @type {?} */ const oldValue = oldIndex < oldKeyValueArray.length ? oldKeyValueArray[oldIndex + 1] : undefined; /** @type {?} */ const newValue = newIndex < newKeyValueArray.length ? newKeyValueArray[newIndex + 1] : undefined; /** @type {?} */ let setKey = null; /** @type {?} */ let setValue = undefined; if (oldKey === newKey) { // UPDATE: Keys are equal => new value is overwriting old value. oldIndex += 2; newIndex += 2; if (oldValue !== newValue) { setKey = newKey; setValue = newValue; } } else if (newKey === null || oldKey !== null && oldKey < (/** @type {?} */ (newKey))) { // DELETE: oldKey key is missing or we did not find the oldKey in the newValue // (because the keyValueArray is sorted and `newKey` is found later alphabetically). // `"background" < "color"` so we need to delete `"background"` because it is not found in the // new array. oldIndex += 2; setKey = oldKey; } else { // CREATE: newKey's is earlier alphabetically than oldKey's (or no oldKey) => we have new key. // `"color" > "background"` so we need to add `color` because it is in new array but not in // old array. ngDevMode && assertDefined(newKey, 'Expecting to have a valid key'); newIndex += 2; setKey = newKey; setValue = newValue; } if (setKey !== null) { updateStyling(tView, tNode, lView, renderer, setKey, setValue, isClassBased, bindingIndex); } oldKey = oldIndex < oldKeyValueArray.length ? oldKeyValueArray[oldIndex] : null; newKey = newIndex < newKeyValueArray.length ? newKeyValueArray[newIndex] : null; } } /** * Update a simple (property name) styling. * * This function takes `prop` and updates the DOM to that value. The function takes the binding * value as well as binding priority into consideration to determine which value should be written * to DOM. (For example it may be determined that there is a higher priority overwrite which blocks * the DOM write, or if the value goes to `undefined` a lower priority overwrite may be consulted.) * * @param {?} tView Associated `TView.data` contains the linked list of binding priorities. * @param {?} tNode `TNode` where the binding is located. * @param {?} lView `LView` contains the values associated with other styling binding at this `TNode`. * @param {?} renderer Renderer to use if any updates. * @param {?} prop Either style property name or a class name. * @param {?} value Either style value for `prop` or `true`/`false` if `prop` is class. * @param {?} isClassBased `true` if `class` (`false` if `style`) * @param {?} bindingIndex Binding index of the binding. * @return {?} */ function updateStyling(tView, tNode, lView, renderer, prop, value, isClassBased, bindingIndex) { if (tNode.type !== 3 /* Element */) { // It is possible to have styling on non-elements (such as ng-container). // This is rare, but it does happen. In such a case, just ignore the binding. return; } /** @type {?} */ const tData = tView.data; /** @type {?} */ const tRange = (/** @type {?} */ (tData[bindingIndex + 1])); /** @type {?} */ const higherPriorityValue = getTStylingRangeNextDuplicate(tRange) ? findStylingValue(tData, tNode, lView, prop, getTStylingRangeNext(tRange), isClassBased) : undefined; if (!isStylingValuePresent(higherPriorityValue)) { // We don't have a next duplicate, or we did not find a duplicate value. if (!isStylingValuePresent(value)) { // We should delete current value or restore to lower priority value. if (getTStylingRangePrevDuplicate(tRange)) { // We have a possible prev duplicate, let's retrieve it. value = findStylingValue(tData, null, lView, prop, bindingIndex, isClassBased); } } /** @type {?} */ const rNode = (/** @type {?} */ (getNativeByIndex(getSelectedIndex(), lView))); applyStyling(renderer, isClassBased, rNode, prop, value); } } /** * Search for styling value with higher priority which is overwriting current value, or a * value of lower priority to which we should fall back if the value is `undefined`. * * When value is being applied at a location, related values need to be consulted. * - If there is a higher priority binding, we should be using that one instead. * For example `<div [style]="{color:exp1}" [style.color]="exp2">` change to `exp1` * requires that we check `exp2` to see if it is set to value other than `undefined`. * - If there is a lower priority binding and we are changing to `undefined` * For example `<div [style]="{color:exp1}" [style.color]="exp2">` change to `exp2` to * `undefined` requires that we check `exp1` (and static values) and use that as new value. * * NOTE: The styling stores two values. * 1. The raw value which came from the application is stored at `index + 0` location. (This value * is used for dirty checking). * 2. The normalized value (converted to `KeyValueArray` if map and sanitized) is stored at `index + * 1`. * The advantage of storing the sanitized value is that once the value is written we don't need * to worry about sanitizing it later or keeping track of the sanitizer. * * @param {?} tData `TData` used for traversing the priority. * @param {?} tNode `TNode` to use for resolving static styling. Also controls search direction. * - `TNode` search next and quit as soon as `isStylingValuePresent(value)` is true. * If no value found consult `tNode.residualStyle`/`tNode.residualClass` for default value. * - `null` search prev and go all the way to end. Return last value where * `isStylingValuePresent(value)` is true. * @param {?} lView `LView` used for retrieving the actual values. * @param {?} prop Property which we are interested in. * @param {?} index Starting index in the linked list of styling bindings where the search should start. * @param {?} isClassBased `true` if `class` (`false` if `style`) * @return {?} */ function findStylingValue(tData, tNode, lView, prop, index, isClassBased) { // `TNode` to use for resolving static styling. Also controls search direction. // - `TNode` search next and quit as soon as `isStylingValuePresent(value)` is true. // If no value found consult `tNode.residualStyle`/`tNode.residualClass` for default value. // - `null` search prev and go all the way to end. Return last value where // `isStylingValuePresent(value)` is true. /** @type {?} */ const isPrevDirection = tNode === null; /** @type {?} */ let value = undefined; while (index > 0) { /** @type {?} */ const rawKey = (/** @type {?} */ (tData[index])); /** @type {?} */ const containsStatics = Array.isArray(rawKey); // Unwrap the key if we contain static values. /** @type {?} */ const key = containsStatics ? ((/** @type {?} */ (rawKey)))[1] : rawKey; /** @type {?} */ const isStylingMap = key === null; /** @type {?} */ let valueAtLViewIndex = lView[index + 1]; if (valueAtLViewIndex === NO_CHANGE) { // In firstUpdatePass the styling instructions create a linked list of styling. // On subsequent passes it is possible for a styling instruction to try to read a binding // which // has not yet executed. In that case we will find `NO_CHANGE` and we should assume that // we have `undefined` (or empty array in case of styling-map instruction) instead. This // allows the resolution to apply the value (which may later be overwritten when the // binding actually executes.) valueAtLViewIndex = isStylingMap ? EMPTY_ARRAY : undefined; } /** @type {?} */ let currentValue = isStylingMap ? keyValueArrayGet(valueAtLViewIndex, prop) : key === prop ? valueAtLViewIndex : undefined; if (containsStatics && !isStylingValuePresent(currentValue)) { currentValue = keyValueArrayGet((/** @type {?} */ (rawKey)), prop); } if (isStylingValuePresent(currentValue)) { value = currentValue; if (isPrevDirection) { return value; } } /** @type {?} */ const tRange = (/** @type {?} */ (tData[index + 1])); index = isPrevDirection ? getTStylingRangePrev(tRange) : getTStylingRangeNext(tRange); } if (tNode !== null) { // in case where we are going in next direction AND we did not find anything, we need to // consult residual styling /** @type {?} */ let residual = isClassBased ? tNode.residualClasses : tNode.residualStyles; if (residual != null /** OR residual !=== undefined */) { value = keyValueArrayGet((/** @type {?} */ (residual)), prop); } } return value; } /** * Determines if the binding value should be used (or if the value is 'undefined' and hence priority * resolution should be used.) * * @param {?} value Binding style value. * @return {?} */ function isStylingValuePresent(value) { // Currently only `undefined` value is considered non-binding. That is `undefined` says I don't // have an opinion as to what this binding should be and you should consult other bindings by // priority to determine the valid value. // This is extracted into a single function so that we have a single place to control this. return value !== undefined; } /** * Sanitizes or adds suffix to the value. * * If value is `null`/`undefined` no suffix is added * @param {?} value * @param {?} suffixOrSanitizer * @return {?} */ function normalizeAndApplySuffixOrSanitizer(value, suffixOrSanitizer) { if (value == null /** || value === undefined */) { // do nothing } else if (typeof suffixOrSanitizer === 'function') { // sanitize the value. value = suffixOrSanitizer(value); } else if (typeof suffixOrSanitizer === 'string') { value = value + suffixOrSanitizer; } else if (typeof value === 'object') { value = stringify(unwrapSafeValue(value)); } return value; } /** * Tests if the `TNode` has input shadow. * * An input shadow is when a directive steals (shadows) the input by using `\@Input('style')` or * `\@Input('class')` as input. * * @param {?} tNode `TNode` which we would like to see if it has shadow. * @param {?} isClassBased `true` if `class` (`false` if `style`) * @return {?} */ export function hasStylingInputShadow(tNode, isClassBased) { return (tNode.flags & (isClassBased ? 16 /* hasClassInput */ : 32 /* hasStyleInput */)) !== 0; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3R5bGluZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL3BhY2thZ2VzL2NvcmUvc3JjL3JlbmRlcjMvaW5zdHJ1Y3Rpb25zL3N0eWxpbmcudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7O0FBUUEsT0FBTyxFQUFZLGVBQWUsRUFBQyxNQUFNLDJCQUEyQixDQUFDO0FBQ3JFLE9BQU8sRUFBQywwQkFBMEIsRUFBRSxlQUFlLEVBQUMsTUFBTSxpQ0FBaUMsQ0FBQztBQUU1RixPQUFPLEVBQWdCLGdCQUFnQixFQUFFLGdCQUFnQixFQUFDLE1BQU0sd0JBQXdCLENBQUM7QUFDekYsT0FBTyxFQUFDLGFBQWEsRUFBRSxXQUFXLEVBQUUsY0FBYyxFQUFFLGNBQWMsRUFBRSxVQUFVLEVBQUMsTUFBTSxtQkFBbUIsQ0FBQztBQUN6RyxPQUFPLEVBQUMsV0FBVyxFQUFDLE1BQU0sa0JBQWtCLENBQUM7QUFDN0MsT0FBTyxFQUFDLHNCQUFzQixFQUFFLFNBQVMsRUFBQyxNQUFNLHNCQUFzQixDQUFDO0FBQ3ZFLE9BQU8sRUFBQyxxQkFBcUIsRUFBQyxNQUFNLFdBQVcsQ0FBQztBQUNoRCxPQUFPLEVBQUMsY0FBYyxFQUFDLE1BQU0sYUFBYSxDQUFDO0FBSzNDLE9BQU8sRUFBQyxvQkFBb0IsRUFBRSw2QkFBNkIsRUFBRSxvQkFBb0IsRUFBRSw2QkFBNkIsRUFBNkIsTUFBTSx1QkFBdUIsQ0FBQztBQUMzSyxPQUFPLEVBQUMsYUFBYSxFQUFTLFFBQVEsRUFBZSxNQUFNLG9CQUFvQixDQUFDO0FBQ2hGLE9BQU8sRUFBQyxZQUFZLEVBQUMsTUFBTSxzQkFBc0IsQ0FBQztBQUNsRCxPQUFPLEVBQUMsc0JBQXNCLEVBQUUsd0JBQXdCLEVBQUUsUUFBUSxFQUFFLGdCQUFnQixFQUFFLFFBQVEsRUFBRSxxQkFBcUIsRUFBRSx3QkFBd0IsRUFBQyxNQUFNLFVBQVUsQ0FBQztBQUNqSyxPQUFPLEVBQUMscUJB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