
var NewDraggable = Class.create({
  initialize: function(element) {
    
    var defaults = {
      endeffect: function(element, origelement) {
        var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
          queue: {scope:'_draggable', position:'end'},
          afterFinish: function(){
            NewDraggable._dragging[origelement] = false
          }
        });
      },
      zindex: 1000
    };

    if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
      Object.extend(defaults, {
        starteffect: function(element, origelement) {
          element._opacity = Element.getOpacity(element);
          NewDraggable._dragging[origelement] = true;
          new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
        }
      });

    var options = Object.extend(defaults, arguments[1] || { });

    this.element = $(element);
    this.handle = this.element;

    Element.makePositioned(this.element); // fix IE

    this.options  = options;
    this.dragging = false;

    this.eventMouseDown = this.initDrag.bindAsEventListener(this);
    Event.observe(this.handle, "mousedown", this.eventMouseDown);

    Draggables.register(this);
  },

  destroy: function() {
    Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
    if( this._clone ) Element.remove(this._clone);
    Draggables.unregister(this);
  },

  currentDelta: function() {
    return([
      parseInt(Element.getStyle(this._clone,'left') || '0'),
      parseInt(Element.getStyle(this._clone,'top') || '0')]);
  },

  initDrag: function(event) {
    if(!Object.isUndefined(NewDraggable._dragging[this.element]) &&
      NewDraggable._dragging[this.element]) return;

    if(Event.isLeftClick(event)) {
      // abort on form elements, fixes a Firefox issue
      var src = Event.element(event);
      if((tag_name = src.tagName.toUpperCase()) && (
        tag_name=='INPUT' ||
        tag_name=='SELECT' ||
        tag_name=='OPTION' ||
        tag_name=='BUTTON' ||
        tag_name=='TEXTAREA')) return;
        
      Draggables.activate(this);
      Event.stop(event);
    }
  },

  startDrag: function(event) {
    
    this._clone = this.element.cloneNode(true);
    $( this._clone ).removeClassName('odd'); //ie6 fix
    this.element.parentNode.insertBefore(this._clone, this.element);
    
    if (this.element.getStyle('position') != 'absolute')
      Position.absolutize(this._clone);
    
    var pointer = [Event.pointerX(event), Event.pointerY(event)];
    var pos     = Position.cumulativeOffset(this._clone);
    this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
    
    this.dragging = true;
    
    if(!this.delta)
      this.delta = this.currentDelta();

    if(this.options.zindex)
      this._clone.style.zIndex = this.options.zindex;
    
    Draggables.notify('onStart', this, event);

    if(this.options.starteffect) 
      this.options.starteffect(this._clone, this.element);
  },

  updateDrag: function(event, pointer) {
    if(!this.dragging) this.startDrag(event);

    if(!this.options.quiet){
      Position.prepare();
      Droppables.show(pointer, this.element);
    }

    Draggables.notify('onDrag', this, event);

    this.draw(pointer);
    if(this.options.change) this.options.change(this);

    // fix AppleWebKit rendering
    if(Prototype.Browser.WebKit) window.scrollBy(0,0);

    Event.stop(event);
  },

  finishDrag: function(event, success) {
    this.dragging = false;

    if(this.options.quiet){
      Position.prepare();
      var pointer = [Event.pointerX(event), Event.pointerY(event)];
      Droppables.show(pointer, this.element);
    }
    
    var dropped = false;
    if(success) {
      dropped = Droppables.fire(event, this.element);
      if (!dropped) {
        dropped = false;
      }
    }
    
    if(dropped && this.options.onDropped) 
      this.options.onDropped(this.element);
      
    Draggables.notify('onEnd', this, event);

    if(this.options.endeffect)
      this.options.endeffect(this._clone, this.element);
      
    Element.remove(this._clone);
    this._clone = null;
    
    Draggables.deactivate(this);
    Droppables.reset();
  },

  keyPress: function(event) {
    if(event.keyCode!=Event.KEY_ESC) return;
    this.finishDrag(event, false);
    Event.stop(event);
  },

  endDrag: function(event) {
    if(!this.dragging) return;
    this.finishDrag(event, true);
    Event.stop(event);
  },

  draw: function(point) {
    var pos = Position.cumulativeOffset(this._clone);
    var d   = this.currentDelta();
    pos[0] -= d[0]; pos[1] -= d[1];

    var p = [0,1].map(function(i){
      return (point[i]-pos[i]-this.offset[i])
    }.bind(this));

    var style = this._clone.style;
    style.left = p[0] + "px";
    style.top  = p[1] + "px";
      
    if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
  }
  
});

NewDraggable._dragging = { };
