|
|
var TYPE = require('../../tokenizer').TYPE;
var IDENT = TYPE.Ident; var STRING = TYPE.String; var COLON = TYPE.Colon; var LEFTSQUAREBRACKET = TYPE.LeftSquareBracket; var RIGHTSQUAREBRACKET = TYPE.RightSquareBracket; var DOLLARSIGN = 0x0024; // U+0024 DOLLAR SIGN ($)
var ASTERISK = 0x002A; // U+002A ASTERISK (*)
var EQUALSSIGN = 0x003D; // U+003D EQUALS SIGN (=)
var CIRCUMFLEXACCENT = 0x005E; // U+005E (^)
var VERTICALLINE = 0x007C; // U+007C VERTICAL LINE (|)
var TILDE = 0x007E; // U+007E TILDE (~)
function getAttributeName() { if (this.scanner.eof) { this.error('Unexpected end of input'); }
var start = this.scanner.tokenStart; var expectIdent = false; var checkColon = true;
if (this.scanner.isDelim(ASTERISK)) { expectIdent = true; checkColon = false; this.scanner.next(); } else if (!this.scanner.isDelim(VERTICALLINE)) { this.eat(IDENT); }
if (this.scanner.isDelim(VERTICALLINE)) { if (this.scanner.source.charCodeAt(this.scanner.tokenStart + 1) !== EQUALSSIGN) { this.scanner.next(); this.eat(IDENT); } else if (expectIdent) { this.error('Identifier is expected', this.scanner.tokenEnd); } } else if (expectIdent) { this.error('Vertical line is expected'); }
if (checkColon && this.scanner.tokenType === COLON) { this.scanner.next(); this.eat(IDENT); }
return { type: 'Identifier', loc: this.getLocation(start, this.scanner.tokenStart), name: this.scanner.substrToCursor(start) }; }
function getOperator() { var start = this.scanner.tokenStart; var code = this.scanner.source.charCodeAt(start);
if (code !== EQUALSSIGN && // =
code !== TILDE && // ~=
code !== CIRCUMFLEXACCENT && // ^=
code !== DOLLARSIGN && // $=
code !== ASTERISK && // *=
code !== VERTICALLINE // |=
) { this.error('Attribute selector (=, ~=, ^=, $=, *=, |=) is expected'); }
this.scanner.next();
if (code !== EQUALSSIGN) { if (!this.scanner.isDelim(EQUALSSIGN)) { this.error('Equal sign is expected'); }
this.scanner.next(); }
return this.scanner.substrToCursor(start); }
// '[' <wq-name> ']'
// '[' <wq-name> <attr-matcher> [ <string-token> | <ident-token> ] <attr-modifier>? ']'
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.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 !== IDENT) { 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 === IDENT) { flags = this.scanner.getTokenValue(); this.scanner.next();
this.scanner.skipSC(); } }
this.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(']'); } };
|