function Bongo() {
	/* a place for our event handlers to live */
	this.handlers = new Array();
	
	/* specify which events will be captured */
	if( !events ) events = 'keydown keyup keypress click dblclick mouseover mouseout';
	var capture = events.split(" ");
	
	/* to allow java-esque self referencing */
	var self = this;	
	
	/* resolve an id to an object, if it's not already done */
	this.resolve = function( obj ) {
		if( typeof(obj) != 'object' ) obj = document.getElementById(obj);
		return obj;
	};
	
	
	/* ------------------------------------------------------------------ */
	
	
	var events = new Array();
	
	/* this relies on... */
	this.registerCallback = function( func ) { events.push( func ); };
	this.callback         = function() {
		for( var n=0; n<events.length; n++ ) {
			var func = events[n];
			func();
		}
	};
	
	
	/* ------------------------------------------------------------------ */
	
	
	/* handy function to fetch a stored cookie */
	this.getCookie = function( name ) {
		/* find the dimensions of the cookie name */
		var start = document.cookie.indexOf( name + "=" );
		var len = start + name.length + 1;
		
		/* if the name wasn't found, return nothing */
		if( (!start) && ( name != document.cookie.substring( 0, name.length ) ) ) return '';
		if( start == -1 ) return '';
		
		/* find the end of the value */
		var end = document.cookie.indexOf( ";", len );
		if( end == -1 ) end = document.cookie.length;
		
		/* unescape and return the value */
		return unescape( document.cookie.substring( len, end ) );
	};
	
	/* set a cookie using a nice interface */
	this.setCookie = function( name, value, expires, path, domain, secure ) {
		/* as default, path is the whole domain */
		if( !path ) path = '/';
		
		/* provide a handy "distant future" expiry */
		if( expires == 'longtime' ) {
			var today = new Date();
			expires   = new Date( today.getTime() + (8 * 7 * 86400000) );
		}
		
		/* build and set the cookie string */
		document.cookie = name + "=" +escape(value) +
		( (expires) ? ";expires=" + expires.toGMTString() : "") +
		( (path)    ? ";path="    + path                  : "") + 
		( (domain)  ? ";domain="  + domain                : "") +
		( (secure)  ? ";secure"                           : "");
	};
	
	/* allow a form field to persist its value using cookies */
	this.persist = function( obj ) {
		obj = self.resolve(obj);
		if( !obj.id ) return false;
		
		/* restore any stored values now (must be called AFTER the element is created */
		if(      obj.tagName == 'SELECT' ) obj.selectedIndex = bongo.getCookie(obj.id) || 0;
		else if( obj.tagName == 'INPUT'  ) obj.value         = bongo.getCookie(obj.id);
		
		/*
			when the element value changes, store the new value. we use
			the "attachExec" function, so the inital value is stored also
		*/
		bongo.attachExec( 'change', obj.id, function(e) {
			if(      obj.tagName == 'SELECT' ) bongo.setCookie( obj.id, obj.selectedIndex );
			else if( obj.tagName == 'INPUT'  ) bongo.setCookie( obj.id, obj.value         );
		});
		
		return true;
	}
	
	
	/* ------------------------------------------------------------------ */
	
	
	this.clickSomething = function( obj ) {
		obj = self.resolve(obj);
		
		/* ie's click method is actually quite handy... */
		if( obj.click ) {
			obj.click();
			
		/* but the DOM must be obeyed! */
		} else {
			var click = document.createEvent('MouseEvents');
			click.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
			obj.dispatchEvent(click);
		}
	};
	
	
	/* ------------------------------------------------------------------ */
	
	
	/* a public function, to allow plugins to attach their own events */
	this.attach = function( type, obj, func, extra ) {
		obj = self.resolve(obj);
		
		var f = function(e) {
			/* fix the event object, for ie */
			if( !e        ) e = window.event;
			if( !e.target ) e.target = e.srcElement;
			
			/* poke in our extra stuff */
			e['extra'] = extra;
			
			/* and call the event function */
			func(e);
		};
		
		/* allow more than one event type to be hooked, using pipes!
		var event_types = type.split('|');
		
		for( n in event_types ) {
			if(      obj.attachEvent      ) obj.attachEvent( 'on' + event_types[n], f );
			else if( obj.addEventListener ) obj.addEventListener( event_types[n], f, false );
		}*/
		
		/* for now, do it the old way, one event per call */
		if(      obj.attachEvent      ) obj.attachEvent( 'on' + type, f );
		else if( obj.addEventListener ) obj.addEventListener( type, f, false );
	};
	
	this.attachExec = function( type, obj, func, extra ) {
		/* attach the event using the usual function (above) */
		self.attach( type, obj, func, extra );
		
		/* and fire the event handler once now (for init stuff) */
		func();
	}
	
	
	/* ------------------------------------------------------------------ */
	
	
	/* a public helper function, to check if an object has a specified className
	   attached to it (to avoid the className.indexOf bullshit) */
	this.hasClass = function( obj, classStr ) {
		obj = self.resolve(obj);
		
		if( obj.className ) {
			var classes = obj.className.split(' ');
			for( i in classes ) {
				if( classes[i] == classStr ) return true;
			}
		}
		return false;
	};
	
	/* another public helper, to ensure an object has the specified className */
	this.giveClass = function( obj, classStr ) {
		obj = self.resolve(obj);
		
		if( !self.hasClass(obj, classStr) ) {
			var classes = obj.className.split(' ');
			classes.push(classStr);
			obj.className = classes.join(' ');
		}
	};
	
	/* another, to ensure that an object DOESN'T have the specified className */
	this.takeClass = function( obj, classStr ) {
		obj = self.resolve(obj);
		
		if( self.hasClass(obj, classStr) ) {
			var oldClasses = obj.className.split(' ');
			var newClasses = new Array();
			
			for( i in oldClasses ) {
				if( oldClasses[i] != classStr ) newClasses.push(oldClasses[i]);
			}
			
			obj.className = newClasses.join(' ');
		}
	};
}


var bongo = new Bongo();

/* attach the handy autofocus behaviour callback - this doesn't really fit
   anywhere else, so here will do... */

bongo.registerCallback(function() { var af = document.getElementById('autofocus'); if( af ) af.focus(); });


/* write something into the document, to force the document.body object to
   be created (so other scripts in the <head> can attach events to it) */

document.write('<span style="position: absolute; display: none;">x</span>');
