
// usage: log('inside coolFunc', this, arguments);
// paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
window.log = function(){
  log.history = log.history || [];   // store logs to an array for reference
  log.history.push(arguments);
  if(this.console && this.console.log) {
      arguments.callee = arguments.callee.caller;
      console.log( Array.prototype.slice.call(arguments) );
  }
};
// make it safe to use console.log always
(function(b){function c(){}for(var d="assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,time,timeEnd,trace,warn".split(","),a;a=d.pop();)b[a]=b[a]||c})(window.console=window.console||{});


// place any jQuery/helper plugins in here, instead of separate, slower script files.

/*
 * jQuery Templates Plugin 1.0.0pre
 * http://github.com/jquery/jquery-tmpl
 * Requires jQuery 1.4.2
 *
 * Copyright Software Freedom Conservancy, Inc.
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 */
(function(a){var r=a.fn.domManip,d="_tmplitem",q=/^[^<]*(<[\w\W]+>)[^>]*$|\{\{\! /,b={},f={},e,p={key:0,data:{}},i=0,c=0,l=[];function g(g,d,h,e){var c={data:e||(e===0||e===false)?e:d?d.data:{},_wrap:d?d._wrap:null,tmpl:null,parent:d||null,nodes:[],calls:u,nest:w,wrap:x,html:v,update:t};g&&a.extend(c,g,{nodes:[],parent:d});if(h){c.tmpl=h;c._ctnt=c._ctnt||c.tmpl(a,c);c.key=++i;(l.length?f:b)[i]=c}return c}a.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(f,d){a.fn[f]=function(n){var g=[],i=a(n),k,h,m,l,j=this.length===1&&this[0].parentNode;e=b||{};if(j&&j.nodeType===11&&j.childNodes.length===1&&i.length===1){i[d](this[0]);g=this}else{for(h=0,m=i.length;h<m;h++){c=h;k=(h>0?this.clone(true):this).get();a(i[h])[d](k);g=g.concat(k)}c=0;g=this.pushStack(g,f,i.selector)}l=e;e=null;a.tmpl.complete(l);return g}});a.fn.extend({tmpl:function(d,c,b){return a.tmpl(this[0],d,c,b)},tmplItem:function(){return a.tmplItem(this[0])},template:function(b){return a.template(b,this[0])},domManip:function(d,m,k){if(d[0]&&a.isArray(d[0])){var g=a.makeArray(arguments),h=d[0],j=h.length,i=0,f;while(i<j&&!(f=a.data(h[i++],"tmplItem")));if(f&&c)g[2]=function(b){a.tmpl.afterManip(this,b,k)};r.apply(this,g)}else r.apply(this,arguments);c=0;!e&&a.tmpl.complete(b);return this}});a.extend({tmpl:function(d,h,e,c){var i,k=!c;if(k){c=p;d=a.template[d]||a.template(null,d);f={}}else if(!d){d=c.tmpl;b[c.key]=c;c.nodes=[];c.wrapped&&n(c,c.wrapped);return a(j(c,null,c.tmpl(a,c)))}if(!d)return[];if(typeof h==="function")h=h.call(c||{});e&&e.wrapped&&n(e,e.wrapped);i=a.isArray(h)?a.map(h,function(a){return a?g(e,c,d,a):null}):[g(e,c,d,h)];return k?a(j(c,null,i)):i},tmplItem:function(b){var c;if(b instanceof a)b=b[0];while(b&&b.nodeType===1&&!(c=a.data(b,"tmplItem"))&&(b=b.parentNode));return c||p},template:function(c,b){if(b){if(typeof b==="string")b=o(b);else if(b instanceof a)b=b[0]||{};if(b.nodeType)b=a.data(b,"tmpl")||a.data(b,"tmpl",o(b.innerHTML));return typeof c==="string"?(a.template[c]=b):b}return c?typeof c!=="string"?a.template(null,c):a.template[c]||a.template(null,q.test(c)?c:a(c)):null},encode:function(a){return(""+a).split("<").join("&lt;").split(">").join("&gt;").split('"').join("&#34;").split("'").join("&#39;")}});a.extend(a.tmpl,{tag:{tmpl:{_default:{$2:"null"},open:"if($notnull_1){__=__.concat($item.nest($1,$2));}"},wrap:{_default:{$2:"null"},open:"$item.calls(__,$1,$2);__=[];",close:"call=$item.calls();__=call._.concat($item.wrap(call,__));"},each:{_default:{$2:"$index, $value"},open:"if($notnull_1){$.each($1a,function($2){with(this){",close:"}});}"},"if":{open:"if(($notnull_1) && $1a){",close:"}"},"else":{_default:{$1:"true"},open:"}else if(($notnull_1) && $1a){"},html:{open:"if($notnull_1){__.push($1a);}"},"=":{_default:{$1:"$data"},open:"if($notnull_1){__.push($.encode($1a));}"},"!":{open:""}},complete:function(){b={}},afterManip:function(f,b,d){var e=b.nodeType===11?a.makeArray(b.childNodes):b.nodeType===1?[b]:[];d.call(f,b);m(e);c++}});function j(e,g,f){var b,c=f?a.map(f,function(a){return typeof a==="string"?e.key?a.replace(/(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g,"$1 "+d+'="'+e.key+'" $2'):a:j(a,e,a._ctnt)}):e;if(g)return c;c=c.join("");c.replace(/^\s*([^<\s][^<]*)?(<[\w\W]+>)([^>]*[^>\s])?\s*$/,function(f,c,e,d){b=a(e).get();m(b);if(c)b=k(c).concat(b);if(d)b=b.concat(k(d))});return b?b:k(c)}function k(c){var b=document.createElement("div");b.innerHTML=c;return a.makeArray(b.childNodes)}function o(b){return new Function("jQuery","$item","var $=jQuery,call,__=[],$data=$item.data;with($data){__.push('"+a.trim(b).replace(/([\\'])/g,"\\$1").replace(/[\r\t\n]/g," ").replace(/\$\{([^\}]*)\}/g,"{{= $1}}").replace(/\{\{(\/?)(\w+|.)(?:\(((?:[^\}]|\}(?!\}))*?)?\))?(?:\s+(.*?)?)?(\(((?:[^\}]|\}(?!\}))*?)\))?\s*\}\}/g,function(m,l,k,g,b,c,d){var j=a.tmpl.tag[k],i,e,f;if(!j)throw"Unknown template tag: "+k;i=j._default||[];if(c&&!/\w$/.test(b)){b+=c;c=""}if(b){b=h(b);d=d?","+h(d)+")":c?")":"";e=c?b.indexOf(".")>-1?b+h(c):"("+b+").call($item"+d:b;f=c?e:"(typeof("+b+")==='function'?("+b+").call($item):("+b+"))"}else f=e=i.$1||"null";g=h(g);return"');"+j[l?"close":"open"].split("$notnull_1").join(b?"typeof("+b+")!=='undefined' && ("+b+")!=null":"true").split("$1a").join(f).split("$1").join(e).split("$2").join(g||i.$2||"")+"__.push('"})+"');}return __;")}function n(c,b){c._wrap=j(c,true,a.isArray(b)?b:[q.test(b)?b:a(b).html()]).join("")}function h(a){return a?a.replace(/\\'/g,"'").replace(/\\\\/g,"\\"):null}function s(b){var a=document.createElement("div");a.appendChild(b.cloneNode(true));return a.innerHTML}function m(o){var n="_"+c,k,j,l={},e,p,h;for(e=0,p=o.length;e<p;e++){if((k=o[e]).nodeType!==1)continue;j=k.getElementsByTagName("*");for(h=j.length-1;h>=0;h--)m(j[h]);m(k)}function m(j){var p,h=j,k,e,m;if(m=j.getAttribute(d)){while(h.parentNode&&(h=h.parentNode).nodeType===1&&!(p=h.getAttribute(d)));if(p!==m){h=h.parentNode?h.nodeType===11?0:h.getAttribute(d)||0:0;if(!(e=b[m])){e=f[m];e=g(e,b[h]||f[h]);e.key=++i;b[i]=e}c&&o(m)}j.removeAttribute(d)}else if(c&&(e=a.data(j,"tmplItem"))){o(e.key);b[e.key]=e;h=a.data(j.parentNode,"tmplItem");h=h?h.key:0}if(e){k=e;while(k&&k.key!=h){k.nodes.push(j);k=k.parent}delete e._ctnt;delete e._wrap;a.data(j,"tmplItem",e)}function o(a){a=a+n;e=l[a]=l[a]||g(e,b[e.parent.key+n]||e.parent)}}}function u(a,d,c,b){if(!a)return l.pop();l.push({_:a,tmpl:d,item:this,data:c,options:b})}function w(d,c,b){return a.tmpl(a.template(d),c,b,this)}function x(b,d){var c=b.options||{};c.wrapped=d;return a.tmpl(a.template(b.tmpl),b.data,c,b.item)}function v(d,c){var b=this._wrap;return a.map(a(a.isArray(b)?b.join(""):b).filter(d||"*"),function(a){return c?a.innerText||a.textContent:a.outerHTML||s(a)})}function t(){var b=this.nodes;a.tmpl(null,null,null,this).insertBefore(b[0]);a(b).remove()}})(jQuery);

// Simulates PHP's date function
// http://jacwright.com/projects/javascript/date_format/
// Simulates PHP's date function
Date.prototype.format = function(format) {
        var returnStr = '';
        var replace = Date.replaceChars;
        for (var i = 0; i < format.length; i++) {               var curChar = format.charAt(i);                 if (i - 1 >= 0 && format.charAt(i - 1) == "\\") {
                        returnStr += curChar;
                }
                else if (replace[curChar]) {
                        returnStr += replace[curChar].call(this);
                } else if (curChar != "\\"){
                        returnStr += curChar;
                }
        }
        return returnStr;
};

Date.replaceChars = {
        shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
        longMonths: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
        shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
        longDays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],

        // Day
        d: function() { return (this.getDate() < 10 ? '0' : '') + this.getDate(); },
        D: function() { return Date.replaceChars.shortDays[this.getDay()]; },
        j: function() { return this.getDate(); },
        l: function() { return Date.replaceChars.longDays[this.getDay()]; },
        N: function() { return this.getDay() + 1; },
        S: function() { return (this.getDate() % 10 == 1 && this.getDate() != 11 ? 'st' : (this.getDate() % 10 == 2 && this.getDate() != 12 ? 'nd' : (this.getDate() % 10 == 3 && this.getDate() != 13 ? 'rd' : 'th'))); },
        w: function() { return this.getDay(); },
        z: function() { var d = new Date(this.getFullYear(),0,1); return Math.ceil((this - d) / 86400000); }, // Fixed now
        // Week
        W: function() { var d = new Date(this.getFullYear(), 0, 1); return Math.ceil((((this - d) / 86400000) + d.getDay() + 1) / 7); }, // Fixed now
        // Month
        F: function() { return Date.replaceChars.longMonths[this.getMonth()]; },
        m: function() { return (this.getMonth() < 9 ? '0' : '') + (this.getMonth() + 1); },
        M: function() { return Date.replaceChars.shortMonths[this.getMonth()]; },
        n: function() { return this.getMonth() + 1; },
        t: function() { var d = new Date(); return new Date(d.getFullYear(), d.getMonth(), 0).getDate() }, // Fixed now, gets #days of date
        // Year
        L: function() { var year = this.getFullYear(); return (year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)); },       // Fixed now
        o: function() { var d  = new Date(this.valueOf());  d.setDate(d.getDate() - ((this.getDay() + 6) % 7) + 3); return d.getFullYear();}, //Fixed now
        Y: function() { return this.getFullYear(); },
        y: function() { return ('' + this.getFullYear()).substr(2); },
        // Time
        a: function() { return this.getHours() < 12 ? 'am' : 'pm'; },
        A: function() { return this.getHours() < 12 ? 'AM' : 'PM'; },
        B: function() { return Math.floor((((this.getUTCHours() + 1) % 24) + this.getUTCMinutes() / 60 + this.getUTCSeconds() / 3600) * 1000 / 24); }, // Fixed now
        g: function() { return this.getHours() % 12 || 12; },
        G: function() { return this.getHours(); },
        h: function() { return ((this.getHours() % 12 || 12) < 10 ? '0' : '') + (this.getHours() % 12 || 12); },
        H: function() { return (this.getHours() < 10 ? '0' : '') + this.getHours(); },
        i: function() { return (this.getMinutes() < 10 ? '0' : '') + this.getMinutes(); },
        s: function() { return (this.getSeconds() < 10 ? '0' : '') + this.getSeconds(); },
        u: function() { var m = this.getMilliseconds(); return (m < 10 ? '00' : (m < 100 ? '0' : '')) + m; },
        // Timezone
        e: function() { return "Not Yet Supported"; },
        I: function() { return "Not Yet Supported"; },
        O: function() { return (-this.getTimezoneOffset() < 0 ? '-' : '+') + (Math.abs(this.getTimezoneOffset() / 60) < 10 ? '0' : '') + (Math.abs(this.getTimezoneOffset() / 60)) + '00'; },
        P: function() { return (-this.getTimezoneOffset() < 0 ? '-' : '+') + (Math.abs(this.getTimezoneOffset() / 60) < 10 ? '0' : '') + (Math.abs(this.getTimezoneOffset() / 60)) + ':00'; }, // Fixed now
        T: function() { var m = this.getMonth(); this.setMonth(0); var result = this.toTimeString().replace(/^.+ \(?([^\)]+)\)?$/, '$1'); this.setMonth(m); return result;},
        Z: function() { return -this.getTimezoneOffset() * 60; },
        // Full Date/Time
        c: function() { return this.format("Y-m-d\\TH:i:sP"); }, // Fixed now
        r: function() { return this.toString(); },
        U: function() { return this.getTime() / 1000; }
};




$.extend({
  getUrlVars: function(){
    var vars = [], hash;
    var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
    for(var i = 0; i < hashes.length; i++)
    {
      hash = hashes[i].split('=');
      vars.push(hash[0]);
      vars[hash[0]] = hash[1];
    }
    return vars;
  },
  getUrlVar: function(name){
    return $.getUrlVars()[name];
  }
});







/** 
 * Primary initialization method.
 * 
 * @category   Lozal 
 * @package    Application
 * @subpackage Dom
 * @author     Jeffrey Sambells <jsambells@wecreate.com>
 * @copyright  1999-2010 We-Create Inc. 
 * @license    http://developer.wecreate.com/ces/license We-Create Proprietary 
 * @version    SVN: $Id:$ 
 * @since      2.0.0
 */

(function( $, undefined ) {
	
var locationPicker = {
	// CONSTANTS
	CONTINENT:1,
	COUNTRY:2,
	REGION:3,
	COUNTY:4,
	LOCALITY:5,
	SUBURB:6,
	POSTAL:7,
	INTERSECTION:8,
	STREET_ADDRESS:9,
	STREET_ADDRESS_UNIT:10,
	POI:11,
	STREET:12,
	ISLAND:13,
	BUSINESS:14,
	
	// default options
	options: {
		location:null,
		zoom:8,
		mapType:google.maps.MapTypeId.ROADMAP,
		acceptLabel:'Use this location',
		verifyLabel:'Find this location',
		disableKeyBindings:false,
		confirm:function(location) { },
		classes:{
			selectedListItem:'selected'
		}
	},
	
	_startEntity:null,
	_selectedEntity:null,
	
	_map:null,
	_input:null,
	_gMap:null,
	_geocoder:null,
	_marker:null,
	_suggestions:null,
	_list:null,
	_submitButton:null,
	_mapBox:null,
	_mapBlackout:null,
	_form:null,
	_dialogTitle:"Where are you?",
	_create: function() {
		var self = this;

		// creation code for mywidget
		// can use this.options
		if (this.options.location == null) {
			//alert('no location was specificed for the map.');
		}
		
		this._selectedEntity = this.options.location;
		this._startEntity = this._selectedEntity;
		
		this._geocoder = new google.maps.Geocoder();
		
		this._mapBox = new google.maps.Rectangle({
			clickable:false,
			fillColor:"#79c142",
			fillOpacity:0,
			strokeColor:"#79c142",
			strokeOpacity:0.8,
			strokeWeight:3
		});
		this._mapBlackout = new google.maps.Polygon({
			clickable:false,
			strokeWeight: 0,
			fillColor: '#000000',
			fillOpacity: 0.4
		})
		
		
		this._form = $('<form>')
			.submit(function(event) {
				if(self._getInput().val() == self._getInput().previewValue) {
					self._trigger('confirm',event,self._selectedEntity);
					self.element.dialog('close');
				} else {
					self._forwardGeocode( self._getInput().val() );
				}
				return false;
			})
			.appendTo(this.element);
		
		this._getInput().appendTo(this._form);
	
		this._getSubmitButton().appendTo(this._form)
		
		this._map = $('<div id="ui-locationpicker-map" style="width:100%;height:300px;">Map</div>')
			.appendTo(this.element);
			
		this._suggestions = $('<div/>')
			.addClass('suggestions')
			.appendTo(this.element)
		
		this._list = $('<ul/>').appendTo(this._suggestions);
		
		$('<div id="locationHelp"></div>').appendTo(this.element);
		
		this._getMarker().setMap(this._getMap());
		
		// Show the initial location;
		// TODO merge preview into select.		
		
		this.element.dialog({
			minHeight:400,
			minWidth:600,
			modal:true,
			show: 'slide',
			title: this._dialogTitle,
			autoOpen: false,
			open: function(){
				google.maps.event.trigger(self._getMap(), 'resize');
				self._preview(self._selectedEntity, true);
			}
		})
	},
	
	
	_geocodeCallback: function( data, adjustZoom ){
		
//		log("DATA:", data);
		
		if( !data || data.length == 0) { 
			var message = "Could not find location.";
			if (locations.HasField('error') && locations.error.HasField('description')) {
				 message = locations.error.description;
			}
			this.element.parent().addClass('error');
			this.element.dialog({ title: message });
			return;
		}
		
		var self = this;
		
        // clear current binds
        $('a', this._list).each(function(){
        	// Unbind the event.
        	$(this).unbind(); 
        	// Remove the li.
        	$(this).parent().remove();
        });
        
        if (data.length < 2) {
        	//TODO hide the list.
        	if (this._suggestions.is(':visible')) {
        		this._suggestions.hide('fast', function() {
        			$(self._map).animate({
        			    width: '100%'
        			}, 500, function() {
        			    google.maps.event.trigger(self._getMap(), 'resize');
        			});
        		});
        	}
        }
        
        if ( data.length > 1 ) {
        	
        	// Show the suggestion list
        	this._map.animate(
        		{width: '66%'},
        		500, 
        		function() {
        			google.maps.event.trigger(self._getMap(), 'resize');
        	    
        			$.each(data, function(i, location) {
        			
            			var units = 'km';
            			for (var i in location.address_components) {
            			    if (location.address_components[i]['short_name'] == 'US') {
            			        units = 'mi';
            			    } else {
            			        units = 'km';
            			    }
            			}
            			
        				// location is a google geocoded result
        				var li 	= $('<li/>').appendTo( self._list ).click(function(e){
        					self._preview({
        					    latitude:location.geometry.location.lat(),
        					    longitude:location.geometry.location.lng(),
        					    name:location.formatted_address,
                		        units:units
        					}, true);
        					var obj = $(this);
        					obj.siblings().removeClass(self.options.classes.selectedListItem);
        					obj.addClass(self.options.classes.selectedListItem);
        					e.preventDefault();
        					e.stopPropagation();
        				});
        				var a 	= $('<a href="#"></a>').text( location.formatted_address ).appendTo( li ).click(function(e){
                            self._preview({
                                latitude:location.geometry.location.lat(),
                                longitude:location.geometry.location.lng(),
                                name:location.formatted_address,
                                units:units
                            }, true);
        					var obj = $(this).parent();
        					obj.siblings().removeClass(self.options.classes.selectedListItem);
        					obj.addClass(self.options.classes.selectedListItem);
        					return false;
        				});
        			});
        			
        			self._suggestions.css({width:'30%'}).show().fadeIn();
        			
        			$('a', self._list).first().click();
        		
        		}
        	);
        }

		if (data.length == 1) {
		
		    var units = 'km';
    		for (var i in data[0].address_components) {
    		    if (data[0].address_components[i]['short_name'] == 'US') {
    		        units = 'mi';
    		    } else {
    		        units = 'km';
    		    }
    		}
    		
			this._preview({
			    latitude:data[0].geometry.location.lat(),
			    longitude:data[0].geometry.location.lng(),
			    name:data[0].formatted_address,
			    units:units
			}, adjustZoom);					
		} 
		
	},
	
	_forwardGeocode: function( term ) {
//		log('_forwardGeocode: ' + term);

		var self = this;

        self.element.parent().removeClass('error');
        self.element.parent().addClass('loading');
        self.element.dialog({ title: 'Search for matching locations...' });
	
    	this._geocoder.geocode(
    		{'address': term}, 
    		function(results, status) {
    			if (status == google.maps.GeocoderStatus.OK) {
    				self._geocodeCallback(results, true);
    			} else {
    	          self.element.parent().addClass('error');
    			  self.element.dialog({ title: 'There was an error with the request. Please try again.' });
    			}
    			
    			self.element.parent().removeClass('loading');
    			if (!self.element.parent().hasClass('error')) { self.element.dialog({ title: self._dialogTitle }); }
    			
    		}
    	);
			
		return this;
	},
	
	_preview: function( entity, adjustZoom ) {
//		log( "_preview:", entity );
		
		this._selectedEntity = entity;
		
		// Update the input	
		this._getInput().previewValue = entity.name;
		this._getInput().val( this._getInput().previewValue );
		this._checkForChanges();
		
		if (entity.latitude && entity.longitude) {
			var ll = new google.maps.LatLng(
					entity.latitude, 
					entity.longitude
				);
		} else {
			var ll = new google.maps.LatLng(0,0);
		}
		
		if (entity.type == this.LOCALITY) {

			// Reposition the box and remove the marker.
			this._getMarker().setMap(null);
			this._drawBoxOnMap( ll, 0.05 );
			$('#locationHelp').text('Search for an address or click anywhere on the map to set your exact location.');
			
			this._getMap().panTo(ll);
			if (adjustZoom) {
				this._getMap().setZoom(11);
			}
			// Set the zoom level.
			//var llBounds = new google.maps.LatLngBounds(
			//  new google.maps.LatLng( ll.lat() + 0.06, ll.lng() - 0.06 ),
			//  new google.maps.LatLng( ll.lat() - 0.06, ll.lng() + 0.06 )
			//);
			//this._getMap().panToBounds(llBounds);

		} else {

			// Remove the box and reposition the marker.
			this._drawBoxOnMap( ll, 0 );
			this._getMarker().setMap(this._getMap());
			this._getMarker().setPosition(ll);
			this._getMap().panTo(ll);
			$('#locationHelp').text('Search for addresses, postal codes, landmarks and phone numbers or just drag the marker around.');
		
			// Set the zoom level
			var zoom;
			switch(entity.type) {
				case this.CONTINENT:
				case this.COUNTRY:
					zoom = 2;
					break;
				case this.REGION:
				case this.COUNTY:
					zoom = 3;
					break;
				case this.SUBURB:
					zoom = 14;
					break;
				
				case this.POSTAL:
				case this.INTERSECTION:
				case this.STREET:
				case this.ISLAND:
					zoom = 15;
					break;
				case this.BUSINESS:
				case this.STREET_ADDRESS:
				case this.STREET_ADDRESS_UNIT:
				case this.POI:
					zoom = 16;
					break;
				default:			
					zoom = 11;
					break;
			}
			if (adjustZoom) {
				if (zoom > 0) this._getMap().setZoom(zoom);
			}
		}
		
	},
	
	_drawBoxOnMap: function( latlng, offset ) {

		if (offset <= 0) {
			this._mapBlackout.setMap(null);
			this._mapBox.setMap( null );
			return;
		}

//		log("_drawBoxOnMap", latlng, offset);
		
		
		// Reversing the order of these points is important!
		// @TODO the first set should be based on the maps viewport
		var paths = [[
			new google.maps.LatLng(latlng.lat() + offset + 20, latlng.lng() - offset - 20), //tl
			new google.maps.LatLng(latlng.lat() + offset + 20, latlng.lng() + offset + 20),  //tr
			new google.maps.LatLng(latlng.lat() - offset - 20, latlng.lng() + offset + 20), //br
			new google.maps.LatLng(latlng.lat() - offset - 20, latlng.lng() - offset - 20) //bl
		], [
			new google.maps.LatLng(latlng.lat() + offset, latlng.lng() + offset),  //tr
			new google.maps.LatLng(latlng.lat() + offset, latlng.lng() - offset), //tl
			new google.maps.LatLng(latlng.lat() - offset, latlng.lng() - offset), //bl
			new google.maps.LatLng(latlng.lat() - offset, latlng.lng() + offset) //br
		]];

		this._mapBlackout.setPaths( paths );
		this._mapBlackout.setMap( this._getMap() );
		
		
		var llBounds = new google.maps.LatLngBounds(
		  new google.maps.LatLng( latlng.lat() + offset, latlng.lng() - offset ),
		  new google.maps.LatLng( latlng.lat() - offset, latlng.lng() + offset )
		);
		
//		log("box", llBounds);
		
		this._mapBox.setBounds(llBounds);
		this._mapBox.setMap( this._getMap() );
		
	},
		
	_checkForChanges: function() {
	
		if (this._getInput().val() == this._getInput().previewValue) {
			this._getSubmitButton().val('Use this location');
		} else {
			this._getSubmitButton().val('Lookup this location');
		}
	
	},
	
	_getInput: function() {
		
		if (this._input == null) {
		
			this._input = $('<input type="text">').addClass('ui-locationpicker-term');
		
			if (this.options.disableKeyBindings == false) {
			
				var self = this;
				
				// Check keydown and re-open or commit if necessary.
				this._input.keydown(function(event) {
						
					if (event.which == 38 || event.which == 40) {

						// Up and Down
						if (event.which == 38) {
//						    log(self.options.classes.selectedListItem);
							// Up
							$('li.' + self.options.classes.selectedListItem ,this._list)
								.prev()
								.find('a')
								.click();
						} else if (event.which == 40) {
							// Down
							$('li.' + self.options.classes.selectedListItem,this._list)
								.next()
								.find('a')
								.click();
						}
						
						// Scroll to the appropriate one.
						var selected = $('li.' + self.options.classes.selectedListItem,this._list);
						selected.parent().scrollTo( selected, 200, {offset:-100} );
					
						// Stops the arrow keys from propagating
						event.preventDefault();
						event.stopPropagation();
					
					}
					
					
				});
					
				// On keyup modify the ui for verification purposes.
				this._input.change(function(e){
					self._checkForChanges();
				});
				this._input.keyup(function(event){
					self._checkForChanges();
				});
				
			}
		}
		return this._input;
	
	},
	
	_getSubmitButton: function() {
		
		if (this._submitButton == null) {
			this._submitButton = $('<input type="submit" value="Search">')
				.addClass('ui-locationpicker-submit');
		}
		return this._submitButton;
	
	},
	
	_getMarker: function() {
	
		if (this._marker == null) {
		
			var self = this;
			
			var markerImage = new google.maps.MarkerImage(
				'/css/i/1.png',
				// This marker is 55 pixels wide by 70 pixels tall.
				new google.maps.Size(44, 60),
				// The origin for this image is 0,0.
				new google.maps.Point(0,0),
				// The anchor for this image is the base of the drop.
				new google.maps.Point(22, 58)
			);
		
			var markerShadow = new google.maps.MarkerImage(
				'/css/i/shadow.png',
				// The shadow image is larger in the horizontal dimension
				// while the position and offset are the same as for the main image.
				new google.maps.Size(100, 60),
				new google.maps.Point(0,0),
				new google.maps.Point(22, 58)
			);
			
			// Shapes define the clickable region of the icon.
			// The type defines an HTML &lt;area&gt; element 'poly' which
			// traces out a polygon as a series of X,Y points. The final
			// coordinate closes the poly by connecting to the first
			// coordinate.
			var markerShape = {
				coord: [ 22,60, 11,43, 0,43, 0,0, 44,0, 44,43, 33,43 ],
				type: 'poly'
			};
			
			if (this._startEntity.latitude && this._startEntity.longitude) {
				var ll = new google.maps.LatLng(
					this._startEntity.latitude, 
					this._startEntity.longitude
				);
			} else {
				var ll = new google.maps.LatLng(0,0);
			}
			
			this._marker = new google.maps.Marker({
			  position:ll,
				title:"You are here!",
				draggable: true,
				icon: markerImage,
				shadow: markerShadow,
				shape: markerShape
			});
			
			google.maps.event.addListener(
				this._marker,
				'dragstart', 
				function() {
				}
			);
			
			google.maps.event.addListener(
				this._marker,
				'dragend', 
				function(e) {
					var latlng = self._getMarker().getPosition();
					self._geocoder.geocode(
						{'latLng': latlng}, 
						function(results, status) {
							if (status == google.maps.GeocoderStatus.OK) {
								self._geocodeCallback(results, false);
							}
						}
					);    
				}
			);
		}
		return this._marker;

	},
	
	_getMap: function() {
	
		if (this._gMap == null) {
		
			var self = this;
			
			if (this._startEntity.location && this._startEntity.location.point) {
				var ll = new google.maps.LatLng(
					this._startEntity.latitude, 
					this._startEntity.longitude
				);
			} else {
				var ll = new google.maps.LatLng(0,0);
			}
			
			this._gMap = new google.maps.Map(
				document.getElementById("ui-locationpicker-map"), 
				{
					zoom: this.options.zoom,
					center: ll,
					mapTypeId: this.options.mapType
				}
			);
			
			google.maps.event.addListener(
				this._gMap, 
				'click', 
				function(e) {
					var latlng = e.latLng;
					self._geocoder.geocode(
						{'latLng': latlng}, 
						function(results, status) {
							if (status == google.maps.GeocoderStatus.OK) {
								self._geocodeCallback(results, false);
							}
						}
					);
				}
			);
		}
		return this._gMap;
	
	},
	
	open: function() {
		this.element.dialog('open');
	},
		
	destroy: function() {
		$.Widget.prototype.destroy.apply(this, arguments); // default destroy
		// now do other stuff particular to this widget
	}
	
 };

$.widget("ui.locationPicker",locationPicker);

$.extend( $.ui.locationPicker, {
	
	constantValue: function( id ) {
		// TODO check that id is a name of a constant.
		return locationPicker[id];
	},
	
	prefixForType: function( type ) {
		switch(type) {
			case locationPicker.CONTINENT:
			case locationPicker.COUNTRY:
			case locationPicker.REGION:
			case locationPicker.COUNTY:
			case locationPicker.LOCALITY:
				return 'somewhere in';
			case locationPicker.SUBURB:
			case locationPicker.POSTAL:
				return 'around';
			case locationPicker.INTERSECTION:
			case locationPicker.STREET_ADDRESS:
			case locationPicker.STREET_ADDRESS_UNIT:
			case locationPicker.BUSINESS:
				return 'at';
			case locationPicker.POI:
				return 'near';
			case locationPicker.STREET:
			case locationPicker.ISLAND:
				return 'on';
			default:
				return '';
				break;
		}
	}
});

}( jQuery ));
 
 
 /*
  * jQuery.ScrollTo
  * Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
  * Dual licensed under MIT and GPL.
  * Date: 5/25/2009
  *
  * @projectDescription Easy element scrolling using jQuery.
  * http://flesler.blogspot.com/2007/10/jqueryscrollto.html
  * Works with jQuery +1.2.6. Tested on FF 2/3, IE 6/7/8, Opera 9.5/6, Safari 3, Chrome 1 on WinXP.
  *
  * @author Ariel Flesler
  * @version 1.4.2
  *
  * @id jQuery.scrollTo
  * @id jQuery.fn.scrollTo
  * @param {String, Number, DOMElement, jQuery, Object} target Where to scroll the matched elements.
  *	  The different options for target are:
  *		- A number position (will be applied to all axes).
  *		- A string position ('44', '100px', '+=90', etc ) will be applied to all axes
  *		- A jQuery/DOM element ( logically, child of the element to scroll )
  *		- A string selector, that will be relative to the element to scroll ( 'li:eq(2)', etc )
  *		- A hash { top:x, left:y }, x and y can be any kind of number/string like above.
  *		- A percentage of the container's dimension/s, for example: 50% to go to the middle.
  *		- The string 'max' for go-to-end. 
  * @param {Number} duration The OVERALL length of the animation, this argument can be the settings object instead.
  * @param {Object,Function} settings Optional set of settings or the onAfter callback.
  *	 @option {String} axis Which axis must be scrolled, use 'x', 'y', 'xy' or 'yx'.
  *	 @option {Number} duration The OVERALL length of the animation.
  *	 @option {String} easing The easing method for the animation.
  *	 @option {Boolean} margin If true, the margin of the target element will be deducted from the final position.
  *	 @option {Object, Number} offset Add/deduct from the end position. One number for both axes or { top:x, left:y }.
  *	 @option {Object, Number} over Add/deduct the height/width multiplied by 'over', can be { top:x, left:y } when using both axes.
  *	 @option {Boolean} queue If true, and both axis are given, the 2nd axis will only be animated after the first one ends.
  *	 @option {Function} onAfter Function to be called after the scrolling ends. 
  *	 @option {Function} onAfterFirst If queuing is activated, this function will be called after the first scrolling ends.
  * @return {jQuery} Returns the same jQuery object, for chaining.
  *
  * @desc Scroll to a fixed position
  * @example $('div').scrollTo( 340 );
  *
  * @desc Scroll relatively to the actual position
  * @example $('div').scrollTo( '+=340px', { axis:'y' } );
  *
  * @dec Scroll using a selector (relative to the scrolled element)
  * @example $('div').scrollTo( 'p.paragraph:eq(2)', 500, { easing:'swing', queue:true, axis:'xy' } );
  *
  * @ Scroll to a DOM element (same for jQuery object)
  * @example var second_child = document.getElementById('container').firstChild.nextSibling;
  *			$('#container').scrollTo( second_child, { duration:500, axis:'x', onAfter:function(){
  *				alert('scrolled!!');																   
  *			}});
  *
  * @desc Scroll on both axes, to different values
  * @example $('div').scrollTo( { top: 300, left:'+=200' }, { axis:'xy', offset:-20 } );
  */
 ;(function( $ ){
 	
 	var $scrollTo = $.scrollTo = function( target, duration, settings ){
 		$(window).scrollTo( target, duration, settings );
 	};
 
 	$scrollTo.defaults = {
 		axis:'xy',
 		duration: parseFloat($.fn.jquery) >= 1.3 ? 0 : 1
 	};
 
 	// Returns the element that needs to be animated to scroll the window.
 	// Kept for backwards compatibility (specially for localScroll & serialScroll)
 	$scrollTo.window = function( scope ){
 		return $(window)._scrollable();
 	};
 
 	// Hack, hack, hack :)
 	// Returns the real elements to scroll (supports window/iframes, documents and regular nodes)
 	$.fn._scrollable = function(){
 		return this.map(function(){
 			var elem = this,
 				isWin = !elem.nodeName || $.inArray( elem.nodeName.toLowerCase(), ['iframe','#document','html','body'] ) != -1;
 
 				if( !isWin )
 					return elem;
 
 			var doc = (elem.contentWindow || elem).document || elem.ownerDocument || elem;
 			
 			return $.browser.safari || doc.compatMode == 'BackCompat' ?
 				doc.body : 
 				doc.documentElement;
 		});
 	};
 
 	$.fn.scrollTo = function( target, duration, settings ){
 		if( typeof duration == 'object' ){
 			settings = duration;
 			duration = 0;
 		}
 		if( typeof settings == 'function' )
 			settings = { onAfter:settings };
 			
 		if( target == 'max' )
 			target = 9e9;
 			
 		settings = $.extend( {}, $scrollTo.defaults, settings );
 		// Speed is still recognized for backwards compatibility
 		duration = duration || settings.speed || settings.duration;
 		// Make sure the settings are given right
 		settings.queue = settings.queue && settings.axis.length > 1;
 		
 		if( settings.queue )
 			// Let's keep the overall duration
 			duration /= 2;
 		settings.offset = both( settings.offset );
 		settings.over = both( settings.over );
 
 		return this._scrollable().each(function(){
 			var elem = this,
 				$elem = $(elem),
 				targ = target, toff, attr = {},
 				win = $elem.is('html,body');
 
 			switch( typeof targ ){
 				// A number will pass the regex
 				case 'number':
 				case 'string':
 					if( /^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(targ) ){
 						targ = both( targ );
 						// We are done
 						break;
 					}
 					// Relative selector, no break!
 					targ = $(targ,this);
 				case 'object':
 					// DOMElement / jQuery
 					if( targ.is || targ.style )
 						// Get the real position of the target 
 						toff = (targ = $(targ)).offset();
 			}
 			$.each( settings.axis.split(''), function( i, axis ){
 				var Pos	= axis == 'x' ? 'Left' : 'Top',
 					pos = Pos.toLowerCase(),
 					key = 'scroll' + Pos,
 					old = elem[key],
 					max = $scrollTo.max(elem, axis);
 
 				if( toff ){// jQuery / DOMElement
 					attr[key] = toff[pos] + ( win ? 0 : old - $elem.offset()[pos] );
 
 					// If it's a dom element, reduce the margin
 					if( settings.margin ){
 						attr[key] -= parseInt(targ.css('margin'+Pos)) || 0;
 						attr[key] -= parseInt(targ.css('border'+Pos+'Width')) || 0;
 					}
 					
 					attr[key] += settings.offset[pos] || 0;
 					
 					if( settings.over[pos] )
 						// Scroll to a fraction of its width/height
 						attr[key] += targ[axis=='x'?'width':'height']() * settings.over[pos];
 				}else{ 
 					var val = targ[pos];
 					// Handle percentage values
 					attr[key] = val.slice && val.slice(-1) == '%' ? 
 						parseFloat(val) / 100 * max
 						: val;
 				}
 
 				// Number or 'number'
 				if( /^\d+$/.test(attr[key]) )
 					// Check the limits
 					attr[key] = attr[key] <= 0 ? 0 : Math.min( attr[key], max );
 
 				// Queueing axes
 				if( !i && settings.queue ){
 					// Don't waste time animating, if there's no need.
 					if( old != attr[key] )
 						// Intermediate animation
 						animate( settings.onAfterFirst );
 					// Don't animate this axis again in the next iteration.
 					delete attr[key];
 				}
 			});
 
 			animate( settings.onAfter );			
 
 			function animate( callback ){
 				$elem.animate( attr, duration, settings.easing, callback && function(){
 					callback.call(this, target, settings);
 				});
 			};
 
 		}).end();
 	};
 	
 	// Max scrolling position, works on quirks mode
 	// It only fails (not too badly) on IE, quirks mode.
 	$scrollTo.max = function( elem, axis ){
 		var Dim = axis == 'x' ? 'Width' : 'Height',
 			scroll = 'scroll'+Dim;
 		
 		if( !$(elem).is('html,body') )
 			return elem[scroll] - $(elem)[Dim.toLowerCase()]();
 		
 		var size = 'client' + Dim,
 			html = elem.ownerDocument.documentElement,
 			body = elem.ownerDocument.body;
 
 		return Math.max( html[scroll], body[scroll] ) 
 			 - Math.min( html[size]  , body[size]   );
 			
 	};
 
 	function both( val ){
 		return typeof val == 'object' ? val : { top:val, left:val };
 	};
 
 })( jQuery );
 
 
/**
 * jQuery Cookie plugin
 *
 * Copyright (c) 2010 Klaus Hartl (stilbuero.de)
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 */
jQuery.cookie = function (key, value, options) {

    // key and at least value given, set cookie...
    if (arguments.length > 1 && String(value) !== "[object Object]") {
        options = jQuery.extend({}, options);

        if (value === null || value === undefined) {
            options.expires = -1;
        }

        if (typeof options.expires === 'number') {
            var days = options.expires, t = options.expires = new Date();
            t.setDate(t.getDate() + days);
        }

        value = String(value);

        return (document.cookie = [
            encodeURIComponent(key), '=',
            options.raw ? value : encodeURIComponent(value),
            options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
            options.path ? '; path=' + options.path : '',
            options.domain ? '; domain=' + options.domain : '',
            options.secure ? '; secure' : ''
        ].join(''));
    }

    // key and possibly options given, get cookie...
    options = value || {};
    var result, decode = options.raw ? function (s) { return s; } : decodeURIComponent;
    return (result = new RegExp('(?:^|; )' + encodeURIComponent(key) + '=([^;]*)').exec(document.cookie)) ? decode(result[1]) : null;
};



