browser/devtools/sourceeditor/css-tokenizer.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /**
michael@0 2 * This file is taken from the below mentioned url and is under CC0 license.
michael@0 3 * https://github.com/tabatkins/css-parser/blob/master/tokenizer.js
michael@0 4 * Please retain this comment while updating this file from upstream.
michael@0 5 */
michael@0 6
michael@0 7 (function (root, factory) {
michael@0 8 // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
michael@0 9 // Rhino, and plain browser loading.
michael@0 10 if (typeof define === 'function' && define.amd) {
michael@0 11 define(['exports'], factory);
michael@0 12 } else if (typeof exports !== 'undefined') {
michael@0 13 factory(exports);
michael@0 14 } else {
michael@0 15 factory(root);
michael@0 16 }
michael@0 17 }(this, function (exports) {
michael@0 18
michael@0 19 var between = function (num, first, last) { return num >= first && num <= last; }
michael@0 20 function digit(code) { return between(code, 0x30,0x39); }
michael@0 21 function hexdigit(code) { return digit(code) || between(code, 0x41,0x46) || between(code, 0x61,0x66); }
michael@0 22 function uppercaseletter(code) { return between(code, 0x41,0x5a); }
michael@0 23 function lowercaseletter(code) { return between(code, 0x61,0x7a); }
michael@0 24 function letter(code) { return uppercaseletter(code) || lowercaseletter(code); }
michael@0 25 function nonascii(code) { return code >= 0xa0; }
michael@0 26 function namestartchar(code) { return letter(code) || nonascii(code) || code == 0x5f; }
michael@0 27 function namechar(code) { return namestartchar(code) || digit(code) || code == 0x2d; }
michael@0 28 function nonprintable(code) { return between(code, 0,8) || between(code, 0xe,0x1f) || between(code, 0x7f,0x9f); }
michael@0 29 function newline(code) { return code == 0xa || code == 0xc; }
michael@0 30 function whitespace(code) { return newline(code) || code == 9 || code == 0x20; }
michael@0 31 function badescape(code) { return newline(code) || isNaN(code); }
michael@0 32
michael@0 33 // Note: I'm not yet acting smart enough to actually handle astral characters.
michael@0 34 var maximumallowedcodepoint = 0x10ffff;
michael@0 35
michael@0 36 function tokenize(str, options) {
michael@0 37 if(options == undefined) options = {transformFunctionWhitespace:false, scientificNotation:false};
michael@0 38 var i = -1;
michael@0 39 var tokens = [];
michael@0 40 var state = "data";
michael@0 41 var code;
michael@0 42 var currtoken;
michael@0 43
michael@0 44 // Line number information.
michael@0 45 var line = 0;
michael@0 46 var column = 0;
michael@0 47 // The only use of lastLineLength is in reconsume().
michael@0 48 var lastLineLength = 0;
michael@0 49 var incrLineno = function() {
michael@0 50 line += 1;
michael@0 51 lastLineLength = column;
michael@0 52 column = 0;
michael@0 53 };
michael@0 54 var locStart = {line:line, column:column};
michael@0 55
michael@0 56 var next = function(num) { if(num === undefined) num = 1; return str.charCodeAt(i+num); };
michael@0 57 var consume = function(num) {
michael@0 58 if(num === undefined)
michael@0 59 num = 1;
michael@0 60 i += num;
michael@0 61 code = str.charCodeAt(i);
michael@0 62 if (newline(code)) incrLineno();
michael@0 63 else column += num;
michael@0 64 //console.log('Consume '+i+' '+String.fromCharCode(code) + ' 0x' + code.toString(16));
michael@0 65 return true;
michael@0 66 };
michael@0 67 var reconsume = function() {
michael@0 68 i -= 1;
michael@0 69 if (newline(code)) {
michael@0 70 line -= 1;
michael@0 71 column = lastLineLength;
michael@0 72 } else {
michael@0 73 column -= 1;
michael@0 74 }
michael@0 75 locStart.line = line;
michael@0 76 locStart.column = column;
michael@0 77 return true;
michael@0 78 };
michael@0 79 var eof = function() { return i >= str.length; };
michael@0 80 var donothing = function() {};
michael@0 81 var emit = function(token) {
michael@0 82 if(token) {
michael@0 83 token.finish();
michael@0 84 } else {
michael@0 85 token = currtoken.finish();
michael@0 86 }
michael@0 87 if (options.loc === true) {
michael@0 88 token.loc = {};
michael@0 89 token.loc.start = {line:locStart.line, column:locStart.column};
michael@0 90 locStart = {line: line, column: column};
michael@0 91 token.loc.end = locStart;
michael@0 92 }
michael@0 93 tokens.push(token);
michael@0 94 //console.log('Emitting ' + token);
michael@0 95 currtoken = undefined;
michael@0 96 return true;
michael@0 97 };
michael@0 98 var create = function(token) { currtoken = token; return true; };
michael@0 99 var parseerror = function() { console.log("Parse error at index " + i + ", processing codepoint 0x" + code.toString(16) + " in state " + state + ".");return true; };
michael@0 100 var switchto = function(newstate) {
michael@0 101 state = newstate;
michael@0 102 //console.log('Switching to ' + state);
michael@0 103 return true;
michael@0 104 };
michael@0 105 var consumeEscape = function() {
michael@0 106 // Assume the the current character is the \
michael@0 107 consume();
michael@0 108 if(hexdigit(code)) {
michael@0 109 // Consume 1-6 hex digits
michael@0 110 var digits = [];
michael@0 111 for(var total = 0; total < 6; total++) {
michael@0 112 if(hexdigit(code)) {
michael@0 113 digits.push(code);
michael@0 114 consume();
michael@0 115 } else { break; }
michael@0 116 }
michael@0 117 var value = parseInt(digits.map(String.fromCharCode).join(''), 16);
michael@0 118 if( value > maximumallowedcodepoint ) value = 0xfffd;
michael@0 119 // If the current char is whitespace, cool, we'll just eat it.
michael@0 120 // Otherwise, put it back.
michael@0 121 if(!whitespace(code)) reconsume();
michael@0 122 return value;
michael@0 123 } else {
michael@0 124 return code;
michael@0 125 }
michael@0 126 };
michael@0 127
michael@0 128 for(;;) {
michael@0 129 if(i > str.length*2) return "I'm infinite-looping!";
michael@0 130 consume();
michael@0 131 switch(state) {
michael@0 132 case "data":
michael@0 133 if(whitespace(code)) {
michael@0 134 emit(new WhitespaceToken);
michael@0 135 while(whitespace(next())) consume();
michael@0 136 }
michael@0 137 else if(code == 0x22) switchto("double-quote-string");
michael@0 138 else if(code == 0x23) switchto("hash");
michael@0 139 else if(code == 0x27) switchto("single-quote-string");
michael@0 140 else if(code == 0x28) emit(new OpenParenToken);
michael@0 141 else if(code == 0x29) emit(new CloseParenToken);
michael@0 142 else if(code == 0x2b) {
michael@0 143 if(digit(next()) || (next() == 0x2e && digit(next(2)))) switchto("number") && reconsume();
michael@0 144 else emit(new DelimToken(code));
michael@0 145 }
michael@0 146 else if(code == 0x2d) {
michael@0 147 if(next(1) == 0x2d && next(2) == 0x3e) consume(2) && emit(new CDCToken);
michael@0 148 else if(digit(next()) || (next(1) == 0x2e && digit(next(2)))) switchto("number") && reconsume();
michael@0 149 else if(namestartchar(next())) switchto("identifier") && reconsume();
michael@0 150 else emit(new DelimToken(code));
michael@0 151 }
michael@0 152 else if(code == 0x2e) {
michael@0 153 if(digit(next())) switchto("number") && reconsume();
michael@0 154 else emit(new DelimToken(code));
michael@0 155 }
michael@0 156 else if(code == 0x2f) {
michael@0 157 if(next() == 0x2a) switchto("comment");
michael@0 158 else emit(new DelimToken(code));
michael@0 159 }
michael@0 160 else if(code == 0x3a) emit(new ColonToken);
michael@0 161 else if(code == 0x3b) emit(new SemicolonToken);
michael@0 162 else if(code == 0x3c) {
michael@0 163 if(next(1) == 0x21 && next(2) == 0x2d && next(3) == 0x2d) consume(3) && emit(new CDOToken);
michael@0 164 else emit(new DelimToken(code));
michael@0 165 }
michael@0 166 else if(code == 0x40) switchto("at-keyword");
michael@0 167 else if(code == 0x5b) emit(new OpenSquareToken);
michael@0 168 else if(code == 0x5c) {
michael@0 169 if(badescape(next())) parseerror() && emit(new DelimToken(code));
michael@0 170 else switchto("identifier") && reconsume();
michael@0 171 }
michael@0 172 else if(code == 0x5d) emit(new CloseSquareToken);
michael@0 173 else if(code == 0x7b) emit(new OpenCurlyToken);
michael@0 174 else if(code == 0x7d) emit(new CloseCurlyToken);
michael@0 175 else if(digit(code)) switchto("number") && reconsume();
michael@0 176 else if(code == 0x55 || code == 0x75) {
michael@0 177 if(next(1) == 0x2b && hexdigit(next(2))) consume() && switchto("unicode-range");
michael@0 178 else if((next(1) == 0x52 || next(1) == 0x72) && (next(2) == 0x4c || next(2) == 0x6c) && (next(3) == 0x28)) consume(3) && switchto("url");
michael@0 179 else switchto("identifier") && reconsume();
michael@0 180 }
michael@0 181 else if(namestartchar(code)) switchto("identifier") && reconsume();
michael@0 182 else if(eof()) { emit(new EOFToken); return tokens; }
michael@0 183 else emit(new DelimToken(code));
michael@0 184 break;
michael@0 185
michael@0 186 case "double-quote-string":
michael@0 187 if(currtoken == undefined) create(new StringToken);
michael@0 188
michael@0 189 if(code == 0x22) emit() && switchto("data");
michael@0 190 else if(eof()) parseerror() && emit() && switchto("data");
michael@0 191 else if(newline(code)) parseerror() && emit(new BadStringToken) && switchto("data") && reconsume();
michael@0 192 else if(code == 0x5c) {
michael@0 193 if(badescape(next())) parseerror() && emit(new BadStringToken) && switchto("data");
michael@0 194 else if(newline(next())) consume();
michael@0 195 else currtoken.append(consumeEscape());
michael@0 196 }
michael@0 197 else currtoken.append(code);
michael@0 198 break;
michael@0 199
michael@0 200 case "single-quote-string":
michael@0 201 if(currtoken == undefined) create(new StringToken);
michael@0 202
michael@0 203 if(code == 0x27) emit() && switchto("data");
michael@0 204 else if(eof()) parseerror() && emit() && switchto("data");
michael@0 205 else if(newline(code)) parseerror() && emit(new BadStringToken) && switchto("data") && reconsume();
michael@0 206 else if(code == 0x5c) {
michael@0 207 if(badescape(next())) parseerror() && emit(new BadStringToken) && switchto("data");
michael@0 208 else if(newline(next())) consume();
michael@0 209 else currtoken.append(consumeEscape());
michael@0 210 }
michael@0 211 else currtoken.append(code);
michael@0 212 break;
michael@0 213
michael@0 214 case "hash":
michael@0 215 if(namechar(code)) create(new HashToken(code)) && switchto("hash-rest");
michael@0 216 else if(code == 0x5c) {
michael@0 217 if(badescape(next())) parseerror() && emit(new DelimToken(0x23)) && switchto("data") && reconsume();
michael@0 218 else create(new HashToken(consumeEscape())) && switchto('hash-rest');
michael@0 219 }
michael@0 220 else emit(new DelimToken(0x23)) && switchto('data') && reconsume();
michael@0 221 break;
michael@0 222
michael@0 223 case "hash-rest":
michael@0 224 if(namechar(code)) currtoken.append(code);
michael@0 225 else if(code == 0x5c) {
michael@0 226 if(badescape(next())) parseerror() && emit(new DelimToken(0x23)) && switchto("data") && reconsume();
michael@0 227 else currtoken.append(consumeEscape());
michael@0 228 }
michael@0 229 else emit() && switchto('data') && reconsume();
michael@0 230 break;
michael@0 231
michael@0 232 case "comment":
michael@0 233 if(code == 0x2a) {
michael@0 234 if(next() == 0x2f) consume() && switchto('data');
michael@0 235 else donothing();
michael@0 236 }
michael@0 237 else if(eof()) parseerror() && switchto('data') && reconsume();
michael@0 238 else donothing();
michael@0 239 break;
michael@0 240
michael@0 241 case "at-keyword":
michael@0 242 if(code == 0x2d) {
michael@0 243 if(namestartchar(next())) consume() && create(new AtKeywordToken([0x40,code])) && switchto('at-keyword-rest');
michael@0 244 else emit(new DelimToken(0x40)) && switchto('data') && reconsume();
michael@0 245 }
michael@0 246 else if(namestartchar(code)) create(new AtKeywordToken(code)) && switchto('at-keyword-rest');
michael@0 247 else if(code == 0x5c) {
michael@0 248 if(badescape(next())) parseerror() && emit(new DelimToken(0x23)) && switchto("data") && reconsume();
michael@0 249 else create(new AtKeywordToken(consumeEscape())) && switchto('at-keyword-rest');
michael@0 250 }
michael@0 251 else emit(new DelimToken(0x40)) && switchto('data') && reconsume();
michael@0 252 break;
michael@0 253
michael@0 254 case "at-keyword-rest":
michael@0 255 if(namechar(code)) currtoken.append(code);
michael@0 256 else if(code == 0x5c) {
michael@0 257 if(badescape(next())) parseerror() && emit() && switchto("data") && reconsume();
michael@0 258 else currtoken.append(consumeEscape());
michael@0 259 }
michael@0 260 else emit() && switchto('data') && reconsume();
michael@0 261 break;
michael@0 262
michael@0 263 case "identifier":
michael@0 264 if(code == 0x2d) {
michael@0 265 if(namestartchar(next())) create(new IdentifierToken(code)) && switchto('identifier-rest');
michael@0 266 else switchto('data') && reconsume();
michael@0 267 }
michael@0 268 else if(namestartchar(code)) create(new IdentifierToken(code)) && switchto('identifier-rest');
michael@0 269 else if(code == 0x5c) {
michael@0 270 if(badescape(next())) parseerror() && switchto("data") && reconsume();
michael@0 271 else create(new IdentifierToken(consumeEscape())) && switchto('identifier-rest');
michael@0 272 }
michael@0 273 else switchto('data') && reconsume();
michael@0 274 break;
michael@0 275
michael@0 276 case "identifier-rest":
michael@0 277 if(namechar(code)) currtoken.append(code);
michael@0 278 else if(code == 0x5c) {
michael@0 279 if(badescape(next())) parseerror() && emit() && switchto("data") && reconsume();
michael@0 280 else currtoken.append(consumeEscape());
michael@0 281 }
michael@0 282 else if(code == 0x28) emit(new FunctionToken(currtoken)) && switchto('data');
michael@0 283 else if(whitespace(code) && options.transformFunctionWhitespace) switchto('transform-function-whitespace');
michael@0 284 else emit() && switchto('data') && reconsume();
michael@0 285 break;
michael@0 286
michael@0 287 case "transform-function-whitespace":
michael@0 288 if(whitespace(code)) donothing();
michael@0 289 else if(code == 0x28) emit(new FunctionToken(currtoken)) && switchto('data');
michael@0 290 else emit() && switchto('data') && reconsume();
michael@0 291 break;
michael@0 292
michael@0 293 case "number":
michael@0 294 create(new NumberToken());
michael@0 295
michael@0 296 if(code == 0x2d) {
michael@0 297 if(digit(next())) consume() && currtoken.append([0x2d,code]) && switchto('number-rest');
michael@0 298 else if(next(1) == 0x2e && digit(next(2))) consume(2) && currtoken.append([0x2d,0x2e,code]) && switchto('number-fraction');
michael@0 299 else switchto('data') && reconsume();
michael@0 300 }
michael@0 301 else if(code == 0x2b) {
michael@0 302 if(digit(next())) consume() && currtoken.append([0x2b,code]) && switchto('number-rest');
michael@0 303 else if(next(1) == 0x2e && digit(next(2))) consume(2) && currtoken.append([0x2b,0x2e,code]) && switchto('number-fraction');
michael@0 304 else switchto('data') && reconsume();
michael@0 305 }
michael@0 306 else if(digit(code)) currtoken.append(code) && switchto('number-rest');
michael@0 307 else if(code == 0x2e) {
michael@0 308 if(digit(next())) consume() && currtoken.append([0x2e,code]) && switchto('number-fraction');
michael@0 309 else switchto('data') && reconsume();
michael@0 310 }
michael@0 311 else switchto('data') && reconsume();
michael@0 312 break;
michael@0 313
michael@0 314 case "number-rest":
michael@0 315 if(digit(code)) currtoken.append(code);
michael@0 316 else if(code == 0x2e) {
michael@0 317 if(digit(next())) consume() && currtoken.append([0x2e,code]) && switchto('number-fraction');
michael@0 318 else emit() && switchto('data') && reconsume();
michael@0 319 }
michael@0 320 else if(code == 0x25) emit(new PercentageToken(currtoken)) && switchto('data') && reconsume();
michael@0 321 else if(code == 0x45 || code == 0x65) {
michael@0 322 if(!options.scientificNotation) create(new DimensionToken(currtoken,code)) && switchto('dimension');
michael@0 323 else if(digit(next())) consume() && currtoken.append([0x25,code]) && switchto('sci-notation');
michael@0 324 else if((next(1) == 0x2b || next(1) == 0x2d) && digit(next(2))) currtoken.append([0x25,next(1),next(2)]) && consume(2) && switchto('sci-notation');
michael@0 325 else create(new DimensionToken(currtoken,code)) && switchto('dimension');
michael@0 326 }
michael@0 327 else if(code == 0x2d) {
michael@0 328 if(namestartchar(next())) consume() && create(new DimensionToken(currtoken,[0x2d,code])) && switchto('dimension');
michael@0 329 else if(next(1) == 0x5c && badescape(next(2))) parseerror() && emit() && switchto('data') && reconsume();
michael@0 330 else if(next(1) == 0x5c) consume() && create(new DimensionToken(currtoken, [0x2d,consumeEscape()])) && switchto('dimension');
michael@0 331 else emit() && switchto('data') && reconsume();
michael@0 332 }
michael@0 333 else if(namestartchar(code)) create(new DimensionToken(currtoken, code)) && switchto('dimension');
michael@0 334 else if(code == 0x5c) {
michael@0 335 if(badescape(next)) emit() && switchto('data') && reconsume();
michael@0 336 else create(new DimensionToken(currtoken,consumeEscape)) && switchto('dimension');
michael@0 337 }
michael@0 338 else emit() && switchto('data') && reconsume();
michael@0 339 break;
michael@0 340
michael@0 341 case "number-fraction":
michael@0 342 currtoken.type = "number";
michael@0 343
michael@0 344 if(digit(code)) currtoken.append(code);
michael@0 345 else if(code == 0x2e) emit() && switchto('data') && reconsume();
michael@0 346 else if(code == 0x25) emit(new PercentageToken(currtoken)) && switchto('data') && reconsume();
michael@0 347 else if(code == 0x45 || code == 0x65) {
michael@0 348 if(!options.scientificNotation) create(new DimensionToken(currtoken,code)) && switchto('dimension');
michael@0 349 else if(digit(next())) consume() && currtoken.append([0x25,code]) && switchto('sci-notation');
michael@0 350 else if((next(1) == 0x2b || next(1) == 0x2d) && digit(next(2))) currtoken.append([0x25,next(1),next(2)]) && consume(2) && switchto('sci-notation');
michael@0 351 else create(new DimensionToken(currtoken,code)) && switchto('dimension');
michael@0 352 }
michael@0 353 else if(code == 0x2d) {
michael@0 354 if(namestartchar(next())) consume() && create(new DimensionToken(currtoken,[0x2d,code])) && switchto('dimension');
michael@0 355 else if(next(1) == 0x5c && badescape(next(2))) parseerror() && emit() && switchto('data') && reconsume();
michael@0 356 else if(next(1) == 0x5c) consume() && create(new DimensionToken(currtoken, [0x2d,consumeEscape()])) && switchto('dimension');
michael@0 357 else emit() && switchto('data') && reconsume();
michael@0 358 }
michael@0 359 else if(namestartchar(code)) create(new DimensionToken(currtoken, code)) && switchto('dimension');
michael@0 360 else if(code == 0x5c) {
michael@0 361 if(badescape(next)) emit() && switchto('data') && reconsume();
michael@0 362 else create(new DimensionToken(currtoken,consumeEscape)) && switchto('dimension');
michael@0 363 }
michael@0 364 else emit() && switchto('data') && reconsume();
michael@0 365 break;
michael@0 366
michael@0 367 case "dimension":
michael@0 368 if(namechar(code)) currtoken.append(code);
michael@0 369 else if(code == 0x5c) {
michael@0 370 if(badescape(next())) parseerror() && emit() && switchto('data') && reconsume();
michael@0 371 else currtoken.append(consumeEscape());
michael@0 372 }
michael@0 373 else emit() && switchto('data') && reconsume();
michael@0 374 break;
michael@0 375
michael@0 376 case "sci-notation":
michael@0 377 if(digit(code)) currtoken.append(code);
michael@0 378 else emit() && switchto('data') && reconsume();
michael@0 379 break;
michael@0 380
michael@0 381 case "url":
michael@0 382 if(code == 0x22) switchto('url-double-quote');
michael@0 383 else if(code == 0x27) switchto('url-single-quote');
michael@0 384 else if(code == 0x29) emit(new URLToken) && switchto('data');
michael@0 385 else if(whitespace(code)) donothing();
michael@0 386 else switchto('url-unquoted') && reconsume();
michael@0 387 break;
michael@0 388
michael@0 389 case "url-double-quote":
michael@0 390 if(currtoken == undefined) create(new URLToken);
michael@0 391
michael@0 392 if(code == 0x22) switchto('url-end');
michael@0 393 else if(newline(code)) parseerror() && switchto('bad-url');
michael@0 394 else if(code == 0x5c) {
michael@0 395 if(newline(next())) consume();
michael@0 396 else if(badescape(next())) parseerror() && emit(new BadURLToken) && switchto('data') && reconsume();
michael@0 397 else currtoken.append(consumeEscape());
michael@0 398 }
michael@0 399 else currtoken.append(code);
michael@0 400 break;
michael@0 401
michael@0 402 case "url-single-quote":
michael@0 403 if(currtoken == undefined) create(new URLToken);
michael@0 404
michael@0 405 if(code == 0x27) switchto('url-end');
michael@0 406 else if(newline(code)) parseerror() && switchto('bad-url');
michael@0 407 else if(code == 0x5c) {
michael@0 408 if(newline(next())) consume();
michael@0 409 else if(badescape(next())) parseerror() && emit(new BadURLToken) && switchto('data') && reconsume();
michael@0 410 else currtoken.append(consumeEscape());
michael@0 411 }
michael@0 412 else currtoken.append(code);
michael@0 413 break;
michael@0 414
michael@0 415 case "url-end":
michael@0 416 if(whitespace(code)) donothing();
michael@0 417 else if(code == 0x29) emit() && switchto('data');
michael@0 418 else parseerror() && switchto('bad-url') && reconsume();
michael@0 419 break;
michael@0 420
michael@0 421 case "url-unquoted":
michael@0 422 if(currtoken == undefined) create(new URLToken);
michael@0 423
michael@0 424 if(whitespace(code)) switchto('url-end');
michael@0 425 else if(code == 0x29) emit() && switchto('data');
michael@0 426 else if(code == 0x22 || code == 0x27 || code == 0x28 || nonprintable(code)) parseerror() && switchto('bad-url');
michael@0 427 else if(code == 0x5c) {
michael@0 428 if(badescape(next())) parseerror() && switchto('bad-url');
michael@0 429 else currtoken.append(consumeEscape());
michael@0 430 }
michael@0 431 else currtoken.append(code);
michael@0 432 break;
michael@0 433
michael@0 434 case "bad-url":
michael@0 435 if(code == 0x29) emit(new BadURLToken) && switchto('data');
michael@0 436 else if(code == 0x5c) {
michael@0 437 if(badescape(next())) donothing();
michael@0 438 else consumeEscape()
michael@0 439 }
michael@0 440 else donothing();
michael@0 441 break;
michael@0 442
michael@0 443 case "unicode-range":
michael@0 444 // We already know that the current code is a hexdigit.
michael@0 445
michael@0 446 var start = [code], end = [code];
michael@0 447
michael@0 448 for(var total = 1; total < 6; total++) {
michael@0 449 if(hexdigit(next())) {
michael@0 450 consume();
michael@0 451 start.push(code);
michael@0 452 end.push(code);
michael@0 453 }
michael@0 454 else break;
michael@0 455 }
michael@0 456
michael@0 457 if(next() == 0x3f) {
michael@0 458 for(;total < 6; total++) {
michael@0 459 if(next() == 0x3f) {
michael@0 460 consume();
michael@0 461 start.push("0".charCodeAt(0));
michael@0 462 end.push("f".charCodeAt(0));
michael@0 463 }
michael@0 464 else break;
michael@0 465 }
michael@0 466 emit(new UnicodeRangeToken(start,end)) && switchto('data');
michael@0 467 }
michael@0 468 else if(next(1) == 0x2d && hexdigit(next(2))) {
michael@0 469 consume();
michael@0 470 consume();
michael@0 471 end = [code];
michael@0 472 for(var total = 1; total < 6; total++) {
michael@0 473 if(hexdigit(next())) {
michael@0 474 consume();
michael@0 475 end.push(code);
michael@0 476 }
michael@0 477 else break;
michael@0 478 }
michael@0 479 emit(new UnicodeRangeToken(start,end)) && switchto('data');
michael@0 480 }
michael@0 481 else emit(new UnicodeRangeToken(start)) && switchto('data');
michael@0 482 break;
michael@0 483
michael@0 484 default:
michael@0 485 console.log("Unknown state '" + state + "'");
michael@0 486 }
michael@0 487 }
michael@0 488 }
michael@0 489
michael@0 490 function stringFromCodeArray(arr) {
michael@0 491 return String.fromCharCode.apply(null,arr.filter(function(e){return e;}));
michael@0 492 }
michael@0 493
michael@0 494 function CSSParserToken(options) { return this; }
michael@0 495 CSSParserToken.prototype.finish = function() { return this; }
michael@0 496 CSSParserToken.prototype.toString = function() { return this.tokenType; }
michael@0 497 CSSParserToken.prototype.toJSON = function() { return this.toString(); }
michael@0 498
michael@0 499 function BadStringToken() { return this; }
michael@0 500 BadStringToken.prototype = new CSSParserToken;
michael@0 501 BadStringToken.prototype.tokenType = "BADSTRING";
michael@0 502
michael@0 503 function BadURLToken() { return this; }
michael@0 504 BadURLToken.prototype = new CSSParserToken;
michael@0 505 BadURLToken.prototype.tokenType = "BADURL";
michael@0 506
michael@0 507 function WhitespaceToken() { return this; }
michael@0 508 WhitespaceToken.prototype = new CSSParserToken;
michael@0 509 WhitespaceToken.prototype.tokenType = "WHITESPACE";
michael@0 510 WhitespaceToken.prototype.toString = function() { return "WS"; }
michael@0 511
michael@0 512 function CDOToken() { return this; }
michael@0 513 CDOToken.prototype = new CSSParserToken;
michael@0 514 CDOToken.prototype.tokenType = "CDO";
michael@0 515
michael@0 516 function CDCToken() { return this; }
michael@0 517 CDCToken.prototype = new CSSParserToken;
michael@0 518 CDCToken.prototype.tokenType = "CDC";
michael@0 519
michael@0 520 function ColonToken() { return this; }
michael@0 521 ColonToken.prototype = new CSSParserToken;
michael@0 522 ColonToken.prototype.tokenType = ":";
michael@0 523
michael@0 524 function SemicolonToken() { return this; }
michael@0 525 SemicolonToken.prototype = new CSSParserToken;
michael@0 526 SemicolonToken.prototype.tokenType = ";";
michael@0 527
michael@0 528 function OpenCurlyToken() { return this; }
michael@0 529 OpenCurlyToken.prototype = new CSSParserToken;
michael@0 530 OpenCurlyToken.prototype.tokenType = "{";
michael@0 531
michael@0 532 function CloseCurlyToken() { return this; }
michael@0 533 CloseCurlyToken.prototype = new CSSParserToken;
michael@0 534 CloseCurlyToken.prototype.tokenType = "}";
michael@0 535
michael@0 536 function OpenSquareToken() { return this; }
michael@0 537 OpenSquareToken.prototype = new CSSParserToken;
michael@0 538 OpenSquareToken.prototype.tokenType = "[";
michael@0 539
michael@0 540 function CloseSquareToken() { return this; }
michael@0 541 CloseSquareToken.prototype = new CSSParserToken;
michael@0 542 CloseSquareToken.prototype.tokenType = "]";
michael@0 543
michael@0 544 function OpenParenToken() { return this; }
michael@0 545 OpenParenToken.prototype = new CSSParserToken;
michael@0 546 OpenParenToken.prototype.tokenType = "(";
michael@0 547
michael@0 548 function CloseParenToken() { return this; }
michael@0 549 CloseParenToken.prototype = new CSSParserToken;
michael@0 550 CloseParenToken.prototype.tokenType = ")";
michael@0 551
michael@0 552 function EOFToken() { return this; }
michael@0 553 EOFToken.prototype = new CSSParserToken;
michael@0 554 EOFToken.prototype.tokenType = "EOF";
michael@0 555
michael@0 556 function DelimToken(code) {
michael@0 557 this.value = String.fromCharCode(code);
michael@0 558 return this;
michael@0 559 }
michael@0 560 DelimToken.prototype = new CSSParserToken;
michael@0 561 DelimToken.prototype.tokenType = "DELIM";
michael@0 562 DelimToken.prototype.toString = function() { return "DELIM("+this.value+")"; }
michael@0 563
michael@0 564 function StringValuedToken() { return this; }
michael@0 565 StringValuedToken.prototype = new CSSParserToken;
michael@0 566 StringValuedToken.prototype.append = function(val) {
michael@0 567 if(val instanceof Array) {
michael@0 568 for(var i = 0; i < val.length; i++) {
michael@0 569 this.value.push(val[i]);
michael@0 570 }
michael@0 571 } else {
michael@0 572 this.value.push(val);
michael@0 573 }
michael@0 574 return true;
michael@0 575 }
michael@0 576 StringValuedToken.prototype.finish = function() {
michael@0 577 this.value = stringFromCodeArray(this.value);
michael@0 578 return this;
michael@0 579 }
michael@0 580
michael@0 581 function IdentifierToken(val) {
michael@0 582 this.value = [];
michael@0 583 this.append(val);
michael@0 584 }
michael@0 585 IdentifierToken.prototype = new StringValuedToken;
michael@0 586 IdentifierToken.prototype.tokenType = "IDENT";
michael@0 587 IdentifierToken.prototype.toString = function() { return "IDENT("+this.value+")"; }
michael@0 588
michael@0 589 function FunctionToken(val) {
michael@0 590 // These are always constructed by passing an IdentifierToken
michael@0 591 this.value = val.finish().value;
michael@0 592 }
michael@0 593 FunctionToken.prototype = new CSSParserToken;
michael@0 594 FunctionToken.prototype.tokenType = "FUNCTION";
michael@0 595 FunctionToken.prototype.toString = function() { return "FUNCTION("+this.value+")"; }
michael@0 596
michael@0 597 function AtKeywordToken(val) {
michael@0 598 this.value = [];
michael@0 599 this.append(val);
michael@0 600 }
michael@0 601 AtKeywordToken.prototype = new StringValuedToken;
michael@0 602 AtKeywordToken.prototype.tokenType = "AT-KEYWORD";
michael@0 603 AtKeywordToken.prototype.toString = function() { return "AT("+this.value+")"; }
michael@0 604
michael@0 605 function HashToken(val) {
michael@0 606 this.value = [];
michael@0 607 this.append(val);
michael@0 608 }
michael@0 609 HashToken.prototype = new StringValuedToken;
michael@0 610 HashToken.prototype.tokenType = "HASH";
michael@0 611 HashToken.prototype.toString = function() { return "HASH("+this.value+")"; }
michael@0 612
michael@0 613 function StringToken(val) {
michael@0 614 this.value = [];
michael@0 615 this.append(val);
michael@0 616 }
michael@0 617 StringToken.prototype = new StringValuedToken;
michael@0 618 StringToken.prototype.tokenType = "STRING";
michael@0 619 StringToken.prototype.toString = function() { return "\""+this.value+"\""; }
michael@0 620
michael@0 621 function URLToken(val) {
michael@0 622 this.value = [];
michael@0 623 this.append(val);
michael@0 624 }
michael@0 625 URLToken.prototype = new StringValuedToken;
michael@0 626 URLToken.prototype.tokenType = "URL";
michael@0 627 URLToken.prototype.toString = function() { return "URL("+this.value+")"; }
michael@0 628
michael@0 629 function NumberToken(val) {
michael@0 630 this.value = [];
michael@0 631 this.append(val);
michael@0 632 this.type = "integer";
michael@0 633 }
michael@0 634 NumberToken.prototype = new StringValuedToken;
michael@0 635 NumberToken.prototype.tokenType = "NUMBER";
michael@0 636 NumberToken.prototype.toString = function() {
michael@0 637 if(this.type == "integer")
michael@0 638 return "INT("+this.value+")";
michael@0 639 return "NUMBER("+this.value+")";
michael@0 640 }
michael@0 641 NumberToken.prototype.finish = function() {
michael@0 642 this.repr = stringFromCodeArray(this.value);
michael@0 643 this.value = this.repr * 1;
michael@0 644 if(Math.abs(this.value) % 1 != 0) this.type = "number";
michael@0 645 return this;
michael@0 646 }
michael@0 647
michael@0 648 function PercentageToken(val) {
michael@0 649 // These are always created by passing a NumberToken as val
michael@0 650 val.finish();
michael@0 651 this.value = val.value;
michael@0 652 this.repr = val.repr;
michael@0 653 }
michael@0 654 PercentageToken.prototype = new CSSParserToken;
michael@0 655 PercentageToken.prototype.tokenType = "PERCENTAGE";
michael@0 656 PercentageToken.prototype.toString = function() { return "PERCENTAGE("+this.value+")"; }
michael@0 657
michael@0 658 function DimensionToken(val,unit) {
michael@0 659 // These are always created by passing a NumberToken as the val
michael@0 660 val.finish();
michael@0 661 this.num = val.value;
michael@0 662 this.unit = [];
michael@0 663 this.repr = val.repr;
michael@0 664 this.append(unit);
michael@0 665 }
michael@0 666 DimensionToken.prototype = new CSSParserToken;
michael@0 667 DimensionToken.prototype.tokenType = "DIMENSION";
michael@0 668 DimensionToken.prototype.toString = function() { return "DIM("+this.num+","+this.unit+")"; }
michael@0 669 DimensionToken.prototype.append = function(val) {
michael@0 670 if(val instanceof Array) {
michael@0 671 for(var i = 0; i < val.length; i++) {
michael@0 672 this.unit.push(val[i]);
michael@0 673 }
michael@0 674 } else {
michael@0 675 this.unit.push(val);
michael@0 676 }
michael@0 677 return true;
michael@0 678 }
michael@0 679 DimensionToken.prototype.finish = function() {
michael@0 680 this.unit = stringFromCodeArray(this.unit);
michael@0 681 this.repr += this.unit;
michael@0 682 return this;
michael@0 683 }
michael@0 684
michael@0 685 function UnicodeRangeToken(start,end) {
michael@0 686 // start and end are array of char codes, completely finished
michael@0 687 start = parseInt(stringFromCodeArray(start),16);
michael@0 688 if(end === undefined) end = start + 1;
michael@0 689 else end = parseInt(stringFromCodeArray(end),16);
michael@0 690
michael@0 691 if(start > maximumallowedcodepoint) end = start;
michael@0 692 if(end < start) end = start;
michael@0 693 if(end > maximumallowedcodepoint) end = maximumallowedcodepoint;
michael@0 694
michael@0 695 this.start = start;
michael@0 696 this.end = end;
michael@0 697 return this;
michael@0 698 }
michael@0 699 UnicodeRangeToken.prototype = new CSSParserToken;
michael@0 700 UnicodeRangeToken.prototype.tokenType = "UNICODE-RANGE";
michael@0 701 UnicodeRangeToken.prototype.toString = function() {
michael@0 702 if(this.start+1 == this.end)
michael@0 703 return "UNICODE-RANGE("+this.start.toString(16).toUpperCase()+")";
michael@0 704 if(this.start < this.end)
michael@0 705 return "UNICODE-RANGE("+this.start.toString(16).toUpperCase()+"-"+this.end.toString(16).toUpperCase()+")";
michael@0 706 return "UNICODE-RANGE()";
michael@0 707 }
michael@0 708 UnicodeRangeToken.prototype.contains = function(code) {
michael@0 709 return code >= this.start && code < this.end;
michael@0 710 }
michael@0 711
michael@0 712
michael@0 713 // Exportation.
michael@0 714 // TODO: also export the various tokens objects?
michael@0 715 module.exports = tokenize;
michael@0 716
michael@0 717 }));

mercurial