(function($) {
	/**
	 * @desc Provide functional and data requirements for the NZHerald Navigation.
	 * @author Craig Bassett
	 * @version 1.0
	 *
	**/

	var debug = false; 			// Set to false for production.
	
	$.fn.nzhNav = function(options) {
		
		// Our options.
		options = jQuery.extend({
			jsonURL					: "/nzh_lib/dynamic_html/navigation.js",
			navContainer 			: ".nav-container",
			slideUpDuration 		: 200,
			slideDownDuration		: 400,
			closeTimer				: 600,
			easingOut				: 'easeOutCubic',
			easingIn				: 'easeInCubic',
			onLoad					: function() {}
		}, options);
		
		// Defined Variables.
		var pluginElement 		= $(this); // The element the plugin was invoked on.
		var navContainer	 	= $(options.navContainer);
		var primaryNav			= navContainer.find('.primary');
		var secondaryNav		= navContainer.find('.secondary');
		var secondaryWrapper	= secondaryNav.find('.secondary-wrapper');
		var secondaryContainer  = secondaryNav.find('.secondarybg');
		
		var newsID				= "#nzhnav_news_" 	// Define the ID prefix to name our editorial items by.
		var pickID				= "#nzhnav_pick_" 	// Define the ID prefix to name our editorial pick items by.
		var subID				= "#subnav_" 		// Define the ID prefix to name our sub nav items by.
		var editorialClass		= ".editorialItem" 	// Define the class to use for picking editorial items.
		var subClass			= ".subnav"			// Define the class to use for picking subnav items.
		
		var secondaryVisible 	= false;
		var closeTimer			= "";
		var _tmplCache 			= {};
		var errorCount			= 0;
		var maxHeight			= 0; 				// Keep track of the largest element we're loading.
		
		/* Check for CSS support */
		var hasCSS = function() {
			$('body').append(
				$(document.createElement('div')).attr('id','css_test').css({ width:'1px', height:'1px', display:'none' })
			);
			var _v = ($('#css_test').width() != 1) ? false : true;
			$('#css_test').remove();
			return _v;
		}

		/* Determine if we're using an apple device */
		var isAppleDevice = function() {
			var isiPad = navigator.userAgent.match(/iPad/i) != null;
			if (isiPad) {
				return true;
			}
			return false;
		}

		/* Template parser */
		var parseTemplate = function(str, data) {
			/*
			 * <summary>
			 * Client side template parser that uses &lt;#= #&gt; and &lt;# code #&gt; expressions.
			 * and # # code blocks for template expansion.
			 * NOTE: chokes on single quotes in the document in some situations
			 *       use &amp;rsquo; for literals in text and avoid any single quote
			 *       attribute delimiters.
			 * </summary>
			 * <param name="str" type="string">The text of the template to expand</param>
			 * <param name="data" type="var"> 
			 * Any data that is to be merged. Pass an object and  
			 * that object's properties are visible as variables.  
			 * </param>  
			 * <returns type="string" />  
			 *  
			 */
		    var err = "";
		    try {
		        var func = _tmplCache[str];
		        if (!func) {
		            var strFunc =
		            "var p=[],print=function(){p.push.apply(p,arguments);};" +
		                        "with(obj){p.push('" +

					str.replace(/[\r\t\n]/g, " ")
					   .replace(/'(?=[^%]*%>)/g,"\t")
					   .split("'").join("\\'")
					   .split("\t").join("'")
					   .replace(/<%=(.+?)%>/g, "',$1,'")
					   .split("<%").join("');")
					   .split("%>").join("p.push('")
					   + "');}return p.join('');";
		
		            //alert(strFunc);
		            func = new Function("obj", strFunc);
		            _tmplCache[str] = func;
		        }
		        return func(data);
		    } catch (e) { err = e.message; }
		    
			$.fn.nzhNav.errorHandler({ // Catch our error.
				errorText: 			"Template data or scripting error.",
				internalError: 		err
			});
			return "error";
		}
		
		/* A wrapper for grabbing our JSON data. */
		var getJsonData = function(feed, callback) {
			
			// Get our JSON data.
			$.ajax( {
				url: 		feed,
				dataType: 	"json",
				cache: 		true,
				timeout: 	2500,
				dataFilter: function(data){
					if (data.substring(0,2) == "//") return data.substring(2,data.length);
					return data;
				},
				success: 	function(data) {
					if (data.SUCCESS) {
						callback(data);
					} else {
						$.fn.nzhNav.errorHandler({ // Catch our error.
							severity: 			10,
							errorText: 			"Error encountered while fetching JSON data."
						});
						callback({ SUCCESS: "false" });
					}
				},
				error: 		function(XMLHttpRequest, textStatus) {
					$.fn.nzhNav.errorHandler({ // Catch our error.
						severity: 			10,
						errorText: 			"Error encountered while fetching JSON data.",
						internalError: 		textStatus
					});
					callback({ SUCCESS: "false" }); // Run our call back with a failure result.
				}
			});
		}
		
		/* Determine if we have subnav or not */
		var hasSubnav = function(element) {
			var thisNav = $(secondaryContainer).find(subID + element.attr("id"));
			if (!thisNav.length) return false;
			return true;
		}
		
		/* Show editorial items */
		var showEditorialContent = function(element) {
			$(secondaryContainer).find(editorialClass).css({visibility: "hidden"});
			$(newsID + element.attr("id").toUpperCase()).css({visibility: "visible"});
			$(pickID + element.attr("id").toUpperCase()).css({visibility: "visible"});
		}
		
		/* Display the appropriate subnav */
		var showSubnav = function(element) {
			$(secondaryContainer).find(subClass).css({visibility: "hidden"});
			$(secondaryContainer).find(subID + element.attr("id")).css({visibility: "visible"});
		}
		
		/* Turn the secondary nav on */
		var secondaryOn = function(element) {
			showSubnav(element); 											// Turn on the appropriate sub nav.
			showEditorialContent(element)									// Turn on the appropriate editorial content.
			if (!secondaryVisible && hasSubnav(element)) {
				$(element).parent().addClass("hover"); 					// Turn on the arrow for this nav item.
				$(navContainer).addClass('active');
				secondaryVisible = true;
				$(secondaryNav).slideDown({ 								// Show the secondary navigation.
					duration:	options.slideDownDuration,	
					easing:		options.easingOut
				});
			} else if (secondaryVisible) { 									// The secondary nav is already on.
				$(primaryNav).find("div.hover").removeClass("hover"); 	// Turn the arrow off.
				$(element).parent().addClass("hover"); 					// Turn the arrow ON.
			}
		}
		
		/* Turn the secondary nav off */
		var secondaryOff = function(element) {
			// Slide the navigation Up.
			secondaryVisible = false;
			$(secondaryNav).slideUp({
				duration:	options.slideUpDuration,
				easing:		options.easingIn,
				complete: 	function(){
					$(navContainer).removeClass('active'); // Once complete - remove the classes.
					$(primaryNav).find("div.hover").removeClass("hover");
					$(secondaryContainer).find(editorialClass).css({visibility: "visible"});
				}
			});
		}
		
		// Begin our DOM preperation
		if (!hasCSS()) { return false; }
		
		// Setup our Events
		$(primaryNav).find("a").each(function(){
			
			if (hasSubnav($(this))) {
				$("<div id='" + pickID.substr(1) + $(this).attr("id").toUpperCase() + "' class='pick navcol editorialItem'><span class='notification'>Loading...</span></div>").appendTo(secondaryContainer);
				$("<div id='" + newsID.substr(1) + $(this).attr("id").toUpperCase() + "' class='news navcol editorialItem'><span class='notification'>Loading...</span></div>").appendTo(secondaryContainer);
			}
			
			if (isAppleDevice()) {
				$(this).click(function(){
					if (hasSubnav($(this))) {
						if ($(this).parent().hasClass("hover")) {
							secondaryOff($(this));
						} else {
							secondaryOn($(this));
						}
						return false;
					}
				});
			} else {
			$(this).hoverIntent({ // Add the sub nav display event.
				over: function(){
					if (hasSubnav($(this))) {
							clearInterval(closeTimer);
							secondaryOn($(this));
						}
						else {
							clearInterval(closeTimer);
							secondaryOff();
					}
				},
				timeout: 0,
					out: function(){
					}, // Required.
				sensitivity: 4,
				interval: 200
			});
			}
		});
		
		if (!isAppleDevice()) {
		$(pluginElement).mouseleave(function() { // Add the subnav hide event.
			closeTimer = setInterval(function() {
					clearInterval(closeTimer);
					secondaryOff();
			}, options.closeTimer);
		});
		
		$(pluginElement).mouseenter(function(){ // Avoid the nav closing on us if we move the cursor back over our navigation area.
			clearInterval(closeTimer);
		});
		}
		
		getJsonData(options.jsonURL, function(data) { // Load our JSON data.
			$(primaryNav).find("a").each(function() {
				var element = $(this);
				var currentID = $(element).attr("id").toUpperCase();
				if (data.SUCCESS && data.DATA != undefined) {
					if (hasSubnav(element)) {
						var t = "";
						var news = secondaryContainer.find(newsID + currentID);
						var picks = secondaryContainer.find(pickID + currentID);
						news.empty();
						picks.empty();
						if (data.DATA[currentID] != undefined) {
							
							t = parseTemplate($("#nzhnav_newsTmpl" + data.DATA[currentID].MAIN.LAYOUT).html(), data.DATA[currentID].MAIN);
							(t != "error") ? $(t).appendTo(news) : $("<span class='notification'>No data is available at this time. Please try again later.</span>").appendTo(news);
							
							t = parseTemplate($("#nzhnav_pickTmpl1").html(), data.DATA[currentID].EDITORIAL);
							(t != "error") ? $(t).appendTo(picks) : $("<span class='notification'>No data is available at this time. Please try again later.</span>").appendTo(picks);
							
							//news.css({visibility: "visible"});
							//alert(news.height());
							//$(news).evenIfHidden(function(element){
							//	if (element.height() > maxHeight) maxHeight = element.height();
							//})
						}
						else {
							$("<span class='notification'>No data is available at this time. Please try again later.</span>").appendTo(news);
							$("<span class='notification'>No data is available at this time. Please try again later.</span>").appendTo(picks);
						}
					}
				} else {
					$("<span class='notification'>No data is available at this time. Please try again later.</span>").appendTo(news);
					$("<span class='notification'>No data is available at this time. Please try again later.</span>").appendTo(picks);
				}
			});
			
			// Set our height based on the largest element.
			/*
			if (maxHeight > $(secondaryContainer).css("height").substr(0,$(secondaryContainer).css("height").length - 2)) {
				var val3 = maxHeight;
				var val2 = maxHeight + 36;
				var val1 = val2 + 16;
				
					$(secondaryNav).height(val1);
					$(secondaryWrapper).height(val2);
					$(secondaryContainer).height(val3);
				}
			*/
			
		});

		options.onLoad(); // Fire our onLoad event.
		
		return this;
	};
	

	/**
	 * @desc Provide a basic means to track any errors.
	 * @author Craig Bassett
	 * Version: 1.0
	 * Date:	2010-10-13
	**/
	
	$.fn.nzhNav.errorHandler = function(options) {
		
		// Our options.
		options = jQuery.extend({
			severity					: 0,
			errorText					: "",
			internalError				: ""
		}, options);
		
		//if (debug) alert("Severity:" + options.severity + "\n" + options.errorText);
		if (debug) console.error("Error", options);
		
	};
	
	/**
	 * jQuery.evenIfHidden - get layout information of hidden DOM elements
	 * http://petr.illodavi.de/jquery-evenifhidden
	 *
	 * Copyright (c) 2010, Davide Petrillo
	 * Licensed under the MIT license http://creativecommons.org/licenses/MIT/
	 *
	 * Version: 1.0
	 * Date:    2010/04/22
	 *
	 **/ 
	
	jQuery.fn.evenIfHidden = function( callback ) {
	
	  return this.each( function() {
	    var self = $(this);
	    var styleBackups = [];
	    
	    var hiddenElements = self.parents().andSelf().filter(':hidden');
	    
	    if ( ! hiddenElements.length ) {
	      callback( self );
	      return true; //continue the loop
	    }
	
	    hiddenElements.each( function() {
	      var style = $(this).attr('style');
	      style = typeof style == 'undefined'? '': style;
	      styleBackups.push( style );
	      $(this).attr( 'style', style + ' display: block !important;' );
	    });
	    
	    hiddenElements.eq(0).css( 'left', -10000 );
	
	    callback(self);
	
	    hiddenElements.each( function() {
	      $(this).attr( 'style', styleBackups.shift() );
	    });
	
	  });
	};
	

})(jQuery);
