'use strict'; var names = require('../utils/names.js'); // https://www.w3.org/TR/css-values-3/#lengths var LENGTH = { // absolute length units 'px': true, 'mm': true, 'cm': true, 'in': true, 'pt': true, 'pc': true, 'q': true, // relative length units 'em': true, 'ex': true, 'ch': true, 'rem': true, // viewport-percentage lengths 'vh': true, 'vw': true, 'vmin': true, 'vmax': true, 'vm': true }; var ANGLE = { 'deg': true, 'grad': true, 'rad': true, 'turn': true }; var TIME = { 's': true, 'ms': true }; var FREQUENCY = { 'hz': true, 'khz': true }; // https://www.w3.org/TR/css-values-3/#resolution (https://drafts.csswg.org/css-values/#resolution) var RESOLUTION = { 'dpi': true, 'dpcm': true, 'dppx': true, 'x': true // https://github.com/w3c/csswg-drafts/issues/461 }; // https://drafts.csswg.org/css-grid/#fr-unit var FLEX = { 'fr': true }; // https://www.w3.org/TR/css3-speech/#mixing-props-voice-volume var DECIBEL = { 'db': true }; // https://www.w3.org/TR/css3-speech/#voice-props-voice-pitch var SEMITONES = { 'st': true }; // can be used wherever <length>, <frequency>, <angle>, <time>, <percentage>, <number>, or <integer> values are allowed // https://drafts.csswg.org/css-values/#calc-notation function isCalc(node) { if (node.data.type !== 'Function') { return false; } var keyword = names.keyword(node.data.name); // check the function name return ( keyword.name === 'calc' || keyword.name === '-moz-calc' || keyword.name === '-webkit-calc' ); } function astNode(type) { return function(node) { return node.data.type === type; }; } function dimension(type) { return function(node) { return isCalc(node) || (node.data.type === 'Dimension' && type.hasOwnProperty(node.data.unit.toLowerCase())); }; } function zeroUnitlessDimension(type) { return function(node) { return isCalc(node) || (node.data.type === 'Dimension' && type.hasOwnProperty(node.data.unit.toLowerCase())) || (node.data.type === 'Number' && Number(node.data.value) === 0); }; } function attr(node) { return node.data.type === 'Function' && node.data.name.toLowerCase() === 'attr'; } function number(node) { return isCalc(node) || node.data.type === 'Number'; } function numberZeroOne(node) { if (isCalc(node) || node.data.type === 'Number') { var value = Number(node.data.value); return value >= 0 && value <= 1; } return false; } function numberOneOrGreater(node) { if (isCalc(node) || node.data.type === 'Number') { return Number(node.data.value) >= 1; } return false; } // TODO: fail on 10e-2 function integer(node) { return isCalc(node) || (node.data.type === 'Number' && node.data.value.indexOf('.') === -1); } // TODO: fail on 10e-2 function positiveInteger(node) { return isCalc(node) || (node.data.type === 'Number' && node.data.value.indexOf('.') === -1 && node.data.value.charAt(0) !== '-'); } function percentage(node) { return isCalc(node) || node.data.type === 'Percentage'; } function hexColor(node) { if (node.data.type !== 'HexColor') { return false; } var hex = node.data.value; return /^[0-9a-fA-F]{3,8}$/.test(hex) && (hex.length === 3 || hex.length === 4 || hex.length === 6 || hex.length === 8); } function expression(node) { return node.data.type === 'Function' && node.data.name.toLowerCase() === 'expression'; } // https://developer.mozilla.org/en-US/docs/Web/CSS/custom-ident // https://drafts.csswg.org/css-values-4/#identifier-value function customIdent(node) { if (node.data.type !== 'Identifier') { return false; } var name = node.data.name.toLowerCase(); // ยง 3.2. Author-defined Identifiers: the <custom-ident> type // The CSS-wide keywords are not valid <custom-ident>s if (name === 'unset' || name === 'initial' || name === 'inherit') { return false; } // The default keyword is reserved and is also not a valid <custom-ident> if (name === 'default') { return false; } // TODO: ignore property specific keywords (as described https://developer.mozilla.org/en-US/docs/Web/CSS/custom-ident) return true; } module.exports = { 'angle': zeroUnitlessDimension(ANGLE), 'attr()': attr, 'custom-ident': customIdent, 'decibel': dimension(DECIBEL), 'dimension': astNode('Dimension'), 'frequency': dimension(FREQUENCY), 'flex': dimension(FLEX), 'hex-color': hexColor, 'id-selector': astNode('IdSelector'), // element( <id-selector> ) 'ident': astNode('Identifier'), 'integer': integer, 'length': zeroUnitlessDimension(LENGTH), 'number': number, 'number-zero-one': numberZeroOne, 'number-one-or-greater': numberOneOrGreater, 'percentage': percentage, 'positive-integer': positiveInteger, 'resolution': dimension(RESOLUTION), 'semitones': dimension(SEMITONES), 'string': astNode('String'), 'time': dimension(TIME), 'unicode-range': astNode('UnicodeRange'), 'url': astNode('Url'), // old IE stuff 'progid': astNode('Raw'), 'expression': expression };