[79a0317] | 1 | var Marker = require('../../tokenizer/marker');
|
---|
| 2 |
|
---|
| 3 | var Selector = {
|
---|
| 4 | ADJACENT_SIBLING: '+',
|
---|
| 5 | DESCENDANT: '>',
|
---|
| 6 | DOT: '.',
|
---|
| 7 | HASH: '#',
|
---|
| 8 | NON_ADJACENT_SIBLING: '~',
|
---|
| 9 | PSEUDO: ':'
|
---|
| 10 | };
|
---|
| 11 |
|
---|
| 12 | var LETTER_PATTERN = /[a-zA-Z]/;
|
---|
| 13 | var NOT_PREFIX = ':not(';
|
---|
| 14 | var SEPARATOR_PATTERN = /[\s,(>~+]/;
|
---|
| 15 |
|
---|
| 16 | function specificity(selector) {
|
---|
| 17 | var result = [0, 0, 0];
|
---|
| 18 | var character;
|
---|
| 19 | var isEscaped;
|
---|
| 20 | var isSingleQuoted;
|
---|
| 21 | var isDoubleQuoted;
|
---|
| 22 | var roundBracketLevel = 0;
|
---|
| 23 | var couldIntroduceNewTypeSelector;
|
---|
| 24 | var withinNotPseudoClass = false;
|
---|
| 25 | var wasPseudoClass = false;
|
---|
| 26 | var i, l;
|
---|
| 27 |
|
---|
| 28 | for (i = 0, l = selector.length; i < l; i++) {
|
---|
| 29 | character = selector[i];
|
---|
| 30 |
|
---|
| 31 | if (isEscaped) {
|
---|
| 32 | // noop
|
---|
| 33 | } else if (character == Marker.SINGLE_QUOTE && !isDoubleQuoted && !isSingleQuoted) {
|
---|
| 34 | isSingleQuoted = true;
|
---|
| 35 | } else if (character == Marker.SINGLE_QUOTE && !isDoubleQuoted && isSingleQuoted) {
|
---|
| 36 | isSingleQuoted = false;
|
---|
| 37 | } else if (character == Marker.DOUBLE_QUOTE && !isDoubleQuoted && !isSingleQuoted) {
|
---|
| 38 | isDoubleQuoted = true;
|
---|
| 39 | } else if (character == Marker.DOUBLE_QUOTE && isDoubleQuoted && !isSingleQuoted) {
|
---|
| 40 | isDoubleQuoted = false;
|
---|
| 41 | } else if (isSingleQuoted || isDoubleQuoted) {
|
---|
| 42 | continue;
|
---|
| 43 | } else if (roundBracketLevel > 0 && !withinNotPseudoClass) {
|
---|
| 44 | // noop
|
---|
| 45 | } else if (character == Marker.OPEN_ROUND_BRACKET) {
|
---|
| 46 | roundBracketLevel++;
|
---|
| 47 | } else if (character == Marker.CLOSE_ROUND_BRACKET && roundBracketLevel == 1) {
|
---|
| 48 | roundBracketLevel--;
|
---|
| 49 | withinNotPseudoClass = false;
|
---|
| 50 | } else if (character == Marker.CLOSE_ROUND_BRACKET) {
|
---|
| 51 | roundBracketLevel--;
|
---|
| 52 | } else if (character == Selector.HASH) {
|
---|
| 53 | result[0]++;
|
---|
| 54 | } else if (character == Selector.DOT || character == Marker.OPEN_SQUARE_BRACKET) {
|
---|
| 55 | result[1]++;
|
---|
| 56 | } else if (character == Selector.PSEUDO && !wasPseudoClass && !isNotPseudoClass(selector, i)) {
|
---|
| 57 | result[1]++;
|
---|
| 58 | withinNotPseudoClass = false;
|
---|
| 59 | } else if (character == Selector.PSEUDO) {
|
---|
| 60 | withinNotPseudoClass = true;
|
---|
| 61 | } else if ((i === 0 || couldIntroduceNewTypeSelector) && LETTER_PATTERN.test(character)) {
|
---|
| 62 | result[2]++;
|
---|
| 63 | }
|
---|
| 64 |
|
---|
| 65 | isEscaped = character == Marker.BACK_SLASH;
|
---|
| 66 | wasPseudoClass = character == Selector.PSEUDO;
|
---|
| 67 | couldIntroduceNewTypeSelector = !isEscaped && SEPARATOR_PATTERN.test(character);
|
---|
| 68 | }
|
---|
| 69 |
|
---|
| 70 | return result;
|
---|
| 71 | }
|
---|
| 72 |
|
---|
| 73 | function isNotPseudoClass(selector, index) {
|
---|
| 74 | return selector.indexOf(NOT_PREFIX, index) === index;
|
---|
| 75 | }
|
---|
| 76 |
|
---|
| 77 | module.exports = specificity;
|
---|