/**
 * author: Daniel Caldeweyher (daniel@pdslive.com.au)
 * company: Property Data Solutions Pty. Ltd.
 * PDS Core
 * 
 * Example Usage (during loadup):
 * PDS.Util.loadResources(PDS.getRequiredResources({json:true,dimensions:true,format:true}));
 */
(function($j){
window.PDS = window.PDS || {};
var $ = PDS.byId = PDS.$ = function(id) {
	var elements = new Array();
	for (var i = 0; i < arguments.length; i++) {
		var element = arguments[i];
		if (typeof element == 'string')
			element = document.getElementById(element);
		if (arguments.length == 1)
			return element;
		elements.push(element);
	}
	return elements;
};
window.$ = window.$ || PDS.$;
	
var loaded = false;
$j(function(){loaded=true;})

$j.fn.showHide = function(value) {
	this.css("display", value ? "" : "none");
	return this;
};
$j.fn.combinedHeight = function(includeMargin) {
	var height = 0;
	this.each(function(){height += $j(this).outerHeight(includeMargin);});
	return height;
};
$j.fn.toggler = function(selector) {
	this.toggleClass("pds-toggler-on",$j(selector).is(":visible")).click(function(){
		$j(this).toggleClass("pds-toggler-on",$j(selector).toggle().is(":visible"));
	}).css("cursor","pointer");
	return this;
};
$j.fn.PFSlider = function(options) {
	return this.slider({
		value: (options.syncField ? $j($(options.syncField)).val() : options.value || 3),
		min: options.min || 1,
		max: options.max || 5,
		step: options.step || 1,
		animate: true,
		slide: function(event, ui) {
			if(options.syncField) {
				$j($(options.syncField)).val(ui.value);
			}
			if(options.onchange) {
				options.onchange(event, ui);
			}
		}
	})
	.toggleClass('pf-slider-ticks', options.ticks === true)
	.toggleClass('pf-slider-blue', options.colour === 'blue')
	.append("<div class='pf-slider-inner'><div class='pf-slider-trackstart'><div class='pf-slider-trackend'><div class='pf-slider-track'/></div></div></div>")
	.wrap("<div class='pf-slider-container'/>");
};

function log(msg) {
	if(window.console && window.console.log) {
		console.log(msg);
	}
}

PDS.Class = function() {
	var sources = [{}];
	for(var i=0;i<arguments.length; i++) {
		if(typeof arguments[i] == "function") {
			sources.push(arguments[i].prototype);
		} else {
			sources.push(arguments[i]);
		}
	}
	PDS.Util.extend.apply(this,sources);
	var clazz = function() {
		if(this.initialize) this.initialize.apply(this,arguments);
	};
	clazz.prototype = sources[0];
	return clazz;
};
	
PDS.Util = {
	//Must be called with at least 2 arguments
	extend: function(dest, source) {
		return $j.extend.apply($j,arguments);
	},
	clone: function(obj,deep) {
		return obj ? this.extend(deep==true,{},obj) : null;
	},
	copy: function(dest, source, properties) {
		for(var i in properties) {
			var prop = properties[i];
			if(source[prop]) {
				dest[prop] = source[prop];
			}
		}
		return dest;
	},
	coalesce: function(value, ifNull) {
		for(var i=0;i<arguments.length-1;i++) {
			if(arguments[i]) return arguments[i];
		}
		return undefined;
	},
	parseBoolean: function(value) {
		return typeof value === "boolean" 
			? value 
			: isNaN(parseInt(value))  
				? value.toString().toLowerCase() === "true" 
				: new Boolean(parseInt(value)).valueOf();
	},	
	createCallback: function(scope, fn) {
		if(arguments.length == 1 && typeof scope == "function") {
			fn = scope;
			scope = window;
		}
		return function() {
			fn.apply(scope,arguments);
		};
	},
	//only works while page is loading, use jQuery.getScript once document.load has fired
	loadResources: function(resources, callback) {
		var h = document.getElementsByTagName("head").length ? 
				document.getElementsByTagName("head")[0] : 
				document.body;
		var numLoading = 0;
		$j(resources).each(function() {
			var resource = this;
			if(resource.type == "text/javascript") {
				//$j.getScript(this.src,this.callback);
				//var script = $j("<script/>").attr(resource);
				//if(!loaded)
				//document.write("<script type='text/javascript' src='"+resource.src+"'><\/script>");
				var script = document.createElement("script");
				script.type = "text/javascript";
				script.src = resource.src;
				if(callback) {
					script.onload = script.onreadystatechange = function(){
						if (!this.readyState || this.readyState == "loaded" || this.readyState == "complete") {
							//log("numLoading="+(numLoading-1));
							if(--numLoading == 0) {
								callback({ready:true});
							}
							// Handle memory leak in IE
							script.onload = script.onreadystatechange = null;
							//head.removeChild( script );
						}
					};
					//$j(h).append(script);
					numLoading++;
					//log("numLoading="+numLoading);			
				}
				h.appendChild(script);
			} else if(this.type == "text/css") {
				$j(h).append($j("<link/>").attr("rel","stylesheet").attr(this));
			}
		});
		if(numLoading == 0 && callback) callback({ready:true});
	},
	destroyObject: function(obj) {
		var toDestroy = [obj];
		while(toDestroy.length > 0) {
			var obj = toDestroy.shift();
			this._destroyObjectRecursive(obj,toDestroy);				
		}
	},
	_currentlyDestroying: [],
	_destroyObjectRecursive: function(obj,destroyQueue) {
		if(!obj || typeof obj != "object" || PDS.Array.indexOf(this._currentlyDestroying,obj) >= 0 || obj == window || obj == document || obj.nodeType) {
			return;
		}
		this._currentlyDestroying.push(obj);
		if(obj.destroy) obj.destroy();
		var toDestroy = [];
		for(var x in obj) {
			var property = obj[x];
			if(property && typeof property != "function") {
				if(property.constructor == Array) {
					destroyQueue.push(property);
				} else if(property.destroy) {
					try { 
						property.destroy();
					} catch(e){
						//ignore?
					}
				} else if(property.constructor == Object) {
					destroyQueue.push(property);
				}
				obj[x] == null;
			}
		}
		this._currentlyDestroying.pop();
	},
	_destroyOnUnload: null,
	destroy: function() {
		if(this._destroyOnUnload) {
			this._destroyObject(this._destroyOnUnload);	
			this._destroyOnUnload = null;
		}
	},
	destroyOnUnload: function(obj) {
		if(!this._destroyOnUnload) {
			this._destroyOnUnload = [];
			$j(window).bind("unload", PDS.Util.createCallback(this,this.destroy));
		}
		this._destroyOnUnload.push(obj);
	},
	
	/*
	 * returns the url script of the currently loading script.
	 * must be called in the BODY of the script file, BEFORE any other dynamic 'add script' calls 
	 */
	getCurrentScriptUrl: function(scriptName) {
		var scripts = document.getElementsByTagName("script");
		var src = null;
		for(var i=scripts.length - 1; i>=0;i--) {
			if(!scriptName || scripts[i].src.toLowerCase().indexOf(scriptName.toLowerCase()) >= 0) {
				src = scripts[i].src;
				break;
			}
		}
		if(src.indexOf("http") != 0) src = this.getRelativeUrl(documentLocation,src);
		return this.parseUri(src);
	},
	
	getRelativeUrl: function(parseUriObj, path) {
		return parseUriObj.protocol + "://" + parseUriObj.authority + parseUriObj.directory + path;
	},
	
	getQueryParam: function(url,param) {
		if(!param) {
			param = url;
			url = document.location.href;
		}
		var match = url.match(new RegExp("[&?]"+param+"=([^$&]*)"));
		return match ? match[1] : null;
	}
};

PDS.Data = {
	extractPropertyIdCriteria: function(srcPropertyId,srcAddressId,propertyId) {
		if(typeof arguments[0] == "object") {
			return PDS.Util.copy({},arguments[0],['propertyId','srcPropertyId','srcAddressId','state']);
		} else {
			return {srcPropertyId:srcPropertyId,srcAddressId:srcAddressId,propertyId:propertyId};
		}
	}
};

(function(){
	/*
	 * parseUri 1.2.1
	 * (c) 2007 Steven Levithan <stevenlevithan.com>
	 * MIT License
	 * source: http://blog.stevenlevithan.com/archives/parseuri
	 */	
	var parseUriOptions = {
		strictMode: false,
		key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
		q:   {
			name:   "queryKey",
			parser: /(?:^|&)([^&=]*)=?([^&]*)/g
		},
		parser: {
			strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
			loose:  /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
		}
	};	
	
	PDS.Util.parseUri = function(str) {
		str = str || document.location.href;
		var	o   = parseUriOptions,
			m   = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
			uri = {},
			i   = 14;
	
		while (i--) uri[o.key[i]] = m[i] || "";
	
		uri[o.q.name] = {};
		uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
			if ($1) uri[o.q.name][$1] = $2;
		});
	
		return uri;
	};
})();

