160 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			160 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| var isHexDigit = require('../tokenizer').isHexDigit;
 | |
| var cmpChar = require('../tokenizer').cmpChar;
 | |
| var TYPE = require('../tokenizer').TYPE;
 | |
| 
 | |
| var IDENT = TYPE.Ident;
 | |
| var DELIM = TYPE.Delim;
 | |
| var NUMBER = TYPE.Number;
 | |
| var DIMENSION = TYPE.Dimension;
 | |
| var PLUSSIGN = 0x002B;     // U+002B PLUS SIGN (+)
 | |
| var HYPHENMINUS = 0x002D;  // U+002D HYPHEN-MINUS (-)
 | |
| var QUESTIONMARK = 0x003F; // U+003F QUESTION MARK (?)
 | |
| var U = 0x0075;            // U+0075 LATIN SMALL LETTER U (u)
 | |
| 
 | |
| function isDelim(token, code) {
 | |
|     return token !== null && token.type === DELIM && token.value.charCodeAt(0) === code;
 | |
| }
 | |
| 
 | |
| function startsWith(token, code) {
 | |
|     return token.value.charCodeAt(0) === code;
 | |
| }
 | |
| 
 | |
| function hexSequence(token, offset, allowDash) {
 | |
|     for (var pos = offset, hexlen = 0; pos < token.value.length; pos++) {
 | |
|         var code = token.value.charCodeAt(pos);
 | |
| 
 | |
|         if (code === HYPHENMINUS && allowDash && hexlen !== 0) {
 | |
|             if (hexSequence(token, offset + hexlen + 1, false) > 0) {
 | |
|                 return 6; // dissallow following question marks
 | |
|             }
 | |
| 
 | |
|             return 0; // dash at the ending of a hex sequence is not allowed
 | |
|         }
 | |
| 
 | |
|         if (!isHexDigit(code)) {
 | |
|             return 0; // not a hex digit
 | |
|         }
 | |
| 
 | |
|         if (++hexlen > 6) {
 | |
|             return 0; // too many hex digits
 | |
|         };
 | |
|     }
 | |
| 
 | |
|     return hexlen;
 | |
| }
 | |
| 
 | |
| function withQuestionMarkSequence(consumed, length, getNextToken) {
 | |
|     if (!consumed) {
 | |
|         return 0; // nothing consumed
 | |
|     }
 | |
| 
 | |
|     while (isDelim(getNextToken(length), QUESTIONMARK)) {
 | |
|         if (++consumed > 6) {
 | |
|             return 0; // too many question marks
 | |
|         }
 | |
| 
 | |
|         length++;
 | |
|     }
 | |
| 
 | |
|     return length;
 | |
| }
 | |
| 
 | |
| // https://drafts.csswg.org/css-syntax/#urange
 | |
| // Informally, the <urange> production has three forms:
 | |
| // U+0001
 | |
| //      Defines a range consisting of a single code point, in this case the code point "1".
 | |
| // U+0001-00ff
 | |
| //      Defines a range of codepoints between the first and the second value, in this case
 | |
| //      the range between "1" and "ff" (255 in decimal) inclusive.
 | |
| // U+00??
 | |
| //      Defines a range of codepoints where the "?" characters range over all hex digits,
 | |
| //      in this case defining the same as the value U+0000-00ff.
 | |
| // In each form, a maximum of 6 digits is allowed for each hexadecimal number (if you treat "?" as a hexadecimal digit).
 | |
| //
 | |
| // <urange> =
 | |
| //   u '+' <ident-token> '?'* |
 | |
| //   u <dimension-token> '?'* |
 | |
| //   u <number-token> '?'* |
 | |
| //   u <number-token> <dimension-token> |
 | |
| //   u <number-token> <number-token> |
 | |
| //   u '+' '?'+
 | |
| module.exports = function urange(token, getNextToken) {
 | |
|     var length = 0;
 | |
| 
 | |
|     // should start with `u` or `U`
 | |
|     if (token === null || token.type !== IDENT || !cmpChar(token.value, 0, U)) {
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     token = getNextToken(++length);
 | |
|     if (token === null) {
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     // u '+' <ident-token> '?'*
 | |
|     // u '+' '?'+
 | |
|     if (isDelim(token, PLUSSIGN)) {
 | |
|         token = getNextToken(++length);
 | |
|         if (token === null) {
 | |
|             return 0;
 | |
|         }
 | |
| 
 | |
|         if (token.type === IDENT) {
 | |
|             // u '+' <ident-token> '?'*
 | |
|             return withQuestionMarkSequence(hexSequence(token, 0, true), ++length, getNextToken);
 | |
|         }
 | |
| 
 | |
|         if (isDelim(token, QUESTIONMARK)) {
 | |
|             // u '+' '?'+
 | |
|             return withQuestionMarkSequence(1, ++length, getNextToken);
 | |
|         }
 | |
| 
 | |
|         // Hex digit or question mark is expected
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     // u <number-token> '?'*
 | |
|     // u <number-token> <dimension-token>
 | |
|     // u <number-token> <number-token>
 | |
|     if (token.type === NUMBER) {
 | |
|         if (!startsWith(token, PLUSSIGN)) {
 | |
|             return 0;
 | |
|         }
 | |
| 
 | |
|         var consumedHexLength = hexSequence(token, 1, true);
 | |
|         if (consumedHexLength === 0) {
 | |
|             return 0;
 | |
|         }
 | |
| 
 | |
|         token = getNextToken(++length);
 | |
|         if (token === null) {
 | |
|             // u <number-token> <eof>
 | |
|             return length;
 | |
|         }
 | |
| 
 | |
|         if (token.type === DIMENSION || token.type === NUMBER) {
 | |
|             // u <number-token> <dimension-token>
 | |
|             // u <number-token> <number-token>
 | |
|             if (!startsWith(token, HYPHENMINUS) || !hexSequence(token, 1, false)) {
 | |
|                 return 0;
 | |
|             }
 | |
| 
 | |
|             return length + 1;
 | |
|         }
 | |
| 
 | |
|         // u <number-token> '?'*
 | |
|         return withQuestionMarkSequence(consumedHexLength, length, getNextToken);
 | |
|     }
 | |
| 
 | |
|     // u <dimension-token> '?'*
 | |
|     if (token.type === DIMENSION) {
 | |
|         if (!startsWith(token, PLUSSIGN)) {
 | |
|             return 0;
 | |
|         }
 | |
| 
 | |
|         return withQuestionMarkSequence(hexSequence(token, 1, true), ++length, getNextToken);
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| };
 |