// DragonDrop.info drag script -- copyright 2001 by Bob Kanefsky
// May be freely used as long as (1) this notice is preserved and
// (2) your site has a prominent link to one of mine: dragondrop.info, moons.info, or roving-mouse.com
// This is a rewrite of the code in http://www.mozilla.org/docs/dom/samples/resize/index.html
// which is titled "From Dynatable demo - produced by Marcio Galli"
 
var burdens = new Array (0);
var dragging_burden = null;
var destinations = new Array(0);
var mouse_over_destination = null;
var default_drop_destination = null;
var burden_zIndex_increment = 50; 
var destination_zIndex_increment = 100; 
var clear_image_url = 'images/clear.gif';

function browser_supports_drag_and_drop () {
  return (document.implementation
                   && typeof(document.implementation.hasFeature)=="function"
                   && document.implementation.hasFeature("CSS", "2.0")
                   && document.implementation.hasFeature("MouseEvents", "2.0")
                 )
}

function dd_error(msg) {
  alert(msg)
}

function dd_object () {
   }

function say (msg) {
   window.status = msg;
}


function make_burden (image, object) {
     if (!object) object = new dd_object();
     if (image) object.image = image;
     if (!object.image.style.zIndex) object.image.style.zIndex = 0; // otherwise we get ""+50+50 = "5050"
     object.image.dd_burden = object;
     object.image.addEventListener("mousedown", event_to_start_dragging, false);
     if (!object.acceptable_destination) object.acceptable_destination = function () {return true};
     if (!object.name) object.name = object.image.id;
     if (!object.zIndex_increment) object.zIndex_increment = burden_zIndex_increment;
     burdens.push(object);
     return object;
  }

function disable_burden (burden) {
      burden.image.removeEventListener("mousedown", event_to_start_dragging, false);
}

function make_destination (hotspot, object) {
     if (!object) object = new dd_object();
     if (hotspot) object.hotspot = hotspot;
     object.hotspot.dd_destination = object;
     if (!object.hotspot.style.zIndex) object.hotspot.style.zIndex = 0; // otherwise we get ""+50+50 = "5050"
     if (!object.sensitize) object.sensitize =
           function ()
              {object.hotspot.addEventListener("mouseover", mouse_entering, false);
               object.hotspot.addEventListener("mouseout", mouse_exiting, false);
               object.hotspot.style.zIndex = Number(object.hotspot.style.zIndex) + object.zIndex_increment;
               object.hotspot.display='none';
            };
     if (!object.desensitize) object.desensitize =
           function ()
              {object.hotspot.removeEventListener("mouseover", mouse_entering, false);
               object.hotspot.removeEventListener("mouseout", mouse_exiting, false);
               object.hotspot.style.zIndex -= object.zIndex_increment;
               object.hotspot.display='inherit';
            };
     if (!object.zIndex_increment) object.zIndex_increment = destination_zIndex_increment;
     if (!object.acceptable_burden) object.acceptable_burden = function (burden) {return true};
     if (!object.receive_burden) object.receive_burden = function (burden) {return true};
     if (object.hotspot.image) {id = object.hotspot.image.id} else id = object.hotspot.id;;
     if (!object.name) object.name = id;
     destinations.push(object);
     return object;
  }

function make_hotspot (image) {
  element = document.createElement("IMG");
  element.src = clear_image_url;
  element.alt = "";
  element.image = image; // the real underlying image or other visual rendering at a lower zIndex
  element.style.position = image.style.position; 
  element.style.left = image.style.left;
  element.style.top = image.style.top; 
  if (image.width)  {element.style.width = image.width + 'px'} else {element.style.width = image.style.width};
  if (image.height)  {element.style.height = image.height + 'px'} else {element.style.height = image.style.height};
  element.style.zIndex = image.style.zIndex;
  image.parentNode.appendChild(element);
  return element;
}

