function $chk(obj){
	return !!(obj || obj === 0);
};
function $dump(o) {
  if (o instanceof Array) {
    return o.join(",");
  } else {
    var a = [];
    for (i in o) {
      a.push(i + "=" + o[i]);
    }
    document.title = a.join(", ");
    return a.join(",");
  }
}

tom.dnd = {};

tom.dnd.Draggable = tom.Class.create();
tom.extend(tom.dnd.Draggable.prototype, {
  initialize: function(el, options) {
    var fields = {
      element: null,
	  handle: false,
	  container: false,
      opacity: 0.6,
      
      position: {element: null, container: null},
      mouse: {'now': {}, 'pos': {}},
      value: {'start': {}, 'now': {}},
      revert: tom.fnFalse,
      bound: {},
      savePoint: {parent: null, nextSibling: null, previousSibling: null, left: 0, top: 0, width: 0, height: 0},
      isOnDrag: false,
      
      droppable: null,
      droppableFilter: tom.fnTrue,

      events: null,
      onBeforeStart: tom.fnTrue,
      onStart: tom.fnEmpty,
      onComplete: tom.fnEmpty,
      onDrag: tom.fnEmpty,
      onCancel: tom.fnEmpty,
	
      overflown: [],
	
      limit: false
    };
    tom.extend(this, fields, options || {});
    this.element = $(el);
    this.handle = this.handle ? $(this.handle) : this.element;
    var _this = this;
    this.events = {
      start: function(e) {
          e = e || window.event;
          if (_this.onBeforeStart(e)) _this.start(e);
          tom.Event.stop(e);
          return false;
        },
      drag: function(e) {
          e = e || window.event;
          _this.drag(e || window.event);
          tom.Event.stop(e);
          return false; 
        },
      stop: function(e) {
          e = e || window.event;
          _this.stop(e || window.event);
          tom.Event.stop(e);
          return false;
        },
      cancel: function(e) {
          e = e || window.event;
          if (e.keyCode == 27) {
            _this.cancel(e || window.event);
          }
          tom.Event.stop(e);
          return false;
        }
    };
    
	this.position = {'element': tom.Element.getStyle(this.element, 'position'), 'container': false};
	if (!tom.util.Arrays.contains(["relative", "absolute", "fixed"], this.position.element.trim())) {
	  this.position.element = 'relative';
	}
	this.container = $(this.container);
	if (this.container) {
	  this.position.container = tom.Element.getStyle(this.container, 'position');
	}
	
	var top = parseInt(tom.Element.getStyle(this.element, 'top')),
	    left = parseInt(tom.Element.getStyle(this.element, 'left'));
	  
	if (this.position.element == 'absolute' && !(tom.util.Arrays.contains(['relative', 'absolute', 'fixed'], this.position.container))) {
		top = $chk(top) ? top : tom.Position.cumulativeOffset(this.element).top;
		left = $chk(left) ? left : tom.Position.cumulativeOffset(this.element).left;
	} else {
		top = $chk(top) ? top : 0;
		left = $chk(left) ? left : 0;
	}
	if (window.opera) {
	  left = top = 0;
	}

//    this.element.style.left = left + "px";
//    this.element.style.top = top + "px";
//    this.element.style.position = this.position.element;
    
    tom.Event.addEvent(this.handle, "mousedown", this.events.start, false);
  },
  start: function(e) {
    this.mouse.start = this.mouse.now = tom.Event.pointer(e);
    this.value.now = {x: parseInt(tom.Element.getStyle(this.element, "left")), y: parseInt(tom.Element.getStyle(this.element, "top"))};
    this.mouse.pos.x = this.mouse.start.x - this.value.now.x;
    this.mouse.pos.y = this.mouse.start.y - this.value.now.y;
    
    if (!this.isOnDrag) {
      this.saveSavePoint();
    }
    this.isOnDrag = true;
    
    if (this.droppable) {
      tom.dnd.Droppable.detectDragStart(e, this);
    }
    
	tom.Event.addEvent(document, "mousemove", this.events.drag, false);
	tom.Event.addEvent(document, "mouseup", this.events.stop, false);
	tom.Event.addEvent(document, "keypress", this.events.cancel, false);
    tom.Event.removeEvent(document, "mousedown", this.events.start, false);
    this.onStart(e);
    if (this.opacity) {
      tom.Effect.setOpacity(this.element, this.opacity);
    }
	tom.Event.stop(e);
  },
  drag: function(e) {
    this.mouse.now = tom.Event.pointer(e);
    this.value.now.x = this.mouse.now.x - this.mouse.pos.x;
    this.value.now.y = this.mouse.now.y - this.mouse.pos.y;
    
    if (this.droppable) {
      tom.dnd.Droppable.detectDrag(e, this);
    }
    
    this.element.style.left = this.value.now.x + "px";
    this.element.style.top = this.value.now.y + "px";
	
	this.onDrag(e);
	tom.Event.stop(e);
  },
  stop: function(e) {
    tom.Event.removeEvent(document, 'mousemove', this.events.drag, false);
    tom.Event.removeEvent(document, 'mouseup', this.events.stop, false);
    tom.Event.removeEvent(document, 'keypress', this.events.cancel, false);
    if (this.droppable) {
      tom.dnd.Droppable.detectComplete(e, this);
    }
    if (this.revert()) {
      this.reveal();
    }
    this.onComplete(e);
    if (this.opacity) {
      tom.Effect.setOpacity(this.element, 1);
    }
    this.isOnDrag = false;
  },
  cancel: function(e) {
    if (this.revert()) {
      this.reveal();
    }
    if (this.droppable) {
      tom.dnd.Droppable.hideDropTmpDiv();
    }
    tom.Event.removeEvent(document, 'mousemove', this.events.drag, false);
    tom.Event.removeEvent(document, 'mouseup', this.events.stop, false);
    tom.Event.removeEvent(document, 'keypress', this.events.cancel, false);
    this.onCancel(e);
    this.isOnDrag = false;
  },
  _findAvailNextSibling: function(elem) {
    var sibling = elem;
    while (sibling = sibling.nextSibling) {
      if (sibling.nodeType != 3) {
        return sibling;
      }
    }
    return null;
  },
  _findAvailPreviousSibling: function(elem) {
    var sibling = elem;
    while (sibling = sibling.previousSibling) {
      if (sibling.nodeType != 3) {
        return sibling;
      }
    }
    return null;
  },
  resetBound: function() {
  	tom.Position.makeBound(this.element, this.bound);
  },
  saveSavePoint: function() {
      this.savePoint.parent = this.element.parentNode;
      this.savePoint.nextSibling = this._findAvailNextSibling(this.element);
      this.savePoint.previousSibling = this._findAvailPreviousSibling(this.element);
      this.savePoint.left = this.value.now.x;
      this.savePoint.top = this.value.now.y;
//      this.savePoint.width = tom.Element.getStyle(this.element, "width");
//      this.savePoint.height = tom.Element.getStyle(this.element, "height");
      this.savePoint.position = tom.Element.getStyle(this.element, "position");
  },
  reveal: function() {
    this.element.style.left = (parseInt(this.savePoint.left) || 0) + "px";
    this.element.style.top = (parseInt(this.savePoint.top) || 0) + "px";
//    this.element.style.width = parseInt(this.savePoint.width) + "px";
//    this.element.style.height = parseInt(this.savePoint.height) + "px";
    this.element.style.position = this.savePoint.position;
//    if ((!this.savePoint.nextSibling == this.element.nextSibling && this.savePoint.previousSibling == this.element.previousSibling && this.savePoint.parent == this.element.parentNode)) {
      if (this.savePoint.nextSibling) {
        this.savePoint.parent.insertBefore(this.element, this.savePoint.nextSibling);
      } else {
        this.savePoint.parent.appendChild(this.element);
      }
//    }
  }
});


