var isCustomProperty = require('../../utils/names').isCustomProperty; var TYPE = require('../../tokenizer').TYPE; var CHARCODE = require('../../tokenizer').CHARCODE; var rawMode = require('./Raw').mode; var IDENT = TYPE.Ident; var HASH = TYPE.Hash; var COLON = TYPE.Colon; var SEMICOLON = TYPE.Semicolon; var DELIM = TYPE.Delim; var EXCLAMATIONMARK = CHARCODE.ExclamationMark; var SOLIDUS = CHARCODE.Solidus; var ASTERISK = CHARCODE.Asterisk; var AMPERSAND = CHARCODE.Ampersand; var DOLLARSIGN = CHARCODE.DollarSign; var PLUSSIGN = CHARCODE.PlusSign; var NUMBERSIGN = CHARCODE.NumberSign; function consumeValueRaw(startToken) { return this.Raw(startToken, rawMode.exclamationMarkOrSemicolon, true); } function consumeCustomPropertyRaw(startToken) { return this.Raw(startToken, rawMode.exclamationMarkOrSemicolon, false); } function consumeValue() { var startValueToken = this.scanner.tokenIndex; var value = this.Value(); if (value.type !== 'Raw' && this.scanner.eof === false && this.scanner.tokenType !== SEMICOLON && this.scanner.isDelim(EXCLAMATIONMARK) === false && this.scanner.isBalanceEdge(startValueToken) === false) { this.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.tokenIndex; 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.eat(COLON); if (!customProperty) { this.scanner.skipSC(); } if (parseValue) { value = this.parseWithFallback(consumeValue, consumeRaw); } else { value = consumeRaw.call(this, this.scanner.tokenIndex); } if (this.scanner.isDelim(EXCLAMATIONMARK)) { important = getImportant.call(this); 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.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 if (this.scanner.tokenType === DELIM) { switch (this.scanner.source.charCodeAt(this.scanner.tokenStart)) { case ASTERISK: case DOLLARSIGN: case PLUSSIGN: case NUMBERSIGN: case AMPERSAND: this.scanner.next(); break; // TODO: not sure we should support this hack case SOLIDUS: this.scanner.next(); if (this.scanner.isDelim(SOLIDUS)) { this.scanner.next(); } break; } } if (prefix) { this.scanner.skip(prefix); } if (this.scanner.tokenType === HASH) { this.eat(HASH); } else { this.eat(IDENT); } return this.scanner.substrToCursor(start); } // ! ws* important function getImportant() { this.eat(DELIM); this.scanner.skipSC(); var important = this.consume(IDENT); // store original value in case it differ from `important` // for better original source restoring and hacks like `!ie` support return important === 'important' ? true : important; }