/**
 * fusionLib JavaScript for widget library.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * @package fusionLib
 * @copyright Copyright (c) 2013 - 2016 fusionLib. All rights reserved.
 * @link http://fusionlib.com
 */

(function() {

	/**
	 * Tab Widget
	 */
	fusionLib.fn.extend({
		/**
		 * Enable tab functionality.
		 *
		 * @param string activeTab The optional name of the active tab.
		 */
		tabs: function(activeTab) {
			var widgetName = this[0].id,
				tabActive,
				aTab,
				tabs = {};

			if(this.attr('data-tabs-enabled') != '1') {
				this.attr('data-tabs-enabled', '1');

				// Check for active tab
				if((aTab = this.attr('data-tabs-active')) != null) {
					tabActive = aTab;
				}

				this.find('.tabs li').each(function () {
					var el = $fl(this),
						tabName,
						panel;

					// If direct child
					if (el.parent().parent()[0].id == widgetName) {

						if ((tabName = el.attr('data-tabpanel')) == null)
							tabName = el.find('a').attr('href').replace(/^.*#/, '');
						panel = $fl('#' + tabName);

						if (tabActive == undefined || (activeTab != undefined && activeTab == tabName))
							tabActive = tabName;

						// Hide panel
						if (tabName != tabActive)
							panel.addClass('tabhidepanel').attr('aria-expanded', false);

						el.parent().attr('aria-selected', false);
						tabs[tabName] = [el, panel];

						// Add click handler to tabs
						el.bind('click', function (evt) {
							tabs[tabActive][1].addClass('tabhidepanel').attr('aria-expanded', false);
							tabs[tabName][1].removeClass('tabhidepanel').attr('aria-expanded', true);
							tabs[tabActive][0].removeClass('active').parent().attr('aria-selected', false);
							tabs[tabName][0].addClass('active').parent().attr('aria-selected', true);
							tabActive = tabName;
							evt.preventDefault();
							evt.stopPropagation();
							return false;
						})
					}
				});

				// Set the active tab
				if (tabActive != undefined) {
					tabs[tabActive][0].addClass('active').parent().attr('aria-selected', true);
					tabs[tabActive][1].removeClass('tabhidepanel').attr('aria-expanded', true);
				}

				// Look to see if in a form
				var n = this[0];
				while (n) {
					if (n.tagName == 'FORM') {
						function selectTabWithErrors() {
							for (var m in tabs) {
								if (tabs[m][1].find('.validation').hasClass('failed')) {
									tabs[m][0].trigger('click');
									break;
								}
							}
						}

						// Capture validation failures
						$fl(n).bind('formValidationFailed', selectTabWithErrors).addClass('tabwidgetcontainer');
						selectTabWithErrors();
						break;
					}
					n = n.parentNode;
				}
			}

			return this;
		}
	});
})();

/**
 * Treeview Widget
 */
$fl(document).ready(function() {

	// Enable tabs
	$fl('.tabwidget').each(function() {
		$fl(this).tabs();
	});

	// Drag and drop handler
	var isDragging = false,
		dragX, dragY,
		dropTarget = false,
		dropPosition = 0,
		dragElement,
		dragElementTree, // The tree holding the drag element
		autoOpenTimer = false; // Timer to auto open nodes

	// Start of the drag and drop event
	function dragStart(e) {
		var el = $fl(e.target).closest('span');

		// Drag operations on spans
		if(el.length) {
			if (e.touches && e.touches.length) {
				e.clientX = e.touches[0].clientX;
				e.clientY = e.touches[0].clientY;
			}

			dragX = e.clientX;
			dragY = e.clientY;
			dragElement = el;
			dragElementTree = dragElement.closest('.treeviewwidget');
			dropTarget = false;

			if (dragElement.attr('data-treeview-nodeid') != null &&
				dragElement.attr('data-treeview-draggable') !== 'false') {
				$fl(document)
					.bind('mousemove', dragNode)
					.bind('mouseup', dragEnd)
					.bind('touchmove', dragNode)
					.bind('touchend', dragEnd);

				e.preventDefault();
			}
		}
	}

	// Handle dragging of the node
	function dragNode(e) {
		if(e.touches && e.touches.length) {
			e.clientX = e.touches[0].clientX;
			e.clientY = e.touches[0].clientY;
		}

		var mouseY = e.clientY + $fl(window).scrollTop();

		// Track the element we're over
		var el = document.elementFromPoint(e.clientX, e.clientY);
		el = $fl(el).closest('span');

		// If changed element
		if(!dropTarget || el[0] != dropTarget[0]) {

			// Deselect previous element
			if(dropTarget) {
				if(isDragging) {
					dropTarget.removeClass('dragBefore').removeClass('dragAfter').removeClass('dragInto');
					dropTarget = false;
				}

				if(autoOpenTimer) {
					clearTimeout(autoOpenTimer);
				}
			}

			// Select the new element
			if(
				el.length &&
				isDragging &&
				el.attr('data-treeview-nodeid') != null &&
				el.closest('.treeviewwidget').attr('data-treeviewname') == dragElementTree.attr('data-treeviewname')
			) {
				dropTarget = el;

				// If collapsed node then start a timer to auto open it
				var li = el.closest('li');
				if(li.attr('aria-expanded') === 'false') {
					// If dropping into then open the node
					autoOpenTimer = setTimeout(function() {
						if(dropPosition == 0) {
							li.find('b').first().trigger('click');
						}
					}, 1000);
				}
			}
		}

		if(!isDragging) {
			if(Math.abs(dragX - e.clientX) > 5 || Math.abs(dragY - e.clientY) > 5) {
				isDragging = true;
				$fl('#treeviewDragOverlay')
					.addClass('active')
					.html(dragElement.html())
					.css('top', e.clientY + $fl(window).scrollTop() + 'px')
					.css('left', e.clientX + 'px');
			}
		}
		else if(dropTarget) {
			var top = dropTarget.offset().top,
				bottom = top + dropTarget.height(),
				qt = dropTarget.height() / 4;

			$fl('#treeviewDragOverlay')
				.css('top', e.clientY + $fl(window).scrollTop() + 'px')
				.css('left', e.clientX + 'px');

			dropTarget.removeClass('dragBefore').removeClass('dragAfter').removeClass('dragInto');

			// Calculate before, after or into
			if(mouseY < top + qt) {
				dropPosition = -1;
				dropTarget.addClass('dragBefore');
			}
			else if(mouseY > bottom - qt) {
				dropPosition = 1;
				dropTarget.addClass('dragAfter');
			}
			else {
				if(dragElementTree.attr('data-treeview-disableinto') == 'true') {
					dropPosition = 1;
					dropTarget.addClass('dragAfter');
				}
				else {
					dropPosition = 0;
					dropTarget.addClass('dragInto');
				}
			}
		}

		// If dragging and off top/bottom of visible tree
		if(isDragging) {
			var container = dragElementTree.closest('.treeviewwidgetContainer');

			// If need to scroll window
			if(container.height() > $fl(window).height()) {
				if(e.clientY < 50)
					$fl(window).scrollTop($fl(window).scrollTop() - 10);
				else if(e.clientY > ($fl(window).height() - 50))
					$fl(window).scrollTop($fl(window).scrollTop() + 10);
			}
			// Else scroll the container
			else {
				if(mouseY < container.offset().top + 10) {
					container.scrollTop(container.scrollTop() - 10);
				}
				else if(mouseY > (container.offset().top + container.height()) - 10) {
					container.scrollTop(container.scrollTop() + 10);
				}
			}
		}
	}

	// End of drag event
	function dragEnd(e) {
		if(isDragging) {
			$fl('#treeviewDragOverlay').removeClass('active');
			if(autoOpenTimer) {
				clearTimeout(autoOpenTimer);
			}

			if(dropTarget) {
				dropTarget.removeClass('dragBefore').removeClass('dragAfter').removeClass('dragInto');

				// Check drop target and drag element belong to the same tree
				if(dropTarget.closest('.treeviewwidget').attr('data-treeviewname') == dragElementTree.attr('data-treeviewname')) {

					// If AJAX
					if(dragElementTree.attr('data-treeview-dndajax-move')) {

						// Show busy
						$fl('#treeviewDragBusy').addClass('treeviewDragBusyShow');

						// Issue update call
						$fl.ajax(dragElementTree.attr('data-treeview-dndajax-move'), {
							type: 'post',
							timeout: 10000,
							async: true,
							success: function (d) {
								// Set new tree
								dragElementTree.parent().html(d.tree);
								restoreOpenClose();
								scrollToActive();

								// Hide busy and show result
								$fl('#treeviewDragBusy').removeClass('treeviewDragBusyShow');
								$fl.toastShow(d.success ? 'success' : 'error', d.msg, d.dismiss ? d.dismiss : 'X', null, true);
							},
							error: function () {
								$fl('#treeviewDragBusy').removeClass('treeviewDragBusyShow');
							},
							data: {
								position: dropPosition,
								moving: dragElement.attr('data-treeview-nodeid'),
								target: dropTarget.attr('data-treeview-nodeid')
							}
						});
					}
				}
			}

			isDragging = false;
			dropTarget = false;
			e.preventDefault();
		}

		// Release events
		$fl(document)
			.unbind('mousemove', dragNode)
			.unbind('mouseup', dragEnd)
			.unbind('touchmove', dragNode)
			.unbind('touchend', dragEnd);
	}

	// Enable drag and drop on trees as required
	$fl('.treeviewwidget').each(function () {
		var tree = $fl(this);

		if(tree.attr('data-treeview-dndajax-move') != null) {
			tree.parent()
				.bind('mousedown', dragStart)
				.bind('touchstart', dragStart);
		}
	});

	$fl('body')
		.append('<div id="treeviewDragOverlay"></div>')
		.append('<div id="treeviewDragBusy"><div class="treeviewDragBusyIcon"></div></div>');

	/**
	 * Bind a click handler to the treeview open/close
	 */
	$fl('.treeviewwidgetContainer').bind('click', function (e) {
		var el = $fl(e.target),
			p = el.parent(),
			tree = p;

		// Respond to clicks on b
		if(el.is('b')) {
			// Find the tree name
			while (!tree.hasClass('treeviewwidget'))
				tree = tree.parent();
			tree = 'flWidgetTreeView.' + tree.attr('data-treeviewname');

			var states = JSON.parse(sessionStorage.getItem(tree)) || {};

			if (p.hasClass('open')) {
				p.removeClass('open')
					.attr('aria-expanded', 'false');
				states[p.attr('data-treeviewbranchid')] = false;
			}
			else {
				p.addClass('open')
					.attr('aria-expanded', 'true');
				states[p.attr('data-treeviewbranchid')] = true;
			}

			sessionStorage.setItem(tree, JSON.stringify(states));
		}
	});

	// Restore the save node open/close states
	function restoreOpenClose() {
		$fl('.treeviewwidget').each(function () {
			var root = $fl(this),
				tree = 'flWidgetTreeView.' + root.attr('data-treeviewname'),
				states = JSON.parse(sessionStorage.getItem(tree)) || {},
				cleaned = {};

			// Find the containers and open those required
			root.find('li').each(function () {
				var el = $fl(this),
					id = el.attr('data-treeviewbranchid');

				if (states[id]) {
					el.addClass('open')
						.attr('aria-expanded', 'true');
					cleaned[id] = true;
				}
			});

			// Find the active node in the tree and open the nodes above it
			root.find('em').each(function () {
				var p = $fl(this);

				// Move about active li
				while (!p.hasClass('treeviewwidget') && !p.is('li')) {
					p = p.parent();
				}
				p = p.parent();

				// Open all parents
				while (!p.hasClass('treeviewwidget')) {
					if (p.is('li')) {
						p.addClass('open')
							.attr('aria-expanded', 'true');
						cleaned[p.attr('data-treeviewbranchid')] = true;
					}
					p = p.parent();
				}
			});

			// Store only the open nodes, cleans out removed nodes
			sessionStorage.setItem(tree, JSON.stringify(cleaned));
		});
	}
	restoreOpenClose();

	// Scroll to active node
	function scrollToActive() {
		$fl('.treeviewwidgetContainer').each(function () {
			var t = $fl(this),
				a = t.find('em');

			if (a.length) {
				var p = a.position(),
					o = t.position(),
					d = p.top - o.top;
				if(t.height() < d)
					t.scrollTop(d);
			}
		});
	}
	setTimeout(scrollToActive, 10);
});
