You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
170 lines
4.9 KiB
170 lines
4.9 KiB
2 years ago
|
var isCustomProperty = require('../../utils/names').isCustomProperty;
|
||
|
var TYPE = require('../../tokenizer').TYPE;
|
||
|
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 WHITESPACE = TYPE.WhiteSpace;
|
||
|
var EXCLAMATIONMARK = 0x0021; // U+0021 EXCLAMATION MARK (!)
|
||
|
var NUMBERSIGN = 0x0023; // U+0023 NUMBER SIGN (#)
|
||
|
var DOLLARSIGN = 0x0024; // U+0024 DOLLAR SIGN ($)
|
||
|
var AMPERSAND = 0x0026; // U+0026 ANPERSAND (&)
|
||
|
var ASTERISK = 0x002A; // U+002A ASTERISK (*)
|
||
|
var PLUSSIGN = 0x002B; // U+002B PLUS SIGN (+)
|
||
|
var SOLIDUS = 0x002F; // U+002F SOLIDUS (/)
|
||
|
|
||
|
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);
|
||
|
|
||
|
const valueStart = this.scanner.tokenIndex;
|
||
|
|
||
|
if (!customProperty) {
|
||
|
this.scanner.skipSC();
|
||
|
}
|
||
|
|
||
|
if (parseValue) {
|
||
|
value = this.parseWithFallback(consumeValue, consumeRaw);
|
||
|
} else {
|
||
|
value = consumeRaw.call(this, this.scanner.tokenIndex);
|
||
|
}
|
||
|
|
||
|
if (customProperty && value.type === 'Value' && value.children.isEmpty()) {
|
||
|
for (let offset = valueStart - this.scanner.tokenIndex; offset <= 0; offset++) {
|
||
|
if (this.scanner.lookupType(offset) === WHITESPACE) {
|
||
|
value.children.appendData({
|
||
|
type: 'WhiteSpace',
|
||
|
loc: null,
|
||
|
value: ' '
|
||
|
});
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|