// supplements.js v1.0
//
// Copyright (c) 2007 Einstein Industries
// Author: kmiller@einsteinindustries.com
// 
// Supplements is freely distributable under the terms of an MIT-style license.
//
// This file contains class extensions and new methods for Prototype/Scriptaculous
//

/*-----------------------------------------------------------------------------------------------*/

//
//  Extends Draggable to allow for dragging an item out of a parent container with the overflow set to 'hidden' or 'scroll'
//
//	Usage:
//		overflow: true 		// Just add this option
//
Draggable.prototype = Object.extend(Draggable.prototype, {
  initialize: function(element) {
    var defaults = {
      handle: false,
      reverteffect: function(element, top_offset, left_offset) {
        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
        new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
          queue: {scope:'_draggable', position:'end'}
        });
      },
      endeffect: function(element) {
        var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0;
        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, 
          queue: {scope:'_draggable', position:'end'},
          afterFinish: function(){ 
            Draggable._dragging[element] = false 
          }
        }); 
      },
      zindex: 1000,
      revert: false,
      overflow: false,
      scroll: false,
      scrollSensitivity: 20,
      scrollSpeed: 15,
      snap: false,  // false, or xy or [x,y] or function(x,y){ return [x,y] }
      delay: 0
    };

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

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

    this.element = $(element);

    if(options.handle && (typeof options.handle == 'string'))
      this.handle = this.element.down('.'+options.handle, 0);

    if(!this.handle) this.handle = $(options.handle);
    if(!this.handle) this.handle = this.element;

    if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
      options.scroll = $(options.scroll);
      this._isScrollChild = Element.childOf(this.element, options.scroll);
    }

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

    this.delta    = this.currentDelta();
    this.options  = options;
    this.dragging = false;   

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

    Draggables.register(this);
  },

  startDrag: function(event) {
    this.dragging = true;

    if(this.options.zindex) {
      this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
      this.element.style.zIndex = this.options.zindex;
    }

    if (this.options.overflow) {
      var element = this.element;
      this.deltaOverflow = Position.cumulativeOffset($(element));
      this._clone = $(element).cloneNode(true);
      this._clone.setStyle({
        position: 'absolute',
        top: this.deltaOverflow[1]+'px',
        left: this.deltaOverflow[0]+'px'
      });
      
      document.getElementsByTagName('body')[0].appendChild(this._clone);
      
      this.element = this._clone;
      this._clone = $(element);
      
      if (!this.options.ghosting) this._clone.style.visibility = 'hidden';
    } else {
      if(this.options.ghosting) {
        this._clone = this.element.cloneNode(true);
        Position.absolutize(this.element);
        this.element.parentNode.insertBefore(this._clone, this.element);
      }				
    }

    if(this.options.scroll) {
      if (this.options.scroll == window) {
        var where = this._getWindowScroll(this.options.scroll);
        this.originalScrollLeft = where.left;
        this.originalScrollTop = where.top;
      } else {
        this.originalScrollLeft = this.options.scroll.scrollLeft;
        this.originalScrollTop = this.options.scroll.scrollTop;
      }
    }

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

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

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

    if(this.options.ghosting && !this.options.overflow) {
      Position.relativize(this.element);
      Element.remove(this._clone);
      this._clone = null;
    }

    if(success) Droppables.fire(event, this.element);
    Draggables.notify('onEnd', this, event);

    var revert = this.options.revert;
    if(revert && typeof revert == 'function') revert = revert(this.element);

    var d = this.currentDelta();
    if(revert && this.options.reverteffect) {
      if (this.options.overflow) {
        this.options.reverteffect(this.element, 
        d[1]-this.deltaOverflow[1], d[0]-this.deltaOverflow[0]);
		
        this.checkDraggableEffects = new PeriodicalExecuter(function() {
          if (!Draggable._dragging[this.element]) {
            this.checkDraggableEffects.stop();
            if (!this.options.ghosting) this._clone.style.visibility = 'visible';
            
            $(this.element).remove();
            this.element = this._clone;
            $(this._clone).style.zIndex = '';
            this._clone = null;
            Position.relativize(this.element);
          }
        }.bind(this), .0001);
      } else {
        this.options.reverteffect(this.element, 
        d[1]-this.delta[1], d[0]-this.delta[0]);
      }
    } else {
      this.delta = d;
    }

    if(this.options.zindex)
      this.element.style.zIndex = this.originalZ;

    if(this.options.endeffect) 
      this.options.endeffect(this.element);

	Draggables.deactivate(this);
	Droppables.reset();
  }
});
