/**
 * @version		$Id$
 * @author		Pham Minh Tuan
 * @package		Joomla!
 * @subpackage	ZJ_NestedAccordion
 * @copyright	Copyright (C) 2008 - 2010 by Zerosoft. All rights reserved.
 * @license		http://www.gnu.org/licenses/old-licenses/gpl-2.0.html GNU/GPL version 2
 */

(function ($) {
	$.fn.orphans = function () {
		var txt = [];
		this.each(function () {
			$.each(this.childNodes, function () {
				if (this.nodeType == 3 && $.trim(this.nodeValue)) txt.push(this)
			})
		});
		return $(txt);
	};

	$.fn.accordion = function (options) {
		var o = $.extend({}, $.fn.accordion.defaults, options);

		return this.each(function () {
			var containerID = o.container ? '#' + this.id : '',
				objID = o.objID ? o.objID : o.obj + o.objClass,
				Obj = o.container ? containerID + ' ' + objID : '#' + this.id,
				El = Obj + ' ' + o.el,
				hTimeout = null;

			// build
			if (o.head) $(Obj).find(o.head).addClass('h');
			$(El).each(function () {
				var $node = $(this);

				if (o.event == 'click' && $node.hasClass('havechild')) {
					$node.find('> a').attr('href', 'javascript:void(0)');
				}

				if ($node.find(o.next).length || $node.next(o.next).length) {
					if ($node.find('> a').length) {
						$node.find('> a').addClass("trigger").css('display', "block");
					} else {
						var anchor = '<a class="trigger" style="display:block" href="#" />'
						if (o.elToWrap) {
							var $t = $node.orphans(),
								$s = $node.find(o.elToWrap);
							$t.add($s).wrapAll(anchor);
						} else {
							$node.orphans().wrap(anchor);
						}
					}
				} else {
					$node.addClass('last-child');
					if (o.lastChild && $node.find('> a').length) {
						$node.find('> a').addClass("trigger").css('display', "block");
					}
				}
			});
			if (o.head) {
				if ($(El).next('div:not(.outer)').length) {
					$(El).next('div:not(.outer)').wrap('<div class="outer" />');
				}
				$(Obj + ' .h').each(function () {
					var $this = $(this);
					if (o.wrapper == 'div') {
						$this.add($this.next('div.outer')).wrapAll('<div class="new"></div>');
					}
				});
			}
			// init state
			$(El + ' a.trigger').closest(o.wrapper).find('> ' + o.next).not('.shown').hide().closest(o.wrapper).find('a.open').removeClass('open');
			if (o.activeLink) {
				var loc, fullURL = window.location.href,
					path = window.location.pathname.split('/'),
					page = path[path.length - 1];
				(o.uri == 'full') ? loc = fullURL : loc = page;
				$(Obj + ' a:not([href $= "#"])[href$="' + loc + '"]').addClass('active').parent().attr('id', 'current').closest(o.obj).addClass('current');
				if (o.shift && $(Obj + ' a.active').closest(o.wrapper).prev(o.wrapper).length) {
					var $currentWrap = $(Obj + ' a.active').closest(o.wrapper),
						$curentStack = $currentWrap.nextAll().andSelf(),
						$siblings = $currentWrap.siblings(o.wrapper),
						$first = $siblings.filter(":first");
					if (o.shift == 'clicked' || (o.shift == 'all' && $siblings.length)) {
						$currentWrap.insertBefore($first).addClass('shown').siblings(o.wrapper).removeClass('shown');
					}
					if (o.shift == 'all' && $siblings.length > 1) {
						$curentStack.insertBefore($first);
					}
				}
			}
			if (o.initShow) {
				$(Obj).find(o.initShow).show().addClass('shown').parents(o.next + ', ' + o.wrapper).show().addClass('shown').end().parents(o.wrapper).find('> a.trigger, > ' + o.el + ' a.trigger').addClass('open');
				if (o.expandSub) {
					$(Obj + ' ' + o.initShow).children(o.next).show().end().find('> a').addClass('open');
				}
			}
			// event
			if (o.event == 'click') {
				var ev = 'click';
			} else {
				if (o.focus) {
					var f = ' focus';
				} else {
					var f = '';
				}
				var ev = 'mouseenter' + f;
			}

			var scrollElem;
			(typeof scrollableElement == 'function') ? (scrollElem = scrollableElement('html', 'body')) : (scrollElem = 'html, body');

			// The event handler is bound to the "accordion" element
			// The event is filtered to only fire when the <a class="trigger"> was clicked on.
			$(Obj).delegate('a.trigger', ev, function (ev) {
				var $thislink = $(this),
					$thisWrapper = $thislink.closest(o.wrapper),
					$nextEl = $thisWrapper.find('> ' + o.next),
					$siblings = $thisWrapper.siblings(o.wrapper),
					$trigger = $(El + ' a.trigger'),
					$shownEl = $thisWrapper.siblings(o.wrapper).find('>' + o.next + ':visible'),
					shownElOffset;
				$shownEl.length ? shownElOffset = $shownEl.offset().top : shownElOffset = false;

				function action(obj) {
					if (($nextEl).length && ($nextEl.is(':visible')) && (o.collapsible)) {
						$thislink.removeClass('open');
						$nextEl.filter(':visible')[o.hideMethod](o.hideSpeed);
					}
					if (($nextEl.length && $nextEl.is(':hidden')) || (!($nextEl).length && $thislink.closest(o.wrapper).not('.shown'))) {
						if (!o.standardExpansible) {
							$siblings.find('> a.open, >' + o.el + ' a.open').removeClass('open').end().find('> ' + o.next + ':visible')[o.hideMethod](o.hideSpeed);
							if (shownElOffset && shownElOffset < $(window).scrollTop()) {
								$(scrollElem).animate({
									scrollTop: shownElOffset
								}, o.scrollSpeed);
							}
						}

						$thislink.addClass('open');
						$nextEl[o.showMethod](o.showSpeed);
					}
					if (o.shift && $thisWrapper.prev(o.wrapper).length) {
						var $thisStack = $thisWrapper.nextAll().andSelf(),
							$first = $siblings.filter(":first");
						if (o.shift == 'clicked' || (o.shift == 'all' && $siblings.length)) {
							$thisWrapper.insertBefore($first).addClass('shown').siblings(o.wrapper).removeClass('shown');
						}
						if (o.shift == 'all' && $siblings.length > 1) {
							$thisStack.insertBefore($first);
						}
					}
				}
				if (o.event == 'click') {
					action($trigger);
					if ($thislink.is('[href $= "#"]')) {
						return false;
					} else {
						if ($.isFunction(o.retFunc)) {
							return o.retFunc($thislink)
						} else {
							return true;
						}
					}
				}
				if (o.event != 'click') {
					hTimeout = window.setTimeout(function () {
						action($trigger);
					}, o.interval);
					$thislink.click(function () {
						$thislink.blur();
						if ($thislink.attr('href') == '#') {
							$thislink.blur();
							return false;
						}
					});
				}
			});
			if (o.event != 'click') {
				$(Obj).delegate('a.trigger', 'mouseleave', function () {
					window.clearTimeout(hTimeout);
				});
			}

			function scrollableElement(els) {
				for (var i = 0, argLength = arguments.length; i < argLength; i++) {
					var el = arguments[i],
						$scrollElement = $(el);
					if ($scrollElement.scrollTop() > 0) {
						return el;
					} else {
						$scrollElement.scrollTop(1);
						var isScrollable = $scrollElement.scrollTop() > 0;
						$scrollElement.scrollTop(0);
						if (isScrollable) {
							return el;
						}
					}
				};
				return [];
			}; /* ----------------------------------------------- */
		});
	};
	$.fn.accordion.defaults = {
		container: true,
		// {true} if the plugin is called on the closest named container, {false} if the pligin is called on the accordion element
		obj: 'ul',
		// the element which contains the accordion - 'ul', 'ol', 'div'
		objClass: '.accordion',
		// the class name of the accordion - required if you call the accordion on the container
		objID: '',
		// the ID of the accordion (optional)
		wrapper: 'li',
		// the common parent of 'a.trigger' and 'o.next' - 'li', 'div'
		el: 'li',
		// the parent of 'a.trigger' - 'li', '.h'
		head: '',
		// the headings that are parents of 'a.trigger' (if any)
		next: 'ul',
		// the collapsible element - 'ul', 'ol', 'div'
		initShow: '',
		// the initially expanded section (optional)
		expandSub: true,
		// {true} forces the sub-content under the 'current' item to be expanded on page load
		showMethod: 'slideDown',
		// 'slideDown', 'show', 'fadeIn', or custom
		hideMethod: 'slideUp',
		// 'slideUp', 'hide', 'fadeOut', or custom
		showSpeed: 400,
		hideSpeed: 800,
		scrollSpeed: 600,
		//speed of repositioning the newly opened section when it is pushed off screen.
		activeLink: true,
		//{true} if the accordion is used for site navigation
		event: 'click',
		//'click', 'hover'
		focus: true,
		// it is needed for  keyboard accessibility when we use {event:'hover'}
		interval: 400,
		// time-interval for delayed actions used to prevent the accidental activation of animations when we use {event:hover} (in milliseconds)
		collapsible: true,
		// {true} - makes the accordion fully collapsible, {false} - forces one section to be open at any time
		standardExpansible: false,
		//if {true}, the functonality will be standard Expand/Collapse without 'accordion' effect
		lastChild: true,
		//if {true}, the items without sub-elements will also trigger the 'accordion' animation
		shift: false,
		// false, 'clicked', 'all'. If 'clicked', the clicked item will be moved to the first position inside its level,
		// If 'all', the clicked item and all following siblings will be moved to the top
		elToWrap: null,
		// null, or the element, besides the text node, to be wrapped by the trigger, e.g. 'span:first'
		uri: 'full',
		//
		retFunc: null //
	};
})(jQuery);
