/*
HTML Elementİװ
*/
var Element = new Native({

 //Ϊ$typeṩ׼
 name: 'Element',

 //װչԭ
 legacy: window.Element,

 //ʼʹDocumentnewElement
 initialize: function(tag, props){

  //ȿûHTML Element
  var konstructor = Element.Constructors.get(tag);

  //HTML Elementر
  if (konstructor) return konstructor(props);

  //ṩǩݱǩHTML Element
  if (typeof tag == 'string') return document.newElement(tag, props);

  //
  return $(tag).set(props);
 },

 //ִк,ÿΪElementһչ,ElementsҲͬʱʵ
 afterImplement: function(key, value){
  if (!Array[key]) Elements.implement(key, Elements.multi(key));
  Element.Prototype[key] = value;
 }

});

Element.Prototype = {$family: {name: 'element'}};

Element.Constructors = new Hash;

 

//ͬʱElementDocumentչ
Native.implement([Element, Document], {

 //ѡѡһElement
 getElement: function(selector, notrash){
  return $(this.getElements(selector, true)[0] || null, notrash);
 },

 //ѡѡElement
 getElements: function(tags, nocash){
  tags = tags.split(',');
  var elements = [];
  var ddup = (tags.length > 1);
  tags.each(function(tag){
   var partial = this.getElementsByTagName(tag.trim());
   (ddup) ? elements.extend(partial) : elements = partial;
  }, this);
  return new Elements(elements, {ddup: ddup, cash: !nocash});
 }

});

//ڻȡڵǰElementµʱ󣬲οFx.TweenDrag
Element.Storage = {

 get: function(uid){
  return (this[uid] || (this[uid] = {}));
 }

};

//൱ڵǰElementĸλõDOM
Element.Inserters = new Hash({

 //contextָElement֮ǰeleemnt
 before: function(context, element){
  if (element.parentNode) element.parentNode.insertBefore(context, element);
 },

 //contextָElement֮element
 after: function(context, element){
  if (!element.parentNode) return;
  var next = element.nextSibling;
  (next) ? element.parentNode.insertBefore(context, next) : element.parentNode.appendChild(context);
 },

 //contextָElementeleemnt
 bottom: function(context, element){
  element.appendChild(context);
 },

 //contextָElementλeleemnt
 top: function(context, element){
  var first = element.firstChild;
  (first) ? element.insertBefore(context, first) : element.appendChild(context);
 }

});

//
Element.Inserters.inside = Element.Inserters.bottom;

//injectTop/grabTop֮Ŀݷʽ
Element.Inserters.each(function(value, key){

 var Key = key.capitalize();

 //injectTop,injectBefore,injectAfter,injectBottom
 Element.implement('inject' + Key, function(el){
  value(this, $(el, true));
  return this;
 });

 //grabTop,grabBefore,grabAfter,grabBottom
 Element.implement('grab' + Key, function(el){
  value($(el, true), this);
  return this;
 });

});

