UNPKG

@nativescript/core

Version:

A JavaScript library providing an easy to use api for interacting with iOS and Android platform APIs.

187 lines • 5.82 kB
import { CoreTypes } from '../../core-types'; import { Trace } from '../../trace'; import { Length } from './length-shared'; export function cleanupImportantFlags(value, propertyName) { if (typeof value !== 'string') { return '' + value; } const index = value?.indexOf('!important'); if (index >= 0) { if (Trace.isEnabled()) { Trace.write(`The !important css rule is currently not supported. Property: ${propertyName}`, Trace.categories.Style, Trace.messageType.warn); } return value.substring(0, index).trim(); } return value; } function isWhiteSpaceCode(code) { return code === 32 || (code >= 9 && code <= 13) || code === 160; } const ANY_WHITE_SPACE_RE = /\s+/; const SPACES_AND_COMMAS_RE = /[\s,]+/; /** * Matches whitespace outside of parenthesis, but only reliably for a single * nesting level - fine for `rgb(a, b, c)`, wrong for `color-mix(...)` holding * nested functions. */ const FLAT_WHITE_SPACE_RE = /\s(?![^(]*\))/; /** * The comma form of {@link FLAT_WHITE_SPACE_RE}, with the same one-level limit. */ const FLAT_COMMA_RE = /,(?![^(]*\))/; function isNotEmpty(part) { return part !== ''; } function hasNestedParens(value, open) { while (open !== -1) { const nextOpen = value.indexOf('(', open + 1); if (nextOpen === -1) { return false; } const close = value.indexOf(')', open + 1); if (close === -1 || nextOpen < close) { return true; } open = nextOpen; } return false; } // The character scan below is what handles arbitrary nesting, but without a JIT // it is several times slower than the native regex engine - so values a regex // splits correctly (none or a single level of parenthesis) keep taking one. function splitTopLevel(value, commaIsSeparator) { const parts = []; let depth = 0; let start = 0; for (let i = 0, length = value.length; i < length; i++) { const code = value.charCodeAt(i); if (code === 40 /* ( */) { depth++; } else if (code === 41 /* ) */) { if (depth > 0) { depth--; } } else if (depth === 0 && (isWhiteSpaceCode(code) || (commaIsSeparator && code === 44))) { if (i > start) { parts.push(value.slice(start, i)); } start = i + 1; } } if (start < value.length) { parts.push(value.slice(start)); } return parts; } /** * Split on whitespace outside of any parenthesis, so function values keep their * arguments together whatever their nesting - e.g. `color-mix(in srgb, var(--x) 35%, transparent)`. * Consecutive whitespace produces no empty parts. */ export function splitOnTopLevelWhiteSpace(value) { const open = value.indexOf('('); if (open === -1) { return value.split(ANY_WHITE_SPACE_RE).filter(isNotEmpty); } if (!hasNestedParens(value, open)) { return value.split(FLAT_WHITE_SPACE_RE).filter(isNotEmpty); } return splitTopLevel(value, false); } /** * Like {@link splitOnTopLevelWhiteSpace}, but commas separate too - the tolerant * form positioning shorthands like `border-color` accept. */ export function splitOnTopLevelSpacesAndCommas(value) { if (!value.includes('(')) { return value.split(SPACES_AND_COMMAS_RE).filter(isNotEmpty); } return splitTopLevel(value, true); } /** * Split on commas outside of any parenthesis - e.g. `rgb(a, b, c), rgb(d, e, f)` * splits only between the two functions. Empty parts are kept, like `String.split`. */ export function splitOnTopLevelComma(value) { const open = value.indexOf('('); if (open === -1) { return value.split(','); } if (!hasNestedParens(value, open)) { return value.split(FLAT_COMMA_RE); } return splitTopLevelComma(value); } function splitTopLevelComma(value) { const parts = []; let depth = 0; let start = 0; for (let i = 0, length = value.length; i < length; i++) { const code = value.charCodeAt(i); if (code === 40 /* ( */) { depth++; } else if (code === 41 /* ) */) { if (depth > 0) { depth--; } } else if (depth === 0 && code === 44 /* , */) { parts.push(value.slice(start, i)); start = i + 1; } } parts.push(value.slice(start)); return parts; } /** * Matches a Length value with or without a unit */ const LENGTH_RE = /^-?[0-9]+[a-zA-Z%]*?$/; /** * Checks if the value is a Length or 0 */ const isLength = (v) => v === '0' || LENGTH_RE.test(v); export function parseCSSCommaSeparatedListOfValues(value) { const values = []; if (!value) { return []; } return splitOnTopLevelComma(value); } export function parseCSSShorthand(value) { const parts = splitOnTopLevelWhiteSpace(value.trim()); const first = parts[0]; if (!first || ['none', 'unset'].includes(first)) { return null; } const invalidColors = ['inset', 'unset']; const inset = parts.includes('inset'); const last = parts[parts.length - 1]; let color = 'black'; if (first && !isLength(first) && !invalidColors.includes(first)) { color = first; } else if (last && !isLength(last) && !invalidColors.includes(last)) { color = last; } const values = parts .filter((n) => !invalidColors.includes(n)) .filter((n) => n !== color) .map((val) => { try { return Length.parse(val); } catch (err) { return CoreTypes.zeroLength; } }); return { inset, color, values, }; } //# sourceMappingURL=css-utils.js.map