1 /* normalize-selector v0.1.0 (c) 2014 Kyle Simpson */
3 (function UMD(name,context,definition){
4 if (typeof module !== "undefined" && module.exports) { module.exports = definition(); }
5 else if (typeof define === "function" && define.amd) { define(definition); }
6 else { context[name] = definition(name,context); }
7 })("normalizeSelector",this,function DEF(name,context){
10 function normalizeSelector(sel) {
12 // save unmatched text, if any
13 function saveUnmatched() {
15 // whitespace needed after combinator?
16 if (tokens.length > 0 &&
17 /^[~+>]$/.test(tokens[tokens.length-1])
22 // save unmatched text
23 tokens.push(unmatched);
27 var tokens = [], match, unmatched, regex, state = [0],
28 next_match_idx = 0, prev_match_idx,
29 not_escaped_pattern = /(?:[^\\]|(?:^|[^\\])(?:\\\\)+)$/,
30 whitespace_pattern = /^\s+$/,
31 attribute_nonspecial_pattern = /[^\s=~!^|$*\[\]\(\)]{2}/,
33 /\s+|\/\*|["'>~+\[\(]/g, // general
34 /\s+|\/\*|["'\[\]\(\)]/g, // [..] set
35 /\s+|\/\*|["'\[\]\(\)]/g, // (..) set
36 null, // string literal (placeholder)
46 regex = state_patterns[state[state.length-1]];
48 regex.lastIndex = next_match_idx;
49 match = regex.exec(sel);
51 // matched text to process?
53 prev_match_idx = next_match_idx;
54 next_match_idx = regex.lastIndex;
56 // collect the previous string chunk not matched before this token
57 if (prev_match_idx < next_match_idx - match[0].length) {
58 unmatched = sel.substring(prev_match_idx,next_match_idx - match[0].length);
61 // need to force a space (possibly skipped
62 // previously by the parser)?
64 state[state.length-1] === 1 &&
65 attribute_nonspecial_pattern.test(
66 tokens[tokens.length-1].substr(-1) +
73 // general, [ ] pair, ( ) pair?
74 if (state[state.length-1] < 3) {
77 // starting a [ ] pair?
78 if (match[0] === "[") {
81 // starting a ( ) pair?
82 else if (match[0] === "(") {
85 // starting a string literal?
86 else if (/^["']$/.test(match[0])) {
88 state_patterns[3] = new RegExp(match[0],"g");
90 // starting a comment?
91 else if (match[0] === "/*") {
94 // ending a [ ] or ( ) pair?
95 else if (/^[\]\)]$/.test(match[0]) && state.length > 0) {
98 // handling whitespace or a combinator?
99 else if (/^(?:\s+|[~+>])$/.test(match[0])) {
100 // need to insert whitespace before?
101 if (tokens.length > 0 &&
102 !whitespace_pattern.test(tokens[tokens.length-1]) &&
103 state[state.length-1] === 0
105 // add normalized whitespace
109 // whitespace token we can skip?
110 if (whitespace_pattern.test(match[0])) {
116 tokens.push(match[0]);
118 // otherwise, string literal or comment
120 // save unmatched text
121 tokens[tokens.length-1] += unmatched;
123 // unescaped terminator to string literal or comment?
124 if (not_escaped_pattern.test(tokens[tokens.length-1])) {
125 // comment terminator?
126 if (state[state.length-1] === 4) {
127 // ok to drop comment?
128 if (tokens.length < 2 ||
129 whitespace_pattern.test(tokens[tokens.length-2])
133 // otherwise, turn comment into whitespace
135 tokens[tokens.length-1] = " ";
145 // append matched text to existing token
146 tokens[tokens.length-1] += match[0];
149 // otherwise, end of processing (no more matches)
151 unmatched = sel.substr(next_match_idx);
158 return tokens.join("").trim();
161 return normalizeSelector;