Element.implement({

 //ǰElementDocument
 getDocument: function(){
  return this.ownerDocument;
 },

 //ǰElementWindow
 getWindow: function(){
  return this.ownerDocument.getWindow();
 },

 //IDҵǰElementµĶ
 getElementById: function(id, nocash){
  var el = this.ownerDocument.getElementById(id);
  if (!el) return null;
  for (var parent = el.parentNode; parent != this; parent = parent.parentNode){
   if (!parent) return null;
  }
  return $.element(el, nocash);
 },

 //ܵsetter
 set: function(prop, value){
  switch ($type(prop)){
   case 'object':
    for (var p in prop) this.set(p, prop[p]);
    break;
   case 'string':
    var property = Element.Properties.get(prop);
    (property && property.set) ? property.set.apply(this, Array.slice(arguments, 1)) : this.setProperty(prop, value);
  }
  return this;
 },

 //ܵgetter
 get: function(prop){
  var property = Element.Properties.get(prop);
  return (property && property.get) ? property.get.apply(this, Array.slice(arguments, 1)) : this.getProperty(prop);
 },

 //ܵeraser
 erase: function(prop){
  var property = Element.Properties.get(prop);
  (property && property.erase) ? property.erase.apply(this, Array.slice(arguments, 1)) : this.removeProperty(prop);
  return this;
 },

 //жϵǰElementǷƥָǩ
 match: function(tag){
  return (!tag || Element.get(this, 'tag') == tag);
 },

 //whereָλãǰElementע뵽el
 inject: function(el, where){
  Element.Inserters.get(where || 'bottom')(this, $(el, true));
  return this;
 },

 //whereָλãʹǰElementΪelĸElement
 wraps: function(el, where){
  el = $(el, true);
  return this.replaces(el).grab(el, where);
 },

 //inject෴
 grab: function(el, where){
  Element.Inserters.get(where || 'bottom')($(el, true), this);
  return this;
 },

 //׷ı
 appendText: function(text, where){
  return this.grab(this.getDocument().newTextNode(text), where);
 },

 //׷ӵǰElement
 adopt: function(){
  Array.flatten(arguments).each(function(element){
   element = $(element, true);
   if (element) this.appendChild(element);
  }, this);
  return this;
 },

 //DOMڵɾ
 dispose: function(){
  return (this.parentNode) ? this.parentNode.removeChild(this) : this;
 },

 //ӵǰElement¡µElementcontentsָǷӽڵ㣬keepidָǷid
 clone: function(contents, keepid){
  switch ($type(this)){
   case 'element':
    var attributes = {};
    for (var j = 0, l = this.attributes.length; j < l; j++){
     var attribute = this.attributes[j], key = attribute.nodeName.toLowerCase();
     var value = (key == 'style' && this.style) ? this.style.cssText : attribute.nodeValue;
     if (!$chk(value) || key == 'uid' || (key == 'id' && !keepid)) continue;
     if (value != 'inherit' && ['string', 'number'].contains($type(value))) attributes[key] = value;
    }
    var element = new Element(this.nodeName.toLowerCase(), attributes);
    if (contents !== false){
     for (var i = 0, k = this.childNodes.length; i < k; i++){
      var child = Element.clone(this.childNodes[i], true, keepid);
      if (child) element.grab(child);
     }
    }
    return element;
   //ǰıڵ
   case 'textnode': return document.newTextNode(this.nodeValue);
  }
  return null;
 },

 //滻ڵ
 replaces: function(el){
  el = $(el, true);
  el.parentNode.replaceChild(this, el);
  return this;
 },

 //Ƿʹָcss
 hasClass: function(className){
  return this.className.contains(className, ' ');
 },

 //ָcss
 addClass: function(className){
  if (!this.hasClass(className)) this.className = (this.className + ' ' + className).clean();
  return this;
 },

 //Ƴָcss
 removeClass: function(className){
  this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1').clean();
  return this;
 },

 //лָcss
 toggleClass: function(className){
  return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
 },

 //ȡʱʽ
 getComputedStyle: function(property){
  //IE
  if (this.currentStyle) return this.currentStyle[property.camelCase()];
  //IE
  var computed = this.getWindow().getComputedStyle(this, null);
  return (computed) ? computed.getPropertyValue([property.hyphenate()]) : null;
 },

 //յǰElementڲڵɾڴͷ
 empty: function(){
  $A(this.childNodes).each(function(node){
   Element.empty(node);
   this.removeChild(node);
   memfree(node);
  }, this);
  return this;
 },

 //ٵǰElementɾйصһ
 destroy: function(){
  memfree(this.empty().dispose());
  return null;
 },

 //ȡѡе<select multi>Ч
 getSelected: function(){
  return $A(this.options).filter(function(option){
   return option.selected;
  });
 },

 //ǰElementڵıתΪѯʽAjaxgetʽύ
 toQueryString: function(){
  var queryString = [];
  this.getElements('input, select, textarea').each(function(el){
   if (!el.name || el.disabled) return;
   var value = (el.tagName.toLowerCase() == 'select') ? Element.getSelected(el).map(function(opt){
    return opt.value;
   }) : ((el.type == 'radio' || el.type == 'checkbox') && !el.checked) ? null : el.value;
   $splat(value).each(function(val){
    if (val) queryString.push(el.name + '=' + encodeURIComponent(val));
   });
  });
  return queryString.join('&');
 },

 //ȡֵԶ
 getProperty: function(attribute){
  var EA = Element.Attributes, key = EA.Props[attribute];
  var value = (key) ? this[key] : this.getAttribute(attribute, 2);
  return (EA.Bools[attribute]) ? !!value : (key) ? value : value || null;
 },

 //бȡӦֵobjecctʽؽ
 getProperties: function(){
  var args = $A(arguments);
  return args.map(function(attr){
   return this.getProperty(attr);
  }, this).associate(args);
 },

 //
 setProperty: function(attribute, value){
  var EA = Element.Attributes, key = EA.Props[attribute], hasValue = $defined(value);
  if (key && EA.Bools[attribute]) value = (value || !hasValue) ? true : false;
  else if (!hasValue) return this.removeProperty(attribute);
  (key) ? this[key] = value : this.setAttribute(attribute, value);
  return this;
 },

 //
 setProperties: function(attributes){
  for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
  return this;
 },

 //Ƴ
 removeProperty: function(attribute){
  var EA = Element.Attributes, key = EA.Props[attribute], isBool = (key && EA.Bools[attribute]);
  (key) ? this[key] = (isBool) ? false : '' : this.removeAttribute(attribute);
  return this;
 },

 //Ƴ
 removeProperties: function(){
  Array.each(arguments, this.removeProperty, this);
  return this;
 }

});

