/*!
  * =============================================================
  * Ender: open module JavaScript framework (https://ender.no.de)
  * Build: ender build ../../
  * =============================================================
  */

/*!
  * Ender: open module JavaScript framework (client-lib)
  * copyright Dustin Diaz & Jacob Thornton 2011 (@ded @fat)
  * http://ender.no.de
  * License MIT
  */
!function (context) {

  // a global object for node.js module compatiblity
  // ============================================

  context['global'] = context

  // Implements simple module system
  // losely based on CommonJS Modules spec v1.1.1
  // ============================================

  var modules = {}
    , old = context.$

  function require (identifier) {
    // modules can be required from ender's build system, or found on the window
    var module = modules[identifier] || window[identifier]
    if (!module) throw new Error("Requested module '" + identifier + "' has not been defined.")
    return module
  }

  function provide (name, what) {
    return (modules[name] = what)
  }

  context['provide'] = provide
  context['require'] = require

  function aug(o, o2) {
    for (var k in o2) k != 'noConflict' && k != '_VERSION' && (o[k] = o2[k])
    return o
  }

  function boosh(s, r, els) {
    // string || node || nodelist || window
    if (typeof s == 'string' || s.nodeName || (s.length && 'item' in s) || s == window) {
      els = ender._select(s, r)
      els.selector = s
    } else els = isFinite(s.length) ? s : [s]
    return aug(els, boosh)
  }

  function ender(s, r) {
    return boosh(s, r)
  }

  aug(ender, {
      _VERSION: '0.3.6'
    , fn: boosh // for easy compat to jQuery plugins
    , ender: function (o, chain) {
        aug(chain ? boosh : ender, o)
      }
    , _select: function (s, r) {
        return (r || document).querySelectorAll(s)
      }
  })

  aug(boosh, {
    forEach: function (fn, scope, i) {
      // opt out of native forEach so we can intentionally call our own scope
      // defaulting to the current item and be able to return self
      for (i = 0, l = this.length; i < l; ++i) i in this && fn.call(scope || this[i], this[i], i, this)
      // return self for chaining
      return this
    },
    $: ender // handy reference to self
  })

  ender.noConflict = function () {
    context.$ = old
    return this
  }

  if (typeof module !== 'undefined' && module.exports) module.exports = ender
  // use subscript notation as extern for Closure compilation
  context['ender'] = context['$'] = context['ender'] || ender

}(this);