var documentLocation = PDS.Util.parseUri(document.location.href);
var documentReferrer = PDS.Util.parseUri(document.referrer);
var scriptLocation = PDS.Util.getCurrentScriptUrl();

PDS.Http = {
	getReferrerHost: function() {
		return documentReferrer.host;
	},
	getCacheBuster: function(expiry,param) {
		param = param || 'v';
		var now = new Date();
		if((expiry=='build' || expiry=='version') && scriptLocation.queryKey['v']) {
			return param+'='+scriptLocation.queryKey['v'];
		} else if(expiry=='day') {
			return param+'='+now.getYear()+now.getMonth()+now.getDate();		
		} else if(expiry=='hour') {
			return param+'='+now.getYear()+now.getMonth()+now.getDate()+now.getHours();		
		} else {
			return param+'='+now.getTime();
		}
	}
}

PDS.getRequiredResources = function(options) {
	options = options || PDS.options || {};
	var required = [];
	if(!$j.toJSON && options.json) {
		required.push({type:"text/javascript",src:PDS.Util.getRelativeUrl(scriptLocation,"../jquery.json-2.2.min.js")});
	}
	if(!$j.dimensions && options.dimensions) {
		required.push({type:"text/javascript",src:PDS.Util.getRelativeUrl(scriptLocation,"../jquery.dimensions.pack.js")});
	}
	if((!PDS.Format || !Number.prototype.numberFormat) && options.format) {
		required.push({type:"text/javascript",src:PDS.Util.getRelativeUrl(scriptLocation,"pds.format.js")});
	}
	if((!window.p$ || !window.pure) && options.templating) {
		required.push({type:"text/javascript",src:PDS.Util.getRelativeUrl(scriptLocation,"../pure/libs/pure2_packed.js")});
	}
	if(!$j.autocomplete && options.autocomplete) {
		required.push({type:"text/javascript",src:PDS.Util.getRelativeUrl(scriptLocation,"jquery.autocomplete.js")});
		required.push({type:"text/css",href:PDS.Util.getRelativeUrl(scriptLocation,"../../css/jquery/pds.jquery.address.autocompleter.css")});
	}
	return required;
};

