/**
 * Lightbox launch controller
 *
 * @usage		LaunchController.init
 * @copyright	Productive Business Systems Ab
 * @uses		jquery.history
 * @uses		nyroModal
 */
var LaunchController = new function() {
	this.settings = {};
	this.currentUri = null;
	this.rootUri = '/';
	this.domains = [];
	var _this = this;
	
	// Launch control initializer
	this.init = function(settings) {

		_this.settings = settings;
		if( typeof settings.rootUri !== 'undefined' ) {
			_this.rootUri = settings.rootUri;
		}
		if( typeof settings.domains !== 'undefined' ) {
			_this.domains = settings.domains;
		}		
		
		// Get the url that the lightbox was launched from
		_this.source = {
			href: location.href,
			title: document.title
		}

		$.historyInit( _this.pageLoad );
		
		$('a').livequery( 'click', _this.click );
	}
	
	// Click handler
	this.click = function( e ) {
		// Shouldn't be in this class...
		$('#map_menu').fadeOut( 'fast' );		
		
		var target = $(this).attr('target');
		if( target != null && target != '' && target != '_self' ) {
			return true;
		}
		
		// Disable hash linking by using no-forward class in links
		if( $(this).hasClass('no-forward') ) {
			return true;
		}
				
		var href = $(this).attr('href');
		if( typeof href == 'undefined' ) {
			return true;
		}
		
		if( href == '' ) {
			e.preventDefault();
			return false;
		}

		// Do not handle external links
		if( href.indexOf('/') !== 0 ) {
			var domain = href.replace(/[^\:]+:\/\/[^\.]+\.([^/]+).*/, '$1');
			if( domain == href ) {
				$(this).attr('href', LaunchController.rootUri + $(this).attr('href') );
			} else if( $.inArray( domain, _this.domains ) == -1 ) {
				return true;
			} else {
				href = href.replace(/[^\:]+:\/\/[^/]+(\/+)?(.*)/, '$2');
			}
		}

		// Direct access to physical files
		if( ( lastDot = href.lastIndexOf('.') ) > -1 ) {
			var ext = href.substr( lastDot + 1 );
			if( ext.substr( ext.length - 1, 1 ) == '/' ) {
				ext = ext.substr( 0, ext.length - 1 );
			}

			if( ext != parseInt( ext ) && ext.length >= 2 && ext.length <= 4 ) {
				return true;
			}
		}
			
		var parents = $(this).parents();
	
		parents.each(function (index, domEle) {
			if( $(domEle).attr('id') == 'nyroModalFull' )
				return true;
		});
		
		e.preventDefault();
		var hash = _this.getHash( $(this).attr('href') );	
		
		// Livequery plugin doesn't obviously work in IE, so we have to use a workaround
		if( $.browser.msie ) {
			hash = hash.replace(/http\:\/\/[^\/]+/gi, '');
		}
		if( hash ) {
			$.historyLoad( hash );
		}
		
		return false;
	}
	
	// Helper method to extract hash from a URL
	this.getHash = function( url ) {
		return url.replace(/^.*#/, '');
	}
	
	this.normalizeHash = function( hash ) {
		if( !hash ) {
			return null;
		} else {	
			// Illegal hash, return to main page
			if( hash.indexOf( _this.rootUri ) != 0 ) {
				hash = null;
				location.href = _this.rootUri;
			} else {
				// Remove extra slashes
				while( hash.indexOf('//') != -1 ) {
					hash = hash.replace(/\/\//g, '/');
				}	
				
				var hashParts = hash.split('?', 2);
					
				// Add slash at the end if needed
				if( hashParts[0].lastIndexOf('/') != hashParts[0].length - 1 ) {
					hashParts[0] += '/';
				}
				
				// Check if already escaped
				var alreadyEscaped = escape( hashParts[0] ) == encodeURI( hashParts[0] );
				if( alreadyEscaped == false ) {
					hashParts[0] = escape( hashParts[0] )
				}	
				hash = hashParts[0] + ( typeof hashParts[1] != 'undefined' ? '?' + hashParts[1] : '' )				
			}
		}
	
		return hash;
	}
	
	// For some reason jQuery is unable to extract data from head and body tags
	this.getItemData = function( tagName, data ) {
		var tagData = null;
		var tagStart = data.indexOf( '<' + tagName );
		if( tagStart != -1 ) {
			tagEnd = data.indexOf( '</' + tagName + '>', tagStart + tagName.length + 1 ) + tagName.length + 3;
			tagData = data.substring( tagStart, tagEnd );

			tagData = tagData.replace('<' + tagName, '<div');
			tagData = tagData.replace('<\/' + tagName, '</div'); 
			
			return $(tagData);
		}
		
		return null;
	}
	
	// Page loader used by browser history plugin
	this.pageLoad = function( hash ) {
		var lightbox = false;
		var folder = null;
		
		hash = _this.normalizeHash( hash );
		
		// Get parent folder
		if( hash != null ) {
			var folders = hash.replace(_this.rootUri, '/').split('/');
			
			if( folders.length < 4 ) {
				folder = null					
			} else {
				folder  = folders[1];
			}
		}

		// Handle root index requests
		if( hash == null ) {
			location.href = _this.rootUri + '#' + _this.rootUri;
			return;
		}
		
		// Use non-hash URI for folders defined
		if( folder != null && $.inArray( folder, _this.settings.directFolders ) != -1 ) {
			location.href = hash
		}
			
		if( hash == _this.currentUri || ( _this.currentUri == null && hash == _this.rootUri ) ) {
			return;
		}
		
		_this.currentUri = hash;

		// ...otherwise make an ajax request
		$.ajax({
 			type: 'GET',
 			url: hash,
 			data: location.search.substring(1),
 			dataType: 'html',
 			
 			error: function(data) {
 				// Display the 404 error page
 				var body = _this.getItemData( 'body', data.responseText );
 				var title = _this.getItemData( 'title', data.responseText );
				
 				document.title = title.text();
 				$('body').html( body.html() );
 				$('body').attr('class', body.attr('class') );
 			},
 			success: function(data) {
 				// Used for password-protected folders
 				matches = data.match(/Location\:\s\<(.+)\>/);
 				if( matches != null && typeof matches[1] !== 'undefined' ) {
 					location.href = matches[1];
 				}
 				
 				_this.currentUri = hash;
 				
 				if( typeof pageTracker !== 'undefined' ) {
 					pageTracker._trackPageview( hash );
 				}
 				
 				// Add additional stylesheets if they don't already exist 
 				var head = _this.getItemData( 'head', data );
 				
 				$('link', head).each(function( a, html ){
 					if( $('head link[@href="' + $(html).attr('href') + '"]').length == 0 ) {
 						$('head').append( html );
 					}
 				});

				var body = _this.getItemData( 'body', data );			
				
				if( body.hasClass( 'family_lightbox' ) ) {
					// This is a workaround because jQuery doesn't allow fetching head elements using the normal way
					var title = _this.getItemData( 'title', data );
					document.title = title.text();
					
					var rawDimensions = body.attr('class').replace(/.*type_([0-9]{1,4}x[0-9]{1,4}).*/, '$1');
					if( rawDimensions == body.attr('class') ) {
						var dimensions = { width: 580, height: 555 };
					} else {
						var dim = rawDimensions.split('x');
						var dimensions = { width: dim[0], height: dim[1] };
					}
					
					var prevLightboxClass = $('body').attr('prev_lightbox_class');
					$('body').attr('prev_lightbox_class', body.attr('class') );
					
					// If directly accessing a lightbox set root as the source
					if( location.href == _this.source.href ) {
						_this.source.href = _this.rootUri;
					}
					
					$.nyroModalManual({
						type: 'content',
						debug: false,
						content: body.html(),
						modal: true,
						width: dimensions.width,
						height: dimensions.height,
						endShowContent: endShowContent,	
						showContent: showContent,
						showTransition: showTransition,
						hideTransition: hideTransition,
						bodyClasses: { newBody: body.attr('class'), oldBody: prevLightboxClass }
					});
				} else {
					_this.source = {
						href: location.href,
						title: document.title
					}					
					
					if( $('#nyroModalFull').length ) {
						_this.closeLightbox();
					}

					// This is a workaround because jQuery doesn't allow fetching head elements using the normal way
					var title = _this.getItemData( 'title', data );
					document.title = title.text();
					
					// Find old class with family
					var classes = $('body').attr('class').split(' ');
					var oldFamilyClass = false;
	
					for( i in classes ) {
						if( classes[i].indexOf('family_') == 0 ) {
							oldFamilyClass = classes[i];
							break;
						}
					}

					// Find new class with family
					var classes = body.attr('class').split(' ');
					var newFamilyClass = false;
	
					for( i in classes ) {
						if( classes[i].indexOf('family_') == 0 ) {
							newFamilyClass = classes[i];
							break;
						}
					}
					
					// Apply transitions
					if( typeof _this.settings.transitions[oldFamilyClass] !== 'undefined' && typeof _this.settings.transitions[oldFamilyClass][newFamilyClass] !== 'undefined' ) {
						// Try custom function
						_this.settings.transitions[oldFamilyClass][newFamilyClass]( body );
					} else if( typeof _this.settings.transitions[oldFamilyClass] !== 'undefined' && typeof _this.settings.transitions[oldFamilyClass]['ALL'] !== 'undefined' ) {
						_this.settings.transitions[oldFamilyClass]['ALL']( body );
					} else {
						var head = _this.getItemData( 'head', data );

						$('script[rel="init"]', head).each(function(){
							$.getScript( $(this).attr('src') );
						});

						$('body').attr('class', body.attr('class') );						
					}
				} 				
 			}
		});
		
		//Check if current page is in history and add it if not.
		$.prevPagesCheck(location.href);
		$.prevPagesAdd(location.href);
		
	}
	
	// Lightbox closer method
	this.closeLightbox = function() {
		$.nyroModalRemove();
		this.currentUri = _this.source.href;
		location.href = _this.source.href + (_this.source.href.indexOf('#') == -1 ? '#' : '');
	}
}

/**
 * Content replacer animation
 *
 * @usage		$(<selector>).replace(<selector>, <OPTIONAL context>);
 * @example		$('#content_container').replace('#content_container', $('body'));
 * @copyright	Productive Business Systems Ab
 */
jQuery.fn.replace = function( selector, context, callback ) {
	return this.each(function(){
		// Make some adjustments to the content element
		var content = $(this);
		content.css('position', 'relative');
		content.css('overflow', 'hidden');
		
		// Wrap contents into a absolute div transport element and set fixed height for the element.
		// Furthermore, store the original css height definition to restore it after the animation.
		var oldTransporter = $('<div id="old" class="slide" style="position: absolute; left: 0; width: 100%; height: 100%; overflow: hidden;"></div>');
		var newHeight = $(selector, context).css('height');
		content.height( content.height() );
		content.wrapInner( oldTransporter );
		oldTransporter = content.find('.slide');
		
		// Create a transport element for the new contents and place it in the margin
		var newTransporter = $('<div id="new" class="slide" style="position: absolute; left: 0; width: 100%; margin-left: 100%; height: 100%; overflow: hidden;"></div>')
			.html( $( selector, context ).html() )
			.attr('set_height', newHeight)
			.appendTo( content );

		// Animate the appearance of the new contents
		newTransporter.animate({
			marginLeft: 0
		}, 1000, 'linear', function(){ 
			// Reset the store height property
			$(this).parent().css('height', $(this).attr('set_height') );
			// Replace the transporter with its contents
			$(this).children().appendTo( $(this).parent() );
			if( typeof callback != 'undefined' ) {
				callback.apply( $( selector, context ) );
			}

			$(this).remove();
		});
		
		// Animate the disappearance of the old contents.
		oldTransporter.animate({
			marginLeft: -oldTransporter.width()
		}, 1000, 'linear', function(){
			// Finally remove the transporter
			$(this).remove();
		});
	});
}

jQuery.fn.backReplace = function( selector, context, callback ) {
	return this.each(function(){
		// Make some adjustments to the content element
		var content = $(this);
		content.css('position', 'relative');
		content.css('overflow', 'hidden');
		
		// Wrap contents into a absolute div transport element and set fixed height for the element.
		// Furthermore, store the original css height definition to restore it after the animation.
		var oldTransporter = $('<div id="old" class="slide" style="position: absolute; right: 0; width: 100%; height: 100%; overflow: hidden;"></div>');
		var newHeight = $(selector, context).css('height');
		content.height( content.height() );
		content.wrapInner( oldTransporter );
		oldTransporter = content.find('.slide');
		
		// Create a transport element for the new contents and place it in the margin
		var newTransporter = $('<div id="new" class="slide" style="position: absolute; right: 0; width: 100%; margin-right: 100%; height: 100%; overflow: hidden;"></div>')
			.html( $( selector, context ).html() )
			.attr('set_height', newHeight)
			.appendTo( content );

		// Animate the appearance of the new contents
		newTransporter.animate({
			marginRight: 0
		}, 1000, 'linear', function(){ 
			// Reset the store height property
			$(this).parent().css('height', $(this).attr('set_height') );
			// Replace the transporter with its contents
			$(this).children().appendTo( $(this).parent() );
			if( typeof callback != 'undefined' ) {
				callback.apply( $( selector, context ) );
			}

			$(this).remove();
		});
		
		// Animate the disappearance of the old contents.
		oldTransporter.animate({
			marginRight: -oldTransporter.width()
		}, 1000, 'linear', function(){
			// Finally remove the transporter
			$(this).remove();
		});
	});
}

/**
 * Vertical scroller jQuery plugin
 *
 * @usage		$(<selector>).vertScroller();
 * @example		$('.scrollable').vertScroller();
 * @copyright	Productive Business Systems Ab
 */
jQuery.fn.vertScroller = function( settings ) {
	settings = settings || {};
	var contentEls = [];
	var scrollerEls = [];
	var clickPixelStep = 200;
	var wheelPixelStep = 30;
	var pixelsPerSecond = 200;
	var i = 0;
	var settings = {
		width: typeof settings.width !== 'undefined' ? settings.width : null,
		height: typeof settings.height !== 'undefined' ? settings.height : null,
		position: typeof settings.position !== 'undefined' ? settings.position : null
	}
	
	return this.each(function(){
		// Make a scroller div inside the content element
		var contentEl = $(this);

		if( contentEl.find('.scroller_area').length > 0 ) {
			// Remove scroller
			$('.scroller_area', this).children().appendTo( $('.scroller_area', this).parent() );
			$('.scroller_area', this).remove();
		}
				
		i++;
		
		var scrollerEl = $('<div/>')
			.addClass('scroller_area')
			.css({
				//width: '100%',
				//height: 'auto',
				margin: 0,
				padding: 0
			});
			
		contentEl.wrapInner( scrollerEl )
			.css('overflow', 'hidden');

			// Manually set element position if required
		if( settings.position != null ) {
			contentEl.css( 'position', settings.position );
		} else if( contentEl.css('position') != 'absolute' ) {
			contentEl.css('position', 'relative');
		}
		
		// If no height is set for the content element, we need to do some calculations using parent elements
		/*var testEl = contentEl;
		var cssHeight = parseInt( testEl.get(0).style.height );

		if( isNaN( cssHeight ) || cssHeight == 0 ) {
			var x = 0;
			while( x++ < 100 && testEl.parent().length == 1 && testEl.parent().get(0).tagName != 'HTML' )
			{
				testEl = testEl.parent();
				var cssHeight = parseInt( testEl.get(0).style.height );

				if( !isNaN( cssHeight ) && cssHeight > 0 ) {
					contentEl.css( 'height', cssHeight + 'px' );
					x = 1000;
				}
			}
		}*/

		// Update the scroller object. For some reason required here.
		var scrollerEl = contentEl.find('.scroller_area');
		
		// Manually set element height if required
		if( settings.height != null ) {
			contentEl.height( settings.height );
		}
		
		// Manually set element width if required
		if( settings.width != null ) {
			contentEl.width( settings.width );
		}
		
		// Do not apply the scroller if there's no need for one
		if( contentEl.height() >= scrollerEl.height() ) {
			return;
		}

		// Make the scroll area a little bit smaller
		scrollerEl.width( scrollerEl.width() - 20 );
		
		// Save elements for use in mouse events
		contentEls[i] = contentEl;
		scrollerEls[i] = scrollerEl;

		// Upwards scroll button
		$('<button class="scroll_up scroller"/>')
		.attr( 'scroller_id', i )
		.appendTo( contentEls[i] )
		.css( 'top', contentEls[i].height() - 36 + 'px' )
		.mousedown(function(){
			var i = $(this).attr('scroller_id');
			scrollerEls[i].stop();
			var maxMargin = contentEls[i].height() - scrollerEls[i].height() - 20;
			var progress = parseInt( scrollerEls[i].css('marginTop') ) / maxMargin;

			scrollerEls[i].animate({ 
				marginTop: 0
			}, 1000 / pixelsPerSecond * -maxMargin * progress );
			$(this).attr('start_margin',  parseInt( scrollerEls[i].css('marginTop') ) );
		}).mouseup(function(){
			var i = $(this).attr('scroller_id');
			scrollerEls[i].stop();
		}).click(function(){
			var i = $(this).attr('scroller_id');
			if( $(this).attr('start_margin') - parseInt(scrollerEls[i].css('marginTop')) < -10 )
				return;
						
			scrollerEls[i].stop();
			var maxMargin = contentEls[i].height() - scrollerEls[i].height() - 20;
			var margin = parseInt( scrollerEls[i].css('marginTop') );
			var step = margin < - clickPixelStep ? clickPixelStep : -margin
			scrollerEls[i].css('marginTop', ( margin + step ) + 'px' );
		});	
		
		// Downwards scroll button
		$('<button class="scroll_down scroller"/>')
		.attr( 'scroller_id', i )
		.appendTo( contentEls[i] )
		.css( 'top', contentEls[i].height() - 20 )
		.mousedown(function(){
			var i = $(this).attr('scroller_id');
			scrollerEls[i].stop();
			var maxMargin = contentEls[i].height() - scrollerEls[i].height() - 20;
			var progress = parseInt(scrollerEls[i].css('marginTop')) / maxMargin;

			scrollerEls[i].animate({ 
				marginTop: (contentEls[i].height() - scrollerEls[i].height() - 20) + 'px'
			}, 1000 / pixelsPerSecond * -maxMargin * (1 - progress) );
			
			$(this).attr('start_margin',  parseInt(scrollerEls[i].css('marginTop')) );
		}).mouseup(function(){
			var i = $(this).attr('scroller_id');		
			scrollerEls[i].stop();
		}).click(function(){
			var i = $(this).attr('scroller_id');			
			if( $(this).attr('start_margin') - parseInt(scrollerEls[i].css('marginTop')) > 10 )
				return;
			
			scrollerEls[i].stop();
			var maxMargin = contentEls[i].height() - scrollerEls[i].height() - 20;
			var margin = parseInt( scrollerEls[i].css('marginTop') );
			var step = margin - clickPixelStep > maxMargin ? clickPixelStep : margin - maxMargin
			scrollerEls[i].css('marginTop', ( margin - step ) + 'px' );
		});			
		
		// Mouse wheel handling
		scrollerEls[i]
		.attr( 'scroller_id', i )
		.mousewheel(function(event, delta){
			var i = $(this).attr('scroller_id');
			scrollerEls[i].stop();
			var maxMargin = contentEls[i].height() - scrollerEls[i].height() - 20;
			var margin = parseInt( scrollerEls[i].css('marginTop') );
			var step = wheelPixelStep * delta;
			if( margin + step > 0 )	{
				step = -margin;
			} else if ( margin + step < maxMargin ) {
				step = maxMargin - margin;
			}
		
			scrollerEls[i].css('marginTop', ( margin + step ) + 'px' );		
			event.preventDefault();
		});
	});
}

// Handle clicks outside the dropdowns in order to close them...
function handleBodyElementClicks() {
	if( $('.dropdown .content:visible').length > 0 && $('body').attr('hit') == 0 )
	{
		if( $(this).hasClass('dropdown') || $(this).parents('.dropdown').length > 0 ) {
			$('body').attr('hit', 1);
		}
	}		
}

// Body hits are handle last so here we close all dropdowns that should be closed
function handleBodyClicks() {
	if( $(this).attr('hit') == 0 ) {
		$('.dropdown .content').dropdownClose();			
	}
	$(this).attr('hit', 0);		
}

/**
 * Dropdown jQuery plugin
 *
 * @usage		$(<selector>).dropdown( <item hit callback>, <direction> );
 * @example		$('.dropdown').dropdown( showItem, 'down' );
 * @copyright	Productive Business Systems Ab
 */
jQuery.fn.dropdown = function(callback, direction, settings) {
	$('body *').unbind('click', handleBodyElementClicks).bind('click', handleBodyElementClicks);
	$('body').unbind('click', handleBodyClicks).bind('click', handleBodyClicks);
	
	return this.each(function(){
		// Stop all animations
		$('.content', this).stop();
		$('.content', this).find('.top_row').remove();
		$('.content', this).find('.bottom_row').remove();
		
		if( direction == 'up' ) {
			$('.content', this).css('height', 0);
			$('.content', this).css('top', 0);
			$('.content', this).attr('direction', 'up');
			$('h4', this).removeClass('down').addClass('up');
			$('.content', this).removeClass('down').addClass('up');
			
			// Top corners
			$('<div class="top_row"><div class="corner_container"><div class="corner corner_top_left"></div><div class="corner corner_top_right"></div><div style="clear: both; font-size: 0; height: 0;"></div></div></div>')
				.prependTo( $('.content', this) );			
		} else {
			$('.content', this).css('height', 'auto');
			$('.content', this).css('top', '100%');
			$('.content', this).attr('direction', 'down');
			$('h4', this).removeClass('up').addClass('down');
			$('.content', this).removeClass('up').addClass('down');
			
			// Bottom corners
			$('<div class="bottom_row"><div class="corner_container"><div class="corner corner_bottom_left"></div><div class="corner corner_bottom_right"></div><div style="clear: both; font-size: 0; height: 0;"></div></div></div>')
				.appendTo( $('.content', this) );
		}
		
		if( typeof settings != 'undefined' && typeof settings.height != 'undefined' ) {
			$('ul', this).attr('max_height', settings.height);
		}
		
		if( $(this).hasClass('dropdown')  ) {
			return;
		}
		$(this).addClass('dropdown');
		
		var el = $(this);
		
		// Hit on items
		$('a', this).click(function(){
			$(this).parent().parent().parent().dropdownClose();
		});
		$('a', this).click( callback );
		
		
		// Hit on a dropdown
		$('h4', this).click(function(e){
			// Close all other dropdowns
			$('.dropdown[@id!="' + $(this).parent().attr('id') + '"] .content').dropdownClose();
			$(this).parent().find('.content').dropdownToggle();
		});
		
		jQuery.fn.dropdownOpen = function( callback ) {
			if( $(this).attr('direction') == 'up') {
				$(this).height(0);
				$(this).show();
				var height = 12 + parseInt($('*:first', this).next().height());
				$(this).attr('sliding', 1);
				$(this).animate({
					'marginTop': '-' + height + 'px',
					'height': height + 'px'
				}, function(){
					//$(this).vertScroller();
					if( callback != null ) {
						callback();
					}
				});	
			} else {
				$(this).slideDown( null, callback );
			}
		}

		jQuery.fn.dropdownClose = function( callback ) {
			if( $(this).attr('direction') == 'up') {
				$(this).removeAttr('sliding');
				$(this).animate({
					'marginTop': 0,
					'height': 0
				}, function() {
					$(this).hide();
					if( callback != null ) {
						callback();
					}
				});
			} else {
 				$(this).animate({
 					'top': '100%'
 				});
 				
				$(this).slideUp( null, callback );				
			}
		}
		
		jQuery.fn.dropdownToggle = function( callback ) {
			// Upwards "throwup" :)
			if( $(this).attr('direction') == 'up' ) {
				if( $(this).attr('sliding') != 1 ) {
					$(this).dropdownOpen();
				} else {
					$(this).dropdownClose();		
				}
			// Classic dropdown
			} else {
				// Toggle the scrollbar and add a scroller
				$(this).slideToggle('fast', function(){

					// IE 7 fix
					var marginLeft = $('.corner_bottom_left', this).css('marginLeft');
					$('.corner_bottom_left', this).css('marginLeft', '0');
					$('.corner_bottom_left', this).css('marginLeft', marginLeft);
					
					// If max height is exceeded, extend upwards
					if( $(this).css('display') != 'none' ) {
						if( $(this).height() > $('ul', this).attr('max_height') ) {
							
							if( $('.top_row', this).length == 0 ) {
								$('<div class="top_row"><div class="corner_container"><div class="corner corner_top_left"></div><div class="corner corner_top_right"></div><div style="clear: both; font-size: 0; height: 0;"></div></div></div>').prependTo( $(this) );
							}							
							$(this).animate({
								'top': - ( $(this).height() - $('ul', this).attr('max_height') )
							});
						}	
					}
					//$(this).vertScroller();
				});				
			}			
		}		
		
	});
}

/**
 * Vertical scroller jQuery plugin
 *
 * @usage		$(<selector>).slideMenu(<click selector>);
 * @example		$('.side_menu').slideMenu('h4 a');
 * @copyright	Productive Business Systems Ab
 */
jQuery.fn.slideMenu = function(tag, menuClass) {
	if( tag == null ) {
		tag = 'h3';
	}
	if( menuClass == null) {
		menuClass = 'slide_menu';
	}
	
	return this.each(function(){
		$(this).addClass(menuClass);
		
		$(tag, this).click(function(){
			$(this).parents('.'+menuClass).eq(0).find('.content:visible').slideUp();
			$(this).parents('li').eq(0).find('.content').slideDown();
		});
	});
}

/**
 * Vertical mouseover slider
 *
 * @usage		new vertMouseoverSlider( <menu object selector>, [ launcher selectors ] );
 * @example		new vertMouseoverSlider( '#access_menu', [ '#access', '#access_menu' ] );
 * @copyright	Productive Business Systems Ab
 */
vertMouseoverSlider = function( menuEl, els ) {
	this.menuEl = menuEl;
	this.els = els;
		
	var _this = this;
	$(document).ready(function(){
		_this.init();
	});
}

vertMouseoverSlider.prototype = {
	
	// Slider constuctor
	init: function() {
		if( typeof this.els == 'object' ) {
			for( i in this.els ) {
				$(this.els[i]).bind( 'mouseover', {'_this':this}, this.slideDown );
				$(this.els[i]).bind( 'mouseout', {'_this':this}, this.slideUp );
			}
		} else {
			$(this.els).bind( 'mouseover', {'_this':this}, this.slideDown );
			$(this.els).bind( 'mouseout', {'_this':this}, this.slideUp );
		}
	},
	
	// Slide open method
	slideDown: function( e ) {
		var _this = e.data['_this'];
		if( !$(_this.menuEl).attr('max_height') ) {
			$(_this.menuEl).attr('max_height', $(_this.menuEl).height())
			$(_this.menuEl).height( 0 )
		}

		var progress = $(_this.menuEl).height() / $(_this.menuEl).attr('max_height') * 100;
		
		$(_this.menuEl).show(); // Webkit fix
		$(_this.menuEl).stop().animate({ 
	     		height: $(_this.menuEl).attr('max_height')
	      	}, 
	      	2 * (100 - progress)
		);			
	},
	
	// Slide close method
	slideUp: function( e ) {
		var _this = e.data['_this'];
		var progress = $(_this.menuEl).height() / $(_this.menuEl).attr('max_height') * 100;
		$(_this.menuEl).stop().animate({ 
				height: '0px'
			}, 
			2 * progress, 
			function(){ $(_this.menuEl).hide(); } // Webkit fix
		);			
	}
}