(function(){
	
	var $ = jQuery,
	    ecd = window.ecd || (window.ecd = {}),
		undefined;
	
	//Get the Site instance
	var site = ecd.site = $(document.documentElement).controller(Site);
	
	
	site.bind("beforeInit", function(evt){
	    //Store some jQuery wrappers for global use
	    site.el.$win = $(window);
	    site.el.$doc = $(document);
	    site.el.$html = $(document.documentElement);
	    site.el.$head = site.el.$html.children("head:first");
	    site.el.$body = site.el.$html.children("body:first");
	    
	    site.el.$wrapper = $("#wrapper");
	    site.el.$header = $("#header");
	    site.el.$page = $("#page");
	    site.el.$footer = $("#footer");
    	
    	site.el.$a = $("a");		
    	
	});
	
	site.bind("init", function(evt){
	     
	});
	
	site.bind("afterInit", function(evt){
	     
	});
	
	site.bind("afterInitComponents", function(evt){
	    
		//Add the "ready" class to the html tag after the site, pages, and components have been initialized. The CSS can hook onto it to avoid FOUC.
	    site.el.$html.addClass("ready");
	});
	
	//Intialize the site when the document is ready
	$(document).ready(function(){
		site.init();
	});
	
	$(document).load(function(){
	    site.el.$html.addClass("loaded");
	});
	
	
	
	/* Franklin Page and Component base classes
	.......................................................... */
	site.Page = Site.Page.extend({
	    classPath:"ecd.site.page",
		construct:function(){
	        this._super.apply(this, arguments);
	    }		
	});
	
	
	
	site.Component = Site.Component.extend({
		classPath:"ecd.site.component",
	    construct:function(){
	        this._super.apply(this, arguments);
	    },
		prepLinkList:function(elements){

			//Make all items with # href link to their parent. This is really just for overview-like links
			$(elements).find("a[href='#']").each(function(a, link){
				var $link = $(link),
					$model = $link.closest("ul").closest("li").children("a").first(),
					href = $model.attr("href");
				if(href){
					$link.attr("href", href);
				}
				if($model.closest("li").hasClass("selected") && !$link.siblings("li").hasClass("selected")){
					$link.closest("li").addClass("selected");	
				}				
			});
			
		}
	});
	
	
	
	
	
	
	
	
	
	
	
	
	
	/* Common (applies to all pages)
	.......................................................... */
	site.page.Common = site.Page.extend({
	    className:"Common",
		construct:function(){
	        this._super.apply(this, arguments);
			
			this.createDispatcher({prepAdminActions:"_prepAdminActions"});
	    },
		test:function(){
			return true;
		},
		_init:function(evt){
			this.prepAdminActions();			
		},
		_prepAdminActions:function(){
			var $actions = $("div.admin-actions");
			
			$actions.each(function(d, div){
				var $div = $(div),
					scope = $div.parent().addClass("admin-action-scope");				
			});
			
			return this;
		}
	});
	
	
	
	
	
	
	
	
	
	
	
	
	/* Header
	.......................................................... */
	site.component.Header = site.Component.extend({
		className:"Header",
		construct:function(){
			this._super.apply(this, arguments);
		},
		_init:function(){
			var I = this;
			this.prepLinkList(this.el.$container.find("#nav"));
			
			this.el.$searchForm = this.el.$container.find("form#search");
			
			this.el.$searchForm.submit(function(evt){
				
				var $form = $(this),
					$keywords = $form.find("input[name='query']");
				
				if(!$keywords.val() || $keywords.val().length < 1 || $keywords.val() == $keywords.attr("placeholder")){
					return false;	
				}
			});
			this.el.$searchForm.find("a.submit").click(function(evt){
				I.el.$searchForm.submit();
			});
		}
	});
	
	
	/* Sub (Left) Navigation
	.......................................................... */
	site.component.SubNav = site.Component.extend({
		className:"SubNav",
		construct:function(){
			this._super.apply(this, arguments);
		},
		_init:function(){
			var $subNav = this.el.$container;
			
			this.prepLinkList($subNav);
			
			//Prep topmost overview "ghost" link
			var $overviewLink = $subNav.find("a:first").filter("[href='#']");
			if($overviewLink.length){
				$overviewLink.attr("href", site.el.$page.children(".section-title").find("a").attr("href"));
				if($overviewLink[0].href.toString() == window.location.toString()){
					$overviewLink.closest("li").addClass("selected");	
				}
			}
			
			//Change parent "selected" items to "focused"
			var $activeItem = $subNav.find("li.selected").last();
			
			$activeItem.parents("li.selected").removeClass("selected").addClass("focused");	
			
				
		}
	});
	
	
	
	
	
	
	
	
	
	/* Home Banner
	.......................................................... */
	site.component.HomeBanner = site.Component.extend({
		className:"HomeBanner",
		construct:function(){
			this._super.apply(this, arguments);
			
		},
		cycleOptions:{
			fx: "fade",
			timeout:6000,
			activePagerClass:"active",			
			slideResize:false,
			height:"auto"			
		},
		_init:function(){
			
			var $wrap = this.el.$container,
				$banners = $wrap.children("ul.banners"),
				$pager = $wrap.children(".pager"),
				cycleOptions = $.extend({
					pager:$pager,
					before:function(currSlide, nextSlide, options, forwardFlag){
						//var index = $.inArray(nextSlide, $banners.children("li"));
					}
				}, this.cycleOptions);
			
			
			if(!$banners.children("li").length){
				return;	
			}
			$banners.cycle(cycleOptions);
				
		}
	});
	
	
	
	/* Page Banner
	.......................................................... */
	site.component.PageBanner = site.Component.extend({
		className:"PageBanner",
		construct:function(){
			this._super.apply(this, arguments);			
		},
		_init:function(){
			var I = this;
			//If there's a category dropdown, use it for the sibling directory indexes
			this.el.$selCategory = this.el.$container.find("select");
			this.el.$selCategory.change(function(evt){
				var $dirIndex = I.el.$container.siblings("div.directory-index"),
					$cid = $dirIndex.find("input[name='cid']");
				$cid.val($(this).val());
				$cid.closest("form").submit();
			});
		}
	});
	
	
	
	
	/* GoogleMap
	.......................................................... */
	site.component.GoogleMap = site.Component.extend({
		className:"GoogleMap",
		construct:function(){
			this._super.apply(this, arguments);
			
		},
		options:{
			map:{
				center: new google.maps.LatLng(29.78315148347812, -95.64508438110352),
				zoom: 15,
				mapTypeId: google.maps.MapTypeId.ROADMAP
			},
			dialog:{
				autoOpen:false,
				resizable:false,
				dialogClass:"map-dialog",
				hide:"fade"
			}
		},
		_init:function(){
			this.map = new google.maps.Map(this.el.$container.find("div.canvas").get(0), this.options.map);			

			//Create the dialog
			this.el.$dialog = this.el.$container.find("div.dialog").first();
			this.el.$dialog.dialog(this.options.dialog);
			
		},
		map:null
	});
	
	
	
	/* VCard
	.......................................................... */
	site.component.Vcard = site.Component.extend({
		className:"Vcard",
		construct:function(){
			this._super.apply(this, arguments);
			
		},
		_init:function(){
			
		},
		getAddress:function(){
			var address = {
				street:this.el.$container.find(".street-address").text(),
				city:this.el.$container.find(".locality").text(),
				state:this.el.$container.find(".region").text(),
				zip:this.el.$container.find(".postal-code").text()
			};
			return address;
		},
		getFullName:function(){
			return this.el.$container.find(".fn").text();
		},
		getLatLng:function(){
			var $geo = this.el.$container.find(".geo"),
				lat = parseFloat($geo.find(".latitude").text()),
				lng = parseFloat($geo.find(".longitude").text());
			return {lat:lat, lng:lng};
		}
	});
	
	
	
	
	
	/* Directory Index
	.......................................................... */
	site.component.DirectoryIndex = site.Component.extend({
		className:"DirectoryIndex",
		construct:function(){
			this._super.apply(this, arguments);
			
		},
		_init:function(){
			
		}
	});
	
	
	
	
	
	/* Directory Listing
	.......................................................... */
	site.component.DirectoryListing = site.Component.extend({
		className:"DirectoryListing",
		construct:function(){
			this._super.apply(this, arguments);
			
		},
		_init:function(){
			//Get the map
			this.googleMap = this.el.$container.find("div.wrap-map").controller(site.component.GoogleMap, null, true);	
			
			//Get the vcard
			this.el.$vcard = this.el.$container.find("div.vcard").first();
			this.vcard = $(this.el.$vcard).controller(site.component.Vcard, null, true);
			
			//Set the map center
			var latlng = this.vcard.getLatLng(),
				gLatLng = new google.maps.LatLng(latlng.lat, latlng.lng);
			this.googleMap.map.setCenter(gLatLng);
			
			//Create the map marker
			var markerOptions = $.extend(this.mapMarkerOptions, {
				title:this.vcard.getFullName() || "Energy Corridor Listing",
				icon:this.el.$vcard.find(".marker").text(),
				position:gLatLng,
				map:this.googleMap.map
			});
			this.mapMarker = new google.maps.Marker(markerOptions);
		},		
		
		googleMap:null,
		vcard:null,
		
		mapMarkerOptions:{
			shadow:"/sites/ecd/themes/default/images/map_markers/marker-shadow.png"
		},
		mapMarker:null
	});
	
	
	
	
	/* Directory Map
	.......................................................... */
	site.component.DirectoryMap = site.Component.extend({
		className:"DirectoryMap",
		debug:true,
		construct:function(){
			var I = this;
			$.each([this.options.map,this.options.marker,this.options.groupMarker], function(key, val){
				I.options[key] = $.extend(true, {}, val);
			});
			
			this._super.apply(this, arguments);	

			this._locations = [];
			this._markers = [];
			
			this.createDispatcher({addLocation:"_addLocation"});
			this.createDispatcher({filter:"_filter"});
		},
		options:{
			map:{
				bounds:	new google.maps.LatLngBounds(new google.maps.LatLng(29.72055744093897, -95.76644897460938), new google.maps.LatLng(29.845706414292636, -95.52371978759766))
			},
			marker:{
				shadow:"/sites/ecd/themes/default/images/map_markers/marker-shadow.png"
			},
			groupMarker:{
				titleSuffix:" Directory Listings",
				icon:"/sites/ecd/themes/default/images/map_markers/multi.png"	
			}
		},
		_init:function(){
			var I = this;
			//Get the map and set its default bounds
			this.googleMap = this.el.$container.find("[data-component='GoogleMap']").controller(site.component.GoogleMap, null, true);
			
			google.maps.event.addListenerOnce(this.googleMap.map, "idle", function(){
				I.googleMap.map.fitBounds(I.options.map.bounds);
			});			
			
			//Get the dialog
			this.el.$dialog = this.googleMap.el.$dialog;
			var before = new Date();
			
			//Add all vcards
			this.el.$listings = this.el.$container.children("div.listings");
			var $vcards = this.el.$listings.find("div.vcard");
			$vcards.each(function(v, vcard){
				I.addLocation(vcard);
			});
			this.log("Processed "+ $vcards.length+ " listings in "+(((new Date())-before)/1000) +" seconds.");
			
			//Bind to category dropdown
			this.el.$selCategoryGroup = this.el.$container.find("select[name='category_group']");
			this.el.$selCategoryGroup.change(function(evt){
				I.filter($(this).val());
			});
			//Set initial filter
			this.filter(this.el.$selCategoryGroup.val());
		},
		googleMap:null,	
		_locations:null,
		_markers:null,
		
		_addLocation:function(evt, location){
			var I = this,
				vcard,
				marker;
			if(!(location instanceof site.component.DirectoryMapLocation)){
				//It's an HTML element, so create a DirectoryMapLocation
				location = $(location).controller(site.component.DirectoryMapLocation, null, true);			
			}
			//If we don't already have this location...
			if($.inArray(location, this._locations) < 0){
				this._locations.push(location);
				
				//Find a marker already at this lat/lng
				marker = this.getMarkerAtLatLng(location.getLatLng());
				if(!marker){
					
					var markerOptions = $.extend(this.options.marker, {
						title:location.vcard.getFullName() || "Energy Corridor Listing",
						icon:location.getIconPath(),
						position:location.getLatLng()
					});
					marker = new google.maps.Marker(markerOptions);	
					marker.setMap(this.googleMap.map);
					this._markers.push(marker);
				}	
				
				
				//Bind to the marker
				google.maps.event.addListener(marker, "click", function(){
					/*for(var l=0; l < I._locations.length; l++){
						var location2 = I._locations[l],
							distance = google.maps.geometry.spherical.computeDistanceBetween(marker.getPosition(), location2.getLatLng());
						
						if(distance < 5){
							I.el.$dialog.empty().append(location2.vcard.el.$container.clone());
						}
					}*/			
					I._onMarkerClick(marker);
					
				});
			
			}else{
				evt.result=false;
				return false;	
			}
			
		},
		_catGroupId:null,
		_filter:function(evt, catGroupId, locations, markers){
			this._catGroupId = catGroupId;
			
			var locations = locations || this._locations,				
				markers = markers || this._markers,
				filtered = this.getLocationsInCategoryGroup(catGroupId, locations);	
			
			//Display relevant markers
			for(var m=0; m<markers.length; m++){
				var marker = markers[m],
					markerLocations = this.getLocationsAtLatLng(marker.getPosition(), filtered);
				if(markerLocations.length){
					marker.setTitle(markerLocations.length + this.options.groupMarker.titleSuffix);
					
					//Remove group icon if there is only one location
					if(markerLocations.length === 1){
						marker.setTitle(markerLocations[0].vcard.getFullName());
						marker.setIcon(markerLocations[0].getIconPath());	
					}else{
						marker.setIcon(this.options.groupMarker.icon);
					}
					
					marker.setVisible(true);
				}else{
					marker.setVisible(false);	
				}
			}
			return locations;
		},
		_onMarkerClick:function(marker){

			var I = this,
				locations = I.getLocationsAtLatLng(marker.getPosition());
			
			locations = I.getLocationsInCategoryGroup(this._catGroupId, locations);
			//Empty the dialog
			I.el.$dialog.empty();
			//Get the contents of the relevant locations
			for(var l=0; l < locations.length; l++){
				var loc = locations[l],
					$content = loc.vcard.el.$container;
				
				I.el.$dialog.append($content.clone());
			}
			//Set the dialog title
			I.el.$dialog.dialog("option", "title", locations.length + " Directory Listing(s)");
			
			//Set the max height on the dialog
			if(locations.length > 4){
				I.el.$dialog.dialog("option", "height", 300);
			}else{
				I.el.$dialog.dialog("option", "height", "auto");	
			}
			
			//Show the dialog
			I.el.$dialog.dialog("open");
		},
		getLocationsAtLatLng:function(latlng, locations){
			var locations = locations || this._locations,
				results = [];
			for(var l=0; l < locations.length; l++){
				var loc = locations[l];
				if(loc.getLatLng().equals(latlng)){
					results.push(loc);	
				}
			}
			return results;
		},
		getMarkerAtLatLng:function(latlng, markers){
			var markers = markers || this._markers,
				result = null;
			
			for(var m=0; m < markers.length; m++){				
				if(markers[m].getPosition().equals(latlng)){
					result = markers[m];
					break;
				}
			}		
			
			return result;	
		},
		getLocationsInCategoryGroup:function(catGroupId, locations){
			var locations = locations || this._locations,
				results = [];
			for(var l=0; l < locations.length; l++){
				var location = locations[l];
				//empty or null group id should mean "all"
				if(catGroupId == null || !$.trim(catGroupId).length || location.getIsInCategoryGroup(catGroupId)){
					results.push(location);
				}
			}
			return results;
		}
	});
	
	
	site.component.DirectoryMapLocation = site.Component.extend({
		className:"DirectoryMapLocation",
		_init:function(evt, vcard){
			
			this.vcard = this.el.$container.controller(site.component.Vcard);
		},
		vcard:null,
		getCategories:function(){
			var catStr = this.vcard.el.$container.find(".categories").text(),
				cats = [];
			if(catStr && catStr.length){
				cats = catStr.split(",");
				$.each(cats, function(i, val){
					cats[i] = val.replace(/^\d+:(\d+)$/, "$1");
				});
			}
			return cats;			
		},
		getIconPath:function(){
			return this.vcard.el.$container.find(".marker").text()
		},
		getIsInCategory:function(catId){
			var cats = this.getCategories();
			if($.inArray(catId+"", cats) > -1){
				return true;	
			}
			return false;
		},
		getCategoryGroups:function(){
			var catStr = this.vcard.el.$container.find(".categories").text(),
				groups = [];
			if(catStr && catStr.length){
				groups = catStr.split(",");
				$.each(groups, function(i, val){
					groups[i] = val.replace(/^(\d+):(\d+)$/, "$1");
				});
			}
			return groups;	
		},
		getIsInCategoryGroup:function(groupId){
			var groups = this.getCategoryGroups();
			if($.inArray(groupId+"", groups) > -1){
				return true;	
			}
			return false;
		},
		getLatLng:function(){
			var latlng = this.vcard.getLatLng(), //Vcard does not return a Google LatLng instance
				gLatLng = new google.maps.LatLng(latlng.lat, latlng.lng);
			return gLatLng;
		}
		
	});
	
	
	
	
	/* Bike to Work Signup
	.......................................................... */
	site.component.BtwSignup = site.Component.extend({
		className:"BtwSignup",
		construct:function(){
			this._super.apply(this, arguments);
		},
		_init:function(){
			var I = this;
			this.el.$form = this.el.$container.find("form").first();
			
			if(!this.el.$form.length) return;
			
			this.el.$validSummary = this.el.$container.find(".valid-summary");
			
			//http://docs.jquery.com/Plugins/Validation/validate#toptions
			this.validator = this.el.$form.validate({
				debug:false,
				/*onsubmit:true,
				onfocusout:true,
				onkeyup:false,*/
				errorClass:"invalid",
				validClass:"valid"
			});
			
			
			var rules = this.validator.settings.rules = {},
				messages = this.validator.settings.messages = {};
				
			this.el.$form.find(":input").not(":button").each(function(){
				var $input = $(this),
					$label = $input.closest("tr").find("label[for='']"),
					$messages = $input.closest("tr").find("var[data-message]"),
					rule = rules[$input.attr("name")] = {},
					msg = messages[$input.attr("name")] = {};
				
				/*if($input.attr("data-required") == "true"){
					rule.required = true;
					if($message.filter("[data-message*='required']")){
						rule.require	
					}
				}*/
				$messages.each(function(m, message){
					var $message = $(message),
						msgType = $message.attr("data-message"),
						dependsOn = $message.attr("data-depends");
						
					if(dependsOn && dependsOn.length){
						var dependsSplit = dependsOn.split(/=/),
							dependsName = dependsSplit[0],
							dependsVal = dependsSplit[1];
							
						rule[msgType] = {
							depends: function(el){
								var $dependency = I.el.$form.find("[name='"+dependsName+"']");	
								//console.log(dependsName, $dependency, $dependency.val(), dependsVal, $dependency.val() == dependsVal);
								if($dependency.is(":checkbox,:radio")){
									$dependency = $dependency.filter(":checked");	
								}
								return $dependency.val() == dependsVal;
							}
						};
						
					}else{
					
						rule[msgType] = true;	
					
					}
					
					msg[msgType] = $message.html();
					
				});
				
			});
			
			//console.log(this.validator.settings);
			
		},
		validator:null
	});
	
	
	
	
	/* Sitemap
	.......................................................... */
	site.component.Sitemap = site.Component.extend({
		className:"Sitemap",
		construct:function(options){
			this._super.apply(this, arguments);
		},
		options:{},
		_init:function(){
			
			this.prepLinkList(this.el.$container);
			this._initLayout();
		},
		_initLayout:function(){
			//Add row separators
			var maxWidth = this.el.$container.width(),
				$roots = this.el.$container.children("ul").children("li"),
				colWidth = $roots.outerWidth(true),
				perRow = Math.floor(maxWidth/colWidth);
			
			$roots.each(function(r, root){
				var $root = $(root)
				if(r === 0){
					$root.addClass("row-first");
				}else if(r % perRow === 0){
					var $clear = $("<li class='clearboth'/>");
					$root.prev("li").addClass("row-last");
					$root.addClass("row-first").before($clear);
				}
			});
		}		
	});
	
})();
