//UI,϶/Ż
var Drag = new Class({

 //̳EventsOptions,UIĻҪ:¼ּ֧Կѡ
 Implements: [Events, Options],

 options: {/*
  //ʼ϶ǰ¼
  onBeforeStart: $empty,
  //ʼ϶¼
  onStart: $empty,
  //϶¼
  onDrag: $empty,
  //ȡ϶¼
  onCancel: $empty,
  //϶¼
  onComplete: $empty,*/
  //Զ
  snap: 6,
  //λ
  unit: 'px',
  //С
  grid: false,
  //ָǷCSS
  style: true,
  //϶Χ
  limit: false,
  //϶ľ
  handle: false,
  //ת
  invert: false,
  //϶ʱıʽ,x,yΪlefttopʱƶ,ΪwidthheightʱΪ
  modifiers: {x: 'left', y: 'top'}
 },

 //캯
 initialize: function(){
  //Array.linkԲ,ʵöλ޹
  var params = Array.link(arguments, {'options': Object.type, 'element': $defined});
  //϶õĶ
  this.element = $(params.element);
  //϶öĵ
  this.document = this.element.getDocument();
  //Optionsķ,ʵֿѡ
  this.setOptions(params.options || {});
  //
  var htype = $type(this.options.handle);
  //϶ľ,ֶ֧,ҲΪ,ṩhandleʱʹ϶õĶ
  this.handles = (htype == 'array' || htype == 'collection') ? $$(this.options.handle) : $(this.options.handle) || this.element;
  //ֵ
  this.mouse = {'now': {}, 'pos': {}};
  //ֵ
  this.value = {'start': {}, 'now': {}};
  //ѡȡ¼,ieںʹonselectstart
  this.selection = (Browser.Engine.trident) ? 'selectstart' : 'mousedown';
  
  //ʹñհ󶨵ǰ¼
  this.bound = {
   //ʼ
   start: this.start.bind(this),
   //
   check: this.check.bind(this),
   //϶
   drag: this.drag.bind(this),
   //϶
   stop: this.stop.bind(this),
   //ȡ
   cancel: this.cancel.bind(this),
   //ֹͣ¼ð,ʹ$lambdaٹfuncton(){return false;}ĺ
   eventStop: $lambda(false)
  };
  //¼
  this.attach();
 },

 //¼
 attach: function(){
  this.handles.addEvent('mousedown', this.bound.start);
  return this;
 },

 //Ƴ¼
 detach: function(){
  //ΪƳ갴¼,֮Ҫǰ󶨲淽,Ϊ¼ĿƳ
  //addeventremoveEventжʹthis.start.bind(this),Ƴ¼ʧ
  this.handles.removeEvent('mousedown', this.bound.start);
  return this;
 },

 //ʼ϶
 start: function(event){
  //϶ǰ¼
  this.fireEvent('onBeforeStart', this.element);
  //ĳʼλϢ
  this.mouse.start = event.page;
  //Χ
  var limit = this.options.limit;
  this.limit = {'x': [], 'y': []};
  //õ
  for (var z in this.options.modifiers){
   //ֵΪfalse,
   if (!this.options.modifiers[z]) continue;
   //ָʹʽ,ʽȡӦֵ
   if (this.options.style) this.value.now[z] = this.element.getStyle(this.options.modifiers[z]).toInt();
   //ȡֵ
   else this.value.now[z] = this.element[this.options.modifiers[z]];
   //ָת,ȡֵ(ȡ,ȡ)
   if (this.options.invert) this.value.now[z] *= -1;
   //λ
   this.mouse.pos[z] = event.page[z] - this.value.now[z];
   //ָӦϵķΧ
   if (limit && limit[z]){
    //ΪΧһ,ָ޼,ѭ2(Ϊʲôֱ(2).times?)
    for (var i = 2; i--; i){
     //ж,ȡֵ,Ϊÿֵ,ʹ$lambda,Կʹúֵ
     //limit :{x : false, y : [function(){return 1}, function(){return 10}]}
     //Ȼ,ĺӵĴ
     if ($chk(limit[z][i])) this.limit[z][i] = $lambda(limit[z][i])();
    }
   }
  }
  //ʽƶ/
  if ($type(this.options.grid) == 'number') this.options.grid = {'x': this.options.grid, 'y': this.options.grid};
  //ǰĵƶ͵¼
  this.document.addEvents({mousemove: this.bound.check, mouseup: this.bound.cancel});
  //εǰĵѡ
  this.document.addEvent(this.selection, this.bound.eventStop);
 },

 //Լ
 check: function(event){
  //㵱ǰƶľ
  var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this.mouse.start.x, 2) + Math.pow(event.page.y - this.mouse.start.y, 2)));
  //
  if (distance > this.options.snap){
   //Ƴ϶ǰԼ¼
   this.cancel();
   //϶ص¼
   this.document.addEvents({
    mousemove: this.bound.drag,
    mouseup: this.bound.stop
   });
   //onStartonSnap¼
   this.fireEvent('onStart', this.element).fireEvent('onSnap', this.element);
  }
 },

 //϶
 drag: function(event){
  //ǰ
  this.mouse.now = event.page;
  //϶õ
  for (var z in this.options.modifiers){
   //ֵΪfalse,Ը
   if (!this.options.modifiers[z]) continue;
   //㵱ǰֵ
   this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z];
   //תֵ
   if (this.options.invert) this.value.now[z] *= -1;
   //Χƴ
   if (this.options.limit && this.limit[z]){
    //ǰϵֵ
    if ($chk(this.limit[z][1]) && (this.value.now[z] > this.limit[z][1])){
     //ȡֵ
     this.value.now[z] = this.limit[z][1];
    //ǰֵС
    } else if ($chk(this.limit[z][0]) && (this.value.now[z] < this.limit[z][0])){
     //ȡֵ
     this.value.now[z] = this.limit[z][0];
    }
   }
   //Сȡֵ
   if (this.options.grid[z]) this.value.now[z] -= (this.value.now[z] % this.options.grid[z]);
   //ָʽԵֵ
   if (this.options.style) this.element.setStyle(this.options.modifiers[z], this.value.now[z] + this.options.unit);
   else this.element[this.options.modifiers[z]] = this.value.now[z];
  }
  //onDrag¼
  this.fireEvent('onDrag', this.element);
 },

 //ȡ
 cancel: function(event){
  //Ƴ¼
  this.document.removeEvent('mousemove', this.bound.check);
  this.document.removeEvent('mouseup', this.bound.cancel);
  if (event){
   this.document.removeEvent(this.selection, this.bound.eventStop);
   //onCancel¼
   this.fireEvent('onCancel', this.element);
  }
 },

 //ֹͣ,ɨս
 stop: function(event){
  //Ƴ¼
  this.document.removeEvent(this.selection, this.bound.eventStop);
  this.document.removeEvent('mousemove', this.bound.drag);
  this.document.removeEvent('mouseup', this.bound.stop);
  //onComplete¼
  if (event) this.fireEvent('onComplete', this.element);
 }

});

//DragΪElementչ
Element.implement({
 //ʹǰ
 makeResizable: function(options){
  return new Drag(this, $merge({modifiers: {'x': 'width', 'y': 'height'}}, options));
 }

});

