| 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; |
| } |