PDS.Json = {
	asQueryParams: function(json,leadingAmpersand) {
		var queryFragment = "";
		for(x in json) {
			if(json[x] != null && typeof json[x] != "function") {
				if(queryFragment.length > 0 || leadingAmpersand || leadingAmpersand == undefined || leadingAmpersand == null) queryFragment += "&";
				queryFragment += x+"="+escape(unescape(json[x].toString()));
			}
		}
		return queryFragment;
	},	
	//Serializes a javascript object, number, string, or arry into JSON.
	serialize: function(jsObj) {
		return $j.toJSON(jsObj);
	},
	//Converts from JSON to Javascript, quickly, and is trivial.
	deserialize: function(json,secure) {
		return !secure ? $j.evalJSON(json) : $j.secureEvalJSON(json);
	},
	//Converts from JSON to Javascript, but does so while checking to see if the source is actually JSON, 
	//and not with otherJavascript statements thrown in.
	deserializeSecure: function(json) {
		return this.deserialize(json,true);
	}
};
	
PDS.Link = {
	// /pds/app?antiAlias=true&contentType=image%2Fjpeg&fsImageId=98&fsScheme=PDS&height=40&lastModified=1233019898922&service=image&userId=12812&width=54
	image: function(image,panel,userId) {
		var url = "app?service=image";
		if(image) url += PDS.Json.asQueryParams(image, true);
		if(panel) url += PDS.Json.asQueryParams(panel, true)+"&contentType=mime/"+panel.imageType;
		if(userId) url += "&userId="+userId;
		return url;
	}
};

