(function() {
	
	var publ = {
		bandwidth: null,
		available: false,
		setup: function(o) { priv.init(o) },
		test: function(force) { priv.test(force) }
		};
	
	var priv = {
		o: {
			cacheDays: 14,
			cachePath: '/',
			cacheDomain: null,
			swf: '../swf/bandwidth.swf',
			flv: 'bandwidth.flv',
			maxMBytes: 15,
			maxSeconds: 10,
			width: 380,
			height: 40
			},
		inProgress: false,
		obj: null,
		inited: false,
		waitingToTest: false,
		loaded: false,
		
		test: function(force) {
			if(!priv.inited) priv.init(null, true);
			if(publ.available && !force) {
				priv.safeTrigger('bandwidthAvailable', [ publ.bandwidth ]);
				}
			else {
				if(priv.loaded) {
					priv.testNow();
					}
				else {
					priv.waitingToTest = true;
					}
				priv.safeTrigger('bandwidthTest');
				}
			},
		
		testNow: function() {
			if(priv.inProgress) return;
			priv.inProgress = true;
			if(!priv.obj) {
				// display: none seems to stop/pause the flash movie, at least in FireFox
				var c = $('<div id="bandwidth_test_container" style="position: absolute; top: -1000px; left: -1000px"></div>').appendTo('body');
				var so = new SWFObject($.absUrl(priv.o.swf), 'bandwidth_test', priv.o.width, priv.o.height, 8, '#FFF');
				so.addVariable('testNow', 1);
				so.addVariable('flv', priv.o.flv);
				so.addVariable('maxMBytes', priv.o.maxMBytes);
				so.addVariable('maxSeconds', priv.o.maxSeconds);
				so.write('bandwidth_test_container');
				priv.obj = c.find('#bandwidth_test')[0];
				}
			else {
				priv.obj.bandwidth_test_start();
				}
			},
		
		documentLoaded: function() {
			priv.loaded = true;
			if(priv.waitingToTest) {
				priv.waitingToTest = false;
				priv.test();
				}
			},
		
		init: function(o, noTrigger) {
			if(priv.inited) return;
			if(o) jQuery.extend(priv.o, o);
			
			window.bandwidth_callback = priv.callback;
			
			var cookie = jQuery.cookie('jquery_bandwidth');
			if(cookie) {
				publ.bandwidth = parseInt(cookie);
				publ.available = true;
				if(!noTrigger) priv.safeTrigger('bandwidthAvailable', [ publ.bandwidth ]);
				}

			priv.inited = true;
			},
		
		callback: function(kbps) {
			priv.inProgress = false;
			publ.bandwidth = kbps;
			publ.available = true;
			jQuery.cookie('jquery_bandwidth', publ.bandwidth, {expires: priv.o.cacheDays, path: priv.o.cachePath, domain: priv.o.cacheDomain});
			priv.safeTrigger('bandwidthTestComplete');
			priv.safeTrigger('bandwidthAvailable', [ publ.bandwidth ]);
			},
		
		safeTrigger: function(event, args, obj, errorHandler) {
			if(!obj) obj = window;
			if(!errorHandler && window.console && window.console.log) errorHandler = function(e) { window.console.log(e) };
			try {
				jQuery(obj).trigger(event, args);
				}
			catch(e) {
				if(errorHandler) errorHandler.apply(null, [ e ]);
				}
			}
		};
	
	jQuery.fn.extend({
		bandwidthAvailable: function(handler) {
			var result = this.bind('bandwidthAvailable', handler);
			if(publ.bandwidthAvailable) {
				try {
					handler.apply(this, [ publ.bandwidth ]);
					}
				catch(e) {}
				}
			return result;
			},
		bandwidthTest: function(handler) {
			var result = this.bind("bandwidthTest", handler);
			if(publ.inProgress) {
				try {
					handler.apply(this, []);
					}
				catch(e) {}
				}
			return result;
			},
		bandwidthTestComplete: function(handler) {
			return this.bind("bandwidthTestComplete", handler);
			}
		});
	
	jQuery.bandwidth = publ;

	$(window).load(priv.documentLoaded);
	
	})();
