specificity.js 2.4 KB

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