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(']');
|
}
|
};
|