function getElementsByClass (needle, node, tagName) {
	var elements = node.getElementsByTagName (tagName);
	var found = Array ();

	for (var i = 0, j = 0; i < elements.length; i++) {
		var c = " " + elements[i].className + " ";
		if (c.indexOf (" " + needle + " ") != -1) {
			found[j++] = elements[i];
		}
	}
	return found;
}

function addLoadEvent (func) {
	var oldonload = window.onload;
	if (typeof window.onload != 'function') {
		window.onload = func;
	}
	else {
		window.onload = function () {
			oldonload ();
			func ();
		}
	}
}

var charCounters = new Array();

function addCommas(numberAsString) {
	var sRegExp = new RegExp('(-?[0-9]+)([0-9]{3})');
	while(sRegExp.test(numberAsString)) {
		numberAsString = numberAsString.replace(sRegExp, '$1,$2');
	}
	return numberAsString;
}

function killKids( el ){ while( el.hasChildNodes() ){ el.removeChild( el.firstChild ) } }
function setAttrs(el, attrs){ if (attrs) { for (var attr in attrs){ if (attr != 'type') setAttr ( el, attr, attrs[attr]); } } }
function setCss( el, rls ){ if (rls) { for (var rl in rls){ el.style[rl] = rls[rl]; } } }
function setTxt(el, txt){ killKids(el); el.appendChild(document.createTextNode(txt)); }
/*
function setAttr(el, name, val) { el.setAttribute( name, val); }
function remAttr(el, name){ el.removeAttribute( name ); }
function getEl(strId){ return document.getElementById(strId);}
function getTags(t, el){ return (el ? el:document).getElementsByTagName(t); }
*/

function mkEl(tag, attrs, styles, txt){
	var newEl = document.createElement(tag);
	if (attrs && attrs.type) newEl.setAttribute("type", attrs.type);
	if (attrs) setAttrs(newEl, attrs);
	if (styles) setCss(newEl, styles);
	if (txt) setTxt(newEl, txt);
	return newEl
}

function mkChild( parent, tag, attrs, styles, txt){ return parent.appendChild( mkEl(tag, attrs, styles, txt) ); }


function CharCounter(el){
	if (!el) return false;

	var fldEl = el;
	var msgEl, statusEl, limitEl, maxLen;

	var check = function () {
		var newEl = statusEl.cloneNode(true);
		if (fldEl.value.length > maxLen) {
			remVal = maxLen - fldEl.value.length;
			phrase = (maxLen - fldEl.value.length == -1)? ' character ':' characters ';
			newEl.innerHTML = '(' + addCommas(-remVal + '') + phrase + 'over' + '.)';
			newEl.className = "status error";
		} else if (fldEl.value.length <= maxLen) {
			remVal = maxLen - fldEl.value.length
			phrase = (maxLen - fldEl.value.length == 1)? ' character ':' characters ';
			newEl.innerHTML = '(' + addCommas(remVal.toString()) + ' more ' + phrase + 'available)';
			newEl.className = 'status';
		} 
		msgEl.replaceChild(newEl, statusEl);
		statusEl = newEl;
	}

	var init = function(){
		var lenMatch = fldEl.className.match(/.*?maxChars(\d+)/);
		if (lenMatch && lenMatch[1]) maxLen = lenMatch[1];
		
		msgEl = mkEl("p");
		msgEl.className = 'charcounter';
		limitEl = mkChild(msgEl, "span", false, false, 'Up to ' + addCommas(maxLen) + ' characters. ');
		limitEl.className = 'limit';
		statusEl = mkChild(msgEl, "span");
		statusEl.className = 'status';

		fldEl.parentNode.insertBefore(msgEl, fldEl.nextSibling);

		check()
		fldEl.onkeyup = check;
	}
	init();

}

addLoadEvent (function () {
	var formEls = new Array ();
	formEls = formEls.concat (getElementsByClass ('hasMaxLen', document, 'INPUT'));
	formEls = formEls.concat (getElementsByClass ('hasMaxLen', document, 'TEXTAREA'));
	for (var i = 0; i < formEls.length; i++) {
		charCounters.push (new CharCounter (formEls[i]));
	}
} );
