var mouse_x = 0;
var mouse_y = 0;
var current_offset_x = 0;
var current_offset_y = 0;
var current_drop;
var drop_array = Array();
var dragging = 0;

var drop_box = document.getElementById(drop_box_id);
var dummy_box = document.getElementById(dummy_box_id);
var total_drops = dummy_box.childNodes.length;

// hide the dummy_box
dummy_box.style.position = "absolute";
dummy_box.style.top = "0px";
dummy_box.style.left = "0px";
dummy_box.style.width = "1px";
dummy_box.style.height = "1px";
dummy_box.style.visibility = "hidden";
dummy_box.style.overflow = "hidden";

var IE = document.all?true:false;
if (!IE) {
	document.captureEvents(Event.MOUSEMOVE);
}

// for Mac IE 5
if (!Array.prototype.push) {
  Array.prototype.push = function() {
    for (var i=0; i<arguments.length; ++i) {
      this[this.length] = arguments[i];
    }
    
    return this.length;
  };
}

document.onmousemove = move_obj;
document.onmouseup = drop_obj;
document.onselectstart = disable_select; // IE
drop_box.style.MozUserSelect = "none"; // Mozilla

var this_drop;
var drop_num = 0;
var dummy_node_id = 0;
for (var i=0; i<total_drops; i++) {
  if (dummy_box.childNodes[i].innerHTML) {
    dummy_node_id = i;
    this_drop = new Drop(dummy_node_id, drop_num++);
    drop_box.appendChild(this_drop.obj);
    
    drop_array.push(this_drop);
  }
}


function debug(str) {
  document.getElementById('debug').innerHTML = str;
}

function set_current_drop(drop) {
  /*
    checking dragging variable to prevent messed up start_x and start_y values
    if mouse was dragged offscreen.  otherwise set_current_drop() would be allowed
    to be called more than once in a row
  */
  if (!dragging) {
    dragging = 1;
    
    drop.start_x = drop.x;
    drop.start_y = drop.y;
    
    current_offset_x = mouse_x - drop.x;
    current_offset_y = mouse_y - drop.y;
    
    current_drop = drop;
  }
}

function move_obj(e) {
  update_mouse_position(e);
  if (current_drop) {
    current_drop.send_to_front();
    
    var new_x = mouse_x - current_offset_x;
    var new_y = mouse_y - current_offset_y;
    
    set_position(current_drop, new_x, new_y);
    
    // for snapping drops into place vertically
    if (constrain_y && snap_into_place) {
      // grab the position of the next drop and make sure it's valid
      var current_drop_num = current_drop.drop_num;
      var next_drop_num = current_drop_num + 1;
      var previous_drop_num = current_drop_num - 1;
      
      // moving down *****************
      if (next_drop_num < drop_array.length) {
        var next_drop_y = drop_array[next_drop_num].y;
        
        // drop has moved into place to snap to
        if (new_y >= next_drop_y) {
          // swap drop positions
          set_position(drop_array[next_drop_num], drop_array[next_drop_num].x, drop_array[current_drop_num].start_y);
          
          // swap drops in drop_array and drop_nums
          var tmp_drop = current_drop;
          var tmp_current_drop_start_y = current_drop.start_y;
          current_drop.start_y = drop_array[next_drop_num].start_y;
          drop_array[next_drop_num].start_y = tmp_current_drop_start_y;
          drop_array[current_drop_num] = drop_array[next_drop_num];
          drop_array[next_drop_num] = tmp_drop;
          drop_array[current_drop_num].drop_num = current_drop_num;
          drop_array[next_drop_num].drop_num = next_drop_num;
        }
      }
      
      // moving up *****************
      if (previous_drop_num >= 0) {
        var previous_drop_y = drop_array[previous_drop_num].y;
        
        // drop has moved into place to snap to
        if (new_y <= previous_drop_y) {
          // swap drop positions
          set_position(drop_array[previous_drop_num], drop_array[previous_drop_num].x, drop_array[current_drop_num].start_y);
          
          // swap drops in drop_array and drop_nums
          var tmp_drop = current_drop;
          var tmp_current_drop_start_y = current_drop.start_y;
          current_drop.start_y = drop_array[previous_drop_num].start_y;
          drop_array[previous_drop_num].start_y = tmp_current_drop_start_y;
          drop_array[current_drop_num] = drop_array[previous_drop_num];
          drop_array[previous_drop_num] = tmp_drop;
          drop_array[current_drop_num].drop_num = current_drop_num;
          drop_array[previous_drop_num].drop_num = previous_drop_num;
        }
      }
      
    }
    
    // for snapping into place horizontally
    if (constrain_x && snap_into_place) {
      // grab the position of the next drop and make sure it's valid
      var current_drop_num = current_drop.drop_num;
      var next_drop_num = current_drop_num + 1;
      var previous_drop_num = current_drop_num - 1;
      
      // moving right *****************
      if (next_drop_num < drop_array.length) {
        var next_drop_x = drop_array[next_drop_num].x;
        
        // drop has moved into place to snap to
        if (new_x >= next_drop_x) {
          // swap drop positions
          set_position(drop_array[next_drop_num], drop_array[current_drop_num].start_x, drop_array[next_drop_num].y);
          
          // swap drops in drop_array and drop_nums
          var tmp_drop = current_drop;
          var tmp_current_drop_start_x = current_drop.start_x;
          current_drop.start_x = drop_array[next_drop_num].start_x;
          drop_array[next_drop_num].start_x = tmp_current_drop_start_x;
          drop_array[current_drop_num] = drop_array[next_drop_num];
          drop_array[next_drop_num] = tmp_drop;
          drop_array[current_drop_num].drop_num = current_drop_num;
          drop_array[next_drop_num].drop_num = next_drop_num;
        }
      }
      
      // moving left *****************
      if (previous_drop_num >= 0) {
        var previous_drop_x = drop_array[previous_drop_num].x;
        
        // drop has moved into place to snap to
        if (new_x <= previous_drop_x) {
          // swap drop positions
          set_position(drop_array[previous_drop_num], drop_array[current_drop_num].start_x, drop_array[previous_drop_num].y);
          
          // swap drops in drop_array and drop_nums
          var tmp_drop = current_drop;
          var tmp_current_drop_start_x = current_drop.start_x;
          current_drop.start_x = drop_array[previous_drop_num].start_x;
          drop_array[previous_drop_num].start_x = tmp_current_drop_start_x;
          drop_array[current_drop_num] = drop_array[previous_drop_num];
          drop_array[previous_drop_num] = tmp_drop;
          drop_array[current_drop_num].drop_num = current_drop_num;
          drop_array[previous_drop_num].drop_num = previous_drop_num;
        }
      }
      
    }
    
  }
}