function make_image (src) {
  element = document.createElement("IMG");
  element.src = src;
  element.style.position = "absolute";
  element.style.zIndex = 1;
  document.body.appendChild(element);
  return element;
}

function acceptable_drop(burden,destination) {
   return (burden.acceptable_destination(destination)
                  && destination.acceptable_burden(burden)) }

function event_to_start_dragging(event) {
  dragging_burden = event.target.dd_burden;
  if (dragging_burden) {
     if (typeof(stop_motion)=='function') stop_motion(dragging_burden);  // in case of self-propelled objects
     if (dragging_burden.start_dragging) dragging_burden.start_dragging();
     for (i in destinations)
        if (acceptable_drop (dragging_burden, destinations[i]))
           destinations[i].sensitize(dragging_burden);

     dragging_burden.pickup_point_x = event.clientX;
     dragging_burden.pickup_point_y = event.clientY;

     mouse_over_destination = null;
     
     window.addEventListener("mousemove", event_to_continue_dragging, false);
     window.addEventListener("mouseup", event_to_drop, false);
     event.preventDefault();
           }
      }


function event_to_continue_dragging(event) {
     if (!dragging_burden) return dd_error('Not dragging anything');

     update_location_after_event(dragging_burden, event, false); // ought to figure grab point in

     if (dragging_burden.while_dragging) dragging_burden.while_dragging(mouse_over_destination);

     // Override browser's default drag-and-drop behavior
     event.preventDefault();
      }

     
//----------------------------------------------------------------------------
// Called when the mouse button is released
//
function event_to_drop(event) {

     for (i in destinations) if (destinations[i].desensitize) destinations[i].desensitize();

     window.removeEventListener("mousemove", event_to_continue_dragging, false);
     window.removeEventListener("mouseup", event_to_drop, false);

     if (dragging_burden)
         update_location_after_event(dragging_burden, event, true)

     if (mouse_over_destination) dropped_onto_object = mouse_over_destination
     else dropped_onto_object = default_drop_destination;
       
/////     dragging_burden.image.style.zIndex -= dragging_burden.zIndex_increment;
    
     drop_into(dragging_burden, dropped_onto_object);

     dragging_burden = null;
}

function mouse_entering(event) {
  object = event.target.dd_destination;
  if (object &&  object != dragging_burden) {
    if (object != dragging_burden)
      {mouse_over_destination = object;
        if (dragging_burden.entering_destination) dragging_burden.entering_destination(object);
        if (object .burden_entering) object .burden_entering(dragging_burden);
       }
    }
}

function mouse_exiting(event) {
  object = event.target.dd_destination;
  if (object &&  object != dragging_burden) {
      {mouse_over_destination = null;
        if (dragging_burden.leaving_destination) dragging_burden.leaving_destination(object);
        if (object .burden_leaving) object .burden_leaving(dragging_burden);
       }
    }
}

function provide_running_commentary (burden, for_which) {
   if (!for_which) for_which = "drag, drop";
   old = new dd_object();
   old.start_dragging = burden.start_dragging;
   old.entering_destination = burden.entering_destination;
   old.dropped_onto = burden.dropped_onto;
   if (for_which.match(/(drag)/i)) {   
     burden.start_dragging =   burden.leaving_destination =
          function (dest) {say ("You are dragging " + burden.name);
                                            if (old.start_dragging) old.start_dragging(dest);}
     burden.entering_destination =
         function (dest) {say ("You are dragging " + burden.name + " over " + dest.name);
                                            if (old.entering_destination) old.entering_destination(dest);}
    };
   if (for_which.match(/(drop)/i)) {   
    burden.dropped_onto =
        function (dest) {msg = "You have dropped " + burden.name;
                                          if (dest && dest.name) msg += " onto " + dest.name;
                                          say (msg);
                                          if (old.dropped_onto) old.dropped_onto(dest);}
  };
  return burden;
}

