text: /**
   * @module  selector
   * @author  lifesinger@gmail.com
   */
  KISSY.add('selector', function(S, undefined) {
  
      var doc = document, DOM = S.DOM,
          SPACE = ' ', ANY = '*',
          GET_DOM_NODE = 'getDOMNode', GET_DOM_NODES = GET_DOM_NODE + 's',
          REG_ID = /^#[\w-]+$/,
          REG_QUERY = /^(?:#([\w-]+))?\s*([\w-]+|\*)?\.?([\w-]+)?$/;
  
      /**
       * Retrieves an Array of HTMLElement based on the given CSS selector.
       * @param {string} selector
       * @param {string|HTMLElement} context An #id string or a HTMLElement used as context
       * @return {Array} The array of found HTMLElement
       */
      function query(selector, context) {
          var match, t, ret = [], id, tag, cls;
          context = tuneContext(context);
  
          // Ref: http://ejohn.org/blog/selectors-that-people-actually-use/
          // 考虑 2/8 原则，仅支持以下选择器：
          // #id
          // tag
          // .cls
          // #id tag
          // #id .cls
          // tag.cls
          // #id tag.cls
          // 注 1：REG_QUERY 还会匹配 #id.cls
          // 注 2：tag 可以为 * 字符
          // 返回值为数组
          // 选择器不支持时，抛出异常
  
          // selector 为字符串是最常见的情况，优先考虑
          // 注：空白字符串无需判断，运行下去自动能返回空数组
          if (S.isString(selector)) {
              selector = S.trim(selector);
  
              // selector 为 #id 是最常见的情况，特殊优化处理
              if (REG_ID.test(selector)) {
                  t = getElementById(selector.slice(1), context);
                  if (t) ret = [t]; // #id 无效时，返回空数组
              }
              // selector 为支持列表中的其它 6 种
              else if ((match = REG_QUERY.exec(selector))) {
                  // 获取匹配出的信息
                  id = match[1];
                  tag = match[2];
                  cls = match[3];
  
                  if ((context = id ? getElementById(id, context) : context)) {
                      // #id .cls | #id tag.cls | .cls | tag.cls
                      if (cls) {
                          if (!id || selector.indexOf(SPACE) !== -1) { // 排除 #id.cls
                              ret = getElementsByClassName(cls, tag, context);
                          }
                          // 处理 #id.cls
                          else {
                              t = getElementById(id, context);
                              if(t && DOM.hasClass(t, cls)) {
                                  ret = [t];
                              }
                          }
                      }
                      // #id tag | tag
                      else if (tag) { // 排除空白字符串
                          ret = getElementsByTagName(tag, context);
                      }
                  }
              }
              // 采用外部选择器
              else if(S.ExternalSelector) {
                  return S.ExternalSelector(selector, context);
              }
              // 依旧不支持，抛异常
              else {
                  error(selector);
              }
          }
          // 传入的 selector 是 KISSY.Node/NodeList. 始终返回原生 DOM Node
          else if(selector && (selector[GET_DOM_NODE] || selector[GET_DOM_NODES])) {
              ret = selector[GET_DOM_NODE] ? [selector[GET_DOM_NODE]()] : selector[GET_DOM_NODES]();
          }
          // 传入的 selector 是 Node
          else if (selector && selector.nodeType) {
              ret = [selector];
          }
          // 传入的 selector 是 NodeList 或已是 Array
          else if (selector && (S.isArray(selector) || selector.item)) {
              ret = selector;
          }
          // 传入的 selector 是其它值时，返回空数组
  
          // 将 NodeList 转换为普通数组
          if(ret.item) {
              ret = S.makeArray(ret);
          }
  
          return ret;
      }
  
      // 调整 context 为合理值
      function tuneContext(context) {
          // 1). context 为 undefined 是最常见的情况，优先考虑
          if (context === undefined) {
              context = doc;
          }
          // 2). context 的第二使用场景是传入 #id
          else if (S.isString(context) && REG_ID.test(context)) {
              context = getElementById(context.slice(1), doc);
              // 注：#id 可能无效，这时获取的 context 为 null
          }
          // 3). context 还可以传入 HTMLElement, 此时无需处理
          // 4). 经历 1 - 3, 如果 context 还不是 HTMLElement, 赋值为 null
          else if (context && context.nodeType !== 1 && context.nodeType !== 9) {
              context = null;
          }
          return context;
      }
  
      // query #id
      function getElementById(id, context) {
          if(context.nodeType !== 9) {
              context = context.ownerDocument;
          }
          return context.getElementById(id);
      }
  
      // query tag
      function getElementsByTagName(tag, context) {
          return context.getElementsByTagName(tag);
      }
      (function() {
          // Check to see if the browser returns only elements
          // when doing getElementsByTagName('*')
  
          // Create a fake element
          var div = doc.createElement('div');
          div.appendChild(doc.createComment(''));
  
          // Make sure no comments are found
          if (div.getElementsByTagName(ANY).length > 0) {
              getElementsByTagName = function(tag, context) {
                  var ret = context.getElementsByTagName(tag);
  
                  if (tag === ANY) {
                      var t = [], i = 0, j = 0, node;
                      while ((node = ret[i++])) {
                          // Filter out possible comments
                          if (node.nodeType === 1) {
                              t[j++] = node;
                          }
                      }
                      ret = t;
                  }
                  return ret;
              };
          }
      })();
  
      // query .cls
      function getElementsByClassName(cls, tag, context) {
          var els = context.getElementsByClassName(cls),
              ret = els, i = 0, j = 0, len = els.length, el;
  
          if (tag && tag !== ANY) {
              ret = [];
              tag = tag.toUpperCase();
              for (; i < len; ++i) {
                  el = els[i];
                  if (el.tagName === tag) {
                      ret[j++] = el;
                  }
              }
          }
          return ret;
      }
      if (!doc.getElementsByClassName) {
          // 降级使用 querySelectorAll
          if (doc.querySelectorAll) {
              getElementsByClassName = function(cls, tag, context) {
                  return context.querySelectorAll((tag ? tag : '') + '.' + cls);
              }
          }
          // 降级到普通方法
          else {
              getElementsByClassName = function(cls, tag, context) {
                  var els = context.getElementsByTagName(tag || ANY),
                      ret = [], i = 0, j = 0, len = els.length, el, t;
  
                  cls = SPACE + cls + SPACE;
                  for (; i < len; ++i) {
                      el = els[i];
                      t = el.className;
                      if (t && (SPACE + t + SPACE).indexOf(cls) > -1) {
                          ret[j++] = el;
                      }
                  }
                  return ret;
              }
          }
      }
  
      // throw exception
      function error(msg) {
          S.error('Unsupported selector: ' + msg);
      }
  
      // public api
      S.query = query;
      S.get = function(selector, context) {
          return query(selector, context)[0] || null;
      };
  
      S.mix(DOM, {
  
          query: query,
  
          get: S.get,
  
          /**
           * Filters an array of elements to only include matches of a filter.
           * @param filter selector or fn
           */
          filter: function(selector, filter) {
              var elems = query(selector), match, tag, cls, ret = [];
  
              // 默认仅支持最简单的 tag.cls 形式
              if (S.isString(filter) && (match = REG_QUERY.exec(filter)) && !match[1]) {
                  tag = match[2];
                  cls = match[3];
                  filter = function(elem) {
                      return !((tag && elem.tagName !== tag.toUpperCase()) || (cls && !DOM.hasClass(elem, cls)));
                  }
              }
  
              if (S.isFunction(filter)) {
                  ret = S.filter(elems, filter);
              }
              // 其它复杂 filter, 采用外部选择器
              else if (filter && S.ExternalSelector) {
                  ret = S.ExternalSelector._filter(selector, filter);
              }
              // filter 为空或不支持的 selector
              else {
                  error(filter);
              }
  
              return ret;
          },
  
          /**
           * Returns true if the passed element(s) match the passed filter
           */
          test: function(selector, filter) {
              var elems = query(selector);
              return DOM.filter(elems, filter).length === elems.length;
          }
  
      });
  });
  
  /**
   * NOTES:
   *
   * 2010.01
   *  - 对 reg exec 的结果(id, tag, className)做 cache, 发现对性能影响很小，去掉。
   *  - getElementById 使用频率最高，使用直达通道优化。
   *  - getElementsByClassName 性能优于 querySelectorAll, 但 IE 系列不支持。
   *  - instanceof 对性能有影响。
   *  - 内部方法的参数，比如 cls, context 等的异常情况，已经在 query 方法中有保证，无需冗余“防卫”。
   *  - query 方法中的条件判断考虑了“频率优先”原则。最有可能出现的情况放在前面。
   *  - Array 的 push 方法可以用 j++ 来替代，性能有提升。
   *  - 返回值策略和 Sizzle 一致，正常时，返回数组；其它所有情况，返回空数组。
   *
   *  - 从压缩角度考虑，还可以将 getElmentsByTagName 和 getElementsByClassName 定义为常量，
   *    不过感觉这样做太“压缩控”，还是保留不替换的好。
   *
   *  - 调整 getElementsByClassName 的降级写法，性能最差的放最后。
   *
   * 2010.02
   *  - 添加对分组选择器的支持（主要参考 Sizzle 的代码，代去除了对非 Grade A 级浏览器的支持）
   *
   * 2010.03
   *  - 基于原生 dom 的两个 api: S.query 返回数组; S.get 返回第一个。
   *    基于 Node 的 api: S.one, 在 Node 中实现。
   *    基于 NodeList 的 api: S.all, 在 NodeList 中实现。
   *    通过 api 的分层，同时满足初级用户和高级用户的需求。
   *
   * 2010.05
   *  - 去掉给 S.query 返回值默认添加的 each 方法，保持纯净。
   *  - 对于不支持的 selector, 采用外部耦合进来的 Selector.
   *
   * 2010.06
   *  - 增加 filter 和 test 方法
   * 
   * 2010.07
   *  - 取消对 , 分组的支持，group 直接用 Sizzle
   *
   * Bugs:
   *  - S.query('#test-data *') 等带 * 号的选择器，在 IE6 下返回的值不对。jQuery 等类库也有此 bug, 诡异。
   *
   * References:
   *  - http://ejohn.org/blog/selectors-that-people-actually-use/
   *  - http://ejohn.org/blog/thoughts-on-queryselectorall/
   *  - MDC: querySelector, querySelectorAll, getElementsByClassName
   *  - Sizzle: http://github.com/jeresig/sizzle
   *  - MINI: http://james.padolsey.com/javascript/mini/
   *  - Peppy: http://jamesdonaghue.com/?p=40
   *  - Sly: http://github.com/digitarald/sly
   *  - XPath, TreeWalker：http://www.cnblogs.com/rubylouvre/archive/2009/07/24/1529640.html
   *
   *  - http://www.quirksmode.org/blog/archives/2006/01/contains_for_mo.html
   *  - http://www.quirksmode.org/dom/getElementsByTagNames.html
   *  - http://ejohn.org/blog/comparing-document-position/
   *  - http://github.com/jeresig/sizzle/blob/master/sizzle.js
   */
  