| 621 | , ID = 'id' |
| 622 | |
| 623 | function tokenize() { |
| 624 | var escaped = false |
| 625 | , gathered = [] |
| 626 | , state = READY |
| 627 | , data = [] |
| 628 | , idx = 0 |
| 629 | , stream |
| 630 | , length |
| 631 | , quote |
| 632 | , depth |
| 633 | , lhs |
| 634 | , rhs |
| 635 | , cmp |
| 636 | , c |
| 637 | |
| 638 | return stream = through(ondata, onend) |
| 639 | |
| 640 | function ondata(chunk) { |
| 641 | data = data.concat(chunk.split('')) |
| 642 | length = data.length |
| 643 | |
| 644 | while(idx < length && (c = data[idx++])) { |
| 645 | switch(state) { |
| 646 | case READY: state_ready(); break |
| 647 | case ANY_CHILD: state_any_child(); break |
| 648 | case OPERATION: state_op(); break |
| 649 | case ATTR_START: state_attr_start(); break |
| 650 | case ATTR_COMP: state_attr_compare(); break |
| 651 | case ATTR_END: state_attr_end(); break |
| 652 | case PSEUDOCLASS: |
| 653 | case PSEUDOPSEUDO: state_pseudo(); break |
| 654 | case PSEUDOSTART: state_pseudostart(); break |
| 655 | case ID: |
| 656 | case TAG: |
| 657 | case CLASS: state_gather(); break |
| 658 | } |
| 659 | } |
| 660 | |
| 661 | data = data.slice(idx) |
| 662 | } |
| 663 | |
| 664 | function onend(chunk) { |
| 665 | if(arguments.length) { |
| 666 | ondata(chunk) |
| 667 | } |
| 668 | |
| 669 | if(gathered.length) { |
| 670 | stream.queue(token()) |
| 671 | } |
| 672 | } |
| 673 | |
| 674 | function state_ready() { |
| 675 | switch(true) { |
| 676 | case '#' === c: state = ID; break |
| 677 | case '.' === c: state = CLASS; break |
| 678 | case ':' === c: state = PSEUDOCLASS; break |
| 679 | case '[' === c: state = ATTR_START; break |
| 680 | case '!' === c: subject(); break |