tom.dnd.Droppable = tom.Class.create();
tom.extend(tom.dnd.Droppable.prototype, {
  initialize: function(el, options) {
    var fields = {
      element: null,
	
	  draggables: new tom.util.Set(),
	
	  mode: "normal",
	  //normal
	  //vertical、verticalUp、verticalDown
	  //horizontal、horizontalLeft、horizontalRight
	  withinMode: "normal",
	  
	  currentMoveIndex: 65535,
	
	  onStart: tom.fnEmpty,
	  onDrag: tom.fnEmpty,
	  onComplete: tom.fnEmpty
    };
    tom.extend(this, fields, options || {});
    this.element = $(el);
    _this = this;
    
    tom.dnd.Droppable.droppables.add(this);
  },
  add: function(dg) {
    this.draggables.add(dg);
    dg.droppable = this;
    if (dg.element.parentNode != this.element) {
      this.element.appendChild(dg.element);
    }
  },
  remove: function(dg) {
    this.draggables.removeObject(dg);
    dg.draggable = null;
    if (dg.element.parentNode == this.element) {
      this.element.removeChild(dg.element);
    }
  },
  start: function(e, dg) {
    this.handler[this.mode].start(e, this, dg);
    this.onStart(e, dg);
  },
  drag: function(e, dg) {
    
    this.handler[this.mode].drag(e, this, dg);
    this.onDrag(e, dg);
  },
  stop: function(e, dg) {
    this.handler[this.mode].stop(e, this, dg);
    this.onComplete(e, dg);
  },
  handler:{
    normal: {
        drag: tom.fnEmpty,
        stop: tom.fnEmpty
      },
    horizontal: {
        drag: function(e, dp, dg) {
          if (!dg.hasStartMove) {
            if (dg.position.element != "absolute") {
                dg.mouse.start = dg.mouse.now = tom.Event.pointer(e);
                dg.value.now = {x: tom.Position.cumulativeOffset(dg.element).left, y: tom.Position.cumulativeOffset(dg.element).top};
                dg.mouse.pos.x = dg.mouse.start.x - dg.value.now.x;
                dg.mouse.pos.y = dg.mouse.start.y - dg.value.now.y;
                dg.element.style.width = dg.element.offsetWidth;
            }
            var pos = tom.Position.cumulativeOffset(dg.element);
            
            dg.element.parentNode.removeChild(dg.element);
            document.body.appendChild(dg.element);
            dg.element.style.position = "absolute";
            
            dg.value.now.x = dg.mouse.now.x - dg.mouse.pos.x;
            dg.value.now.y = dg.mouse.now.y - dg.mouse.pos.y;
            
            dg.hasStartMove = true;
          }
          dp.dragHV(e, dp, dg, "horizontal");
        },
        stop: function(e, dp, dg) {
          dp.dropHV(e, dp, dg, "horizontal");
        }
      },
    vertical: {
        drag: function(e, dp, dg) {
          if (!dg.hasStartMove) {
            if (dg.position.element != "absolute") {
                dg.mouse.start = dg.mouse.now = tom.Event.pointer(e);
                dg.value.now = {x: tom.Position.cumulativeOffset(dg.element).left, y: tom.Position.cumulativeOffset(dg.element).top};
                dg.mouse.pos.x = dg.mouse.start.x - dg.value.now.x;
                dg.mouse.pos.y = dg.mouse.start.y - dg.value.now.y;
                dg.element.style.width = dg.element.offsetWidth;
            }
            var pos = tom.Position.cumulativeOffset(dg.element);
            
            dg.element.parentNode.removeChild(dg.element);
            document.body.appendChild(dg.element);
            dg.element.style.position = "absolute";
            
            dg.value.now.x = dg.mouse.now.x - dg.mouse.pos.x;
            dg.value.now.y = dg.mouse.now.y - dg.mouse.pos.y;
            
            dg.hasStartMove = true;
          }
          dp.dragHV(e, dp, dg, "vertical");
        },
        stop: function(e, dp, dg) {
          dp.dropHV(e, dp, dg, "vertical");
        }
      }
  },
  within: function(bound, x, y) {
  	return x > bound.x && x < bound.x + bound.w && y > bound.y && y < bound.y + bound.h;
  },
  withinBefore: function(bd, xy, mode) {
    return xy > bd[mode == "vertical" ? "y" : "x"] && (xy <= bd[mode == "vertical" ? "y" : "x"] + bd[mode == "vertical" ? "h" : "w"] / 2);
  },
  withinAfter: function(bd, xy, mode) {
    return xy > (bd[mode == "vertical" ? "y" : "x"] + bd[mode == "vertical" ? "h" : "w"] / 2) && (xy < bd[mode == "vertical" ? "y" : "x"] + bd[mode == "vertical" ? "h" : "w"]);
  },
  dragHV: function(e, droppable, draggable, mode) {
    var xy = (mode == "vertical" ? "y" : "x");
    var wh = (mode == "vertical" ? "h" : "w");
    var widthHeight = (mode == "vertical" ? "height" : "width");
    var offsetWidthHeight = (mode == "vertical" ? "offsetHeight" : "offsetWidth");
    
    var p = tom.Event.pointer(e);
  	var dgs = droppable.draggables;
  	//如果droppable内无draggable或者只有正在拖动的draggable
  	var index = -1;
	
	var dg = null;
	var bd = {x: 0, y: 0, w: 0, h: 0};
	var isSelf = false;
	if (dgs.size() == 0) {
	  index = -2048;
	} else if (dgs.size() == 1) {
	  dg = dgs.get(0);
	  if (dg == draggable) {
	  	index = 1024;
	  } else {
	  	if (droppable.currentMoveIndex == 1023) {
	  	  tom.extend(bd, dg.bound);
	  	  bd[xy] = dg.bound[xy] + tom.dnd.Droppable.getDropTmpDiv()[offsetWidthHeight];
	  	} else if (droppable.currentMoveIndex == 1025) {
	  	  tom.extend(bd, dg.bound);
	  	} else {
	  	  tom.extend(bd, dg.bound);
	  	}
	  	if (this.withinBefore(bd, p[xy], mode) || bd[xy] >= p[xy]) {
	  	  index = 1023;
	  	} else if (this.withinAfter(bd, p[xy], mode) || bd[xy] + bd[wh] <= p[xy]) {
	  	  index = 1025;
	  	}
	  }
	} else {
	  var prevDg, nextDg;
	  var offsetHV = tom.dnd.Droppable.getDropTmpDiv()[offsetWidthHeight] + 8;
      var selfIndex = dgs.indexOf(draggable);
	  for (var j = 0; j < dgs.size(); j++) {
	  	dg = dgs.get(j);
	  	
	  	prevDg = j == 0 ? null : dgs.get(j - 1);
	  	nextDg = j == dgs.size() - 1 ? null : dgs.get(j + 1);
	  	nextNextDg = j == dgs.size() - 2 ? null : dgs.get(j + 2);
	    if (j >= droppable.currentMoveIndex) {
	      if (selfIndex != -1 && j >= selfIndex) {
	      	tom.extend(bd, dg.bound);
	      	bd[xy] = dg.bound[xy] + offsetHV - draggable.bound[xy];
	      } else {
	      	tom.extend(bd, dg.bound);
	      	bd[xy] = dg.bound[xy] + offsetHV;
	      }
	    } else {
	      if (selfIndex != -1 && j >= selfIndex) {
	      	tom.extend(bd, dg.bound - draggable.bound[wh]);
	      } else {
	        tom.extend(bd, dg.bound);
	      }
	    }
	    if (this.withinBefore(bd, p[xy], mode)) {
	      if (dg == draggable) {
	      	if (nextDg) {
	      	  index = j;
	      	  isSelf = true;
	      	} else {
	      	  index = dgs.size();
	      	}
	      } else {
	  	    index = j;
	      }
	      break;
	  	} else if (this.withinAfter(bd, p[xy], mode)) {
          if (nextDg && nextDg == draggable) {
          	if (nextNextDg) {
          	  index = j + 2;
          	} else {
          	  index = dgs.size();
          	}
          } else {
            index = j + 1;
          }
	  	  break;
	  	} else if (p[xy] <= bd[xy]) {
	  	  if (prevDg) {
	  	  	 if (p[xy] >= prevDg.bound[xy]) {
		      if (dg == draggable) {
		      	index = j;
		      	isSelf = true;
		      } else {
		  	    index = j;
		      }
	  	  	 }
	  	  } else {
		      if (dg == draggable) {
		      	index = j;
		      	isSelf = true;
		      } else {
		  	    index = j;
		      }
	  	  }
	      break;
	  	} else if (p[xy] >= bd[xy] + bd[wh]) {
	  	  if (nextDg) {
	  	    if (p[xy] <= nextDg.bound[xy]) {
		      if (dg == draggable) {
		      	index = j + 1;
		      } else {
		        if (nextDg == draggable) {
		          if (nextNextDg) {
		          	index = j + 2;
		          } else {
		          	index = dgs.size();
		          }
		        } else {
		          index = j + 1;
		        }
		      }
		      break;
	  	    }
	  	  } else {
	  	    index = j + 1;
	  	    break;
	  	  }
	  	}
	  }
	}

   if (index != droppable.currentMoveIndex) {
	  droppable.currentMoveIndex = index;
	  
	  if (mode == "vertical") {
	      tom.dnd.Droppable.getDropTmpDiv().className = "";
    	  tom.dnd.Droppable.getDropTmpDiv().style.position = "relative";
    	  tom.dnd.Droppable.getDropTmpDiv().style.display = "block";
    	  tom.dnd.Droppable.getDropTmpDiv().style['float'] = "none";
    	  tom.dnd.Droppable.getDropTmpDiv().style.left = "auto";
    	  tom.dnd.Droppable.getDropTmpDiv().style.top = "auto";
    	  tom.dnd.Droppable.getDropTmpDiv().style.width = "auto";
    	  tom.dnd.Droppable.getDropTmpDiv().style[widthHeight] = draggable.bound[wh] + "px";
	  } else {
	      tom.dnd.Droppable.getDropTmpDiv().className = "navtab";
          tom.dnd.Droppable.getDropTmpDiv().style['float'] = "left";
          tom.dnd.Droppable.getDropTmpDiv().style.display = "block";
    	  tom.dnd.Droppable.getDropTmpDiv().style.background = "";
    	  tom.dnd.Droppable.getDropTmpDiv().style.left = "auto";
    	  tom.dnd.Droppable.getDropTmpDiv().style.top = "auto";
    	  tom.dnd.Droppable.getDropTmpDiv().style.height = draggable.bound.h + "px";
    	  tom.dnd.Droppable.getDropTmpDiv().style[widthHeight] = draggable.bound[wh] + "px";
	  }

	  
	  tom.dnd.Droppable.getDropTmpDiv().parentNode.removeChild(tom.dnd.Droppable.getDropTmpDiv());
	  if (index == -2048) {
	  	droppable.element.appendChild(tom.dnd.Droppable.getDropTmpDiv());
	  } else if (index == 1024) {
	  	droppable.element.appendChild(tom.dnd.Droppable.getDropTmpDiv());
	  } else if (index == 1023) {
	  	droppable.element.insertBefore(tom.dnd.Droppable.getDropTmpDiv(), droppable.draggables.get(0).element);
	  } else if (index == 1025) {
	  	droppable.element.appendChild(tom.dnd.Droppable.getDropTmpDiv());
	  } else if (index == dgs.size()) {
	  	droppable.element.appendChild(tom.dnd.Droppable.getDropTmpDiv());
	  } else {
	  	if (isSelf) {
	  	  droppable.element.insertBefore(tom.dnd.Droppable.getDropTmpDiv(), droppable.draggables.get(index + 1).element);  
	  	} else {
	  	  droppable.element.insertBefore(tom.dnd.Droppable.getDropTmpDiv(), droppable.draggables.get(index).element);
	  	}
	  }
	}
  },
  dropHV: function(e, droppable, draggable, mode) {
    if (draggable.hasStartMove) {
      tom.Position.prepare();
      var p = tom.Event.pointer(e);
    
      var index = droppable.currentMoveIndex;
      
      switch (index) {
        case -2048:
        case 1024:
        case 1025:
        case droppable.draggables.size():
          draggable.savePoint.nextSibling = null;
          break;
        case 1023:
          draggable.savePoint.nextSibling = droppable.draggables.get(0).element;
          break;
        default:
          draggable.savePoint.nextSibling = droppable.draggables.get(index).element
          break;
      }
      draggable.savePoint.parent = droppable.element;
      
      try {
        draggable.droppable.remove(draggable);
      } catch (err) {}
      droppable.draggables.add(draggable);
      
      draggable.element.style.position = "relative";
	  draggable.element.style.left = "0";
	  draggable.element.style.top = "0";
      draggable.element.style.width = "";
      
//      droppable.draggables.add(draggable);
      draggable.droppable = droppable;
	  draggable.hasStartMove = false;
    }
  }
});
tom.extend(tom.dnd.Droppable, {
  droppables: new tom.util.List(),
  ghost: null,
  detectDragStart: function(e, dg) {
      dg.resetBound();
      tom.dnd.Droppable.droppables.each(function(dp) {
          if (dg.droppableFilter(dp)) {
            dp.draggables.each(function(o) {
                o.resetBound();
              });
            if (dp.mode == "horizontal") {
              dp.draggables.sort(function(a, b) { return a.bound.x - b.bound.x; });
            } else if (dp.mode == "vertical") {
              dp.draggables.sort(function(a, b) { return a.bound.y - b.bound.y; });
            }
            dp.onStart(e, dg);
          }
        });
  },
  detectDrag: function(e, dg) {
    var curDp = null;
    this.droppables.each(function(dp) {
        if (dg.droppableFilter(dp)) {
          if (tom.Position.within(dp.element, dg.mouse.now.x, dg.mouse.now.y)) {
            dp.drag(e, dg);
            curDp = dp;
            return false;
          }
        }
      });
    if (curDp == null) {
      this.droppables.each(function(dp) {
          if (dg.droppableFilter(dp)) {
            if (dp.withinMode == "vertical") {
                if (tom.Position.withinVertical(dp.element, dg.mouse.now.x)) {
                  dp.drag(e, dg);
                  curDp = dp;
                  return false;
                }              
            } else if (dp.withinMode == "horizontal") {
                if (tom.Position.withinHorizontal(dp.element, dg.mouse.now.y)) {
                  dp.drag(e, dg);
                  curDp = dp;
                  return false;
                } 
            }
          }
        });
    this.droppables.each(function(dp) {
        if (dg.droppableFilter(dp)) {
          if (dp != curDp) {
            dp.currentMoveIndex = 65535;
          }
        }
      });
    }
  },
  detectComplete: function(e, dg) {
    var curDp = null;
    this.droppables.each(function(dp) {
        if (dg.droppableFilter(dp) && tom.Position.within(dp.element, dg.mouse.now.x, dg.mouse.now.y)) {
          dp.stop(e, dg);
          return false;
        }
      });
    if (curDp == null) {
      this.droppables.each(function(dp) {
          if (dg.droppableFilter(dp)) {
            if (dp.withinMode == "vertical") {
              if (tom.Position.withinVertical(dp.element, dg.mouse.now.x)) {
                dp.stop(e, dg);
                return false;                  
              }
            } else if (dp.withinMode == "horizontal") {
              if (tom.Position.withinHorizontal(dp.element, dg.mouse.now.y)) {
                dp.stop(e, dg);
                return false;
              }
            }
          }
        });
    }
    tom.dnd.Droppable.hideDropTmpDiv();
  },
  
  dropTmpDiv: false,
  getDropTmpDiv: function() {
  	if (!this.dropTmpDiv) {
  	  var elem = document.createElement("DIV");
  	  elem.style.border = "1px dashed #f00";
  	  elem.style.display = "none";
  	  document.body.appendChild(elem);
      this.dropTmpDiv = elem;
  	}
  	return this.dropTmpDiv;
  },
  hideDropTmpDiv: function() {
  	var dtd = this.getDropTmpDiv();
  	dtd.style.display = "none";
  	if (dtd.parentNode) {
  	  dtd.parentNode.removeChild(dtd);
  	}
  	document.body.appendChild(dtd);
  }
});