PDS.Html = {
	nodeAttributesToJson: function(namedNodeMap) {
		var json = {};
		for(var i=0;i<namedNodeMap.length;i++) {
			json[namedNodeMap[i].nodeName] = namedNodeMap[i].nodeValue;
		}
		return json;
	},
	redirect: function(url,target) {
		target = target || document;
		target.location = url;
	}
};

PDS.Object = PDS.Class({
	initialize: function(options) {
		if(options && typeof(options) == "object") {
			PDS.Util.extend(this,options);
		}
	},
	serialize: function() {
		return PDS.Json.serialize(this);
	},
	clone: function() {
		return PDS.Util.extend({},this);
	}	
});

PDS.Component = PDS.Class(PDS.Object, {
	PDSMapPane: null,
	PDSJsonClient: null,
	getMapPane: function() {
		return this.PDSMapPane;
	},
	getJsonClient: function() {
		return this.PDSJsonClient;
	},
	createCallback: function(fn) {
		return PDS.Util.createCallback(this,fn);
	}
});

PDS.Colour = PDS.Class(PDS.Object, {
	r:null,
	g:null,
	b:null,
	initialise: function(r,g,b) {
		if(arguments.length = 1 && typeof r == "object") {
			PDS.Util.extend(this,r);
		} else if(arguments.length = 1 && typeof r == "string") {
			PDS.Util.extend(this,this.parseHex(r));
		} else {
			this.r = r;this.g=g;this.b=b;
		}
	},
    //e.g. #ff0000 -> {r:255,g:0,b:0}
    parseHex: function(hex) {
    	if(hex[0] == "#") {
    		return new PDS.Colour(parseInt(c.slice(1,3),16), parseInt(c.slice(3,5),16), parseInt(c.slice(5,7),16));
    	} else {
    		return new PDS.Colour(parseInt(c.slice(0,2),16), parseInt(c.slice(2,4),16), parseInt(c.slice(4,6),16));
    	}
    },
	toHex: function() {
		return "#"+this.r.toString(16)+this.g.toString(16)+this.b.toString(16);
	},
	isDark: function() {
		return ((this.r + this.g + this.b) / 3) < 100;
	},
	isLight: function() {
		return !this.isDark();
	}
});

PDS.Image = {
	Panel: PDS.Class(PDS.Object, {
		width: null,
		height: null,
		imageType: null,
		initialize: function(width,height,imageType) {
			if(arguments.length == 1 && typeof arguments[0] == "object" ) {
				PDS.Util.extend(this,arguments[0]);
			} else {
				this.setSize(width,height);
				this.imageType = imageType || "jpeg";
			}
		},
		setSize: function(width,height) {
			this.width = width;
			this.height = height;
		}			
	})
};
PDS.Image.Sizes = {
	"max": new PDS.Image.Panel(),
	"very-large": new PDS.Image.Panel(640, 480),
	"large": new PDS.Image.Panel(480, 360),
	"thumbnail": new PDS.Image.Panel(128, 96),
	"small-thumbnail": new PDS.Image.Panel(54, 40)
};

PDS.Range = PDS.Class(PDS.Object, {
	min: null,
	max: null,
	initialize: function(min, max) {
		if(arguments.length == 1 && typeof arguments[0] == "object" ) {
			PDS.Util.extend(this,arguments[0]);
		} else {
			this.min = min;
			this.max = max;
		}
	},
	between: function(value) {
		return value >= this.min && value <= this.max;
	},
	extend: function(value) {
		this.min = Math.min(value);
		this.max = Math.max(value);
	},
	limit: function(value) {
		return Math.min(Math.max(value,this.min),this.max);
	}
});

