blob: fb41359a4b7f195d11d2cbb1f168145dd7ccb1ae [file] [log] [blame]
var TYPE = require('../../tokenizer').TYPE;
var IDENTIFIER = TYPE.Identifier;
var STRING = TYPE.String;
var DOLLARSIGN = TYPE.DollarSign;
var ASTERISK = TYPE.Asterisk;
var COLON = TYPE.Colon;
var EQUALSSIGN = TYPE.EqualsSign;
var LEFTSQUAREBRACKET = TYPE.LeftSquareBracket;
var RIGHTSQUAREBRACKET = TYPE.RightSquareBracket;
var CIRCUMFLEXACCENT = TYPE.CircumflexAccent;
var VERTICALLINE = TYPE.VerticalLine;
var TILDE = TYPE.Tilde;
function getAttributeName() {
if (this.scanner.eof) {
this.scanner.error('Unexpected end of input');
}
var start = this.scanner.tokenStart;
var expectIdentifier = false;
var checkColon = true;
if (this.scanner.tokenType === ASTERISK) {
expectIdentifier = true;
checkColon = false;
this.scanner.next();
} else if (this.scanner.tokenType !== VERTICALLINE) {
this.scanner.eat(IDENTIFIER);
}
if (this.scanner.tokenType === VERTICALLINE) {
if (this.scanner.lookupType(1) !== EQUALSSIGN) {
this.scanner.next();
this.scanner.eat(IDENTIFIER);
} else if (expectIdentifier) {
this.scanner.error('Identifier is expected', this.scanner.tokenEnd);
}
} else if (expectIdentifier) {
this.scanner.error('Vertical line is expected');
}
if (checkColon && this.scanner.tokenType === COLON) {
this.scanner.next();
this.scanner.eat(IDENTIFIER);
}
return {
type: 'Identifier',
loc: this.getLocation(start, this.scanner.tokenStart),
name: this.scanner.substrToCursor(start)
};
}
function getOperator() {
var start = this.scanner.tokenStart;
var tokenType = this.scanner.tokenType;
if (tokenType !== EQUALSSIGN && // =
tokenType !== TILDE && // ~=
tokenType !== CIRCUMFLEXACCENT && // ^=
tokenType !== DOLLARSIGN && // $=
tokenType !== ASTERISK && // *=
tokenType !== VERTICALLINE // |=
) {
this.scanner.error('Attribute selector (=, ~=, ^=, $=, *=, |=) is expected');
}
if (tokenType === EQUALSSIGN) {
this.scanner.next();
} else {
this.scanner.next();
this.scanner.eat(EQUALSSIGN);
}
return this.scanner.substrToCursor(start);
}
// '[' S* attrib_name ']'
// '[' S* attrib_name S* attrib_matcher S* [ IDENT | STRING ] S* attrib_flags? S* ']'
module.exports = {
name: 'AttributeSelector',
structure: {
name: 'Identifier',
matcher: [String, null],
value: ['String', 'Identifier', null],
flags: [String, null]
},
parse: function() {
var start = this.scanner.tokenStart;
var name;
var matcher = null;
var value = null;
var flags = null;
this.scanner.eat(LEFTSQUAREBRACKET);
this.scanner.skipSC();
name = getAttributeName.call(this);
this.scanner.skipSC();
if (this.scanner.tokenType !== RIGHTSQUAREBRACKET) {
// avoid case `[name i]`
if (this.scanner.tokenType !== IDENTIFIER) {
matcher = getOperator.call(this);
this.scanner.skipSC();
value = this.scanner.tokenType === STRING
? this.String()
: this.Identifier();
this.scanner.skipSC();
}
// attribute flags
if (this.scanner.tokenType === IDENTIFIER) {
flags = this.scanner.getTokenValue();
this.scanner.next();
this.scanner.skipSC();
}
}
this.scanner.eat(RIGHTSQUAREBRACKET);
return {
type: 'AttributeSelector',
loc: this.getLocation(start, this.scanner.tokenStart),
name: name,
matcher: matcher,
value: value,
flags: flags
};
},
generate: function(node) {
var flagsPrefix = ' ';
this.chunk('[');
this.node(node.name);
if (node.matcher !== null) {
this.chunk(node.matcher);
if (node.value !== null) {
this.node(node.value);
// space between string and flags is not required
if (node.value.type === 'String') {
flagsPrefix = '';
}
}
}
if (node.flags !== null) {
this.chunk(flagsPrefix);
this.chunk(node.flags);
}
this.chunk(']');
}
};