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