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