1 | var isHexDigit = require('../tokenizer').isHexDigit;
|
---|
2 | var cmpChar = require('../tokenizer').cmpChar;
|
---|
3 | var TYPE = require('../tokenizer').TYPE;
|
---|
4 |
|
---|
5 | var IDENT = TYPE.Ident;
|
---|
6 | var DELIM = TYPE.Delim;
|
---|
7 | var NUMBER = TYPE.Number;
|
---|
8 | var DIMENSION = TYPE.Dimension;
|
---|
9 | var PLUSSIGN = 0x002B; // U+002B PLUS SIGN (+)
|
---|
10 | var HYPHENMINUS = 0x002D; // U+002D HYPHEN-MINUS (-)
|
---|
11 | var QUESTIONMARK = 0x003F; // U+003F QUESTION MARK (?)
|
---|
12 | var U = 0x0075; // U+0075 LATIN SMALL LETTER U (u)
|
---|
13 |
|
---|
14 | function isDelim(token, code) {
|
---|
15 | return token !== null && token.type === DELIM && token.value.charCodeAt(0) === code;
|
---|
16 | }
|
---|
17 |
|
---|
18 | function startsWith(token, code) {
|
---|
19 | return token.value.charCodeAt(0) === code;
|
---|
20 | }
|
---|
21 |
|
---|
22 | function hexSequence(token, offset, allowDash) {
|
---|
23 | for (var pos = offset, hexlen = 0; pos < token.value.length; pos++) {
|
---|
24 | var code = token.value.charCodeAt(pos);
|
---|
25 |
|
---|
26 | if (code === HYPHENMINUS && allowDash && hexlen !== 0) {
|
---|
27 | if (hexSequence(token, offset + hexlen + 1, false) > 0) {
|
---|
28 | return 6; // dissallow following question marks
|
---|
29 | }
|
---|
30 |
|
---|
31 | return 0; // dash at the ending of a hex sequence is not allowed
|
---|
32 | }
|
---|
33 |
|
---|
34 | if (!isHexDigit(code)) {
|
---|
35 | return 0; // not a hex digit
|
---|
36 | }
|
---|
37 |
|
---|
38 | if (++hexlen > 6) {
|
---|
39 | return 0; // too many hex digits
|
---|
40 | };
|
---|
41 | }
|
---|
42 |
|
---|
43 | return hexlen;
|
---|
44 | }
|
---|
45 |
|
---|
46 | function withQuestionMarkSequence(consumed, length, getNextToken) {
|
---|
47 | if (!consumed) {
|
---|
48 | return 0; // nothing consumed
|
---|
49 | }
|
---|
50 |
|
---|
51 | while (isDelim(getNextToken(length), QUESTIONMARK)) {
|
---|
52 | if (++consumed > 6) {
|
---|
53 | return 0; // too many question marks
|
---|
54 | }
|
---|
55 |
|
---|
56 | length++;
|
---|
57 | }
|
---|
58 |
|
---|
59 | return length;
|
---|
60 | }
|
---|
61 |
|
---|
62 | // https://drafts.csswg.org/css-syntax/#urange
|
---|
63 | // Informally, the <urange> production has three forms:
|
---|
64 | // U+0001
|
---|
65 | // Defines a range consisting of a single code point, in this case the code point "1".
|
---|
66 | // U+0001-00ff
|
---|
67 | // Defines a range of codepoints between the first and the second value, in this case
|
---|
68 | // the range between "1" and "ff" (255 in decimal) inclusive.
|
---|
69 | // U+00??
|
---|
70 | // Defines a range of codepoints where the "?" characters range over all hex digits,
|
---|
71 | // in this case defining the same as the value U+0000-00ff.
|
---|
72 | // In each form, a maximum of 6 digits is allowed for each hexadecimal number (if you treat "?" as a hexadecimal digit).
|
---|
73 | //
|
---|
74 | // <urange> =
|
---|
75 | // u '+' <ident-token> '?'* |
|
---|
76 | // u <dimension-token> '?'* |
|
---|
77 | // u <number-token> '?'* |
|
---|
78 | // u <number-token> <dimension-token> |
|
---|
79 | // u <number-token> <number-token> |
|
---|
80 | // u '+' '?'+
|
---|
81 | module.exports = function urange(token, getNextToken) {
|
---|
82 | var length = 0;
|
---|
83 |
|
---|
84 | // should start with `u` or `U`
|
---|
85 | if (token === null || token.type !== IDENT || !cmpChar(token.value, 0, U)) {
|
---|
86 | return 0;
|
---|
87 | }
|
---|
88 |
|
---|
89 | token = getNextToken(++length);
|
---|
90 | if (token === null) {
|
---|
91 | return 0;
|
---|
92 | }
|
---|
93 |
|
---|
94 | // u '+' <ident-token> '?'*
|
---|
95 | // u '+' '?'+
|
---|
96 | if (isDelim(token, PLUSSIGN)) {
|
---|
97 | token = getNextToken(++length);
|
---|
98 | if (token === null) {
|
---|
99 | return 0;
|
---|
100 | }
|
---|
101 |
|
---|
102 | if (token.type === IDENT) {
|
---|
103 | // u '+' <ident-token> '?'*
|
---|
104 | return withQuestionMarkSequence(hexSequence(token, 0, true), ++length, getNextToken);
|
---|
105 | }
|
---|
106 |
|
---|
107 | if (isDelim(token, QUESTIONMARK)) {
|
---|
108 | // u '+' '?'+
|
---|
109 | return withQuestionMarkSequence(1, ++length, getNextToken);
|
---|
110 | }
|
---|
111 |
|
---|
112 | // Hex digit or question mark is expected
|
---|
113 | return 0;
|
---|
114 | }
|
---|
115 |
|
---|
116 | // u <number-token> '?'*
|
---|
117 | // u <number-token> <dimension-token>
|
---|
118 | // u <number-token> <number-token>
|
---|
119 | if (token.type === NUMBER) {
|
---|
120 | if (!startsWith(token, PLUSSIGN)) {
|
---|
121 | return 0;
|
---|
122 | }
|
---|
123 |
|
---|
124 | var consumedHexLength = hexSequence(token, 1, true);
|
---|
125 | if (consumedHexLength === 0) {
|
---|
126 | return 0;
|
---|
127 | }
|
---|
128 |
|
---|
129 | token = getNextToken(++length);
|
---|
130 | if (token === null) {
|
---|
131 | // u <number-token> <eof>
|
---|
132 | return length;
|
---|
133 | }
|
---|
134 |
|
---|
135 | if (token.type === DIMENSION || token.type === NUMBER) {
|
---|
136 | // u <number-token> <dimension-token>
|
---|
137 | // u <number-token> <number-token>
|
---|
138 | if (!startsWith(token, HYPHENMINUS) || !hexSequence(token, 1, false)) {
|
---|
139 | return 0;
|
---|
140 | }
|
---|
141 |
|
---|
142 | return length + 1;
|
---|
143 | }
|
---|
144 |
|
---|
145 | // u <number-token> '?'*
|
---|
146 | return withQuestionMarkSequence(consumedHexLength, length, getNextToken);
|
---|
147 | }
|
---|
148 |
|
---|
149 | // u <dimension-token> '?'*
|
---|
150 | if (token.type === DIMENSION) {
|
---|
151 | if (!startsWith(token, PLUSSIGN)) {
|
---|
152 | return 0;
|
---|
153 | }
|
---|
154 |
|
---|
155 | return withQuestionMarkSequence(hexSequence(token, 1, true), ++length, getNextToken);
|
---|
156 | }
|
---|
157 |
|
---|
158 | return 0;
|
---|
159 | };
|
---|