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