!function () {

  var module = { exports: {} }, exports = module.exports;

  /*
   * Copyright (C) 2007-2014 Diego Perini
   * All rights reserved.
   *
   * nwmatcher.js - A fast CSS selector engine and matcher
   *
   * Author: Diego Perini <diego.perini at gmail com>
   * Version: 1.3.3
   * Created: 20070722
   * Release: 20140330
   *
   * License:
   *  http://javascript.nwbox.com/NWMatcher/MIT-LICENSE
   * Download:
   *  http://javascript.nwbox.com/NWMatcher/nwmatcher.js
   */
  
  (function(global, factory) {
  
    if (typeof module == 'object' && typeof exports == 'object') {
      // in a Node.js environment, the nwmatcher functions will operate on
      // the passed "browserGlobal" and will be returned in an object
      module.exports = function (browserGlobal) {
        // passed global does not contain
        // references to native objects
        browserGlobal.console = console;
        browserGlobal.parseInt = parseInt;
        browserGlobal.Function = Function;
        browserGlobal.Boolean = Boolean;
        browserGlobal.Number = Number;
        browserGlobal.RegExp = RegExp;
        browserGlobal.String = String;
        browserGlobal.Object = Object;
        browserGlobal.Array = Array;
        browserGlobal.Error = Error;
        browserGlobal.Date = Date;
        browserGlobal.Math = Math;
        var exports = browserGlobal.Object();
        factory(browserGlobal, exports);
        return exports;
      };
      module.factory = factory; 
    } else {
      // in a browser environment, the nwmatcher functions will operate on
      // the "global" loading them and be attached to "global.NW.Dom"
      factory(global,
        (global.NW || (global.NW = global.Object())) &&
        (global.NW.Dom || (global.NW.Dom = global.Object())));
      global.NW.Dom.factory = factory;
    }
  
  })(this, function(global, exports) {
  
    var version = 'nwmatcher-1.3.3',
  
    Dom = exports,
  
    // processing context & root element
    doc = global.document,
    root = doc.documentElement,
  
    // save utility methods references
    slice = global.Array.prototype.slice,
    string = global.Object.prototype.toString,
  
    // persist previous parsed data
    isSingleMatch,
    isSingleSelect,
  
    lastSlice,
    lastContext,
    lastPosition,
  
    lastMatcher,
    lastSelector,
  
    lastPartsMatch,
    lastPartsSelect,
  
    // accepted prefix identifiers
    // (id, class & pseudo-class)
    prefixes = '[#.:]?',
  
    // accepted attribute operators
    operators = '([~*^$|!]?={1})',
  
    // accepted whitespace characters
    whitespace = '[\\x20\\t\\n\\r\\f]*',
  
    // 4 combinators F E, F>E, F+E, F~E
    combinators = '[\\x20]|[>+~][^>+~]',
  
    // an+b format params for pseudo-classes
    pseudoparms = '(?:[-+]?\\d*n)?[-+]?\\d*',
  
    // CSS quoted string values
    quotedvalue = '"[^"]*"' + "|'[^']*'",
  
    // skip round brackets groups
    skipround = '\\([^()]+\\)|\\(.*\\)',
    // skip curly brackets groups
    skipcurly = '\\{[^{}]+\\}|\\{.*\\}',
    // skip square brackets groups
    skipsquare = '\\[[^[\\]]*\\]|\\[.*\\]',
  
    // skip [ ], ( ), { } brackets groups
    skipgroup = '\\[.*\\]|\\(.*\\)|\\{.*\\}',
  
    // http://www.w3.org/TR/css3-syntax/#characters
    // unicode/ISO 10646 characters 161 and higher
    // NOTE: Safari 2.0.x crashes with escaped (\\)
    // Unicode ranges in regular expressions so we
    // use a negated character range class instead
    encoding = '(?:[-\\w]|[^\\x00-\\xa0]|\\\\.)',
  
    // CSS identifier syntax
    identifier = '(?:-?[_a-zA-Z]{1}[-\\w]*|[^\\x00-\\xa0]+|\\\\.+)+',
  
    // build attribute string
    attrcheck = '(' + quotedvalue + '|' + identifier + ')',
    attributes = whitespace + '(' + encoding + '*:?' + encoding + '+)' +
      whitespace + '(?:' + operators + whitespace + attrcheck + ')?' + whitespace,
    attrmatcher = attributes.replace(attrcheck, '([\\x22\\x27]*)((?:\\\\?.)*?)\\3'),
  
    // build pseudoclass string
    pseudoclass = '((?:' +
      // an+b parameters or quoted string
      pseudoparms + '|' + quotedvalue + '|' +
      // id, class, pseudo-class selector
      prefixes + '|' + encoding + '+|' +
      // nested HTML attribute selector
      '\\[' + attributes + '\\]|' +
      // nested pseudo-class selector
      '\\(.+\\)|' + whitespace + '|' +
      // nested pseudos/separators
      ',)+)',
  
    // placeholder for extensions
    extensions = '.+',
  
    // CSS3: syntax scanner and
    // one pass validation only
    // using regular expression
    standardValidator =
      // discard start
      '(?=[\\x20\\t\\n\\r\\f]*[^>+~(){}<>])' +
      // open match group
      '(' +
      //universal selector
      '\\*' +
      // id/class/tag/pseudo-class identifier
      '|(?:' + prefixes + identifier + ')' +
      // combinator selector
      '|' + combinators +
      // HTML attribute selector
      '|\\[' + attributes + '\\]' +
      // pseudo-classes parameters
      '|\\(' + pseudoclass + '\\)' +
      // dom properties selector (extension)
      '|\\{' + extensions + '\\}' +
      // selector group separator (comma)
      '|(?:,|' + whitespace + ')' +
      // close match group
      ')+',
  
    // validator for complex selectors in ':not()' pseudo-classes
    extendedValidator = standardValidator.replace(pseudoclass, '.*'),
  
    // validator for standard selectors as default
    reValidator = new global.RegExp(standardValidator, 'g'),
  
    // whitespace is any combination of these 5 character [\x20\t\n\r\f]
    // http://www.w3.org/TR/css3-selectors/#selector-syntax
    reTrimSpaces = new global.RegExp('^' +
      whitespace + '|' + whitespace + '$', 'g'),
  
    // only allow simple selectors nested in ':not()' pseudo-classes
    reSimpleNot = new global.RegExp('^(' +
      '(?!:not)' +
      '(' + prefixes +
      '|' + identifier +
      '|\\([^()]*\\))+' +
      '|\\[' + attributes + '\\]' +
      ')$'),
  
    // split comma groups, exclude commas from
    // quotes '' "" and from brackets () [] {}
    reSplitGroup = new global.RegExp('(' +
      '[^,\\\\()[\\]]+' +
      '|' + skipsquare +
      '|' + skipround +
      '|' + skipcurly +
      '|\\\\.' +
      ')+', 'g'),
  
    // split last, right most, selector group token
    reSplitToken = new global.RegExp('(' +
      '\\[' + attributes + '\\]|' +
      '\\(' + pseudoclass + '\\)|' +
      '\\\\.|[^\\x20\\t\\r\\n\\f>+~])+', 'g'),
  
    // for in excess whitespace removal
    reWhiteSpace = /[\x20\t\n\r\f]+/g,
  
    reOptimizeSelector = new global.RegExp(identifier + '|^$'),
  
    /*----------------------------- FEATURE TESTING ----------------------------*/
  
    // detect native methods
    isNative = (function() {
      var s = (doc.appendChild + '').replace(/appendChild/g, '');
      return function(object, method) {
        var m = object && object[method] || false;
        return m && typeof m != 'string' &&
          s == (m + '').replace(new global.RegExp(method, 'g'), '');
      };
    })(),
  
    // NATIVE_XXXXX true if method exist and is callable
    // detect if DOM methods are native in browsers
    NATIVE_FOCUS = isNative(doc, 'hasFocus'),
    NATIVE_QSAPI = isNative(doc, 'querySelector'),
    NATIVE_GEBID = isNative(doc, 'getElementById'),
    NATIVE_GEBTN = isNative(root, 'getElementsByTagName'),
    NATIVE_GEBCN = isNative(root, 'getElementsByClassName'),
  
    // detect native getAttribute/hasAttribute methods,
    // frameworks extend these to elements, but it seems
    // this does not work for XML namespaced attributes,
    // used to check both getAttribute/hasAttribute in IE
    NATIVE_GET_ATTRIBUTE = isNative(root, 'getAttribute'),
    NATIVE_HAS_ATTRIBUTE = isNative(root, 'hasAttribute'),
  
    // check if slice() can convert nodelist to array
    // see http://yura.thinkweb2.com/cft/
    NATIVE_SLICE_PROTO =
      (function() {
        var isBuggy = false;
        try {
          isBuggy = !!slice.call(doc.childNodes, 0)[0];
        } catch(e) { }
        return isBuggy;
      })(),
  
    // supports the new traversal API
    NATIVE_TRAVERSAL_API =
      'nextElementSibling' in root && 'previousElementSibling' in root,
  
    // BUGGY_XXXXX true if method is feature tested and has known bugs
    // detect buggy gEBID
    BUGGY_GEBID = NATIVE_GEBID ?
      (function() {
        var isBuggy = true, x = 'x' + global.String(+new global.Date),
          a = doc.createElementNS ? 'a' : '<a name="' + x + '">';
        (a = doc.createElement(a)).name = x;
        root.insertBefore(a, root.firstChild);
        isBuggy = !!doc.getElementById(x);
        root.removeChild(a);
        return isBuggy;
      })() :
      true,
  
    // detect IE gEBTN comment nodes bug
    BUGGY_GEBTN = NATIVE_GEBTN ?
      (function() {
        var div = doc.createElement('div');
        div.appendChild(doc.createComment(''));
        return !!div.getElementsByTagName('*')[0];
      })() :
      true,
  
    // detect Opera gEBCN second class and/or UTF8 bugs as well as Safari 3.2
    // caching class name results and not detecting when changed,
    // tests are based on the jQuery selector test suite
    BUGGY_GEBCN = NATIVE_GEBCN ?
      (function() {
        var isBuggy, div = doc.createElement('div'), test = '\u53f0\u5317';
  
        // Opera tests
        div.appendChild(doc.createElement('span')).
          setAttribute('class', test + 'abc ' + test);
        div.appendChild(doc.createElement('span')).
          setAttribute('class', 'x');
  
        isBuggy = !div.getElementsByClassName(test)[0];
  
        // Safari test
        div.lastChild.className = test;
        return isBuggy || div.getElementsByClassName(test).length != 2;
      })() :
      true,
  
    // detect IE bug with dynamic attributes
    BUGGY_GET_ATTRIBUTE = NATIVE_GET_ATTRIBUTE ?
      (function() {
        var input = doc.createElement('input');
        input.setAttribute('value', 5);
        return input.defaultValue != 5;
      })() :
      true,
  
    // detect IE bug with non-standard boolean attributes
    BUGGY_HAS_ATTRIBUTE = NATIVE_HAS_ATTRIBUTE ?
      (function() {
        var option = doc.createElement('option');
        option.setAttribute('selected', 'selected');
        return !option.hasAttribute('selected');
      })() :
      true,
  
    // detect Safari bug with selected option elements
    BUGGY_SELECTED =
      (function() {
        var select = doc.createElement('select');
        select.appendChild(doc.createElement('option'));
        return !select.firstChild.selected;
      })(),
  
    // initialized with the loading context
    // and reset for each different context
    BUGGY_QUIRKS_GEBCN,
    BUGGY_QUIRKS_QSAPI,
  
    QUIRKS_MODE,
    XML_DOCUMENT,
  
    // detect Opera browser
    OPERA = /opera/i.test(string.call(global.opera)),
  
    // skip simple selector optimizations for Opera >= 11
    OPERA_QSAPI = OPERA && global.parseFloat(global.opera.version()) >= 11,
  
    // check Selector API implementations
    RE_BUGGY_QSAPI = NATIVE_QSAPI ?
      (function() {
        var pattern = new global.Array(), div = doc.createElement('div'), element,
  
        expect = function(selector, context, element, n) {
          var result = false;
          context.appendChild(element);
          try { result = context.querySelectorAll(selector).length == n; } catch(e) { }
          while (context.firstChild) { context.removeChild(context.firstChild); }
          return result;
        };
  
        // ^= $= *= operators bugs with empty values (Opera 10 / IE8)
        element = doc.createElement('p');
        element.setAttribute('class', '');
        expect('[class^=""]', div, element, 1) &&
          pattern.push('[*^$]=[\\x20\\t\\n\\r\\f]*(?:""|' + "'')");
  
        // :checked bug with option elements (Firefox 3.6.x)
        // it wrongly includes 'selected' options elements
        // HTML5 rules says selected options also match
        element = doc.createElement('option');
        element.setAttribute('selected', 'selected');
        expect(':checked', div, element, 0) &&
          pattern.push(':checked');
  
        // :enabled :disabled bugs with hidden fields (Firefox 3.5)
        // http://www.w3.org/TR/html5/links.html#selector-enabled
        // http://www.w3.org/TR/css3-selectors/#enableddisabled
        // not supported by IE8 Query Selector
        element = doc.createElement('input');
        element.setAttribute('type', 'hidden');
        expect(':enabled', div, element, 0) &&
          pattern.push(':enabled', ':disabled');
  
        // :link bugs with hyperlinks matching (Firefox/Safari)
        element = doc.createElement('link');
        element.setAttribute('href', 'x');
        expect(':link', div, element, 1) ||
          pattern.push(':link');
  
        // avoid attribute selectors for IE QSA
        if (BUGGY_HAS_ATTRIBUTE) {
          // IE fails in reading:
          // - original values for input/textarea
          // - original boolean values for controls
          pattern.push('\\[[\\x20\\t\\n\\r\\f]*(?:checked|disabled|ismap|multiple|readonly|selected|value)');
        }
  
        return pattern.length ?
          new global.RegExp(pattern.join('|')) :
          { 'test': function() { return false; } };
  
      })() :
      true,
  
    // matches class selectors
    RE_CLASS = new global.RegExp('(?:\\[[\\x20\\t\\n\\r\\f]*class\\b|\\.' + identifier + ')'),
  
    // matches simple id, tag & class selectors
    RE_SIMPLE_SELECTOR = new global.RegExp(
      BUGGY_GEBTN && BUGGY_GEBCN || OPERA ?
        '^#?-?[_a-zA-Z]{1}' + encoding + '*$' : BUGGY_GEBTN ?
        '^[.#]?-?[_a-zA-Z]{1}' + encoding + '*$' : BUGGY_GEBCN ?
        '^(?:\\*|#-?[_a-zA-Z]{1}' + encoding + '*)$' :
        '^(?:\\*|[.#]?-?[_a-zA-Z]{1}' + encoding + '*)$'),
  
    /*----------------------------- LOOKUP OBJECTS -----------------------------*/
  
    LINK_NODES = new global.Object({ 'a': 1, 'A': 1, 'area': 1, 'AREA': 1, 'link': 1, 'LINK': 1 }),
  
    // boolean attributes should return attribute name instead of true/false
    ATTR_BOOLEAN = new global.Object({
      'checked': 1, 'disabled': 1, 'ismap': 1,
      'multiple': 1, 'readonly': 1, 'selected': 1
    }),
  
    // dynamic attributes that needs to be checked against original HTML value
    ATTR_DEFAULT = new global.Object({
      'value': 'defaultValue',
      'checked': 'defaultChecked',
      'selected': 'defaultSelected'
    }),
  
    // attributes referencing URI data values need special treatment in IE
    ATTR_URIDATA = new global.Object({
      'action': 2, 'cite': 2, 'codebase': 2, 'data': 2, 'href': 2,
      'longdesc': 2, 'lowsrc': 2, 'src': 2, 'usemap': 2
    }),
  
    // HTML 5 draft specifications
    // http://www.whatwg.org/specs/web-apps/current-work/#selectors
    HTML_TABLE = new global.Object({
      // class attribute must be treated case-insensitive in HTML quirks mode
      // initialized by default to Standard Mode (case-sensitive),
      // set dynamically by the attribute resolver
      'class': 0,
      'accept': 1, 'accept-charset': 1, 'align': 1, 'alink': 1, 'axis': 1,
      'bgcolor': 1, 'charset': 1, 'checked': 1, 'clear': 1, 'codetype': 1, 'color': 1,
      'compact': 1, 'declare': 1, 'defer': 1, 'dir': 1, 'direction': 1, 'disabled': 1,
      'enctype': 1, 'face': 1, 'frame': 1, 'hreflang': 1, 'http-equiv': 1, 'lang': 1,
      'language': 1, 'link': 1, 'media': 1, 'method': 1, 'multiple': 1, 'nohref': 1,
      'noresize': 1, 'noshade': 1, 'nowrap': 1, 'readonly': 1, 'rel': 1, 'rev': 1,
      'rules': 1, 'scope': 1, 'scrolling': 1, 'selected': 1, 'shape': 1, 'target': 1,
      'text': 1, 'type': 1, 'valign': 1, 'valuetype': 1, 'vlink': 1
    }),
  
    // the following attributes must be treated case-insensitive in XHTML mode
    // Niels Leenheer http://rakaz.nl/item/css_selector_bugs_case_sensitivity
    XHTML_TABLE = new global.Object({
      'accept': 1, 'accept-charset': 1, 'alink': 1, 'axis': 1,
      'bgcolor': 1, 'charset': 1, 'codetype': 1, 'color': 1,
      'enctype': 1, 'face': 1, 'hreflang': 1, 'http-equiv': 1,
      'lang': 1, 'language': 1, 'link': 1, 'media': 1, 'rel': 1,
      'rev': 1, 'target': 1, 'text': 1, 'type': 1, 'vlink': 1
    }),
  
    /*-------------------------- REGULAR EXPRESSIONS ---------------------------*/
  
    // placeholder to add functionalities
    Selectors = new global.Object({
      // as a simple example this will check
      // for chars not in standard ascii table
      //
      // 'mySpecialSelector': {
      //  'Expression': /\u0080-\uffff/,
      //  'Callback': mySelectorCallback
      // }
      //
      // 'mySelectorCallback' will be invoked
      // only after passing all other standard
      // checks and only if none of them worked
    }),
  
    // attribute operators
    Operators = new global.Object({
       '=': "n=='%m'",
      '^=': "n.indexOf('%m')==0",
      '*=': "n.indexOf('%m')>-1",
      '|=': "(n+'-').indexOf('%m-')==0",
      '~=': "(' '+n+' ').indexOf(' %m ')>-1",
      '$=': "n.substr(n.length-'%m'.length)=='%m'"
    }),
  
    // optimization expressions
    Optimize = new global.Object({
      ID: new global.RegExp('^\\*?#(' + encoding + '+)|' + skipgroup),
      TAG: new global.RegExp('^(' + encoding + '+)|' + skipgroup),
      CLASS: new global.RegExp('^\\*?\\.(' + encoding + '+$)|' + skipgroup)
    }),
  
    // precompiled Regular Expressions
    Patterns = new global.Object({
      // structural pseudo-classes and child selectors
      spseudos: /^\:(root|empty|(?:first|last|only)(?:-child|-of-type)|nth(?:-last)?(?:-child|-of-type)\(\s*(even|odd|(?:[-+]{0,1}\d*n\s*)?[-+]{0,1}\s*\d*)\s*\))?(.*)/i,
      // uistates + dynamic + negation pseudo-classes
      dpseudos: /^\:(link|visited|target|active|focus|hover|checked|disabled|enabled|selected|lang\(([-\w]{2,})\)|not\(([^()]*|.*)\))?(.*)/i,
      // element attribute matcher
      attribute: new global.RegExp('^\\[' + attrmatcher + '\\](.*)'),
      // E > F
      children: /^[\x20\t\n\r\f]*\>[\x20\t\n\r\f]*(.*)/,
      // E + F
      adjacent: /^[\x20\t\n\r\f]*\+[\x20\t\n\r\f]*(.*)/,
      // E ~ F
      relative: /^[\x20\t\n\r\f]*\~[\x20\t\n\r\f]*(.*)/,
      // E F
      ancestor: /^[\x20\t\n\r\f]+(.*)/,
      // all
      universal: /^\*(.*)/,
      // id
      id: new global.RegExp('^#(' + encoding + '+)(.*)'),
      // tag
      tagName: new global.RegExp('^(' + encoding + '+)(.*)'),
      // class
      className: new global.RegExp('^\\.(' + encoding + '+)(.*)')
    }),
  
    /*------------------------------ UTIL METHODS ------------------------------*/
  
    // concat elements to data
    concatList =
      function(data, elements) {
        var i = -1, element;
        if (!data.length && global.Array.slice)
          return global.Array.slice(elements);
        while ((element = elements[++i]))
          data[data.length] = element;
        return data;
      },
  
    // concat elements to data and callback
    concatCall =
      function(data, elements, callback) {
        var i = -1, element;
        while ((element = elements[++i])) {
          if (false === callback(data[data.length] = element)) { break; }
        }
        return data;
      },
  
    // change context specific variables
    switchContext =
      function(from, force) {
        var div, oldDoc = doc;
        // save passed context
        lastContext = from;
        // set new context document
        doc = from.ownerDocument || from;
        if (force || oldDoc !== doc) {
          // set document root
          root = doc.documentElement;
          // set host environment flags
          XML_DOCUMENT = doc.createElement('DiV').nodeName == 'DiV';
  
          // In quirks mode css class names are case insensitive.
          // In standards mode they are case sensitive. See docs:
          // https://developer.mozilla.org/en/Mozilla_Quirks_Mode_Behavior
          // http://www.whatwg.org/specs/web-apps/current-work/#selectors
          QUIRKS_MODE = !XML_DOCUMENT &&
            typeof doc.compatMode == 'string' ?
            doc.compatMode.indexOf('CSS') < 0 :
            (function() {
              var style = doc.createElement('div').style;
              return style && (style.width = 1) && style.width == '1px';
            })();
  
          div = doc.createElement('div');
          div.appendChild(doc.createElement('p')).setAttribute('class', 'xXx');
          div.appendChild(doc.createElement('p')).setAttribute('class', 'xxx');
  
          // GEBCN buggy in quirks mode, match count is:
          // Firefox 3.0+ [xxx = 1, xXx = 1]
          // Opera 10.63+ [xxx = 0, xXx = 2]
          BUGGY_QUIRKS_GEBCN =
            !XML_DOCUMENT && NATIVE_GEBCN && QUIRKS_MODE &&
            (div.getElementsByClassName('xxx').length != 2 ||
            div.getElementsByClassName('xXx').length != 2);
  
          // QSAPI buggy in quirks mode, match count is:
          // At least Chrome 4+, Firefox 3.5+, Opera 10.x+, Safari 4+ [xxx = 1, xXx = 2]
          // Safari 3.2 QSA doesn't work with mixedcase in quirksmode [xxx = 1, xXx = 0]
          // https://bugs.webkit.org/show_bug.cgi?id=19047
          // must test the attribute selector '[class~=xxx]'
          // before '.xXx' or the bug may not present itself
          BUGGY_QUIRKS_QSAPI =
            !XML_DOCUMENT && NATIVE_QSAPI && QUIRKS_MODE &&
            (div.querySelectorAll('[class~=xxx]').length != 2 ||
            div.querySelectorAll('.xXx').length != 2);
  
          Config.CACHING && Dom.setCache(true, doc);
        }
      },
  
    /*------------------------------ DOM METHODS -------------------------------*/
  
    // element by id (raw)
    // @return reference or null
    byIdRaw =
      function(id, elements) {
        var i = -1, element = null;
        while ((element = elements[++i])) {
          if (element.getAttribute('id') == id) {
            break;
          }
        }
        return element;
      },
  
    // element by id
    // @return reference or null
    _byId = !BUGGY_GEBID ?
      function(id, from) {
        id = id.replace(/\\([^\\]{1})/g, '$1');
        return from.getElementById && from.getElementById(id) ||
          byIdRaw(id, from.getElementsByTagName('*'));
      } :
      function(id, from) {
        var element = null;
        id = id.replace(/\\([^\\]{1})/g, '$1');
        if (XML_DOCUMENT || from.nodeType != 9) {
          return byIdRaw(id, from.getElementsByTagName('*'));
        }
        if ((element = from.getElementById(id)) &&
          element.name == id && from.getElementsByName) {
          return byIdRaw(id, from.getElementsByName(id));
        }
        return element;
      },
  
    // publicly exposed byId
    // @return reference or null
    byId =
      function(id, from) {
        from || (from = doc);
        if (lastContext !== from) { switchContext(from); }
        return _byId(id, from);
      },
  
    // elements by tag (raw)
    // @return array
    byTagRaw =
      function(tag, from) {
        var any = tag == '*', element = from, elements = new global.Array(), next = element.firstChild;
        any || (tag = tag.toUpperCase());
        while ((element = next)) {
          if (element.tagName > '@' && (any || element.tagName.toUpperCase() == tag)) {
            elements[elements.length] = element;
          }
          if ((next = element.firstChild || element.nextSibling)) continue;
          while (!next && (element = element.parentNode) && element !== from) {
            next = element.nextSibling;
          }
        }
        return elements;
      },
  
    // elements by tag
    // @return array
    _byTag = !BUGGY_GEBTN && NATIVE_SLICE_PROTO ?
      function(tag, from) {
        return XML_DOCUMENT || from.nodeType == 11 ? byTagRaw(tag, from) :
          slice.call(from.getElementsByTagName(tag), 0);
      } :
      function(tag, from) {
        var i = -1, j = i, data = new global.Array(),
          element, elements = from.getElementsByTagName(tag);
        if (tag == '*') {
          while ((element = elements[++i])) {
            if (element.nodeName > '@')
              data[++j] = element;
          }
        } else {
          while ((element = elements[++i])) {
            data[i] = element;
          }
        }
        return data;
      },
  
    // publicly exposed byTag
    // @return array
    byTag =
      function(tag, from) {
        from || (from = doc);
        if (lastContext !== from) { switchContext(from); }
        return _byTag(tag, from);
      },
  
    // publicly exposed byName
    // @return array
    byName =
      function(name, from) {
        return select('[name="' + name.replace(/\\([^\\]{1})/g, '$1') + '"]', from);
      },
  
    // elements by class (raw)
    // @return array
    byClassRaw =
      function(name, from) {
        var i = -1, j = i, data = new global.Array(), element, elements = _byTag('*', from), n;
        name = ' ' + (QUIRKS_MODE ? name.toLowerCase() : name).replace(/\\([^\\]{1})/g, '$1') + ' ';
        while ((element = elements[++i])) {
          n = XML_DOCUMENT ? element.getAttribute('class') : element.className;
          if (n && n.length && (' ' + (QUIRKS_MODE ? n.toLowerCase() : n).
            replace(reWhiteSpace, ' ') + ' ').indexOf(name) > -1) {
            data[++j] = element;
          }
        }
        return data;
      },
  
    // elements by class
    // @return array
    _byClass =
      function(name, from) {
        return (BUGGY_GEBCN || BUGGY_QUIRKS_GEBCN || XML_DOCUMENT || !from.getElementsByClassName) ?
          byClassRaw(name, from) : slice.call(from.getElementsByClassName(name.replace(/\\([^\\]{1})/g, '$1')), 0);
      },
  
    // publicly exposed byClass
    // @return array
    byClass =
      function(name, from) {
        from || (from = doc);
        if (lastContext !== from) { switchContext(from); }
        return _byClass(name, from);
      },
  
    // check element is descendant of container
    // @return boolean
    contains = 'compareDocumentPosition' in root ?
      function(container, element) {
        return (container.compareDocumentPosition(element) & 16) == 16;
      } : 'contains' in root ?
      function(container, element) {
        return container !== element && container.contains(element);
      } :
      function(container, element) {
        while ((element = element.parentNode)) {
          if (element === container) return true;
        }
        return false;
      },
  
    // attribute value
    // @return string
    getAttribute = !BUGGY_GET_ATTRIBUTE ?
      function(node, attribute) {
        return node.getAttribute(attribute) || '';
      } :
      function(node, attribute) {
        attribute = attribute.toLowerCase();
        if (typeof node[attribute] == 'object') {
          return node.attributes[attribute] &&
            node.attributes[attribute].value || '';
        }
        return (
          // 'type' can only be read by using native getAttribute
          attribute == 'type' ? node.getAttribute(attribute) || '' :
          // specific URI data attributes (parameter 2 to fix IE bug)
          ATTR_URIDATA[attribute] ? node.getAttribute(attribute, 2) || '' :
          // boolean attributes should return name instead of true/false
          ATTR_BOOLEAN[attribute] ? node.getAttribute(attribute) ? attribute : 'false' :
            ((node = node.getAttributeNode(attribute)) && node.value) || '');
      },
  
    // attribute presence
    // @return boolean
    hasAttribute = !BUGGY_HAS_ATTRIBUTE ?
      function(node, attribute) {
        return XML_DOCUMENT ?
          !!node.getAttribute(attribute) :
          node.hasAttribute(attribute);
      } :
      function(node, attribute) {
        attribute = attribute.toLowerCase();
        if (ATTR_DEFAULT[attribute]) {
          return !!node[ATTR_DEFAULT[attribute]];
        }
        // read the attribute node
        node = node.getAttributeNode(attribute);
        return !!(node && node.specified);
      },
  
    // check node emptyness
    // @return boolean
    isEmpty =
      function(node) {
        node = node.firstChild;
        while (node) {
          if (node.nodeType == 3 || node.nodeName > '@') return false;
          node = node.nextSibling;
        }
        return true;
      },
  
    // check if element matches the :link pseudo
    // @return boolean
    isLink =
      function(element) {
        return hasAttribute(element,'href') && LINK_NODES[element.nodeName];
      },
  
    // child position by nodeType
    // @return number
    nthElement =
      function(element, last) {
        var count = 1, succ = last ? 'nextSibling' : 'previousSibling';
        while ((element = element[succ])) {
          if (element.nodeName > '@') ++count;
        }
        return count;
      },
  
    // child position by nodeName
    // @return number
    nthOfType =
      function(element, last) {
        var count = 1, succ = last ? 'nextSibling' : 'previousSibling', type = element.nodeName;
        while ((element = element[succ])) {
          if (element.nodeName == type) ++count;
        }
        return count;
      },
  
    /*------------------------------- DEBUGGING --------------------------------*/
  
    // get/set (string/object) working modes
    configure =
      function(option) {
        if (typeof option == 'string') { return Config[option]; }
        if (typeof option != 'object') { return false; }
        for (var i in option) {
          Config[i] = !!option[i];
          if (i == 'SIMPLENOT') {
            matchContexts = new global.Object();
            matchResolvers = new global.Object();
            selectContexts = new global.Object();
            selectResolvers = new global.Object();
            Config['USE_QSAPI'] = false;
          } else if (i == 'USE_QSAPI') {
            Config[i] = !!option[i] && NATIVE_QSAPI;
          }
        }
        reValidator = new global.RegExp(Config.SIMPLENOT ?
          standardValidator : extendedValidator, 'g');
        return true;
      },
  
    // control user notifications
    emit =
      function(message) {
        if (Config.VERBOSITY) { throw new global.Error(message); }
        if (global.console && global.console.log) {
          global.console.log(message);
        }
      },
  
    Config = new global.Object({
  
      // used to enable/disable caching of result sets
      CACHING: false,
  
      // by default do not add missing left/right context
      // to selector string shortcuts like "+div" or "ul>"
      // callable Dom.shortcuts method has to be available
      SHORTCUTS: false,
  
      // by default disable complex selectors nested in
      // ':not()' pseudo-classes, as for specifications
      SIMPLENOT: true,
  
      // strict QSA match all non-unique IDs (false)
      // speed & libs compat match unique ID (true)
      UNIQUE_ID: true,
  
      // HTML5 handling for the ":checked" pseudo-class
      USE_HTML5: true,
  
      // controls enabling the Query Selector API branch
      USE_QSAPI: NATIVE_QSAPI,
  
      // controls the engine error/warning notifications
      VERBOSITY: true
  
    }),
  
    /*---------------------------- COMPILER METHODS ----------------------------*/
  
    // code string reused to build compiled functions
    ACCEPT_NODE = 'r[r.length]=c[k];if(f&&false===f(c[k]))break main;else continue main;',
  
    // compile a comma separated group of selector
    // @mode boolean true for select, false for match
    // return a compiled function
    compile =
      function(selector, source, mode) {
  
        var parts = typeof selector == 'string' ? selector.match(reSplitGroup) : selector;
  
        // ensures that source is a string
        typeof source == 'string' || (source = '');
  
        if (parts.length == 1) {
          source += compileSelector(parts[0], mode ? ACCEPT_NODE : 'f&&f(k);return true;', mode);
        } else {
          // for each selector in the group
          var i = -1, seen = new global.Object(), token;
          while ((token = parts[++i])) {
            token = token.replace(reTrimSpaces, '');
            // avoid repeating the same token
            // in comma separated group (p, p)
            if (!seen[token] && (seen[token] = true)) {
              source += compileSelector(token, mode ? ACCEPT_NODE : 'f&&f(k);return true;', mode);
            }
          }
        }
  
        if (mode) {
          // for select method
          return new global.Function('c,s,r,d,h,g,f,v',
            'var N,n,x=0,k=-1,e;main:while((e=c[++k])){' + source + '}return r;');
        } else {
          // for match method
          return new global.Function('e,s,r,d,h,g,f,v',
            'var N,n,x=0,k=e;' + source + 'return false;');
        }
      },
  
    // allows to cache already visited nodes
    FILTER =
      'var z=v[@]||(v[@]=[]),l=z.length-1;' +
      'while(l>=0&&z[l]!==e)--l;' +
      'if(l!==-1){break;}' +
      'z[z.length]=e;',
  
    // compile a CSS3 string selector into ad-hoc javascript matching function
    // @return string (to be compiled)
    compileSelector =
      function(selector, source, mode) {
  
        var a, b, n, k = 0, expr, match, result, status, test, type;
  
        while (selector) {
  
          k++;
  
          // *** Universal selector
          // * match all (empty block, do not remove)
          if ((match = selector.match(Patterns.universal))) {
            // do nothing, handled in the compiler where
            // BUGGY_GEBTN return comment nodes (ex: IE)
            expr = '';
          }
  
          // *** ID selector
          // #Foo Id case sensitive
          else if ((match = selector.match(Patterns.id))) {
            // document can contain conflicting elements (id/name)
            // prototype selector unit need this method to recover bad HTML forms
            source = 'if(' + (XML_DOCUMENT ?
              's.getAttribute(e,"id")' :
              '(e.submit?s.getAttribute(e,"id"):e.id)') +
              '=="' + match[1] + '"' +
              '){' + source + '}';
          }
  
          // *** Type selector
          // Foo Tag (case insensitive)
          else if ((match = selector.match(Patterns.tagName))) {
            // both tagName and nodeName properties may be upper/lower case
            // depending on their creation NAMESPACE in createElementNS()
            source = 'if(e.nodeName' + (XML_DOCUMENT ?
              '=="' + match[1] + '"' : '.toUpperCase()' +
              '=="' + match[1].toUpperCase() + '"') +
              '){' + source + '}';
          }
  
          // *** Class selector
          // .Foo Class (case sensitive)
          else if ((match = selector.match(Patterns.className))) {
            // W3C CSS3 specs: element whose "class" attribute has been assigned a
            // list of whitespace-separated values, see section 6.4 Class selectors
            // and notes at the bottom; explicitly non-normative in this specification.
            source = 'if((n=' + (XML_DOCUMENT ?
              's.getAttribute(e,"class")' : 'e.className') +
              ')&&n.length&&(" "+' + (QUIRKS_MODE ? 'n.toLowerCase()' : 'n') +
              '.replace(' + reWhiteSpace + '," ")+" ").indexOf(" ' +
              (QUIRKS_MODE ? match[1].toLowerCase() : match[1]) + ' ")>-1' +
              '){' + source + '}';
          }
  
          // *** Attribute selector
          // [attr] [attr=value] [attr="value"] [attr='value'] and !=, *=, ~=, |=, ^=, $=
          // case sensitivity is treated differently depending on the document type (see map)
          else if ((match = selector.match(Patterns.attribute))) {
  
            // xml namespaced attribute ?
            expr = match[1].split(':');
            expr = expr.length == 2 ? expr[1] : expr[0] + '';
  
            if (match[2] && !Operators[match[2]]) {
              emit('Unsupported operator in attribute selectors "' + selector + '"');
              return '';
            }
  
            test = false;
            type = 'false';
  
            // replace Operators parameter if needed
            if (match[2] && match[4] && (type = Operators[match[2]])) {
              // case treatment depends on document
              HTML_TABLE['class'] = QUIRKS_MODE ? 1 : 0;
              // replace escaped values and HTML entities
              match[4] = match[4].replace(/(\x22|\x27)/g, '\\$1');
              match[4] = match[4].replace(/\\([0-9a-f]{2,2})/g, '\\x$1');
              test = (XML_DOCUMENT ? XHTML_TABLE : HTML_TABLE)[expr.toLowerCase()];
              type = type.replace(/\%m/g, test ? match[4].toLowerCase() : match[4]);
            } else if (match[2] == '!=' || match[2] == '=') {
              match[4] = match[4].replace(/(\x22|\x27)/g, '\\$1');
              type = 'n' + match[2] + '="' + match[4] + '"';
            }
  
            // build expression for has/getAttribute
            expr = 'n=s.' + (match[2] ? 'get' : 'has') +
              'Attribute(e,"' + match[1] + '")' +
              (test ? '.toLowerCase();' : ';');
  
            source = expr + 'if(' + (match[2] ? type : 'n') + '){' + source + '}';
          }
  
          // *** Adjacent sibling combinator
          // E + F (F adiacent sibling of E)
          else if ((match = selector.match(Patterns.adjacent))) {
            source = (mode ? '' : FILTER.replace(/@/g, k)) + source;
            source = NATIVE_TRAVERSAL_API ?
              'var N' + k + '=e;while(e&&(e=e.previousElementSibling)){' + source + 'break;}e=N' + k + ';' :
              'var N' + k + '=e;while(e&&(e=e.previousSibling)){if(e.nodeName>"@"){' + source + 'break;}}e=N' + k + ';';
          }
  
          // *** General sibling combinator
          // E ~ F (F relative sibling of E)
          else if ((match = selector.match(Patterns.relative))) {
            source = (mode ? '' : FILTER.replace(/@/g, k)) + source;
            source = NATIVE_TRAVERSAL_API ?
              ('var N' + k + '=e;e=e.parentNode.firstElementChild;' +
              'while(e&&e!==N' + k + '){' + source + 'e=e.nextElementSibling;}e=N' + k + ';') :
              ('var N' + k + '=e;e=e.parentNode.firstChild;' +
              'while(e&&e!==N' + k + '){if(e.nodeName>"@"){' + source + '}e=e.nextSibling;}e=N' + k + ';');
          }
  
          // *** Child combinator
          // E > F (F children of E)
          else if ((match = selector.match(Patterns.children))) {
            source = (mode ? '' : FILTER.replace(/@/g, k)) + source;
            source = 'var N' + k + '=e;while(e&&e!==h&&e!==g&&(e=e.parentNode)){' + source + 'break;}e=N' + k + ';';
          }
  
          // *** Descendant combinator
          // E F (E ancestor of F)
          else if ((match = selector.match(Patterns.ancestor))) {
            source = (mode ? '' : FILTER.replace(/@/g, k)) + source;
            source = 'var N' + k + '=e;while(e&&e!==h&&e!==g&&(e=e.parentNode)){' + source + '}e=N' + k + ';';
          }
  
          // *** Structural pseudo-classes
          // :root, :empty,
          // :first-child, :last-child, :only-child,
          // :first-of-type, :last-of-type, :only-of-type,
          // :nth-child(), :nth-last-child(), :nth-of-type(), :nth-last-of-type()
          else if ((match = selector.match(Patterns.spseudos)) && match[1]) {
  
            switch (match[1]) {
              case 'root':
                // element root of the document
                if (match[3]) {
                  source = 'if(e===h||s.contains(h,e)){' + source + '}';
                } else {
                  source = 'if(e===h){' + source + '}';
                }
                break;
  
              case 'empty':
                // element that has no children
                source = 'if(s.isEmpty(e)){' + source + '}';
                break;
  
              default:
                if (match[1] && match[2]) {
                  if (match[2] == 'n') {
                    source = 'if(e!==h){' + source + '}';
                    break;
                  } else if (match[2] == 'even') {
                    a = 2;
                    b = 0;
                  } else if (match[2] == 'odd') {
                    a = 2;
                    b = 1;
                  } else {
                    // assumes correct "an+b" format, "b" before "a" to keep "n" values
                    b = ((n = match[2].match(/(-?\d+)$/)) ? global.parseInt(n[1], 10) : 0);
                    a = ((n = match[2].match(/(-?\d*)n/i)) ? global.parseInt(n[1], 10) : 0);
                    if (n && n[1] == '-') a = -1;
                  }
  
                  // build test expression out of structural pseudo (an+b) parameters
                  // see here: http://www.w3.org/TR/css3-selectors/#nth-child-pseudo
                  test = a > 1 ?
                    (/last/i.test(match[1])) ? '(n-(' + b + '))%' + a + '==0' :
                    'n>=' + b + '&&(n-(' + b + '))%' + a + '==0' : a < -1 ?
                    (/last/i.test(match[1])) ? '(n-(' + b + '))%' + a + '==0' :
                    'n<=' + b + '&&(n-(' + b + '))%' + a + '==0' : a=== 0 ?
                    'n==' + b :
                    (/last/i.test(match[1])) ?
                      a == -1 ? 'n>=' + b : 'n<=' + b :
                      a == -1 ? 'n<=' + b : 'n>=' + b;
  
                  // 4 cases: 1 (nth) x 4 (child, of-type, last-child, last-of-type)
                  source =
                    'if(e!==h){' +
                      'n=s[' + (/-of-type/i.test(match[1]) ? '"nthOfType"' : '"nthElement"') + ']' +
                        '(e,' + (/last/i.test(match[1]) ? 'true' : 'false') + ');' +
                      'if(' + test + '){' + source + '}' +
                    '}';
  
                } else {
                  // 6 cases: 3 (first, last, only) x 1 (child) x 2 (-of-type)
                  a = /first/i.test(match[1]) ? 'previous' : 'next';
                  n = /only/i.test(match[1]) ? 'previous' : 'next';
                  b = /first|last/i.test(match[1]);
  
                  type = /-of-type/i.test(match[1]) ? '&&n.nodeName!=e.nodeName' : '&&n.nodeName<"@"';
  
                  source = 'if(e!==h){' +
                    ( 'n=e;while((n=n.' + a + 'Sibling)' + type + ');if(!n){' + (b ? source :
                      'n=e;while((n=n.' + n + 'Sibling)' + type + ');if(!n){' + source + '}') + '}' ) + '}';
                }
                break;
            }
  
          }
  
          // *** negation, user action and target pseudo-classes
          // *** UI element states and dynamic pseudo-classes
          // CSS3 :not, :checked, :enabled, :disabled, :target
          // CSS3 :active, :hover, :focus
          // CSS3 :link, :visited
          else if ((match = selector.match(Patterns.dpseudos)) && match[1]) {
  
            switch (match[1].match(/^\w+/)[0]) {
              // CSS3 negation pseudo-class
              case 'not':
                // compile nested selectors, DO NOT pass the callback parameter
                // SIMPLENOT allow disabling complex selectors nested
                // in ':not()' pseudo-classes, breaks some test units
                expr = match[3].replace(reTrimSpaces, '');
  
                if (Config.SIMPLENOT && !reSimpleNot.test(expr)) {
                  // see above, log error but continue execution
                  emit('Negation pseudo-class only accepts simple selectors "' + selector + '"');
                  return '';
                } else {
                  if ('compatMode' in doc) {
                    source = 'if(!' + compile(expr, '', false) + '(e,s,r,d,h,g)){' + source + '}';
                  } else {
                    source = 'if(!s.match(e, "' + expr.replace(/\x22/g, '\\"') + '",g)){' + source +'}';
                  }
                }
                break;
  
              // CSS3 UI element states
              case 'checked':
                // for radio buttons checkboxes (HTML4) and options (HTML5)
                source = 'if((typeof e.form!=="undefined"&&(/^(?:radio|checkbox)$/i).test(e.type)&&e.checked)' +
                  (Config.USE_HTML5 ? '||(/^option$/i.test(e.nodeName)&&(e.selected||e.checked))' : '') +
                  '){' + source + '}';
                break;
              case 'disabled':
                // does not consider hidden input fields
                source = 'if(((typeof e.form!=="undefined"' +
                  (Config.USE_HTML5 ? '' : '&&!(/^hidden$/i).test(e.type)') +
                  ')||s.isLink(e))&&e.disabled===true){' + source + '}';
                break;
              case 'enabled':
                // does not consider hidden input fields
                source = 'if(((typeof e.form!=="undefined"' +
                  (Config.USE_HTML5 ? '' : '&&!(/^hidden$/i).test(e.type)') +
                  ')||s.isLink(e))&&e.disabled===false){' + source + '}';
                break;
  
              // CSS3 lang pseudo-class
              case 'lang':
                test = '';
                if (match[2]) test = match[2].substr(0, 2) + '-';
                source = 'do{(n=e.lang||"").toLowerCase();' +
                  'if((n==""&&h.lang=="' + match[2].toLowerCase() + '")||' +
                  '(n&&(n=="' + match[2].toLowerCase() +
                  '"||n.substr(0,3)=="' + test.toLowerCase() + '")))' +
                  '{' + source + 'break;}}while((e=e.parentNode)&&e!==g);';
                break;
  
              // CSS3 target pseudo-class
              case 'target':
                n = doc.location ? doc.location.hash : '';
                if (n) {
                  source = 'if(e.id=="' + n.slice(1) + '"){' + source + '}';
                }
                break;
  
              // CSS3 dynamic pseudo-classes
              case 'link':
                source = 'if(s.isLink(e)&&!e.visited){' + source + '}';
                break;
              case 'visited':
                source = 'if(s.isLink(e)&&e.visited){' + source + '}';
                break;
  
              // CSS3 user action pseudo-classes IE & FF3 have native support
              // these capabilities may be emulated by some event managers
              case 'active':
                if (XML_DOCUMENT) break;
                source = 'if(e===d.activeElement){' + source + '}';
                break;
              case 'hover':
                if (XML_DOCUMENT) break;
                source = 'if(e===d.hoverElement){' + source + '}';
                break;
              case 'focus':
                if (XML_DOCUMENT) break;
                source = NATIVE_FOCUS ?
                  'if(e===d.activeElement&&d.hasFocus()&&(e.type||e.href||typeof e.tabIndex=="number")){' + source + '}' :
                  'if(e===d.activeElement&&(e.type||e.href)){' + source + '}';
                break;
  
              // CSS2 selected pseudo-classes, not part of current CSS3 drafts
              // the 'selected' property is only available for option elements
              case 'selected':
                // fix Safari selectedIndex property bug
                expr = BUGGY_SELECTED ? '||(n=e.parentNode)&&n.options[n.selectedIndex]===e' : '';
                source = 'if(/^option$/i.test(e.nodeName)&&(e.selected||e.checked' + expr + ')){' + source + '}';
                break;
  
              default:
                break;
            }
  
          }
  
          else {
  
            // this is where external extensions are
            // invoked if expressions match selectors
            expr = false;
            status = false;
            for (expr in Selectors) {
              if ((match = selector.match(Selectors[expr].Expression)) && match[1]) {
                result = Selectors[expr].Callback(match, source);
                source = result.source;
                status = result.status;
                if (status) { break; }
              }
            }
  
            // if an extension fails to parse the selector
            // it must return a false boolean in "status"
            if (!status) {
              // log error but continue execution, don't throw real exceptions
              // because blocking following processes maybe is not a good idea
              emit('Unknown pseudo-class selector "' + selector + '"');
              return '';
            }
  
            if (!expr) {
              // see above, log error but continue execution
              emit('Unknown token in selector "' + selector + '"');
              return '';
            }
  
          }
  
          // error if no matches found by the pattern scan
          if (!match) {
            emit('Invalid syntax in selector "' + selector + '"');
            return '';
          }
  
          // ensure "match" is not null or empty since
          // we do not throw real DOMExceptions above
          selector = match && match[match.length - 1];
        }
  
        return source;
      },
  
    /*----------------------------- QUERY METHODS ------------------------------*/
  
    // match element with selector
    // @return boolean
    match =
      function(element, selector, from, callback) {
  
        var parts;
  
        if (!(element && element.nodeName > '@')) {
          emit('Invalid element argument');
          return false;
        } else if (typeof selector != 'string') {
          emit('Invalid selector argument');
          return false;
        } else if (from && from.nodeType == 1 && !contains(from, element)) {
          return false;
        } else if (lastContext !== from) {
          // reset context data when it changes
          // and ensure context is set to a default
          switchContext(from || (from = element.ownerDocument));
        }
  
        selector = selector.replace(reTrimSpaces, '');
  
        Config.SHORTCUTS && (selector = Dom.shortcuts(selector, element, from));
  
        if (lastMatcher != selector) {
          // process valid selector strings
          if ((parts = selector.match(reValidator)) && parts[0] == selector) {
            isSingleMatch = (parts = selector.match(reSplitGroup)).length < 2;
            // save passed selector
            lastMatcher = selector;
            lastPartsMatch = parts;
          } else {
            emit('The string "' + selector + '", is not a valid CSS selector');
            return false;
          }
        } else parts = lastPartsMatch;
  
        // compile matcher resolvers if necessary
        if (!matchResolvers[selector] || matchContexts[selector] !== from) {
          matchResolvers[selector] = compile(isSingleMatch ? [selector] : parts, '', false);
          matchContexts[selector] = from;
        }
  
        return matchResolvers[selector](element, Snapshot, [ ], doc, root, from, callback, new global.Object());
      },
  
    // select only the first element
    // matching selector (document ordered)
    first =
      function(selector, from) {
        return select(selector, from, function() { return false; })[0] || null;
      },
  
    // select elements matching selector
    // using new Query Selector API
    // or cross-browser client API
    // @return array
    select =
      function(selector, from, callback) {
  
        var i, changed, element, elements, parts, token, original = selector;
  
        if (arguments.length === 0) {
          emit('Not enough arguments');
          return [ ];
        } else if (typeof selector != 'string') {
          return [ ];
        } else if (from && !(/1|9|11/).test(from.nodeType)) {
          emit('Invalid or illegal context element');
          return [ ];
        } else if (lastContext !== from) {
          // reset context data when it changes
          // and ensure context is set to a default
          switchContext(from || (from = doc));
        }
  
        if (Config.CACHING && (elements = Dom.loadResults(original, from, doc, root))) {
          return callback ? concatCall([ ], elements, callback) : elements;
        }
  
        if (!OPERA_QSAPI && RE_SIMPLE_SELECTOR.test(selector)) {
          switch (selector.charAt(0)) {
            case '#':
              if (Config.UNIQUE_ID) {
                elements = (element = _byId(selector.slice(1), from)) ? [ element ] : [ ];
              }
              break;
            case '.':
              elements = _byClass(selector.slice(1), from);
              break;
            default:
              elements = _byTag(selector, from);
              break;
          }
        }
  
        else if (!XML_DOCUMENT && Config.USE_QSAPI &&
          !(BUGGY_QUIRKS_QSAPI && RE_CLASS.test(selector)) &&
          !RE_BUGGY_QSAPI.test(selector)) {
          try {
            elements = from.querySelectorAll(selector);
          } catch(e) { }
        }
  
        if (elements) {
          elements = callback ? concatCall([ ], elements, callback) :
            NATIVE_SLICE_PROTO ? slice.call(elements) : concatList([ ], elements);
          Config.CACHING && Dom.saveResults(original, from, doc, elements);
          return elements;
        }
  
        selector = selector.replace(reTrimSpaces, '');
  
        Config.SHORTCUTS && (selector = Dom.shortcuts(selector, from));
  
        if ((changed = lastSelector != selector)) {
          // process valid selector strings
          if ((parts = selector.match(reValidator)) && parts[0] == selector) {
            isSingleSelect = (parts = selector.match(reSplitGroup)).length < 2;
            // save passed selector
            lastSelector = selector;
            lastPartsSelect = parts;
          } else {
            emit('The string "' + selector + '", is not a valid CSS selector');
            return [ ];
          }
        } else parts = lastPartsSelect;
  
        // commas separators are treated sequentially to maintain order
        if (from.nodeType == 11) {
  
          elements = byTagRaw('*', from);
  
        } else if (!XML_DOCUMENT && isSingleSelect) {
  
          if (changed) {
            // get right most selector token
            parts = selector.match(reSplitToken);
            token = parts[parts.length - 1];
  
            // only last slice before :not rules
            lastSlice = token.split(':not')[0];
  
            // position where token was found
            lastPosition = selector.length - token.length;
          }
  
          // ID optimization RTL, to reduce number of elements to visit
          if (Config.UNIQUE_ID && (parts = lastSlice.match(Optimize.ID)) && (token = parts[1])) {
            if ((element = _byId(token, from))) {
              if (match(element, selector)) {
                callback && callback(element);
                elements = new global.Array(element);
              } else elements = new global.Array();
            }
          }
  
          // ID optimization LTR, to reduce selection context searches
          else if (Config.UNIQUE_ID && (parts = selector.match(Optimize.ID)) && (token = parts[1])) {
            if ((element = _byId(token, doc))) {
              if ('#' + token == selector) {
                callback && callback(element);
                elements = new global.Array(element);
              } else if (/[>+~]/.test(selector)) {
                from = element.parentNode;
              } else {
                from = element;
              }
            } else elements = new global.Array();
          }
  
          if (elements) {
            Config.CACHING && Dom.saveResults(original, from, doc, elements);
            return elements;
          }
  
          if (!NATIVE_GEBCN && (parts = lastSlice.match(Optimize.TAG)) && (token = parts[1])) {
            if ((elements = _byTag(token, from)).length === 0) { return [ ]; }
            selector = selector.slice(0, lastPosition) + selector.slice(lastPosition).replace(token, '*');
          }
  
          else if ((parts = lastSlice.match(Optimize.CLASS)) && (token = parts[1])) {
            if ((elements = _byClass(token, from)).length === 0) { return [ ]; }
            if (reOptimizeSelector.test(selector.charAt(selector.indexOf(token) - 1))) {
              selector = selector.slice(0, lastPosition) + selector.slice(lastPosition).replace('.' + token, '');
            } else {
              selector = selector.slice(0, lastPosition) + selector.slice(lastPosition).replace('.' + token, '*');
            }
          }
  
          else if ((parts = selector.match(Optimize.CLASS)) && (token = parts[1])) {
            if ((elements = _byClass(token, from)).length === 0) { return [ ]; }
            for (i = 0, els = new global.Array(); elements.length > i; ++i) {
              els = concatList(els, elements[i].getElementsByTagName('*'));
            }
            elements = els;
            if (reOptimizeSelector.test(selector.charAt(selector.indexOf(token) - 1))) {
              selector = selector.slice(0, lastPosition) + selector.slice(lastPosition).replace('.' + token, '');
            } else {
              selector = selector.slice(0, lastPosition) + selector.slice(lastPosition).replace('.' + token, '*');
            }
          }
  
          else if (NATIVE_GEBCN && (parts = lastSlice.match(Optimize.TAG)) && (token = parts[1])) {
            if ((elements = _byTag(token, from)).length === 0) { return [ ]; }
            selector = selector.slice(0, lastPosition) + selector.slice(lastPosition).replace(token, '*');
          }
  
        }
  
        if (!elements) {
          elements = /^(?:applet|object)$/i.test(from.nodeName) ? from.childNodes : _byTag('*', from);
        }
        // end of prefiltering pass
  
        // compile selector resolver if necessary
        if (!selectResolvers[selector] || selectContexts[selector] !== from) {
          selectResolvers[selector] = compile(isSingleSelect ? [selector] : parts, '', true);
          selectContexts[selector] = from;
        }
  
        elements = selectResolvers[selector](elements, Snapshot, [ ], doc, root, from, callback, new global.Object());
  
        Config.CACHING && Dom.saveResults(original, from, doc, elements);
  
        return elements;
      },
  
    /*-------------------------------- STORAGE ---------------------------------*/
  
    // empty function handler
    FN = function(x) { return x; },
  
    // compiled match functions returning booleans
    matchContexts = new global.Object(),
    matchResolvers = new global.Object(),
  
    // compiled select functions returning collections
    selectContexts = new global.Object(),
    selectResolvers = new global.Object(),
  
    // used to pass methods to compiled functions
    Snapshot = new global.Object({
  
      // element indexing methods
      nthElement: nthElement,
      nthOfType: nthOfType,
  
      // element inspection methods
      getAttribute: getAttribute,
      hasAttribute: hasAttribute,
  
      // element selection methods
      byClass: _byClass,
      byName: byName,
      byTag: _byTag,
      byId: _byId,
  
      // helper/check methods
      contains: contains,
      isEmpty: isEmpty,
      isLink: isLink,
  
      // selection/matching
      select: select,
      match: match
    }),
  
    Tokens = new global.Object({
      prefixes: prefixes,
      encoding: encoding,
      operators: operators,
      whitespace: whitespace,
      identifier: identifier,
      attributes: attributes,
      combinators: combinators,
      pseudoclass: pseudoclass,
      pseudoparms: pseudoparms,
      quotedvalue: quotedvalue
    });
  
    /*------------------------------- PUBLIC API -------------------------------*/
  
    // code referenced by extensions
    Dom.ACCEPT_NODE = ACCEPT_NODE;
  
    // retrieve element by id attr
    Dom.byId = byId;
  
    // retrieve elements by tag name
    Dom.byTag = byTag;
  
    // retrieve elements by name attr
    Dom.byName = byName;
  
    // retrieve elements by class name
    Dom.byClass = byClass;
  
    // read the value of the attribute
    // as was in the original HTML code
    Dom.getAttribute = getAttribute;
  
    // check for the attribute presence
    // as was in the original HTML code
    Dom.hasAttribute = hasAttribute;
  
    // element match selector, return boolean true/false
    Dom.match = match;
  
    // first element match only, return element or null
    Dom.first = first;
  
    // elements matching selector, starting from element
    Dom.select = select;
  
    // compile selector into ad-hoc javascript resolver
    Dom.compile = compile;
  
    // check that two elements are ancestor/descendant
    Dom.contains = contains;
  
    // handle selector engine configuration settings
    Dom.configure = configure;
  
    // initialize caching for each document
    Dom.setCache = FN;
  
    // load previously collected result set
    Dom.loadResults = FN;
  
    // save previously collected result set
    Dom.saveResults = FN;
  
    // handle missing context in selector strings
    Dom.shortcuts = FN;
  
    // log resolvers errors/warnings
    Dom.emit = emit;
  
    // options enabing specific engine functionality
    Dom.Config = Config;
  
    // pass methods references to compiled resolvers
    Dom.Snapshot = Snapshot;
  
    // operators descriptor
    // for attribute operators extensions
    Dom.Operators = Operators;
  
    // selectors descriptor
    // for pseudo-class selectors extensions
    Dom.Selectors = Selectors;
  
    // export string patterns
    Dom.Tokens = Tokens;
  
    // export version string
    Dom.Version = version;
  
    // add or overwrite user defined operators
    Dom.registerOperator =
      function(symbol, resolver) {
        Operators[symbol] || (Operators[symbol] = resolver);
      };
  
    // add selector patterns for user defined callbacks
    Dom.registerSelector =
      function(name, rexp, func) {
        Selectors[name] || (Selectors[name] = new global.Object({
          Expression: rexp,
          Callback: func
        }));
      };
  
    /*---------------------------------- INIT ----------------------------------*/
  
    // init context specific variables
    switchContext(doc, true);
  
  });

  provide("nwmatcher", module.exports(this));

  !function (doc, $) {
    // a bunch of this code is borrowed from Qwery so NW is a drop-in replacement
    var nw = require('nwmatcher')
      , isNode = function (el, t) {
          return el && typeof el === 'object' && (t = el.nodeType) && (t == 1 || t == 9)
        }
      , arrayLike = function (o) {
          return (typeof o === 'object' && isFinite(o.length))
        }
      , flatten = function (ar) {
          for (var r = [], i = 0, l = ar.length; i < l; ++i) arrayLike(ar[i]) ? (r = r.concat(ar[i])) : (r[r.length] = ar[i])
          return r
        }
      , uniq = function (ar) {
          var a = [], i, j
          o: for (i = 0; i < ar.length; ++i) {
            for (j = 0; j < a.length; ++j) {
              if (a[j] == ar[i]) {
                continue o
              }
            }
            a[a.length] = ar[i]
          }
          return a
        }
      , normalizeRoot = function (root) {
          if (!root) return doc
          if (typeof root == 'string') return nw.select(root)[0]
          if (!root.nodeType && arrayLike(root)) return root[0]
          return root
        }
      , select = function (selector, _root) {
          var root = normalizeRoot(_root)
          if (!root || !selector) return []
          if (selector === window || isNode(selector)) {
            return !_root || (selector !== window && isNode(root) && nw.contains(root, container)) ? [selector] : []
          }
          if (selector && arrayLike(selector)) return flatten(selector)
          return nw.select(selector, root)
        }
      , is = function (s, r) {
          var i, l
          for (i = 0, l = this.length; i < l; i++) {
            if (nw.match(this[i], s, r)) {
              return true
            }
          }
          return false
        }
  
    $._select = function (selector, root) {
      // if 'bonzo' is available at run-time use it for <element> creation
      return ($._select = (function(bonzo) {
        try {
          bonzo = require('bonzo')
          return function (selector, root) {
            return /^\s*</.test(selector) ? bonzo.create(selector, root) : select(selector, root)
          }
        } catch (e) { }
        return select
      })())(selector, root)
    }
  
    $.ender({
        // boolean, does at least one element in the collection match the given selector
        is: is
      , match: is
        // find all elements that are children of the elements in this collection matching
        // the given selector
      , find: function (s) {
          var r = [], i, l, j, k, els
          for (i = 0, l = this.length; i < l; i++) {
            els = select(s, this[i])
            for (j = 0, k = els.length; j < k; j++) r.push(els[j])
          }
          return $(uniq(r))
        }
        // add additional elements to this collection matching the given selector
      , and: function (s, r) {
          var plus = $(s, r)
          for (var i = this.length, j = 0, l = this.length + plus.length; i < l; i++, j++) {
            this[i] = plus[j]
          }
          return this
        }
    }, true)
  
    $.ender({
        // allow for NW.Dom.select(selector, root, callback), for speedy code such as
        // $.select("div", null, function ( e ) { e.style.backgroundColor = "#ffe"; });
        select: function () {
          return $(nw.select.apply(null, arguments))
        }
        // like querySelector(), return only the first match, document-order
      , first: function (selector, root) {
          return $(nw.select(selector, root, function() { return false }))
        }
        // direct access in to NW.Dom.configure(options), for use such as:
        // $.configure({ USE_QSAPI: false, VERBOSITY: false });
      , configure: function (options) {
          nw.configure(options)
        }
    })
  
  }(document, ender)
  

}();