function drop_into (dropped_burden, destination) {
  if ((destination == null || destination == dropped_burden))
    {if (default_drop_destination) {
         if (dragging_burden.dropped_onto) dragging_burden.dropped_onto(default_drop_destination);
        default_drop_destination.receive_burden(dropped_burden)
     }}
    else if (acceptable_drop (dropped_burden, destination))
        {destination.receive_burden(dropped_burden);
         if (dragging_burden.dropped_onto) dragging_burden.dropped_onto(destination);}
    else {abort_drop(dropped_burden);}
 }

function abort_drop (burden) {
 if (burden)
   slowly_change_pos(burden, burden.current_x, burden.current_y, burden.pickup_point_x, burden.pickup_point_y, 500);
}

///////// Implementation of the actual visible motion: /////////////

function slowly_change_pos (object, start_left, start_top, final_left, final_top, duration) {
   w = object.image.width/2;
   h = object.image.height/2;
   if (start_left != null) {x0 = start_left-w} else {x0 = object.current_x-w};
   if (final_left != null) {x1 = final_left-w} else {x1 = object.current_x-w};
   if (start_top != null) {y0 = start_top-h} else {y0 = object.current_y-h};
   if (final_top != null) {y1 = final_top-h} else {y1 = object.current_y=h};
   if (typeof(set_in_motion)=="function")
      {set_in_motion(object, new linear_trajectory(x0, y0, x1, y1, duration))}
  else { jump_object (object, x1, y1)  ///// log += window.status = "jump_object" + "(" + x1 + "," + y1 + ")" + " called by slowly_change_pos"
            };
}

function position_of (burden) {
  if (!burden || !burden.image) {alert ("Not a drag-and-drop burden " + burden); return "error"}
  else {return burden.image.style}
 }

var reasonable_interval_for_this_cpu = 10.0;

function update_location_after_event (burden, event, immediate) {
   x = event.clientX;
   y = event.clientY;
   if (burden.constrain)  {
       x = burden.constrain("x", x,y);
       y = burden.constrain("y", x,y);
    }
   if (burden.thread_updating_location)  {
       clearTimeout(burden.thread_updating_location);
       reasonable_interval_for_this_cpu *= 1.2
     }
   if (immediate)
   { jump_object (burden, x, y)    //// log += window.status = "jump_object"  + "(" + x + "," + y + ")" + " immediate called by update_location_after_event(" + event.type + ")"
    }
   else {burden.thread_updating_location = setTimeout(jump_object, 10, burden, x, y);}
}

function jump_object (object, x, y) {
   ///// log += window.status = "jump_object" + "(" + x + "," + y + ")" + " called by something else, presumably setTimeout"
   object.thread_updating_location = null;
   // Internally track where we put it.
   object.current_x = x;
   object.current_y = y;
   // Pluck it out of wherever it was embedded and move it across the page.
    old_parent = object.image.parentNode;
    if (old_parent != document.body) {
      blank = document.createElement("IMG");  // just removing it could cause reflow, which looks confusing
      blank.src = clear_image_url;
      blank.alt = "";
      blank.width = object.image.width;
      blank.height = object.image.height;
      old_parent.replaceChild(blank, object.image);
      if (old_parent.style.zIndex)
           object.image.style.zIndex = old_parent.style.zIndex + 1;
      document.body.appendChild(object.image);
    }
   // Move it visibly
   pos = position_of(object);
   pos.position = "absolute"; // corresponds to window coordinates now that it's removed from parent
   new_left = x; new_top = y;
   new_left -= object.image.width/2;
   new_top  -= object.image.height/2;
   new_left += window.scrollX;
   new_top += window.scrollY;
   pos.left = Math.round(new_left) + 'px';
   pos.top  = Math.round(new_top) + 'px';
   // If it's a destination too, move the hotspot along with the visible object.
   if (object.hotspot) {object.hotspot.style.left = pos.left; object.hotspot.style.top = pos.top;}
}
