1var Marker = require('../../tokenizer/marker');
2
3var Selector = {
4  ADJACENT_SIBLING: '+',
5  DESCENDANT: '>',
6  DOT: '.',
7  HASH: '#',
8  NON_ADJACENT_SIBLING: '~',
9  PSEUDO: ':'
10};
11
12var LETTER_PATTERN = /[a-zA-Z]/;
13var NOT_PREFIX = ':not(';
14var SEPARATOR_PATTERN = /[\s,\(>~\+]/;
15
16function 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
73function isNotPseudoClass(selector, index) {
74  return selector.indexOf(NOT_PREFIX, index) === index;
75}
76
77module.exports = specificity;
78