function $(id) { return document.getElementById(id); }
function ct(t) { return document.createTextNode(t); }

function ce(e) 
{
	if(typeof(e) == 'string')
		e = document.createElement(e);
	
	e.ac = function(c) { e.appendChild(c); return e; };
	e.at = function(t) { e.ac(document.createTextNode(t)); return e; };
	e.ia = function(n, o) { e.insertBefore(n, o.nextSibline); return e; };
	e.ib = function(n, o) { e.insertBefore(n, o); return e; };
	e.rs = function() { e.parentNode.removeChild(e); return e; };
	e.rw = function(c) { e.parentNode.replaceChild(c, e); return e; };
	e.cl = function() { while(e.lastChild) e.removeChild(e.lastChild); return e; };
	
	e.sp = function(k, v, f) { /* if(!f && e[k] === undefined) document.body.appendChild(document.createTextNode('Error, setting invalid property: '+k)); */ e[k] = v; return e; };
	e.st = function(k, v) { e.style[k.replace(/\-(\w)/, function(_, s) { return s.toUpperCase(); })] = v; return e; };
	
	e.printR = function(v) { printR(v, e); };
	e.dump = function(d) { printR(e, d); };
	return e;
}

function printR(obj, dest)
{
	if((obj instanceof Object) && !(obj instanceof Function) || (obj instanceof Array))
	{
		var e = ce('dl');
		for(var key in obj)
		{
			var c = obj[key];
			if((c instanceof Object) || (c instanceof Array))
			{
				var dd = ce('dd');
				e.ac(ce('dt').ac(ce('a')
					.at(key)
					.sp('href', 'javascript:;')
					.sp('dd', dd)
					.sp('c', c)
					.sp('onclick', function() { if(this.dd.firstChild) this.dd.cl(); else printR(this.c, this.dd); }))
				).ac(dd);
			}
			else e.ac(ce('dt').at(key)).ac(ce('dd').at(c));
		}
	}
	else var e = ct(obj);
	
	(dest ? dest : document.body).appendChild(e);
};

function getElementsByClassName(oElm, strTagName, strClassName)
{
	var arrElements = (strTagName == "*" && oElm.all) ? oElm.all : oElm.getElementsByTagName(strTagName);
	var arrReturnElements = [];
	strClassName = strClassName.replace(/\-/g, "\\-");
	var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)");
	var oElement;
	for(var i = 0; i < arrElements.length; i++)
	{
		oElement = arrElements[i];
		if(oRegExp.test(oElement.className))
			arrReturnElements.push(oElement);
	}
	return arrReturnElements;
}


// from http://eriwen.com/javascript/js-stack-trace/
function printStackTrace() {
  var callstack = [];
  var isCallstackPopulated = false;
  try {
    i.dont.exist+=0; //doesn't exist- that's the point
  } catch(e) {
    if (e.stack) { //Firefox
      var lines = e.stack.split('\n');
      for (var i=0, len=lines.length; i<len; i++) {
        if (lines[i].match(/^\s*[A-Za-z0-9\-_$]+\(/)) {
          callstack.push(lines[i]);
        }
      }
      //Remove call to printStackTrace()
      callstack.shift();
      isCallstackPopulated = true;
    }
    else if (window.opera && e.message) { //Opera
      var lines = e.message.split('\n');
      for (var i=0, len=lines.length; i<len; i++) {
        if (lines[i].match(/^\s*[A-Za-z0-9\-_$]+\(/)) {
          var entry = lines[i];
          //Append next line also since it has the file info
          if (lines[i+1]) {
            entry += ' at ' + lines[i+1];
            i++;
          }
          callstack.push(entry);
        }
      }
      //Remove call to printStackTrace()
      callstack.shift();
      isCallstackPopulated = true;
    }
  }
  if (!isCallstackPopulated) { //IE and Safari
    var currentFunction = arguments.callee.caller;
    while (currentFunction) {
      var fn = currentFunction.toString();
      var fname = fn.substring(fn.indexOf('function') + 8, fn.indexOf('')) || 'anonymous';
      callstack.push(fname);
      currentFunction = currentFunction.caller;
    }
  }
  alert(callstack.join('\n\n'));
}