PDS.Array = {
	forEach: function(arr,fun,scope) {
	    var len = arr.length;
	    if (typeof fun != "function")
	      throw new TypeError();
	
	    var thisp = scope;
	    for (var i = 0; i < len; i++) {
	      if (i in arr)
	        fun.call(thisp, arr[i], i, arr);
	    }
	  },
	indexOf: function(arr,o,i){for(var j=arr.length,i=i<0?i+j<0?0:i+j:i||0;i<j&&arr[i]!==o;i++);return j<=i?-1:i},
	lastIndexOf: function(arr,o,i){return arr.slice(0).reverse().indexOf(o,i);}
}

PDS.Menu = {
	menu: null,
	documentClickHandlerAttached: false,
	show: function(container, event, removeOnHide) {
		this.hide();
		this.setMenu(container);
		$j(this.menu).show();
		if(!this.documentClickHandlerAttached) {
			$j(document).bind("click",PDS.Util.createCallback(this,this.hide));
			this.documentClickHandlerAttached = true;
		}
		if(window.tb_init) {
			tb_init($j(".thickbox", this.menu));
		}
		if(typeof event === "boolean") {
			removeOnHide = event;
			event = null;
		}
		$j(this.menu).data("removeOnHide",removeOnHide === true);
		if(event) {
			event.cancelBubble = true;
		}
	},
	hide: function() {
	    if(this.menu) {
	    	var menu = $j(this.menu);
	    	menu.hide();
	    	if(menu.data("removeOnHide")){ 
	    		this.menu = null;
	    		menu.remove();
	    	}     
	    }	
	},
	setMenu: function(containerId) {
		this.menu = $(containerId);
		this.menu.onclick = PDS.Events.preventBubble;
		//this.menu.onmouseup = PDS.Events.preventBubble;
	},
	getMenu: function() {
		return this.menu;
	}
};

var capsLockWarningShown = false;
PDS.Form = {
	checkAll: function(container) {
		$j(":checkbox", $(container)).attr("checked", true);
	},
	uncheckAll: function(container) {
		$j(":checkbox", $(container)).attr("checked", false);
	},
	toggleAll: function(container) {
		$j(":checkbox", $(container)).each(function(i,n){n.checked = !n.checked;});
	},
	toggleCheckbox: function(id) {
		var chxbox = $(id); chxbox.checked = !chxbox.checked;
	},
	
	checkCapsLock: function( e ) {
		var myKeyCode=0;
		// Internet Explorer 4+
		if ( document.all ) {
			myKeyCode=e.keyCode;
		// Netscape 4
		} else if ( document.layers ) {
			myKeyCode=e.which;
		// Netscape 6
		} else if ( document.getElementById ) {
			myKeyCode=e.which;
		}
	
		// Upper case letters are seen without depressing the Shift key, therefore Caps Lock is on
		if (myKeyCode == 0 || myKeyCode == 27) {
			hideDialog();
			capsLockWarningShown = false;
		} else {
			if (capsDetect( e ) == true) {
				showDialog();	
			} else {
				if (capsLockWarningShown == true) {
					hideDialog();
					capsLockWarningShown = false;
				}
			}
		}
		
		function capsDetect( e ) {
			if( !e ) { e = window.event; }
			//what (case sensitive in good browsers) key was pressed
			var theKey = e.which ? e.which : ( e.keyCode ? e.keyCode : ( e.charCode ? e.charCode : 0 ) );
			//was the shift key was pressed
			var theShift = e.shiftKey || ( e.modifiers && ( e.modifiers & 4 ) );
			
			//if upper case, check if shift is not pressed. if lower case, check if shift is pressed
			if (theShift == true) {
				return ( theKey > 96 && theKey < 123 );
			} else {
				return ( theKey > 64 && theKey < 91 );
			}
		}
		
		function showDialog() {
			if (!capsLockWarningShown) {
				capsLockWarningShown = true;
				$j('#warnDiv').fadeIn('slow');
				//return overlib('<p align="center">Warning! Caps lock is on!</p>', BUBBLE, BUBBLETYPE, 'quotation', FILTER, SHADOW, SHADOWX, 3, SHADOWY, 3, FADEIN,25,FADEOUT,25, REF, 'inputPassword', REFC, 'UR', REFP, 'LL', TEXTSIZE, 'x-small')
			}
		}
		
		function hideDialog() {
			capsLockWarningShown = false;
			$j('#warnDiv').fadeOut('slow');
		}	
	}	
};

