//Ϸ
//ʾ:http://demos.mootools.net/Sortables
var Sortables = new Class({

 //̳EventsOptions,UIص
 Implements: [Events, Options],

 options: {/*
  //¼
  onSort: $empty,
  //ʼ¼
  onStart: $empty,
  //¼
  onComplete: $empty,*/
  snap: 4,
  //͸
  opacity: 1,
  //Ƿ
  clone: false,
  //Ƿʹα䶯
  revert: false,
  //϶
  handle: false,
  //Լ
  constrain: false
 },

 //캯
 initialize: function(lists, options){
  //ò
  this.setOptions(options);
  this.elements = [];
  this.lists = [];
  this.idle = true;
  
  //бԵ,ҲǷ
  this.addLists($$($(lists) || lists));
  //¡,Ҫζ
  if (!this.options.clone) this.options.revert = false;
  //ָζЧ
  if (this.options.revert) this.effect = new Fx.Morph(null, $merge({duration: 250, link: 'cancel'}, this.options.revert));
 },

 //¼
 attach: function(){
  this.addLists(this.lists);
  return this;
 },

 //Ƴ¼
 detach: function(){
  this.lists = this.removeLists(this.lists);
  return this;
 },

 //б
 addItems: function(){
  //άٱ
  Array.flatten(arguments).each(function(element){
   //ӵ
   this.elements.push(element);
   //бstart
   var start = element.retrieve('sortables:start', this.start.bindWithEvent(this, element));

   //ָ϶,ӵǰблȡ,򽫵ǰбΪ
   //Ȼ갴¼,start
   (this.options.handle ? element.getElement(this.options.handle) || element : element).addEvent('mousedown', start);
  }, this);
  return this;
 },

 //б
 addLists: function(){
  //άٱ
  Array.flatten(arguments).each(function(list){
   this.lists.push(list);
   //ӵǰбµ
   this.addItems(list.getChildren());
  }, this);
  return this;
 },

 //Ƴб
 removeItems: function(){
  //ڷص
  var elements = [];
  //άٱ
  Array.flatten(arguments).each(function(element){
   //ӵ
   elements.push(element);
   //ɾ
   this.elements.erase(element);
   //ȡе¼󶨷
   var start = element.retrieve('sortables:start');
   //бʱж,ҵƳ¼
   (this.options.handle ? element.getElement(this.options.handle) || element : element).removeEvent('mousedown', start);
  }, this);
  //Ƴб
  return $$(elements);
 },

 //Ƴб
 removeLists: function(){
  //ڷص
  var lists = [];
  //άٱ
  Array.flatten(arguments).each(function(list){
   //ӵ
   lists.push(list);
   //ɾ
   this.lists.erase(list);
   //Ƴǰбб,ΪremoveItemsԶά,ԿֱӴ
   this.removeItems(list.getChildren());
  }, this);
  //Ƴб
  return $$(lists);
 },

 //ȡƵĶ
 getClone: function(event, element){
  //ָ,´div뵽body
  if (!this.options.clone) return new Element('div').inject(document.body);
  //Ϊ,޸õָ򲢴ָ
  if ($type(this.options.clone) == 'function') return this.options.clone.call(this, event, element, this.list);
  //ʣµָΪƵ
  //Ƶǰб޸ʽ
  return element.clone(true).setStyles({
   'margin': '0px',
   'position': 'absolute',
   'visibility': 'hidden',
   'width': element.getStyle('width')
  }).inject(this.list).position(element.getPosition(element.offsetParent));
 },

 //ȡɷĶ
 getDroppables: function(){
  //ȡǰбµб
  var droppables = this.list.getChildren();
  //ûԼ,бҲΪɷ
  if (!this.options.constrain) droppables = this.lists.concat(droppables).erase(this.list);
  //ӿɷ󼯺ųǰ¡
  return droppables.erase(this.clone).erase(this.element);
 },

 //
 insert: function(dragging, element){
  //ָλ
  var where = 'inside';
  if (this.lists.contains(element)){
   this.list = element;
   this.drag.droppables = this.getDroppables();
  } else {
   where = this.element.getAllPrevious().contains(element) ? 'before' : 'after';
  }
  this.element.inject(element, where);
  this.fireEvent('onSort', [this.element, this.clone]);
 },

 //ʼ
 start: function(event, element){
  //ǰ״̬,˳
  if (!this.idle) return;
  //״̬ʶ
  this.idle = false;
  //ǰ
  this.element = element;
  //ȡǰ͸
  this.opacity = element.get('opacity');
  //ȡǰڵб
  this.list = element.getParent();
  //ȡǰĿ¡
  this.clone = this.getClone(event, element);

  //ϷŶ
  this.drag = new Drag.Move(this.clone, {
   snap: this.options.snap,
   //ϷŷΧ,ָconstrainΪtrueŻȡǰڵб
   container: this.options.constrain && this.element.getParent(),
   //ȡɷĶ
   droppables: this.getDroppables(),
   //onSnap¼
   onSnap: function(){
    //ֹͣ¼ðݼֵ
    event.stop();
    //ʾ¡
    this.clone.setStyle('visibility', 'visible');
    //͸
    this.element.set('opacity', this.options.opacity || 0);
    //onStart¼
    this.fireEvent('onStart', [this.element, this.clone]);
   }.bind(this),
   //϶¼
   onEnter: this.insert.bind(this),
   //ȡ϶¼
   onCancel: this.reset.bind(this),
   //϶¼
   onComplete: this.end.bind(this)
  });
  //¡󵽵ǰǰ
  this.clone.inject(this.element, 'before');
  //ʼ϶
  this.drag.start(event);
 },

 //϶
 end: function(){
  //Ƴ¼
  this.drag.detach();
  //ָ͸
  this.element.set('opacity', this.opacity);
  //ʹα䶯(Ҫγߴλϵ)
  if (this.effect){
   //ȡߴ
   var dim = this.element.getStyles('width', 'height');
   //λ
   var pos = this.clone.computePosition(this.element.getPosition(this.clone.offsetParent));
   //ǰ¡Ϊα
   this.effect.element = this.clone;
   //ʼЧ
   this.effect.start({
    top: pos.top,
    left: pos.left,
    width: dim.width,
    height: dim.height,
    opacity: 0.25
   //ɺĺ
   }).chain(this.reset.bind(this));
  } else {
   //
   this.reset();
  }
 },

 //
 reset: function(){
  //״̬
  this.idle = true;
  //¡
  this.clone.destroy();
  //onComplete¼
  this.fireEvent('onComplete', this.element);
 },

 //л
 serialize: function(){
  //ʹArray.link
  var params = Array.link(arguments, {modifier: Function.type, index: $defined});
  //б
  var serial = this.lists.map(function(list){
   //бб,дк,ʹøúб,ĬϷid
   return list.getChildren().map(params.modifier || function(element){
    //ȡбid
    return element.get('id');
   }, this);
  }, this);
  
  //д͵ֵ
  var index = params.index;
  //бֻһ,ֵΪ0
  if (this.lists.length == 1) index = 0;
  //ڲֵںϷΧ,ֵָбл,򷵻бл
  return $chk(index) && index >= 0 && index < this.lists.length ? serial[index] : serial;
 }

});

