/**
*
* JavaScript knihovna implementující drag&drop
*
* @package		sllibs3
* @subpackage	jslibs
* @version		$$
* @encoding		UTF-8
* @author		Filip Zach <filip.zach@e4you.cz>
* @author		Michal Kouďa <michal.kouda@e4you.cz>
* @copyright	(c) e4you spol. s r.o. 2002-2010, <design@e4you.cz>
*
* Obsah tohoto souboru je majetkem e4you spol. s r.o. Jeho kopírování,
* pozměňování, šíření a jakékoli další využití je možné výhradně
* se souhlasem e4you spol. s r.o.
*
*/

/// pokud není knihovna označena jako natažená, načteme ji
if(!sl.loaded['sl.draganddrop.js']) {

	/// vložíme potřebné knihovny
	sl.require('sl.dom.js');
	sl.require('sl.event.js');

	/// definujeme modul a jeho metody
	sl.dragAndDrop = {

		/**
		 * Počáteční X souřadnice myši
		 */
		initial_mouse_x: undefined,

		/**
		 * Počáteční Y souřadnice myši
		 */
		initial_mouse_y: undefined,

		/**
		 * Počáteční X souřadnice objektu
		 */
		start_x: undefined,

		/**
		 * Počáteční Y souřadnice objektu
		 */
		start_y: undefined,

		/**
		 * Pozicovaný objekt
		 */
		dragged_object: undefined,

		/**
		 * Ukazatel, určující, kam bude možné vložit pozicovaný objekt
		 */
		placement_indicator: undefined,

		/**
		 * Pole objektů, které lze třídit
		 */
		sortable_elements: undefined,

		/**
		 *
		 */
		sortable_element_containers: new Object,

		/**
		 * Funkce, která se zavolá po ukončení přesunu
		 */
		callback_function: undefined,


		/**
		 * Pole objektů, které lze třídit
		 */
		copy: false,


		/**
		 * Pozicovaný objekt, pokud není detekován z události, ale je
		 * inicializován při volání funkce initElement
		 *
		 * v IE modelu detekce událostí mohou nastat u vnořených prvků problémy -
		 * nelze v něm správně určit, na kterém prvku událost nastala (tedy který prvek se má pozicovat)
		 */
		preset_dragged_object: undefined,

		/**
		 * Inicializuje drag&drop na předaném objektu
		 *
		 * @param element string Objekt, kterému se má přiřadit drag&drop handler
		 * @param preset_dragged_object boolean Má-li se inicializovaný objekt nastavit jako přesouvaný (nebude detekován z události)
		 */
		initElement: function (element,preset_dragged_object) {
			sl.event.addHandler(element,'mousedown',sl.dragAndDrop.mouseDownHandler);
			/// pokud není zapnuta detekce přesouvaného objektu z události
			/// budeme přesouvat inicializovaný objekt
			if (preset_dragged_object == true) {
				sl.dragAndDrop.preset_dragged_object = element;
			}
		},

		/**
		 * Inicializuje třídění
		 * Metodu je nutno zavolat, pokud chceme umožnit přesouvání více objektů mezi sebou
		 *
		 * @param sortable_elements Array Pole s prvky, které je možné vůči sobě přemístovat
		 */
		initSorting: function (sortable_elements,callback_function) {
			if (callback_function) {
				sl.dragAndDrop.callback_function = callback_function;
			}
			/// vytvoříme ukazatel nového umístění přesouvaného prvku
			sl.dragAndDrop.placement_indicator = document.createElement("DIV");
			/// definujeme vlastnost source_element, v níž uchováme informaci o přemístovaném prvku
			sl.dragAndDrop.placement_indicator.source_element = null;
			/// inicializujeme objekt s prvky, které lze vzájemně přesouvat
			sl.dragAndDrop.sortable_elements = sortable_elements;
			/// uchováme informaci o unikátních rodičovských prvcích přesouvaných objektů - kontejnerech, mezi kterými prvky přesouváme
			for (var i=0; i<sortable_elements.length; i++) {
				var tmp_obj = sortable_elements[i].parentNode;
				sl.dragAndDrop.setSortableElementContainer(tmp_obj);
			}

		},

		/**
		 * Přidá do objektu kontejnerů přesouvatelných prvků nový element, pokud v objektu již není přítomen
		 *
		 * @param element Jeden z elementů, meni nimiž se přesouvají tříditelné prvky
		 */
		setSortableElementContainer : function(element) {
			if (!sl.dragAndDrop.sortable_element_containers[element.id]) {
				sl.dragAndDrop.sortable_element_containers[element.id] = element;
			}
		},

		/**
		 * Ostraní drag&drop na předaném objektu
		 *
		 * @param element Object Objekt, kterému se má odebrat drag&drop handler
		 */
		detachElement: function (element) {
			sl.event.removeHandler(element,'mousedown',sl.dragAndDrop.mouseDownHandler);
		},

		/**
		 * Zahájí drag&drop
		 */
		mouseDownHandler: function (e) {
			var evt = sl.event.getEvent(e);
			sl.event.stopPropagation(evt);
			sl.event.preventDefault(evt);
			/// pokud je explicitně určen přesouvaný objekt, použijeme ho
			if (sl.dragAndDrop.preset_dragged_object) {
				sl.dragAndDrop.startDrag(sl.dragAndDrop.preset_dragged_object);
			/// pokud není, detekujeme ho podle události
			} else {
				/// nalezneme zdrojový element události - MSIE || Gecko
				var src_element = evt.srcElement || this;
				sl.dragAndDrop.startDrag(src_element);
			}
			/// uchováme výchozí pozici ukazatele myši
			sl.dragAndDrop.initial_mouse_x = evt.clientX;
			sl.dragAndDrop.initial_mouse_y = evt.clientY;

			/// @todo - zjistit, proč následující řádek funkguje tak jak funguje
			sl.dragAndDrop.setPosition(evt.clientX - sl.dragAndDrop.initial_mouse_x,evt.clientY - sl.dragAndDrop.initial_mouse_y);

			/// připojíme prvku handlery událostí na pohyb myši a uvolnění tlačítka myši
			sl.event.addHandler(document, 'mousemove', sl.dragAndDrop.dragMouse);
			sl.event.addHandler(document, 'mouseup', sl.dragAndDrop.releaseElement);

			return false;
		},

		/**
		 * Provede úkony nutné pro zahájení pohybu myši
		 *
		 * @param obj Object Přesouvaný prvek
		 */
		startDrag: function (obj) {

			/// pokud objekt s přesouvaným prvkem není prázdný
			/// (již byl dříve nějaký prvek přesouván), uvolníme ho
			if (sl.dragAndDrop.dragged_object) {
				sl.dragAndDrop.releaseElement();
			}
			/// uchováme počáteční souřadnice přesouvaného objektu
			sl.dragAndDrop.start_x = obj.offsetLeft;
			sl.dragAndDrop.start_y = obj.offsetTop;

			/// uchováme informaci o přesouvaném objektu
			sl.dragAndDrop.dragged_object = obj;

			/// pokud je zapnuto třídění více prvků
			if (sl.dragAndDrop.placement_indicator) {
				/// pozicujeme přesouvaný obujekt absolutně
				sl.dragAndDrop.dragged_object.style.position = 'absolute';
				/// nastavíme vlastnosti ukazatele přesunu
				sl.dragAndDrop.setPlacementIndicator();
			}

			sl.dom.addClass(obj,'slElementDragged');
		},

		/**
		 * Nastavíme vlastnosti ukazatele nového umístění prvku
		 *
		 * @param obj Object Přesouvaný prvek
		 */
		setPlacementIndicator: function() {
			/// uchováme informaci o zdrojovém elementu
			sl.dragAndDrop.placement_indicator.source_element = sl.dragAndDrop.dragged_object;
			sl.dragAndDrop.placement_indicator.className = sl.dragAndDrop.dragged_object.className;
			sl.dom.addClass(sl.dragAndDrop.placement_indicator,'slPlacementIndicator');
			sl.dragAndDrop.placement_indicator.style.height = sl.dragAndDrop.dragged_object.offsetWidth+'px';
			sl.dragAndDrop.placement_indicator.style.width = sl.dragAndDrop.dragged_object.offsetHeight+'px';
			sl.dragAndDrop.placement_indicator.style.backgroundColor = '#eee';
			sl.dragAndDrop.dragged_object.parentNode.insertBefore(sl.dragAndDrop.placement_indicator, sl.dragAndDrop.dragged_object);

		},


		/**
		 * Handler pohybu myši
		 */
		dragMouse: function (e) {
			var evt = e || window.event;
			sl.event.stopPropagation(evt);
			var dX = evt.clientX - sl.dragAndDrop.initial_mouse_x;
			var dY = evt.clientY - sl.dragAndDrop.initial_mouse_y;
			sl.dragAndDrop.setPosition(dX,dY);

			/// pokud je zapnuto třídění více prvků
			if (sl.dragAndDrop.sortable_elements) {
				sl.dragAndDrop.handleSorting(dX,dY);
			}

			return false;
		},

		/**
		 * Stará se o umístění ukazatele nové pozice prvku při zapnutém
		 * třídění více prvků mezi sebou
		 */
		handleSorting: function(pos_x,pos_y) {

			var position_found = false;
			var target_break_point_x;
			var target_break_point_y;
			var mouse_position_x = sl.dragAndDrop.start_x + pos_x;
			var mouse_position_y = sl.dragAndDrop.start_y + pos_y;

			/// projdeme prvky, které lze přesouvat, a zjistíme, jestli se pozice některého z nich nepřekrývá
			/// s ukazatelem myši
			for (var i=0; i<sl.dragAndDrop.sortable_elements.length; i++) {

				target_break_point_x = sl.dragAndDrop.sortable_elements[i].offsetLeft+sl.dragAndDrop.sortable_elements[i].offsetWidth/2;
				target_break_point_y = sl.dragAndDrop.sortable_elements[i].offsetTop+sl.dragAndDrop.sortable_elements[i].offsetHeight/4

				/// pokud je ukazatel myší v levé horní části prvku
				if (
					mouse_position_x > sl.dragAndDrop.sortable_elements[i].offsetLeft
					&& mouse_position_y > sl.dragAndDrop.sortable_elements[i].offsetTop
					&& mouse_position_x < target_break_point_x
					&& mouse_position_y < target_break_point_y
					)
				{
					/// vložíme před ukazatel před něj
					sl.dragAndDrop.sortable_elements[i].parentNode.insertBefore(sl.dragAndDrop.placement_indicator, sl.dragAndDrop.sortable_elements[i]);
					position_found = true;
					break;

				/// pokud je v vpravé nebo spodní části prvku
				} else if (
					mouse_position_x > target_break_point_x
					&& mouse_position_y > target_break_point_y
					&& mouse_position_x < sl.dragAndDrop.sortable_elements[i].offsetLeft+sl.dragAndDrop.sortable_elements[i].offsetWidth
					&& mouse_position_y < sl.dragAndDrop.sortable_elements[i].offsetTop+sl.dragAndDrop.sortable_elements[i].offsetHeight
					)
				{
					/// vložíme ukazatel před následující prvek
					sl.dragAndDrop.sortable_elements[i].parentNode.insertBefore(sl.dragAndDrop.placement_indicator, sl.dragAndDrop.sortable_elements[i].nextSibling);
					position_found = true;
					break;
				}
			}

			/// pokud prvek není umístěn
			if (!position_found) {

				/// projdeme kontejenry, mezi kterými prvky přesouváme
				for ( var i in sl.dragAndDrop.sortable_element_containers ) {

					var tmp_obj = sl.dragAndDrop.sortable_element_containers[i];

					/// pokud prvek již není v aktuálním kontejneru umístěn
					if (sl.dragAndDrop.placement_indicator.parentNode.id !=tmp_obj.id) {
						/// nacházíme-li se nad ním přesuneme do něj prvek
						if ( 	mouse_position_x > tmp_obj.offsetLeft
								&& mouse_position_y > tmp_obj.offsetTop
								&& mouse_position_x < tmp_obj.offsetLeft+tmp_obj.offsetWidth
								&& mouse_position_y < tmp_obj.offsetTop+tmp_obj.offsetHeight
							) {
								tmp_obj.appendChild(sl.dragAndDrop.placement_indicator);
								break;
						}
					}
				}
			}

		},

		/**
		 * Pozicuje element na předané souřadnice
		 */
		setPosition: function (pos_x,pos_y) {
			sl.dragAndDrop.dragged_object.style.left = sl.dragAndDrop.start_x + pos_x + 'px';
			sl.dragAndDrop.dragged_object.style.top = sl.dragAndDrop.start_y + pos_y + 'px';
			/// testovací výpis pozice
			//sl.dom.getElement('test_x').textContent = sl.dragAndDrop.start_x + dx;
			//sl.dom.getElement('test_y').textContent = sl.dragAndDrop.start_y + dy;
		},

		/**
		 * Funkce sdružujcí úkony, které je nutné provést po přesunu prvku
		 *
		 * @param obj Object Přesunutý prvek
		 *
		 * @returns boolean Vrátí-li true, přesun bude proveden, vrátí-li false, přesun bude zrušen
		 */
		processPositionChange: function() {
			if (sl.dragAndDrop.callback_function) {
				return sl.dragAndDrop.callback_function(sl.dragAndDrop.placement_indicator.source_element,sl.dragAndDrop.placement_indicator);
			} else {
				return true;
			}
		},

		/**
		 * Uvolní z objektu událostni drag&drop
		 */
		releaseElement: function() {

			/// je-li zapnuto třídění prvků
			if (sl.dragAndDrop.sortable_elements) {

				/// zavoláme sdružující úkony, které je nutno provést po přesunu prvku
				/// pokud vrátí true
				if (sl.dragAndDrop.processPositionChange()) {
					/// nahradíme ukazatel nové pozice prvku přesouvaným prvkem
					sl.dragAndDrop.placement_indicator.parentNode.replaceChild(sl.dragAndDrop.placement_indicator.source_element, sl.dragAndDrop.placement_indicator);
				/// vrátí-li false
				} else {
					/// odstraníme indikátor přesunu, čímž přesun zrušíme
					sl.dragAndDrop.placement_indicator.parentNode.removeChild(sl.dragAndDrop.placement_indicator);
				}

				/// zrušíme absolutní pozicování
				sl.dragAndDrop.dragged_object.style.position = 'static';
			}

			/// odstraníme handlery pro zprcování událostí myši
			sl.event.removeHandler(document, 'mousemove', sl.dragAndDrop.dragMouse);
			sl.event.removeHandler(document, 'mouseup', sl.dragAndDrop.releaseElement);

			/// odstraníme css třídu indikující, že prvek je přesouván
			sl.dom.removeClass(sl.dragAndDrop.dragged_object,'slElementDragged');


			sl.dragAndDrop.dragged_object = null;

		}

	}

	/// označíme knihovnu jako načtenou
	sl.loaded['sl.draganddrop.js'] = true;
}