/*
$j(function(){
	$j("<div id='pds-templates' style='display:none'/>").appendTo(document.body)
});
*/

PDS.Template = PDS.Class(PDS.Object, {
	element: null,
	directive: null,
	directives: [],
	load: function(id,url) {
		this.element = $j("<div/>").attr("id",id).appendTo("#pds-templates").load(url);
	},
	renderTo: function(data, target) {
		var el = $j(this.element).clone().appendTo(target);
		if(this.directive) {
			el.autoRender(data, this.directive)
		} else {
			for (var i = 0; i < this.directives.length; i++) {
				el.render(data,this.directives[i]);		
			}
		}
	}
});

PDS.Renderable = PDS.Class(PDS.Object, {
	/**
	 * @return jQuery
	 * subclasses should return e.g. return $j("<div class='my-html-fragment'/>");
	 */
	render: function() {
		return $j([]);
	},
	html: function() {
		return $j('<div/>').append(this.render.apply(this,arguments)).html();
	}
});

PDS.Constants = {
	STATES: ['ACT','NSW','NT','QLD','SA','VIC','TAS','WA'],
	STATE_LABELS: ['ACT','New South Wales','Northern Territory','Queensland','South Australia','Victoria','Tasmania','Western Australia']
};

var PF_KEY = PDS.Util.getQueryParam("key");
PDS.Widget = {
	Widget: PDS.Class(PDS.Component, PDS.Renderable, {
		element: null,//container
		widgetId: null,
		version: "1.0",//default
		config: null,
		$ga: null,
		initialize: function(container,config,gaTracker) {
			//check if user is authenticated/authorised
			if(!window.pds) {
				//raise error
				alert('This site does not have access to pricefinder widget. Please verify your settings.');
			} else {
				var baseConfig = {
					element: $(container),
					config: config,
					PDSJsonClient:PDS.Util.clone(pds,true)
				};
				this.$ga = gaTracker;
				PDS.Component.prototype.initialize.call(this,baseConfig);
			}
		},
		getFullWidgetId: function() {
			return this.widgetId+"-v."+this.version;
		},
		checkWidth: function(width) {return width;},
		checkHeight: function(height) {return height;},
		render: function() {
			$j(this.element)
				.addClass("pf-widget")
				.width(this.checkWidth(this.config.width || $j(document).width() - ($j(this.element).outerWidth(true) - $j(this.element).width())))
				.height(this.checkHeight(this.config.height || $j(document).height() - ($j(this.element).outerHeight(true) - $j(this.element).height())))
				.append($j("<div class='pf-widget-header'/>")
					.append($j("<a target='_BLANK'/>")
						.append($j("<img/>").attr("src",PDS.Util.getRelativeUrl(scriptLocation,"../../images/blank.gif")))))
				.append("<div class='pf-widget-body'/>")
				.append($j("<div class='pf-widget-footer'/>")
					/*.append("<a href='http://www.pricefinder.com.au/widget-terms.html' class='pf-terms' target='_BLANK'>Terms & Conditions</a>")
					.append($j("<a class='pf-getwidget' target='_BLANK'>Get this widget</a>")
						.attr("href","http://www.pricefinder.com.au/getwidget.html?widgetId="+this.widgetId+"&referer="+PDS.Widgets.getKey()))*/)
				.children()
					.width($j(this.element).width())
					.filter(".pf-widget-body")
						.height($j(this.element).height()-$j(".pf-widget-header,.pf-widget-footer",this.element).combinedHeight());
			this.updateHeader(this.getDefaultState());
			return $j(".pf-widget-body",this.element);
		},
		updateHeader: function(state) {
			var basehref = (state == 'WA' ? "http://www.reiwa.com.au" : "http://www.pricefinder.com.au");
			$j(this.element).find(".pf-widget-header")
				.toggleClass("pf-reiwa",state == 'WA')
				.toggleClass("pf-veda",state == 'SA')
				.find("a").attr("href", basehref+"/?widgetId="+this.widgetId+"&referer="+PDS.Widgets.getKey());
		},		
		setState: function(state) {
			this.updateHeader(state);
		},
		getDefaultState: function() {
			return this.config && this.config.state && this.getJsonClient().visitState.stateAccess[this.config.state]
			    ? this.config.state
			    : this.getJsonClient().visitState.currentState;
		},
		trackEvent: function(action,opt_label,opt_value) {
			if(this.$ga) {
				if(!this.$ga.cb) {
					setTimeout(this.createCallback(function() {
						this.trackEvent(action,opt_label,opt_value);
					}),200);
					return;
				}
				this.$ga._trackEvent(this.widgetId,action,opt_label,opt_value);
			}
		},
		getTrackingUrl: function() {
			var url = document.referrer;
			if(documentReferrer.anchor == "") url += "#";
			url += PDS.Json.asQueryParams({
/*				_pfkey:'foo',//PF_KEY,
				_pfhost:PDS.Http.getReferrerHost(),
				_pfwidget:this.widgetId,
				_pfstate:this.getDefaultState()*/
				utm_campaign:'foo',//PF_KEY,
				utm_source:PDS.Http.getReferrerHost(),
				utm_medium:this.widgetId,
				_pfstate:this.getDefaultState()
			},false);
			return url;
		}
	})
};

