/**
 * PRODUCT SELECTOR
 */


	/**
	 * Notification Types
	 */
	var NOTIFICATIONTYPES = {
		WARNING: "warning",
		ERROR: "error"
	}


	/**
	 * Notification Object
	 * @param {Object} options
	 */
	var Notification = base2.Base.extend({
		constructor: function(options) {
			this.SetOptions(options);
		},
		
		SetOptions: function(options) {
			this.Defaults = {
				Type: NOTIFICATIONTYPES.WARNING,
				Template: "templates/gbp_cart_notification.html",
				Message: ""
			}
			
			this.Options = jQuery.extend({}, this.Defaults, options);
		},

		SetMessage: function(message) {
			this.Options.Message = message;
		},
		
		Render: function() {
			var template = LoadTemplate(this.Options.Template);
			template = template.replace("%TYPE%", this.Options.Type);
			template = template.replace("%MESSAGE%", this.Options.Message);

			var item = jQuery(template);
			return item;
		}
		
	});
	
	/**
	 * Error Object Definition
	 * @param {Object} options
	 */
	var Error = Notification.extend({
		constructor: function(options) {
			this.SetOptions(options);
			this.Options.Type = NOTIFICATIONTYPES.ERROR; //Hardcode the mode to Error
		}
	})


	/**
	 * Warning Object Definition
	 * @param {Object} options
	 */
	var Warning = Notification.extend({
		constructor: function(options) {
			this.SetOptions(options);
			this.Options.Type = NOTIFICATIONTYPES.WARNING; //Hardcode the mode to Warning
		}
	})


	


	var NiceSelect = base2.Base.extend({
		constructor: function(options) {
			this.Options = new base2.Array2();
		},
		
		ID: "",
		
		Value: "",
		
		ZIndex: 2000,
		
		Options: new base2.Array2(),
		
		Render: function(container) {

			var object = jQuery("<div class='NFSelect NFSelect2' style='z-index: " + this.ZIndex + "'></div>");
			object.append("<img class='NFSelectLeft' src='img/niceforms/0.png'/>");
			object.append("<div id='" + this.ID + "' class='NFSelectRight optionValue'>" + this.Value + "</div>");
			object.append("<div class='NFSelectTarget' style='display:none'><ul class='NFSelectOptions'></ul></div>");

			var options = object.find(".NFSelectOptions");
			base2.Array2.forEach(this.Options, function(option) {
				options.append("<li><a rel='" + option.Value + "'>" + option.Name + "</a></li>");
			});

			container.append(object);
		},

		addOption: function(option) {
			this.Options.push(option);
		}

	});



	/**
	 * Generic ListItem object using name/value pairs
	 * @param {Object} name
	 * @param {Object} value
	 */
	var ListItem = base2.Base.extend({
		constructor: function(name, value) {
			this.Name = name;
			this.Value = value;
		}
	});



	/**
	 * Generic Price object.
	 * Formats it's value to have a price and a price description
	 * @param {Object} options
	 */
	var Price = base2.Base.extend({
		constructor: function(value, options) {
			this.Value = value;
			this.SetOptions(options);
		},

		Value: 0.00,

		IsPrice: function() {
			var parseValue = parseFloat(this.Value);
			
			return !(isNaN(parseValue));
		},

		SetOptions: function(options) {
			this.Defaults = {
				Value: 0,
				Precision: 2,
				Currency: "AUD",
				Caption: "inc. GST",
				Template: "%PRICE% <span>%CURRENCY% <em>%CAPTION%</em></span>"
			}

			this.Options = jQuery.extend({}, this.Defaults, options);
		},

		Render: function() {
			var template = this.Options.Template;
			
			template = template.replace("%PRICE%", this.Price());
			template = template.replace("%CURRENCY%", this.Options.Currency);
			template = template.replace("%CAPTION%", this.Options.Caption);
			
			var item = jQuery(template);
			
			return template;
		},
		
		Price: function() {
			var price = this.Value;
			try {
				price = parseFloat(price).toFixed(this.Options.Precision);

				if (price == "NaN") {
					price = this.Value;
				} else {
					price = price.toString();
					if (price.charAt(0) != '$') price = "$" + price;					
				}
				
			} catch (Exception) {
				price = this.Value;
			}
			
			return price;
		}
		
	});



	/**
	 * Product Selector Overlay object.
	 * Used to interface site components with the overlay
	 * @param {Object} options
	 */
	var ProductSelector = base2.Base.extend({
		constructor: function(options) {
			this.SetOptions(options);
			this.LoadTemplates();
		},

		Container: null,

		Overlay: null,

		ProductData: null,

		Cart: null,

		WorkingKey: null,
		
		OrderGuid: null,
		
		RatesRequest: null,

		Defaults: {
			MaxDailyDetails: 9,
			ContainerSelector: "#productSelector",
			OverlaySelector: "#overlay",
			RenderContainer: "body",
			FeaturesContainerList: ".features ul",
			MeetingPointsContainerList: ".meeting",
			APIRates: GBPServiceUrl + "/{channelID}/rates",
			APIRefreshProducts: GBPServiceUrl + "/{channelID}/cart/product/refresh",
			Channel: GBPChannel,
			Template: "templates/gbp_productselector.html",
			TemplateSalesConfig: "templates/gbp_productselector_salesconfig.html",
			TemplateDailyDetail: "templates/gbp_productselector_dailydetail.html",
			TemplateOption: "templates/gbp_productselector_option.html",
			TemplateOptionsRow: "templates/gbp_productselector_optionsrow.html",
			InitialDate: "today",
			DateJumpSize: 9,
			DateFormat: "yyyy/MM/dd",
			LoadCart: false,
			TriggerElement: "#productSelectorTrigger"
		},
		
		Templates: {
			Main: "templates/gbp_productselector.html",
			Option: "templates/gbp_productselector_option.html",
			OptionsRow: "templates/gbp_productselector_optionsrow.html",
			DailyDetail: "templates/gbp_productselector_dailydetail.html",
			SalesConfig: "templates/gbp_productselector_salesconfig.html"
		},
		
		Options: {},

		SetOptions: function(options) {
			this.Options = jQuery.extend({}, this.Defaults, options);

		},
		
		/* Loads the templates into the Templates object */
		LoadTemplates: function() {
			this.Templates.Main = LoadTemplate(this.Options.Template);
			this.Templates.Option = LoadTemplate(this.Options.TemplateOption);
			this.Templates.OptionsRow = LoadTemplate(this.Options.TemplateOptionsRow);
			this.Templates.DailyDetail = LoadTemplate(this.Options.TemplateDailyDetail);
			this.Templates.SalesConfig = LoadTemplate(this.Options.TemplateSalesConfig);
		},
		

		SetOverlay: function() {
			if (this.ProductData) {

			} else {
				//Cancel opening the overlay
				return false;
			}
		},

		CompleteOverlay: function() {
			this.LoadProductData(this.ProductData);
		},

		Show: function() {
			this.Container.find(".bookingRates").hide();
			this.Container.find(".buttons").hide();

			jQuery(this.Options.TriggerElement).fancybox({
				modal: true,
				autoDimensions: true,
				autoScale: false,
				centerOnScroll: false,
				overlayOpacity: 0.6,
				overlayColor: '#222',
				titleShow: false,
				onStart: jQuery.proxy(this.SetOverlay, this),
				onComplete: jQuery.proxy(this.CompleteOverlay, this)
			})

						
			jQuery(this.Options.TriggerElement).click();

		},

		SetEvents: function() {
			var object = this;


			// Close button
			this.Container.find(".close").live("click", function() {
				object.Close();
				
				//if (jQuery.isFunction(object.RatesRequest.abort())) {
				//}
				return false;
			});

			// Cancel button
			this.Container.find(".cancel").live("click", function() {
				object.Close();
				
				return false;
			});

			// OK/Done button
			this.Container.find(".ok").live("click", function() {
				
				var button = jQuery(this);
				
				if (!button.hasClass("disabled")) {
				
					if (object.Container.find(".day.active").length == 0) {
						object.Close();
					}
					else {
						// Transfer selections to users cart
						object.Cart.TransferTo(cart, object.OrderGuid, object.CriteriaDefinitions(), function(response){

							object.Close();
							cartList.Refresh(cart);
							cartErrorList.Refresh();
						});
					}
				} else {
					if (object.Cart.Purchase.Errors[0]) {
						Notify(object.Cart.Purchase.Errors[0].Message, "Alert!");
					}
				}

				return false;
			});


			// Rates Selection
			this.Container.find(".bookingRatesSelect tbody .day").live("click", function() {
				var MAXREQUESTS = 3;
				var day = jQuery(this);

				if (object.Container.find(".day.loading").length <= MAXREQUESTS 
					&& !day.hasClass("loading")
					&& !day.hasClass("inactive")) {

					day.addClass("loading");

					//Determine if addon
					var isAddon = day.parent().hasClass("addon");

					/***********************************************************
					 * ADD/REMOVE FROM CART
					 */
					var salesConfig = day.parent().data("salesConfiguration");
					var productData = object.Container.data("product");
					var criteria = object.CriteriaDefinitions();
					//var date = object.SelectedDate().toString("yyyy/MM/dd");
					
					var parent = day.parent();
					var index = parent.find(".day").index(day);
					
					var date = Date.parse(day.data("dailyDetail").Date).toString(object.Options.DateFormat);
					
					var callback = function(purchase) {
						day.removeClass("loading");
						//day.toggleClass("active");
						object.Cart.Purchase = purchase;
						object.MapPurchase(purchase);
					}
					
					
					//Addon Sales ConfigurationID
					var addonSalesConfigurationID = null;
					if (isAddon) {
						var addonData = day.parent().data("addonConfiguration");
						addonSalesConfigurationID = addonData.SalesConfigurationID

					}

					
					if (!day.hasClass("active")) {
						//Add to Cart
						//var result = object.Cart.Add(object.OrderGuid, productData.ProductCode, salesConfig.SalesConfigurationID, date, criteria, callback);
						var result = object.Cart.Add(object.OrderGuid, productData.ProductCode, salesConfig.SalesConfigurationID, date, criteria, addonSalesConfigurationID, callback);
					}
					else {
						//Remove from Cart
						var component = day.data("component");
						//var result = object.Cart.Remove(object.OrderGuid, productData.ProductCode, salesConfig.SalesConfigurationID, date, criteria, callback)
						var result = object.Cart.Remove(component, productData.ProductCode, salesConfig.SalesConfigurationID, date, criteria, callback)
					}
				} else {
					// Too many requests...wait for others to finish
				}

			});

			// Datepicker
			this.Container.find('.datepicker').each(function(){
				var a = jQuery('.trigger', this);
				jQuery(this).append('<div class="datepopup"></div>');
				jQuery('.datepopup', this).datepicker({
					inline: true,
					dateFormat: 'dd MM yy',
					onSelect: function(date, inst){
						a.find('span').html(date.toUpperCase()).end().
							parents('.datepicker').
								removeClass('datepicker-active').
								find('input[type="hidden"]').val(date).end();
						object.InitializeLoadingRates();
						object.SetDate(inst.selectedYear, inst.selectedMonth, inst.selectedDay);
						//object.RefreshRates();
						
					}
				});
				jQuery('.trigger', this).click(function(){
					var datepickerContainer =jQuery(this.parentNode); 
					datepickerContainer.toggleClass('datepicker-active');
					
					object.Container.find(".NFSelectTarget").hide();
					
					return false;
				});
			});

			// Prev
			this.Container.find(".bookingRates .prev").live("click", function() {
				var date = object.SelectedDate();

				var newDate = date.add({days: -object.Options.DateJumpSize});

				var year = newDate.toString("yyyy");
				var month = parseInt(newDate.toString("M")) - 1; // for 0 based index
				var day = newDate.toString("d");

				object.InitializeLoadingRates();
				object.SetDate(year, month, day);
				//object.MapPurchase(object.Cart.Purchase);

				return false;
			});
			// Next
			this.Container.find(".bookingRates .next", this.Container.find(".bookingRates")[0]).live("click", function() {
				var date = object.SelectedDate();

				var newDate = date.add({days: object.Options.DateJumpSize});

				var year = newDate.toString("yyyy");
				var month = parseInt(newDate.toString("M")) - 1; // for 0 based index
				var day = newDate.toString("d");

				object.InitializeLoadingRates();
				object.SetDate(year, month, day);
				//object.MapPurchase(object.Cart.Purchase);

				return false;
			});

			this.SetEvents_NiceForms();



			// Scroll through the images
			this.Container.find(".images .nav a").click(function() {
				var button = jQuery(this);
				var images = button.parent().prev().find("li");

				var currentImage = images.filter(function() { return !jQuery(this).hasClass("hide") });

				var direction = button.hasClass("prev") ? "prev" : "next";

				var activeImage;
				switch(direction) {
					case "prev":
						activeImage = currentImage.prev();
						if (activeImage.length == 0) {
							activeImage = images.last();
						}
						break;
					case "next":
						activeImage = currentImage.next();
						if (activeImage.length == 0) {
							activeImage = images.first();
						}
						break;
				}


				currentImage.css({zIndex: 300}).stop().animate({opacity: 0}).addClass("hide");
				activeImage.css({zIndex: 299}).stop().animate({opacity: 1}).removeClass("hide");

				return false;
			});		


		},
		
		SetEvents_NiceForms: function() {
			var object = this;
			// NiceForms Select
			this.Container.delegate(".NFSelect", "click", function() {
				var target = jQuery(this);
				var selectOptions = target.find(".NFSelectTarget").toggle();
				object.Container.find(".NFSelectTarget").not(selectOptions).hide();
				//Also hide the date picker !!
				object.Container.find(".datepicker").removeClass("datepicker-active");
				selectOptions.css({width: target.outerWidth() - 21});
			});

			this.Container.delegate(".bookingOptions .options .NFSelectOptions a", "click", function(event) {
				var selection = jQuery(this);
				var fieldValue = selection.parent().parent().parent().prev();
				var value = selection.attr("rel");

				if (fieldValue.html() != value) {
					fieldValue.html(value);
					//Determine whether to open child definitions
					var valueContainer = selection.closest(".value");
					var childOptionsContainer = valueContainer.attr("rel");
					if (childOptionsContainer != null && childOptionsContainer != "") {
						if (value != "0" && value != "" && value != "No") {
							// Open
							var relChildOptions = jQuery("#" + childOptionsContainer);
							if (relChildOptions.height() > 70) {
								relChildOptions.height(0);
							}
							relChildOptions.stop().animate({
								height: 70
							}, "normal", "swing", function(){
								object.ResizeContainer();
							});
						}
						else {
							// Close
							jQuery("#" + childOptionsContainer).stop().animate({
								height: 0
							}, "normal", "swing", function(){
								jQuery(this).css({
									display: "none"
								});
								object.ResizeContainer();
							});
							
							// Reset child selections
							var childOptions = jQuery("#" + childOptionsContainer).find(".value");
							base2.Array2.forEach(childOptions, function(childOption){
								var value = jQuery(childOption).find(".optionValue");
								var data = jQuery(childOption).data("criteriaDefinition");
								value.html(data.DefaultSelection.Value);
							});
							
							object.RefreshCriteria();
						}
						
					}
					else {
						object.RefreshCriteria();
					}
				}
			});
		},


		SetToolTips: function() {
			// Tooltips
			if (!jQuery('#tooltip1').length)
			{
				jQuery('body').append('<div id="tooltip1"><div class="in"></div></div>');
			}

			this.Container.find('.bookingRatesSelect').each(function(){

				/*
				jQuery('tbody td', this).each(function(){
					if (this.title)
					{
						var tip = this.title;
						jQuery(this).hover(function(){
							var p = jQuery(this).offset();
							var t = jQuery('#tooltip1');
							t.find('.in').html(tip);
							t.css({
								left: Math.round(p['left'] + 12) + 'px',
								top: Math.round(p['top'] - t.height() + 5) + 'px'
							}).show();
						}, function(){
							jQuery('#tooltip1').css({ left: '-1000em' });
						});

					}
				});
				*/
			}).delegate("tbody td", "mouseenter", function() {
				var title = this.title;
				//console.log(title);
				
				var p = jQuery(this).offset();
				var tooltip = jQuery("#tooltip1");
				tooltip.find(".in").html(title);
				if (title) {
					tooltip.stop().show().css({
						left: Math.round(p['left'] + 12) + 'px',
						top: Math.round(p['top'] - tooltip.height() + 5) + 'px',
						display: "block",
						opacity: 1
					}, 300)
				}	
			}).delegate("tbody td", "mouseleave", function() {
				jQuery("#tooltip1").stop().css({ display: "none"}, 300);
				
			});
		},

		Load: function(container) {
			this.Render(container);
			this.SetEvents();
			
			//Disable text select
			this.Container.disableTextSelect();
		},

		Render: function(renderContainer) {
			var object = this;
			if (renderContainer == undefined) {
				renderContainer = jQuery(this.Options.RenderContainer);
			}

			//Check if exists already
			var overlay = jQuery(this.Options.OverlaySelector);
			var container = jQuery(this.Options.ContainerSelector);

			if (overlay.length <= 0 && container.length <= 0) {
				var template = this.Templates.Main;
				jQuery(renderContainer).append(template);

				this.Container = jQuery(this.Options.ContainerSelector);
				this.Overlay = jQuery(this.Options.OverlaySelector);

				
			}
		},

	
		InitializeLoadingRates: function() {
			//Start pulsing Loading
			this.Container.find(".loadingRates").stop(true, true).slideDown().pulse({
				color: ['#FFF','#778085']
			}, {
				duration: 200,
				times: 20,
				easing: "swing",
				complete: jQuery.proxy(function() {  }, this)
			});
			
			this.Container.find(".bookingRates, .buttons").slideUp("normal", jQuery.proxy(function() { this.ResizeContainer();}, this));
		},
		
		FinalizeLoadingRates: function() {
			this.Container.find(".loadingRates").stop(true, true).slideUp("normal", jQuery.proxy(function() { this.ResizeContainer();}, this));

			this.Container.find(".bookingRates, .buttons").css({ display: "block" }).slideDown("normal", jQuery.proxy(function() { /*this.ResizeContainer();*/}, this));
			//this.Container.find(".buttons").css({ display: "block"}).slideDown();
			

		},
		
		ResizeContainer: function() {
			var containerHeight = this.Container.height();
			var containerWidth = this.Container.width();
			
			var padding = 20;
			
			var fInner = this.Container.parent();
			var fOuter = fInner.parent();
			var fWrapper = fOuter.parent();
			
			var windowHeight = jQuery(window).height();
			
			var fOuterTop = parseInt(fWrapper.css("top").replace("px", ""));
			var oldTop = fWrapper.css("top");
			
			var scrollY = (jQuery.browser.msie) ? document.body.scrollTop : window.scrollY;
			var newTop = parseInt((windowHeight - containerHeight)/2) + scrollY - padding;
			if (newTop < 0) newTop = padding;
			
			//console.log("Window: " + windowHeight + " Old Top: " + oldTop + " NewTop: " + newTop);
			
			/* DEBUG ONLY 
			jQuery(".window-height").html(windowHeight);
			jQuery(".old-top").html(oldTop)
			jQuery(".new-top").html(newTop);
			jQuery(".scroller-height").html(window.scrollY);
			jQuery(".layer-coords").html(containerHeight);
			*/
			
			fInner.css({ overflow: "hidden"});
			fOuter.css({ overflow: "hidden"});
			
			fInner.animate({ height: containerHeight + padding, width: containerWidth});
			fOuter.animate({ height: containerHeight + padding, width: containerWidth + padding});			
			fWrapper.animate({ top: newTop });
		},


		/**
		 * Handler for ajax call for content
		 * @param {Object} response
		 */
		LoadProductContent: function(response) {
			var productContent = response.Value;

			var productDescription = GetContent(productContent.Content, "Small Description").Media[0].Label.Text;
			var productGallery = GetContent(productContent.Content, "ImageGallery");
			var providerName = productContent.ProviderName;

			productDescription = productDescription.replace(/\n\r/g, "</p><p>");

			// Product Title
			this.Container.find(".fullProductTitle").html(providerName + " | " + this.ProductData.ProductName);

			// Description
			try {
				this.Container.find(".description").empty();
				this.Container.find(".description").append("<p>" + productDescription + "</p>");
			} catch (ex) {
				this.Container.find(".description").html("Error retrieving description: " + ex);
			}

			// Images
			var images = this.Container.find(".images ul");
			images.empty();

			var imageCount = 0;
			base2.Array2.forEach(productGallery.Media, function(media) {

				if (media.ID != "thumbnail") {
					var url = media.Url;
					images.append("<li class='hide'><img src='" + url + "' border='0' alt='' /></li>");
					imageCount ++;
				}
				
			});
			images.find("li:first").removeClass("hide");
			
			if (imageCount <= 1) {
				this.Container.find(".images .nav").css({ display: "none"});
			} else {
				this.Container.find(".images .nav").css({ display: "block"});
			}
			
			
			// Facilities
			var featuresContainer = this.Container.find(this.Options.FeaturesContainerList);
			featuresContainer.empty();
			
			if (productContent.Facilities) {
				base2.Array2.forEach(productContent.Facilities, function(facility) {
					var feature = jQuery("<li>");
					//feature.html("Feature now present. Please update me to the correct reference");
					feature.html(facility.Description[0].Media[0].Label.Text);
					featuresContainer.append(feature);
				});
			}
			

		},

		/**
		 * Loads the product data into the container
		 * @param {Object} productData
		 */
		LoadProductData: function(productData) {

			var container = this.Container;

			this.Container.data("product", productData);
			this.ProductData = productData;


			this.InitializeLoadingRates();
			
			var productContent = new ProductContent(productData.ProductCode, { Callback: jQuery.proxy(this.LoadProductContent, this) });

			var productCode = productData.ProductCode;

			this.Container.find(".productTitle").html(productData.ProductName);

			
			var meetingPointsContainer = this.Container.find(this.Options.MeetingPointsContainerList);
			meetingPointsContainer.empty();
			
			if (productData.MeetingInfo) {
				base2.Array2.forEach(productData.MeetingInfo.MeetingPoints, function(meetingPoint){
					var point1 = jQuery("<span>");
					point1.html(meetingPoint.CollectionPoint);
					meetingPointsContainer.append(point1);
					
					meetingPointsContainer.append(" ");
					
					var point2 = jQuery("<span>");
					point2.html(meetingPoint.DeparturePoint);
					meetingPointsContainer.append(point2);
				});
			}
			



			this.LoadCriteriaDefinitions(productData);


			var datepicker = this.Container.find(".datepopup").data("datepicker");
			var year = datepicker.selectedYear;
			var month = datepicker.selectedMonth;
			var day = datepicker.selectedDay;
			this.SetDate(year, month, day);
		},


		/**
		 * Load Criteria Definitions
		 * @param {Object} productData
		 */
		LoadCriteriaDefinitions: function(productData) {
			var object = this;
			var childOptionsContainer = this.Container.find(".childOptions");
			//Criteria Definitions
			if (productData.CriteriaDefinitions) {
				if (productData.CriteriaDefinitions.length > 0) {
					var template = this.Templates.Option;
					//Show options
					var optionsTitle = jQuery("<div class='title'><p><strong>Select your<br>options:</strong></p></div>");
					
					var optionsContainer = this.Container.find(".parentOptions .options").empty();

					optionsContainer.append(optionsTitle);

					var criteriaCount = 0;
					base2.Array2.forEach(productData.CriteriaDefinitions, jQuery.proxy(function(criteriaDefinition){
						var select = new NiceSelect();
						select.ZIndex = 4000;
						var item = template;

						var key = criteriaDefinition.Key
						item = item.replace("%LABEL%", criteriaDefinition.Display);
						item = item.replace("%ID%", key);
						item = jQuery(item);
						item.data("criteriaDefinition", criteriaDefinition);
						item.attr({
							id: "cd_" + key
						});

						if (criteriaCount == 0) {
							item.addClass("value-first");
						}

						var selectedValue = criteriaDefinition.DefaultSelection.Value;
						if (criteriaDefinition.LastSelected) selectedValue = criteriaDefinition.LastSelected.Value;

						//select.Value = criteriaDefinition.DefaultSelection.Value;
						select.Value = selectedValue;
						base2.Array2.forEach(criteriaDefinition.CriterionSelections, function(option){
							var selectOption = new ListItem(option.Name, option.Value);
							select.addOption(selectOption);
						});


						var container = item.find(".field");
						select.ID = criteriaDefinition.Key;
						select.Render(container);
						optionsContainer.append(item);

						// CHILD DEFINITIONS
						if (criteriaDefinition.ChildDefinitions.length > 0) {
							var childContainer = jQuery(object.Templates.OptionsRow);

							childOptionsContainer.append(childContainer);
							childContainer.css({overflow: "hidden", visibility: "hidden"});
							childContainer.attr({id: "chd_" + key, rel: key});

							item.attr({ rel: "chd_" + key});

							var childOptionsTitle = jQuery("<p><strong>Select child<br>ages:</strong></p>");
							childContainer.find(".title").html(childOptionsTitle);

							// Position child selection
							var titleWidth = childOptionsTitle.parent().outerWidth();
							var titleLeft = childOptionsTitle.offset().left;

							var childTemplate = template;
							var childOptions = childContainer.find(".options");

							var childSpacer = childContainer.find(".empty");
							if (childContainer.offset()) {
								var childContainerLeft = childContainer.offset().left;
							}
							if (optionsContainer.length > 0) {
								childSpacer.width(optionsContainer.offset().left - optionsContainer.parent().offset().left);
							}

							childContainer.css({overflow: "visible", visibility: "visible", display: "none"});


							var childCriteriaCount = 0;
							base2.Array2.forEach(criteriaDefinition.ChildDefinitions, function(childDefinition){
								var select = new NiceSelect();
								var item = template;
								item = item.replace("%LABEL%", childDefinition.Display);
								item = item.replace("%ID%", childDefinition.Key);
								item = jQuery(item);
								item.data("criteriaDefinition", childDefinition);

								if (childCriteriaCount == 0) {
									item.addClass("value-first");
								}

								var childSelection = childDefinition.DefaultSelection.Value;
								if (childDefinition.LastSelected) childSelection = childDefinition.LastSelected.Value; 

								//select.Value = childDefinition.DefaultSelection.Value;
								select.Value = childSelection;
								
								base2.Array2.forEach(childDefinition.CriterionSelections, function(option){
									var selectOption = new ListItem(option.Name, option.Value);
									select.addOption(selectOption);
								});

								var container = item.find(".field");
								select.ID = childDefinition.Key;
								select.Render(container);
								childOptions.append(item);
								childCriteriaCount ++;
							});


							// check the value of the parent element. If not 0 or No then open the child criterias
							if (selectedValue != "0" && selectedValue != "No") {
								// Open
								childContainer.css({ display: "block", height: 0});
								childContainer.stop().animate({ height: 70 }, "normal", "swing", function() {
									object.ResizeContainer();
								});
							}

						}
						
						
						criteriaCount ++;
					}, this));
					
				}
			}
		},


		/**
		 * Opens the popup/overlay
		 * @param {Object} productData
		 */
		Open: function(productData) {

			if (productData != undefined) {
				//Load product data before showing
				this.LoadProductData(productData);
			}
			//this.Container.fancybox();
			//Show Overlay
			//this.Overlay.fadeIn(1000);
			//this.Container.fadeIn(1000).addClass('popup5-active');
		},
		
		/**
		 * Closes the overlay/popup
		 */
		Close: function() {
			//this.Overlay.fadeOut(1000);
			//this.Container.fadeOut(1000).removeClass("popup5-active");
			try {
				this.RatesRequest.Abort();
				this.RatesRequest = null;
			} catch (exception) {}
			

			
			this.Container.find(".bookingRates").hide();
			this.Container.find(".buttons").hide();
			this.Container.find(".loadingRates").show();
			this.Container.find(".childOptions").empty();
			
			this.Container.find(".datepicker").removeClass("datepicker-active");
			 
			this.ResizeContainer();
			
			jQuery.fancybox.close();
			
			return false;
		},

		/** 
		 * Gets the rates
		 */
		GetRates: function(startDate) {
			if (this.ProductData == null) {
				alert("TODO: Catch null product data object");
				return;
			}


			var apiRates = this.Options.APIRates.replace("{channelID}", this.Options.Channel);

			var productArray = new base2.Array2();
			productArray.push(this.ProductData.ProductCode);
			
			var data = {
					ProductIDs: productArray,
					StartDate: startDate.toString(this.Options.DateFormat),
					Criteria: this.CriteriaDefinitions(),
					BestAvailableRate: true
				};
			data = JSON.stringify(data);
			
			this.RatesRequest = new APIHandler({
				Url: apiRates,
				Method: APIMETHODS.POST,
				Data: data,
				Async: true,
				Callback: jQuery.proxy(this.GetRatesPost, this)
				
			});
/*
			if (response.Result) {
			
				var ratesData = response.Value;
				
				this.SetDates(startDate);
				this.SetRates(ratesData);
			} else {
				ShowError("Could not load rates. The system may be under maintenance. Please try again later", "Data Error")
			}
*/
		},
		
		GetRatesPost: function(response) {
			
			if (response.Result) {
			var ratesData = response.Value;
			startDate = Date.parse(ratesData.StartDate);
			this.SetDates(startDate);
			this.SetRates(ratesData);
			if (this.Cart.Purchase) {
				this.MapPurchase(this.Cart.Purchase);
			}
			}
			this.FinalizeLoadingRates();
			
			
		},

		/** 
		 * Loads the Rates Table with rate data
		 * @param {Object} ratesData from API
		 */
		SetRates: function(ratesData) {
			var object = this;
			var ratesTable = this.Container.find(".bookingRates");

			var ratesBody = ratesTable.find("tbody")
			ratesBody.empty();
			
			var itemTemplate = this.Templates.DailyDetail;
			var salesConfigTemplate = this.Templates.SalesConfig;
			
			if (!ratesData.Products || ratesData.Products.length == 0) {
				return;
			}
			var product = ratesData.Products[0];
			//Update overlay data
			this.Container.data("product", product);
			
			
			base2.Array2.forEach(product.SalesConfigurations, function(salesConfig) {
				var salesTemplate = salesConfigTemplate;
				var hasAddons = salesConfig.Addons.length > 0;
				
				var name = salesConfig.Name;
				
				salesTemplate = salesTemplate.replace("%LABEL%", name);
				salesTemplate = salesTemplate.replace("%SELECTIONLABEL%", "No selection");

				salesTemplate = jQuery(salesTemplate);

				var col1 = salesTemplate.find(".col1");
				var col2 = salesTemplate.find(".col2");
				
						
				if (salesConfig.MouseOverText != null && salesConfig.MouseOverText != "") {
					col1.attr({ title: salesConfig.MouseOverText});
				}
				
				// Render the Daily Rates
				var count = 0
				base2.Array2.forEach(salesConfig.DailyDetails, function(dailyDetail) {
						var item = itemTemplate;
						var price = new Price(dailyDetail.DisplayAmount, {Precision: 0});

						item = item.replace("%PRICE%", price.Price());
						item = jQuery(item);
						item.data("dailyDetail", dailyDetail);
						item.attr("title", dailyDetail.MouseOverText);
						
						if (dailyDetail.Inactive) {
							item.empty();
							item.html("<span>" + price.Price() + "</span>");
							item.addClass("inactive");
						} else {
														
						}
						col2.before(item);
						count = count + 1;
				});
				salesTemplate.data("salesConfiguration", salesConfig);
				var salesTemplateID = "sc" + salesConfig.SalesConfigurationID;
				salesTemplate.attr("id", salesTemplateID);
				salesTemplate.addClass("sales-config");
				if (hasAddons) {
					salesTemplate.addClass("expandable");
					
					//Set Addon Rates expand/hide event
					salesTemplate.find(".col1").click(function() {
						var addonRows = jQuery(".addon[rel=" + salesTemplateID + "], ");
						
						var salesTemplateRow = jQuery(this).parent();
						var action = "open";
						if (salesTemplateRow.hasClass("open")) {
							action = "close";
						} 
						
						switch(action) {
							case "open":
								addonRows.show();
								salesTemplateRow.addClass("open");
								break;
							case "close":
								addonRows.hide();
								salesTemplateRow.removeClass("open");
								break;
						}
						object.ResizeContainer();
					});
					
					 
				}
				
				
				ratesBody.append(salesTemplate);
				
				
				
				// Addons				
				if (hasAddons) {
					//display heading
					
					var headingRow = "<tr class='addon-heading addon' rel='" + salesTemplateID + "'><td colspan='11'><span>Extras</span></td></tr>"
					ratesBody.append(headingRow);
					
					//Show daily details
					base2.Array2.forEach(salesConfig.Addons, function(addon) {
						var addonTemplate = salesConfigTemplate;

						addonTemplate = addonTemplate.replace("%LABEL%", addon.Name);
						addonTemplate = addonTemplate.replace("%SELECTIONLABEL%", "No selection");

						addonTemplate = jQuery(addonTemplate);

						var col1 = addonTemplate.find(".col1");
						var col2 = addonTemplate.find(".col2");

						if (salesConfig.MouseOverText != null && salesConfig.MouseOverText != "") {
							col1.attr({ title: salesConfig.MouseOverText});
						}

						// Render the Addon Daily Rates
						var count = 0
						base2.Array2.forEach(addon.DailyDetails, function(dailyDetail) {
								var item = itemTemplate;
								var price = new Price(dailyDetail.DisplayAmount, {Precision: 0});

								item = item.replace("%PRICE%", price.Price());
								item = jQuery(item);
								item.data("dailyDetail", dailyDetail);
								item.attr("title", dailyDetail.MouseOverText);

								//var isActive = price.IsPrice();
								if (dailyDetail.Inactive) {
									item.empty();
									item.html("<span>" + price.Price() + "</span>");
									item.addClass("inactive");
								} else {

								}
								col2.before(item);
								count = count + 1;
						});
						addonTemplate.data("salesConfiguration", salesConfig);
						addonTemplate.data("addonConfiguration", addon);
						addonTemplate.attr("id", "sc" + salesConfig.SalesConfigurationID + "-" + addon.SalesConfigurationID);
						addonTemplate.attr("rel", salesTemplateID);
						addonTemplate.addClass("addon");
						ratesBody.append(addonTemplate);
						
					})
					
					var footerRow = "<tr class='addon-footer addon' rel='" + salesTemplateID + "'><td colspan='11'>&nbsp;</td></tr>";
					ratesBody.append(footerRow);
					
				}

				
				object.SetToolTips();

			});


		},
		
		/**
		 * Clears the rates display
		 * @param message: Message to show while the rates are not showing
		 */
		EmptyRates: function(message) {
			if (message == undefined) {
				message = "";
			}
			var container = this.Container;

			// Remove all Sales Configurations
			var ratesContainer = container.find(".bookingRates tbody");  
			ratesContainer.empty();

			ratesContainer.append("<tr class='emptyRates'><td colspan='11'>" + message + "</td></tr>");
			this.ResizeContainer();

		},
		
		/**
		 * Refreshes the Rates matrix
		 */
		RefreshRates: function() {
			this.InitializeLoadingRates();
			var date = this.SelectedDate();
			this.GetRates(date);
		},

		/**
		 * Sets the date of the Product Selector to the specified Year Month day
		 * Automatically updates the rates and dates.
		 * @param {Object} year
		 * @param {Object} month
		 * @param {Object} day
		 */
		SetDate: function(year, month, day) {
			
			
			var dateObj = {
				year: parseInt(year),
				month: parseInt(month),
				day: parseInt(day)
			}

			var date = new Date().set(dateObj);

			this.SetDatePicker(date);
			
			this.GetRates(date);
			
			if (this.Options.LoadCart) {
				this.MapPurchase(this.Cart.Purchase);
			}

		},
		
		SetDatePicker: function(date) {
			this.Container.find(".datepopup").datepicker("setDate", date);
			this.Container.find(".datepicker .trigger span").text(date.toString("dd MMM yyyy"));			
		},

		/**
		 * Sets the date labels for the  Rates Table
		 * @param {Object} startDate DateJS object
		 */
		SetDates: function(startDate) {
			var object = this;
			var ratesTable = this.Container.find(".bookingRates");
			
			var ratesHead = ratesTable.find("thead");
			
			var limit = this.Options.MaxDailyDetails;
			
			var day = ratesHead.find(".day:first");
			
			for(i=0; i<limit; i++) {
				day.removeClass("weekend");
				var dayOfWeek = startDate.toString("ddd");
				day.html(dayOfWeek + "<br/>" + startDate.toString("d MMM"));

				if (dayOfWeek == "Sat" || dayOfWeek == "Sun") {
					day.addClass("weekend");
				}
				var dayDate = Date.parse(startDate.toString(this.Options.DateFormat));
				day.data("date", dayDate);
				
				startDate.add({days: 1});
				//Increment
				day = day.next();
			}
			
			
			
		},
		
		/**
		 * Retrieves the currently set criteria definitions
		 */
		CriteriaDefinitions: function() {
			var criteriaDefinitions = new base2.Array2();
			
			try {
				var options = this.Container.find(".optionValue");
				
				base2.Array2.forEach(options, function(option){
				var option = jQuery(option);
				var id = option.attr("id");
				var value = option.text();
				
				var criteriaDefinition = new ListItem(id, value);
				
				criteriaDefinitions.push(criteriaDefinition);
			});
			} catch (exception) {
				
			}
			
			return criteriaDefinitions;
			
		},
		
		DefaultCriteriaDefinitions: function() {
			var criteriaDefinitions = new base2.Array2();
			
			try {
				
				var productData = this.ProductData;
				base2.Array2.forEach(productData.CriteriaDefinitions, function(criteriaDefinition) {
					
					var key = criteriaDefinition.Key;
					var value = criteriaDefinition.DefaultSelection.Value;
					
					var cd = new ListItem(key, value);
					criteriaDefinitions.push(cd);
					
				});
				
				
			} catch (exception) {}
			
			return criteriaDefinitions;
		},
		
		RefreshCriteria: function() {
			this.InitializeLoadingRates();
			
			var object = this;
			
			var criteriaDefinitions = object.CriteriaDefinitions();
			
			
			data = {
				StartDate: this.SelectedDate().toString(this.Options.DateFormat),
				OrderGuid: object.OrderGuid,
				Context: contextID,
				Criteria: criteriaDefinitions
			}
			
			//Refresh working cart
			var url = object.Options.APIRefreshProducts.replace("{channelID}", object.Options.Channel);
			var response = new APIHandler({
							Url: url,
							Data: JSON.stringify(data),
							Method: APIMETHODS.POST,
							Async: true,
							Callback: jQuery.proxy(this.RefreshCriteriaHandler, this)
							
						}).Response;
		},
		
		RefreshCriteriaHandler: function(response) {
			if (response.Result) {
				var date = this.SelectedDate();
				this.GetRates(date);
				this.Cart.Purchase = response.Value;
				this.MapPurchase(this.Cart.Purchase);
			} else {
				ShowError("Couldn't update Cart with specified Criteria");
			}
			
		},


		/**
		 * Selected Date Picker
		 */
		SelectedDate: function() {
			var dateContainer = this.Container.find(".datepopup");
			var dateData = dateContainer.data("datepicker");
			
			var dateObj = {
				year: dateData.selectedYear,
				month: dateData.selectedMonth,
				day: dateData.selectedDay
			}
			
			var date = new Date().set(dateObj).clearTime();
			
			return date;
			
		},
		
		/**
		 * Maps a component object onto the rates table
		 * @param {Object} component
		 */
		MapComponent: function(component, orderGuid) {
			var container = this.Container;
			var object = this;
			var ratesTable = container.find(".bookingRates");
			var ratesBody = ratesTable.find("tbody, .tbody");
			
			//Find the Sales configuration row
			var salesConfigRow = ratesBody.find("#sc" + component.SalesConfigurationID);
			var days = salesConfigRow.find(".day");
			base2.Array2.forEach(days, function(day) {
				day = jQuery(day);
				var dailyDetail = day.data("dailyDetail");
				if (dailyDetail.Date == component.ComponentDate) {
					component.OrderGuid = orderGuid;
					day.addClass("active");
					var price = new Price(component.DisplayAmount, {Precision: 0});
					day.find("label .componentRate").text(price.Price());
					day.data("component", component);
				}
				
			});			
			
			//Addons
			base2.Array2.forEach(component.Addons, function(addon) {
				var addon = addon.AddOnProductOrder;
				object.MapAddon(addon, component.SalesConfigurationID, orderGuid);
			});
		
		},
		
		/**
		 * Maps an addon to the  UI
		 * @param {Object} addon
		 * @param {Object} orderGuid
		 */
		MapAddon: function(addon, salesConfigurationID, orderGuid) {
			var container = this.Container;
			var object = this;
			var ratesTable = container.find(".bookingRates");
			var ratesBody = ratesTable.find("tbody, .tbody");
			
			base2.Array2.forEach(addon.Components, function(component) {
				var addonSalesConfigID = component.SalesConfigurationID;
				
				var addonRow = ratesBody.find("#sc" + salesConfigurationID + "-" + addonSalesConfigID);
				var days = addonRow.find(".day");
				base2.Array2.forEach(days, function(day) {
					day = jQuery(day);
					var dailyDetail = day.data("dailyDetail");
					if (dailyDetail.Date == component.ComponentDate) {
						component.OrderGuid = orderGuid;
						day.addClass("active");
						var price = new Price(component.DisplayAmount, {Precision: 0});
						day.find("label .componentRate").text(price.Price());
						day.data("component", component);
					}
				});
				
			});
		},

		/**
		 * Maps a purchase object onto the rates table
		 * @param {Object} purchase
		 */
		MapPurchase: function(purchase) {
			var object = this;
			//Detect whether the working cart is valid
			var okButton = this.Container.find(".buttons .ok");
			if (purchase.IsValid) {
				okButton.removeClass("disabled");
			} else {
				okButton.addClass("disabled");
			}
			
			//Select the appropriate cells
			
			this.Container.find(".active").removeClass("active");
			
			var ratesTable = this.Container.find(".bookingRates");
			var ratesBody = ratesTable.find("tbody, .tbody");
			
			var orderGuid = this.OrderGuid;
			
				base2.Array2.forEach(purchase.Products, function(product){
						base2.Array2.forEach(product.SalesConfigurations, jQuery.proxy(function(salesConfiguration) {
							
							//Find the sales configuration Row
							var salesConfigRow = ratesBody.find("#sc" + salesConfiguration.SalesConfigurationID);
							var salesConfigSummary = salesConfigRow.find(".col2");
							var salesConfigSubTotal = salesConfigSummary.find(".price");
							var subTotal = new Price(salesConfiguration.DisplayAmount);
							salesConfigSubTotal.empty().html(subTotal.Render());
							salesConfigSummary.addClass("active");
							
							// Map the Components
							base2.Array2.forEach(salesConfiguration.Components, jQuery.proxy(function(component) {
								this.MapComponent(component, product.OrderGuid);
							}, object));
							
							base2.Array2.forEach(salesConfiguration.Addons, jQuery.proxy(function(addon) {
								
								var addonConfigRow = ratesBody.find("#sc" + salesConfiguration.SalesConfigurationID + "-" + addon.SalesConfigurationID);
								var addonConfigSummary = addonConfigRow.find(".col2");
								var addonConfigSubTotal = addonConfigSummary.find(".price");
								var subTotal = new Price(addon.DisplayAmount);
								addonConfigSubTotal.empty().html(subTotal.Render());
								addonConfigSummary.addClass("active"); 
								
							}, object));
							
						}, object));
				});
			
		}

	});
	
	




	var CartErrorList = base2.Base.extend({
		constructor: function(options) {
			this.SetOptions(options);
		},

		Errors: null,

		SetOptions: function(options) {
			this.Defaults = {
				Channel: GBPChannel,
				DateFormat: "dddd d MMMM yyyy",
				Container: "#plan .notifications",
				ErrorContainer: "#plan .notifications"
			}
			
			this.Options = jQuery.extend({}, this.Defaults, options);
			
			this.Container = jQuery(this.Options.Container);
		},

		/**
		 * Refresh the errors with the specified Errors object
		 * @param {Object} errorsObject
		 */
		Refresh: function(errorsObject) {
			if (errorsObject == null) {
				errorsObject = this.Errors;
			}
			this.Errors = errorsObject;
			this.Load();
		},		

		Load: function() {
			this.Render(this.Container);
		},

		/**
		 * Render the errors into the specified container (jQuery'fied)
		 * @param {Object} container
		 */
		Render: function(container) {

			container.empty();

			var errors = this.Errors;
			base2.Array2.forEach(errors, function(error) {

				var errorObj = new Error({
					Message: error.Message
				});

				container.append(errorObj.Render());
			});

		}

	})



	var ProductContent = base2.Base.extend({
		constructor: function(productID, options) {
			this.ID = productID;
			this.SetOptions(options);
			this.GetContent();
		},

		ID: null,

		Content: null,

		SetOptions: function(options) {
			this.Defaults = {
				APIGetContent: GBPServiceUrl + "/{channelID}/product/{productID}/content",
				Channel: GBPChannel,
				Callback: null
			}

			this.Options = jQuery.extend({}, this.Defaults, options);
		},

		GetContent: function() {
			
			var url = this.Options.APIGetContent.replace("{channelID}", this.Options.Channel).replace("{productID}", this.ID);

			var response = new APIHandler({
				Url: url,
				Method: APIMETHODS.GET,
				Async: true,
				Callback: this.Options.Callback

			}).Response;
			/*
			if (response.Result) {
				this.Content = response.Value;
				
			} else {
				ShowError("Could not retrieve content for product " + this.ID);
			}
			*/
		}
	});


