//SelectorйصĶDocumentElementչʵ
Native.implement([Document, Element], {
 //ݲѯʽȡӽڵ
 getElements: function(expression, nocash){
  //ֲѯʽ
  expression = expression.split(',');
  var items, local = {};
  for (var i = 0, l = expression.length; i < l; i++){
   //ݲѯƥĽڵ
   var selector = expression[i], elements = Selectors.Utils.search(this, selector, local);
   //ҵڵ㼯,תΪ
   if (i != 0 && elements.item) elements = $A(elements);
   //ӵitems
   items = (i == 0) ? elements : (items.item) ? $A(items).concat(elements) : items.concat(elements);
  }
  //Elements
  return new Elements(items, {ddup: (expression.length > 1), cash: !nocash});
 }
 
});

//SelectorйصĶElementչʵ
Element.implement({
 
 //жϵǰڵǷƥָģʽ
 match: function(selector){
  if (!selector) return true;
  //ѡͳǩid
  var tagid = Selectors.Utils.parseTagAndID(selector);
  var tag = tagid[0], id = tagid[1];
  //ǰڵ㲻ƥIDǩеһfalse
  if (!Selectors.Filters.byID(this, id) || !Selectors.Filters.byTag(this, tag)) return false;
  //ѡƥ
  var parsed = Selectors.Utils.parseSelector(selector);
  return (parsed) ? Selectors.Utils.filter(this, parsed, {}) : true;
 }
 
});

//Ϊ򵥶,ܵȴ䴦
var Selectors = {Cache: {nth: {}, parsed: {}}};

