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