dragME -Objeto para hacer drag & drop en Javascript

Estos días he tenido muy poco tiempo libre, o mejor dicho nada de tiempo libre, pero me las he arreglado para desarrollar este objeto. Espero que les guste. Primero por que no ves un ejemplo en funcionamiento (No soporte para IE)

Como todos los objetos que hago, este tampoco soporta IE, pero soporte se implementará en siguientes versiones. El objeto depende en domCore, otro objeto que no les había presentado por que está  muy precario por ahora.

El uso de este objeto es muy simple, pero primero veamos que es lo que hay dentro:

//draggMe 0.1
//By Buzu
//Depends on domCore by Buzu.

var dragMe = {
	version : '0.1',
	working : false,
	catchers : new Array(),
	overCatcher : false,
	lastOver : null,
	startDrag : function(e,cual,dejarClon){
		e.preventDefault();
		var dC = dejarClon || false;
		var elem = cual.cloneNode(true);
		elem.difX = e.clientX - cual.offsetLeft;
		elem.difY = e.clientY - cual.offsetTop;
		elem.style.zIndex = 900;
		domCore.esIE()? elem.style.filter = 'alpha(opacity = 70)' : elem.style.opacity = 0.7 ;
		dragMe.crearProxyCatcher();
		//uso window.onmousemove y onmouseup para poder eliminar luego
		//el evento. De otra manera habria que usar removeEventListener
		//y su equivalente en IE.
		window.onmousemove = function(e){
			dragMe.working = true;
			domCore.getByName('body')[0].appendChild(elem);
			elem.style.position = 'absolute';
			elem.style.top = (e.clientY - elem.difY) + 'px';
			elem.style.left = (e.clientX - elem.difX) + 'px';
			dragMe.onDragging(parseInt(elem.style.top), parseInt(elem.style.left));
		}
		window.onmouseup =  function(){
			if(!dC){domCore.getByName('body')[0].removeChild(elem)}
			window.onmousemove = '';
			//llamar solo si se ha hecho drag para evitar que se llame cada
			//que se hace click en el elemento sin arrastrarlo
			if(dragMe.working){
				dragMe.onDrop(dragMe.overCatcher, parseInt(elem.style.top), parseInt(elem.style.left));
			}
			//evitar que se repita la llamada a la funcion si se hace click
			//nuevamente.
			window.onmouseup = '';
			if(dragMe.overCatcher){
				//pasamos referencia al elemento original para saber cual
				//fue soltado y poder hacer algo con el...
				dragMe.lastOver.originalElem.func(dragMe.lastOver.originalElem);
				dragMe.overCatcher = false;
			}
			dragMe.removeProxyCatcher();
			dragMe.working = false;
		}
	},
	registerCatcher : function(catcher, func){
		catcher.func = func || function(){};
		dragMe.catchers[dragMe.catchers.length] = catcher;
	},
	crearProxyCatcher : function(){
		for(i=0; dragMe.catchers[i]; i++){
			var catcher = dragMe.catchers[i];
			var proxy = catcher.cloneNode(true);
			//guardamos referencia a proxy para poder eliminarla luego.
			catcher.proxy = proxy;
			proxy.originalElem = catcher;
			proxy.style.position = 'absolute';
			proxy.style.opacity = 0;
			proxy.style.filter = 'alpha(opacity=0)';
			proxy.style.top = catcher.offsetTop + 'px';
			proxy.style.left = catcher.offsetLeft + 'px';
			proxy.style.backgroundColor = '#F00';
			proxy.style.zIndex = 1000;
			domCore.getByName('body')[0].appendChild(proxy);
			proxy.onmouseover = function(){
				dragMe.overCatcher = true;
				dragMe.lastOver = this;
			}
			proxy.onmouseout = function(){
				dragMe.overCatcher = false;
			}
		}
	},
	removeProxyCatcher : function(){
		var b = domCore.getByName('body')[0];
		for(i=0; dragMe.catchers[i]; i++){
			b.removeChild(dragMe.catchers[i].proxy);
		}
	},
	onDrop : function(){},
	onDragging : function(){}
}

No voy a explicar lo que está pasando ahí dentro por que me tomaría mucho tiempo, pero si te interesa saber algo sobre el code, solo pregunta en los comentarios.

Una vez que has agregado el objeto, solo tienes que iniciar los elementos que serán arrastrables.

