/*
	lib_extract.js

	$Id: lib_extract.js,v 1.3 2007/03/23 14:14:36 revence Exp $

	Chunks from the big lib. For tivolaj@yahoo.com.
*/

//	Public vars and stuff
//	No dependencies.
window.__mem_heaps = [];
var OPERA = window.opera,
	IE    = /Explorer/gi.test(window.navigator.userAgent),
	MOZIL = /Gecko/gi.test(window.navigator.userAgent),
	W3C   = document.getElementById && document.getElementsByTagName;		//	Is it W3C DOM compatible?
	//	Last resort. Just test for the objects you are going to use, before you use 'em. This is for hard times.

//	Adds events to objects, in a safe, cross-browser way.
//	Use it, because it even provides for cleaning up, innately.
//	Dependencies: window.__mem_heaps.
function addEvent(toWho, onWhatEvent, closure)
{
	var onWhatEvent = (onWhatEvent.indexOf('on') ? onWhatEvent : onWhatEvent.substring(2));
	if(toWho.attachEvent) toWho.attachEvent('on' + onWhatEvent, closure);
	else toWho.addEventListener(onWhatEvent, closure, true);	//	If it isn't on IE model, it should be on W3C.
	__mem_heaps.push({obj:toWho, evt:onWhatEvent, cls:closure});
}
//	Now, we set up garbage collection.
//	Dependencies: addEvent
addEvent(window, 'unload', 
function()
{
	for(var notI = 0, obj; notI < __mem_heaps.length; ++notI)
	{
		obj = __mem_heaps[notI].obj;
		if(obj.removeEventListener) obj.removeEventListener(__mem_heaps[notI].evt, __mem_heaps[notI].cls, true);
		else obj.detachEvent('on' + __mem_heaps[notI].evt, __mem_heaps[notI].cls);
		__mem_heaps[notI].cls = null;
		__mem_heaps[notI]     = null;
	}
	__mem_heaps = null;	//	People freedom and liberty -- Bob Marley (Babylon System)
});

//	Standard JS has no map function. I call this arrcpy.
function arrcpy(dest, src, judge)
{
	if(! judge) judge = function(){return true;}
	for(var notI = 0, notJ = 0; notI < src.length; ++notI, ++notJ) if(judge(src[notI]) || --notJ) dest[notJ] = src[notI];
}

//	Limits input in the element, whose id is given as param 1, to the value in param 3. Param 2 is the ID of the meter.
//	Dependencies: addEvent
function limitInput(who, meter, limit)
{
	var elem = document.getElementById(who),
		metr = document.getElementById(meter);
	if(! elem || !metr) /* Die silently. */ return;
	function limitCounter(e)
	{
		metr.innerHTML = (elem.value.length > 9 && elem.value.length < 100 ? '' : '0') + elem.value.length;		//	To remove .innerHTML for a better option, in happier times.
		if(elem.value.length <= limit) return true;
		switch((e ? e.keyCode : event.keyCode))
		{
			case 8:	//	Backspace
				return;
		}
		if(e) e.preventDefault(true);
		else event.returnValue = false;
		return false;
	}
	addEvent(elem, 'keydown', limitCounter); 
	addEvent(elem, 'keyup',   limitCounter);
	addEvent(elem.form, 'submit', function(e)
	{
		if(elem.value.length > limit + 1)
		{
			var limitplus = limit + 1;
			alert('You have entered more text than is allowed. Please maintain ' + limitplus + ' letters only.');
			if(e) e.preventDefault(true);
			else event.returnValue = false;
			return false;
		}
		return true;
	});
}

//	This is a method that you will, from now on, call against all your code-instanced arrays to remove the elements you don't want.
//	The parameter is a predicate closure. It returns bool, true if the element should be spared, false to kill it.
//	The editions are, of course, in-place.
//	No dependencies.
Array.prototype.select =
function(judge)
{
	var newArr = [];
	for(var notI = 0, newI = 0; notI < this.length; ++notI)	if(judge(this[notI])) newArr[newI++] = this[notI];
	arrcpy(this, newArr);
	this.length = newArr.length;
	return this;
}

//	In trying to make webpages look cute, we usually assign some style attributes to the INPUT tag. They are meant for text boxes 
//	(i.e.: input type="text"). But since buttons are also input elements (input type="submit|reset|button"), they end up looking like the text
//	boxes. We rerely want that. So, here's a script to give all buttons a set of stylings, while saving the input styling for other INPUTs.
//	Dependencies: Array.prototype.select
function makeButtonsStandOut(style)
{
	var buts  = document.getElementsByTagName('input');
	var buts2 = new Array(buts.length);		//	Why? 'Cause buts was created before Array.prototype.select was made, unlike a new Array.
	arrcpy(buts2, buts);
	buts2.select(function(who){return /button|reset|submit/gi.test(who.type)});
	//	Now, there are only buttons in buts2. We roll ...
	for(var notI = 0; notI < buts2.length; ++notI) buts2[notI].setAttribute('style', style);
}

