/**
 * jQuery PickWhip Plugin
 *
 *Copyright (c) 2008 Matthieu Larcher
 *
 * version: 1.5.1 (16/11/2008)
 * tested with jQuery v1.2.2 
 * tested on PC with : IE6, IE7, FF1.5, FF2, FF3, Opera 8.54, Opera 9.10, Safari 3.0.4
 * tested on mac with : FF2, Safari 1.3.2
  *
 * known issues :
 * Mac FF1.5 seems to have hard time rendering the whip on mousemove
 * not working on IE<6
 * FF selection disabling can cause problem with other scripts listening to the mousedown event
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Revision: Unknown
 *
 * @name pickWhipInit
 * @type Function
 * @param String		imageDir					directory where the images needed by the whip can be found
 *
 * @name  PickWhip
 * @type  Function
 * @param Mixed		droppables					elements that will react to the pickWhip (can be anything that will be identified through $(droppables)
 * @param Hash			options					additional options
 * @param CssClass		options[linkedHoverClass]		css class to give to the already linked potential target when hovered (default: "linkedWhipHoverTarget")
 * @param CssClass		options[unlinkedHoverClass]		css class to give to the non-linked potential target when hovered (default: "unlinkedWhipHoverTarget")
 * @param Boolean		options[showLinks]			set linkClass/unlinkClass on pickWhip start ? (default: true)
 * @param CssClass		options[linkedClass]			css class to give to the already linked target when showLinks is true (default: "linkedWhipTarget")
 * @param CssClass		options[unlinkedClass]			css class to give to the non-linked potential target when showLinks is true (default: "unlinkedWhipTarget")
 * @param Array			options[linkList]				list of the  jQuery elements already linked to this source or of associative Arrays (source, target)
 * @param Function		options[callback]				callback function(jQuerySourceElement, jQueryDroppedOntoElement)
 */