<meta name="keywords" content=""/>
<meta name="description" content=""/>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<meta http-equiv="content-type" content="application/xhtml+xml; charset=UTF-8"/>
<meta http-equiv="content-style-type" content="text/css"/>
<meta http-equiv="expires" content="0"/>
<script type="text/javascript" src="B_libs/domCore.js" ></script>
<script type="text/javascript" src="B_libs/dragMe.js"></script>
<script type="text/javascript">
	domCore.addEvent(window, 'load', function(){
		dragMe.registerCatcher(domCore.getById('catcher'));
		dragMe.registerCatcher(domCore.getById('catcher2'), function(){
			alert('dropped2');
		});
		domCore.addEvent(domCore.getById('drag'), 'mousedown', function(e){
			dragMe.startDrag(e, this);
			dragMe.onDrop = function(i,t,l){
				if(i){
					alert('in');
				}else{
					alert('out');
				}
			}
		});
		domCore.addEvent(domCore.getById('drag2'), 'mousedown', function(e){
			dragMe.startDrag(e, this);
			dragMe.onDrop = function(i,t,l){
				if(i){
					alert('adentro');
				}else{
					alert('afuera');
				}
			}
		});
	});
</script>
<style type="text/css">
	#catcher, #catcher2{
		width: 150px;
		height: 150px;
		border: 1px dashed #CCC;
	}
</style>
</head>
<body>
	<p id="drag">Hola, arrastrame!</p>
	<p id="drag2">Hola, arrastrame!</p>
	<div id="catcher"></div>
	<div id="catcher2"></div>
</body>

El objeto tiene tres métodos principales:

registerCatcher() Este método sirve para registrar las canastas donde queremos que se suelten los elementos. Acepta dos parámetros: El primero es una referencia al elemento que queremos que sea la canasta, el segundo es una función la cual queremos que se ejecute cuando soltemos algo dentro de la canasta. El primer parámetro es obligatorio, el segundo es opcional.

Ejemplo:

dragMe.registerCatcher(domCore.getById('catcher'));

startDrag() Este es el método que inicia el arrastre del elemento. Recibe dos parámetros, el primero es e o el evento en si. Solo ponle una e y es todo. Este parámetro es completamente necesario. El segundo parámetro es el elemento que se quiere arrastrar. Lo más logico es que sea una referencia al elemento sobre el que se hizo click, por lo que lo más seguro es que siempre le tengas que pasar this . Este también es necesario. startDrag() por fuerza tiene que ser ejecutado onmousedown, no lo agregues a otro evento por que dará problemas. Esto no es un error de programación o de planeación. Lo mas lógico es que arrastres un objeto con el mouse, por lo que esta es la forma más lógica de hacerlo.

Ejemplo:

domCore.addEvent(domCore.getById('drag'), 'mousedown', function(e){
	dragMe.startDrag(e, this);
	dragMe.onDrop = function(i,t,l){
		if(i){
			alert('in');
		}else{
			alert('out');
		}
	}
});

El tercer método es onDrop() este se ejecuta cuando el elemento siendo arrastrado es soltado. Este pequeño te regresa tres valores los cuales puedes capturar con la llamada al evento. El primero es un boleano (true o false) que indica si el elemento fue soltado dentro de una canasta o no. El segundo es el valor para top y el tercero el valor para left. Es decir que con el segundo y tercer valor puedes saber exactamente las coordenadas en las que el elemento fue soltado.

Ejemplo:

dragMe.onDrop = function(i,t,l){
	if(i){
		alert('in');
	}else{
		alert('out');
	}
}

onDrop y la función pasada como segundo parámetro a registerCatcher parecen hacer lo mismo, por lo que te preguntarás, que caso tiene tener ambas? la respuesta es simple, onDrop te permite ver si el elemento fue soltado en una canasta, mientras que la función en registerCatcher te permite saber si algo se solto en esa canasta. En otras palabras, con onDrop, el elemento te dice “me has soltado en una canasta”, mientras que la función en registerCatcher te dice “has soltado algo dentro de mi.” Es como ver el drop desde dos perspectivas, la del objeto arrastrable y la de la canasta.

Si estudias el code encontrarás más sorpresas, por ejemplo, un método onDragging() que se ejecuta durante el tiempo de arrastre y otras curiosidades.

Espero que les sirva este objeto, esperen soporte para IE en la siguiente versión. Como siempre, comentarios y sugerencias son bienvenidos.