selector.js 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. 'use strict';
  2. var parser = require('slick/parser');
  3. module.exports = exports = Selector;
  4. /**
  5. * CSS selector constructor.
  6. *
  7. * @param {String} selector text
  8. * @param {Array} optionally, precalculated specificity
  9. * @api public
  10. */
  11. function Selector(text, styleAttribute) {
  12. this.text = text;
  13. this.spec = undefined;
  14. this.styleAttribute = styleAttribute || false;
  15. }
  16. /**
  17. * Get parsed selector.
  18. *
  19. * @api public
  20. */
  21. Selector.prototype.parsed = function() {
  22. if (!this.tokens) { this.tokens = parse(this.text); }
  23. return this.tokens;
  24. };
  25. /**
  26. * Lazy specificity getter
  27. *
  28. * @api public
  29. */
  30. Selector.prototype.specificity = function() {
  31. var styleAttribute = this.styleAttribute;
  32. if (!this.spec) { this.spec = specificity(this.text, this.parsed()); }
  33. return this.spec;
  34. function specificity(text, parsed) {
  35. var expressions = parsed || parse(text);
  36. var spec = [styleAttribute ? 1 : 0, 0, 0, 0];
  37. var nots = [];
  38. for (var i = 0; i < expressions.length; i++) {
  39. var expression = expressions[i];
  40. var pseudos = expression.pseudos;
  41. // id awards a point in the second column
  42. if (expression.id) { spec[1]++; }
  43. // classes and attributes award a point each in the third column
  44. if (expression.attributes) { spec[2] += expression.attributes.length; }
  45. if (expression.classList) { spec[2] += expression.classList.length; }
  46. // tag awards a point in the fourth column
  47. if (expression.tag && expression.tag !== '*') { spec[3]++; }
  48. // pseudos award a point each in the fourth column
  49. if (pseudos) {
  50. spec[3] += pseudos.length;
  51. for (var p = 0; p < pseudos.length; p++) {
  52. if (pseudos[p].name === 'not') {
  53. nots.push(pseudos[p].value);
  54. spec[3]--;
  55. }
  56. }
  57. }
  58. }
  59. for (var ii = nots.length; ii--;) {
  60. var not = specificity(nots[ii]);
  61. for (var jj = 4; jj--;) { spec[jj] += not[jj]; }
  62. }
  63. return spec;
  64. }
  65. };
  66. /**
  67. * Parses a selector and returns the tokens.
  68. *
  69. * @param {String} selector
  70. * @api private.
  71. */
  72. function parse(text) {
  73. try {
  74. return parser(text)[0];
  75. } catch (e) {
  76. return [];
  77. }
  78. }