//> YAJET -- Yet Another JavaScript Emplate Tengine
//> Author: Mihai Bazon <mihai.bazon@gmail.com>
//> Distributed under the BSD license.  Visit www.yajet.net for details.
//> (c) Mihai Bazon 2010
//> Documentation: http://www.yajet.net/yajet/doc/yajet.html
function YAJET(m){m=n(m||{},{reader_char:"$",filters:{},directives:{},with_scope:false});var j=this.TEMPLATES={};var s=0;function r(){return"__GSY"+(++s)}function l(y){return y[y.length-1]}function x(z,B,C){var A=0,D=z.length,y=new Array(D);while(--D>=0){y[A]=B.call(C,z[A++])}return y}var w={"(":")","{":"}","[":"]"};var f=m.reader_char;var g=i(f);this.X_CONT={};this.X_BREK={};this.X_IMPORT={};var o="var __EXPORTS = {};";var v="return (this === YAJET.X_IMPORT) ? __EXPORTS : __BUF;";var d=("var __BUF = '', VUT = OUT;function OUT(str) { if (str != null) __BUF += str };");var t="return __BUF";var c=("} catch(ex) { if (ex === YAJET.X_CONT) continue;if (ex === YAJET.X_BREK) break;throw ex;}");function e(y){y=q(y);return"function "+y+"(){return YAJET.process("+i(y)+", this, arguments)}"}function k(ab){var z=[],L=[],N=0,ad=ab.length,P="",S=this,E=[];var I={"if":function(){K("if ("+Q()+") {")},aif:function(){var af=Q(true);var ae=af.length>1?af[1]:"it";var ag=af.length>2?af[2]:(ae+" != null && "+ae+" !== false && !("+ae+" instanceof Array && "+ae+".length == 0) && !("+ae+" === '')");K("(function("+ae+") { if ("+ag+") {","}}).call(this, "+af[0]+");")},unless:function(){K("if (!("+Q()+")) {")},"else":function(){aa("} else {");X(")")},elsif:function(){aa("} else if ("+Q()+") {");X(")")},maphash:function(){var af=Q(true);var ag=af[0],ai=af[1],ah=af[2],ae=r();K("(function("+ae+") {for (var "+ag+" in "+ae+") {if ("+ae+".hasOwnProperty("+ag+")) try {var "+ai+" = "+ae+"["+ag+"];",c+"}}).call(this, "+ah+");")},map:function(){var ai=Q(true),ag,aj,af,ah=r(),ae=r();if(ai.length==3){ag=ai[0],aj=ai[1],af=ai[2]}else{ag=r();if(ai.length==2){aj=ai[0],af=ai[1]}else{if(ai.length==1){af=ai[0]}}}K("(function("+ah+") {for (var "+(ai.length==1?"$_,":"")+ae+" = "+ah+".length,"+ag+" = 0; "+ag+" < "+ae+"; ++"+ag+") try {"+(ai.length==1?"with ($_ = "+ah+"["+ag+"]) {":"var "+aj+" = "+ah+"["+ag+"];"),(ai.length==1?"}":"")+c+"}).call(this, "+af+");")},repeat:function(){var ag=Q(true),ah,ai=1,ae,af=r();if(ag.length==3){ai=ag.shift()}ah=ag.shift();ae=ag.shift()||r();K("(function("+af+") {for (var "+ae+" = "+ai+"; "+ae+" <= "+af+"; ++"+ae+") try {",c+"}).call(this, "+ah+");")},"continue":function(){aa("throw YAJET.X_CONT;");X(")")},"break":function(){aa("throw YAJET.X_BREK;");X(")")},let:function(){K("(function(){","}).call(this);");J()},"var":function(){J();X(")")},"with":function(){K("with ("+Q()+") {")},block:function(){G();var af=D();var ae=q(Q());K("function "+af+"("+ae+") {"+d,t+"}")},"export":function(){G();var af=D();var ae=q(Q());K(af+" = __EXPORTS["+i(af)+"] = function("+ae+") {"+d,t+"}; ");E.push(af)},"import":function(){var ae=Q(true);X(")");aa(x(ae,e).join(";\n")+";")},process:function(){G();var af=D();var ae=Q();X(")");aa("VUT(YAJET.process("+i(af)+", this, ["+ae+"]));")},wrap:function(){G();var af=D();var ae=q(Q());if(ae){ae+=", "}af="(typeof "+af+" == 'function' ? "+af+" : YAJET.TEMPLATES."+af+")";K("VUT("+af+".call(this, "+ae+"function(OUT, VUT){","}));")},content:function(){aa("if (arguments[arguments.length - 1] instanceof Function) arguments[arguments.length - 1].call(this, OUT, VUT);");X(")")},literal:function(){var ae,ag,af;G();ae=C()+")";W(/^[ \t\xA0]*\n/);ag=ab.indexOf(ae,N);if(ag<0){b("Unfinished LITERAL (was looking for "+ae)}af=ab.substring(N,ag);N=ag+ae.length;aa("OUT("+i(af)+")")},syntax:function(){var ae=f;G();f=T();K("",function(){f=ae})}};I.when=I["if"];I.awhen=I.aif;I.foreach=I.map;var B={peek:U,next:T,rest:O,out:aa,skip_ws:G,assert:A,assert_skip:X,skip:W,looking_at:Y,block_open:K,block_close:V,read_balanced:Q,read_string:C,read_simple_token:D,read_valist:J,to_js_string:i,trim:q,map:x,set_output:H,directives:m.directives,EX_PARSE:b};R();if(m.with_scope){z.unshift("with (this) {");z.push("}")}if(E.length>0){z.unshift("var "+E.join(", ")+";")}var F=u.call(this,z.join("\n"));if(E.length>0){var ac=F(this.X_IMPORT);for(var Z in ac){if(ac.hasOwnProperty(Z)){j[Z]=ac[Z]}}}return F;function U(){return ab.charAt(N)}function T(){return ab.charAt(N++)}function O(ae){return ab.substr(N,ae!=null?ae:ad)}function aa(ae){z.push(ae)}function y(){if(P.length>0){aa("OUT("+i(P)+");")}P=""}function G(af){var ae=false;while(W(" ")||W("\t")||W("\n")||W("\xa0")||(!af&&(W("//","\n")||W("/*","*/")||W("<!--","-->")))){ae=true}return ae}function A(af){var ae=Y(af);if(!ae){b("Expecting "+af+" at "+N)}return ae}function X(ae){G();N+=A(ae).length}function W(ag,ae){var af=Y(ag);if(af){N+=af.length;if(ae){var ah=ab.indexOf(ae,N);if(ah==-1){throw b('Unterminated "'+ag+'" at '+O())}N=ah+ae.length}}return af}function Y(ae){if(ae instanceof RegExp){return(ae=ae.exec(O()))&&{match:ae[0],length:ae[0].length,groups:ae}}return O(ae.length)==ae?{match:ae,length:ae.length}:null}function K(af,ae){if(!ae){ae="}"}aa(af);L.push(ae)}function V(){var ae=L.pop();if(ae instanceof Function){ae()}else{aa(ae)}}function H(af){var ae=z;z=af;return ae}function Q(ak){G();var ae=U();var ai=w[ae];if(ai){var ag=[ai];var aj="";var af=[];++N;while(N<ad){var ah=U();if(ah==l(ag)){ag.pop();++N;if(ag.length==0){if(ak){aj=q(aj);if(aj){af.push(aj)}return af}return aj}aj+=ah}else{if(ah in w){ag.push(w[ah]);++N;aj+=ah}else{if(ah=='"'||ah=="'"){aj+=i(C())}else{if(ak&&ag.length==1&&(W(",")||W(";")||W("=>")||W(".."))){af.push(aj);G();aj=""}else{if(G()){aj+=" "}else{++N;aj+=ah}}}}}}}}function R(){while(N<ad){var ae=T();if(ae==f){if(W(ae)){P+=ae}else{if(W("#")){var af=ab.indexOf("\n",N);if(af==-1){af=ad}N=af}else{y();M()}}}else{P+=ae}}y()}function C(){var af=U();if(af=="'"||af=='"'){var ai=N;var ae=false,ah="";do{++N;var ag=U();if(!ae){if(ag=="\\"){ae=true;continue}if(ag==af){++N;return ah}}else{switch(ag){case"b":ag="\b";break;case"f":ag="\f";break;case"n":ag="\n";break;case"t":ag="\t";break;case"r":continue;case"u":++N;ag=parseInt(O(4),16);if(isNaN(ag)){b("Expecting an Unicode character code at: "+N)}ag=String.fromCharCode(ag);N+=4;break}}ae=false;ah+=ag}while(N<ad);b("Unterminated string at: "+ab.substr(ai))}}function D(ai){var af="",ae=0,ag,ah;for(;;){ag=U();ah=ag.charCodeAt(0);if((ah>=65&&ah<=90)||(ah>=97&&ah<=122)||(ah>=48&&ah<=57)||ah==95||(ah==36&&!ai)||ah==124||ah==46){af+=ag;(ah==36||ah==124||ah==46)?++ae:(ae=0);++N}else{break}}if(ae>0){N-=ae;af=af.substr(0,af.length-ae)}return af}function J(){var ae=[];X("(");while(N<ad){G();if(Y("(")){ae.push(Q(true))}else{if(W(")")){break}else{ae.push(D())}}}aa("var "+x(ae,function(af){return af instanceof Array?af[0]+" = "+af[1]:af}).join(", ")+";")}function M(){if(W("_")){aa("VUT($_);")}else{if(W("-")){G(true)}else{if(W("(")){var ae=D();if(ae){ae=ae.toLowerCase();var aj=m.directives[ae]||I[ae];if(!aj){b("Unknown directive: "+ae.toUpperCase())}aj.call(S,B)}else{--N;aa(Q()+";")}}else{if(W(")")){V()}else{if(W("(")){b("Unrecognized construct at "+O())}else{if(Y("{")){var af=Q(true);if(af.length>0&&/\S/.test(af[0])){var ak=af.shift();while(af.length>0){var ai=q(af.shift());var ah=ai.indexOf("(");var ag=null;if(ah>=0){ag=q(ai.substring(ah+1,ai.length-1));ai=ai.substring(0,ah)}if(!ag){ag=ak}else{ag=ak+", "+ag}ak="YAJET.filter("+i(ai)+", "+ag+")"}aa("VUT("+ak+");")}}else{G();var af=D(true).split(/\s*\|\s*/);var ak=af.shift();while(af.length>0){ak="YAJET.filter("+i(af.shift())+", "+ak+")"}aa("VUT("+ak+");")}}}}}}}}function i(y){return'"'+y.replace(/\x5c/g,"\\\\").replace(/\r?\n/g,"\\n").replace(/\t/g,"\\t").replace(/\x22/g,'\\"')+'"'}function b(y){throw new Error(y)}function h(y){throw new Error(y)}function u(B){try{B=(o+d+B+v);var y=this,C=new Function("YAJET",B);function z(D){return C.call(D,y)}z.orig=C;z.code=B;return z}catch(A){window.console&&console.log("%s",B);A.yajetCode=B;throw A}}function q(y){return y.replace(/^\s+|\s+$/g,"")}var a=YAJET.FILTERS={html:function(y){return String(y).replace(/&/g,"&amp;").replace(/\x22/g,"&quot;").replace(/\x27/g,"&#x27;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/\u00A0/g,"&#xa0;")},upcase:function(y){return String(y).toUpperCase()},downcase:function(y){return String(y).toLowerCase()},plural:function(z,y){if(!(y instanceof Array)){if(arguments.length>2){y=Array.$(arguments,1)}else{y=y.split("|")}}y=z<y.length?y[z]:y[y.length-1];return y.replace(/##?/g,function(A){return A.length==2?"#":z})},trim:q};this.compile=k;this.filter=function(A){var y=p(arguments,1);var z=m.filters[A]||a[A];if(z){return z.apply(this,y)}h("No filter "+A)};this.process=function(y,B,z){if(z==null){z=[]}var A=j[y];if(!A){h("No exported function: "+y)}return A.apply(B,z)};this.reader_char=function(){return f};function n(y,B,z,A){A={};for(z in B){if(B.hasOwnProperty(z)){A[z]=B[z]}}for(z in y){if(y.hasOwnProperty(z)){A[z]=y[z]}}return A}function p(C,D){if(D==null){D=0}var y,B,z;try{y=Array.prototype.slice.call(C,D)}catch(A){y=new Array(C.length-D);for(B=D,z=0;B<C.length;++B,++z){y[z]=C[B]}}return y}};

// Global Class Object
var HAR = new googleMapping();

function googleMapping(){
	
	var CLASS = this;
// Class Variables ------------------------------------------
// ----------------------------------------------------------
	// Instance ID. Used for creating global objects in the window scope,
	// without colliding with other maps on the same page.
	CLASS.ID = (new Date()).getTime();
	CLASS.mapDIV = "myMap";
	CLASS.canvassOverlayView = null; // Used for MapCanvassProjection Access
	CLASS.mapRenderedInContainer = false;
	CLASS.directionsRendered = null;
	CLASS.exposedAPILoadedFlag = 'har_google_map_API_ready';
	window[CLASS.exposedAPILoadedFlag] = window[CLASS.exposedAPILoadedFlag] || false; // Global flag for all map instances
	CLASS.loadParcels 	= true; // By default ALL of our maps will load parcels
	CLASS.loadSimpleGeo = false; // By default ALL of our maps will load SimpleGeo API
	CLASS.loadOAuth		= true; // By default ALL of our maps will load OAuth API [needed for Yelp!]
	CLASS.trafficLayer = null;
	CLASS.mapOptions = {}; /* default -> {center:HOUSTON, zoom:11, mapTypeId:google.maps.MapTypeId.ROADMAP, domElement:"myMap"}    Gets loaded in: CLASS._installMap() */
	CLASS.map = {}; // Official Map Object, initialized in: CLASS._installMap()
	// Parcels API
	CLASS.parcelAPIIsLoaded = function(){ return (typeof Dmp!="undefined" && typeof Dmp.Env!="undefined" && typeof Dmp.Env.Connections!="undefined"); };
	CLASS.parcelLayerLoaded = false;
	CLASS.parcelLayerInitiated = false;
	CLASS.parcelLicense = "d5af22e4-ef37-4b54-9c80-34d2005ffcbd"; //"9a398262-b16a-4135-83f4-314b5a687ef4";
	CLASS.parcelAPI = "http://parcelstream.com/api/DmpApi.aspx?map=Google3&host=parcelstream.com/&v=3&services=Utility,MapComp"; //"http://parcelstream.com/DMPAPI.aspx?key=1";
	// Simple Geo API
	CLASS.simpleGeoJSONPToken = 'qNTBJGGcZ365vAL5LhuBUmn4C84jGjW5';
	CLASS.simpleGeoAPI = "http://cdn.simplegeo.com/js/1.3/simplegeo.all.min.js";
	CLASS.simpleGeoAPIIsLoaded = function(){ return (typeof simplegeo!="undefined") };
	CLASS.simpleGeoClientsLoaded = false;
	// Yelp! Auth
	CLASS.yelpAuth = {
			consumerKey: "ki1LGXNF0hs6QGTDyszrIg", 
			consumerSecret: "75JzH5uRI30yUMFlxa0bDnAJVSw",
			accessToken: "pOFlxsNAUkrvizGxawWOaMB0gHfWjGfD",
			accessTokenSecret: "A-7mtr4R-OZbSP1sq4G94OJjZ14",
			serviceProvider: { 
				signatureMethod: "HMAC-SHA1"
			}
		};
	CLASS.yelpControl = false;
	// OAuth API
	CLASS.OAuthAPI = "http://www.har.com/js/oauth.js";
	CLASS.OAuthAPIIsLoaded = function(){ return (typeof OAuth!="undefined") };
	
	CLASS.mapAPI = 'http://maps.google.com/maps/api/js?client=gme-houstonrealtorsinformation&sensor=true&callback=HAR._confirmAPILoad'; //Removed v=3.3 parameter
	CLASS.pinURL = 'http://www.har.com/images/pointer/';
	CLASS.customControlImageURL = 'http://www.har.com/images/customcontrols/';
	CLASS.pins = {
		golfPrivate: 		CLASS.pinURL+'brownGolf.gif',
		golfPublic: 		CLASS.pinURL+'greenGolf.gif',
		blueHouse: 			CLASS.pinURL+'bluePointer.png',
		purpleHouse: 		CLASS.pinURL+'purplePointer.png',
		redHouse: 			CLASS.pinURL+'redPointer.png',
		greenHouse: 		CLASS.pinURL+'greenPointer.png',
		flatBlueHouse: 		CLASS.pinURL+'flat_blue_house.png',
		flatRedHouse: 		CLASS.pinURL+'flat_red_house.png',
		greenMobile: 		CLASS.pinURL+'greenMobile.png',
		redMobile:	 		CLASS.pinURL+'redMobile.png',
		purpleMobile: 		CLASS.pinURL+'purpleMobile.png',
		schoolElementary: 	CLASS.pinURL+'schoolElementary.gif',
		schoolMiddle: 		CLASS.pinURL+'schoolMiddle.gif',
		schoolHigh: 		CLASS.pinURL+'schoolHigh.gif',
		harLogo: 			CLASS.pinURL+'iconSmHAR.gif',
		blueIcon: 			CLASS.pinURL+'blueIcon.png',
		greenArrow: 		CLASS.pinURL+'greenArrow.gif',
		greenFlag: 			CLASS.pinURL+'flagforSale.gif',
		blueFlag: 			CLASS.pinURL+'flagforLease.gif',
		yellowFlag: 		CLASS.pinURL+'flagallProperty.gif',
		hospitalCross: 		CLASS.pinURL+'flagforhospital.png',
		iconHighrise: 		CLASS.pinURL+'iconHighrise.gif',
		closePoly:			CLASS.pinURL+'closeButton.png',
		movePoly:			CLASS.pinURL+'moveButton.png',
		refreshPoly:		CLASS.pinURL+'refreshButton.png',
		infoPoly:			CLASS.pinURL+'infoButton.png',
		starPin:			'https://chart.googleapis.com/chart?chst=d_map_pin_icon_withshadow&chld=star|FFFFFF'
	};
	CLASS.images = {
		infoWindowArrowRight: CLASS.pinURL+'infowindow/'+'arrowRight.png',
		infoWindowArrowLeft: CLASS.pinURL+'infowindow/'+'arrowLeft.png'
	};
	CLASS.customControlImages = {
		clearDrivingDirections 	: { src:CLASS.customControlImageURL+"clearDrivingDirections.png" },
		clearMap 				: { src:CLASS.customControlImageURL+"clearMap.png" },
		currentTraffic 			: { src:CLASS.customControlImageURL+"currentTraffic.png" },
		drawArea 				: { src:CLASS.customControlImageURL+"drawArea.png" },
		legend	 				: { src:CLASS.customControlImageURL+"legend.png" },
		poi	 					: { src:CLASS.customControlImageURL+"poi.png" },
		showListings 			: { src:CLASS.customControlImageURL+"showListings.png" },
		showSoldListingsRed		: { src:CLASS.customControlImageURL+"showSoldListingsRed.png" },
		showListingsGreen		: { src:CLASS.customControlImageURL+"showListingsGreen.png" },
		yelp					: { src:"http://media3.ct.yelpcdn.com/static/201012162846157596/i/developers/yelp_logo_50x25.png"}
	};
	// YAJET Object Collection
	// YAJET Usage: http://www.yajet.net/yajet/doc/yajet.html
	CLASS.yajet = {};
	CLASS.yajet.main = new YAJET({
		reader_char : "$",
		with_scope  : false
	});
	// Collection of HTML templates
	// YAJET Usage: http://www.yajet.net/yajet/doc/yajet.html
	CLASS.templates = {
		yelpInfoWindow : CLASS.yajet.main.compile("<img height='100' width='100' src='${this.image_url || 'http://search.har.com/images/nophoto.jpg'}' style='float:left;'/>"+
							"<div style='max-width:150px;float:left;margin-left:10px;'><strong style='color:black;'>${this.name}</strong><br><img src='${this.rating_img_url}'><br>"+
							"${this.location.address}<br>"+
							"${this.location.city}, ${this.location.state_code} ${this.location.postal_code}<br><br>"+
							"${this.phone ? \"Call Me: \"+this.phone:\"\"}</div>"+
							"<p style='clear:both;width:200px;padding-top:10px;'>${this.snippet_text} <a target='_blank' href='${this.url}'>read more</a></p><br>"+
							"<img src='http://media4.px.yelpcdn.com/static/20101216159295510/i/map/miniMapLogo.png'/>")
						 
	};
	CLASS.defaultPolyOptions = {
		strokeColor: '#000080',
		strokeWeight: 2,
		fillColor: '#000080',
		fillOpacity: 0.2,
		clickable:false,
		tags:[]
	};
	CLASS.domElementToggle = function(id){
		var el = document.getElementById(id);
		if(!el)
		{
			CLASS.console.error("CLASS.domElementToggle() >> Not passed a valid HTML DOM Element ID");
			return;
		}
		
		// Check to see if it's visible
		if( el.style.display==='block' )
			el.style.display='none';
		else
			el.style.display='block';
	};
	CLASS.PropertyTypeEnum = {
		singleFamily:1,
		townhouseCondo:2,
		residentialLotsLand:4,
		multiFamily:8,
		homesAndOrAcreage:16,
		highriseCondominium:32,
		residentialLease:64,
		forSale:128,
		forLease:256
	};
	CLASS.calcPropType = function(num, bitList){
		// Make sure we have a number
		if( typeof num !== "number" ){ return "Your 'num' parameter is not a number"; }
		// Check for 2nd parameter
		if( typeof bitList === "undefined" ){ bitList = CLASS.PropertyTypeEnum; }
		// Calulate the Property Type
		var list = [];
		for(var key in bitList)
		{
			if( num & bitList[key] )
			{ list.push(key); }
		}		
		// Return the result
		if( list.length==0 )
		{ return "No Match"; }
		else
		{ return list.join(', '); }
	};
	// Returns the distance (in km) between two google.LatLng() points
	// see: http://www.movable-type.co.uk/scripts/latlong.html
	CLASS.distanceBetweenPoints = function(p1, p2) {
		if (!p1 || !p2) {
			return 0;
		}
		
		var R = 6371; // Radius of the Earth in km
		var dLat = (p2.lat() - p1.lat()) * Math.PI / 180;
		var dLon = (p2.lng() - p1.lng()) * Math.PI / 180;
		var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
		Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) *
		Math.sin(dLon / 2) * Math.sin(dLon / 2);
		var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
		var d = R * c;
		return d;
	};
	// Returns the radius of current map view (in km)
	CLASS.getMapRadius = function(){
		// Capture the center of the map
		var c = CLASS.map.getCenter();
		// Capture the NorthEast Corner of the map
		var ne = CLASS.map.getBounds().getNorthEast();
		
		// Send the two points for calculating the radius
		return CLASS.distanceBetweenPoints(c,ne);
	};
	CLASS.markers = [];
	CLASS.shapes = [];
	CLASS.infoWindows = [];
	// Debugging Class that uses the console if present
	CLASS.console = function(){
			if (!window.console) console = {};
			console.log = console.log || function(){};
			console.warn = console.warn || function(){};
			console.error = console.error || function(){};
			console.info = console.info || function(){};
			var activateConsole = true;
			// -------------------------------
			var localObj = {
					log : function(msg){ 
							if(activateConsole){ 
								if(typeof msg!="object")
								{ console.log("["+CLASS.mapDIV+"] "+msg); }
								else
								{ console.log("["+CLASS.mapDIV+"] Object Debug Below"); console.log(msg); }
							} 
						},
					warn : function(msg){ if(activateConsole) console.warn("["+CLASS.mapDIV+"] "+msg); },
					error : function(msg){ if(activateConsole) console.error("["+CLASS.mapDIV+"] "+msg); },
					info : function(msg){ if(activateConsole) console.info("["+CLASS.mapDIV+"] "+msg); },
					set : function(useConsole){ activateConsole = useConsole; },
					off : function(){ activateConsole = false; },
					on : function(){ activateConsole = true; }
				};
			return localObj;
		}();
	// This method is used to debug any object.  It will tell your its properties.
	CLASS.console.debugObj = function(obj, name)
	{		
		var name = name || "";
        var keys = "";
		for( var key in obj)
		{
			var tabs = "";
			switch( typeof obj[key] )
			{
				case "function":
					tabs = "\t";
					break;
				default:
					tabs = "\t\t";
			}
			
			keys += "<"+typeof obj[key]+">"+tabs+key+"\n";	
		}

		CLASS.console.info("Debug of: "+name+"\nTYPE: " + typeof obj + "\n\n"+"Attribute List\n----------------------------\n"+keys);
	}
	
	// Class that helps with tracking User Interaction
	// opts: {url,vars[],error()}
	CLASS.beacon = function(opts){
		// Make sure we have a base object for opts
		opts = opts || {};	
		// Setup defaults for options
		opts.url 	 = opts.url || null;
		opts.vars 	 = opts.vars || {};
		opts.error 	 = opts.error || function(){};
		opts.success = opts.success || function(){};
		
		// Split up vars object into an array
		var varsArray = [];
		for(var key in opts.vars){ varsArray.push(key+'='+opts.vars[key]); }
		// Build query string
		var qString = varsArray.join('&');
		
		// Create a beacon if a url is provided
		if( opts.url )
		{
			// Create a brand NEW image object
			var beacon = new Image();
			// Attach the src for the script call
			beacon.src = opts.url + '?' + qString;
			// Attach the event handlers to the image object
			if( beacon.onerror )
			{ beacon.onerror = opts.error; }
			if( beacon.onload )
			{ beacon.onload  = opts.success; }
		}
		// If we are not provided with an URL we will warn the beacon via console
		else
		{
			// Warn the console about not having a url
			CLASS.console.warn( "CLASS.beacon() >> [No URL passed] ?" + qString );
		}
	};
	
	// Clone Object function
	CLASS.cloneObj = function(obj)
	{
		var clone = {};
		for(var k in obj)
			clone[k] = obj[k];
		return clone;	
	}
	// Convert Cold Fusion Serialized JSON
	CLASS.convertColdFusionJSON = function(serialObj){
		var s = serialObj || {};
		if( !s.COLUMNS && !s.DATA )
			CLASS.console.error("CLASS.convertColdFusionJSON() >>  was not passed a coldfusion serialized object");
		
		// Create returned object
		var obj = [];
		// Loops through serialObj and matches the columns
		for(var i=0; i<s.DATA.length; i++)
		{
			var temp = {};
			for(var j=0; j<s.COLUMNS.length; j++)
			{
				temp[s.COLUMNS[j]] = s.DATA[i][j];
			}
			
			obj.push(temp);
		}
		
		// Return the objects
		return obj;
	}
	
	// Get Current Viewport
	CLASS.getViewportDimensions = function(){
		// Local vars
		var viewportwidth;
		var viewportheight;
		// the more standards compliant browsers (mozilla/netscape/opera/IE7) use window.innerWidth and window.innerHeight
		if (typeof window.innerWidth != 'undefined')
		{
		  viewportwidth = window.innerWidth,
		  viewportheight = window.innerHeight
		}
		// IE6 in standards compliant mode (i.e. with a valid doctype as the first line in the document)
		else if (typeof document.documentElement != 'undefined'
		 && typeof document.documentElement.clientWidth !=
		 'undefined' && document.documentElement.clientWidth != 0)
		{
		   viewportwidth = document.documentElement.clientWidth,
		   viewportheight = document.documentElement.clientHeight
		}
		// older versions of IE
		else
		{
		   viewportwidth = document.getElementsByTagName('body')[0].clientWidth,
		   viewportheight = document.getElementsByTagName('body')[0].clientHeight
		}
		
		return {x:viewportwidth,y:viewportheight};
	}
// PUBLIC Methods Below --------------------------------------------
// -----------------------------------------------------------------
	
	// Called by the developer on: onLoad()
	CLASS.initialize = function(config){ 		
		// Do we need to load parcels?
		if( typeof config != 'undefined' && typeof config.loadParcels != 'undefined' )
			CLASS.loadParcels = config.loadParcels;
		
		// Do we need to load simeplGeo?
		if( typeof config != 'undefined' && typeof config.loadSimpleGeo != 'undefined' )
			CLASS.loadSimpleGeo = config.loadSimpleGeo;
		
		// Do we need to load yelp?
		if( typeof config != 'undefined' && typeof config.loadOAuth != 'undefined' )
			CLASS.loadOAuth = config.loadOAuth;
		
		// Check for channel ID
		if( typeof config != 'undefined' && typeof config.channel != 'undefined' )
		{	
			CLASS.mapAPI += '&channel='+config.channel;
			CLASS.console.log('Requesting google map using Channel: '+config.channel);
		}
		
		// Load Script
		CLASS._loadScript();
		
		// Load map onto DOM
		CLASS._installMap(config);
	}
	
	// Singleton Object holds methods to add custom controls to the map.
	CLASS.customControl = {
		// Inserts a custom control into the map navigation
		// Param: { 
		//			content: <CLASS.customControlImages>|<string>, 
		//			position: <string> "TOP | TOP_LEFT | TOP_RIGHT | BOTTOM | BOTTOM_LEFT | BOTTOM_RIGHT | LEFT_TOP | RIGHT_TOP | LEFT_BOTTOM | RIGHT_BOTTOM | LEFT_CENTER | RIGHT_CENTER ",
		//			handler: <function> ,
		//			title: <string>
		//		  }
		insert : function(options){
			// Create new control node
			var newControl = CLASS.customControl._createElement();
			
			// Attach title to node
			if( typeof options.title!="undefined" )
				newControl.title = options.title;
			
			// Check type of content
			if( typeof options.content == "string" /*Text-only*/ )
			{
				// Apply extra css for text-only controls
				newControl = CLASS.customControl._applyTextCSS(newControl);
				// Apply content to node
				newControl.innerHTML = options.content;
			}
			else if( typeof options.content.src != "undefined" /*Custom Image*/)
			{
				// Using Private image
				// Apply content to node
				newControl.innerHTML = '<img src="'+options.content.src+'" />';
			}
			else
				CLASS.console.error("Content for custom control must be string or from CLASS.customControlImages collection");
				
			// Check for position integrity
			var list = "TOP | TOP_LEFT | TOP_RIGHT | BOTTOM | BOTTOM_LEFT | BOTTOM_RIGHT | LEFT_TOP | RIGHT_TOP | LEFT_BOTTOM | RIGHT_BOTTOM | LEFT_CENTER | RIGHT_CENTER";
			if( typeof options.position!="undefined" && list.indexOf(options.position)!=-1 )
			{	// We are good to go.				
				
				// Create a "click" listener and attach given handler
				if( typeof options.handler=="function" )
					google.maps.event.addDomListener(newControl,'click', options.handler );				
				
				// Push new custom control into our map				
				CLASS.map.controls[google.maps.ControlPosition[options.position]].push(newControl);
			}
			else
				CLASS.console.error("Position used for custom control must be one of:\n\n"+list);
				
			// Return the new DOM node in case developer wants to manipulate it further
			return newControl;
		},
		// Create a new node element for the control
		_createElement : function(){
			// Basic div node for the control
			var newEl = document.createElement('div');
			newEl.style.cursor = 'pointer';
			//newEl.style.height = '20px';
			newEl.style.marginTop = '5px';
			newEl.style.fontFamily = 'Arial,sans-serif';
			// Turn over the new node
			return newEl;
		},
		// Function will apply specific css attribute for text-only controls
		_applyTextCSS : function(node){
			node.style.backgroundColor = 'white';
			node.style.borderStyle = 'solid';
			node.style.borderWidth = '2px';
			node.style.paddingRight = '3px';
			node.style.paddingLeft = '3px';
			node.style.textAlign = 'left';
			node.style.fontSize = '11px';
			node.style.marginRight = '10px';
			// Given the node back
			return node;
		}
	}
	
	// Loads many pins onto the map given records and pinConfig options
	// pinConfig{latField, lngField, pinIcon, titleField, htmlField}
	CLASS.loadPins = function( records, pinConfig ){
	
		// Create loop for the records
		for( var i=0; i<records.length; i++ )
		{
			// Build marker config
			var markerConfig = {
					lat: records[i][pinConfig.latField], 
					lng: records[i][pinConfig.lngField], 
					title: records[i][pinConfig.titleField], 
					html_for_popup: records[i][pinConfig.htmlField], 
					pinIcon: pinConfig.pinIcon,
					defaultInfoWindow: ( typeof pinConfig.defaultInfoWindow != 'undefined' && pinConfig.defaultInfoWindow == true ),
					onClick : ('onClick' in pinConfig && typeof pinConfig.onClick=="function") ? pinConfig.onClick:null,
					showDirections : ('showDirections' in pinConfig && pinConfig.showDirections==false) ? false:true,
					hideInfoWindow : ( 'hideInfoWindow' in pinConfig && pinConfig.hideInfoWindow==true ) ? true:false
				};
			// Pass this record over to plotPin
			CLASS.plotPin(markerConfig);
		}
	
	}
	
	// Loads one pin onto the map.
	// Param: {lat, lng, title, html and pinIcon}
	CLASS.plotPin = function( pinConfig ){ 		
		// Short Hand fix: Now allowing "html" as possible field
		if( typeof pinConfig.html != 'undefined' ) pinConfig.html_for_popup = pinConfig.html;
		
		// Do we want popups?
		var showPopup = ( typeof pinConfig.html_for_popup != 'undefined' );
		// Are we using default popups
		// true: using default, false: using custom HAR infowindow (default)
		var googlePopup = ( typeof pinConfig.defaultInfoWindow != 'undefined' && pinConfig.defaultInfoWindow == true );
		// Do we want to show directions for this pin?
		var showDirections = ('showDirections' in pinConfig && pinConfig.showDirections==false) ? false:true;
		// Do we have a handler for this marker?
		var onClickHandler = ('onClick' in pinConfig && typeof pinConfig.onClick=="function") ? pinConfig.onClick:null;
		// Do we want to hide the google infoWindow? Mostly for Mobile Developement
		var hideInfoWindow = ( 'hideInfoWindow' in pinConfig && pinConfig.hideInfoWindow==true ) ? true:false;
		
		// Create a new marker
		var index = CLASS.markers.length;
		// Create pin configuration
		var finalConfig = {
			position: new google.maps.LatLng( pinConfig.lat, pinConfig.lng ), 
			map: CLASS.map,
			icon: pinConfig.pinIcon
		};
		
		// Check for title
		if( (typeof pinConfig.title != 'undefined' && googlePopup) || (typeof pinConfig.title != 'undefined' && !showPopup))
			finalConfig.title = pinConfig.title;
		// Check for html
		if( showPopup && !hideInfoWindow)
			finalConfig.html_for_popup = pinConfig.html_for_popup;
		
		// Create the marker
		CLASS.markers[index] = new google.maps.Marker( finalConfig );
		
		// Whether to show a html popup and create a listener
		if(showPopup)
		{
			// Get position for infowindow
			var windex = CLASS.infoWindows.length;
			
			if( googlePopup )
			{
				// Create marker's InfoWindow
				CLASS.infoWindows[windex] = new google.maps.InfoWindow({
					content: finalConfig.html_for_popup
				});
				
				// Create listener for this marker
				google.maps.event.addListener( CLASS.markers[index], 'click', function(){
					// Close any possibly open windows
					CLASS.closeWindows();				
					
					// Open this specific window
					CLASS.infoWindows[windex].open( CLASS.map, CLASS.markers[index] );
					
					// Call provided callback for onclick
					if(onClickHandler) onClickHandler(CLASS.markers[index], pinConfig.html_for_popup );
				});
			}
			else
			{
				// CUSTOM HAR INFOWINDOW
				// (Default InfoWindow)	
				CLASS.infoWindows[windex] = {};
				CLASS.infoWindows[windex].custom = true;
				CLASS.infoWindows[windex].content = finalConfig.html_for_popup;
				CLASS.infoWindows[windex].position = finalConfig.position;
				
				// Check to see if user wants to hide the infowindow
				// usually when you only want to use marker callback
				if(!hideInfoWindow)
				{
					// Create listener for this marker
					google.maps.event.addListener( CLASS.markers[index], 'mouseover', function(){
						// Open this specific window
						CLASS.customInfoWindow.fillShow(finalConfig.html_for_popup, finalConfig.position, showDirections);
					});
					// Create listener for this marker
					google.maps.event.addListener( CLASS.markers[index], 'mouseout', function(){
						// Set a 1 second delay to close the window
						// If user pans over the infowindow we will cancel this delay
						CLASS.customInfoWindow.closeDelay = setTimeout(CLASS.customInfoWindow.close,1000);
									
					});
				}
				// Call provided callback for onclick
				if(onClickHandler)
				{
					// Create listener for this marker
					google.maps.event.addListener( CLASS.markers[index], 'click', function(){
						// Call provided callback for onclick
						onClickHandler(CLASS.markers[index], pinConfig.html_for_popup );
					});
				}
			}
		}
		
		// Check to see if developer needs the markerID back
		if( typeof pinConfig.getId != 'undefined' && pinConfig.getId==true ) return index;
		
	}	
	
	// Public Method: Returns whether or not directions are currently rendered on the map
	CLASS.hasDirectionsRendered = function(){
		// Check variables to see if directions are rendered on map
		return (CLASS.directionsRendered!=null && CLASS.directionsRendered.getMap()!=null)
	}
	
	// Returns a DOM element that will render direction to and from a marker location.
	// Params: Marker Location (lat/lng or address) <string>
	CLASS.getDirectionsWrapperElement = function(markerLocation){
		// IDs for certain Elements
		var toHereWrapperID = "HAR_drivingDirections_toHereWrapper";
		var fromHereWrapperID = "HAR_drivingDirections_fromHereWrapper";
		var buttonWrapperID = "HAR_drivingDirections_buttonWrapper";
		var menuWrapperID = "HAR_drivingDirections_menuWrapper";
		var toTxtID = "HAR_drivingDirections_toTxt";
		var fromTxtID = "HAR_drivingDirections_fromTxt";
		var toTxtID2 = "HAR_drivingDirections_toTxt2";
		var fromTxtID2 = "HAR_drivingDirections_fromTxt2";
		
		// Create DOM wrapper for elements
		var wrapper = document.createElement("div");
		// Create Menu Navigation
		var menu = document.createElement("div");
		menu.innerHTML = "<br/>Directions: <a href='#' onclick='javascript:document.getElementById(\""+fromHereWrapperID+"\").style.display=\"none\";document.getElementById(\""+toHereWrapperID+"\").style.display=\"block\";document.getElementById(\""+buttonWrapperID+"\").style.display=\"block\";return false;'>To Here</a>";
		menu.innerHTML += " <a href='#' onclick='javascript:document.getElementById(\""+toHereWrapperID+"\").style.display=\"none\";document.getElementById(\""+fromHereWrapperID+"\").style.display=\"block\";document.getElementById(\""+buttonWrapperID+"\").style.display=\"block\";return false;'>From Here</a>";
		menu.id = menuWrapperID;
		// Create hidden "To This Location"
		var toHere = document.createElement("div");
		toHere.style.display = "none";
		toHere.id = toHereWrapperID;
		// Create hidden "To This Location"
		var fromHere = document.createElement("div");
		fromHere.style.display = "none";
		fromHere.id = fromHereWrapperID;
		// Create hidden "Get Directions" buttons
		var buttonTrigger 					= document.createElement("input"); 
		buttonTrigger.style.display 		= "none";
		buttonTrigger.style.borderWidth		= '2px';
		buttonTrigger.style.borderStyle		= 'solid';
		buttonTrigger.style.borderColor		= '#ccdddd';
		buttonTrigger.style.color			= '#FFFFFF';
		buttonTrigger.style.fontSize 		= '11px';
		buttonTrigger.style.fontFamily 		= 'Arial';
		buttonTrigger.style.padding 		= '3px';
		buttonTrigger.style.backgroundColor = '#006699';
		buttonTrigger.style.cursor			= 'pointer';
		buttonTrigger.value 				= "Show Directions";
		buttonTrigger.type 					= "submit";
		buttonTrigger.id 					= buttonWrapperID;
		// Create handler for button press
		google.maps.event.addDomListener( buttonTrigger, "click", function(){ 
			// Make sure that both text boxes have values
			if( document.getElementById(toTxtID).value!="" && document.getElementById(fromTxtID).value!="" )
			{
				CLASS.showDirections( document.getElementById(toTxtID).value, document.getElementById(fromTxtID).value ); 
			}
			else if( document.getElementById(toTxtID2).value!="" && document.getElementById(fromTxtID2).value!="" )
			{
				CLASS.showDirections( document.getElementById(toTxtID2).value, document.getElementById(fromTxtID2).value ); 
			}
		});
		
		
		// Add all the element nodes into the wrapper
		wrapper.appendChild( menu );
		wrapper.appendChild( toHere );
		wrapper.appendChild( fromHere );
		wrapper.appendChild( buttonTrigger );
		
		// Add content to the "To Here" container
		toHere.innerHTML = "<strong>To:</strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em>To This Location</em><input type='hidden' value='"+markerLocation+"' id='"+toTxtID+"'/><br><strong>From:</strong> <input id='"+fromTxtID+"' value='' type='text'/>";
		// Add content to the "From Here" container
		fromHere.innerHTML = "<strong>To:</strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input id='"+toTxtID2+"' value='' type='text'/><br><strong>From:</strong> <em>From This Location</em><input id='"+fromTxtID2+"' value='"+markerLocation+"' type='hidden'/>";		
		
		// Return all these contents
		return wrapper;
	}
	
	// callBack: ( found: <bool>, location: {lat:<float>,lng:<float>} ) 
	// Params: Address for which to geocode 
	// Note: Only RoofTop location is returned if found 
	CLASS.getRoofTop = function(searchAddress, callBack){ 
		var foundRoofTop= false;
		var foundLocation = {lat:0,lng:0};
		var geoStart 	= new google.maps.Geocoder();
		var geoRequest 	= { address: searchAddress };
		geoStart.geocode( geoRequest, function(geoResult, geoStatus){
			
			// Check the geo status to make sure our request sent through
			if( geoStatus == google.maps.GeocoderStatus.OK )
			{
				// Loop through the results
				for( var i=0; i<geoResult.length; i++ )
				{
					// We are only interested in the rooftop location
					if( geoResult[i].geometry.location_type == google.maps.GeocoderLocationType.ROOFTOP )
					{
						// We have our proper location
						foundRoofTop = true;
						foundLocation.lat = geoResult[i].geometry.location.lat();
						foundLocation.lng = geoResult[i].geometry.location.lng();
						
						// Gather more information about this location
						// Loop through all possible standard addresses
						var addList = geoResult[i].address_components;
						for(var a=0; a < addList.length; a++)
						{
							// Depending on the type of component we will build differently
							switch( addList[a].types[0] )
							{
								case "street_number":
									foundLocation.streetNbr = addList[a].short_name;
									break;
								case "route":
									foundLocation.street = addList[a].short_name;
									break;
								case "postal_code":
									foundLocation.zip = addList[a].short_name;
									break;
								case "locality":
									foundLocation.city = addList[a].short_name;
									break;
								case "administrative_area_level_2":
									foundLocation.county = addList[a].short_name;
									break;
								case "administrative_area_level_1":
									foundLocation.state = addList[a].short_name;
									break;
							}// END - Switch
						}// END - FOR loop through all address components
						
						// Break out of the loop
						break;
						
					}// end if
				}// end for
			}// end if OK
			
			// Return findings
			callBack( foundRoofTop, foundLocation );
		});	
	}
	
	// callBack: ( [{TYPE:,LAT:,LNG:,result:}] )
	// Params: Address for which to geocode
	CLASS.geoCode = function(searchAddress, callBack, opts){ 
		// Error checking
		var opts = opts || {};
		var collection = [];
		var foundLocation = {lat:0,lng:0};
		var geoStart 	= new google.maps.Geocoder();
		var geoRequest 	= { address: searchAddress };
		
		// Add any extra request options
		opts.request = opts.request || {};
		for( var key in opts.request )
			geoRequest[key] = opts.request[key];
		
		// Do the geocoding
		geoStart.geocode( geoRequest, function(geoResult, geoStatus){
			
			// Check the geo status to make sure our request sent through
			if( geoStatus == google.maps.GeocoderStatus.OK )
			{
				// Loop through the results
				for( var i=0; i<geoResult.length; i++ )
				{
					var obj = {};
					obj.type = geoResult[i].geometry.location_type;
					obj.lat = geoResult[i].geometry.location.lat();
					obj.lng = geoResult[i].geometry.location.lng();
					obj.result = geoResult[i];
					collection.push(obj);
				}// end for
			}// end if OK
			
			// Return findings
			callBack( collection );
		});	
	}
	
	// Renders: Directions on the map
	// callBack( Array[n].{instruction:string, distance:string, duration: string} )
	// Params: To, From (either <string> to be geocoded OR google.maps.LatLng Object)
	CLASS.showDirections = function(to,from,callback,rendertomap){
		// Check for optional param
		if( typeof rendertomap == 'undefined' )
			rendertomap = true;
		
		// Make the service request
		var dirRequest = new google.maps.DirectionsService();
		dirRequest.route({
				// Directions Request Object
				destination: to,
				origin: from,
				travelMode: google.maps.DirectionsTravelMode.DRIVING
				
		},function(dirResults, dirStatus){ 
			// Process the service response	status
			if( dirStatus!="OK" )
				alert('Directions request did not go through. One of the addresses might be invalid.');
			else
			{
				// Render Directions on map
				if( rendertomap )
				{
					// Clear Map of all shapes/markers/previous directions
					CLASS.hideMarkers();
					CLASS.hideShapes();
					CLASS.clearDrivingDirections();
					CLASS.customInfoWindow.close();
					
					// Push custom control into map.
					// Custom control lets user hide driving directions from map
					CLASS.customControl.insert({
						content: CLASS.customControlImages.clearDrivingDirections,
						position: "TOP_RIGHT",
						title: "Click here to clear driving directions from the map.",
						handler: function(){ 
							// Clear driving directions
							CLASS.clearDrivingDirections();
							// Show markers again 
							CLASS.showMarkers(); 
							// Show shapes again 
							CLASS.showShapes(); 
							// Remove custom control from map
							CLASS.map.controls[google.maps.ControlPosition["TOP_RIGHT"]].pop();
						}
					});
					// Finally render to map
					CLASS.directionsRendered = new google.maps.DirectionsRenderer({map:CLASS.map,directions:dirResults});
				}
				// CLose any open custom window
				CLASS.customInfoWindow.close();
				// Is there a callback
				if( typeof callback == 'function' )
				{
					// Build a list of instructions, incase they want to print the directions on the DOM
					var instructions = Array();
					for( var i = 0; i < dirResults.routes[0].legs.length; i++ )
					{
						for( var j = 0; j < dirResults.routes[0].legs[i].steps.length; j++ )
						{
							instructions.push({
								instruction:dirResults.routes[0].legs[i].steps[j].instructions,
								distance:dirResults.routes[0].legs[i].steps[j].distance.text,
								duration:dirResults.routes[0].legs[i].steps[j].duration.text
							});
						}
					}
					// Send allway
					callback(instructions);
				}
			}
		});
	}
	
	// Shows the infowindow of given markerID
	// Assume: All markers have InfoWindows (using parallel arrays)
	CLASS.showWindow = function(markerIndex, opts){ 
		// Check for param
		var opts = opts || {};
		opts.showDirections = ("showDirections" in opts) ? opts.showDirections:true;
		
		// Make sure that the window exists before attempting to open it.
		if( typeof CLASS.infoWindows[markerIndex]!='undefined' )
		{
			if( typeof CLASS.infoWindows[markerIndex].custom!='undefined' && CLASS.infoWindows[markerIndex].custom==true )
				CLASS.customInfoWindow.fillShow( CLASS.infoWindows[markerIndex].content, CLASS.infoWindows[markerIndex].position, opts.showDirections );
			else
				CLASS.infoWindows[markerIndex].open(CLASS.map, CLASS.markers[markerIndex]);
		}
			
	}
	// Closes the infowindow of given markerID
	// Assume: All markers have InfoWindows (using parallel arrays)
	CLASS.closeWindow = function(markerIndex){ 
		// Make sure that the window exists before attempting to open it.
		if( typeof CLASS.infoWindows[markerIndex]!='undefined' )
		{
			if( typeof CLASS.infoWindows[markerIndex].custom!='undefined' && CLASS.infoWindows[markerIndex].custom==true )
				CLASS.customInfoWindow.close();
			else
				CLASS.infoWindows[markerIndex].close();
		}
	}
	
	// Draws a polygon on the map given an array of MVC
	CLASS.drawPolyFromMVCArray = function(MVCArrayList, /*fitBounds*/ options){ 
		// Check for new options parameter or old fitbounds param
		if( typeof options == 'boolean' )
		{
			options = {
					fitBounds : options,
					polyControls : {}
				};
		}
		else if( typeof options=="undefined" )
			var options = {};
			
		// Defaults	
		options = {
					fitBounds : options.fitBounds || false,
					polyControls : options.polyControls || {},
					polyConfig : options.polyConfig || {}
				};	
		// Config options for the polygon
		var polyOptions = CLASS.cloneObj(CLASS.defaultPolyOptions);
		// Copy over anything sent in polyConfig
		for( var key in options.polyConfig )
			polyOptions[key] = options.polyConfig[key];
		
		polyOptions.paths = MVCArrayList;
		polyOptions.map = CLASS.map;
		options.polyControls.shapeIndex = CLASS.shapes.length;
		// Poly on map
		CLASS.shapes[options.polyControls.shapeIndex] = new google.maps.Polygon(polyOptions);
		CLASS.shapes[options.polyControls.shapeIndex].controls = new CLASS.addPolyControls(options.polyControls);
		// Assign this poly it's tags
		if( polyOptions.tags )
		{ CLASS.shapes[options.polyControls.shapeIndex].tags = CLASS.bitList.getTagsID(polyOptions.tags); }
		
		// Center and Zoom the Map
		if( typeof options != 'undefined' && typeof options.fitBounds != 'undefined' && options.fitBounds )
			CLASS.map.fitBounds( CLASS.getBoundsFromMVCArray(MVCArrayList) );
		
		// Return the shape
		return CLASS.shapes[options.polyControls.shapeIndex];
	}
	
	// Method adds "poly controls" to a polygon given its paths
	CLASS.addPolyControls = function(options){
		// Log activity
		CLASS.console.log("CLASS.addPolyControls() >> Entered the class.");
		
		// Create default options
		var controls = {
				zoomListener: null,
				shapeIndex: options.shapeIndex, // will except undefined or actual number.
				northernPoint: null,
				controlPosition : options.controlPosition || 'northern', // [ northern, center ]
				// Button Usage
				showClose: options.showClose || false,
				showMove: options.showMove || false,
				showRefresh: options.showRefresh || false,
				showInfo: options.showInfo || false,
				// Button Titles
				closeTitle: options.closeTitle || 'Click to close this Polygon',
				moveTitle: options.moveTitle || 'Drag and drop to move Polygon',
				refreshTitle: options.refreshTitle || 'Click to refresh Polygon',
				infoTitle: options.infoTitle || 'Click for more information',
				infoContent: options.infoContent || false,
				infoContentShowDirections: options.infoContentShowDirections || false,
				// Button Handlers
				onClose: options.onClose || function(){},
				onMoveStart: options.onMoveStart || function(){},
				onMoveEnd: options.onMoveEnd || function(){},
				onRefresh: options.onRefresh || function(){},
				onInfo: options.onInfo || function(){},
				// Actual markers, which will be created later
				closeButton: null,
				moveButton: null,
				refreshButton: null,
				infoButton: null
		};
		// END - default options
		
		// Log what we are working with
		// CLASS.console.debugObj(controls, "PolyControls");
		
		// Check to see if we should proceed
		if( isNaN(controls.shapeIndex) )
		{
			CLASS.console.error("CLASS.addPolyControls() >> options object does not contain shapeIndex.");
			return controls;
		}
			
		// This method sets the paths attribute and calcs the northern point
		controls.calcPaths = function(){
			// Get the paths from the shape
			controls.paths = CLASS.shapes[controls.shapeIndex].getPaths().getAt(0);
			
			// Calculate the center of polyControls
			// Note: variavle northernPoint is being used for center of polycontrols
			if( controls.controlPosition == 'center' )
			{
				var bounds = CLASS.getBoundsFromMVCArray( controls.paths );
				controls.northernPoint = bounds.getCenter();
			}
			// Calculate the Northern most point
			else
			{
				// Calculate northen point
				controls.paths.forEach(function(el,index){
						if( !controls.northernPoint || el.lat() > controls.northernPoint.lat() )
							controls.northernPoint = el;	
				});	
			}
			
			
		}
		// Calc right away
		controls.calcPaths();
		
		// Log northern point
		if(controls.northernPoint) 
			CLASS.console.log("CLASS.addPolyControls() >> Northern Point of Shape: "+controls.northernPoint.toUrlValue(5));
		else
			CLASS.console.warn("CLASS.addPolyControls() >> There is no calculated Northern Point");
		
		
		// This method calculates where the controls will be located on the map
		controls.plotControls = function(){
			// Every control will be shifted 23 pixels on the X axis, so that
			// they can align next to each other
			var buttonShift = -11.5;
			buttonShift += (controls.showMove) ? 11.5:0; 
			buttonShift += (controls.showClose) ? 11.5:0; 
			buttonShift += (controls.showRefresh) ? 11.5:0;
			buttonShift += (controls.showInfo) ? 11.5:0;
			
			// -------------------- Map Controls Below ---------------------------------
			// Create the Close Control
			if( controls.showClose )
			{
				// Rendering again
				if( controls.closeButton )
				{
					if( controls.closeButton.getMap()==null ) controls.closeButton.setMap(CLASS.map);
					controls.closeButton.setPosition( CLASS.addPixelsToLatLng( controls.northernPoint, {y:10,x:buttonShift} ) );
				}
				// First time rendered
				else
				{
					// New marker
					controls.closeButton = new google.maps.Marker({
						position: CLASS.addPixelsToLatLng( controls.northernPoint, {y:10,x:buttonShift} ),
						icon: CLASS.pins.closePoly,
						title: controls.closeTitle,
						map: CLASS.map
					});						
					// Create click listener
					google.maps.event.addListener( controls.closeButton, 'click', function(e){
							// Deallocate all control markers
							controls.close();
							// Deallocate this polygon
							if( !isNaN(controls.shapeIndex) )CLASS.shapes[controls.shapeIndex].setMap(null);
							// Call the developers onClose callback, for further actions
							controls.onClose(e);
						});
				}
				// Add shift for next button to come along
				buttonShift -= 23;
			}// END - Close Control
			
			// Create the Move control
			if( controls.showMove )
			{
				// Render close button
				if( controls.moveButton )
				{
					if( controls.moveButton.getMap()==null ) controls.moveButton.setMap(CLASS.map);
					controls.moveButton.setPosition( CLASS.addPixelsToLatLng( controls.northernPoint, {y:10,x:buttonShift} ) );
				}
				else
				{
					// Create new marker
					controls.moveButton = new google.maps.Marker({
						position: CLASS.addPixelsToLatLng( controls.northernPoint, {y:10,x:buttonShift} ),
						icon: CLASS.pins.movePoly,
						title: controls.moveTitle,
						map: CLASS.map,
						// Only for the move marker
						draggable: true,
						raiseOnDrag: false,
						cursor: 'move'
					});	
					controls.moveMarkerLocation = controls.moveButton.getPosition();
					// MOVE START
					google.maps.event.addListener( controls.moveButton, 'dragstart', function(markerLocation){
						// Copy the markers locaiton
						controls.moveMarkerLocation = markerLocation.latLng;
						// Call the developers callback
						controls.onMoveStart(markerLocation);
					});
					// MOVING
					google.maps.event.addListener( controls.moveButton, 'drag', function(newLoc){
						// get position difference
						var latDiff = controls.moveMarkerLocation.lat()-newLoc.latLng.lat();
						var lngDiff = controls.moveMarkerLocation.lng()-newLoc.latLng.lng();
						var newPolyPoints = [];
						controls.northernPoint = null;
						// recalc all the points inthe poly
						controls.paths.forEach(function(el,ind){
								// new latLng
								var newLatLng = new google.maps.LatLng( el.lat()-latDiff, el.lng()-lngDiff );
								// Check for Northern Point
								if( !controls.northernPoint || newLatLng.lat() > controls.northernPoint.lat() )
									controls.northernPoint = newLatLng;
								newPolyPoints.push(newLatLng);
							});
						// replace the poly's points with new points
						CLASS.shapes[controls.shapeIndex].setPaths(newPolyPoints);
						// plot new controls
						CLASS.shapes[controls.shapeIndex].controls.plotControls();
					});
					// MOVE END
					google.maps.event.addListener( controls.moveButton, 'dragend', function(endLocation){
						// Get the shape paths and recalc northern point
						controls.calcPaths();
						// Call the developer's function
						controls.onMoveEnd({
								location: endLocation,
								shape: CLASS.shapes[controls.shapeIndex],
								paths: controls.paths
							});
					});
				}
				// Add shift for next button to come along
				buttonShift -= 23;
			}// END - Move Control
			
			// Create the Refresh control
			if( controls.showRefresh )
			{
				// Render again
				if( controls.refreshButton )
				{
					if( controls.refreshButton.getMap()==null ) controls.refreshButton.setMap(CLASS.map);
					controls.refreshButton.setPosition( CLASS.addPixelsToLatLng( controls.northernPoint, {y:10,x:buttonShift} ) );
				}
				// First Time rendering
				else
				{
					// Create new marker
					controls.refreshButton = new google.maps.Marker({
						position: CLASS.addPixelsToLatLng( controls.northernPoint, {y:10,x:buttonShift} ),
						icon: CLASS.pins.refreshPoly,
						title: controls.refreshTitle,
						map: CLASS.map
					});
					// Create click listener
					google.maps.event.addListener( controls.refreshButton, 'click', function(e){ 
						// Send back useful information
						controls.onRefresh({
								event: e,
								shape: CLASS.shapes[controls.shapeIndex],
								paths: controls.paths
							});
					});
				}
				// Add shift for next button to come along
				buttonShift -= 23;	
			}// END - Refresh Control	
			
			// Create the Info control
			if( controls.showInfo )
			{
				// Render again
				if( controls.infoButton )
				{
					
					if( controls.infoButton.getMap()===null ){ controls.infoButton.setMap(CLASS.map); }
					controls.infoButton.setPosition( CLASS.addPixelsToLatLng( controls.northernPoint, {y:10,x:buttonShift} ) );
				}
				// First Time rendering
				else
				{
					// Create new marker
					controls.infoButton = new google.maps.Marker({
						position: CLASS.addPixelsToLatLng( controls.northernPoint, {y:10,x:buttonShift} ),
						icon: CLASS.pins.infoPoly,
						title: controls.infoTitle,
						map: CLASS.map
					});
					// Create click listener
					google.maps.event.addListener( controls.infoButton, 'click', function(e){
						// Send back useful information
						controls.onInfo({
								event: e,
								shape: CLASS.shapes[controls.shapeIndex],
								paths: controls.paths
							});
					});
					// Check if we want a popup on infoButton
					if( controls.infoContent )
					{
						// Create listener for this marker
						google.maps.event.addListener( controls.infoButton, 'mouseover', function(e){ 
							CLASS.customInfoWindow.fillShow( controls.infoContent, controls.infoButton.getPosition(), controls.infoContentShowDirections );
						});
						google.maps.event.addListener( controls.infoButton, 'mouseout', function(e){
							// Set a 1 second delay to close the window
							// If user pans over the infowindow we will cancel this delay
							CLASS.customInfoWindow.closeDelay = setTimeout(CLASS.customInfoWindow.close,1000);		
						});
							
					}
						
				}
				// Add shift for next button to come along
				buttonShift -= 23;	
			}// END - Info Control	
			// -------------------- END - Map Controls ---------------------------------
			
			
			// Register zoom listener if necessary
			// Add listener to recalculate the shift on map zoom
			if( controls.zoomListener==null )
				controls.zoomListener = google.maps.event.addListener( CLASS.map, 'zoom_changed', controls.plotControls );
			
		}// END - function plotControls()
		
		// Close Controls
		controls.close = function(){
			if( controls.closeButton ) controls.closeButton.setMap(null);
			if( controls.moveButton ) controls.moveButton.setMap(null);
			if( controls.refreshButton ) controls.refreshButton.setMap(null);
			if( controls.infoButton ) controls.infoButton.setMap(null);
			// Remove zoom listener
			if(controls.zoomListener) google.maps.event.removeListener( controls.zoomListener );
			controls.zoomListener = null;
		}// END - function close()
		
		// --------------------------------
		// Plot controls real quick
		controls.plotControls();
		
		// return this instance to the shape asking for controls
		return controls;
		
	}// END - CLASS.addPolyControls()
	
	// Returns a LatLngBounds Object given an MVCArray of LatLng
	CLASS.getBoundsFromMVCArray = function(MVCArrayList){ 
		// Vars will hold our extreme lat/lng values
		var maxLat = 0;
		var minLat = 100000; // Non-existant extremely high Latitude
		var maxLng = -100000;// Non-existant extremely low Longitude
		var minLng = 0;
		
		// Iterate through the MVC Array
		MVCArrayList.forEach(function(el,index){
			// Check each Lat and Long to possible Extremes
			if( el.lat() < minLat )
				minLat = el.lat()
			if( el.lat() > maxLat )
				maxLat = el.lat()
			if( el.lng() > maxLng )
				maxLng = el.lng()
			if( el.lng() < minLng )
				minLng = el.lng()
		});
		
		// Build our NE/SW corners
		var sw = new google.maps.LatLng( minLat, minLng );
		var ne = new google.maps.LatLng( maxLat, maxLng );
		
		// Return a LatLngBounds Obj
		return new google.maps.LatLngBounds( sw, ne );
		
	}
	
	// Fit map to current visible markers
	CLASS.fitBoundsToMarkers = function(){ 
		// Vars will hold our extreme lat/lng values
		var maxLat = 0;
		var minLat = 100000; // Non-existant extremely high Latitude
		var maxLng = -100000;// Non-existant extremely low Longitude
		var minLng = 0;
		
		if (CLASS.markers.length > 0) 
		{
			for(var i=0; i<CLASS.markers.length; i++)
			{
				var tmpPos = CLASS.markers[i].getPosition();
				var lat = tmpPos.lat();
				var lng = tmpPos.lng();
				// Check each Lat and Long to possible Extremes
				if( lat < minLat )
					minLat = lat;
				if( lat > maxLat )
					maxLat = lat;
				if( lng > maxLng )
					maxLng = lng;
				if( lng < minLng )
					minLng = lng;
			}
			// Build our NE/SW corners
			var sw = new google.maps.LatLng( minLat, minLng );
			var ne = new google.maps.LatLng( maxLat, maxLng );
			
			// Send bounds to the map
			CLASS.map.fitBounds(new google.maps.LatLngBounds( sw, ne ));
		}
	}
	
	// Util function for a unique identifier
	CLASS.getUID = function(){
    	return (new Date).getTime();
	}
	
	// close any possibly open windows
	CLASS.closeWindows = function(){ 
	
	  if (CLASS.infoWindows.length > 0) 
	  {
		for(var i=0; i<CLASS.infoWindows.length; i++)
		{
			if( typeof CLASS.infoWindows[i].custom!='undefined' && CLASS.infoWindows[i].custom==true )
				CLASS.customInfoWindow.close();
			else
				CLASS.infoWindows[i].close();
		}
	  }
	}
	// Gets all the available shapes in a list of tags
	CLASS.getShapesByTags = function(opts){
		// Check for param, set defaults
	 	var opts = opts || {};
	  	opts.tags = opts.tags || [];
		opts.match = opts.match || "matchAny";
		
		// Quick Match Method check
		if( !CLASS.bitList[opts.match] )
		{ CLASS.console.warn("CLASS.getShapesByTags() >> Match method provided does not exist: "+opts.match); return []; }
		
		// Array that will be returned
		var keepers = [];
		// Check to make sure an array was passed
		if( opts.tags.length===0 )
		{ CLASS.console.warn("CLASS.getShapesByTags() >> Tag list is empty."); return []; }
		// Compare groupIDs with passed array of labels
		for( var i = 0; i<CLASS.shapes.length; i++ )
		{
			var shape = CLASS.shapes[i];
			// Check to see if shape has any tags associates with it.	
			if( shape.tags )
			{
				// Only return if the tags of this shape match accordingly with provided tags
				if( CLASS.bitList[opts.match]( shape.tags, opts.tags ) )
				{
					keepers.push(shape);
				}
			}
		}
		// Return matching shapes
		return keepers;
	}
	// Hide (not delete) all shapes on the map
	CLASS.hideShapes = function(opts){ 
	  // Get list of shapes to hide
	  var shapesList = (opts) ? CLASS.getShapesByTags(opts):CLASS.shapes;
	  
	  // If there are elements in the List
	  if (shapesList.length > 0) 
	  {
		for(var i=0; i<shapesList.length; i++)
		{
			// Bug fix, for when people are about to start drawing and they hit clear map
			if(shapesList[i] && typeof shapesList[i].getMap!="undefined")
			{
				// Hide the map
				shapesList[i].setMap(null);
				
				/* Now, Close Map Conrols if exist */
				if( typeof shapesList[i].controls!="undefined" && typeof shapesList[i].controls.close!="undefined" )
				{ shapesList[i].controls.close(); }
			}
		}
	  }
	}
	
	// Show all shapes
	CLASS.showShapes = function(opts)
	{
		// Get list of shapes to show
	  	var shapesList = (opts) ? CLASS.getShapesByTags(opts):CLASS.shapes;
		
		// Check to see if shapes exists
		for(var i=0; i<shapesList.length; i++)
		{
			// Show the map
			shapesList[i].setMap(CLASS.map);
			
			/* Now, Show Map Conrols is exist */
			if( typeof shapesList[i].controls!="undefined" && typeof shapesList[i].controls.plotControls!="undefined" )
			{ shapesList[i].controls.plotControls(); }
		}
	}
	
	// Returns the current number of shapes on the map
	CLASS.visibleShapes = function(opts)
	{
		// Get list of shapes to check
	  	var shapesList = (opts) ? CLASS.getShapesByTags(opts):CLASS.shapes;
		
		// variable to count number of visible shapes
		var count = 0;
		// iterate through shape objects
		for(var i=0; i<shapesList.length; i++)
		{
			// If shape's map is NOT null, then it is considered visible
			count += (shapesList[i] && typeof shapesList[i].getMap!="undefined" && shapesList[i].getMap()!=null) ? 1:0;
		}
		// return the number of visible shapes on the map
		return count;
	}
	
	// Hide (not delete) all icons on the map
	CLASS.hideMarkers = function()
	{   
	  if (CLASS.markers.length > 0) 
	  {
		for(var i=0; i<CLASS.markers.length; i++)
			CLASS.markers[i].setMap(null);
	  }
	}
	
	// Show all markers
	CLASS.showMarkers = function()
	{
		// Check to see if marker exists
		for(var i=0; i<CLASS.markers.length; i++)
			CLASS.markers[i].setMap(CLASS.map);
	}
	
	// Hide only one marker, marker id passed through param
	CLASS.hideMarker = function(id)
	{
		// Check to see if marker exists
		if( typeof CLASS.markers[id]!="undefined" )
			CLASS.markers[id].setMap(null);
	}
	
	// Show only one marker, marker id passed through param
	CLASS.showMarker = function(id)
	{
		// Check to see if marker exists
		if( typeof CLASS.markers[id]!="undefined" )
			CLASS.markers[id].setMap(CLASS.map);
	}
	
	// Deletes all icons on the map
	CLASS.deleteMarkers = function(){ 
	  
	  if (CLASS.markers.length > 0) 
	  {
		CLASS.hideMarkers();  
		CLASS.markers.length = 0;
		CLASS.infoWindows.length = 0;
	  }
	}
	
	// Deletes all shapes on the map
	CLASS.deleteShapes = function(){ 
	  
		if (CLASS.shapes.length > 0) 
		{
			for(var i=0; i<CLASS.shapes.length; i++)
			{
				// Bug fix, for when people are about to start drawing and they hit clear map
				if(CLASS.shapes[i] && typeof CLASS.shapes[i].getMap!="undefined")
				{
					// Make shape go bye-bye
					CLASS.shapes[i].setMap(null);
					
					/* Check for map controls */
					if( typeof CLASS.shapes[i].controls!="undefined" && typeof CLASS.shapes[i].controls.close!="undefined" )
						CLASS.shapes[i].controls.close();
				}
			}
			  
			CLASS.shapes.length = 0;
		}
	}
	
	// Deletes all possible overlays from the map
	CLASS.clearMap = function(){
		// Clear all markers
		CLASS.deleteMarkers();
		// Clear all shapes
		CLASS.deleteShapes();		
	}
	// Deletes the previous driving directiosn
	CLASS.clearDrivingDirections = function(){
		// Clear possible Map directions
		if( CLASS.directionsRendered != null )
			CLASS.directionsRendered.setMap(null);
	}
	
	// Plots a pin given a zip code
	CLASS.plotPinByZip = function(zip, callback)
	{
		// Data Check
		if( typeof zip=="undefined" || isNaN(zip) )
		{
			alert("Zipcode provided is not valid.");
			return;
		}
		
		// Finds the Zip Code and re-centers the map.
		var geo = new google.maps.Geocoder();
		geo.geocode( {address:zip}, function(resultsArray, status){
						var res = resultsArray[0];
						
						// Make sure status returned without problems
						if( status = google.maps.GeocoderStatus.OK )
						{
							// Change Icon SetTitle
							var info_str = Array();
							for(var key in res.address_components) 
								info_str.push(res.address_components[key].long_name);
							// Trim the last 
							
							var loc = res.geometry.location;
							CLASS.plotPin({
									lat: loc.lat(),
									lng: loc.lng(),
									pinIcon: CLASS.pins.greenArrow,
									title: info_str.toString()
								});
							// Center/Zoom the map
							CLASS.map.setCenter( new google.maps.LatLng( loc.lat(),loc.lng() ) );
							CLASS.map.setZoom(13);
							
							// Callback developers method
							if( typeof callback == "function" )
								callback(resultsArray, status);
							
						}else
						{
							alert("Zipcode could not be found.");	
						}
				  });	
	}
	
	// Returns a Pixel Object (Google) is the x,y pixel coordinates that match a LatLng
	CLASS.latLngToPixel = function(latLng, callback)
	{
		if( CLASS.mapRenderedInContainer )
		{	
			try
			{		
				// Call the developer's callback function
				if( typeof callback == "function" )
					callback( CLASS.canvassOverlayView.getProjection().fromLatLngToContainerPixel( latLng ) );
			}catch(e){}
		}
		else
		{
			// We need to wait a little bit more
			setTimeout(function(){ CLASS.latLngToPixel(latLng, callback); },100);
		}
	}
	
	// Object used for drawing a polygon on the map
	CLASS.startDrawingPolygon = {
		index : null,
		pathsCollection: null,
		maxPoints:0, // 0 is unlimited
		callback: null,
		polyControls: null,
		centerPoly: true,
		mouseClick: null,
		mouseDoubleClick: null,
		mouseRightClick: null,
		mouseMove: null,
		mouseToCornerDifference: {x:1,y:1},
		// init function starts the drawing activity and event handlers
		init: function(config){
			// Capture new index
			CLASS.startDrawingPolygon.index = CLASS.shapes.length;
			// Create the shape index for our new polygon
			CLASS.shapes[CLASS.startDrawingPolygon.index] = null;
			
			// Initialize the pathsCollection
			CLASS.startDrawingPolygon.pathsCollection = new google.maps.MVCArray([]);
						
			// Start Event Handlers 
			CLASS.startDrawingPolygon.mouseClick 		= google.maps.event.addListener(CLASS.map,'click', CLASS.startDrawingPolygon.registerPoint );
			CLASS.startDrawingPolygon.mouseDoubleClick	= google.maps.event.addListener(CLASS.map,'dblclick', CLASS.startDrawingPolygon.finalizePolygon );
			CLASS.startDrawingPolygon.mouseRightClick 	= google.maps.event.addListener(CLASS.map,'rightclick', CLASS.startDrawingPolygon.finalizePolygon);
			CLASS.startDrawingPolygon.mouseMove 		= google.maps.event.addListener(CLASS.map,'mousemove', CLASS.startDrawingPolygon.renderNewPoly);
			
			// Assign Callback
			if( typeof config!="undefined" && typeof config.callback == 'function' )
				CLASS.startDrawingPolygon.callback = config.callback;
			// Assign closePolyCallback
			if( typeof config!="undefined" )
				CLASS.startDrawingPolygon.polyControls = config.polyControls || {};
			// Register max number of points
			if( typeof config!="undefined" && typeof config.maxPoints != "undefined")
				CLASS.startDrawingPolygon.maxPoints = config.maxPoints;
			// Check to see if they want to center Poly
			if( typeof config!="undefined" && typeof config.centerPoly != "undefined")
				CLASS.startDrawingPolygon.centerPoly = config.centerPoly;
			// Change cursor to a crosshair
			CLASS.map.setOptions({ draggableCursor: 'crosshair' });
		},
		
		// Registers a new point into the pathsCollection Array
		registerPoint: function(location){
			// Initialize our polygon if this is our first click
			if( CLASS.shapes[CLASS.startDrawingPolygon.index]==null )
			{
				var polyOptions = CLASS.defaultPolyOptions;
				polyOptions.map = CLASS.map;
				CLASS.shapes[CLASS.startDrawingPolygon.index] = new google.maps.Polygon(polyOptions);
				// Apply any tags to this poly
				if( polyOptions.tags )
				{ CLASS.shapes[CLASS.startDrawingPolygon.index].tags = CLASS.bitList.getTagsID(polyOptions.tags); }
			}
			
			// Check to see if we are close to our max number of points
			if( CLASS.startDrawingPolygon.maxPoints!=0 && CLASS.startDrawingPolygon.pathsCollection.getLength()==CLASS.startDrawingPolygon.maxPoints-1 )
				// This will be the eigth point, so we must end the drawing
				CLASS.startDrawingPolygon.finalizePolygon(location);
			else
				// Add the location to the MVC pathsCollection
				// Adjust the location in relation to the mouseclick
				CLASS.startDrawingPolygon.pathsCollection.push( CLASS.addPixelsToLatLng(location.latLng,CLASS.startDrawingPolygon.mouseToCornerDifference) );
		},
		
		// Renders a new polygon with every mouse move
		renderNewPoly: function(location){
			// Wait until the user has clicked for the first time
			if( CLASS.shapes[CLASS.startDrawingPolygon.index]!=null )
			{				
				// Create a local parallel array, copy the elements of the shape and finally add only one additional latLng
				var paths = Array();
				CLASS.startDrawingPolygon.pathsCollection.forEach(function(el,i){
					paths[i] = el;
				});
				// Adjust the location in relation to the mouseclick
				paths.push(CLASS.addPixelsToLatLng(location.latLng,CLASS.startDrawingPolygon.mouseToCornerDifference));
				
				// Finally put the shape on the map
				CLASS.shapes[CLASS.startDrawingPolygon.index].setPaths( paths );
			}
		},
		
		// Close up the polygon and call the developer's callback
		finalizePolygon: function(location){
			// Plot polygon controls
			CLASS.startDrawingPolygon.attachPolyControls();
						
			// Close all event listeners
			google.maps.event.removeListener(CLASS.startDrawingPolygon.mouseClick);
			google.maps.event.removeListener(CLASS.startDrawingPolygon.mouseDoubleClick);
			google.maps.event.removeListener(CLASS.startDrawingPolygon.mouseMove);
			google.maps.event.removeListener(CLASS.startDrawingPolygon.mouseRightClick);
			
			// Adjust the location in relation to the mouseclick
			location.latLng = CLASS.addPixelsToLatLng(location.latLng,CLASS.startDrawingPolygon.mouseToCornerDifference);
			// Register and render the last point
			CLASS.startDrawingPolygon.renderNewPoly(location);
			
			// Change mouse cursor to original
			CLASS.map.setOptions({ draggableCursor: 'default' });
			
			// Check to see if we need to center Poly
			if( CLASS.startDrawingPolygon.centerPoly === true )
				CLASS.map.fitBounds( CLASS.getBoundsFromMVCArray( CLASS.shapes[CLASS.startDrawingPolygon.index].getPaths().getAt(0) ) );
			
			// Call back the developer's function, if provided
			if( typeof CLASS.startDrawingPolygon.callback=="function" )
				CLASS.startDrawingPolygon.callback( CLASS.shapes[CLASS.startDrawingPolygon.index].getPaths().getAt(0) );
		},
		
		// Attaches a closing marker to the map, which allows the user to close this polygon
		attachPolyControls: function(){
			// Create new poly controls
			CLASS.startDrawingPolygon.polyControls.shapeIndex = CLASS.startDrawingPolygon.index;
			CLASS.shapes[CLASS.startDrawingPolygon.index].controls = new CLASS.addPolyControls( CLASS.startDrawingPolygon.polyControls );
		}
		
	}// END startDrawingPolygon object
	
	// Method adds pixels to a latLng object and then returns the new latlng
	// Param: latLng, pixel adjustment {x,y}
	CLASS.addPixelsToLatLng = function(latLng, pixelAdjustment){
		// Check for x and y 
		if( typeof pixelAdjustment.x=="undefined" ) pixelAdjustment.x=0;
		if( typeof pixelAdjustment.y=="undefined" ) pixelAdjustment.y=0;
		
		// local container for new pixels
		var locPixels = {};
		
		CLASS.latLngToPixel(latLng, function(px){
			// We get the returned pixels from our latLng and add additional pixels from param
			locPixels.y = px.y+pixelAdjustment.y;
			locPixels.x = px.x+pixelAdjustment.x;
			
		});
		// Get a new LatLng object from pixels
		var overlay = CLASS.canvassOverlayView.getProjection();
		return overlay.fromContainerPixelToLatLng( locPixels );
	}
	
	// Object used for drawing on the map
	CLASS.startDrawingRectangle = {
		NE:null,
		SW: null,
		original: null,
		index : null,
		firstclick : true,
		doneDrawing : false,
		polyControls : null,
		// Map listener objects
		mapClick1 : null,
		mapMouseMove : null,
		mapClick2 : null,
		callback : null,
		mouseMargin: 0,
		
		
		// Starts the drawing process
		startDrawing : function(loc){
			CLASS.startDrawingRectangle.original = loc;
						
			// Customize the shape setting here
			var recOptions = CLASS.defaultPolyOptions;
			recOptions.map = CLASS.map;
			recOptions.bounds = new google.maps.LatLngBounds( CLASS.startDrawingRectangle.original.latLng,CLASS.startDrawingRectangle.original.latLng );
			CLASS.shapes[CLASS.startDrawingRectangle.index] = new google.maps.Rectangle(recOptions);
			
			// Do not start again, flag also activates the rendering of the shape
			CLASS.startDrawingRectangle.firstclick = false;
			google.maps.event.removeListener(CLASS.startDrawingRectangle.mapClick1);
			CLASS.startDrawingRectangle.mapClick1=null;
		},
		
		// Redraws the rectangle everytime you move the mouse
		renderRectangle : function(loc2){
			if( !CLASS.startDrawingRectangle.firstclick )
			{			
				// Setup East and West Longitude correctly
				if( CLASS.startDrawingRectangle.original.latLng.lng()-0.0015 > loc2.latLng.lng() )
				{
					var eLng = CLASS.startDrawingRectangle.original.latLng.lng();
					var wLng = loc2.latLng.lng()+0.0015;
				}else if( CLASS.startDrawingRectangle.original.latLng.lng()+0.0015 < loc2.latLng.lng() )
				{
					var eLng = loc2.latLng.lng()-0.0015;
					var wLng = CLASS.startDrawingRectangle.original.latLng.lng();
				}else
				{
					var eLng = CLASS.startDrawingRectangle.original.latLng.lng();
					var wLng = CLASS.startDrawingRectangle.original.latLng.lng();
				}
				
				// Setup North and South Latitude correctly
				if( CLASS.startDrawingRectangle.original.latLng.lat() >= loc2.latLng.lat() )
				{
					var nLat = CLASS.startDrawingRectangle.original.latLng.lat();
					var sLat = loc2.latLng.lat();
					
				}else
				{
					var nLat = loc2.latLng.lat();
					var sLat = CLASS.startDrawingRectangle.original.latLng.lat();
				}

				// Setup new NE and SW Coordinates
				CLASS.startDrawingRectangle.SW = new google.maps.LatLng( sLat,wLng );
				CLASS.startDrawingRectangle.NE = new google.maps.LatLng( nLat,eLng );
				
				// Redraw the shape in question
				CLASS.shapes[CLASS.startDrawingRectangle.index].setBounds( new google.maps.LatLngBounds( CLASS.startDrawingRectangle.SW,CLASS.startDrawingRectangle.NE ) );
				
				// Create the listener and handler for second click
				if( CLASS.startDrawingRectangle.mapClick2==null )
					CLASS.startDrawingRectangle.mapClick2 = google.maps.event.addListenerOnce( CLASS.map, 'click', CLASS.startDrawingRectangle.finalizeDrawing );
			
			}
		},
		
		// Finalize the drawing events
		finalizeDrawing : function(locFinal){
			CLASS.startDrawingRectangle.firstclick = true;
			// Disable the event handlers
			// No more drawing
			// mapClick1: was disabled as soon as the drawing started.
			google.maps.event.removeListener(CLASS.startDrawingRectangle.mapClick2);
			google.maps.event.removeListener(CLASS.startDrawingRectangle.mapMouseMove);
			CLASS.startDrawingRectangle.mapClick2=null;
			CLASS.startDrawingRectangle.mapMouseMove=null;
			// Change mouse cursor to original
			CLASS.map.setOptions({ draggableCursor: 'default' });
			// Call provided callback, returning the bounds of the shape
			if( typeof CLASS.startDrawingRectangle.callback == 'function' )
				CLASS.startDrawingRectangle.callback(CLASS.shapes[CLASS.startDrawingRectangle.index].getBounds());
			// Inject polyControls
			CLASS.startDrawingRectangle.addPolyControls();
		},
		
		// Used for activating the listeners
		init : function(issuedCallback, polyControls){
			// Get polyControl Options
			CLASS.startDrawingRectangle.polyControls = polyControls || {};
			// Get the next shape
			CLASS.startDrawingRectangle.index = CLASS.shapes.length;
			CLASS.shapes[CLASS.startDrawingRectangle.index] = null;
			// Nothing happens until user clicks on the map
			CLASS.startDrawingRectangle.mapClick1 = google.maps.event.addListener(CLASS.map, 'click', CLASS.startDrawingRectangle.startDrawing );
			// New rectangle is rendered on every mouse move
			CLASS.startDrawingRectangle.mapMouseMove = google.maps.event.addListener(CLASS.map, 'mousemove', CLASS.startDrawingRectangle.renderRectangle );
			
			// Assign Callback
			if( typeof issuedCallback == 'function' )
				CLASS.startDrawingRectangle.callback = issuedCallback; 
			// Change mouse cursor to crosshair
			CLASS.map.setOptions({ draggableCursor: 'crosshair' });
		},
		
		// Manual deactivate the drawing feature
		terminate: function(){
			CLASS.startDrawingRectangle.firstclick = true;
			if( CLASS.shapes[CLASS.startDrawingRectangle.index]!=null )
			{
				// Erase any existing shape
				CLASS.shapes[CLASS.startDrawingRectangle.index].setMap(null);
				CLASS.shapes.length--;
			}
			// Change mouse cursor to original
			CLASS.map.setOptions({ draggableCursor: 'default' });
			// Disable the event handlers
			if( CLASS.startDrawingRectangle.mapClick1 != null )
				google.maps.event.removeListener(CLASS.startDrawingRectangle.mapClick1);
			if( CLASS.startDrawingRectangle.mapClick2 != null )
				google.maps.event.removeListener(CLASS.startDrawingRectangle.mapClick2);
			if( CLASS.startDrawingRectangle.mapMouseMove != null )
				google.maps.event.removeListener(CLASS.startDrawingRectangle.mapMouseMove);
		},
		
		// This method adds poly controls to this rectangle
		addPolyControls : function(){
			// Add some properties to polyControls
			CLASS.startDrawingRectangle.polyControls.shapeIndex = CLASS.startDrawingRectangle.index;
			CLASS.shapes[CLASS.startDrawingRectangle.index].controls = new CLASS.addPolyControls( CLASS.startDrawingRectangle.polyControls );
		}
		
	}// END CLASS.activateDrawingRectangle()
	
	// Show traffic layer on map
	CLASS.showTraffic = function()
	{
		// Check if the layer has not been used yet
		if( CLASS.trafficLayer == null )
			CLASS.trafficLayer = new google.maps.TrafficLayer();	
			
		// Activate layer on current map
		CLASS.trafficLayer.setMap(CLASS.map);
	}
	
	// Hides the traffic on the current map
	CLASS.hideTraffic = function()
	{
		// Activate layer on current map
		CLASS.trafficLayer.setMap(null);
	}
	
	// Toggles the Traffic Layer between show/hide
	CLASS.toggleTraffic = function()
	{
		if( CLASS.trafficLayer==null || CLASS.trafficLayer.getMap()==null )
			CLASS.showTraffic();
		else
			CLASS.hideTraffic();
	}
	
	// Public Singleton object for custom infowindow
	CLASS.customInfoWindow = {		
		// setTimeout obj used for mouseout on markers
		closeDelay : null,
		
		// DOM Element
		el : document.createElement('div'),
		arrow : document.createElement('img'),
		
		// Overwrite DOM Element
		setElement : function(obj){ CLASS.customInfoWindow.el = obj; },
		
		// Private Method that create a dom Element which will 
		// be used to floated around as a infowindow for all markers
		init : function()
		{
			CLASS.customInfoWindow.el.style.fontFamily 		= 'Arial,sans-serif';
			CLASS.customInfoWindow.el.style.zIndex 			= '5000';
			CLASS.customInfoWindow.el.style.position 		= 'absolute';
			CLASS.customInfoWindow.el.style.borderWidth		= '2px';
			CLASS.customInfoWindow.el.style.borderStyle		= 'solid';
			CLASS.customInfoWindow.el.style.borderColor		= '#000000';
			CLASS.customInfoWindow.el.style.fontSize 		= '12px';
			CLASS.customInfoWindow.el.style.padding 		= '10px';
			CLASS.customInfoWindow.el.style.backgroundColor = '#FFFFFF';
			CLASS.customInfoWindow.el.style.display 		= 'none';
			CLASS.customInfoWindow.el.innerHTML 			= '';
			CLASS.customInfoWindow.el.id					= "HAR_custom_infoWindow";
			CLASS.customInfoWindow.arrow.id					= "HAR_custom_infoWindow_arrow";
			CLASS.customInfoWindow.arrow.style.position 	= 'absolute';
			CLASS.customInfoWindow.arrow.style.display 		= 'none';
			CLASS.customInfoWindow.arrow.style.zIndex 		= '5500';
			
			document.body.appendChild(CLASS.customInfoWindow.el);
			document.body.appendChild(CLASS.customInfoWindow.arrow);
			// Permantly setup an listener to cancel Timeouts that can potentially
			// close a infowindow when user mouses over the window
			google.maps.event.addDomListener( CLASS.customInfoWindow.el, "mouseover", function(){
				// Just incase the setTimeout is active from another marker
				if( CLASS.customInfoWindow.closeDelay != null )
				{
					clearTimeout(CLASS.customInfoWindow.closeDelay);
					CLASS.customInfoWindow.closeDelay = null;
				}
			});
			// Listen for when the user mouses out of the infowindow
			google.maps.event.addDomListener( CLASS.customInfoWindow.el, 'mouseout', function(){
				// Set a 1 second delay to close the window
				// If user pans over the infowindow we will cancel this delay
				CLASS.customInfoWindow.closeDelay = setTimeout(CLASS.customInfoWindow.close,1000);
			});
			// Create a handlers that closes the infowindow when the map is panned or zoomed
			google.maps.event.addListener( CLASS.map, 'dragstart', 		CLASS.customInfoWindow.close );
			google.maps.event.addListener( CLASS.map, 'zoom_changed', 	CLASS.customInfoWindow.close );
		},
		// Closes the infowindow
		close : function(){
			CLASS.customInfoWindow.closeDelay	= null;
			CLASS.customInfoWindow.el.style.display = 'none';
			CLASS.customInfoWindow.arrow.style.display = 'none';
		},
		// Open the infowindow
		open : function(){
			CLASS.customInfoWindow.el.style.display = 'block';
			CLASS.customInfoWindow.arrow.style.display = 'block';
		},
		// Hide the infowindow
		invisible : function(){
			CLASS.customInfoWindow.el.style.visibility = 'hidden';
			CLASS.customInfoWindow.arrow.style.visibility = 'hidden';
		},
		// show the infowindow
		visible : function(){
			CLASS.customInfoWindow.el.style.visibility = 'visible';
			CLASS.customInfoWindow.arrow.style.visibility = 'visible';
		},		
		// Shows the infowindow at a specific location with given content
		fillShow : function(content, latLng, showDirections)
		{
			// Check for 3rd param (optional)
			var showDir_flag = (typeof showDirections=="undefined" || showDirections);
			// Just incase the setTimeout is active from another marker
			if( CLASS.customInfoWindow.closeDelay != null )
			{
				clearTimeout(CLASS.customInfoWindow.closeDelay);
				CLASS.customInfoWindow.closeDelay = null;
			}
			
			// First see if we can get pixel coordinates for given lat and lng
			CLASS.latLngToPixel(latLng, function(markerLocation){
				// Set the content into the infowindow then show the infowindow
				CLASS.customInfoWindow.el.innerHTML = content;
				// Not all maps want to show directions on popups
				if( showDir_flag )
					CLASS.customInfoWindow.el.appendChild( CLASS.getDirectionsWrapperElement(latLng.toUrlValue()) );
				CLASS.customInfoWindow.positionWindow(markerLocation);
			});

			// NOTE: If an exception was thrown trying to get pixel coordinates for latLng
			// Then nothin will happen and infowindow will not be shown
		},
		
		// Method Check to see if the infowindow has been filled yet,
		positionWindow : function(markerLocation){
				// Open the info window (DOM calculations), but hide it from view
				CLASS.customInfoWindow.open();
				CLASS.customInfoWindow.invisible();
				
				// Calculate and set window to appropriate position
				var offSet = CLASS.customInfoWindow.calculateInfoWindowPosition(markerLocation);
				CLASS.customInfoWindow.el.style.left 	= offSet.x+"px";
				CLASS.customInfoWindow.el.style.top		= offSet.y+"px";
				// Set appropriate arrow next to marker
				if( offSet.orientation == 'left' )				
				{
					CLASS.customInfoWindow.arrow.src = CLASS.images.infoWindowArrowRight;
					CLASS.customInfoWindow.arrow.style.left 	= (offSet.markerOffsetX-22)+"px";
					CLASS.customInfoWindow.arrow.style.top		= (offSet.markerOffsetY-15)+"px";
				}else
				{
					CLASS.customInfoWindow.arrow.src = CLASS.images.infoWindowArrowLeft;
					CLASS.customInfoWindow.arrow.style.left 	= (offSet.markerOffsetX+2)+"px";
					CLASS.customInfoWindow.arrow.style.top		= (offSet.markerOffsetY-15)+"px";
				}
				
				// Now Show the infowindow
				CLASS.customInfoWindow.visible();
		},
		
		// Method decides what the location for the customInfoWindow will be.
		// Depending on what quadrant the location is in, the infowindow will
		// always render towards the center of the map.
		calculateInfoWindowPosition : function(pinLocation)
		{			
			// private collection of offsets and custom infoWindow dimensions
			var mO = CLASS.getMapOffset();
			var pO = { x:(pinLocation.x+mO.x), y:(pinLocation.y+mO.y) };
			var wD = { x:CLASS.customInfoWindow.el.offsetWidth, y:CLASS.customInfoWindow.el.offsetHeight };
			var mD = CLASS.getMapDimensions();
			var arrowWidth = 20; 
			
			// Calculate all 4 different formulas for new width and height
			// These are custom calculations designed with map container
			// divided into 4 quadrants.
			var fx1 = pO.x+arrowWidth;
			var fx2 = pO.x-(wD.x)-arrowWidth;
			var fy1 = pO.y-wD.y+30;
			var fy2 = pO.y-30;
			
			// Build return Object
			var returnObj = {
				markerOffsetX: pO.x,
				markerOffsetY: pO.y,
				mapOffsetX: mO.x,
				mapOffsetY: mO.y
			};
			
			// Decide what quadrant the pin is located in and return appropriate coordinates
			if( pinLocation.x <= (mD.x*.5) && pinLocation.y <= (mD.y*.5) )
			{
				// Quadrant 1
				returnObj.x = fx1;
				returnObj.y = fy2;
				returnObj.orientation = 'right';
				return returnObj;
			}
			else if( pinLocation.x > (mD.x*.5) && pinLocation.y <= (mD.y*.5) )
			{
				// Quadrant 2
				returnObj.x = fx2;
				returnObj.y = fy2;
				returnObj.orientation = 'left';
				return returnObj;
			}
			else if( pinLocation.x <= (mD.x*.5) && pinLocation.y > (mD.y*.5) )
			{
				// Quadrant 3
				returnObj.x = fx1;
				returnObj.y = fy1;
				returnObj.orientation = 'right';
				return returnObj;
			}
			else
			{
				// Quadrant 4
				returnObj.x = fx2;
				returnObj.y = fy1;
				returnObj.orientation = 'left';
				return returnObj;
			}
		}// END calculateInfoWindowPosition
		
	};// END customInfoWindow Singleton

	// Public method returns the pixel offset of the map container
	// realtive to the document body.
	CLASS.getMapOffset = function()
	{
		var obj = document.getElementById(CLASS.mapDIV);
		var curleft = curtop = 0;
		if (obj.offsetParent) {
			do{
				curleft += obj.offsetLeft;
				curtop += obj.offsetTop;
			}while(obj = obj.offsetParent);
		}
		return {x:curleft,y:curtop};
	}
	
	// Public method returns the dimensions of the map container
	CLASS.getMapDimensions = function()
	{
		var obj = document.getElementById(CLASS.mapDIV);		
		return { x:obj.offsetWidth, y:obj.offsetHeight };
	}
	
	// Public interface for Yelp!
	CLASS.yelp = {
		// Global Object Name, to be exposed
		exposeSearchByCatTerm : 'yelp_exposeSearchByCat_Instance_'+CLASS.ID,
		
		// [Hack] Makes up for any time difference in browsers
		timeDiff : 8000000000,
		
		// base URL for API calls
		url : 'http://api.yelp.com/v2/',
		
		// API SUPER CLASS interface
		_apiInterface : function(API_NAME){ 
			
			// Setup the default for the API Name
			API_NAME = API_NAME || 'search';
			
			// Return a copy of OUR API Interface
			// This interface is made to work with 
			// search and business APIs.
			return function(opts,callback){
				
				// Check to see if opts is a string
				if( typeof opts === 'string' )
				{
					var busID = new String(opts);
					opts = null;
				}
				
				// Option defaults
				opts = opts || {};
				opts.limit = opts.limit || 20;
					// Check max limit
					if(opts.limit > 20)
					{
						CLASS.console.error("CLASS.yelp."+API_NAME+"() >> limit option can not exceed 20. limit was changed to 20 by API.");
						opts.limit = 20;
					}
				opts.cc = opts.cc || 'US';
				opts.lang = opts.lang || 'en';
				
				// Attach current map bounds
				var b = CLASS.map.getBounds();
				var ne = b.getNorthEast();
				var sw = b.getSouthWest();
				opts.bounds = opts.bounds || sw.toUrlValue(6)+"|"+ne.toUrlValue(6);
				
				// Convert opts into params
				var params = []; 
				// Iterate through provided options (opts)
				for(var key in opts)
				{
					// Add them into params array
					if( opts.hasOwnProperty(key) )
						params.push([ key,opts[key] ]);
				}
				params.push(['callback', 'yelp_'+(new Date()).getTime() ]);
				params.push(['oauth_consumer_key', CLASS.yelpAuth.consumerKey ]);
				params.push(['oauth_consumer_secret', CLASS.yelpAuth.consumerSecret ]);
				params.push(['oauth_token', CLASS.yelpAuth.accessToken ]);
				params.push(['oauth_signature_method', CLASS.yelpAuth.serviceProvider.signatureMethod ]);
				
				// construct the proper URL for this api call
				var nURL = new String( CLASS.yelp.url+API_NAME );
				// Check to see if we need to add more to URL
				if( busID ){ nURL+='/'+busID; }
				
				// OAuth Setup
				var accessor = {
				  consumerSecret: CLASS.yelpAuth.consumerSecret,
				  tokenSecret: CLASS.yelpAuth.accessTokenSecret
				};
				OAuth.timeCorrectionMsec = CLASS.yelp.timeDiff;
				
				// OAuth Message
				var message = { 
				  'action': nURL,
				  'method': 'GET',
				  'parameters': params
				};
				
				// OAuth Signing
				OAuth.setTimestampAndNonce(message);
				OAuth.SignatureMethod.sign(message, accessor);
				var parameterMap = OAuth.getParameterMap(message.parameters);				
				parameterMap.oauth_signature = parameterMap.oauth_signature.replace(/\+/g,'%2B');
				
				
				// Make the API CAll
				CLASS.jsonp({
					url: message.action,
					success: callback,
					data: parameterMap
				});
			}			
		},
		
		// Method that takes a JSONP response from Yelp Server
		// Used for default yelp map control.
		plotByCategoryTerm : function(opts){
			// Default opts
			opts = opts || {};
			opts.cat = opts.cat || false;
			opts.term = opts.term || false;
			opts.callback = opts.callback || function(){};
			
			// Setup criteria
			var criteria = {sort:0};
			if(opts.cat) criteria.category_filter = opts.cat;
			if(opts.term) criteria.term = opts.term;
			
			// BETA yelp API
			CLASS.yelp.search(criteria,function(resp){
				// Clear the map of markers
				CLASS.deleteMarkers();
				var bus = resp.businesses;
				// alert user if nothing is there
				if( bus.length==0 ) 
				{ alert("Yelp has no results for '"+ (criteria.term || criteria.category_filter) +"' in Map Area."); return;}
				
				// Relay the response to desired callback
				opts.callback(resp);
				
				// Iterate the list of businesses
				for(var i=0; i<bus.length; i++)
				{
					var b = bus[i];
					var pin = CLASS.pins.starPin;
					var lat = b.location.coordinate.latitude;
					var lng = b.location.coordinate.longitude;
					// Parse our template
					var htmlString = CLASS.templates.yelpInfoWindow(b);
					// Plot the pin
					CLASS.plotPin({
						pinIcon: pin,
						html: htmlString,
						lat: lat,
						lng: lng
					});
				}
			});
		}		
	};
	// Globally expose CLASS.yelp.plotByCategory() 
	// Not for public use. Only used from CLASS.yelp.createControl()
	window[CLASS.yelp.exposeSearchByCatTerm] = CLASS.yelp.plotByCategoryTerm;
	// HAR.com's Yelp! API Interface for our developers
	// This should be the only part of the yelp class that is 
	// documented. This allows our interface to grow along with 
	// Yelp's evolving API.
	CLASS.yelp.search = new CLASS.yelp._apiInterface('search');
	CLASS.yelp.business = new CLASS.yelp._apiInterface('business');
	// Interface to automatically creating a default yelp control on all maps.
	CLASS.yelp.createControl = function(){
		var yelpControlEl = CLASS.customControl.insert({
			content: "<div>"+
					 "<img src='"+CLASS.customControlImages.poi.src+"' onClick='javascript: HAR.domElementToggle(\"yelpCategoryMenu_"+CLASS.ID+"\");'/>"+
					 "<div id='yelpCategoryMenu_"+CLASS.ID+"' style='display:none;width:91px;background:white;border:1px solid;padding:0px 10px;'>"+
					 "<ul style='list-style-type:none !important;padding:0px !important;padding-top:10px !important;'>"+
					 "<li style='margin-bottom:10px !important;'><a onClick='javascript: "+CLASS.yelp.exposeSearchByCatTerm+"({cat:\"hotdogs\"});'>Fast Food</a></li>"+
					 "<li style='margin-bottom:10px !important;'><a onClick='javascript: "+CLASS.yelp.exposeSearchByCatTerm+"({cat:\"restaurants\"});'>Restaurants</a></li>"+
					 "<li style='margin-bottom:10px !important;'><a onClick='javascript: "+CLASS.yelp.exposeSearchByCatTerm+"({cat:\"grocery\"});'>Grocery</a></li>"+
					 "<li style='margin-bottom:10px !important;'><a onClick='javascript: "+CLASS.yelp.exposeSearchByCatTerm+"({cat:\"childcare\"});'>Childcare</a></li>"+
					 "<li style='margin-bottom:10px !important;'><a onClick='javascript: "+CLASS.yelp.exposeSearchByCatTerm+"({cat:\"religiousorgs\"});'>Worship</a></li>"+
					 "<li style='margin-bottom:10px !important;'><a onClick='javascript: "+CLASS.yelp.exposeSearchByCatTerm+"({cat:\"hospitals\"});'>Hospitals</a></li>"+
					 "<li style='margin-bottom:10px !important;'><a onClick='javascript: "+CLASS.yelp.exposeSearchByCatTerm+"({term:\"park and ride\"});'>Park and Ride</a></li>"+
					 "</ul></div>"+
					 "</div>",
			position: "TOP_RIGHT"
		});
		// DOM Element Styling
		yelpControlEl.style.background = 'none';
		yelpControlEl.style.border = 'none';
	};
	
	// Public Class for Maponics API
	CLASS.maponics = {
		// Submit a request for maponics neighborhood polygon
		request : function(data){
			// Check for proper var
			data = data || {
								submatch:'N',
								name:'houston',
								city:'houston',
								state:'TX',
								method: ''
							};
			data.method = data.method || 'getGeoPolyByName';
			
			// Make a JSONP call to the local maponics API
			CLASS.jsonp({
				url:'/maponics/api.cfm',
				success: CLASS.maponics.process_response,
				data: data
			});
		},
		// Process the response from our own maponics API.cfm
		process_response : function(resp){
			// Process the response from maponics server
			if( !resp.neighborhoods )
			{ CLASS.console.error("Not proper response from http://www.har.com/maponics/api.cfm"); return; }
			else if( !resp.neighborhoods.length )
			{ CLASS.console.warn("No results for maponics query."); }
			
			// Neighborhoods shorthand var
			var n = resp.neighborhoods;
			
			// Shape array
			var sh = [];
			// Loops through neighborhoods
			for(var i=0; i<n.length; i++)
			{
				CLASS.drawPolyFromMVCArray( CLASS.maponics.parse_poly_string(n[i]) );
			}
		},
		// Parse a maponics poly string
		parse_poly_string : function(str){
			// split this string and return an array of points
			var poly_list = str.split(')),((');
			var point_list = []
			// Iterate through poly_list and separate points
			for(var i=0; i<poly_list.length; i++)
			{
				// create a big array with lat/lng strings
				point_list[i] = poly_list[i].split(",");
				// Iterate point_list to create individual Google LatLng objects
				for(var j=0; j<point_list[i].length; j++)
				{
					// tmp save the point string
					var tmp = point_list[i][j].split(" ");
					// Google Obj
					var tmp = new google.maps.LatLng( tmp[1], tmp[0] );
					// Save back to original position
					point_list[i][j] = tmp;
				}
			}
			
			// Return the point_list
			return point_list;
		}
	};
	// Activate different Boundary Layers
	CLASS.layers = {
		// Neighborhood Layer VARs
		NH_Layer:null,
		NH_Boundaries:true,
		NH_Lables:true,
		NH_Boundaries_XML:'Neighborhood_opaque.sld.xml',
		NH_Lables_XML:'NeighborhoodL_blue.sld.xml',
		NH_Boundaries_Options:{zoomRange:{min:11,max:19}},
		NH_Lables_Options:{zoomRange:{min:11,max:19}},
		
		// School ISD Layer VARs
		SCH_Layer:null,
		SCH_Boundaries:true,
		SCH_Lables:true,
		SCH_Boundaries_XML:'SchoolDist.xml',
		SCH_Lables_XML:'SchoolDistL.sld.xml',
		SCH_Boundaries_Options:{zoomRange:{min:11,max:19}},
		SCH_Lables_Options:{zoomRange:{min:11,max:19}},
		
		// Layer Interface
		showNeighborhoods:function(){
			// Make sure we have created a secure connection
			CLASS._initializeParcelAPI(function(){
				// Check to see if layer has not been initiated before
				if( CLASS.layers.NH_Layer==null )
				{
					CLASS.console.log("CLASS.layers.showNeighborhoods() >> Creating Neighborhood Layer from Dmp.Layer.WMSLayer()");
					// Load Neighborhood Layer
					CLASS.layers.NH_Layer = new Dmp.Layer.WMSLayer("neighborhoodLayer", "SS");
					// Add Neighborhood Boundaries
					if( CLASS.layers.NH_Boundaries )
						CLASS.layers.NH_Layer.addChild("neighborhoodB", "ACCOUNT_FOLDER/MO_NeighborhoodPub", "$(ACCOUNT_FOLDER)SLD/"+CLASS.layers.NH_Boundaries_XML, CLASS.layers.NH_Boundaries_Options);
					// Add Neighborhood Labels
					if( CLASS.layers.NH_Lables )
						CLASS.layers.NH_Layer.addChild("neighborhoodL", "ACCOUNT_FOLDER/MO_NeighborhoodPub", "$(ACCOUNT_FOLDER)SLD/"+CLASS.layers.NH_Lables_XML, CLASS.layers.NH_Lables_Options);
				}
				// Set layer to our map office
				CLASS.layers.NH_Layer.setMap(CLASS.map);
			});
		},
		hideNeighborhoods:function(){
			// Make Layer object null, if not already
			if( CLASS.layers.NH_Layer!==null )
			{
				// Assume that it is an object of Dmp.Layer.WMSLayer()
				CLASS.layers.NH_Layer.setMap(null)
			}
		},
		showSchools:function(){
			// Make sure we have created a secure connection
			CLASS._initializeParcelAPI(function(){
				// Check to see if layer has not been initiated before
				if( CLASS.layers.SCH_Layer==null )
				{
					// Load Neighborhood Layer
					CLASS.layers.SCH_Layer = new Dmp.Layer.WMSLayer("schoolDistrict", "SS");
					// Add Neighborhood Boundaries
					if( CLASS.layers.SCH_Boundaries )
						CLASS.layers.SCH_Layer.addChild("schooldistB", "ACCOUNT_FOLDER/MO_SchoolDistrictPub", "$(ACCOUNT_FOLDER)SLD/"+CLASS.layers.SCH_Boundaries_XML, CLASS.layers.SCH_Boundaries_Options);
					// Add Neighborhood Labels
					if( CLASS.layers.SCH_Lables )
						CLASS.layers.SCH_Layer.addChild("schooldistL", "ACCOUNT_FOLDER/MO_SchoolDistrictPub", "$(ACCOUNT_FOLDER)SLD/"+CLASS.layers.SCH_Lables_XML, CLASS.layers.SCH_Lables_Options);
				}
				// Set layer to our map office
				CLASS.layers.SCH_Layer.setMap(CLASS.map);
			});
		},
		hideSchools:function(){
			// Make Layer object null, if not already
			if( CLASS.layers.SCH_Layer!==null )
			{
				// Assume that it is an object of Dmp.Layer.WMSLayer()
				CLASS.layers.SCH_Layer.setMap(null)
			}
		}
	};
	
	// Public CLASS for interaction with the parcel layer
	CLASS.parcels = {
		minZoom : 16,
		maxZoom : 20,
		color : 'CCCCCC',
		mouseOverDelay: 1000,
		mouseOverTimeout: null,
		mouseOverListener: null,
		mouseOverLocation: null,
		mouseOverToHighlight: false,
		onParcelMouseOver: null,
		clickListener : null,
		clickToHighlight : false,
		onParcelClick : null,
		polyControls: {},
		polyConfig: {},
		// -------------------------------------
		utils : function(options){
				// Before ANYTHING, make sure parcel API is loaded into DOM
				if( !CLASS.parcelAPIIsLoaded() ){ CLASS.console.error("Load Parcel API before using the HAR.parcel Class."); return;}
				// check for param
				options = options || {};
				// overwrite defaults or existing values
				CLASS.parcels.clickToHighlight 		= (typeof options.clickToHighlight!="undefined") 		?  options.clickToHighlight		:CLASS.parcels.clickToHighlight || false;
				CLASS.parcels.onParcelClick 		= (typeof options.onParcelClick!="undefined") 			?  options.onParcelClick		:CLASS.parcels.onParcelClick || null;
				CLASS.parcels.mouseOverToHighlight 	= (typeof options.mouseOverToHighlight!="undefined") 	?  options.mouseOverToHighlight	:CLASS.parcels.mouseOverToHighlight || false;
				CLASS.parcels.onParcelMouseOver 	= (typeof options.onParcelMouseOver!="undefined") 		?  options.onParcelMouseOver	:CLASS.parcels.onParcelMouseOver || null;
				CLASS.parcels.mouseOverDelay 		= (typeof options.mouseOverDelay!="undefined") 			?  options.mouseOverDelay		:CLASS.parcels.mouseOverDelay || 1000;
				CLASS.parcels.polyControls 			= (typeof options.polyControls!="undefined") 			?  options.polyControls			:CLASS.parcels.polyControls || {};
				CLASS.parcels.polyConfig 			= (typeof options.polyConfig!="undefined") 				?  options.polyConfig			:CLASS.parcels.polyConfig || {};
				
				// Check to see that the Click listener is null (not active)
				if( CLASS.parcels.clickListener===null )
					CLASS.parcels.clickListener = new google.maps.event.addListener( CLASS.map, "click", CLASS.parcels.clickProxy );
				// Check to see that the Mouse Over listener is null (not active)
				if( CLASS.parcels.mouseOverListener===null )
					CLASS.parcels.mouseOverListener = new google.maps.event.addListener( CLASS.map, "mousemove", CLASS.parcels.mouseOverCheckTimeout );
			},
		closeClickListener : function(){
				// Terminates the listener for clicking on the map
				google.maps.event.removeListener(CLASS.parcels.clickListener);
				CLASS.parcels.clickListener = null;
			},
		closeMouseOverListener : function(){
				// Terminates the listener for clicking on the map
				google.maps.event.removeListener(CLASS.parcels.mouseOverListener);
				CLASS.parcels.mouseOverListener = null;
			},
		mouseOverCheckTimeout : function(location){
				// Make sure we are in parcel range
				if( CLASS.map.getZoom()<CLASS.parcels.minZoom || CLASS.map.getZoom()>CLASS.parcels.maxZoom )
					return; // Game Over
				
				// Capture new location
				CLASS.parcels.mouseOverLocation = location.latLng;
				
				// Reset Time out if called too soon
				if( CLASS.parcels.mouseOverTimeout!=null )
				{
					//CLASS.console.log("mouseOverTimeout is being reset.");
					// Mouse moved too soon, clear the current Time Out.
					clearTimeout(CLASS.parcels.mouseOverTimeout);
				}
				
				// Create the Timeout, when it completes Timeout will be null and mouseOverProxy() will execute properly.
				CLASS.parcels.mouseOverTimeout = setTimeout( function(){ /*CLASS.console.log("MouseOver event is complete.");*/ CLASS.parcels.mouseOverTimeout=null; CLASS.parcels.mouseOverProxy(); },CLASS.parcels.mouseOverDelay );
			},
		mouseOverProxy : function(){
				// Make sure we are in parcel range
				if( CLASS.map.getZoom()<CLASS.parcels.minZoom || CLASS.map.getZoom()>CLASS.parcels.maxZoom || CLASS.parcels.mouseOverLocation===null)
					return; // Game Over
				
				// Make sure developer wants the parcel info
				if( typeof CLASS.parcels.onParcelMouseOver!="function" &&
					!CLASS.parcels.mouseOverToHighlight	  
					/* this condition must expand as functionality for "onMouseOver" grows */
				)
					return; // Game Over: No need to Dmp Query for Parcel Info.
				
				// ----------------------------------------------------------------
				// Get Geometry for this mousedOver location
				// ----------------------------------------------------------------
				CLASS.parcels.getGeometryByLatLng(CLASS.parcels.mouseOverLocation, function(resp){
						// Return parcel information is wanted
						if( typeof CLASS.parcels.onParcelMouseOver=="function" )
							CLASS.parcels.onParcelMouseOver(resp);
							
						// Check for the desire to highlight this parcel
						if( CLASS.parcels.mouseOverToHighlight && resp.geometry )
							CLASS.drawPolyFromMVCArray(resp.geometry, {polyControls: CLASS.parcels.polyControls});	
					});
				// ----------------------------------------------------------------
				
			},
		clickProxy : function(location){
				// Make sure we are in parcel range
				if( CLASS.map.getZoom()<CLASS.parcels.minZoom || CLASS.map.getZoom()>CLASS.parcels.maxZoom )
					return; // Game Over
				
				// Make sure developer wants the parcel info
				if( typeof CLASS.parcels.onParcelClick!="function" &&
					!CLASS.parcels.clickToHighlight	  
					/* this condition must expand as functionality for "onClick" grows */
				)
					return; // Game Over: No need to Dmp Query for Parcel Info.
				
				// ----------------------------------------------------------------
				// Get Geometry for this clicked location
				// ----------------------------------------------------------------
				CLASS.parcels.getGeometryByLatLng(location.latLng, function(resp){
						// Return parcel information is wanted
						if( typeof CLASS.parcels.onParcelClick=="function" )
							CLASS.parcels.onParcelClick(resp);
						
						// Check for the desire to highlight this parcel
						if( CLASS.parcels.clickToHighlight && resp.geometry )
							CLASS.drawPolyFromMVCArray(resp.geometry, {polyControls: CLASS.parcels.polyControls, polyConfig: CLASS.parcels.polyConfig});	

					});
				// ----------------------------------------------------------------
				
			},
		// ----------------------------------------------------
		// This method allows you to configure DMP tile settings
		// on the fly.
		// ----------------------------------------------------
		config : function(options){
				// Before ANYTHING, make sure parcel API is loaded into DOM
				if( !CLASS.parcelAPIIsLoaded() ){ CLASS.console.error("Load Parcel API before using the HAR.parcel Class."); return;}
				else if( !CLASS.parcelLayerLoaded ){ setTimeout(function(){ CLASS.parcels.config(options); }, 200); }
				else{ CLASS.console.log("Attempting reconfigure the parcel layer."); }
				
				// Check for default values
				// if not set, we need to set them
				options 								= options 			|| {};
				options.minZoom,CLASS.parcels.minZoom 	= options.minZoom 	|| CLASS.parcels.minZoom;
				options.maxZoom,CLASS.parcels.maxZoom 	= options.maxZoom 	|| CLASS.parcels.maxZoom;
				options.color,CLASS.parcels.color 		= options.color 	|| CLASS.parcels.color;
				
				// Remove the previous layer, only if not adding an sld
				CLASS.map.overlayMapTypes.pop();
				
				// Create a image layer (containing the parcels) via Dmp API
				var layer = new Dmp.Layer.TileLayer("SS", "Dmp_License/ParcelTiles", options);            
				// Apply the image layer (parcels) to our Google Map
				CLASS.map.overlayMapTypes.push(new google.maps.ImageMapType(layer));
				if( options.sld )
				{
					// Check for label specific options
					if( options.labelMinZoom ) options.minZoom = options.labelMinZoom;
					layer = new Dmp.Layer.TileLayer("SS", "Dmp_License/Parcels", options);            
					// Apply the image layer (parcels) to our Google Map
					CLASS.map.overlayMapTypes.push(new google.maps.ImageMapType(layer));
				}
			},
		
		// ----------------------------------------------------
		// This method highlights a parcel given a LatLng
		// Note: This method makes a query to Dmp, do not use 
		//		 with clicks or mouseOver events.  Thats what 
		//		 clickProxy()/ mouseOverProxy() are for, this 
		//		 method is for hightlighting listing parcels  
		//		 given a LatLng.
		// ----------------------------------------------------
		doHighlight: function(loc, polyOptions, callback){
				// CHeck our param
				if( !loc || !loc.lat || !loc.lng )
				{ CLASS.console.error("Parameter for CLASS.parcels.doHighlight() is not a valid <google.maps.LatLng> object."); return; }
				
				// Check for PolyContols
				polyOptions 				= polyOptions || {};
				polyOptions.polyControls 	= polyOptions.polyControls || {};
				polyOptions.polyConfig 		= polyOptions.polyConfig || {};
				
				// Query for parcel information
				CLASS.parcels.getGeometryByLatLng( loc, function(resp){
						// Check for proper geometry
						if( resp.geometry )
						{
							var shp = CLASS.drawPolyFromMVCArray(resp.geometry,{polyControls: polyOptions.polyControls, polyConfig: polyOptions.polyConfig});
							// Return to developer is requested
							if( typeof callback=='function' )
								callback(shp);
						}
					});
			},
		// ----------------------------------------------------
		// This method return parcel information given a LatLng
		// ----------------------------------------------------
		getGeometryByLatLng: function(loc, callback){
				// CHeck our param
				if( !loc || !loc.lat || !loc.lng )
				{ CLASS.console.error("Parameter for CLASS.parcels.getGeometryByLatLng() is not a valid <google.maps.LatLng> object."); return;}
				if( !callback || typeof callback!="function" )
					var callback = function(){};
				// Prepare a failed callback object (Plan B)
				var failObj = {
						geometry: null,
						apn: null,
						fips: null
					};
				
				// prepare the url for ajax call
				var url = "getByGeometry.aspx?returnGeoType=1&dataSource=Dmp_License/Parcels&inclusionGeometries=POINT(" + loc.lng() + " " + loc.lat() + ")";
				
				// Log activity
				CLASS.console.log("Sending request for: "+loc.lng()+", "+loc.lat()+"\nUrl: "+url);				
				
				// Do ajax call through Dmp Class
				Dmp.Env.Connections["SS"].getJson(url, function(json){
						CLASS.console.log("Entered the successful callback for Dmp.Env.Connections[SS].getJson()");
						// This is a successfull Callback
						if (json.Response.Results.totalRecords<=0)
						{CLASS.console.warn('Dmp.Env.Connections["SS"].getJson() returned 0 records'); return;}
						
						// Create local variables
						var recordSets = json.Response.Results.RecordSet;
						var parcelRecord = null;
						// Get the information
						if(recordSets) {
							parcelRecord =  recordSets[0].Data.Row[0];
							CLASS.console.log("RecordSets is defined. Using: recordSets[0].Data.Row[0]");
						} else {
							parcelRecord = (json.Response.Results.Data.Row.length) ? json.Response.Results.Data.Row[0]:json.Response.Results.Data.Row;
							CLASS.console.log("RecordSets is NOT defined. Using: json.Response.Results.Data.Row");
						}					
						
						// Organize the GEOMETRY STRING into an array
						if( typeof parcelRecord["GEOMETRY"]!='undefined' && typeof parcelRecord["APN"]!='undefined' && typeof parcelRecord["FIPS"]!='undefined' )
						{
							// Log the information for now
							CLASS.console.log("Geometry: "+parcelRecord["GEOMETRY"]+"\n"+"APN: "+parcelRecord.APN+"\n"+"FIPS: "+parcelRecord.FIPS);
						
							var wkt = parcelRecord["GEOMETRY"].replace("POLYGON ((","").replace("))","").split(", ");				
							
							// Arrays used for multi shaped polygons
							var polygon = new Array;
							var shape = new Array;
							for(var i=0; i<wkt.length; i++)
							{
								// Split off our two coordinates
								var loc = wkt[i].split(" ");
								
								// Check to see if we are starting a new shape
								if( loc[0].indexOf("(") != -1 )
								{
									// Push what we have so far into the polygon array
									polygon.push(shape);
									// Create a WHOLE new array
									shape = new Array();
									// Continue as normal
								}
								
								// strip unwanted characters
								loc[1] = loc[1].replace(")","");
								loc[0] = loc[0].replace("(","");
								shape.push(new google.maps.LatLng(loc[1], loc[0]));
							}
							// Save the shape into the global array
							polygon.push(shape);
							
							// Log polygon size
							CLASS.console.log("Polygon has "+polygon.length+" shape(s).");
							
							// Return a MVCArray to the callback
							callback({
										geometry: polygon,
										apn: parcelRecord.APN,
										fips: parcelRecord.FIPS
									});
						}else
						{
							// This record has not GEOMETRY attribute?
							CLASS.console.warn("GEOMETRY, APN or FIPS attribute NOT found for this parcel.\nAPN: "+parcelRecord.APN+"\n"+"FIPS: "+parcelRecord.FIPS);	
							// Return as failure
							callback(failObj);
						}
					},function(){
						// This is a failed Callback
						CLASS.console.error("Dmp.Env.Connections[SS].getJson() failed.");
						// Return as failure
						callback(failObj);
				});
			}
	}
	
	// Public Method to return the HTTP URL to use with static maps API
	CLASS.staticMaps = {
		// Super variables for this mini class, singleton
		initialized : false,
		url : "", // URL reset everytime the url is rendered
		plat: 0,
		plng : 0,
		
		// Function renders everything on the map
		renderMapURL : function()
		{
			// Reset the URL
			CLASS.staticMaps.url = "http://maps.google.com/maps/api/staticmap?";
			// Reset certain varaibles
			CLASS.staticMaps.plat = 0;
			CLASS.staticMaps.plng = 0;
			
			// Get current map settings
			var mapDimensions = CLASS.getMapDimensions();
			var center = CLASS.map.getCenter();
			var zoom = CLASS.map.getZoom();
			var mapType = CLASS.map.getMapTypeId();
			
			// Initialize URL
			CLASS.staticMaps.url += "sensor=false&center="+center.lat()+","+center.lng()+"&zoom="+zoom+"&size="+mapDimensions.x+"x"+mapDimensions.y+"&maptype="+mapType;
			
			// Addon the marker information
			CLASS.staticMaps.url += CLASS.staticMaps._getMarkerQuery();
			
			// Addon the Shape information
			CLASS.staticMaps.url += CLASS.staticMaps._getEncodedShapeQuery();
			
			// Cleanup on isle 1
			CLASS.staticMaps.url = CLASS.staticMaps.url.replace("&&","&"); 
				
			return CLASS.staticMaps.url;
			/*
			// Get this URL signed
			CLASS.ajax.call({
				url: "http://www.har.com/api/google_sign_url.cfm?url_path="+escape(escape(CLASS.staticMaps.url.replace('http://maps.google.com',''))),
				success: function(resp){
					window.location = "http://maps.google.com"+resp;
				}
			});
			*/
		},
		
		// STATIC MAPS - Local Methods -------------------------------------------------------------------------------------------------------------------------
		
		// Generates the marker section of the URL
		_getMarkerQuery : function()
		{
			var query = "";
			if( typeof CLASS.markers[0] != "undefined" )
			{
				var icon = CLASS.markers[0].getIcon();
				icon = ( typeof icon == "string" ) ? icon:icon.url;
				query = "&markers=icon:"+icon+"|";
			}
			
			for( var i=0; i<CLASS.markers.length; i++ )
			{
				// Only allow for first 50 Markers
				if( i==50 ){CLASS.console.warn("Only the first 50 listings will show on printed maps."); break;};
				
				var pos = CLASS.markers[i].getPosition();
				query += ""+pos.lat()+","+pos.lng()+"|";
			}
			return query;
		},

		// Generates the shape encoded data for the URL
		_getEncodedShapeQuery : function()
		{
			var encoded_points = "";
			var firstPoint = null;
			
			// Loop through all the points in the first shape
			if( typeof CLASS.shapes[0]!="undefined" ) 
			{
				encoded_points = "&";
				// Loop through all the points in the polygon
				CLASS.shapes[0].getPath().forEach(function(point,index){
					// Capture only first point
					if( firstPoint ==  null ) firstPoint = point;
					// Encode these Lat and Lngs
					encoded_points += CLASS.staticMaps._encodeLatLng( point.lat(), point.lng() );
				});
				
				// Add first point as last point, so we can close the image
				if( firstPoint !=  null ) encoded_points += CLASS.staticMaps._encodeLatLng( firstPoint.lat(), firstPoint.lng() );
				
				// Get the styling for the polygon
				var opts = CLASS.defaultPolyOptions;
				var path = "path=color:"+opts.strokeColor.replace("#","0x")+"|fillcolor:"+opts.fillColor.replace("#","0x")+"|weight:"+opts.strokeWeight+"|enc:";
				encoded_points = path + encoded_points;
			} 
			
			return encoded_points;
		},
		_encodeLatLng : function(lat,lng)
		{
			var late5 = Math.floor(lat * 1e5);
			var lnge5 = Math.floor(lng * 1e5);
			
			dlat = late5 - CLASS.staticMaps.plat;
			dlng = lnge5 - CLASS.staticMaps.plng;
			
			CLASS.staticMaps.plat = late5;
			CLASS.staticMaps.plng = lnge5;
			
			return CLASS.staticMaps._encodeSignedNumber(dlat) + CLASS.staticMaps._encodeSignedNumber(dlng);
		},
		_encodeSignedNumber : function(num)
		{
		  var sgn_num = num << 1;
		
		  if (num < 0) {
			sgn_num = ~(sgn_num);
		  }
		
		  return(CLASS.staticMaps._encodeNumber(sgn_num));
		},
		_encodeNumber : function(num) {
		  var encodeString = "";
		
		  while (num >= 0x20) {
			encodeString += (String.fromCharCode((0x20 | (num & 0x1f)) + 63));
			num >>= 5;
		  }
		
		  encodeString += (String.fromCharCode(num + 63));
		  return encodeString;
		}
	};// END STATIC MAPS - API
	
	/*
		! Deprecated !
		By: Arlo Carreon
		Date: 12/5/2010
		Printing was fixed in version 3.3
	*/
	// Used for ONLY IE browsers
	// High jacks the printing process and injects a static map instead of the real map.
	CLASS.printPage = {
		// Map Contianer Element
		// Fill this variable right before printing
		mapContainer : null,
		// Hold the original content of the map
		// Fill this variable right before printing
		originalContent : "",
		// Holds a unique identifier for the static map
		uniqueMapID : 	CLASS.getUID(),
		// Holds static map object
		staticMapObj : null,
		// Holds original window.print function
		_print : null,
		// Done before printing
		beforePrinting : function()
		{
			// Get map dimensions
			var mD = CLASS.getMapDimensions();
			
			if( CLASS.printPage.staticMapObj==null )
			{
				// Create an img object to hold the static map
				CLASS.printPage.staticMapObj 				= document.createElement('img');
				CLASS.printPage.staticMapObj.width 			= mD.x;
				CLASS.printPage.staticMapObj.height 		= mD.y;
				CLASS.printPage.staticMapObj.src 			= "";
				CLASS.printPage.staticMapObj.id 			= CLASS.printPage.uniqueMapID;
				CLASS.printPage.staticMapObj.style.display = "none";
				// Get the proper DOM Element for the container
				CLASS.printPage.mapContainer = document.getElementById(CLASS.mapDIV);
				// Insert static map before map container
				CLASS.printPage.mapContainer.parentNode.insertBefore(CLASS.printPage.staticMapObj,CLASS.printPage.mapContainer );
				
			}
			// Everytime you print, we need to render a new static map
			CLASS.printPage.staticMapObj.src = CLASS.staticMaps.renderMapURL().replace("size=0x0","size="+mD.x+"x"+mD.y);
			
			// Hide actual map and show static map.
			CLASS.printPage.mapContainer.style.display = "none";
			// Show static map
			CLASS.printPage.staticMapObj.style.display = "block";	
		},
		// Done After Printing
		afterPrinting : function()
		{
			// Hide static map
			CLASS.printPage.staticMapObj.style.display = "none";
			// Show actual map and show static map.
			CLASS.printPage.mapContainer.style.display = "block";
		},
		// Function used for nonIE browsers
		nonIE : function()
		{
			// Change the map to a static one
			CLASS.printPage.beforePrinting();
			// Time out to call print
			setTimeout(CLASS.printPage._print,100);
			// Wait 2 seconds then put everything back to normal.
			setTimeout(CLASS.printPage.afterPrinting,100);
		}
	};
	
	// Public JSONP Class
	// Note: options.url must not contain any GET params.
	// GET params will be built from data
	CLASS.jsonp = function( options ){
		// Setup defaults
		options = options || {};
		options.success = (typeof options.success=='function') ? options.success:function(){};
		options.url = options.url || '';
		options.data = options.data || false;
		// This one time callback for jsonp
		var cb = 'a'+CLASS.getUID();
		
		// Build data?
		if( options.data )
		{
			var map = options.data;
			var tmp = [];
			for(var key in map)
			{
				if( map.hasOwnProperty(key) )
				{
					tmp.push(key+'='+map[key]);
				}
			}
			// Add data to URL
			options.url += '?'+tmp.join('&');
		}
		
		// Is callback already present?
		// It should be present in OAuth requests
		if( options.data && options.data.callback )
		{
			// Lets take note of desired callback
			cb = options.data.callback;
		}
		else
		{
			// If we created the callback, then
			// add it to the URL
			options.url += '&callback='+cb;
		}
		// Attach the success handler to this dynamic callback
		// We take care of memory leaks before calling success function
		window[cb] = function(){ options.success.apply(this,arguments); try{delete window[cb];}catch(e){CLASS.console.error("CLASS.jsonp(): garbage collection attempt >> "+e.message);} }
		
		// Inject script
		var head = document.getElementsByTagName("head")[0] || document.documentElement;
		var script = document.createElement("script");
		script.src = options.url;
		// using insertBefore instead of appendChild due to an IE6 bug
		head.insertBefore( script, head.firstChild );
	};
	
	// Public AJAX Object
	CLASS.ajax = {
			// Polling Interval
			interval : null,
			
			// Param: { url, success(), fail(), postVars{} }
			call : function( options ){
				// Setup defaults
				options = options || {};
				options.debug 		= (typeof options.debug=='boolean') ? options.debug:true;
				options.success 	= (typeof options.success=='function') ? options.success:function(){};
				options.fail 		= (typeof options.fail=='function') ? options.fail:function(){};
				options.postVars 	= (typeof options.postVars=='object') ? options.postVars:null;
				options.stream	 	= (typeof options.stream=='function') ? options.stream:function(){};
				options.streamDelay	= ( !isNaN(options.streamDelay) ) ? options.streamDelay:15;
				CLASS.console.log("CLASS.ajax.call() >> Created AJAX Obj");
				// Get our object
				var  xmlhttp = CLASS.ajax.getXMLHTTPObject();
				
				// Listen for state change
				xmlhttp.onreadystatechange = function(){
					// Log Entry
					if(options.debug) CLASS.console.log("CLASS.ajax.call() >> Current State: "+xmlhttp.readyState);
					// Check for stream init
					/*if( xmlhttp.readyState == 3 && CLASS.ajax.interval===null )
					{
						// Start polling
						if(options.debug) CLASS.console.log("CLASS.ajax.call() >> Started AJAX Stream");
					
						// Start polling
						CLASS.ajax.interval = setInterval( function(){
								// Send response text to stream handler
								options.stream( xmlhttp.responseText );
							}, options.streamDelay);
					}*/
					
					
					// AJAX Call is done
					if(xmlhttp.readyState == 4) 
					{
						// Stop Polling
						if( CLASS.ajax.interval!==null )
							clearInterval(CLASS.ajax.interval);
						
						// Log the response http status
						if(options.debug) CLASS.console.log("CLASS.ajax.call() >> Status "+xmlhttp.status);
						
						// Only if "OK"
						if (xmlhttp.status == 200)
						{
							 if( typeof options.success=="function" ) options.success(xmlhttp.responseText);
						}
						else
						{
							 if( typeof options.fail=="function" ) options.fail(xmlhttp.responseText, xmlhttp.status);
						}
					}
				};
				
				// This is the actual Call
				if( options.postVars!==null )
				{
					// Prepare for post request
					var params = "";
					for( var key in options.postVars )
					{
						// Check for non-first iteration
						if( params != "" )
							params += "&";
						// Copy the next post var
						params += key+"="+options.postVars[key];
					}
					
					// Send post request
					xmlhttp.open( "POST", options.url, true );
					xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
					xmlhttp.send(params);
				}
				else
				{
					// Send get request
					xmlhttp.open( "GET", options.url, true );
					xmlhttp.send(null);
				}
				
			},
			// Method used to get a XML HTTP obj
		 	getXMLHTTPObject : function(){
				var xmlhttp=null
				// code for W3C Standard.
				if (window.XMLHttpRequest)
				{
					return new XMLHttpRequest()
				}
				// code for IE
				else if (window.ActiveXObject)
				{
					return new ActiveXObject("Microsoft.XMLHTTP")
				}
				if (xmlhttp==null)
				{
					CLASS.console.error("CLASS.ajax.getXMLHTTPObject() >> Your browser does not support XMLHTTP")
				}
			}// END getXMLObject()
	};// END AJAX API
	
// Private Methods Below --------------------------------------------
// ------------------------------------------------------------------
	
	// Private OverlayView() inherited class
	CLASS._MyOverlay = function(map) { this.setMap(map); }
	
	// Loads the Google API into the DOM
	CLASS._loadScript = function(){ 
		
		// Push all APIs into DOM
		var head = document.getElementsByTagName('head')[0];
		
		// If API not loaded 
		if(!window[CLASS.exposedAPILoadedFlag])
		{			
			// Start with providing our Google Map API
			var apisrc 	= document.createElement('script');
			apisrc.id	= 'googleMapAPI';
			apisrc.type	= 'text/javascript';
			apisrc.src	= CLASS.mapAPI;
			head.appendChild(apisrc);
		}
		
		// Load our parcels
		// DEPRECATED: moved to CLASS._loadDependantScripts()
		/*if( CLASS.loadParcels && !CLASS.parcelAPIIsLoaded() )
		{
			var apisrc2 = document.createElement('script');
			apisrc2.id	= 'parcelAPI';
			apisrc2.type= 'text/javascript';
			apisrc2.src	= CLASS.parcelAPI;
			// Make sure another instance has not loaded the API
			if( !document.getElementById(apisrc2.id) )
				head.appendChild(apisrc2);
		}*/
		
		// Load SimpleGeo API
		if( CLASS.loadSimpleGeo && !CLASS.simpleGeoAPIIsLoaded() )
		{
			var apisrc3 = document.createElement('script');
			apisrc3.id	= 'simpleGeoAPI';
			apisrc3.type= 'text/javascript';
			apisrc3.src	= CLASS.simpleGeoAPI;
			// Make sure another instance has not loaded the API
			if( !document.getElementById(apisrc3.id) )
				head.appendChild(apisrc3);
		}
		
		// Load OAuth API
		if( CLASS.loadOAuth && !CLASS.OAuthAPIIsLoaded() )
		{
			var apisrc4 = document.createElement('script');
			apisrc4.id	= 'OAuthAPI';
			apisrc4.type= 'text/javascript';
			apisrc4.src	= CLASS.OAuthAPI;
			// Make sure another instance has not loaded the API
			if( !document.getElementById(apisrc4.id) )
				head.appendChild(apisrc4);
		}
	}
	
	// Load scripts that depend on Google
	CLASS._loadDependantScripts = function(){
		// Push all APIs into DOM
		var head = document.getElementsByTagName('head')[0];
		
		// Load our parcels
		if( CLASS.loadParcels && !CLASS.parcelAPIIsLoaded() )
		{
			var apisrc2 = document.createElement('script');
			apisrc2.id	= 'parcelAPI';
			apisrc2.type= 'text/javascript';
			apisrc2.src	= CLASS.parcelAPI;
			// Make sure another instance has not loaded the API
			if( !document.getElementById(apisrc2.id) )
				head.appendChild(apisrc2);
		}
	}
	
	// Places the actual map in the DOM
	CLASS._installMap = function(config){ 
		
		// Make sure API is finished loading asynchronously
		if( !window[CLASS.exposedAPILoadedFlag] )
		{
			setTimeout(function(){ CLASS._installMap(config) }, 100); //<-- Recursive callback
			return;
		}
		
		// DEFAULT - Setup Google Map Options
		config								= config || {};
		CLASS.mapOptions.center 			= new google.maps.LatLng(29.74887863492847,-95.36201477050782);
		CLASS.mapOptions.zoom 				= 11;
		CLASS.mapOptions.mapTypeId 			= google.maps.MapTypeId.ROADMAP;
		CLASS.mapOptions.domElement			= CLASS.mapDIV = (config.domElement || CLASS.mapDIV);
		CLASS.mapOptions.scaleControl		= true;
		CLASS.mapOptions.streetViewControl	= true;
		CLASS.mapOptions.streetView			= new google.maps.StreetViewPanorama(document.getElementById(CLASS.mapOptions.domElement),{addressControl:false,visible:false,enableCloseButton:true});
		// END DEFAULT - Google  Map Options
		
		// DEFAULT - Setup HAR.com Map Options
		CLASS.yelpControl = (typeof config.yelpControl!=='undefined') ? config.yelpControl:CLASS.yelpControl;
		// END DEFAULT - Setup HAR.com Map Options
		
		// Overwrite with developer's options
		// We trust our developers =)
		for(var key in config)
		{
			if( key=='center' )
				CLASS.mapOptions.center = new google.maps.LatLng(parseFloat(config.center.latitude),parseFloat(config.center.longitude));
			else if( key == 'mapTypeControlStyle' )
				CLASS.mapOptions.mapTypeControlOptions = {style:google.maps.MapTypeControlStyle[config.mapTypeControlStyle]};
			else if( key == 'mapTypeId' )
				CLASS.mapOptions.mapTypeId = google.maps.MapTypeId[config.mapTypeId];
			else if( key == 'navigationControlOptions' )
			{
				// Create an empty object
				CLASS.mapOptions.navigationControlOptions = {};
				// Add the position attribute
				if( config.navigationControlOptions.position )
					CLASS.mapOptions.navigationControlOptions.position = google.maps.ControlPosition[config.navigationControlOptions.position];
				// Add the style attribute
				if( config.navigationControlOptions.style )
					CLASS.mapOptions.navigationControlOptions.style = google.maps.NavigationControlStyle[config.navigationControlOptions.style];
			}
			else
				CLASS.mapOptions[key] = config[key]; 
		}
		
		// Just in case the domElement is different, lets overwrite the class variable
		// if( CLASS.mapOptions.domElement!=CLASS.mapDIV ) CLASS.mapDIV=CLASS.mapOptions.domElement;
		
		try{
			// Install Map on DOM
			CLASS.map = new google.maps.Map( document.getElementById(CLASS.mapOptions.domElement), CLASS.mapOptions );
			// Installing HAR infoWindow dom element
			CLASS.customInfoWindow.init();
			// Setup private overlay var
			CLASS._MyOverlay.prototype = new google.maps.OverlayView();
			CLASS._MyOverlay.prototype.onAdd = function() { }
			CLASS._MyOverlay.prototype.onRemove = function() { }
			CLASS._MyOverlay.prototype.draw = function() { }
			CLASS.canvassOverlayView = new CLASS._MyOverlay(CLASS.map);
			// Listen for when Map is rendered inside container (DIV)
			google.maps.event.addListener(CLASS.map, 'projection_changed', function(){ 
				// Flag this event =)
				CLASS.mapRenderedInContainer = true; 
				/*
					! Deprecated !
					By: Arlo Carreon
					Date: 12/5/2010
					Printing was fixed in version 3.3
				*/
				// Overwrite IE beforePrinting and afterPrinting methods
				if( typeof window.onbeforeprint != "undefined" )
				{
					// Add printing functionality to pages with maps
					window.onbeforeprint = CLASS.printPage.beforePrinting;
					window.onafterprint = CLASS.printPage.afterPrinting;
					// *NOTE:above functionality is IE only
				}else if( typeof window.print!="undefined" )
				{
					CLASS.printPage._print = window.print;
					window.print = CLASS.printPage.nonIE;
					// Above line does not work with multiple maps on one page.
					// Last class object overwrites the print method for the WHOLE DOCUMENT,
					// so only the last map will work well.  Need to use an event queue some how.
					// Needs fix.
				}
			});
		}catch(e){CLASS.console.error("CLASS._installMap() >> "+e.message);}
		
		// Load Default Map Controls
		if( CLASS.yelpControl )
			CLASS.yelp.createControl();
		
		// Initialize 3rd party APIs
		CLASS._initializeParcelAPI();
		CLASS._initializeSimpleGeoAPI();
		CLASS._initializeOAuthAPI();
		
		// Call the callback that the developer provided,
		// Usually a function that ends up ploting the pins
		if( typeof config != 'undefined' && typeof config.callback == 'function') config.callback();

	}
	
	// Callback used by google API to let us know the API has been loaded
	CLASS._confirmAPILoad = function(){ 
		// Set var in Window Object
		// So everyone knows Google API is ready
		window[CLASS.exposedAPILoadedFlag] = true;
		
		// Load [Google] dependant scripts
		CLASS._loadDependantScripts();

	}
	
	// Loads parcels layers into our map
	CLASS._initializeParcelAPI = function(callback){ 
		// Check callback
		callback = callback || function(){};
		
		// Check to see if Parcel API is loaded into the DOM
		if( CLASS.loadParcels && (!CLASS.parcelAPIIsLoaded() || !window[CLASS.exposedAPILoadedFlag]))
		{	
			setTimeout( function(){ CLASS._initializeParcelAPI(callback) },100 ); 
			HAR.console.log("CLASS._initializeParcelAPI() >> Executed timeout for parcels"); 
			return;  
		}
		
		// Check to see if we have already initialized session with DMP
		if( CLASS.parcelLayerLoaded )
		{
			callback();
			return;
		}
		
		// Setup the parcels
		if( CLASS.loadParcels && CLASS.parcelAPIIsLoaded() && window[CLASS.exposedAPILoadedFlag])
		{	
			//
			// TODO: Check for set cookie for DMP
			//
			
			// Make sure we do not do two ajax calls
			if( CLASS.parcelLayerInitiated )
			{	
				setTimeout(function(){ CLASS._initializeParcelAPI(callback) },100); 
				CLASS.console.log("CLASS._initializeParcelAPI() >> DMP AUTH already initiated. Returning in 100ms.");
				return;
			}
			else
			{ CLASS.parcelLayerInitiated = true; CLASS.console.log("CLASS._initializeParcelAPI() >> Initiating DMP AUTH");}
			
			// Call to get the auth token for DMP
			CLASS.ajax.call({
				debug: false,
				url: '/js/dmp_layers_auth.cfm',
				fail: CLASS.console.error,
				success: function(response){
					// Save the new auth HASH for DMP Layers
					CLASS.parcelLicense = response;
					// Start a connection to get authenticated.
					Dmp.Env.Connections["SS"].init( CLASS.parcelLicense, function(){
						CLASS.console.log("CLASS._initializeParcelAPI() >>  Connection 'SS' Established");
						
						// Create a image layer (containing the parcels) via Dmp API
						var tiles = new Dmp.Layer.TileLayer("SS", "Dmp_License/ParcelTiles", {minZoom: CLASS.parcels.minZoom, maxZoom: CLASS.parcels.maxZoom, color: CLASS.parcels.color});            
						// Apply the image layer (parcels) to our Google Map
						CLASS.map.overlayMapTypes.push(new google.maps.ImageMapType(tiles));
						CLASS.console.log("CLASS._initializeParcelAPI() >> Default Parcel Layer is installed.");
						
						// Trigger callback for Parcel dependant code
						callback();
						// Flag this event
						CLASS.parcelLayerLoaded = true;
					});
				}
			});
			
			
			
		}// END IF
	}
	
	// Loads an OAuth API used for Yelp!
	CLASS._initializeOAuthAPI = function(callback){ 
		// Check callback
		callback = callback || function(){};
		
		// Check to see if we want to load this API
		if( !CLASS.loadOAuth )
			return;
		
		// Check to see if Simple Geo API is loaded into the DOM
		if( !CLASS.OAuthAPIIsLoaded() )
			setTimeout( CLASS._initializeOAuthAPI,100 );
		
		// Trigger callback for OAuth dependant code
		if( CLASS.OAuthAPIIsLoaded() )
		{	
			// Trigger callback for OAuth dependant code
			// callback();
			// Flag the successful load of OAuth
			CLASS.OAuthLoaded = true;
			
		}// END IF
	}
	
	// Loads simple Geo API into the DOM
	CLASS._initializeSimpleGeoAPI = function(callback){ 
		// Check callback
		callback = callback || function(){};
		
		// Check to see if we want to load this API
		if( !CLASS.loadSimpleGeo )
			return;
		
		// Check to see if Simple Geo API is loaded into the DOM
		if( !CLASS.simpleGeoAPIIsLoaded() )
			setTimeout( CLASS._initializeSimpleGeoAPI,100 );
		
		// Setup the parcels
		if( CLASS.simpleGeoAPIIsLoaded() )
		{	
			// Start a client for every possible end point
			CLASS.sg = {};
			CLASS.sg.context 	= new simplegeo.ContextClient( CLASS.simpleGeoJSONPToken );
			CLASS.sg.places 	= new simplegeo.PlacesClient( CLASS.simpleGeoJSONPToken );
			CLASS.sg.storage 	= new simplegeo.Client( CLASS.simpleGeoJSONPToken );
			// Trigger callback for Simple Geo dependant code
			// callback();
			// Flag the successful load of all the clients
			CLASS.simpleGeoClientsLoaded = true;
			
		}// END IF
	}
	
	// 3rd Party Classes
	CLASS.bitList = (function(){
	
		// =================================
		// = Private Variables and Methods =
		// =================================
		// Our actual bitmasked list
		var list = {};
		
		// Method that gives us the current length of the list
		getLength = function(){
			var count=0;
			// Iterate through the list and only count local attributes,
			// not variables in the prototype chain
			for(var key in list)
			{
				if( list.hasOwnProperty(key) )
				{ count++; }
			}
			// return the count
			return count;
		}
		// Method returns the value of entire list.
		listValue = function(){
			// Keep track of sum
			var sum = 0;
			// Iterate through the list and return it's sum
			for(var key in list)
			{
				// Make sure that the key is not from a prototype chain
				if(list.hasOwnProperty(key))
				{ sum += list[key]; }
			}
			// Return Sum
			return sum;
		}
		// Inserts a new element into the list
		get = function(element){
			// Make sure we already have this element
			if( !list[element] )
			{
				// Insert element and it's value: 2^(length)
				list[element] = Math.pow(2,getLength());
			}
			
			// return it's value
			return list[element];
		}
		
		// Translate 2 params into groupIDs.
		// Accepts arrays of labels or numbers
		translate = function( a, b ){
			// Local vars
			var aID = 0, bID = 0;
			
			// Check to see if A is number or array
			if( a && a.length )
			{ 
				// Iterate through array
				for(var i=0; i<a.length; i++)
				{ 
					// Get Element
					var el = a[i].toString();
					// Make sure a[i] is in fact in the list
					aID += ( list[el] ) ? get(el):0; 
				
				} 
			}
			else if( typeof a === "number" )
			{ aID = a; }
			
			// Check to see if B is number or array
			if( b && b.length )
			{ 
				// Iterate through array
				for(var i=0; i<b.length; i++)
				{ 
					// Get Element
					var el = b[i].toString();
					// Make sure b[i] is in fact in the list
					bID += ( list[el] ) ? get(el):0;
				}
			}
			else if( typeof b === "number" )
			{ bID = b; }
			// This means b was never passed, so substitute with entire list
			else
			{ bID = listValue() }
			
			return { a:aID, b:bID };
		}
		
		// ==================================
		// = Public Interface for the class =
		// ==================================
		return {
			// matchAny : Returns [number] the number of matches between groups, groupIDs or mixture of the two.
			matchAny : function( a, b ){
				// Translate the params
				var p = translate(a,b);
				// Return number of matches comparison
				// Convert bit operator result to binary, strip the zeros and count the 1s
				return ( p.a & p.b ).toString(2).replace(/0/g,'').length;
			},
			// matchAll :  Returns [boolean] whether or not groups, groupIDs or 
			// mixture of the two match exactly the same
			matchAll : function( a, b ){
				// Translate our parameters
				var p = translate(a,b);
				// Return our boolean
				return (p.a===p.b);
			},
			// matchNone : Returns [boolean] whether or not there are exactly ZERO matches between 
			// groups, groupIDs or mixture of the two.
			matchNone : function(a,b){
				// Translate our parameters
				var p = translate(a,b);
				// return our boolean
				return ( (p.a & p.b)===0 );
			},
			// getTagsID: Takes a collection (array) of tags/labels 
			// and returns that groups value/ID
			getTagsID : function(groupAry){
				// Make sure we were passed an array
				if( !groupAry.length )
					return 0;
				// Our groupID is the BIT SUM of all the elements
				var sum = 0;
				// Iterate through this group to calculate it's identifier
				for(var i=0; i<groupAry.length; i++)
				{
					var el = groupAry[i];
					sum += get(el.toString());
				}
				// Return our groupID
				return sum;
			},
			// getListID: returns the groupID/value of the entire list
			getListID : function(){
				// Relay the private function
				return listValue();
			},
			// ----------------------------------------------------------
			// Temporary functions: Used for debugging only
			getList : function(){ return list; },
			trans : function(a,b){ translate(a,b); }
		};
	})();// END CLASS.BITLIST CLASS
};// END CLASS

