/**
* do dragowania elementow
* @author Marek Lewandowski
*
* @usage przyklad podania callbacka:
$(document).ready(function()
{
	var successFunct = function(idChoiceType, srcElement, targetElement){
			
			switch(idChoiceType)
			{
				case 1: // swap
					if($(targetElement).parent().parent().prev().length == 0 )
					{
						// jesli przed elementem targetu listy nie ma zadnego sasiada
						$(srcElement).parent().parent().after($(targetElement).parent().parent());
						
						// wstawiamy na poczatek listy element
						$(targetElement).parent().parent().parent().prepend( $(srcElement).parent().parent() );
					}
					else
					{
						var targetPrevNode = $(targetElement).parent().parent().prev();
						
						$(srcElement).parent().parent().before($(targetElement).parent().parent());
						
						targetPrevNode.after( $(srcElement).parent().parent() );
					}
				break;
				case 2: // lewo
					$(targetElement).parent().parent().before($(srcElement).parent().parent());
				break;
				case 3: // prawo
					//$(srcElement).parent().parent().css('width', '100px');
					$(targetElement).parent().parent().after($(srcElement).parent().parent());
				break;
				default:
					_auDebug('error');
				break;
			}
		}

	var dragControl = new dragController( 'ul.auCategoryDragList a img', 22, { autoCleaner: 1100, success: successFunct } );
});
* --- koniec
*/

if( typeof _auDebug != 'function' )
{
	function _auDebug(strLog){ return false; } // wersja prodkcyjna skryptu
}