PDS.Widgets = {
	_widgetConstructors: {},
	registerWidget: function(widgetId,version,widgetClass,widgetState) {
		widgetClass.prototype.widgetId = widgetId;
		widgetClass.prototype.version = version;
		this._widgetConstructors[widgetId+"-"+version] = {widgetClass:widgetClass,widgetState: !widgetState ? "ready" : widgetState};
	},
	isWidgetReady: function(widgetId,version) {
		var widget = this._widgetConstructors[widgetId+"-"+version];
		return widget != null && widget.widgetState == "ready";
	},
	setWidgetState: function(widgetId,version, widgetState) {
		this._widgetConstructors[widgetId+"-"+version].widgetState = widgetState;
		//log(widgetId+"-"+version+".widgetState="+widgetState);
	},
	getWidgetInstance: function(widgetId,version,target,config,gaTracker) {
		var widget = this._widgetConstructors[widgetId+"-"+version];
		if(!widget) throw("Widget "+widgetId+" "+version+" not found. Did you load it?");
		return new widget.widgetClass(target,config,gaTracker);
	},
	getKey: function() {
		return PF_KEY;
	},
	getConfig: function() {
		var config = PDS.Util.parseUri(document.location.href).queryKey;
		delete config.widgetId;
		delete config.version;
		delete config.key;
		return config;
	},
	loadResources: function(widgetId, version, callback) {
		version = version || "1.0";
		var widgetRoot = PDS.Util.getRelativeUrl(scriptLocation,"../widgets/"+widgetId+"/"+version+"/");
		var resources = [{
			type:"text/javascript",
			src:widgetRoot+widgetId+".js?"+PDS.Http.getCacheBuster('build')
		},{ 
			type:"text/css",
			href:widgetRoot+widgetId+".css?"+PDS.Http.getCacheBuster('build')
		}];
		PDS.Util.loadResources(resources, callback);
	}
};

PDS.Events = {
	onPageLoad: function(fn) {
		$j(fn);
	},
	preventBubble: function(e) {
		if (e && e.stopPropagation)
			e.stopPropagation();
		else
			event.cancelBubble=true;
	}	
}

})(jQuery);

PDS.Util.loadResources(PDS.getRequiredResources({json:true,format:true,templating:false}));
