| var cmpChar = require('../../tokenizer').cmpChar; | 
| var isDigit = require('../../tokenizer').isDigit; | 
| var TYPE = require('../../tokenizer').TYPE; | 
|   | 
| var WHITESPACE = TYPE.WhiteSpace; | 
| var COMMENT = TYPE.Comment; | 
| var IDENT = TYPE.Ident; | 
| var NUMBER = TYPE.Number; | 
| var DIMENSION = TYPE.Dimension; | 
| var PLUSSIGN = 0x002B;    // U+002B PLUS SIGN (+) | 
| var HYPHENMINUS = 0x002D; // U+002D HYPHEN-MINUS (-) | 
| var N = 0x006E;           // U+006E LATIN SMALL LETTER N (n) | 
| var DISALLOW_SIGN = true; | 
| var ALLOW_SIGN = false; | 
|   | 
| function checkInteger(offset, disallowSign) { | 
|     var pos = this.scanner.tokenStart + offset; | 
|     var code = this.scanner.source.charCodeAt(pos); | 
|   | 
|     if (code === PLUSSIGN || code === HYPHENMINUS) { | 
|         if (disallowSign) { | 
|             this.error('Number sign is not allowed'); | 
|         } | 
|         pos++; | 
|     } | 
|   | 
|     for (; pos < this.scanner.tokenEnd; pos++) { | 
|         if (!isDigit(this.scanner.source.charCodeAt(pos))) { | 
|             this.error('Integer is expected', pos); | 
|         } | 
|     } | 
| } | 
|   | 
| function checkTokenIsInteger(disallowSign) { | 
|     return checkInteger.call(this, 0, disallowSign); | 
| } | 
|   | 
| function expectCharCode(offset, code) { | 
|     if (!cmpChar(this.scanner.source, this.scanner.tokenStart + offset, code)) { | 
|         var msg = ''; | 
|   | 
|         switch (code) { | 
|             case N: | 
|                 msg = 'N is expected'; | 
|                 break; | 
|             case HYPHENMINUS: | 
|                 msg = 'HyphenMinus is expected'; | 
|                 break; | 
|         } | 
|   | 
|         this.error(msg, this.scanner.tokenStart + offset); | 
|     } | 
| } | 
|   | 
| // ... <signed-integer> | 
| // ... ['+' | '-'] <signless-integer> | 
| function consumeB() { | 
|     var offset = 0; | 
|     var sign = 0; | 
|     var type = this.scanner.tokenType; | 
|   | 
|     while (type === WHITESPACE || type === COMMENT) { | 
|         type = this.scanner.lookupType(++offset); | 
|     } | 
|   | 
|     if (type !== NUMBER) { | 
|         if (this.scanner.isDelim(PLUSSIGN, offset) || | 
|             this.scanner.isDelim(HYPHENMINUS, offset)) { | 
|             sign = this.scanner.isDelim(PLUSSIGN, offset) ? PLUSSIGN : HYPHENMINUS; | 
|   | 
|             do { | 
|                 type = this.scanner.lookupType(++offset); | 
|             } while (type === WHITESPACE || type === COMMENT); | 
|   | 
|             if (type !== NUMBER) { | 
|                 this.scanner.skip(offset); | 
|                 checkTokenIsInteger.call(this, DISALLOW_SIGN); | 
|             } | 
|         } else { | 
|             return null; | 
|         } | 
|     } | 
|   | 
|     if (offset > 0) { | 
|         this.scanner.skip(offset); | 
|     } | 
|   | 
|     if (sign === 0) { | 
|         type = this.scanner.source.charCodeAt(this.scanner.tokenStart); | 
|         if (type !== PLUSSIGN && type !== HYPHENMINUS) { | 
|             this.error('Number sign is expected'); | 
|         } | 
|     } | 
|   | 
|     checkTokenIsInteger.call(this, sign !== 0); | 
|     return sign === HYPHENMINUS ? '-' + this.consume(NUMBER) : this.consume(NUMBER); | 
| } | 
|   | 
| // An+B microsyntax https://www.w3.org/TR/css-syntax-3/#anb | 
| module.exports = { | 
|     name: 'AnPlusB', | 
|     structure: { | 
|         a: [String, null], | 
|         b: [String, null] | 
|     }, | 
|     parse: function() { | 
|         /* eslint-disable brace-style*/ | 
|         var start = this.scanner.tokenStart; | 
|         var a = null; | 
|         var b = null; | 
|   | 
|         // <integer> | 
|         if (this.scanner.tokenType === NUMBER) { | 
|             checkTokenIsInteger.call(this, ALLOW_SIGN); | 
|             b = this.consume(NUMBER); | 
|         } | 
|   | 
|         // -n | 
|         // -n <signed-integer> | 
|         // -n ['+' | '-'] <signless-integer> | 
|         // -n- <signless-integer> | 
|         // <dashndashdigit-ident> | 
|         else if (this.scanner.tokenType === IDENT && cmpChar(this.scanner.source, this.scanner.tokenStart, HYPHENMINUS)) { | 
|             a = '-1'; | 
|   | 
|             expectCharCode.call(this, 1, N); | 
|   | 
|             switch (this.scanner.getTokenLength()) { | 
|                 // -n | 
|                 // -n <signed-integer> | 
|                 // -n ['+' | '-'] <signless-integer> | 
|                 case 2: | 
|                     this.scanner.next(); | 
|                     b = consumeB.call(this); | 
|                     break; | 
|   | 
|                 // -n- <signless-integer> | 
|                 case 3: | 
|                     expectCharCode.call(this, 2, HYPHENMINUS); | 
|   | 
|                     this.scanner.next(); | 
|                     this.scanner.skipSC(); | 
|   | 
|                     checkTokenIsInteger.call(this, DISALLOW_SIGN); | 
|   | 
|                     b = '-' + this.consume(NUMBER); | 
|                     break; | 
|   | 
|                 // <dashndashdigit-ident> | 
|                 default: | 
|                     expectCharCode.call(this, 2, HYPHENMINUS); | 
|                     checkInteger.call(this, 3, DISALLOW_SIGN); | 
|                     this.scanner.next(); | 
|   | 
|                     b = this.scanner.substrToCursor(start + 2); | 
|             } | 
|         } | 
|   | 
|         // '+'? n | 
|         // '+'? n <signed-integer> | 
|         // '+'? n ['+' | '-'] <signless-integer> | 
|         // '+'? n- <signless-integer> | 
|         // '+'? <ndashdigit-ident> | 
|         else if (this.scanner.tokenType === IDENT || (this.scanner.isDelim(PLUSSIGN) && this.scanner.lookupType(1) === IDENT)) { | 
|             var sign = 0; | 
|             a = '1'; | 
|   | 
|             // just ignore a plus | 
|             if (this.scanner.isDelim(PLUSSIGN)) { | 
|                 sign = 1; | 
|                 this.scanner.next(); | 
|             } | 
|   | 
|             expectCharCode.call(this, 0, N); | 
|   | 
|             switch (this.scanner.getTokenLength()) { | 
|                 // '+'? n | 
|                 // '+'? n <signed-integer> | 
|                 // '+'? n ['+' | '-'] <signless-integer> | 
|                 case 1: | 
|                     this.scanner.next(); | 
|                     b = consumeB.call(this); | 
|                     break; | 
|   | 
|                 // '+'? n- <signless-integer> | 
|                 case 2: | 
|                     expectCharCode.call(this, 1, HYPHENMINUS); | 
|   | 
|                     this.scanner.next(); | 
|                     this.scanner.skipSC(); | 
|   | 
|                     checkTokenIsInteger.call(this, DISALLOW_SIGN); | 
|   | 
|                     b = '-' + this.consume(NUMBER); | 
|                     break; | 
|   | 
|                 // '+'? <ndashdigit-ident> | 
|                 default: | 
|                     expectCharCode.call(this, 1, HYPHENMINUS); | 
|                     checkInteger.call(this, 2, DISALLOW_SIGN); | 
|                     this.scanner.next(); | 
|   | 
|                     b = this.scanner.substrToCursor(start + sign + 1); | 
|             } | 
|         } | 
|   | 
|         // <ndashdigit-dimension> | 
|         // <ndash-dimension> <signless-integer> | 
|         // <n-dimension> | 
|         // <n-dimension> <signed-integer> | 
|         // <n-dimension> ['+' | '-'] <signless-integer> | 
|         else if (this.scanner.tokenType === DIMENSION) { | 
|             var code = this.scanner.source.charCodeAt(this.scanner.tokenStart); | 
|             var sign = code === PLUSSIGN || code === HYPHENMINUS; | 
|   | 
|             for (var i = this.scanner.tokenStart + sign; i < this.scanner.tokenEnd; i++) { | 
|                 if (!isDigit(this.scanner.source.charCodeAt(i))) { | 
|                     break; | 
|                 } | 
|             } | 
|   | 
|             if (i === this.scanner.tokenStart + sign) { | 
|                 this.error('Integer is expected', this.scanner.tokenStart + sign); | 
|             } | 
|   | 
|             expectCharCode.call(this, i - this.scanner.tokenStart, N); | 
|             a = this.scanner.source.substring(start, i); | 
|   | 
|             // <n-dimension> | 
|             // <n-dimension> <signed-integer> | 
|             // <n-dimension> ['+' | '-'] <signless-integer> | 
|             if (i + 1 === this.scanner.tokenEnd) { | 
|                 this.scanner.next(); | 
|                 b = consumeB.call(this); | 
|             } else { | 
|                 expectCharCode.call(this, i - this.scanner.tokenStart + 1, HYPHENMINUS); | 
|   | 
|                 // <ndash-dimension> <signless-integer> | 
|                 if (i + 2 === this.scanner.tokenEnd) { | 
|                     this.scanner.next(); | 
|                     this.scanner.skipSC(); | 
|                     checkTokenIsInteger.call(this, DISALLOW_SIGN); | 
|                     b = '-' + this.consume(NUMBER); | 
|                 } | 
|                 // <ndashdigit-dimension> | 
|                 else { | 
|                     checkInteger.call(this, i - this.scanner.tokenStart + 2, DISALLOW_SIGN); | 
|                     this.scanner.next(); | 
|                     b = this.scanner.substrToCursor(i + 1); | 
|                 } | 
|             } | 
|         } else { | 
|             this.error(); | 
|         } | 
|   | 
|         if (a !== null && a.charCodeAt(0) === PLUSSIGN) { | 
|             a = a.substr(1); | 
|         } | 
|   | 
|         if (b !== null && b.charCodeAt(0) === PLUSSIGN) { | 
|             b = b.substr(1); | 
|         } | 
|   | 
|         return { | 
|             type: 'AnPlusB', | 
|             loc: this.getLocation(start, this.scanner.tokenStart), | 
|             a: a, | 
|             b: b | 
|         }; | 
|     }, | 
|     generate: function(node) { | 
|         var a = node.a !== null && node.a !== undefined; | 
|         var b = node.b !== null && node.b !== undefined; | 
|   | 
|         if (a) { | 
|             this.chunk( | 
|                 node.a === '+1' ? '+n' : // eslint-disable-line operator-linebreak, indent | 
|                 node.a ===  '1' ?  'n' : // eslint-disable-line operator-linebreak, indent | 
|                 node.a === '-1' ? '-n' : // eslint-disable-line operator-linebreak, indent | 
|                 node.a + 'n'             // eslint-disable-line operator-linebreak, indent | 
|             ); | 
|   | 
|             if (b) { | 
|                 b = String(node.b); | 
|                 if (b.charAt(0) === '-' || b.charAt(0) === '+') { | 
|                     this.chunk(b.charAt(0)); | 
|                     this.chunk(b.substr(1)); | 
|                 } else { | 
|                     this.chunk('+'); | 
|                     this.chunk(b); | 
|                 } | 
|             } | 
|         } else { | 
|             this.chunk(String(node.b)); | 
|         } | 
|     } | 
| }; |