(function($) {
	/**
	** initialisation
	**/
	$.pickWhip = 
	{
		// declare droppables checks
		setHoverTrue: function (e) 
		{
			// show unlinkImg if already linked
			if (this.alreadyLinked) {
				$.pickWhip.unlinkImg.visible = true;
				$(this).addClass($.pickWhip.settings.linkedHoverClass);
			} else {
				$(this).addClass($.pickWhip.settings.unlinkedHoverClass);
			}
			this.hovering = true;
		},
		setHoverFalse: function (e) 
		{
			this.hovering = false;
			if (this.alreadyLinked) {
				$(this).removeClass($.pickWhip.settings.linkedHoverClass);
			} else {
				$(this).removeClass($.pickWhip.settings.unlinkedHoverClass);
			}
			$.pickWhip.unlinkImg.visible = false;
		},
		// create an empty array that will contain all linkLists
		allLinks: new Array(),
		// init
		init: function(options)
		{
			var settings = {
				imageDir	: 'whipImg/'
			}
			if (options) {
				$.extend(settings, options);
				// check if there is a trailing slash and add it if necessary
				var imageDir = settings.imageDir;
				if (imageDir.substring(imageDir.length-1) != '/' && imageDir.substring(imageDir.length-1) != '\\') {
					//try adding the correct kind of slash
					var lastSlash = imageDir.lastIndexOf('/');
					var lastAntiSlash = imageDir.lastIndexOf('\\');
					imageDir = (lastSlash >= lastAntiSlash) ? imageDir + '/' : imageDir + '\\';
					settings.imageDir = imageDir;
				}
			}
			$.pickWhip.imageDir = settings.imageDir;
			// preload other images 
			var imagesToPreload = Array('diag_bl-tr.gif', 'diag_tl-br.gif', 'diag_tl-br_anim.gif', 'diag_tr-bl_anim.gif', 'diag_bl-tr_anim.gif', 'diag_br-tl_anim.gif', 'unlink.gif');
			
			for (var i = 0; i<imagesToPreload.length; i++)
			{
				//$('<img/>').attr('src', $.pickWhip.imageDir + imagesToPreload[i]);
				img = new Image();
				img.src = $.pickWhip.imageDir + imagesToPreload[i];
			}
			// create holder
			$('body').append('<div id="whipDiv" style="z-index:1000;position:absolute;top:0;left:0;width:1px;height:1px;">\
			<img id="whipImg" style="position:absolute;display:none;" src="' + $.pickWhip.imageDir + 'blank.gif"/>\
			<img id="unlinkWhipImg" style="position:absolute;display:none;" src="' + $.pickWhip.imageDir + 'blank.gif"/>\
			</div>');
			$.pickWhip.content = $('#whipDiv');
			$.pickWhip.img = $('#whipImg', $.pickWhip.content);
			
			$.pickWhip.unlinkImg = $('#unlinkWhipImg');
		},
		//
		settings: 
		{
			linkedHoverClass	: 'linkedWhipHoverTarget',
			unlinkedHoverClass	: 'unlinkedWhipHoverTarget',
			linkList			: new Array,
			showLinks			: true,
			linkedClass			: 'linkedWhipTarget',
			unlinkedClass		: 'unlinkedWhipTarget',
			callback			: {}
		}
	};
	
	
	/**
	** binding the pickwhip
	**/
	$.fn.pickWhip = function(droppables, options)
    {
		this.each(function()
		{
			var settings = $.extend({}, $.pickWhip.settings, options);
			//
			var tmpLinkList = new Array();
			for(var i = 0; i < settings.linkList.length; i++) {
				tmpLinkList[i] = new Array();
				// if linkList[i] is an associative Array
				if(settings.linkList[i][1]){
					tmpLinkList[i][0] = settings.linkList[i][0];
					tmpLinkList[i][1] = settings.linkList[i][1];
				} else {
				// if linkList[i] is a jQuery target
					tmpLinkList[i][0] = $(this);
					tmpLinkList[i][1] = settings.linkList[i];
				}
			}
			settings.linkList = tmpLinkList;
			$.pickWhip.allLinks.push(settings.linkList);
			// what happens when launching the pickwhip
			$(this).bind('mousedown', function (e)
			{
				$.pickWhip.source = $(this);
				$.pickWhip.linkList = settings.linkList;
				$.pickWhip.settings = settings;
				$.pickWhip.droppedOnto = undefined;
				$.pickWhip.unlinkImg.visible = false;
				//save origin of click
				$.pickWhip.origX = e.pageX;
				$.pickWhip.origY = e.pageY;
				// disable selection ( IE4+ )
				$(this).get(0).onselectstart = function () { return false; }
				// init droppables
				$.pickWhip.droppables = $(droppables).not(this);
				for (var i = $.pickWhip.droppables.length; i--;) {
						// set defaults
						$.pickWhip.droppables.get(i).hovering = false;
						$.pickWhip.droppables.get(i).alreadyLinked = false;
						if ($.pickWhip.settings.showLinks) {
							$($.pickWhip.droppables.get(i)).addClass($.pickWhip.settings.unlinkedClass)
						}
						// set linked status to target elements
						for (var j=0; j<$.pickWhip.linkList.length; j++) {
							if ($.pickWhip.linkList[j][0].get(0) == $.pickWhip.source.get(0) 
							&& $.pickWhip.linkList[j][1].get(0) == $.pickWhip.droppables.get(i)){
								$.pickWhip.droppables.get(i).alreadyLinked = true;
								if ($.pickWhip.settings.showLinks) {
									$($.pickWhip.droppables.get(i)).removeClass($.pickWhip.settings.unlinkedClass);
									$($.pickWhip.droppables.get(i)).addClass($.pickWhip.settings.linkedClass);
								}
							}
						}
						// bind mouseover functions
						$($.pickWhip.droppables.get(i)).bind('mouseover', $.pickWhip.setHoverTrue);
						$($.pickWhip.droppables.get(i)).bind('mouseout', $.pickWhip.setHoverFalse);
				}
				/**
				** what happens when moving the mouse
				**/
				$().bind('mousemove.pickWhip',function(e)
				{
					$.pickWhip.unlinkImg.attr('src',$.pickWhip.imageDir + 'unlink.gif')
					// set scroll 
					var ev = e || event;
					var pagePosX = ev.pageX;
					var pagePosY = ev.pageY;
					var mousePosX = ev.clientX;
					var mousePosY = ev.clientY;
					// (standards compliant browsers (mozilla/netscape/opera/IE7)  || IE6 in standards compliant mode)
					var viewPortHeight = window.innerHeight || document.documentElement.clientHeight;
					var viewPortWidth = window.innerWidth || document.documentElement.clientWidth;
					// scroll
					if (mousePosY < 50 && pagePosY > 0) {
						window.scrollBy(0,-20);
					}
					if (viewPortHeight - mousePosY < 50) {
						window.scrollBy(0,20);
					}
					if (mousePosX < 50 && pagePosX > 0) {
						window.scrollBy(-20,0);
					}
					if (viewPortWidth - mousePosX < 50) {
						window.scrollBy(20,0);
					}
					// define new values
					if (e.pageX > $.pickWhip.origX) {
						$.pickWhip.zoneX = 'right';
						newWidth = e.pageX-$.pickWhip.origX;
						newX = $.pickWhip.origX;
						// prevent image from being over the dropzone
						newX -= 1;
					} else {
						$.pickWhip.zoneX = 'left';
						newWidth = $.pickWhip.origX-e.pageX;
						newX = $.pickWhip.origX-newWidth;
						newX += 1;
					}
					if (e.pageY > $.pickWhip.origY) {
						$.pickWhip.zoneY = 'bottom';
						newHeight = e.pageY - $.pickWhip.origY;
						newY = $.pickWhip.origY;
						newY -= 1;
					} else {
						$.pickWhip.zoneY = 'top';
						newHeight = $.pickWhip.origY-e.pageY;
						newY = $.pickWhip.origY-newHeight;
						newY += 1;
					}
					if ($.pickWhip.zoneX == 'right' && $.pickWhip.zoneY == 'top' || $.pickWhip.zoneX == 'left' && $.pickWhip.zoneY == 'bottom' ) {
						$.pickWhip.img.attr('src', $.pickWhip.imageDir + 'diag_bl-tr.gif');
					} else {
						$.pickWhip.img.attr('src', $.pickWhip.imageDir + 'diag_tl-br.gif');
					}
					//move unlink img
					var newUnlinkX = e.pageX-26;
					var newUnlinkY = e.pageY+1;
					var unlinkDisplay = ($.pickWhip.unlinkImg.visible) ? 'display:block' : 'display:none';
					$.pickWhip.unlinkImg.attr('style','position:absolute; top:'+newUnlinkY+'px; left:'+newUnlinkX+'px;'+unlinkDisplay)
					// set new values
					$.pickWhip.img.attr('style','position:absolute; top:'+newY+'px;left:'+newX+'px;');
					$.pickWhip.img.attr('height', newHeight);
					$.pickWhip.img.attr('width', newWidth);
					$.pickWhip.content.show();
					
				});

				//if the mouse leaves the screen
				$('body').bind('mouseout.pickWhip',function(e){
					var ev = e || window.event;
					// FF
					if(ev.target){
						if(ev.target.tagName == 'HTML'){
							$().trigger('mouseup.pickWhip');
						}
					} else {
					// IE
						if(!ev.toElement){
							$().trigger('mouseup.pickWhip');
							return;
						}
						if(ev.toElement.tagName == "HTML"){
							$().trigger('mouseup.pickWhip');
						}
					}
				});

				/**
				** what happens  releasing the pickwhip
				**/
				$().bind('mouseup.pickWhip',function(e)
				{
					//get the target onto which the whip is released
					for (i=0; i < $.pickWhip.droppables.length; i++) {
						if ($.pickWhip.droppables.get(i).hovering) {
							if ($.pickWhip.droppables.get(i).alreadyLinked) {
								$($($.pickWhip.droppables).get(i)).removeClass($.pickWhip.settings.linkedHoverClass);
								var linking = false;
							} else {
								$($($.pickWhip.droppables).get(i)).removeClass($.pickWhip.settings.unlinkedHoverClass);
								var linking = true;
							}
							$.pickWhip.droppedOnto = $($($.pickWhip.droppables).get(i));
						}
						//remove link classes
						if	($($($.pickWhip.droppables).get(i)).hasClass($.pickWhip.settings.linkedClass)) {
							$($($.pickWhip.droppables).get(i)).removeClass($.pickWhip.settings.linkedClass);
						}
						if	($($($.pickWhip.droppables).get(i)).hasClass($.pickWhip.settings.unlinkedClass)) {
							$($($.pickWhip.droppables).get(i)).removeClass($.pickWhip.settings.unlinkedClass);
						}
					}
					// prevent the end animation from being relaunched before it has finished
					if (!$.pickWhip.ending) {
						// if no target was found
						if (!$.pickWhip.droppedOnto) {
							$.pickWhip.ending = true;
							// decide which end animation is needed
							if ($.pickWhip.zoneY == 'top') {
								if ($.pickWhip.zoneX == 'right') {
									endAnim = $.pickWhip.imageDir + 'diag_tr-bl_anim.gif';
								} else {
									endAnim = $.pickWhip.imageDir + 'diag_tl-br_anim.gif';
								}
							} else {
								if($.pickWhip.zoneX == 'right'){
									endAnim = $.pickWhip.imageDir + 'diag_br-tl_anim.gif';
								} else {
									endAnim = $.pickWhip.imageDir + 'diag_bl-tr_anim.gif';
								}
							}
							// replace image with end animation
							$.pickWhip.img.attr('src', endAnim);
							// hide content after end animation (empty src are here to hide images in case the css is disabled)
							setTimeout("$.pickWhip.content.hide();$.pickWhip.ending=false;$.pickWhip.unlinkImg.attr('src','');$.pickWhip.unlinkImg.attr('src','');$.pickWhip.img.attr('src','')", 500);
						} else {
						// if target is ok
							if (!linking) {
								// unlink target and source in linkList
								for (var i=0; i<$.pickWhip.linkList.length; i++) {
									if ($.pickWhip.linkList[i][0].get(0) == $.pickWhip.source.get(0) 
									&& $.pickWhip.linkList[i][1].get(0) == $.pickWhip.droppedOnto.get(0)){
										$.pickWhip.linkList.splice(i,1);
									}
								}
							} else {
								// link target and source in linkList
								$.pickWhip.linkList.push(Array($.pickWhip.source, $.pickWhip.droppedOnto));
							}
							$.pickWhip.content.hide();
							$().unbind('mousemove.pickWhip');
							$().unbind('mouseout.pickWhip');
							// execute callback
							if ($.isFunction($.pickWhip.settings.callback)) {
								$.pickWhip.settings.callback($.pickWhip.source, $.pickWhip.droppedOnto, linking);
							}
							
						}
						
					}
					// stop updating
					$().unbind('mousemove.pickWhip');
					$().unbind('mouseout.pickWhip');
					$().unbind('mouseup.pickWhip');
					for (var i = 0; i < $.pickWhip.droppables.length; i++) {
						$($.pickWhip.droppables.get(i)).unbind('mouseover', $.pickWhip.setHoverTrue);
						$($.pickWhip.droppables.get(i)).unbind('mouseout', $.pickWhip.setHoverFalse);
					}
				});

				// disable selection  (  firefox, NS6+, Opera )
				return false;
			});
		});
	}

})(jQuery);