/**
* elementem targetowanym w funkcji okreslony jest element na ktory najezdzamy (dragujac jakis inny) w celu jego zastapienia, lub wsuniecia dragowanego elementu w jego sasiedztwie
*
* @param string strSelector - selektor elementow drag`a
* @param int iSens in - wrazliwosc w proc
*
*
* luźne powiązania:
* jQuery 1.4.4
*/
function dragController(strSelector, iCzulosc, pConfig)
{
	/**
	* @var DOMElement curTarget - element 'aktualnie pod myszka'
	*/
	var curTarget = null;
	
	/**
	* @var DOMElement curTarget - element przeciagany (czyli ten od ktorego user zczal gest draga )
	*/
	var curSrc = null;
	
	/**
	* @var int iDragSensit - czulosc draga, czyli ile % od krawedzi elementu system zaczyna uznawac ze user chce wsadzic element od danej strony - zalecane wartosci z przedzialu <5,25>
	*/
	this.iSens = iCzulosc;
	
	/**
	* @var string strActiveElementClassName - nazwa klasy jaka ma miec przypisywana 'celowany' element
	*/
	this.strActiveElementClassName = 'dragDestinyMark';
	
	/**
	* @var string strDefaultClass - nazwa klasy elementu w spoczynku
	*/ 
	this.strDefaultClass = false;
	
if(pConfig && typeof pConfig.success == 'function')
	this.choiceCallback = pConfig.success;
else
	this.choiceCallback = function(idChoiceType, srcElement, targetElement, e)
	{
		/**
		* defaultowa funkcja
		* @param int idChoiceType oznacza enum gestu jaki wykonal user: (czyli gdzie upuscil srcElement)
		* 	0 - blad
		*	1 - centralny obszar
		*	2 - lewa strona (prepend)
		*	3 - prawa strona (append)
		*
		* @param DOMElement srcElement - element zrodlowy (czyli ten, od ktorego user rozpoczal dragowanie )
		* @param DOMElement targetElement - element na ktory user upuscil element zrodlowy
		* @param MouseEvent e - obiekt eventu dropa
		*/
		_auDebug( 'choiceCallback(): id gestu: '+idChoiceType+', src: '+srcElement.alt+', target '+targetElement.alt );
	}
	
	var insertMethod = 0;
	
	var that = this;
	
	// akcesory
	
	/**
	* setter, ustawia param elem jako 'namierzony' cel dragu, oraz podpina pod niego event na mouseover, wykrywajacy pozycje kursora (wzgledem krawedzi - czy lewa, czy prawa czy mid)
	* @param DOMElemenet elem - element DOM ktory chcemu ustawic jako cel
	*/
	this.setTarget = function(elem)
	{
		curTarget = elem;
		curTarget.ondragover = calculateTargetFocusedArea;
		//_auDebug('mamy element typu '+elem);
	}
	
	this.getTarget = function(){ return curTarget; }
	
	/**
	* kasuje aktualny target, wraz  z ta operacja resetuje event ondragover odpinanego targetu
	*
	*/
	this.clearTarget = function()
	{
		// deny dla dragover, aby zlikwidowac 'wykrywacz pozycji kursora'
		if(curTarget)
		{
			curTarget.ondragover = dragDenyFunction;
			curTarget.className = null;
		}
		
		curTarget = null;
	}
	
	this.setSrc = function(elem)
	{
		curSrc = elem;
		assert('src na '+elem);
	}
	
	this.clearSrc = function()
	{
		assert('src clean');
		curSrc = null;
	}
	
	this.getSrc = function()
	{
		return curSrc;
	}
	
	/**
	* funkcja assercji
	* @param string strMessage in - komunikat bledu
	* @return false
	*/
	var assert = function(strMessage)
	{
		_auDebug('Assert: '+strMessage+'.');
		
		return false;
	}
	
	/**
	* ustawia klase/klasy focusowanego elementu
	* @param string|array classNameSet - moze byc zarowno stringiem jak i tablica stirngow
	*/
	var setFocusElementClass = function( classNameSet )
	{
		if(!curTarget)
			assert('setFocusElementClass(): brak elementu curTarget');
		
		curTarget.className = ( typeof classNameSet == 'object' ? classNameSet.join(' ') : classNameSet );
	}
	
	/**
	* @param bool bHighLightLeftSide in - czy podswietlac lewa strone (jesli false prawa)
	*/
	this.highLightTargetSiders = function( bHighLightLeftSide )
	{
		if(typeof bHighLightLeftSide != 'boolean')
		{
			assert('highLightTargetSiders(): niewlasciwa wartosc przekazana jako parametr bHighLightLeftSide');
			bHighLightLeftSide = true;
		}
		
		setFocusElementClass( [that.strActiveElementClassName , (that.strActiveElementClassName+(bHighLightLeftSide?'Left':'Right')) ]);
		
		//_auDebug('pragniemy zaznaczyc '+( bHighLightLeftSide ? 'lewa' : 'prawa' )+' strone');
	}
	
	/**
	* funkcja przekazywana do eveneta dragover elementu targetowanego
	* w tej funkcji formulujemy rowniez odwolanie do callbacka sukcesu
	*
	* @param MouseEvent e in - mouse event
	*
	* @uses jQuery 1.4.4
	*/
	var calculateTargetFocusedArea = function(e)
	{
		if(e.type == 'dragover')
		{
			e.preventDefault();
		}
		
		// upewniamy sie czy target jest poprawnie pobrany
		if( !curTarget )
			return assert('calculateTargetFocusedArea(): curTarget nie jest właściwą wartośćią');
		
		var targetOffset = $(curTarget).offset();
		
		
		// targetOffset - offset lewej krawedzi, total - offset prawej krawedzi, width szerokosc
		var targetParams = { leftOffset: targetOffset.left, width: $(curTarget).width(), total: targetOffset.left +$(curTarget).width() };
		var mouseParams = { leftOffset: e.clientX, width: 0 };
		
		if( targetParams.leftOffset <= mouseParams.leftOffset && mouseParams.leftOffset <= (targetParams.leftOffset + targetParams.width) )
		{
			// przeliczony w pikselach margines wrazliwosci
			var fSensMargin = targetParams.width * that.iSens / 100;
			// niech to leci w takiej, mniej przejrzystej formie bez nawiasow, bo jak wiemy javascript ma problem z dzialanaimi na zmiennoprzecinkowych
			
			// zmienne kontrolne boolowskie okreslajace czy upuszczono drag po ktorejsc konkretnej ze stron
			var bLeftSideFocused = (mouseParams.leftOffset >= targetParams.leftOffset && mouseParams.leftOffset <= (targetParams.leftOffset+fSensMargin));
			var bRightSideFocused = (mouseParams.leftOffset <= targetParams.total && mouseParams.leftOffset >= (targetParams.total-fSensMargin));
			
			insertMethod = 0; // standardowo przypisanie 0 czyli error
			
			if(bLeftSideFocused || bRightSideFocused)
			{
				that.highLightTargetSiders(bLeftSideFocused);
				
				// aby uzyskac w kazdym przypadku liczbe wieksza niz 1 dodajemy 2, wiec mozemy otrzymac identyfikatory 3 (dla lewej) lub 4( dla prawej )
				// negacja boola po to, aby left mial wczesniejszy identyfikator niz right (bardziej naturalne ze pierw spodziewamy sie lewej )
				insertMethod = 2+Number( !bLeftSideFocused );
			}else{
				//_auDebug('center');
				insertMethod = 1;
				setFocusElementClass( that.strActiveElementClassName );
			}
			
			// callback sukcesu
			//that.choiceCallback(insertMethod, 'src', curTarget);
			
		}else{
			assert('calculateTargetFocusedArea(): blednie obliczane koordynaty kursora');
		}
	}
	
	var dumpEvent = function(e){ return null; document.getElementById('dumpInfo').innerHTML += "\nlog: "+e.type+' event'; }
	
	var genericDragEvent = function(e)
	{
		if(e === undefined)
			e = window.event;
		
		var touchedElement = e.target ? e.target : e.srcElement;
		
		if(touchedElement.tagName === undefined)
			touchedElement = touchedElement.parentNode;
		
		if(e.type == 'dragover')
			e.preventDefault();
		else// if(e.type != 'drag')
			_auDebug('event '+e.type);
	}
	
	/**
	* 
	*/
	var targetDropEvent = function(e)
	{
		_auDebug('DROP');

		// callback sukcesu
		that.choiceCallback(insertMethod, curSrc, curTarget, e);
		
		sourceDragEndEvent(e);
		
		e.preventDefault();
	}
	
	/**
	* czyscimy src oraz target
	*/
	var sourceDragEndEvent = function(e)
	{
		that.clearSrc();
		targetDeactivator(e);
	}
	
	/**
	*
	*/
	var srcDragStartEvent = function(e)
	{
		if(e.srcElement)
		{
			if(e.srcElement && e.srcElement.nodeName == '#text')
			{
				// dziwny bug na chromie, ktory lapal element tekstu
				that.setSrc( e.srcElement.parentNode );
			}else{
				that.setSrc( e.srcElement );
			}
			
		}else{
			_auDebug('src element niezdef');
		}
	}
	
	/**
	* obsluga eventow deaktywujacych aktualnie zaznaczony element, czyli dragleave, drop
	*
	*/
	var targetDeactivator = function(e)
	{
		that.clearTarget();
	}
	
	/**
	* obsluga eventow aktywujacych aktualnie zaznaczony element, czyli dragleave, drop
	*
	*/
	var targetActivator = function(e)
	{
		//_auDebug(e.target);
		that.setTarget( e.target );
		_auDebug('mam '+that.getTarget());
		dumpEvent( e );
		return false;
	}
	
	/**
	* funkcja uzywana jako dummy deny dla eventu mouseover
	* @param MouseEvent e
	*/
	var dragDenyFunction = function(e)
	{
		if(e.type == 'dragover')
		{
			_auDebug('e dragover prevent');
			e.preventDefault();
		}
		
		_auDebug(e.type);
		
		return false;
	}
	
	/**
	* naklada eventy
	* @param DOMElement elem - element domu na ktory nakladamy eventy
	*/
	var appendListeners = function(elem)
	{
		// ustawiamy attr
		// elem.setAttribute('draggable', false);

		// deny dla dropa oraz leave
		elem.ondrop = targetDropEvent;
		elem.ondragleave = targetDeactivator;
		
		// dragend
		elem.ondragend = sourceDragEndEvent;
		
		// actuvatir dla dragenter
		elem.ondragenter = targetActivator;
		
		elem.ondragover = dragDenyFunction;
		
		elem.ondrag = dragDenyFunction;
		elem.ondragstart = srcDragStartEvent;
		/*elem.addEventListener ("dragstart", function(){ alert(e); }, false );
		
		//elem.ondragend = function(e){ alert('d'); }*/
		
		/*var tracedEvents =
			{
				dragstart: genericDragEvent,
				drag: genericDragEvent,
				dragend: genericDragEvent,
				
				dragenter: genericDragEvent,
				dragover: genericDragEvent,
				dragleave: genericDragEvent,
				dragexit: genericDragEvent,
				drop: genericDragEvent,
				dragdrop: genericDragEvent
			};
		
		for( var evType in tracedEvents )
		{
			elem.addEventListener( evType, tracedEvents[evType], false );
		}*/
	}
	
	$(strSelector).each(function()
	{
		appendListeners($(this)[0]);
	});
	
	// dodawanie klasy .auCategoryDragList dla rootowego elementu listy
	$(strSelector).parents('ul').addClass('auCategoryDragList');
	
	// jesli ustawiony byl paramet autoCleaner (czyli odpalany jest interval, ktory co 1500 sek czysci klasy, bo czasem nie zaskakuja)
	if(pConfig && typeof pConfig.autoCleaner == 'number')
	{
		_auDebug('odpalenie cleanera');
		var cleaner = window.setInterval( function(){ $(strSelector).attr('class', that.strDefaultClass); }, pConfig.autoCleaner );
	}
}