Selectors.RegExps = {
 //ѯʽжidƥģʽ
 id: (/#([\w-]+)/),
 //ѯʽжԱǩƥģʽ
 tag: (/^(\w+|\*)/),
 //ѯʽжԿģʽƥģʽ
 quick: (/^(\w+|\*)$/),
 //ѯʽжԷָƥģʽ
 splitter: (/\s*([+>~\s])\s*([a-zA-Z#.*:\[])/g),
 //ģʽ 
 combined: (/\.([\w-]+)|\[(\w+)(?:([!*^$~|]?=)["']?(.*?)["']?)?\]|:([\w-]+)(?:\(["']?(.*?)?["']?\)|$)/g)
};

//һЩĹ߷
Selectors.Utils = {

 //itemǷظ
 chk: function(item, uniques){
  if (!uniques) return true;
  //ǷصǰԪصΨһid
  var uid = $uid(item);
  //,˵лûд,Ϊtrue
  if (!uniques[uid]) return uniques[uid] = true;
  return false;
 },
 
 /*
 nthѯʱĲ
 nthʾԵnĲѯ,http://www.w3.org/TR/2005/WD-css3-selectors-20051215/#nth-child-pseudo
 */
 parseNthArgument: function(argument){
  //ͬѯ,ػĽ,Ĳ,ÿ¼
  if (Selectors.Cache.nth[argument]) return Selectors.Cache.nth[argument];
  //nthѯĲ
  var parsed = argument.match(/^([+-]?\d*)?([a-z]+)?([+-]?\d*)?$/);
  if (!parsed) return false;
  //һֵ
  var inta = parseInt(parsed[1]);
  //intaûֵʱaĬΪ1
  var a = (inta || inta === 0) ? inta : 1;
  //
  var special = parsed[2] || false;
  //ڶֵ
  var b = parseInt(parsed[3]) || 0;
  if (a != 0){
   b--;
   while (b < 1) b += a;
   while (b >= a) b -= a;
  } else {
   a = b;
   special = 'index';
  }
  switch (special){
   case 'n': parsed = {a: a, b: b, special: 'n'}; break;
   case 'odd': parsed = {a: 2, b: 0, special: 'n'}; break;
   case 'even': parsed =  {a: 2, b: 1, special: 'n'}; break;
   case 'first': parsed = {a: 0, special: 'index'}; break;
   case 'last': parsed = {special: 'last-child'}; break;
   case 'only': parsed = {special: 'only-child'}; break;
   default: parsed = {a: (a - 1), special: 'index'};
  }
  
  return Selectors.Cache.nth[argument] = parsed;
 },
 
 //ѡ
 parseSelector: function(selector){
  //ͬĻ
  if (Selectors.Cache.parsed[selector]) return Selectors.Cache.parsed[selector];
  var m, parsed = {classes: [], pseudos: [], attributes: []};
  //עexecmatch
  while ((m = Selectors.RegExps.combined.exec(selector))){
   //ƥΪ:CSS,,Բ,ֵ,α,α
   var cn = m[1], an = m[2], ao = m[3], av = m[4], pn = m[5], pa = m[6];
   if (cn){
    //ӵCSS
    parsed.classes.push(cn);
   } else if (pn){
    var parser = Selectors.Pseudo.get(pn);
    //α,paΪα
    if (parser) parsed.pseudos.push({parser: parser, argument: pa});
    //ΪԱȽϵֵ
    else parsed.attributes.push({name: pn, operator: '=', value: pa});
   } else if (an){
    //ԱȽ
    parsed.attributes.push({name: an, operator: ao, value: av});
   }
  }
  //ѡвCSSĲѯ,ɾclasses
  if (!parsed.classes.length) delete parsed.classes;
  //ѡвElementԵĲѯ,ɾattributes
  if (!parsed.attributes.length) delete parsed.attributes;
  //ѡвαĲѯ,ɾpseudos
  if (!parsed.pseudos.length) delete parsed.pseudos;
  //߶,null
  if (!parsed.classes && !parsed.attributes && !parsed.pseudos) parsed = null;
  //
  return Selectors.Cache.parsed[selector] = parsed;
 },
 
 //Ͳѯʽеıǩid
 parseTagAndID: function(selector){
  //ƥǩ
  var tag = selector.match(Selectors.RegExps.tag);
  //ƥid
  var id = selector.match(Selectors.RegExps.id);
  //ûƥǩʱǺ'*',ûƥidʱfalse
  return [(tag) ? tag[1] : '*', (id) ? id[1] : false];
 },
 
 filter: function(item, parsed, local){
  var i;
  //CSSƥ
  if (parsed.classes){
   for (i = parsed.classes.length; i--; i){
    var cn = parsed.classes[i];
    if (!Selectors.Filters.byClass(item, cn)) return false;
   }
  }
  //ƥ
  if (parsed.attributes){
   for (i = parsed.attributes.length; i--; i){
    var att = parsed.attributes[i];
    if (!Selectors.Filters.byAttribute(item, att.name, att.operator, att.value)) return false;
   }
  }
  //αƥ
  if (parsed.pseudos){
   for (i = parsed.pseudos.length; i--; i){
    var psd = parsed.pseudos[i];
    if (!Selectors.Filters.byPseudo(item, psd.parser, psd.argument, local)) return false;
   }
  }
  return true;
 },
 
 //ݱǩIDElement
 getByTagAndID: function(ctx, tag, id){
  //ṩid
  if (id){
   //ȸidҵElement
   var item = ctx.getElementById(id, true);
   //ٸݱǩ
   return (item && Selectors.Filters.byTag(item, tag)) ? [item] : [];
  } else {
   //ṩidʱֱӲǩElement
   return ctx.getElementsByTagName(tag);
  }
 },
 
 //
 search: function(self, expression, local){
  //ѯʽİָָĲ
  var splitters = [];
  /*
  ݷָиѯʽ,expressionвܰ':)'
  滻m0ƥַ,m1Ϊ+>~Ϳհַ֮,m2Ϊӱʽ
  */
  var selectors = expression.trim().replace(Selectors.RegExps.splitter, function(m0, m1, m2){
   splitters.push(m1);
   return ':)' + m2;
  }).split(':)');
  
  var items, match, filtered, item;
  
  for (var i = 0, l = selectors.length; i < l; i++){
   
   var selector = selectors[i];
   
   //ƥquickģʽζſʹñǩƥ
   if (i == 0 && Selectors.RegExps.quick.test(selector)){
    items = self.getElementsByTagName(selector);
    continue;
   }
   
   var splitter = splitters[i - 1];
   
   //ͳǩid
   var tagid = Selectors.Utils.parseTagAndID(selector);
   var tag = tagid[0], id = tagid[1];

   if (i == 0){
    //λʱĴ
    items = Selectors.Utils.getByTagAndID(self, tag, id);
   } else {
    //Ȱǩ,idͲƥ
    var uniques = {}, found = [];
    for (var j = 0, k = items.length; j < k; j++) found = Selectors.Getters[splitter](found, items[j], tag, id, uniques);
    items = found;
   }
   
   //ѡ
   var parsed = Selectors.Utils.parseSelector(selector);
   
   if (parsed){
    filtered = [];
    for (var m = 0, n = items.length; m < n; m++){
     item = items[m];
     //ƥ
     if (Selectors.Utils.filter(item, parsed, local)) filtered.push(item);
    }
    items = filtered;
   }
   
  }
  
  return items;
  
 }
 
};