function set_position(drop, x, y) {
  if (drop.obj) {
    drop.x = x;
    drop.y = y;
    
    if (!constrain_y) {
      var new_x = drop.x - drop.x_offset;
      drop.obj.style.left = new_x + 'px';
    }
    
    if (!constrain_x) {
      var new_y = drop.y - drop.y_offset;
      drop.obj.style.top = new_y + 'px';
    }
  }
}

function drop_obj() {
  if (current_drop) {
    if (snap_into_place && (constrain_x || constrain_y)) {
      set_position(current_drop, current_drop.start_x, current_drop.start_y);
      
      // fix for Safari redraw bug
      if (navigator.userAgent.toLowerCase().indexOf('safari') + 1) {
        window.resizeBy(1, 0);
        window.resizeBy(-1, 0);
      }
    }
    
    current_drop = null;
    dragging = 0;
  }
}

function update_mouse_position(e) {
  if (IE) {
    mouse_y = event.clientY + document.body.scrollTop;
    mouse_x = event.clientX + document.body.scrollLeft;
  } else {
    mouse_y = e.pageY;
    mouse_x = e.pageX;
  }
  
  mouse_x = Math.max(0, mouse_x);
  mouse_y = Math.max(0, mouse_y);
}

function disable_select() {
  return false;
}



function Drop(dummy_node_id, drop_num) {
  this.dummy_node_id = dummy_node_id;
  this.drop_num = drop_num;
  this.obj = null;
  this.x = 0;
  this.y = 0;
  this.start_x = 0;
  this.start_y = 0;
  this.x_offset = 0;
  this.y_offset = 0;
  this.parent_offset_x = 0;
  this.parent_offset_y = 0;
  this.z_index = 0;
  
  this.create = create;
  this.send_to_front = send_to_front;
  this.update_parent_offsets = update_parent_offsets;
  this.get_x_offset = get_x_offset;
  this.get_y_offset = get_y_offset;
  
  this.create(this);
  this.send_to_front();
}

function create(parent) {
  var drop_obj = document.createElement("div");
  drop_obj.id = dummy_box.childNodes[this.dummy_node_id].id;
  drop_obj.className = dummy_box.childNodes[this.dummy_node_id].className;
  drop_obj.innerHTML = dummy_box.childNodes[this.dummy_node_id].innerHTML;
  
  this.update_parent_offsets();
  
  this.x = this.start_x = this.x_offset = this.get_x_offset();
  this.y = this.start_y = this.y_offset = this.get_y_offset();
  
  this.obj = drop_obj;
  this.obj.onmousedown = function(){set_current_drop(parent)};
}

function send_to_front() {
  this.z_index = current_drop_z_index++;
  this.obj.style.zIndex = this.z_index;
}

function update_parent_offsets() {
  this.parent_offset_x = drop_box.offsetLeft;
  this.parent_offset_y = drop_box.offsetTop;
  var current_node = drop_box.parentNode;
  while (current_node) {
    if (current_node.offsetLeft) {
      this.parent_offset_x += current_node.offsetLeft;
      this.parent_offset_y += current_node.offsetTop;
    }
    
    current_node = current_node.parentNode;
  }
}

function get_x_offset() {
  return dummy_box.childNodes[this.dummy_node_id].offsetLeft + this.parent_offset_x;
}

function get_y_offset() {
  return dummy_box.childNodes[this.dummy_node_id].offsetTop + this.parent_offset_y;
}