[d565449] | 1 | var cmpChar = require('../../tokenizer').cmpChar;
|
---|
| 2 | var isDigit = require('../../tokenizer').isDigit;
|
---|
| 3 | var TYPE = require('../../tokenizer').TYPE;
|
---|
| 4 |
|
---|
| 5 | var WHITESPACE = TYPE.WhiteSpace;
|
---|
| 6 | var COMMENT = TYPE.Comment;
|
---|
| 7 | var IDENT = TYPE.Ident;
|
---|
| 8 | var NUMBER = TYPE.Number;
|
---|
| 9 | var DIMENSION = TYPE.Dimension;
|
---|
| 10 | var PLUSSIGN = 0x002B; // U+002B PLUS SIGN (+)
|
---|
| 11 | var HYPHENMINUS = 0x002D; // U+002D HYPHEN-MINUS (-)
|
---|
| 12 | var N = 0x006E; // U+006E LATIN SMALL LETTER N (n)
|
---|
| 13 | var DISALLOW_SIGN = true;
|
---|
| 14 | var ALLOW_SIGN = false;
|
---|
| 15 |
|
---|
| 16 | function checkInteger(offset, disallowSign) {
|
---|
| 17 | var pos = this.scanner.tokenStart + offset;
|
---|
| 18 | var code = this.scanner.source.charCodeAt(pos);
|
---|
| 19 |
|
---|
| 20 | if (code === PLUSSIGN || code === HYPHENMINUS) {
|
---|
| 21 | if (disallowSign) {
|
---|
| 22 | this.error('Number sign is not allowed');
|
---|
| 23 | }
|
---|
| 24 | pos++;
|
---|
| 25 | }
|
---|
| 26 |
|
---|
| 27 | for (; pos < this.scanner.tokenEnd; pos++) {
|
---|
| 28 | if (!isDigit(this.scanner.source.charCodeAt(pos))) {
|
---|
| 29 | this.error('Integer is expected', pos);
|
---|
| 30 | }
|
---|
| 31 | }
|
---|
| 32 | }
|
---|
| 33 |
|
---|
| 34 | function checkTokenIsInteger(disallowSign) {
|
---|
| 35 | return checkInteger.call(this, 0, disallowSign);
|
---|
| 36 | }
|
---|
| 37 |
|
---|
| 38 | function expectCharCode(offset, code) {
|
---|
| 39 | if (!cmpChar(this.scanner.source, this.scanner.tokenStart + offset, code)) {
|
---|
| 40 | var msg = '';
|
---|
| 41 |
|
---|
| 42 | switch (code) {
|
---|
| 43 | case N:
|
---|
| 44 | msg = 'N is expected';
|
---|
| 45 | break;
|
---|
| 46 | case HYPHENMINUS:
|
---|
| 47 | msg = 'HyphenMinus is expected';
|
---|
| 48 | break;
|
---|
| 49 | }
|
---|
| 50 |
|
---|
| 51 | this.error(msg, this.scanner.tokenStart + offset);
|
---|
| 52 | }
|
---|
| 53 | }
|
---|
| 54 |
|
---|
| 55 | // ... <signed-integer>
|
---|
| 56 | // ... ['+' | '-'] <signless-integer>
|
---|
| 57 | function consumeB() {
|
---|
| 58 | var offset = 0;
|
---|
| 59 | var sign = 0;
|
---|
| 60 | var type = this.scanner.tokenType;
|
---|
| 61 |
|
---|
| 62 | while (type === WHITESPACE || type === COMMENT) {
|
---|
| 63 | type = this.scanner.lookupType(++offset);
|
---|
| 64 | }
|
---|
| 65 |
|
---|
| 66 | if (type !== NUMBER) {
|
---|
| 67 | if (this.scanner.isDelim(PLUSSIGN, offset) ||
|
---|
| 68 | this.scanner.isDelim(HYPHENMINUS, offset)) {
|
---|
| 69 | sign = this.scanner.isDelim(PLUSSIGN, offset) ? PLUSSIGN : HYPHENMINUS;
|
---|
| 70 |
|
---|
| 71 | do {
|
---|
| 72 | type = this.scanner.lookupType(++offset);
|
---|
| 73 | } while (type === WHITESPACE || type === COMMENT);
|
---|
| 74 |
|
---|
| 75 | if (type !== NUMBER) {
|
---|
| 76 | this.scanner.skip(offset);
|
---|
| 77 | checkTokenIsInteger.call(this, DISALLOW_SIGN);
|
---|
| 78 | }
|
---|
| 79 | } else {
|
---|
| 80 | return null;
|
---|
| 81 | }
|
---|
| 82 | }
|
---|
| 83 |
|
---|
| 84 | if (offset > 0) {
|
---|
| 85 | this.scanner.skip(offset);
|
---|
| 86 | }
|
---|
| 87 |
|
---|
| 88 | if (sign === 0) {
|
---|
| 89 | type = this.scanner.source.charCodeAt(this.scanner.tokenStart);
|
---|
| 90 | if (type !== PLUSSIGN && type !== HYPHENMINUS) {
|
---|
| 91 | this.error('Number sign is expected');
|
---|
| 92 | }
|
---|
| 93 | }
|
---|
| 94 |
|
---|
| 95 | checkTokenIsInteger.call(this, sign !== 0);
|
---|
| 96 | return sign === HYPHENMINUS ? '-' + this.consume(NUMBER) : this.consume(NUMBER);
|
---|
| 97 | }
|
---|
| 98 |
|
---|
| 99 | // An+B microsyntax https://www.w3.org/TR/css-syntax-3/#anb
|
---|
| 100 | module.exports = {
|
---|
| 101 | name: 'AnPlusB',
|
---|
| 102 | structure: {
|
---|
| 103 | a: [String, null],
|
---|
| 104 | b: [String, null]
|
---|
| 105 | },
|
---|
| 106 | parse: function() {
|
---|
| 107 | /* eslint-disable brace-style*/
|
---|
| 108 | var start = this.scanner.tokenStart;
|
---|
| 109 | var a = null;
|
---|
| 110 | var b = null;
|
---|
| 111 |
|
---|
| 112 | // <integer>
|
---|
| 113 | if (this.scanner.tokenType === NUMBER) {
|
---|
| 114 | checkTokenIsInteger.call(this, ALLOW_SIGN);
|
---|
| 115 | b = this.consume(NUMBER);
|
---|
| 116 | }
|
---|
| 117 |
|
---|
| 118 | // -n
|
---|
| 119 | // -n <signed-integer>
|
---|
| 120 | // -n ['+' | '-'] <signless-integer>
|
---|
| 121 | // -n- <signless-integer>
|
---|
| 122 | // <dashndashdigit-ident>
|
---|
| 123 | else if (this.scanner.tokenType === IDENT && cmpChar(this.scanner.source, this.scanner.tokenStart, HYPHENMINUS)) {
|
---|
| 124 | a = '-1';
|
---|
| 125 |
|
---|
| 126 | expectCharCode.call(this, 1, N);
|
---|
| 127 |
|
---|
| 128 | switch (this.scanner.getTokenLength()) {
|
---|
| 129 | // -n
|
---|
| 130 | // -n <signed-integer>
|
---|
| 131 | // -n ['+' | '-'] <signless-integer>
|
---|
| 132 | case 2:
|
---|
| 133 | this.scanner.next();
|
---|
| 134 | b = consumeB.call(this);
|
---|
| 135 | break;
|
---|
| 136 |
|
---|
| 137 | // -n- <signless-integer>
|
---|
| 138 | case 3:
|
---|
| 139 | expectCharCode.call(this, 2, HYPHENMINUS);
|
---|
| 140 |
|
---|
| 141 | this.scanner.next();
|
---|
| 142 | this.scanner.skipSC();
|
---|
| 143 |
|
---|
| 144 | checkTokenIsInteger.call(this, DISALLOW_SIGN);
|
---|
| 145 |
|
---|
| 146 | b = '-' + this.consume(NUMBER);
|
---|
| 147 | break;
|
---|
| 148 |
|
---|
| 149 | // <dashndashdigit-ident>
|
---|
| 150 | default:
|
---|
| 151 | expectCharCode.call(this, 2, HYPHENMINUS);
|
---|
| 152 | checkInteger.call(this, 3, DISALLOW_SIGN);
|
---|
| 153 | this.scanner.next();
|
---|
| 154 |
|
---|
| 155 | b = this.scanner.substrToCursor(start + 2);
|
---|
| 156 | }
|
---|
| 157 | }
|
---|
| 158 |
|
---|
| 159 | // '+'? n
|
---|
| 160 | // '+'? n <signed-integer>
|
---|
| 161 | // '+'? n ['+' | '-'] <signless-integer>
|
---|
| 162 | // '+'? n- <signless-integer>
|
---|
| 163 | // '+'? <ndashdigit-ident>
|
---|
| 164 | else if (this.scanner.tokenType === IDENT || (this.scanner.isDelim(PLUSSIGN) && this.scanner.lookupType(1) === IDENT)) {
|
---|
| 165 | var sign = 0;
|
---|
| 166 | a = '1';
|
---|
| 167 |
|
---|
| 168 | // just ignore a plus
|
---|
| 169 | if (this.scanner.isDelim(PLUSSIGN)) {
|
---|
| 170 | sign = 1;
|
---|
| 171 | this.scanner.next();
|
---|
| 172 | }
|
---|
| 173 |
|
---|
| 174 | expectCharCode.call(this, 0, N);
|
---|
| 175 |
|
---|
| 176 | switch (this.scanner.getTokenLength()) {
|
---|
| 177 | // '+'? n
|
---|
| 178 | // '+'? n <signed-integer>
|
---|
| 179 | // '+'? n ['+' | '-'] <signless-integer>
|
---|
| 180 | case 1:
|
---|
| 181 | this.scanner.next();
|
---|
| 182 | b = consumeB.call(this);
|
---|
| 183 | break;
|
---|
| 184 |
|
---|
| 185 | // '+'? n- <signless-integer>
|
---|
| 186 | case 2:
|
---|
| 187 | expectCharCode.call(this, 1, HYPHENMINUS);
|
---|
| 188 |
|
---|
| 189 | this.scanner.next();
|
---|
| 190 | this.scanner.skipSC();
|
---|
| 191 |
|
---|
| 192 | checkTokenIsInteger.call(this, DISALLOW_SIGN);
|
---|
| 193 |
|
---|
| 194 | b = '-' + this.consume(NUMBER);
|
---|
| 195 | break;
|
---|
| 196 |
|
---|
| 197 | // '+'? <ndashdigit-ident>
|
---|
| 198 | default:
|
---|
| 199 | expectCharCode.call(this, 1, HYPHENMINUS);
|
---|
| 200 | checkInteger.call(this, 2, DISALLOW_SIGN);
|
---|
| 201 | this.scanner.next();
|
---|
| 202 |
|
---|
| 203 | b = this.scanner.substrToCursor(start + sign + 1);
|
---|
| 204 | }
|
---|
| 205 | }
|
---|
| 206 |
|
---|
| 207 | // <ndashdigit-dimension>
|
---|
| 208 | // <ndash-dimension> <signless-integer>
|
---|
| 209 | // <n-dimension>
|
---|
| 210 | // <n-dimension> <signed-integer>
|
---|
| 211 | // <n-dimension> ['+' | '-'] <signless-integer>
|
---|
| 212 | else if (this.scanner.tokenType === DIMENSION) {
|
---|
| 213 | var code = this.scanner.source.charCodeAt(this.scanner.tokenStart);
|
---|
| 214 | var sign = code === PLUSSIGN || code === HYPHENMINUS;
|
---|
| 215 |
|
---|
| 216 | for (var i = this.scanner.tokenStart + sign; i < this.scanner.tokenEnd; i++) {
|
---|
| 217 | if (!isDigit(this.scanner.source.charCodeAt(i))) {
|
---|
| 218 | break;
|
---|
| 219 | }
|
---|
| 220 | }
|
---|
| 221 |
|
---|
| 222 | if (i === this.scanner.tokenStart + sign) {
|
---|
| 223 | this.error('Integer is expected', this.scanner.tokenStart + sign);
|
---|
| 224 | }
|
---|
| 225 |
|
---|
| 226 | expectCharCode.call(this, i - this.scanner.tokenStart, N);
|
---|
| 227 | a = this.scanner.source.substring(start, i);
|
---|
| 228 |
|
---|
| 229 | // <n-dimension>
|
---|
| 230 | // <n-dimension> <signed-integer>
|
---|
| 231 | // <n-dimension> ['+' | '-'] <signless-integer>
|
---|
| 232 | if (i + 1 === this.scanner.tokenEnd) {
|
---|
| 233 | this.scanner.next();
|
---|
| 234 | b = consumeB.call(this);
|
---|
| 235 | } else {
|
---|
| 236 | expectCharCode.call(this, i - this.scanner.tokenStart + 1, HYPHENMINUS);
|
---|
| 237 |
|
---|
| 238 | // <ndash-dimension> <signless-integer>
|
---|
| 239 | if (i + 2 === this.scanner.tokenEnd) {
|
---|
| 240 | this.scanner.next();
|
---|
| 241 | this.scanner.skipSC();
|
---|
| 242 | checkTokenIsInteger.call(this, DISALLOW_SIGN);
|
---|
| 243 | b = '-' + this.consume(NUMBER);
|
---|
| 244 | }
|
---|
| 245 | // <ndashdigit-dimension>
|
---|
| 246 | else {
|
---|
| 247 | checkInteger.call(this, i - this.scanner.tokenStart + 2, DISALLOW_SIGN);
|
---|
| 248 | this.scanner.next();
|
---|
| 249 | b = this.scanner.substrToCursor(i + 1);
|
---|
| 250 | }
|
---|
| 251 | }
|
---|
| 252 | } else {
|
---|
| 253 | this.error();
|
---|
| 254 | }
|
---|
| 255 |
|
---|
| 256 | if (a !== null && a.charCodeAt(0) === PLUSSIGN) {
|
---|
| 257 | a = a.substr(1);
|
---|
| 258 | }
|
---|
| 259 |
|
---|
| 260 | if (b !== null && b.charCodeAt(0) === PLUSSIGN) {
|
---|
| 261 | b = b.substr(1);
|
---|
| 262 | }
|
---|
| 263 |
|
---|
| 264 | return {
|
---|
| 265 | type: 'AnPlusB',
|
---|
| 266 | loc: this.getLocation(start, this.scanner.tokenStart),
|
---|
| 267 | a: a,
|
---|
| 268 | b: b
|
---|
| 269 | };
|
---|
| 270 | },
|
---|
| 271 | generate: function(node) {
|
---|
| 272 | var a = node.a !== null && node.a !== undefined;
|
---|
| 273 | var b = node.b !== null && node.b !== undefined;
|
---|
| 274 |
|
---|
| 275 | if (a) {
|
---|
| 276 | this.chunk(
|
---|
| 277 | node.a === '+1' ? '+n' : // eslint-disable-line operator-linebreak, indent
|
---|
| 278 | node.a === '1' ? 'n' : // eslint-disable-line operator-linebreak, indent
|
---|
| 279 | node.a === '-1' ? '-n' : // eslint-disable-line operator-linebreak, indent
|
---|
| 280 | node.a + 'n' // eslint-disable-line operator-linebreak, indent
|
---|
| 281 | );
|
---|
| 282 |
|
---|
| 283 | if (b) {
|
---|
| 284 | b = String(node.b);
|
---|
| 285 | if (b.charAt(0) === '-' || b.charAt(0) === '+') {
|
---|
| 286 | this.chunk(b.charAt(0));
|
---|
| 287 | this.chunk(b.substr(1));
|
---|
| 288 | } else {
|
---|
| 289 | this.chunk('+');
|
---|
| 290 | this.chunk(b);
|
---|
| 291 | }
|
---|
| 292 | }
|
---|
| 293 | } else {
|
---|
| 294 | this.chunk(String(node.b));
|
---|
| 295 | }
|
---|
| 296 | }
|
---|
| 297 | };
|
---|