/*
 * BDC DrillDown Menu - http://www.barandis.com/dev/jquery/ddmenu
 *
 * A UI component implementing a compact, multi-level sliding menu.
 *
 * Requires:
 *     * jQuery 1.2
 *     * jQuery Dimensions 1.2 (unless jQuery is version 1.2.6+)
 * Optional:
 *     * jQuery Easing 1.3
 *
 * TERMS OF USE
 *
 * Copyright (C) 2008, Thomas J. Otterson (dev@barandis.com)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
 * conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notice, this list of conditions and the
 *       following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
 *       following disclaimer in the documentation and/or other materials provided with the distribution.
 *     * Neither the name of the author nor the names of contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Version 0.3, 28.06.2008
 */

(function($){

	$.fn.ddMenu = function(options) {
		DDMenu(this, options);
	};

	function DDMenu(root, opts) {

		opts = options(opts);		
		function object(parent) {
			function F() {}
			F.prototype = parent;
			return new F();
		}

		function options(opts) {
			var that = object($.fn.ddMenu.defaults);
			for (var i in opts) {
				that[i] = opts[i];
			}
			return that;
		}

		function init() {
			
			var self = $(root).wrap('<div class="' + opts.menuClass + '"><div class="' + opts.menuPanelClass + '"><div class="' +opts.scrollPaneClass + '"></div></div></div>');
			var pane = self.parents('.' + opts.scrollPaneClass);
			var panel = self.parents('.' + opts.menuPanelClass);
			var menu = self.parents('.' + opts.menuClass);
			
			if ($.browser.msie) {
				removeComments(self.get(0));
			}

			decorateMenu(self);

			setOuterHeight(panel, menu.height(), true);
			setOuterWidth(panel, menu.width(), true);
			
			self.find('.' + opts.itemClass + ' > *:first-child')
				.wrap('<div class="' + opts.labelClass + '"></div>')
				.addClass(opts.textClass);
				
			self.find('.' + opts.itemClass + ':has(.' + opts.subMenuClass + ') > div.' + opts.labelClass).each(function() {
				var label = $(this);
				var height = label.height();
				label.prepend('<div class="' + opts.iconClass + '">&nbsp;</div>');

				label.height(height);
				
				var iconDiv = label.children('.' + opts.iconClass);
				setOuterHeight(iconDiv, height, true);
				setOuterWidth(label.children('.' + opts.textClass), label.outerWidth() - iconDiv.outerWidth(true));
				
			});

			self.find('.' + opts.labelClass).each(function() {
				addInteraction($(this));
			});
			self.find('.' + opts.subMenuClass).hide();
			addTitle(panel, opts.rootTitle, true);
			setScrollPaneDimensions(panel);	
			addScrollButtons(pane);

			choosePanel(menu);
			highlight(menu);							

			function decorateMenu(element) {
				if ($.browser.msie) {
					element.addClass(opts.subMenuClass).css('position', 'absolute');
				}else{
					element.addClass(opts.subMenuClass).css('position', 'relative');
				}
				
				element.children().addClass(opts.itemClass).each(function() {
					if ($(this).children().size() > 1) {
						decorateMenu($(this).children(':last'));
					}
				});
			}
		}

		function drillDown(item) {
			var panel = cloneSubmenu(item);
			var title = item.children(':first').children(':last').text();
			var dir = opts.inDirection ? opts.inDirection : opts.direction;

			var menu = item.parents('.' + opts.menuClass).append(panel);
			setOuterHeight(panel, menu.height(), true);
			setOuterWidth(panel, menu.width(), true);

			var pos = getPanelPosition(menu, panel, dir);

			panel.css({ position: 'relative', left: pos.outside.left, top: pos.outside.top });
			panel.show();
			addTitle(panel, title, false);

			setScrollPaneDimensions(panel);
			addScrollButtons(panel.children('.' + opts.scrollPaneClass));

			highlight(item.parents('.' + opts.menuClass));

			panel.animate({ left: pos.inside.left, top: pos.inside.top },
					opts.inDuration ? opts.inDuration : opts.duration,
					opts.inEasing ? opts.inEasing : opts.easing);
		}

		function drillUp(panel) {
			var menu = panel.parent();
			var dir = opts.outDirection ? opts.outDirection : opts.direction;

			var pos = getPanelPosition(menu, panel, dir);

			panel.animate({ left: pos.outside.left, top: pos.outside.top },
					opts.outDuration ? opts.outDuration : opts.duration,
					opts.outEasing ? opts.outEasing : opts.easing, function(){
						panel.remove();
					});
		}

		function choosePanel(menu) {
			if (opts.initial != null) {
				var parts = opts.initial.split(opts.separator);
				var current = menu.children(':first');

				for (var i=0 ; i < parts.length ; i++) {
					if (i < parts.length - 1) {
						var label = current
							.find('.' + opts.labelClass + ' .' + opts.textClass + ":contains('" + parts[i] + "')");
						if (label) {
							var submenu = label.parents('.' + opts.itemClass).children('.' + opts.subMenuClass);
							if (submenu) {
								var panel = cloneSubmenu(submenu.parent());
								menu.append(panel);
								setOuterHeight(panel, menu.height(), true);
								setOuterWidth(panel, menu.width(), true);

								var dir = opts.inDirection ? opts.inDirection : opts.direction;
								var pos = getPanelPosition(menu, panel, dir);

								panel.css({ position: 'relative', top: pos.inside.top, left: pos.inside.left });
								panel.show();
								addTitle(panel, parts[i], false);

								setScrollPaneDimensions(panel);
								addScrollButtons(panel.children('.' + opts.scrollPaneClass));

								current = panel;
							}
						}
					}
				}
			}
		}

		function getPanelPosition(menu, panel, dir) {
			var left, top;
			var index = panel.prevAll().size();	
			var offset = (panel.outerHeight() + getMarginGap(panel)) * index;
			switch (dir) {
				case 'west':
					left = -panel.outerWidth(true);
					top = -offset;
					break;
				case 'north':
					left = 0;
					top = -(menu.outerHeight() + offset);
					break;
				case 'south':
					left = 0;
					top = panel.outerHeight(true) - offset;
					break;
				default:
					left = menu.outerWidth();
					top = -offset;
					break;
			}
			return {
				outside: { top: top, left: left },
				inside: { top: -offset, left: 0 }
			}
		}

		function getMarginGap(panel) {
			var top = getWidth(panel, 'margin-top');
			var bottom = getWidth(panel, 'margin-bottom');
			return top > bottom ? top : bottom;
		}

		function highlight(menu) {
			if (opts.initial != null && menu.find('.' + opts.labelInitialClass).size() == 0) {
				var parts = opts.initial.split(opts.separator);
				var current = menu.children('.' + opts.menuPanelClass + ':first');
				for (var i = 0, count = parts.length; i < count; i++) {
					if (i == count - 1) {
						current.children('.' + opts.scrollPaneClass)
							.children('.' + opts.subMenuClass).children('.' + opts.itemClass).children('.' + opts.labelClass)
							.each(function(){
								if ($(this).find('.' + opts.textClass).text() == parts[i]) {
									replaceClass($(this), opts.labelClass, opts.labelInitialClass);
									$(this).hover(
										function() {
											replaceClass($(this), opts.labelInitialClass, opts.labelInitialHoverClass);
										},
										function() {
											replaceClass($(this), opts.labelInitialHoverClass, opts.labelInitialClass);
										}
									);
								}
							});
					}
					else {
						var next = current.next();
						if (!next)
							return;
						var present = false;
						next.children('.' + opts.titleClass).find('.' + opts.titleLabelClass).each(function() {
							if ($(this).text() == parts[i]) {
								present = true;
								return false;
							}
						});
						if (!present)
							return;
						else
							current = next;
					}
				}
			}
		}

		function cloneSubmenu(item) {
			var panel = $('<div class="' + opts.menuPanelClass + '"><div class="' + opts.scrollPaneClass + '"></div</div>');
			var menu = item.children('.' + opts.subMenuClass + ':last').clone(true).show();
			panel.children('.' + opts.scrollPaneClass).append(menu);

			return panel;
		}

		function addTitle(panel, title, root) {
			var markup = '<div class="' + opts.titleClass + '">';
			if (!root) {
				markup += '<div class="' + opts.titleIconClass + '">&nbsp;</div>';
			}
			markup += '<div class="' + opts.titleLabelClass + '">' + title + '</div></div>';
			panel.prepend(markup);

			var titleDiv = panel.find('.' + opts.titleClass);
			if (root) {
				replaceClass(titleDiv, opts.titleClass, opts.titleRootClass);
				titleDiv.hover(
					function() {
						replaceClass($(this), opts.titleRootClass, opts.titleRootHoverClass);
					},
					function() {
						replaceClass($(this), opts.titleRootHoverClass, opts.titleRootClass);
					}
				);
			}
			else {
				var label = titleDiv.find('.' + opts.titleLabelClass);
				var icon = titleDiv.find('.' + opts.titleIconClass);

				titleDiv.hover(function() {
					replaceClass($(this), opts.titleClass, opts.titleHoverClass);
				}, function() {
					replaceClass($(this), opts.titleHoverClass, opts.titleClass);
				}).click(function() {
					drillUp(panel);
				});

				setOuterHeight(icon, label.outerHeight(), true);
				setOuterWidth(label, titleDiv.outerWidth() - (icon.size() == 0 ? 0 : icon.outerWidth(true)));
				label.css('margin-right', icon.outerWidth({ margin: true }));
			}
		}

		function addInteraction(label) {
			if (label.children().size() == 1) {
				label.hover(function() {
					replaceClass($(this), opts.labelClass, opts.labelHoverClass);
				}, function() {
					replaceClass($(this), opts.labelHoverClass, opts.labelClass);
				});
			}
			else {
				var link = label.find('a[href].' + opts.textClass);
				if (link.size() == 1) {
					link.hover(function() {
						replaceClass($(this), opts.textClass, opts.textHoverClass);
					}, function(){
						replaceClass($(this), opts.textHoverClass, opts.textClass);
					});
					label.find('div.' + opts.iconClass).hover(function() {
						replaceClass($(this), opts.iconClass, opts.iconHoverClass);
					}, function() {
						replaceClass($(this), opts.iconHoverClass, opts.iconClass);
					}).click(function() {
						drillDown(label.parent());
					});
				}
				else {
					label.hover(function(){
						replaceClass($(this), opts.labelClass, opts.labelHoverClass);
					}, function(){
						replaceClass($(this), opts.labelHoverClass, opts.labelClass);
					}).click(function(){
						drillDown(label.parent());
					});
				}
			}
		}

		function addScrollButtons(pane) {
			var submenu = pane.children('.' + opts.subMenuClass);
			if (submenu.height() > pane.height()) {
				var up = $('<div class="' + opts.scrollUpClass + '">&nbsp;</div>');
				var down = $('<div class="' + opts.scrollDownClass + '">&nbsp;</div>');
				pane.prepend(down).prepend(up);
				var position = getScrollPosition(pane, submenu);

				up
					.hover(
						function() {
							replaceClass($(this), opts.scrollUpClass, opts.scrollUpHoverClass);
						},
						function() {
							replaceClass($(this), opts.scrollUpHoverClass, opts.scrollUpClass);
							replaceClass($(this), opts.scrollUpClickClass, opts.scrollUpClass);
							up.scroll = false;
						})
					.mousedown(
						function() {
							replaceClass($(this), opts.scrollUpHoverClass, opts.scrollUpClickClass);
							up.scroll = true;
							scrollUp(submenu, up, down, position);
						})
					.mouseup(
						function() {
							replaceClass($(this), opts.scrollUpClickClass, opts.scrollUpHoverClass);
							up.scroll = false;
						});
				down.
					hover(
						function() {
							replaceClass($(this), opts.scrollDownClass, opts.scrollDownHoverClass);
						},
						function() {
							replaceClass($(this), opts.scrollDownHoverClass, opts.scrollDownClass);
							replaceClass($(this), opts.scrollDownClickClass, opts.scrollDownClass);
							down.scroll = false;
						})
					.mousedown(
						function() {
							replaceClass($(this), opts.scrollDownHoverClass, opts.scrollDownClickClass);
							down.scroll = true;
							scrollDown(submenu, up, down, position);
						})
					.mouseup(
						function() {
							replaceClass($(this), opts.scrollDownClickClass, opts.scrollDownHoverClass);
							down.scroll = false;
						});

				up.hide();
			}
		}

		function scrollUp(submenu, up, down, position) {
			if (up.scroll) {
				var newTop = getWidth(submenu, 'top') + 2;
				submenu.css('top', newTop);
				down.show();
				if (newTop >= position.start) {
					submenu.css('top', position.start);
					up.hide();
					up.scroll = false;
				}
				else {
					setTimeout(function() { scrollUp(submenu, up, down, position) }, opts.scrollSpeed);
				}
			}
		}

		function scrollDown(submenu, up, down, position) {
			if (down.scroll) {
				var newTop = getWidth(submenu, 'top') - 2;
				submenu.css('top', newTop);
				up.show();
				if (newTop <= position.end) {
						submenu.css('top', position.end);
					down.hide();
					down.scroll = false;
				}
				else {
					setTimeout(function() { scrollDown(submenu, up, down, position) }, opts.scrollSpeed);
				}
			}
		}

		function getScrollPosition(pane, submenu) {
			var initialTop = getWidth(submenu, 'top');
			var submenuHeight = submenu.outerHeight(true);
			var paneHeight = pane.height();
			if (submenuHeight <= paneHeight) {
				return { start: initialTop, end: initialTop	};
			}
			return { start: initialTop, end: initialTop - (submenuHeight - paneHeight) };
		}

		function setOuterHeight(elements, height, margins) {
			elements.each(function() {
				var element = $(this);
				var outside = 0;
				var props = ['border-top-width', 'border-bottom-width', 'padding-top', 'padding-bottom'];

				if (margins) {
					props.push('margin-top');
					props.push('margin-bottom');
				}

				for (var i=0 ; i< props.length ; i++) {
					if(props[i]){
					outside += getWidth(element, props[i]);
					}
				}
				
				var tmp = parseInt($(element).css("height")) - parseInt(outside);
				
				element.height(height - outside);
				
			});
		}

		function setOuterWidth(elements, width, margins) {
			elements.each(function() {
				var element = $(this);
				var outside = 0;
				var props = ['border-left-width', 'border-right-width', 'padding-left', 'padding-right'];

				if (margins) {
					props.push('margin-left');
					props.push('margin-right');
				}

				for (var i = 0 ; i < props.length ; i++) {
					outside += getWidth(element, props[i]);
				}
				element.width(width - outside);
			});
		}

		function setScrollPaneDimensions(panel) {
			var pane = panel.children('.' + opts.scrollPaneClass);
			setOuterHeight(pane,
					panel.height() - panel.children('.' + opts.titleClass + ', .' + opts.titleRootClass).outerHeight(true),
					true);
			setOuterWidth(pane, panel.width(), true);
		}

		function removeComments(node) {
			var i = 0;
			var children = node.childNodes;
			var x;

			while ((x = children[i++])) {
				switch (x.nodeType) {
					case 1:
						removeComments(x);
						break;
					case 8: 
						node.removeChild(x);
						i--;
						break;
				}
			}
			
		}

		function replaceClass(element, oldClass, newClass) {
			if (element.hasClass(oldClass)) {
				element.removeClass(oldClass);
				element.addClass(newClass);
			}
		}

		function getWidth(element, property) {
			if(property!='top'){
				var value = $(element).css(""+property+"");
				if(isNaN(value)){
					value=0;
				}
			}else{
				var value = $(element).css(""+property+"");
				if(value=="auto" && property=='top'){
					value =  0;
					$(element).css({"top":"0px"});
				}
			
			}
				
			if (value != NaN && value != '') {
				return parseInt(value, 10);
			}else{
				return 0;
			}
		}

		init();
		return this;
	}

	$.fn.ddMenu.defaults = {
		rootTitle: 'AES Menu', 	
		initial: null,
		separator: '|',
		scrollSpeed: 10,
		duration: 500, 	
		inDuration: null,
		outDuration: null,
		easing: 'swing', 	
		
			
		inEasing: null,
		outEasing: null,
		direction: 'east',
		inDirection: null, 
		outDirection: null, 	

		menuClass: 'bdc-dd-menu', 
		menuPanelClass: 'bdc-dd-menu-panel', 
		scrollPaneClass: 'bdc-dd-scroll-pane',	
						
		scrollUpClass: 'bdc-dd-scroll-up',
							
		scrollUpHoverClass: 'bdc-dd-scroll-up-hover',
		scrollUpClickClass: 'bdc-dd-scroll-up-click',	
		scrollDownClass: 'bdc-dd-scroll-down',	
							
								
		scrollDownHoverClass: 'bdc-dd-scroll-down-hover',
		scrollDownClickClass: 'bdc-dd-scroll-down-click',	
		subMenuClass: 'bdc-dd-sub-menu', 
					
		titleClass: 'bdc-dd-title',	
		titleRootClass: 'bdc-dd-title-root',
		titleHoverClass: 'bdc-dd-title-hover',
		titleRootHoverClass: 'bdc-dd-title-root-hover',	
							
		titleLabelClass: 'bdc-dd-title-label',
		titleIconClass: 'bdc-dd-title-icon', 
		itemClass: 'bdc-dd-item',
					
		labelClass: 'bdc-dd-label',	
		labelHoverClass: 'bdc-dd-label-hover',	
		labelInitialClass: 'bdc-dd-label-initial',	
		labelInitialHoverClass: 'bdc-dd-label-initial-hover',
				
		textClass: 'bdc-dd-text',	
		textHoverClass: 'bdc-dd-text-hover',
					
		iconClass: 'bdc-dd-icon', 	
		iconHoverClass: 'bdc-dd-icon-hover'
					
	};

})(jQuery);