//	This function has some important usage, in that you no longer have to put text against a form field (INPUTs only) to tell users what to enter.
//	You just have to enter that, say "Username", as the default text, and this function (with a bit of help from other comrades) will take care of
//	making sure that that field (of the username, in the above example) will always have something in it, i.e., either the username of someone, or
//	the default text.
//	It also does some neat trick on password fields. If the password has not yet been filled in, it will display the default text as though that were a
//	regular text field. When there is data in there, it becomes a veiled field again. Sexy, no?
//	Dependencies: addEvent
function handleDefaultText()
{
	var inps = document.getElementsByTagName('input');
	for(var notI = 0, inp; notI < inps.length; ++notI)
	{
		inp = inps[notI];
		if(/button|reset|submit/gi.test(inp.type)) continue;
		inp.setAttribute('origtext', inp.value);
		if(inp.type.toLowerCase() == 'password')
		{
			inp.setAttribute('ispassword', true);
			inp.setAttribute('type', 'text');
		}
		addEvent(inp, 'focus', 
		function(evt)
		{
			evt = evt ? evt : event;
			if(! evt.target) evt.target = evt.srcElement;
			if(evt.target.value == evt.target.getAttribute('origtext')) evt.target.value = '';
			if(evt.target.getAttribute('ispassword')) evt.target.setAttribute('type', 'password');
		});
		addEvent(inp, 'blur',
		function(evt)
		{
			evt = evt ? evt : event;
			if(! evt.target) evt.target = evt.srcElement;
			if(evt.target.value == '')
			{
				evt.target.value = evt.target.getAttribute('origtext');
				if(evt.target.getAttribute('ispassword')) evt.target.setAttribute('type', 'text');	//	Just 4 da heck of it!
			}
		});
	}
}

//	Alerts the user about a field with input that is deemed invalid.
//	It is incomplete, as yet, because it must keep focus on the invalid field.
//	No dependencies, but is used in conjuction with other funcs here.
function alertInvalidity(who, cleanup)
{
	if(arguments.length > 1)
	{
		who.setAttribute('style', '');	//	I should maybe save the original styling and then re-instate it. But only after you complain :o) -- Rev
		return;
	}
	who.setAttribute('style', 'background:#f88');
}

//	Generates a random HTTP parameter, which should prevent caching.
function generate_random_param()
{
	var randnum = Math.random().toString().split('.').join('');
	return 'very_random_str' + randnum + '=' + randnum;
}

//	This is the one to call when the Ajax call is strictly unidirectional -- i.e., it only affects the server, and does no
//	more.
//	The reason it is separated from other Ajax calls is because it's frequently needed, and does not employ heavy-duty
//	objects, so is better for performance. You can call it dozens of times per second. If need be.
function unidirectionalAjax(url, prevent_cache)
{
	var engine = document.createElement('script');
	prevent_cache = (arguments.length - 1 ? arguments[arguments.length - 1] : true);
	engine.setAttribute('src', url + (prevent_cache ? (/\?/.test(url) ? '&' : '?') + generate_random_param() : ''));
	document.documentElement.appendChild(engine);	//	Until you append (run) it, it won't be fetched (rather dumb, no?).
}

//	This is used to set debug strings. Strings you want to have appearing in a different form, depending on whether
//	we are debugging or running for real. It determines debug state via the domain.
//	Think ifdef __DEBUG__ ... ;o)
//	So, you run setDebugStr(key_string, debug_time_string, shipped_string);
//	Next time you say getDebugStr(key_string), you get back the appropriate one, depending on status. Don't worry about much
//	else. Please see usage in Sawuti -- the bugger can be very nifty, when you use him well.
//	No dependencies.
function setDebugStr(key, debval, runval)
{
	if(! window.__debug_strs__) window.__debug_strs__ = {};
	window.__debug_strs__[key] = [debval, runval];
}

//	Tells if we are debugging, or running for real, depending on the domain.
//	Add your domain to the list below, if you will ever use this chap. I've put the ones I use or may use.
//	No dependencies, but is used by getDebugStr.
function __inDebugStage()
{
	var arr = ['julius', 'kamp3x', 'main', 'noble', 'localhost', '127.0.0.1'];
	//	return arr.select(function(maybe){return maybe == document.domain;});	//	Changing...
	for(var notI = 0; notI < arr.length; ++notI) if(arr[notI] == document.domain) return true;
	return false;
}

//	See setDebugStr doc.
//	Dependencies: __inDebugStage.
function getDebugStr(key)
{
	return window.__debug_strs__[key][(__inDebugStage() ? 0 : 1)];
}

//	Makes an Ajax call to URL, and returns you the JSON served (with application/json MIME type).
//	The second arg is the function to which the returned JSON will be fed.
//	Yet to be debugged for IE, which uses an ActiveX Object, instead. Merde, as the French would say.
//	No dependencies.
function getJSONFrom(url, proc)
{
	var req;	//	Not global, because the calls may get concurrent, which cancels the previous.
	if(! window.XMLHttpRequest && window.ActiveXObject)	req = new ActiveXObject('Microsoft.XMLHTTP');
	else req  = new XMLHttpRequest();
	req.open('GET', url, true);
	req.send(null);
	req.onreadystatechange = 
	function(ev)
	{
		if(req.readyState != 4 || req.status != '200') return;
		if(req.getResponseHeader('Content-type') != 'application/json') return;	//	No sneakin' stuff in.
		proc(req.responseText);
	};
}

//	Applies styling in a cross-browser way.
function applyStyle(who, style)
{
	var level1 = style.split(/\;/),
		who_style,
		pair;
	if(/\[object\]/.test(who.getAttribute('style')))
	{
		who_style = who.getAttribute('style');
		level1.map(function(mem)
		{
			return mem.split(':');
		});
		level1.map(function(pair)
		{
			who_style[pair[0]] = pair[1];
		});
	}
	else who.setAttribute('style', style);
}

