browser/devtools/sourceeditor/css-tokenizer.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/devtools/sourceeditor/css-tokenizer.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,717 @@
     1.4 +/**
     1.5 + * This file is taken from the below mentioned url and is under CC0 license.
     1.6 + * https://github.com/tabatkins/css-parser/blob/master/tokenizer.js
     1.7 + * Please retain this comment while updating this file from upstream.
     1.8 + */
     1.9 +
    1.10 +(function (root, factory) {
    1.11 +    // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
    1.12 +    // Rhino, and plain browser loading.
    1.13 +    if (typeof define === 'function' && define.amd) {
    1.14 +        define(['exports'], factory);
    1.15 +    } else if (typeof exports !== 'undefined') {
    1.16 +        factory(exports);
    1.17 +    } else {
    1.18 +        factory(root);
    1.19 +    }
    1.20 +}(this, function (exports) {
    1.21 +
    1.22 +var between = function (num, first, last) { return num >= first && num <= last; }
    1.23 +function digit(code) { return between(code, 0x30,0x39); }
    1.24 +function hexdigit(code) { return digit(code) || between(code, 0x41,0x46) || between(code, 0x61,0x66); }
    1.25 +function uppercaseletter(code) { return between(code, 0x41,0x5a); }
    1.26 +function lowercaseletter(code) { return between(code, 0x61,0x7a); }
    1.27 +function letter(code) { return uppercaseletter(code) || lowercaseletter(code); }
    1.28 +function nonascii(code) { return code >= 0xa0; }
    1.29 +function namestartchar(code) { return letter(code) || nonascii(code) || code == 0x5f; }
    1.30 +function namechar(code) { return namestartchar(code) || digit(code) || code == 0x2d; }
    1.31 +function nonprintable(code) { return between(code, 0,8) || between(code, 0xe,0x1f) || between(code, 0x7f,0x9f); }
    1.32 +function newline(code) { return code == 0xa || code == 0xc; }
    1.33 +function whitespace(code) { return newline(code) || code == 9 || code == 0x20; }
    1.34 +function badescape(code) { return newline(code) || isNaN(code); }
    1.35 +
    1.36 +// Note: I'm not yet acting smart enough to actually handle astral characters.
    1.37 +var maximumallowedcodepoint = 0x10ffff;
    1.38 +
    1.39 +function tokenize(str, options) {
    1.40 +  if(options == undefined) options = {transformFunctionWhitespace:false, scientificNotation:false};
    1.41 +  var i = -1;
    1.42 +  var tokens = [];
    1.43 +  var state = "data";
    1.44 +  var code;
    1.45 +  var currtoken;
    1.46 +
    1.47 +  // Line number information.
    1.48 +  var line = 0;
    1.49 +  var column = 0;
    1.50 +  // The only use of lastLineLength is in reconsume().
    1.51 +  var lastLineLength = 0;
    1.52 +  var incrLineno = function() {
    1.53 +    line += 1;
    1.54 +    lastLineLength = column;
    1.55 +    column = 0;
    1.56 +  };
    1.57 +  var locStart = {line:line, column:column};
    1.58 +
    1.59 +  var next = function(num) { if(num === undefined) num = 1; return str.charCodeAt(i+num); };
    1.60 +  var consume = function(num) {
    1.61 +    if(num === undefined)
    1.62 +      num = 1;
    1.63 +    i += num;
    1.64 +    code = str.charCodeAt(i);
    1.65 +    if (newline(code)) incrLineno();
    1.66 +    else column += num;
    1.67 +    //console.log('Consume '+i+' '+String.fromCharCode(code) + ' 0x' + code.toString(16));
    1.68 +    return true;
    1.69 +  };
    1.70 +  var reconsume = function() {
    1.71 +    i -= 1;
    1.72 +    if (newline(code)) {
    1.73 +      line -= 1;
    1.74 +      column = lastLineLength;
    1.75 +    } else {
    1.76 +      column -= 1;
    1.77 +    }
    1.78 +    locStart.line = line;
    1.79 +    locStart.column = column;
    1.80 +    return true;
    1.81 +  };
    1.82 +  var eof = function() { return i >= str.length; };
    1.83 +  var donothing = function() {};
    1.84 +  var emit = function(token) {
    1.85 +    if(token) {
    1.86 +      token.finish();
    1.87 +    } else {
    1.88 +      token = currtoken.finish();
    1.89 +    }
    1.90 +    if (options.loc === true) {
    1.91 +      token.loc = {};
    1.92 +      token.loc.start = {line:locStart.line, column:locStart.column};
    1.93 +      locStart = {line: line, column: column};
    1.94 +      token.loc.end = locStart;
    1.95 +    }
    1.96 +    tokens.push(token);
    1.97 +    //console.log('Emitting ' + token);
    1.98 +    currtoken = undefined;
    1.99 +    return true;
   1.100 +  };
   1.101 +  var create = function(token) { currtoken = token; return true; };
   1.102 +  var parseerror = function() { console.log("Parse error at index " + i + ", processing codepoint 0x" + code.toString(16) + " in state " + state + ".");return true; };
   1.103 +  var switchto = function(newstate) {
   1.104 +    state = newstate;
   1.105 +    //console.log('Switching to ' + state);
   1.106 +    return true;
   1.107 +  };
   1.108 +  var consumeEscape = function() {
   1.109 +    // Assume the the current character is the \
   1.110 +    consume();
   1.111 +    if(hexdigit(code)) {
   1.112 +      // Consume 1-6 hex digits
   1.113 +      var digits = [];
   1.114 +      for(var total = 0; total < 6; total++) {
   1.115 +        if(hexdigit(code)) {
   1.116 +          digits.push(code);
   1.117 +          consume();
   1.118 +        } else { break; }
   1.119 +      }
   1.120 +      var value = parseInt(digits.map(String.fromCharCode).join(''), 16);
   1.121 +      if( value > maximumallowedcodepoint ) value = 0xfffd;
   1.122 +      // If the current char is whitespace, cool, we'll just eat it.
   1.123 +      // Otherwise, put it back.
   1.124 +      if(!whitespace(code)) reconsume();
   1.125 +      return value;
   1.126 +    } else {
   1.127 +      return code;
   1.128 +    }
   1.129 +  };
   1.130 +
   1.131 +  for(;;) {
   1.132 +    if(i > str.length*2) return "I'm infinite-looping!";
   1.133 +    consume();
   1.134 +    switch(state) {
   1.135 +    case "data":
   1.136 +      if(whitespace(code)) {
   1.137 +        emit(new WhitespaceToken);
   1.138 +        while(whitespace(next())) consume();
   1.139 +      }
   1.140 +      else if(code == 0x22) switchto("double-quote-string");
   1.141 +      else if(code == 0x23) switchto("hash");
   1.142 +      else if(code == 0x27) switchto("single-quote-string");
   1.143 +      else if(code == 0x28) emit(new OpenParenToken);
   1.144 +      else if(code == 0x29) emit(new CloseParenToken);
   1.145 +      else if(code == 0x2b) {
   1.146 +        if(digit(next()) || (next() == 0x2e && digit(next(2)))) switchto("number") && reconsume();
   1.147 +        else emit(new DelimToken(code));
   1.148 +      }
   1.149 +      else if(code == 0x2d) {
   1.150 +        if(next(1) == 0x2d && next(2) == 0x3e) consume(2) && emit(new CDCToken);
   1.151 +        else if(digit(next()) || (next(1) == 0x2e && digit(next(2)))) switchto("number") && reconsume();
   1.152 +        else if(namestartchar(next())) switchto("identifier") && reconsume();
   1.153 +        else emit(new DelimToken(code));
   1.154 +      }
   1.155 +      else if(code == 0x2e) {
   1.156 +        if(digit(next())) switchto("number") && reconsume();
   1.157 +        else emit(new DelimToken(code));
   1.158 +      }
   1.159 +      else if(code == 0x2f) {
   1.160 +        if(next() == 0x2a) switchto("comment");
   1.161 +        else emit(new DelimToken(code));
   1.162 +      }
   1.163 +      else if(code == 0x3a) emit(new ColonToken);
   1.164 +      else if(code == 0x3b) emit(new SemicolonToken);
   1.165 +      else if(code == 0x3c) {
   1.166 +        if(next(1) == 0x21 && next(2) == 0x2d && next(3) == 0x2d) consume(3) && emit(new CDOToken);
   1.167 +        else emit(new DelimToken(code));
   1.168 +      }
   1.169 +      else if(code == 0x40) switchto("at-keyword");
   1.170 +      else if(code == 0x5b) emit(new OpenSquareToken);
   1.171 +      else if(code == 0x5c) {
   1.172 +        if(badescape(next())) parseerror() && emit(new DelimToken(code));
   1.173 +        else switchto("identifier") && reconsume();
   1.174 +      }
   1.175 +      else if(code == 0x5d) emit(new CloseSquareToken);
   1.176 +      else if(code == 0x7b) emit(new OpenCurlyToken);
   1.177 +      else if(code == 0x7d) emit(new CloseCurlyToken);
   1.178 +      else if(digit(code)) switchto("number") && reconsume();
   1.179 +      else if(code == 0x55 || code == 0x75) {
   1.180 +        if(next(1) == 0x2b && hexdigit(next(2))) consume() && switchto("unicode-range");
   1.181 +        else if((next(1) == 0x52 || next(1) == 0x72) && (next(2) == 0x4c || next(2) == 0x6c) && (next(3) == 0x28)) consume(3) && switchto("url");
   1.182 +        else switchto("identifier") && reconsume();
   1.183 +      }
   1.184 +      else if(namestartchar(code)) switchto("identifier") && reconsume();
   1.185 +      else if(eof()) { emit(new EOFToken); return tokens; }
   1.186 +      else emit(new DelimToken(code));
   1.187 +      break;
   1.188 +
   1.189 +    case "double-quote-string":
   1.190 +      if(currtoken == undefined) create(new StringToken);
   1.191 +
   1.192 +      if(code == 0x22) emit() && switchto("data");
   1.193 +      else if(eof()) parseerror() && emit() && switchto("data");
   1.194 +      else if(newline(code)) parseerror() && emit(new BadStringToken) && switchto("data") && reconsume();
   1.195 +      else if(code == 0x5c) {
   1.196 +        if(badescape(next())) parseerror() && emit(new BadStringToken) && switchto("data");
   1.197 +        else if(newline(next())) consume();
   1.198 +        else currtoken.append(consumeEscape());
   1.199 +      }
   1.200 +      else currtoken.append(code);
   1.201 +      break;
   1.202 +
   1.203 +    case "single-quote-string":
   1.204 +      if(currtoken == undefined) create(new StringToken);
   1.205 +
   1.206 +      if(code == 0x27) emit() && switchto("data");
   1.207 +      else if(eof()) parseerror() && emit() && switchto("data");
   1.208 +      else if(newline(code)) parseerror() && emit(new BadStringToken) && switchto("data") && reconsume();
   1.209 +      else if(code == 0x5c) {
   1.210 +        if(badescape(next())) parseerror() && emit(new BadStringToken) && switchto("data");
   1.211 +        else if(newline(next())) consume();
   1.212 +        else currtoken.append(consumeEscape());
   1.213 +      }
   1.214 +      else currtoken.append(code);
   1.215 +      break;
   1.216 +
   1.217 +    case "hash":
   1.218 +      if(namechar(code)) create(new HashToken(code)) && switchto("hash-rest");
   1.219 +      else if(code == 0x5c) {
   1.220 +        if(badescape(next())) parseerror() && emit(new DelimToken(0x23)) && switchto("data") && reconsume();
   1.221 +        else create(new HashToken(consumeEscape())) && switchto('hash-rest');
   1.222 +      }
   1.223 +      else emit(new DelimToken(0x23)) && switchto('data') && reconsume();
   1.224 +      break;
   1.225 +
   1.226 +    case "hash-rest":
   1.227 +      if(namechar(code)) currtoken.append(code);
   1.228 +      else if(code == 0x5c) {
   1.229 +        if(badescape(next())) parseerror() && emit(new DelimToken(0x23)) && switchto("data") && reconsume();
   1.230 +        else currtoken.append(consumeEscape());
   1.231 +      }
   1.232 +      else emit() && switchto('data') && reconsume();
   1.233 +      break;
   1.234 +
   1.235 +    case "comment":
   1.236 +      if(code == 0x2a) {
   1.237 +        if(next() == 0x2f) consume() && switchto('data');
   1.238 +        else donothing();
   1.239 +      }
   1.240 +      else if(eof()) parseerror() && switchto('data') && reconsume();
   1.241 +      else donothing();
   1.242 +      break;
   1.243 +
   1.244 +    case "at-keyword":
   1.245 +      if(code == 0x2d) {
   1.246 +        if(namestartchar(next())) consume() && create(new AtKeywordToken([0x40,code])) && switchto('at-keyword-rest');
   1.247 +        else emit(new DelimToken(0x40)) && switchto('data') && reconsume();
   1.248 +      }
   1.249 +      else if(namestartchar(code)) create(new AtKeywordToken(code)) && switchto('at-keyword-rest');
   1.250 +      else if(code == 0x5c) {
   1.251 +        if(badescape(next())) parseerror() && emit(new DelimToken(0x23)) && switchto("data") && reconsume();
   1.252 +        else create(new AtKeywordToken(consumeEscape())) && switchto('at-keyword-rest');
   1.253 +      }
   1.254 +      else emit(new DelimToken(0x40)) && switchto('data') && reconsume();
   1.255 +      break;
   1.256 +
   1.257 +    case "at-keyword-rest":
   1.258 +      if(namechar(code)) currtoken.append(code);
   1.259 +      else if(code == 0x5c) {
   1.260 +        if(badescape(next())) parseerror() && emit() && switchto("data") && reconsume();
   1.261 +        else currtoken.append(consumeEscape());
   1.262 +      }
   1.263 +      else emit() && switchto('data') && reconsume();
   1.264 +      break;
   1.265 +
   1.266 +    case "identifier":
   1.267 +      if(code == 0x2d) {
   1.268 +        if(namestartchar(next())) create(new IdentifierToken(code)) && switchto('identifier-rest');
   1.269 +        else switchto('data') && reconsume();
   1.270 +      }
   1.271 +      else if(namestartchar(code)) create(new IdentifierToken(code)) && switchto('identifier-rest');
   1.272 +      else if(code == 0x5c) {
   1.273 +        if(badescape(next())) parseerror() && switchto("data") && reconsume();
   1.274 +        else create(new IdentifierToken(consumeEscape())) && switchto('identifier-rest');
   1.275 +      }
   1.276 +      else switchto('data') && reconsume();
   1.277 +      break;
   1.278 +
   1.279 +    case "identifier-rest":
   1.280 +      if(namechar(code)) currtoken.append(code);
   1.281 +      else if(code == 0x5c) {
   1.282 +        if(badescape(next())) parseerror() && emit() && switchto("data") && reconsume();
   1.283 +        else currtoken.append(consumeEscape());
   1.284 +      }
   1.285 +      else if(code == 0x28) emit(new FunctionToken(currtoken)) && switchto('data');
   1.286 +      else if(whitespace(code) && options.transformFunctionWhitespace) switchto('transform-function-whitespace');
   1.287 +      else emit() && switchto('data') && reconsume();
   1.288 +      break;
   1.289 +
   1.290 +    case "transform-function-whitespace":
   1.291 +      if(whitespace(code)) donothing();
   1.292 +      else if(code == 0x28) emit(new FunctionToken(currtoken)) && switchto('data');
   1.293 +      else emit() && switchto('data') && reconsume();
   1.294 +      break;
   1.295 +
   1.296 +    case "number":
   1.297 +      create(new NumberToken());
   1.298 +
   1.299 +      if(code == 0x2d) {
   1.300 +        if(digit(next())) consume() && currtoken.append([0x2d,code]) && switchto('number-rest');
   1.301 +        else if(next(1) == 0x2e && digit(next(2))) consume(2) && currtoken.append([0x2d,0x2e,code]) && switchto('number-fraction');
   1.302 +        else switchto('data') && reconsume();
   1.303 +      }
   1.304 +      else if(code == 0x2b) {
   1.305 +        if(digit(next())) consume() && currtoken.append([0x2b,code]) && switchto('number-rest');
   1.306 +        else if(next(1) == 0x2e && digit(next(2))) consume(2) && currtoken.append([0x2b,0x2e,code]) && switchto('number-fraction');
   1.307 +        else switchto('data') && reconsume();
   1.308 +      }
   1.309 +      else if(digit(code)) currtoken.append(code) && switchto('number-rest');
   1.310 +      else if(code == 0x2e) {
   1.311 +        if(digit(next())) consume() && currtoken.append([0x2e,code]) && switchto('number-fraction');
   1.312 +        else switchto('data') && reconsume();
   1.313 +      }
   1.314 +      else switchto('data') && reconsume();
   1.315 +      break;
   1.316 +
   1.317 +    case "number-rest":
   1.318 +      if(digit(code)) currtoken.append(code);
   1.319 +      else if(code == 0x2e) {
   1.320 +        if(digit(next())) consume() && currtoken.append([0x2e,code]) && switchto('number-fraction');
   1.321 +        else emit() && switchto('data') && reconsume();
   1.322 +      }
   1.323 +      else if(code == 0x25) emit(new PercentageToken(currtoken)) && switchto('data') && reconsume();
   1.324 +      else if(code == 0x45 || code == 0x65) {
   1.325 +        if(!options.scientificNotation) create(new DimensionToken(currtoken,code)) && switchto('dimension');
   1.326 +        else if(digit(next())) consume() && currtoken.append([0x25,code]) && switchto('sci-notation');
   1.327 +        else if((next(1) == 0x2b || next(1) == 0x2d) && digit(next(2))) currtoken.append([0x25,next(1),next(2)]) && consume(2) && switchto('sci-notation');
   1.328 +        else create(new DimensionToken(currtoken,code)) && switchto('dimension');
   1.329 +      }
   1.330 +      else if(code == 0x2d) {
   1.331 +        if(namestartchar(next())) consume() && create(new DimensionToken(currtoken,[0x2d,code])) && switchto('dimension');
   1.332 +        else if(next(1) == 0x5c && badescape(next(2))) parseerror() && emit() && switchto('data') && reconsume();
   1.333 +        else if(next(1) == 0x5c) consume() && create(new DimensionToken(currtoken, [0x2d,consumeEscape()])) && switchto('dimension');
   1.334 +        else emit() && switchto('data') && reconsume();
   1.335 +      }
   1.336 +      else if(namestartchar(code)) create(new DimensionToken(currtoken, code)) && switchto('dimension');
   1.337 +      else if(code == 0x5c) {
   1.338 +        if(badescape(next)) emit() && switchto('data') && reconsume();
   1.339 +        else create(new DimensionToken(currtoken,consumeEscape)) && switchto('dimension');
   1.340 +      }
   1.341 +      else emit() && switchto('data') && reconsume();
   1.342 +      break;
   1.343 +
   1.344 +    case "number-fraction":
   1.345 +      currtoken.type = "number";
   1.346 +
   1.347 +      if(digit(code)) currtoken.append(code);
   1.348 +      else if(code == 0x2e) emit() && switchto('data') && reconsume();
   1.349 +      else if(code == 0x25) emit(new PercentageToken(currtoken)) && switchto('data') && reconsume();
   1.350 +      else if(code == 0x45 || code == 0x65) {
   1.351 +        if(!options.scientificNotation) create(new DimensionToken(currtoken,code)) && switchto('dimension');
   1.352 +        else if(digit(next())) consume() && currtoken.append([0x25,code]) && switchto('sci-notation');
   1.353 +        else if((next(1) == 0x2b || next(1) == 0x2d) && digit(next(2))) currtoken.append([0x25,next(1),next(2)]) && consume(2) && switchto('sci-notation');
   1.354 +        else create(new DimensionToken(currtoken,code)) && switchto('dimension');
   1.355 +      }
   1.356 +      else if(code == 0x2d) {
   1.357 +        if(namestartchar(next())) consume() && create(new DimensionToken(currtoken,[0x2d,code])) && switchto('dimension');
   1.358 +        else if(next(1) == 0x5c && badescape(next(2))) parseerror() && emit() && switchto('data') && reconsume();
   1.359 +        else if(next(1) == 0x5c) consume() && create(new DimensionToken(currtoken, [0x2d,consumeEscape()])) && switchto('dimension');
   1.360 +        else emit() && switchto('data') && reconsume();
   1.361 +      }
   1.362 +      else if(namestartchar(code)) create(new DimensionToken(currtoken, code)) && switchto('dimension');
   1.363 +      else if(code == 0x5c) {
   1.364 +        if(badescape(next)) emit() && switchto('data') && reconsume();
   1.365 +        else create(new DimensionToken(currtoken,consumeEscape)) && switchto('dimension');
   1.366 +      }
   1.367 +      else emit() && switchto('data') && reconsume();
   1.368 +      break;
   1.369 +
   1.370 +    case "dimension":
   1.371 +      if(namechar(code)) currtoken.append(code);
   1.372 +      else if(code == 0x5c) {
   1.373 +        if(badescape(next())) parseerror() && emit() && switchto('data') && reconsume();
   1.374 +        else currtoken.append(consumeEscape());
   1.375 +      }
   1.376 +      else emit() && switchto('data') && reconsume();
   1.377 +      break;
   1.378 +
   1.379 +    case "sci-notation":
   1.380 +      if(digit(code)) currtoken.append(code);
   1.381 +      else emit() && switchto('data') && reconsume();
   1.382 +      break;
   1.383 +
   1.384 +    case "url":
   1.385 +      if(code == 0x22) switchto('url-double-quote');
   1.386 +      else if(code == 0x27) switchto('url-single-quote');
   1.387 +      else if(code == 0x29) emit(new URLToken) && switchto('data');
   1.388 +      else if(whitespace(code)) donothing();
   1.389 +      else switchto('url-unquoted') && reconsume();
   1.390 +      break;
   1.391 +
   1.392 +    case "url-double-quote":
   1.393 +      if(currtoken == undefined) create(new URLToken);
   1.394 +
   1.395 +      if(code == 0x22) switchto('url-end');
   1.396 +      else if(newline(code)) parseerror() && switchto('bad-url');
   1.397 +      else if(code == 0x5c) {
   1.398 +        if(newline(next())) consume();
   1.399 +        else if(badescape(next())) parseerror() && emit(new BadURLToken) && switchto('data') && reconsume();
   1.400 +        else currtoken.append(consumeEscape());
   1.401 +      }
   1.402 +      else currtoken.append(code);
   1.403 +      break;
   1.404 +
   1.405 +    case "url-single-quote":
   1.406 +      if(currtoken == undefined) create(new URLToken);
   1.407 +
   1.408 +      if(code == 0x27) switchto('url-end');
   1.409 +      else if(newline(code)) parseerror() && switchto('bad-url');
   1.410 +      else if(code == 0x5c) {
   1.411 +        if(newline(next())) consume();
   1.412 +        else if(badescape(next())) parseerror() && emit(new BadURLToken) && switchto('data') && reconsume();
   1.413 +        else currtoken.append(consumeEscape());
   1.414 +      }
   1.415 +      else currtoken.append(code);
   1.416 +      break;
   1.417 +
   1.418 +    case "url-end":
   1.419 +      if(whitespace(code)) donothing();
   1.420 +      else if(code == 0x29) emit() && switchto('data');
   1.421 +      else parseerror() && switchto('bad-url') && reconsume();
   1.422 +      break;
   1.423 +
   1.424 +    case "url-unquoted":
   1.425 +      if(currtoken == undefined) create(new URLToken);
   1.426 +
   1.427 +      if(whitespace(code)) switchto('url-end');
   1.428 +      else if(code == 0x29) emit() && switchto('data');
   1.429 +      else if(code == 0x22 || code == 0x27 || code == 0x28 || nonprintable(code)) parseerror() && switchto('bad-url');
   1.430 +      else if(code == 0x5c) {
   1.431 +        if(badescape(next())) parseerror() && switchto('bad-url');
   1.432 +        else currtoken.append(consumeEscape());
   1.433 +      }
   1.434 +      else currtoken.append(code);
   1.435 +      break;
   1.436 +
   1.437 +    case "bad-url":
   1.438 +      if(code == 0x29) emit(new BadURLToken) && switchto('data');
   1.439 +      else if(code == 0x5c) {
   1.440 +        if(badescape(next())) donothing();
   1.441 +        else consumeEscape()
   1.442 +      }
   1.443 +      else donothing();
   1.444 +      break;
   1.445 +
   1.446 +    case "unicode-range":
   1.447 +      // We already know that the current code is a hexdigit.
   1.448 +
   1.449 +      var start = [code], end = [code];
   1.450 +
   1.451 +      for(var total = 1; total < 6; total++) {
   1.452 +        if(hexdigit(next())) {
   1.453 +          consume();
   1.454 +          start.push(code);
   1.455 +          end.push(code);
   1.456 +        }
   1.457 +        else break;
   1.458 +      }
   1.459 +
   1.460 +      if(next() == 0x3f) {
   1.461 +        for(;total < 6; total++) {
   1.462 +          if(next() == 0x3f) {
   1.463 +            consume();
   1.464 +            start.push("0".charCodeAt(0));
   1.465 +            end.push("f".charCodeAt(0));
   1.466 +          }
   1.467 +          else break;
   1.468 +        }
   1.469 +        emit(new UnicodeRangeToken(start,end)) && switchto('data');
   1.470 +      }
   1.471 +      else if(next(1) == 0x2d && hexdigit(next(2))) {
   1.472 +        consume();
   1.473 +        consume();
   1.474 +        end = [code];
   1.475 +        for(var total = 1; total < 6; total++) {
   1.476 +          if(hexdigit(next())) {
   1.477 +            consume();
   1.478 +            end.push(code);
   1.479 +          }
   1.480 +          else break;
   1.481 +        }
   1.482 +        emit(new UnicodeRangeToken(start,end)) && switchto('data');
   1.483 +      }
   1.484 +      else emit(new UnicodeRangeToken(start)) && switchto('data');
   1.485 +      break;
   1.486 +
   1.487 +    default:
   1.488 +      console.log("Unknown state '" + state + "'");
   1.489 +    }
   1.490 +  }
   1.491 +}
   1.492 +
   1.493 +function stringFromCodeArray(arr) {
   1.494 +  return String.fromCharCode.apply(null,arr.filter(function(e){return e;}));
   1.495 +}
   1.496 +
   1.497 +function CSSParserToken(options) { return this; }
   1.498 +CSSParserToken.prototype.finish = function() { return this; }
   1.499 +CSSParserToken.prototype.toString = function() { return this.tokenType; }
   1.500 +CSSParserToken.prototype.toJSON = function() { return this.toString(); }
   1.501 +
   1.502 +function BadStringToken() { return this; }
   1.503 +BadStringToken.prototype = new CSSParserToken;
   1.504 +BadStringToken.prototype.tokenType = "BADSTRING";
   1.505 +
   1.506 +function BadURLToken() { return this; }
   1.507 +BadURLToken.prototype = new CSSParserToken;
   1.508 +BadURLToken.prototype.tokenType = "BADURL";
   1.509 +
   1.510 +function WhitespaceToken() { return this; }
   1.511 +WhitespaceToken.prototype = new CSSParserToken;
   1.512 +WhitespaceToken.prototype.tokenType = "WHITESPACE";
   1.513 +WhitespaceToken.prototype.toString = function() { return "WS"; }
   1.514 +
   1.515 +function CDOToken() { return this; }
   1.516 +CDOToken.prototype = new CSSParserToken;
   1.517 +CDOToken.prototype.tokenType = "CDO";
   1.518 +
   1.519 +function CDCToken() { return this; }
   1.520 +CDCToken.prototype = new CSSParserToken;
   1.521 +CDCToken.prototype.tokenType = "CDC";
   1.522 +
   1.523 +function ColonToken() { return this; }
   1.524 +ColonToken.prototype = new CSSParserToken;
   1.525 +ColonToken.prototype.tokenType = ":";
   1.526 +
   1.527 +function SemicolonToken() { return this; }
   1.528 +SemicolonToken.prototype = new CSSParserToken;
   1.529 +SemicolonToken.prototype.tokenType = ";";
   1.530 +
   1.531 +function OpenCurlyToken() { return this; }
   1.532 +OpenCurlyToken.prototype = new CSSParserToken;
   1.533 +OpenCurlyToken.prototype.tokenType = "{";
   1.534 +
   1.535 +function CloseCurlyToken() { return this; }
   1.536 +CloseCurlyToken.prototype = new CSSParserToken;
   1.537 +CloseCurlyToken.prototype.tokenType = "}";
   1.538 +
   1.539 +function OpenSquareToken() { return this; }
   1.540 +OpenSquareToken.prototype = new CSSParserToken;
   1.541 +OpenSquareToken.prototype.tokenType = "[";
   1.542 +
   1.543 +function CloseSquareToken() { return this; }
   1.544 +CloseSquareToken.prototype = new CSSParserToken;
   1.545 +CloseSquareToken.prototype.tokenType = "]";
   1.546 +
   1.547 +function OpenParenToken() { return this; }
   1.548 +OpenParenToken.prototype = new CSSParserToken;
   1.549 +OpenParenToken.prototype.tokenType = "(";
   1.550 +
   1.551 +function CloseParenToken() { return this; }
   1.552 +CloseParenToken.prototype = new CSSParserToken;
   1.553 +CloseParenToken.prototype.tokenType = ")";
   1.554 +
   1.555 +function EOFToken() { return this; }
   1.556 +EOFToken.prototype = new CSSParserToken;
   1.557 +EOFToken.prototype.tokenType = "EOF";
   1.558 +
   1.559 +function DelimToken(code) {
   1.560 +  this.value = String.fromCharCode(code);
   1.561 +  return this;
   1.562 +}
   1.563 +DelimToken.prototype = new CSSParserToken;
   1.564 +DelimToken.prototype.tokenType = "DELIM";
   1.565 +DelimToken.prototype.toString = function() { return "DELIM("+this.value+")"; }
   1.566 +
   1.567 +function StringValuedToken() { return this; }
   1.568 +StringValuedToken.prototype = new CSSParserToken;
   1.569 +StringValuedToken.prototype.append = function(val) {
   1.570 +  if(val instanceof Array) {
   1.571 +    for(var i = 0; i < val.length; i++) {
   1.572 +      this.value.push(val[i]);
   1.573 +    }
   1.574 +  } else {
   1.575 +    this.value.push(val);
   1.576 +  }
   1.577 +  return true;
   1.578 +}
   1.579 +StringValuedToken.prototype.finish = function() {
   1.580 +  this.value = stringFromCodeArray(this.value);
   1.581 +  return this;
   1.582 +}
   1.583 +
   1.584 +function IdentifierToken(val) {
   1.585 +  this.value = [];
   1.586 +  this.append(val);
   1.587 +}
   1.588 +IdentifierToken.prototype = new StringValuedToken;
   1.589 +IdentifierToken.prototype.tokenType = "IDENT";
   1.590 +IdentifierToken.prototype.toString = function() { return "IDENT("+this.value+")"; }
   1.591 +
   1.592 +function FunctionToken(val) {
   1.593 +  // These are always constructed by passing an IdentifierToken
   1.594 +  this.value = val.finish().value;
   1.595 +}
   1.596 +FunctionToken.prototype = new CSSParserToken;
   1.597 +FunctionToken.prototype.tokenType = "FUNCTION";
   1.598 +FunctionToken.prototype.toString = function() { return "FUNCTION("+this.value+")"; }
   1.599 +
   1.600 +function AtKeywordToken(val) {
   1.601 +  this.value = [];
   1.602 +  this.append(val);
   1.603 +}
   1.604 +AtKeywordToken.prototype = new StringValuedToken;
   1.605 +AtKeywordToken.prototype.tokenType = "AT-KEYWORD";
   1.606 +AtKeywordToken.prototype.toString = function() { return "AT("+this.value+")"; }
   1.607 +
   1.608 +function HashToken(val) {
   1.609 +  this.value = [];
   1.610 +  this.append(val);
   1.611 +}
   1.612 +HashToken.prototype = new StringValuedToken;
   1.613 +HashToken.prototype.tokenType = "HASH";
   1.614 +HashToken.prototype.toString = function() { return "HASH("+this.value+")"; }
   1.615 +
   1.616 +function StringToken(val) {
   1.617 +  this.value = [];
   1.618 +  this.append(val);
   1.619 +}
   1.620 +StringToken.prototype = new StringValuedToken;
   1.621 +StringToken.prototype.tokenType = "STRING";
   1.622 +StringToken.prototype.toString = function() { return "\""+this.value+"\""; }
   1.623 +
   1.624 +function URLToken(val) {
   1.625 +  this.value = [];
   1.626 +  this.append(val);
   1.627 +}
   1.628 +URLToken.prototype = new StringValuedToken;
   1.629 +URLToken.prototype.tokenType = "URL";
   1.630 +URLToken.prototype.toString = function() { return "URL("+this.value+")"; }
   1.631 +
   1.632 +function NumberToken(val) {
   1.633 +  this.value = [];
   1.634 +  this.append(val);
   1.635 +  this.type = "integer";
   1.636 +}
   1.637 +NumberToken.prototype = new StringValuedToken;
   1.638 +NumberToken.prototype.tokenType = "NUMBER";
   1.639 +NumberToken.prototype.toString = function() {
   1.640 +  if(this.type == "integer")
   1.641 +    return "INT("+this.value+")";
   1.642 +  return "NUMBER("+this.value+")";
   1.643 +}
   1.644 +NumberToken.prototype.finish = function() {
   1.645 +  this.repr = stringFromCodeArray(this.value);
   1.646 +  this.value = this.repr * 1;
   1.647 +  if(Math.abs(this.value) % 1 != 0) this.type = "number";
   1.648 +  return this;
   1.649 +}
   1.650 +
   1.651 +function PercentageToken(val) {
   1.652 +  // These are always created by passing a NumberToken as val
   1.653 +  val.finish();
   1.654 +  this.value = val.value;
   1.655 +  this.repr = val.repr;
   1.656 +}
   1.657 +PercentageToken.prototype = new CSSParserToken;
   1.658 +PercentageToken.prototype.tokenType = "PERCENTAGE";
   1.659 +PercentageToken.prototype.toString = function() { return "PERCENTAGE("+this.value+")"; }
   1.660 +
   1.661 +function DimensionToken(val,unit) {
   1.662 +  // These are always created by passing a NumberToken as the val
   1.663 +  val.finish();
   1.664 +  this.num = val.value;
   1.665 +  this.unit = [];
   1.666 +  this.repr = val.repr;
   1.667 +  this.append(unit);
   1.668 +}
   1.669 +DimensionToken.prototype = new CSSParserToken;
   1.670 +DimensionToken.prototype.tokenType = "DIMENSION";
   1.671 +DimensionToken.prototype.toString = function() { return "DIM("+this.num+","+this.unit+")"; }
   1.672 +DimensionToken.prototype.append = function(val) {
   1.673 +  if(val instanceof Array) {
   1.674 +    for(var i = 0; i < val.length; i++) {
   1.675 +      this.unit.push(val[i]);
   1.676 +    }
   1.677 +  } else {
   1.678 +    this.unit.push(val);
   1.679 +  }
   1.680 +  return true;
   1.681 +}
   1.682 +DimensionToken.prototype.finish = function() {
   1.683 +  this.unit = stringFromCodeArray(this.unit);
   1.684 +  this.repr += this.unit;
   1.685 +  return this;
   1.686 +}
   1.687 +
   1.688 +function UnicodeRangeToken(start,end) {
   1.689 +  // start and end are array of char codes, completely finished
   1.690 +  start = parseInt(stringFromCodeArray(start),16);
   1.691 +  if(end === undefined) end = start + 1;
   1.692 +  else end = parseInt(stringFromCodeArray(end),16);
   1.693 +
   1.694 +  if(start > maximumallowedcodepoint) end = start;
   1.695 +  if(end < start) end = start;
   1.696 +  if(end > maximumallowedcodepoint) end = maximumallowedcodepoint;
   1.697 +
   1.698 +  this.start = start;
   1.699 +  this.end = end;
   1.700 +  return this;
   1.701 +}
   1.702 +UnicodeRangeToken.prototype = new CSSParserToken;
   1.703 +UnicodeRangeToken.prototype.tokenType = "UNICODE-RANGE";
   1.704 +UnicodeRangeToken.prototype.toString = function() {
   1.705 +  if(this.start+1 == this.end)
   1.706 +    return "UNICODE-RANGE("+this.start.toString(16).toUpperCase()+")";
   1.707 +  if(this.start < this.end)
   1.708 +    return "UNICODE-RANGE("+this.start.toString(16).toUpperCase()+"-"+this.end.toString(16).toUpperCase()+")";
   1.709 +  return "UNICODE-RANGE()";
   1.710 +}
   1.711 +UnicodeRangeToken.prototype.contains = function(code) {
   1.712 +  return code >= this.start && code < this.end;
   1.713 +}
   1.714 +
   1.715 +
   1.716 +// Exportation.
   1.717 +// TODO: also export the various tokens objects?
   1.718 +module.exports = tokenize;
   1.719 +
   1.720 +}));

mercurial