var isCustomProperty = require('../../utils/names').isCustomProperty; var TYPE = require('../../tokenizer').TYPE; var IDENTIFIER = TYPE.Identifier; var COLON = TYPE.Colon; var EXCLAMATIONMARK = TYPE.ExclamationMark; var SOLIDUS = TYPE.Solidus; var ASTERISK = TYPE.Asterisk; var DOLLARSIGN = TYPE.DollarSign; var HYPHENMINUS = TYPE.HyphenMinus; var SEMICOLON = TYPE.Semicolon; var PLUSSIGN = TYPE.PlusSign; var NUMBERSIGN = TYPE.NumberSign; function consumeValueRaw(startToken) { return this.Raw(startToken, EXCLAMATIONMARK, SEMICOLON, false, true); } function consumeCustomPropertyRaw(startToken) { return this.Raw(startToken, EXCLAMATIONMARK, SEMICOLON, false, false); } function consumeValue() { var startValueToken = this.scanner.currentToken; var value = this.Value(); if (value.type !== 'Raw' && this.scanner.eof === false && this.scanner.tokenType !== SEMICOLON && this.scanner.tokenType !== EXCLAMATIONMARK && this.scanner.isBalanceEdge(startValueToken) === false) { this.scanner.error(); } return value; } module.exports = { name: 'Declaration', structure: { important: [Boolean, String], property: String, value: ['Value', 'Raw'] }, parse: function() { var start = this.scanner.tokenStart; var startToken = this.scanner.currentToken; var property = readProperty.call(this); var customProperty = isCustomProperty(property); var parseValue = customProperty ? this.parseCustomProperty : this.parseValue; var consumeRaw = customProperty ? consumeCustomPropertyRaw : consumeValueRaw; var important = false; var value; this.scanner.skipSC(); this.scanner.eat(COLON); if (!customProperty) { this.scanner.skipSC(); } if (parseValue) { value = this.parseWithFallback(consumeValue, consumeRaw); } else { value = consumeRaw.call(this, this.scanner.currentToken); } if (this.scanner.tokenType === EXCLAMATIONMARK) { important = getImportant(this.scanner); this.scanner.skipSC(); } // Do not include semicolon to range per spec // https://drafts.csswg.org/css-syntax/#declaration-diagram if (this.scanner.eof === false && this.scanner.tokenType !== SEMICOLON && this.scanner.isBalanceEdge(startToken) === false) { this.scanner.error(); } return { type: 'Declaration', loc: this.getLocation(start, this.scanner.tokenStart), important: important, property: property, value: value }; }, generate: function(node) { this.chunk(node.property); this.chunk(':'); this.node(node.value); if (node.important) { this.chunk(node.important === true ? '!important' : '!' + node.important); } }, walkContext: 'declaration' }; function readProperty() { var start = this.scanner.tokenStart; var prefix = 0; // hacks switch (this.scanner.tokenType) { case ASTERISK: case DOLLARSIGN: case PLUSSIGN: case NUMBERSIGN: prefix = 1; break; // TODO: not sure we should support this hack case SOLIDUS: prefix = this.scanner.lookupType(1) === SOLIDUS ? 2 : 1; break; } if (this.scanner.lookupType(prefix) === HYPHENMINUS) { prefix++; } if (prefix) { this.scanner.skip(prefix); } this.scanner.eat(IDENTIFIER); return this.scanner.substrToCursor(start); } // ! ws* important function getImportant(scanner) { scanner.eat(EXCLAMATIONMARK); scanner.skipSC(); var important = scanner.consume(IDENTIFIER); // store original value in case it differ from `important` // for better original source restoring and hacks like `!ie` support return important === 'important' ? true : important; }