| /* normalize-selector v0.1.0 (c) 2014 Kyle Simpson */ |
| |
| (function UMD(name,context,definition){ |
| if (typeof module !== "undefined" && module.exports) { module.exports = definition(); } |
| else if (typeof define === "function" && define.amd) { define(definition); } |
| else { context[name] = definition(name,context); } |
| })("normalizeSelector",this,function DEF(name,context){ |
| "use strict"; |
| |
| function normalizeSelector(sel) { |
| |
| // save unmatched text, if any |
| function saveUnmatched() { |
| if (unmatched) { |
| // whitespace needed after combinator? |
| if (tokens.length > 0 && |
| /^[~+>]$/.test(tokens[tokens.length-1]) |
| ) { |
| tokens.push(" "); |
| } |
| |
| // save unmatched text |
| tokens.push(unmatched); |
| } |
| } |
| |
| var tokens = [], match, unmatched, regex, state = [0], |
| next_match_idx = 0, prev_match_idx, |
| not_escaped_pattern = /(?:[^\\]|(?:^|[^\\])(?:\\\\)+)$/, |
| whitespace_pattern = /^\s+$/, |
| attribute_nonspecial_pattern = /[^\s=~!^|$*\[\]\(\)]{2}/, |
| state_patterns = [ |
| /\s+|\/\*|["'>~+\[\(]/g, // general |
| /\s+|\/\*|["'\[\]\(\)]/g, // [..] set |
| /\s+|\/\*|["'\[\]\(\)]/g, // (..) set |
| null, // string literal (placeholder) |
| /\*\//g // comment |
| ] |
| ; |
| |
| sel = sel.trim(); |
| |
| while (true) { |
| unmatched = ""; |
| |
| regex = state_patterns[state[state.length-1]]; |
| |
| regex.lastIndex = next_match_idx; |
| match = regex.exec(sel); |
| |
| // matched text to process? |
| if (match) { |
| prev_match_idx = next_match_idx; |
| next_match_idx = regex.lastIndex; |
| |
| // collect the previous string chunk not matched before this token |
| if (prev_match_idx < next_match_idx - match[0].length) { |
| unmatched = sel.substring(prev_match_idx,next_match_idx - match[0].length); |
| } |
| |
| // need to force a space (possibly skipped |
| // previously by the parser)? |
| if ( |
| state[state.length-1] === 1 && |
| attribute_nonspecial_pattern.test( |
| tokens[tokens.length-1].substr(-1) + |
| unmatched.charAt(0) |
| ) |
| ) { |
| tokens.push(" "); |
| } |
| |
| // general, [ ] pair, ( ) pair? |
| if (state[state.length-1] < 3) { |
| saveUnmatched(); |
| |
| // starting a [ ] pair? |
| if (match[0] === "[") { |
| state.push(1); |
| } |
| // starting a ( ) pair? |
| else if (match[0] === "(") { |
| state.push(2); |
| } |
| // starting a string literal? |
| else if (/^["']$/.test(match[0])) { |
| state.push(3); |
| state_patterns[3] = new RegExp(match[0],"g"); |
| } |
| // starting a comment? |
| else if (match[0] === "/*") { |
| state.push(4); |
| } |
| // ending a [ ] or ( ) pair? |
| else if (/^[\]\)]$/.test(match[0]) && state.length > 0) { |
| state.pop(); |
| } |
| // handling whitespace or a combinator? |
| else if (/^(?:\s+|[~+>])$/.test(match[0])) { |
| // need to insert whitespace before? |
| if (tokens.length > 0 && |
| !whitespace_pattern.test(tokens[tokens.length-1]) && |
| state[state.length-1] === 0 |
| ) { |
| // add normalized whitespace |
| tokens.push(" "); |
| } |
| |
| // whitespace token we can skip? |
| if (whitespace_pattern.test(match[0])) { |
| continue; |
| } |
| } |
| |
| // save matched text |
| tokens.push(match[0]); |
| } |
| // otherwise, string literal or comment |
| else { |
| // save unmatched text |
| tokens[tokens.length-1] += unmatched; |
| |
| // unescaped terminator to string literal or comment? |
| if (not_escaped_pattern.test(tokens[tokens.length-1])) { |
| // comment terminator? |
| if (state[state.length-1] === 4) { |
| // ok to drop comment? |
| if (tokens.length < 2 || |
| whitespace_pattern.test(tokens[tokens.length-2]) |
| ) { |
| tokens.pop(); |
| } |
| // otherwise, turn comment into whitespace |
| else { |
| tokens[tokens.length-1] = " "; |
| } |
| |
| // handled already |
| match[0] = ""; |
| } |
| |
| state.pop(); |
| } |
| |
| // append matched text to existing token |
| tokens[tokens.length-1] += match[0]; |
| } |
| } |
| // otherwise, end of processing (no more matches) |
| else { |
| unmatched = sel.substr(next_match_idx); |
| saveUnmatched(); |
| |
| break; |
| } |
| } |
| |
| return tokens.join("").trim(); |
| } |
| |
| return normalizeSelector; |
| }); |
| |