SOURCE CODE: Uize.Web.xSelector (view docs)

/*______________
|       ______  |   U I Z E    J A V A S C R I P T    F R A M E W O R K
|     /      /  |   ---------------------------------------------------
|    /    O /   |    MODULE : Uize.Web.xSelector Object Extension
|   /    / /    |
|  /    / /  /| |    ONLINE : http://www.uize.com
| /____/ /__/_| | COPYRIGHT : (c)2009-2016 UIZE
|          /___ |   LICENSE : Available under MIT License or GNU General Public License
|_______________|             http://www.uize.com/license.html
*/

/* Module Meta Data
  type: Extension
  importance: 5
  codeCompleteness: 100
  docCompleteness: 0
*/

/*?
  Introduction
    The =Uize.Web.xSelector= module extends the =Uize.Web= object by adding functionality for selecting nodes via CSS selector syntax, not supported natively by the browser.

    *DEVELOPERS:* `Ben Ilegbodu`, CSS selector syntax provided by `Sizzle.js`
*/

Uize.module ({
  name:'Uize.Web.xSelector',
  builder:function (_object) {
    var
      /*** Variables for Scruncher Optimization ***/
        _Uize = Uize,
        _window = typeof window != 'undefined' ? window : null
    ;

    /*** Static Methods ***/
      _object.matches = function(_node, _selector) { return _object._Sizzle.matchesSelector(_node, _selector) };
      _object.selectCss = function(_selector, _rootNode) { return _object._Sizzle(_selector, _rootNode) };

    /*** Sizzle ***/
      /*!
       * Sizzle CSS Selector Engine
       * Copyright 2012 jQuery Foundation and other contributors
       * Released under the MIT license
       * http://sizzlejs.com/
       */
      _window && (function( window, undefined ) {

      var cachedruns,
        assertGetIdNotName,
        Expr,
        getText,
        isXML,
        contains,
        compile,
        sortOrder,
        hasDuplicate,
        outermostContext,

        strundefined = "undefined",

        // Used in sorting
        MAX_NEGATIVE = 1 << 31,
        baseHasDuplicate = true,

        expando = ( "sizcache" + Math.random() ).replace( ".", "" ),

        Token = String,
        document = window.document,
        docElem = document.documentElement,
        dirruns = 0,
        done = 0,
        pop = [].pop,
        push = [].push,
        slice = [].slice,
        // Use a stripped-down indexOf if a native one is unavailable
        indexOf = [].indexOf || function( elem ) {
          var i = 0,
            len = this.length;
          for ( ; i < len; i++ ) {
            if ( this[i] === elem ) {
              return i;
            }
          }
          return -1;
        },

        // Augment a function for special use by Sizzle
        markFunction = function( fn, value ) {
          fn[ expando ] = value == null || value;
          return fn;
        },

        createCache = function() {
          var cache = {},
            keys = [];

          return markFunction(function( key, value ) {
            // Only keep the most recent entries
            if ( keys.push( key ) > Expr.cacheLength ) {
              delete cache[ keys.shift() ];
            }

            // Retrieve with (key + " ") to avoid collision with native Object.prototype properties (see Issue #157)
            return (cache[ key + " " ] = value);
          }, cache );
        },

        classCache = createCache(),
        tokenCache = createCache(),
        compilerCache = createCache(),

        // Regex

        // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
        whitespace = "[\\x20\\t\\r\\n\\f]",
        // http://www.w3.org/TR/css3-syntax/#characters
        characterEncoding = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",

        // Loosely modeled on CSS identifier characters
        // An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors)
        // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
        identifier = characterEncoding.replace( "w", "w#" ),

        // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
        operators = "([*^$|!~]?=)",
        attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
          "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",

        // Prefer arguments not in parens/brackets,
        //   then attribute selectors and non-pseudos (denoted by :),
        //   then anything else
        // These preferences are here to reduce the number of selectors
        //   needing tokenize in the PSEUDO preFilter
        pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:" + attributes + ")|[^:]|\\\\.)*|.*))\\)|)",

        // For matchExpr.POS and matchExpr.needsContext
        pos = ":(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace +
          "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)",

        // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
        rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),

        rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
        rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ),
        rpseudo = new RegExp( pseudos ),

        // Easily-parseable/retrievable ID or TAG or CLASS selectors
        rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,

        rsibling = /[\x20\t\r\n\f]*[+~]/,

        rheader = /h\d/i,
        rinputs = /input|select|textarea|button/i,

        rbackslash = /\\(?!\\)/g,

        matchExpr = {
          "ID": new RegExp( "^#(" + characterEncoding + ")" ),
          "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
          "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ),
          "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
          "ATTR": new RegExp( "^" + attributes ),
          "PSEUDO": new RegExp( "^" + pseudos ),
          "POS": new RegExp( pos, "i" ),
          "CHILD": new RegExp( "^:(only|nth|first|last)-child(?:\\(" + whitespace +
            "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
            "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
          // For use in libraries implementing .is()
          "needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" )
        },

        // Support

        // Used for testing something on an element
        assert = function( fn ) {
          var div = document.createElement("div");

          try {
            return fn( div );
          } catch (e) {
            return false;
          } finally {
            // release memory in IE
            div = null;
          }
        },

        // Check if getElementsByTagName("*") returns only elements
        assertTagNameNoComments = assert(function( div ) {
          div.appendChild( document.createComment("") );
          return !div.getElementsByTagName("*").length;
        }),

        // Check if getAttribute returns normalized href attributes
        assertHrefNotNormalized = assert(function( div ) {
          div.innerHTML = "<a href='#'></a>";
          return div.firstChild && typeof div.firstChild.getAttribute !== strundefined &&
            div.firstChild.getAttribute("href") === "#";
        }),

        // Check if attributes should be retrieved by attribute nodes
        assertAttributes = assert(function( div ) {
          div.innerHTML = "<select></select>";
          var type = typeof div.lastChild.getAttribute("multiple");
          // IE8 returns a string for some attributes even when not present
          return type !== "boolean" && type !== "string";
        }),

        // Check if getElementsByClassName can be trusted
        assertUsableClassName = assert(function( div ) {
          // Opera can't find a second classname (in 9.6)
          div.innerHTML = "<div class='hidden e'></div><div class='hidden'></div>";
          if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) {
            return false;
          }

          // Safari 3.2 caches class attributes and doesn't catch changes
          div.lastChild.className = "e";
          return div.getElementsByClassName("e").length === 2;
        }),

        // Check if getElementById returns elements by name
        // Check if getElementsByName privileges form controls or returns elements by ID
        assertUsableName = assert(function( div ) {
          // Inject content
          div.id = expando + 0;
          div.innerHTML = "<a name='" + expando + "'></a><div name='" + expando + "'></div>";
          docElem.insertBefore( div, docElem.firstChild );

          // Test
          var pass = document.getElementsByName &&
            // buggy browsers will return fewer than the correct 2
            document.getElementsByName( expando ).length === 2 +
            // buggy browsers will return more than the correct 0
            document.getElementsByName( expando + 0 ).length;
          assertGetIdNotName = !document.getElementById( expando );

          // Cleanup
          docElem.removeChild( div );

          return pass;
        });

      // If slice is not available, provide a backup
      try {
        slice.call( docElem.childNodes, 0 )[0].nodeType;
      } catch ( e ) {
        slice = function( i ) {
          var elem,
            results = [];
          for ( ; (elem = this[i]); i++ ) {
            results.push( elem );
          }
          return results;
        };
      }

      _object._Sizzle = Sizzle = function ( selector, context, results, seed ) {
        results = results || [];
        context = context || document;
        var match, elem, xml, m,
          nodeType = context.nodeType;

        if ( !selector || typeof selector !== "string" ) {
          return results;
        }

        if ( nodeType !== 1 && nodeType !== 9 ) {
          return [];
        }

        xml = isXML( context );

        if ( !xml && !seed ) {
          if ( (match = rquickExpr.exec( selector )) ) {
            // Speed-up: Sizzle("#ID")
            if ( (m = match[1]) ) {
              if ( nodeType === 9 ) {
                elem = context.getElementById( m );
                // Check parentNode to catch when Blackberry 4.6 returns
                // nodes that are no longer in the document #6963
                if ( elem && elem.parentNode ) {
                  // Handle the case where IE, Opera, and Webkit return items
                  // by name instead of ID
                  if ( elem.id === m ) {
                    results.push( elem );
                    return results;
                  }
                } else {
                  return results;
                }
              } else {
                // Context is not a document
                if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
                  contains( context, elem ) && elem.id === m ) {
                  results.push( elem );
                  return results;
                }
              }

            // Speed-up: Sizzle("TAG")
            } else if ( match[2] ) {
              push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) );
              return results;

            // Speed-up: Sizzle(".CLASS")
            } else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) {
              push.apply( results, slice.call(context.getElementsByClassName( m ), 0) );
              return results;
            }
          }
        }

        // All others
        return select( selector.replace( rtrim, "$1" ), context, results, seed, xml );
      };

      Sizzle.matches = function( expr, elements ) {
        return Sizzle( expr, null, null, elements );
      };

      Sizzle.matchesSelector = function( elem, expr ) {
        return Sizzle( expr, null, null, [ elem ] ).length > 0;
      };

      // Returns a function to use in pseudos for input types
      function createInputPseudo( type ) {
        return function( elem ) {
          var name = elem.nodeName.toLowerCase();
          return name === "input" && elem.type === type;
        };
      }

      // Returns a function to use in pseudos for buttons
      function createButtonPseudo( type ) {
        return function( elem ) {
          var name = elem.nodeName.toLowerCase();
          return (name === "input" || name === "button") && elem.type === type;
        };
      }

      // Returns a function to use in pseudos for positionals
      function createPositionalPseudo( fn ) {
        return markFunction(function( argument ) {
          argument = +argument;
          return markFunction(function( seed, matches ) {
            var j,
              matchIndexes = fn( [], seed.length, argument ),
              i = matchIndexes.length;

            // Match elements found at the specified indexes
            while ( i-- ) {
              if ( seed[ (j = matchIndexes[i]) ] ) {
                seed[j] = !(matches[j] = seed[j]);
              }
            }
          });
        });
      }

      /**
       * Utility function for retrieving the text value of an array of DOM nodes
       * @param {Array|Element} elem
       */
      getText = Sizzle.getText = function( elem ) {
        var node,
          ret = "",
          i = 0,
          nodeType = elem.nodeType;

        if ( nodeType ) {
          if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
            // Use textContent for elements
            // innerText usage removed for consistency of new lines (see #11153)
            if ( typeof elem.textContent === "string" ) {
              return elem.textContent;
            } else {
              // Traverse its children
              for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
                ret += getText( elem );
              }
            }
          } else if ( nodeType === 3 || nodeType === 4 ) {
            return elem.nodeValue;
          }
          // Do not include comment or processing instruction nodes
        } else {

          // If no nodeType, this is expected to be an array
          for ( ; (node = elem[i]); i++ ) {
            // Do not traverse comment nodes
            ret += getText( node );
          }
        }
        return ret;
      };

      isXML = Sizzle.isXML = function( elem ) {
        // documentElement is verified for cases where it doesn't yet exist
        // (such as loading iframes in IE - #4833)
        var documentElement = elem && (elem.ownerDocument || elem).documentElement;
        return documentElement ? documentElement.nodeName !== "HTML" : false;
      };

      // Element contains another
      contains = Sizzle.contains = docElem.contains ?
        function( a, b ) {
          var adown = a.nodeType === 9 ? a.documentElement : a,
            bup = b && b.parentNode;
          return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) );
        } :
        docElem.compareDocumentPosition ?
        function( a, b ) {
          return b && !!( a.compareDocumentPosition( b ) & 16 );
        } :
        function( a, b ) {
          while ( (b = b.parentNode) ) {
            if ( b === a ) {
              return true;
            }
          }
          return false;
        };

      Sizzle.attr = function( elem, name ) {
        var val,
          xml = isXML( elem );

        if ( !xml ) {
          name = name.toLowerCase();
        }
        if ( (val = Expr.attrHandle[ name ]) ) {
          return val( elem );
        }
        if ( xml || assertAttributes ) {
          return elem.getAttribute( name );
        }
        val = elem.getAttributeNode( name );
        return val ?
          typeof elem[ name ] === "boolean" ?
            elem[ name ] ? name : null :
            val.specified ? val.value : null :
          null;
      };

      Expr = Sizzle.selectors = {

        // Can be adjusted by the user
        cacheLength: 50,

        createPseudo: markFunction,

        match: matchExpr,

        // IE6/7 return a modified href
        attrHandle: assertHrefNotNormalized ?
          {} :
          {
            "href": function( elem ) {
              return elem.getAttribute( "href", 2 );
            },
            "type": function( elem ) {
              return elem.getAttribute("type");
            }
          },

        find: {
          "ID": assertGetIdNotName ?
            function( id, context, xml ) {
              if ( typeof context.getElementById !== strundefined && !xml ) {
                var m = context.getElementById( id );
                // Check parentNode to catch when Blackberry 4.6 returns
                // nodes that are no longer in the document #6963
                return m && m.parentNode ? [m] : [];
              }
            } :
            function( id, context, xml ) {
              if ( typeof context.getElementById !== strundefined && !xml ) {
                var m = context.getElementById( id );

                return m ?
                  m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ?
                    [m] :
                    undefined :
                  [];
              }
            },

          "TAG": assertTagNameNoComments ?
            function( tag, context ) {
              if ( typeof context.getElementsByTagName !== strundefined ) {
                return context.getElementsByTagName( tag );
              }
            } :
            function( tag, context ) {
              var results = context.getElementsByTagName( tag );

              // Filter out possible comments
              if ( tag === "*" ) {
                var elem,
                  tmp = [],
                  i = 0;

                for ( ; (elem = results[i]); i++ ) {
                  if ( elem.nodeType === 1 ) {
                    tmp.push( elem );
                  }
                }

                return tmp;
              }
              return results;
            },

          "NAME": assertUsableName && function( tag, context ) {
            if ( typeof context.getElementsByName !== strundefined ) {
              return context.getElementsByName( name );
            }
          },

          "CLASS": assertUsableClassName && function( className, context, xml ) {
            if ( typeof context.getElementsByClassName !== strundefined && !xml ) {
              return context.getElementsByClassName( className );
            }
          }
        },

        relative: {
          ">": { dir: "parentNode", first: true },
          " ": { dir: "parentNode" },
          "+": { dir: "previousSibling", first: true },
          "~": { dir: "previousSibling" }
        },

        preFilter: {
          "ATTR": function( match ) {
            match[1] = match[1].replace( rbackslash, "" );

            // Move the given value to match[3] whether quoted or unquoted
            match[3] = ( match[4] || match[5] || "" ).replace( rbackslash, "" );

            if ( match[2] === "~=" ) {
              match[3] = " " + match[3] + " ";
            }

            return match.slice( 0, 4 );
          },

          "CHILD": function( match ) {
            /* matches from matchExpr["CHILD"]
              1 type (only|nth|...)
              2 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
              3 xn-component of xn+y argument ([+-]?\d*n|)
              4 sign of xn-component
              5 x of xn-component
              6 sign of y-component
              7 y of y-component
            */
            match[1] = match[1].toLowerCase();

            if ( match[1] === "nth" ) {
              // nth-child requires argument
              if ( !match[2] ) {
                Sizzle.error( match[0] );
              }

              // numeric x and y parameters for Expr.filter.CHILD
              // remember that false/true cast respectively to 0/1
              match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === "even" || match[2] === "odd" ) );
              match[4] = +( ( match[6] + match[7] ) || match[2] === "odd" );

            // other types prohibit arguments
            } else if ( match[2] ) {
              Sizzle.error( match[0] );
            }

            return match;
          },

          "PSEUDO": function( match ) {
            var unquoted, excess;
            if ( matchExpr["CHILD"].test( match[0] ) ) {
              return null;
            }

            if ( match[3] ) {
              match[2] = match[3];
            } else if ( (unquoted = match[4]) ) {
              // Only check arguments that contain a pseudo
              if ( rpseudo.test(unquoted) &&
                // Get excess from tokenize (recursively)
                (excess = tokenize( unquoted, true )) &&
                // advance to the next closing parenthesis
                (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {

                // excess is a negative index
                unquoted = unquoted.slice( 0, excess );
                match[0] = match[0].slice( 0, excess );
              }
              match[2] = unquoted;
            }

            // Return only captures needed by the pseudo filter method (type and argument)
            return match.slice( 0, 3 );
          }
        },

        filter: {
          "ID": assertGetIdNotName ?
            function( id ) {
              id = id.replace( rbackslash, "" );
              return function( elem ) {
                return elem.getAttribute("id") === id;
              };
            } :
            function( id ) {
              id = id.replace( rbackslash, "" );
              return function( elem ) {
                var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
                return node && node.value === id;
              };
            },

          "TAG": function( nodeName ) {
            if ( nodeName === "*" ) {
              return function() { return true; };
            }
            nodeName = nodeName.replace( rbackslash, "" ).toLowerCase();

            return function( elem ) {
              return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
            };
          },

          "CLASS": function( className ) {
            var pattern = classCache[ expando ][ className + " " ];

            return pattern ||
              (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
              classCache( className, function( elem ) {
                return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" );
              });
          },

          "ATTR": function( name, operator, check ) {
            return function( elem ) {
              var result = Sizzle.attr( elem, name );

              if ( result == null ) {
                return operator === "!=";
              }
              if ( !operator ) {
                return true;
              }

              result += "";

              return operator === "=" ? result === check :
                operator === "!=" ? result !== check :
                operator === "^=" ? check && result.indexOf( check ) === 0 :
                operator === "*=" ? check && result.indexOf( check ) > -1 :
                operator === "$=" ? check && result.substr( result.length - check.length ) === check :
                operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
                operator === "|=" ? result === check || result.substr( 0, check.length + 1 ) === check + "-" :
                false;
            };
          },

          "CHILD": function( type, argument, first, last ) {

            if ( type === "nth" ) {
              return function( elem ) {
                var node, diff,
                  parent = elem.parentNode;

                if ( first === 1 && last === 0 ) {
                  return true;
                }

                if ( parent ) {
                  diff = 0;
                  for ( node = parent.firstChild; node; node = node.nextSibling ) {
                    if ( node.nodeType === 1 ) {
                      diff++;
                      if ( elem === node ) {
                        break;
                      }
                    }
                  }
                }

                // Incorporate the offset (or cast to NaN), then check against cycle size
                diff -= last;
                return diff === first || ( diff % first === 0 && diff / first >= 0 );
              };
            }

            return function( elem ) {
              var node = elem;

              switch ( type ) {
                case "only":
                case "first":
                  while ( (node = node.previousSibling) ) {
                    if ( node.nodeType === 1 ) {
                      return false;
                    }
                  }

                  if ( type === "first" ) {
                    return true;
                  }

                  node = elem;

                  /* falls through */
                case "last":
                  while ( (node = node.nextSibling) ) {
                    if ( node.nodeType === 1 ) {
                      return false;
                    }
                  }

                  return true;
              }
            };
          },

          "PSEUDO": function( pseudo, argument ) {
            // pseudo-class names are case-insensitive
            // http://www.w3.org/TR/selectors/#pseudo-classes
            // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
            // Remember that setFilters inherits from pseudos
            var args,
              fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
                Sizzle.error( "unsupported pseudo: " + pseudo );

            // The user may use createPseudo to indicate that
            // arguments are needed to create the filter function
            // just as Sizzle does
            if ( fn[ expando ] ) {
              return fn( argument );
            }

            // But maintain support for old signatures
            if ( fn.length > 1 ) {
              args = [ pseudo, pseudo, "", argument ];
              return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
                markFunction(function( seed, matches ) {
                  var idx,
                    matched = fn( seed, argument ),
                    i = matched.length;
                  while ( i-- ) {
                    idx = indexOf.call( seed, matched[i] );
                    seed[ idx ] = !( matches[ idx ] = matched[i] );
                  }
                }) :
                function( elem ) {
                  return fn( elem, 0, args );
                };
            }

            return fn;
          }
        },

        pseudos: {
          "not": markFunction(function( selector ) {
            // Trim the selector passed to compile
            // to avoid treating leading and trailing
            // spaces as combinators
            var input = [],
              results = [],
              matcher = compile( selector.replace( rtrim, "$1" ) );

            return matcher[ expando ] ?
              markFunction(function( seed, matches, context, xml ) {
                var elem,
                  unmatched = matcher( seed, null, xml, [] ),
                  i = seed.length;

                // Match elements unmatched by `matcher`
                while ( i-- ) {
                  if ( (elem = unmatched[i]) ) {
                    seed[i] = !(matches[i] = elem);
                  }
                }
              }) :
              function( elem, context, xml ) {
                input[0] = elem;
                matcher( input, null, xml, results );
                return !results.pop();
              };
          }),

          "has": markFunction(function( selector ) {
            return function( elem ) {
              return Sizzle( selector, elem ).length > 0;
            };
          }),

          "contains": markFunction(function( text ) {
            return function( elem ) {
              return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
            };
          }),

          "enabled": function( elem ) {
            return elem.disabled === false;
          },

          "disabled": function( elem ) {
            return elem.disabled === true;
          },

          "checked": function( elem ) {
            // In CSS3, :checked should return both checked and selected elements
            // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
            var nodeName = elem.nodeName.toLowerCase();
            return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
          },

          "selected": function( elem ) {
            // Accessing this property makes selected-by-default
            // options in Safari work properly
            if ( elem.parentNode ) {
              elem.parentNode.selectedIndex;
            }

            return elem.selected === true;
          },

          "parent": function( elem ) {
            return !Expr.pseudos["empty"]( elem );
          },

          "empty": function( elem ) {
            // http://www.w3.org/TR/selectors/#empty-pseudo
            // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
            //   not comment, processing instructions, or others
            // Thanks to Diego Perini for the nodeName shortcut
            //   Greater than "@" means alpha characters (specifically not starting with "#" or "?")
            var nodeType;
            elem = elem.firstChild;
            while ( elem ) {
              if ( elem.nodeName > "@" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) {
                return false;
              }
              elem = elem.nextSibling;
            }
            return true;
          },

          "header": function( elem ) {
            return rheader.test( elem.nodeName );
          },

          "text": function( elem ) {
            var type, attr;
            // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
            // use getAttribute instead to test this case
            return elem.nodeName.toLowerCase() === "input" &&
              (type = elem.type) === "text" &&
              ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === type );
          },

          // Input types
          "radio": createInputPseudo("radio"),
          "checkbox": createInputPseudo("checkbox"),
          "file": createInputPseudo("file"),
          "password": createInputPseudo("password"),
          "image": createInputPseudo("image"),

          "submit": createButtonPseudo("submit"),
          "reset": createButtonPseudo("reset"),

          "button": function( elem ) {
            var name = elem.nodeName.toLowerCase();
            return name === "input" && elem.type === "button" || name === "button";
          },

          "input": function( elem ) {
            return rinputs.test( elem.nodeName );
          },

          "focus": function( elem ) {
            var doc = elem.ownerDocument;
            return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
          },

          "active": function( elem ) {
            return elem === elem.ownerDocument.activeElement;
          },

          // Positional types
          "first": createPositionalPseudo(function() {
            return [ 0 ];
          }),

          "last": createPositionalPseudo(function( matchIndexes, length ) {
            return [ length - 1 ];
          }),

          "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
            return [ argument < 0 ? argument + length : argument ];
          }),

          "even": createPositionalPseudo(function( matchIndexes, length ) {
            for ( var i = 0; i < length; i += 2 ) {
              matchIndexes.push( i );
            }
            return matchIndexes;
          }),

          "odd": createPositionalPseudo(function( matchIndexes, length ) {
            for ( var i = 1; i < length; i += 2 ) {
              matchIndexes.push( i );
            }
            return matchIndexes;
          }),

          "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
            for ( var i = argument < 0 ? argument + length : argument; --i >= 0; ) {
              matchIndexes.push( i );
            }
            return matchIndexes;
          }),

          "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
            for ( var i = argument < 0 ? argument + length : argument; ++i < length; ) {
              matchIndexes.push( i );
            }
            return matchIndexes;
          })
        }
      };

      function siblingCheck( a, b ) {

        if ( a && b ) {
          var cur = a.nextSibling;

          while ( cur ) {
            if ( cur === b ) {
              return -1;
            }

            cur = cur.nextSibling;
          }
        }

        return a ? 1 : -1;
      }

      sortOrder = docElem.compareDocumentPosition ?
        function( a, b ) {
          var compare, parent;
          if ( a === b ) {
            hasDuplicate = true;
            return 0;
          }

          if ( a.compareDocumentPosition && b.compareDocumentPosition ) {
            if ( (compare = a.compareDocumentPosition( b )) & 1 || (( parent = a.parentNode ) && parent.nodeType === 11) ) {
              if ( a === document || contains(document, a) ) {
                return -1;
              }
              if ( b === document || contains(document, b) ) {
                return 1;
              }
              return 0;
            }
            return compare & 4 ? -1 : 1;
          }

          return a.compareDocumentPosition ? -1 : 1;
        } :
        function( a, b ) {
          // The nodes are identical, we can exit early
          if ( a === b ) {
            hasDuplicate = true;
            return 0;

          // Fallback to using sourceIndex (in IE) if it's available on both nodes
          } else if ( a.sourceIndex && b.sourceIndex ) {
            return ( ~b.sourceIndex || ( MAX_NEGATIVE ) ) - ( contains( document, a ) && ~a.sourceIndex || ( MAX_NEGATIVE ) );
          }

          var i = 0,
            ap = [ a ],
            bp = [ b ],
            aup = a.parentNode,
            bup = b.parentNode,
            cur = aup;

          // If no parents were found then the nodes are disconnected
          if ( a === document ) {
            return -1;

          } else if ( b === document ) {
            return 1;

          } else if ( !aup && !bup ) {
            return 0;

          } else if ( !bup ) {
            return -1;

          } else if ( !aup ) {
            return 1;

          // If the nodes are siblings (or identical) we can do a quick check
          } else if ( aup === bup ) {
            return siblingCheck( a, b );
          }

          // Otherwise they're somewhere else in the tree so we need
          // to build up a full list of the parentNodes for comparison
          while ( cur ) {
            ap.unshift( cur );
            cur = cur.parentNode;
          }

          cur = bup;

          while ( cur ) {
            bp.unshift( cur );
            cur = cur.parentNode;
          }

          // Walk down the tree looking for a discrepancy
          while ( ap[i] === bp[i] ) {
            i++;
          }

          // Prefer our document
          if ( i === 0 ) {
            if ( ap[0] === document || contains(document, ap[0]) ) {
              return -1;
            }
            if ( bp[0] === document || contains(document, bp[0]) ) {
              return 1;
            }
            return 0;
          }

          // We ended someplace up the tree so do a sibling check
          return siblingCheck( ap[i], bp[i] );
        };

      // Always assume the presence of duplicates if sort doesn't
      // pass them to our comparison function (as in Google Chrome).
      [0, 0].sort( sortOrder );
      baseHasDuplicate = !hasDuplicate;

      // Document sorting and removing duplicates
      Sizzle.uniqueSort = function( results ) {
        var elem,
          duplicates = [],
          i = 1,
          j = 0;

        hasDuplicate = baseHasDuplicate;
        results.sort( sortOrder );

        if ( hasDuplicate ) {
          for ( ; (elem = results[i]); i++ ) {
            if ( elem === results[ i - 1 ] ) {
              j = duplicates.push( i );
            }
          }
          while ( j-- ) {
            results.splice( duplicates[ j ], 1 );
          }
        }

        return results;
      };

      Sizzle.error = function( msg ) {
        throw new Error( "Syntax error, unrecognized expression: " + msg );
      };

      function tokenize( selector, parseOnly ) {
        var matched, match, tokens, type,
          soFar, groups, preFilters,
          cached = tokenCache[ expando ][ selector + " " ];

        if ( cached ) {
          return parseOnly ? 0 : cached.slice( 0 );
        }

        soFar = selector;
        groups = [];
        preFilters = Expr.preFilter;

        while ( soFar ) {

          // Comma and first run
          if ( !matched || (match = rcomma.exec( soFar )) ) {
            if ( match ) {
              // Don't consume trailing commas as valid
              soFar = soFar.slice( match[0].length ) || soFar;
            }
            groups.push( tokens = [] );
          }

          matched = false;

          // Combinators
          if ( (match = rcombinators.exec( soFar )) ) {
            tokens.push( matched = new Token( match.shift() ) );
            soFar = soFar.slice( matched.length );

            // Cast descendant combinators to space
            matched.type = match[0].replace( rtrim, " " );
          }

          // Filters
          for ( type in Expr.filter ) {
            if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
              (match = preFilters[ type ]( match ))) ) {

              tokens.push( matched = new Token( match.shift() ) );
              soFar = soFar.slice( matched.length );
              matched.type = type;
              matched.matches = match;
            }
          }

          if ( !matched ) {
            break;
          }
        }

        // Return the length of the invalid excess
        // if we're just parsing
        // Otherwise, throw an error or return tokens
        return parseOnly ?
          soFar.length :
          soFar ?
            Sizzle.error( selector ) :
            // Cache the tokens
            tokenCache( selector, groups ).slice( 0 );
      }

      function addCombinator( matcher, combinator, base ) {
        var dir = combinator.dir,
          checkNonElements = base && combinator.dir === "parentNode",
          doneName = done++;

        return combinator.first ?
          // Check against closest ancestor/preceding element
          function( elem, context, xml ) {
            while ( (elem = elem[ dir ]) ) {
              if ( checkNonElements || elem.nodeType === 1  ) {
                return matcher( elem, context, xml );
              }
            }
          } :

          // Check against all ancestor/preceding elements
          function( elem, context, xml ) {
            // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
            if ( !xml ) {
              var cache,
                dirkey = dirruns + " " + doneName + " ",
                cachedkey = dirkey + cachedruns;
              while ( (elem = elem[ dir ]) ) {
                if ( checkNonElements || elem.nodeType === 1 ) {
                  if ( (cache = elem[ expando ]) === cachedkey ) {
                    return elem.sizset;
                  } else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) {
                    if ( elem.sizset ) {
                      return elem;
                    }
                  } else {
                    elem[ expando ] = cachedkey;
                    if ( matcher( elem, context, xml ) ) {
                      elem.sizset = true;
                      return elem;
                    }
                    elem.sizset = false;
                  }
                }
              }
            } else {
              while ( (elem = elem[ dir ]) ) {
                if ( checkNonElements || elem.nodeType === 1 ) {
                  if ( matcher( elem, context, xml ) ) {
                    return elem;
                  }
                }
              }
            }
          };
      }

      function elementMatcher( matchers ) {
        return matchers.length > 1 ?
          function( elem, context, xml ) {
            var i = matchers.length;
            while ( i-- ) {
              if ( !matchers[i]( elem, context, xml ) ) {
                return false;
              }
            }
            return true;
          } :
          matchers[0];
      }

      function condense( unmatched, map, filter, context, xml ) {
        var elem,
          newUnmatched = [],
          i = 0,
          len = unmatched.length,
          mapped = map != null;

        for ( ; i < len; i++ ) {
          if ( (elem = unmatched[i]) ) {
            if ( !filter || filter( elem, context, xml ) ) {
              newUnmatched.push( elem );
              if ( mapped ) {
                map.push( i );
              }
            }
          }
        }

        return newUnmatched;
      }

      function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
        if ( postFilter && !postFilter[ expando ] ) {
          postFilter = setMatcher( postFilter );
        }
        if ( postFinder && !postFinder[ expando ] ) {
          postFinder = setMatcher( postFinder, postSelector );
        }
        return markFunction(function( seed, results, context, xml ) {
          var temp, i, elem,
            preMap = [],
            postMap = [],
            preexisting = results.length,

            // Get initial elements from seed or context
            elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),

            // Prefilter to get matcher input, preserving a map for seed-results synchronization
            matcherIn = preFilter && ( seed || !selector ) ?
              condense( elems, preMap, preFilter, context, xml ) :
              elems,

            matcherOut = matcher ?
              // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
              postFinder || ( seed ? preFilter : preexisting || postFilter ) ?

                // ...intermediate processing is necessary
                [] :

                // ...otherwise use results directly
                results :
              matcherIn;

          // Find primary matches
          if ( matcher ) {
            matcher( matcherIn, matcherOut, context, xml );
          }

          // Apply postFilter
          if ( postFilter ) {
            temp = condense( matcherOut, postMap );
            postFilter( temp, [], context, xml );

            // Un-match failing elements by moving them back to matcherIn
            i = temp.length;
            while ( i-- ) {
              if ( (elem = temp[i]) ) {
                matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
              }
            }
          }

          if ( seed ) {
            if ( postFinder || preFilter ) {
              if ( postFinder ) {
                // Get the final matcherOut by condensing this intermediate into postFinder contexts
                temp = [];
                i = matcherOut.length;
                while ( i-- ) {
                  if ( (elem = matcherOut[i]) ) {
                    // Restore matcherIn since elem is not yet a final match
                    temp.push( (matcherIn[i] = elem) );
                  }
                }
                postFinder( null, (matcherOut = []), temp, xml );
              }

              // Move matched elements from seed to results to keep them synchronized
              i = matcherOut.length;
              while ( i-- ) {
                if ( (elem = matcherOut[i]) &&
                  (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {

                  seed[temp] = !(results[temp] = elem);
                }
              }
            }

          // Add elements to results, through postFinder if defined
          } else {
            matcherOut = condense(
              matcherOut === results ?
                matcherOut.splice( preexisting, matcherOut.length ) :
                matcherOut
            );
            if ( postFinder ) {
              postFinder( null, results, matcherOut, xml );
            } else {
              push.apply( results, matcherOut );
            }
          }
        });
      }

      function matcherFromTokens( tokens ) {
        var checkContext, matcher, j,
          len = tokens.length,
          leadingRelative = Expr.relative[ tokens[0].type ],
          implicitRelative = leadingRelative || Expr.relative[" "],
          i = leadingRelative ? 1 : 0,

          // The foundational matcher ensures that elements are reachable from top-level context(s)
          matchContext = addCombinator( function( elem ) {
            return elem === checkContext;
          }, implicitRelative, true ),
          matchAnyContext = addCombinator( function( elem ) {
            return indexOf.call( checkContext, elem ) > -1;
          }, implicitRelative, true ),
          matchers = [ function( elem, context, xml ) {
            return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
              (checkContext = context).nodeType ?
                matchContext( elem, context, xml ) :
                matchAnyContext( elem, context, xml ) );
          } ];

        for ( ; i < len; i++ ) {
          if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
            matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ];
          } else {
            matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );

            // Return special upon seeing a positional matcher
            if ( matcher[ expando ] ) {
              // Find the next relative operator (if any) for proper handling
              j = ++i;
              for ( ; j < len; j++ ) {
                if ( Expr.relative[ tokens[j].type ] ) {
                  break;
                }
              }
              return setMatcher(
                i > 1 && elementMatcher( matchers ),
                i > 1 && tokens.slice( 0, i - 1 ).join("").replace( rtrim, "$1" ),
                matcher,
                i < j && matcherFromTokens( tokens.slice( i, j ) ),
                j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
                j < len && tokens.join("")
              );
            }
            matchers.push( matcher );
          }
        }

        return elementMatcher( matchers );
      }

      function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
        var bySet = setMatchers.length > 0,
          byElement = elementMatchers.length > 0,
          superMatcher = function( seed, context, xml, results, expandContext ) {
            var elem, j, matcher,
              setMatched = [],
              matchedCount = 0,
              i = "0",
              unmatched = seed && [],
              outermost = expandContext != null,
              contextBackup = outermostContext,
              // We must always have either seed elements or context
              elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
              // Nested matchers should use non-integer dirruns
              dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.E);

            if ( outermost ) {
              outermostContext = context !== document && context;
              cachedruns = superMatcher.el;
            }

            // Add elements passing elementMatchers directly to results
            for ( ; (elem = elems[i]) != null; i++ ) {
              if ( byElement && elem ) {
                for ( j = 0; (matcher = elementMatchers[j]); j++ ) {
                  if ( matcher( elem, context, xml ) ) {
                    results.push( elem );
                    break;
                  }
                }
                if ( outermost ) {
                  dirruns = dirrunsUnique;
                  cachedruns = ++superMatcher.el;
                }
              }

              // Track unmatched elements for set filters
              if ( bySet ) {
                // They will have gone through all possible matchers
                if ( (elem = !matcher && elem) ) {
                  matchedCount--;
                }

                // Lengthen the array for every element, matched or not
                if ( seed ) {
                  unmatched.push( elem );
                }
              }
            }

            // Apply set filters to unmatched elements
            matchedCount += i;
            if ( bySet && i !== matchedCount ) {
              for ( j = 0; (matcher = setMatchers[j]); j++ ) {
                matcher( unmatched, setMatched, context, xml );
              }

              if ( seed ) {
                // Reintegrate element matches to eliminate the need for sorting
                if ( matchedCount > 0 ) {
                  while ( i-- ) {
                    if ( !(unmatched[i] || setMatched[i]) ) {
                      setMatched[i] = pop.call( results );
                    }
                  }
                }

                // Discard index placeholder values to get only actual matches
                setMatched = condense( setMatched );
              }

              // Add matches to results
              push.apply( results, setMatched );

              // Seedless set matches succeeding multiple successful matchers stipulate sorting
              if ( outermost && !seed && setMatched.length > 0 &&
                ( matchedCount + setMatchers.length ) > 1 ) {

                Sizzle.uniqueSort( results );
              }
            }

            // Override manipulation of globals by nested matchers
            if ( outermost ) {
              dirruns = dirrunsUnique;
              outermostContext = contextBackup;
            }

            return unmatched;
          };

        superMatcher.el = 0;
        return bySet ?
          markFunction( superMatcher ) :
          superMatcher;
      }

      compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
        var i,
          setMatchers = [],
          elementMatchers = [],
          cached = compilerCache[ expando ][ selector + " " ];

        if ( !cached ) {
          // Generate a function of recursive functions that can be used to check each element
          if ( !group ) {
            group = tokenize( selector );
          }
          i = group.length;
          while ( i-- ) {
            cached = matcherFromTokens( group[i] );
            if ( cached[ expando ] ) {
              setMatchers.push( cached );
            } else {
              elementMatchers.push( cached );
            }
          }

          // Cache the compiled function
          cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
        }
        return cached;
      };

      function multipleContexts( selector, contexts, results ) {
        var i = 0,
          len = contexts.length;
        for ( ; i < len; i++ ) {
          Sizzle( selector, contexts[i], results );
        }
        return results;
      }

      function select( selector, context, results, seed, xml ) {
        var i, tokens, token, type, find,
          match = tokenize( selector );

        if ( !seed ) {
          // Try to minimize operations if there is only one group
          if ( match.length === 1 ) {

            // Take a shortcut and set the context if the root selector is an ID
            tokens = match[0] = match[0].slice( 0 );
            if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
                context.nodeType === 9 && !xml &&
                Expr.relative[ tokens[1].type ] ) {

              context = Expr.find["ID"]( token.matches[0].replace( rbackslash, "" ), context, xml )[0];
              if ( !context ) {
                return results;
              }

              selector = selector.slice( tokens.shift().length );
            }

            // Fetch a seed set for right-to-left matching
            for ( i = matchExpr["POS"].test( selector ) ? -1 : tokens.length - 1; i >= 0; i-- ) {
              token = tokens[i];

              // Abort if we hit a combinator
              if ( Expr.relative[ (type = token.type) ] ) {
                break;
              }
              if ( (find = Expr.find[ type ]) ) {
                // Search, expanding context for leading sibling combinators
                if ( (seed = find(
                  token.matches[0].replace( rbackslash, "" ),
                  rsibling.test( tokens[0].type ) && context.parentNode || context,
                  xml
                )) ) {

                  // If seed is empty or no tokens remain, we can return early
                  tokens.splice( i, 1 );
                  selector = seed.length && tokens.join("");
                  if ( !selector ) {
                    push.apply( results, slice.call( seed, 0 ) );
                    return results;
                  }

                  break;
                }
              }
            }
          }
        }

        // Compile and execute a filtering function
        // Provide `match` to avoid retokenization if we modified the selector above
        compile( selector, match )(
          seed,
          context,
          xml,
          results,
          rsibling.test( selector )
        );
        return results;
      }

      if ( document.querySelectorAll ) {
        (function() {
          var disconnectedMatch,
            oldSelect = select,
            rescape = /'|\\/g,
            rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,

            // qSa(:focus) reports false when true (Chrome 21), no need to also add to buggyMatches since matches checks buggyQSA
            // A support test would require too much code (would include document ready)
            rbuggyQSA = [ ":focus" ],

            // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
            // A support test would require too much code (would include document ready)
            // just skip matchesSelector for :active
            rbuggyMatches = [ ":active" ],
            matches = docElem.matchesSelector ||
              docElem.mozMatchesSelector ||
              docElem.webkitMatchesSelector ||
              docElem.oMatchesSelector ||
              docElem.msMatchesSelector;

          // Build QSA regex
          // Regex strategy adopted from Diego Perini
          assert(function( div ) {
            // Select is set to empty string on purpose
            // This is to test IE's treatment of not explictly
            // setting a boolean content attribute,
            // since its presence should be enough
            // http://bugs.jquery.com/ticket/12359
            div.innerHTML = "<select><option selected=''></option></select>";

            // IE8 - Some boolean attributes are not treated correctly
            if ( !div.querySelectorAll("[selected]").length ) {
              rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" );
            }

            // Webkit/Opera - :checked should return selected option elements
            // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
            // IE8 throws error here (do not put tests after this one)
            if ( !div.querySelectorAll(":checked").length ) {
              rbuggyQSA.push(":checked");
            }
          });

          assert(function( div ) {

            // Opera 10-12/IE9 - ^= $= *= and empty values
            // Should not select anything
            div.innerHTML = "<p test=''></p>";
            if ( div.querySelectorAll("[test^='']").length ) {
              rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" );
            }

            // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
            // IE8 throws error here (do not put tests after this one)
            div.innerHTML = "<input type='hidden'/>";
            if ( !div.querySelectorAll(":enabled").length ) {
              rbuggyQSA.push(":enabled", ":disabled");
            }
          });

          // rbuggyQSA always contains :focus, so no need for a length check
          rbuggyQSA = /* rbuggyQSA.length && */ new RegExp( rbuggyQSA.join("|") );

          select = function( selector, context, results, seed, xml ) {
            // Only use querySelectorAll when not filtering,
            // when this is not xml,
            // and when no QSA bugs apply
            if ( !seed && !xml && !rbuggyQSA.test( selector ) ) {
              var groups, i,
                old = true,
                nid = expando,
                newContext = context,
                newSelector = context.nodeType === 9 && selector;

              // qSA works strangely on Element-rooted queries
              // We can work around this by specifying an extra ID on the root
              // and working up from there (Thanks to Andrew Dupont for the technique)
              // IE 8 doesn't work on object elements
              if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
                groups = tokenize( selector );

                if ( (old = context.getAttribute("id")) ) {
                  nid = old.replace( rescape, "\\$&" );
                } else {
                  context.setAttribute( "id", nid );
                }
                nid = "[id='" + nid + "'] ";

                i = groups.length;
                while ( i-- ) {
                  groups[i] = nid + groups[i].join("");
                }
                newContext = rsibling.test( selector ) && context.parentNode || context;
                newSelector = groups.join(",");
              }

              if ( newSelector ) {
                try {
                  push.apply( results, slice.call( newContext.querySelectorAll(
                    newSelector
                  ), 0 ) );
                  return results;
                } catch(qsaError) {
                } finally {
                  if ( !old ) {
                    context.removeAttribute("id");
                  }
                }
              }
            }

            return oldSelect( selector, context, results, seed, xml );
          };

          if ( matches ) {
            assert(function( div ) {
              // Check to see if it's possible to do matchesSelector
              // on a disconnected node (IE 9)
              disconnectedMatch = matches.call( div, "div" );

              // This should fail with an exception
              // Gecko does not error, returns false instead
              try {
                matches.call( div, "[test!='']:sizzle" );
                rbuggyMatches.push( "!=", pseudos );
              } catch ( e ) {}
            });

            // rbuggyMatches always contains :active and :focus, so no need for a length check
            rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") );

            Sizzle.matchesSelector = function( elem, expr ) {
              // Make sure that attribute selectors are quoted
              expr = expr.replace( rattributeQuotes, "='$1']" );

              // rbuggyMatches always contains :active, so no need for an existence check
              if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && !rbuggyQSA.test( expr ) ) {
                try {
                  var ret = matches.call( elem, expr );

                  // IE 9's matchesSelector returns false on disconnected nodes
                  if ( ret || disconnectedMatch ||
                      // As well, disconnected nodes are said to be in a document
                      // fragment in IE 9
                      elem.document && elem.document.nodeType !== 11 ) {
                    return ret;
                  }
                } catch(e) {}
              }

              return Sizzle( expr, null, null, [ elem ] ).length > 0;
            };
          }
        })();
      }

      // Deprecated
      Expr.pseudos["nth"] = Expr.pseudos["eq"];

      // Back-compat
      function setFilters() {}
      Expr.filters = setFilters.prototype = Expr.pseudos;
      Expr.setFilters = new setFilters();

      })( window );
  }
});