function org_benow_Util() {
	// util object
}

var Util=new org_benow_Util();

/**
 * Dumps the stack to firebug console
 */
org_benow_Util.prototype.dumpStackTrace=function() {
	if (typeof console != 'undefined' && console.log) 
		console.log(Util.getStackTraceString());
}

/**
 * Gets the stack trace (cross browser)
 * Reinterpretation of
 * http://eriwen.com/javascript/js-stack-trace/
 */
org_benow_Util.prototype.getStackTrace=function() {
  var callstack = [];
  var isCallstackPopulated = false;
  try {
    i.dont.exist+=0; //cause exception
  } catch(e) {
    if (e.stack) { //Firefox
      var lines = e.stack.split('\n');
      for (var i=0;i<lines.length; i++) {
        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;i<lines.length; i++) {
        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;
    }
  }
  return callstack;
}

/**
 * Get stack trace as a string
 */
org_benow_Util.prototype.getStackTraceString=function() {
	var stack=this.getStackTrace();
	var msg='';
	for (var i=0;i<stack.length;i++) 
		msg+=stack[i]+'\n';
	return msg;
}

org_benow_Util.prototype.deprecated=function(because) {
	if (typeof this._shownDeps == 'undefined') 
		this._shownDeps=new Array();

	if (typeof console != 'undefined' && console.warn) {
		var sig='';
		var from='';
		var lines=Util.getStackTrace();
    sig=lines[1].split(')@')[0]+')';
    from=lines[2].split(')@')[1];
	  if (!this._shownDeps[from]) {
	  	console.warn('DEPRECATED: '+sig+'\ncalled from: '+from+'\n'+because);
	  	this._shownDeps[from]='true';
	  }
	}
}

/**
 * Log a message to the firebug console, if it exists
 * @param msg message to log
 */
org_benow_Util.prototype.log=function(msg) {
	if (typeof console !='undefined' && typeof console.log == 'function') {
		console.log(msg);
	}
}

/**
 * Add a class to the elements class attribute.
 * @param className name of class to add
 */
Element.prototype.addClass=function(className) {
  if (this.className.indexOf(className)==-1) {
    if (this.className.length==0)
      this.className=className;
    else
      this.className+=' '+className;
  }
//  debug('now '+elem.id+'.className: "'+elem.className+'"');
}

/**
 * Add or remove a class from an element
 * @param elem element to work with
 * @param className class to add or remove
 * @param addOrRemove add the class if true, remove if false
 */
Element.prototype.alterClass=function(className,addOrRemove) {
	if (addOrRemove) {
		this.addClass(className);
	} else {
		this.removeClass(className);
	}
}

/**
 * Removes a class of a given name from this elements class attribute
 * @param className name of class to remove
 */
Element.prototype.removeClass=function(className) {
  if (this.className==className) {
    this.className='';
  } else {
    var pos=this.className.indexOf(className);
    if (pos>-1) {
      if (pos==0) 
        this.className=this.className.substring(className.length+1);
      else
        this.className=this.className.substring(0,pos)+this.className.substring(pos+className.length);
    }
  }
//  debug('now '+elem.id+'.className: "'+elem.className+'"');
}

/**
Sets the innerHTML of the this element with the given text.
Scripts within the innerHTML are evaluated so that they may 
be called.  

*Use this if you wish to call scripts within dynamically assigned html content*
  
*/
Element.prototype.setInnerHTML=function(text) {
	var elem=this;
  if (navigator.appName == 'Microsoft Internet Explorer') {
    var newdiv = document.createElement("div");
    removeChildNodes(elem);
    this.appendChild(newdiv);
    elem=newdiv;
    elem.appendChild(document.createTextNode(text));
  } else
    elem.innerHTML=text;
  var h = document.getElementsByTagName("head")[0];
  var scripts=elem.getElementsByTagName('script');
  for (var i=0;i<scripts.length;i++) {
    var s = document.createElement("script");
    s.type="text/javascript";
    h.appendChild(s);
    s.text=scripts[i].text;
  }
}

/**
 * Replaces all occurrences in string
 * from http://www.bradino.com/javascript/string-replace/
 * @param toFind string to be replaced
 * @param toReplace string to replace with
 */
String.prototype.replaceAll=function(toFind,toReplace) {
	return this.replace(new RegExp(toFind,'g'),toReplace);
}

/**
 * Get object type for this object.  This is the name
 * of the constructor of the type.  This method
 * is useful to determine which type to use when amending
 * a prototype.
 */
Object.prototype.typeOf = function() { 
  var funcNameRegex = /function (.{1,})\(/;
  var results = (funcNameRegex).exec((this).constructor.toString());
  return (results && results.length > 1) ? results[1] : "";
};


/**
 * Gets a parameter with the given name from the current URL.
 */
function util_getURLParam(name) {
	var href=document.location.href;
	var pos=href.indexOf('?');
	if (pos==-1)
		return null;
	href=href.substring(pos+1);
	var params=href.split("&");
	for (var i=0;i<params.length;i++) {
		var curr=params[i];
		if (curr.indexOf(name+"=")==0) {
			return curr.substring((name+"=").length);
		}
	}
	return null;
}

// http://pugong.spaces.live.com/Blog/cns!E63BB053F5C780A3!496.entry
function util_clientSize() {
  var myWidth = 0, myHeight = 0;
  if( typeof( window.innerWidth ) == 'number' ) {
    //Non-IE
    myWidth = window.innerWidth;
    myHeight = window.innerHeight;
  } else if( document.documentElement &&
      ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
    //IE 6+ in 'standards compliant mode'
    myWidth = document.documentElement.clientWidth;
    myHeight = document.documentElement.clientHeight;
  } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
    //IE 4 compatible
    myWidth = document.body.clientWidth;
    myHeight = document.body.clientHeight;
  }
  return [myWidth,myHeight];
}

function util_windowHeight() {
  return util_clientSize()[1];
}

function util_windowWidth() {
  return util_clientSize()[0];
}

/**
 * returns true if given array contains given value 
 */
function util_contains(ary,value) {
	for (var i=0;i<ary.length;i++) {
		if (ary[i]==value) 
			return true;
	}
	return false;
}

/**
 * Selects an option in a sel by the given value
 */
function util_selectByValue(sel,value) {
	for (var i=0;i<sel.options.length;i++) {
		if (sel.options[i].value==value) {
			sel.selectedIndex=i;
			sel.options[i].selected=true;
		}
	}
}

function util_absorbEvent(e) {
  // http://www.quirksmode.org/js/events_order.html
  if (!e) var e = window.event;
  e.cancelBubble = true;
  if (e.stopPropagation) e.stopPropagation();
}

/**
* derived from
* http://www.webtoolkit.info/
*
**/
function util_trim(str) {
    return str.replace(new RegExp("^[\\s]+", "g"), "");
}

/**
 * Cross browser function to attach a named event 
 * to given elem which is handled by given func
 *  
 * @param obj elem to attach event to 
 * @param eventName name of event to capture
 * @param func function to call on event
 * @return
 */
function util_attachEvent(obj,eventName,func) {
	// http://www.quirksmode.org/js/events_advanced.html
	if (obj.addEventListener) {
		obj.addEventListener (eventName,func,false);
	} else if (obj.attachEvent) {
		obj.attachEvent('on'+eventName,func);
	} else {
		eval('obj.'+eventName)= func;
		eval('obj.'+eventName)= func;
	}
}

function util_coords(x,y) {
	this.x=x;
	this.y=y;
}

function util_getMouseCoords(event) {
	var tempX = 0;
	var tempY = 0;
	var IE=document.all?true:false;
	if (IE) { // grab the x-y pos.s if browser is IE
		tempX = event.clientX + document.body.scrollLeft;
		tempY = event.clientY + document.body.scrollTop;
	} else {  // grab the x-y pos.s if browser is NS
		tempX = event.pageX;
		tempY = event.pageY;
	}
	return new util_coords(tempX,tempY);
}

function util_findPos(obj) {
	var curleft = curtop = 0;
	if (obj.offsetParent) {
		do {
			curleft += obj.offsetLeft;
			curtop += obj.offsetTop;
		} while (obj = obj.offsetParent);
	}
	return new util_coords(curleft,curtop);
}

function addEvent(src,eventName,func) {
  if (src.addEventListener) // w3c        
    src.addEventListener(eventName,func,false);
  else // ie
    src.attachEvent('on'+eventName,func);
}

function cancelBubble(e) {
  // http://www.quirksmode.org/js/events_order.html
  if (!e) var e = window.event;
  e.cancelBubble = true;
  if (e.stopPropagation) e.stopPropagation();
}

function addOnLoad(func) {
	addEvent(window,'load',func);
}

function copyToClipboard(txt) {
  window.clipboardData.setData('text',txt);
}


/**
  Inserts the given text as the innerHTML of the element with the given id
  Included scripts are evaluated after the simple innerHTML assignment
*/
function insertHTML(id,text) {
  var modCnt=document.getElementById(id);
  util_setInnerHTML(modCnt,text);
}

function removeChildNodes(ctrl)
{
  while (ctrl.childNodes[0])
    ctrl.removeChild(ctrl.childNodes[0]);
}

/**
  Inserts text from request as innerHTML of elem with given id.  Performs
  exception assertion and script evaluation.
*/
function insertResponse(id,req) {
  assertException(req);
  insertHTML(id,req.responseText);
}

var _onScrolls;
function _doOnScroll() {
	for (var i=0;i<_onScrolls.length;i++) 
		_onScrolls[i]();
}

function addOnScroll(func) {
	if (!_onScrolls) {
		_onScrolls=new Array();
		window.onscroll=_doOnScroll;
	}
	_onScrolls[_onScrolls.length]=func;
}

var _stickyE;
var _stickyTop;
function _stickyScroll() {
	_stickyE.alterClass('sticky',document.documentElement.scrollTop>_stickyTop);
}

/**
 * Makes the given element stick to the top as the page scrolls.  The element 
 * will retain it's initial display position until the page is scrolled past the
 * top of the element, from then on it will positioned at the top
 * @param elem
 */
function makeTopSticky(elem) {
	_stickyTop=util_findPos(elem).y;
	_stickyE=elem;
	addOnScroll(_stickyScroll);
}

function windowHeight() {
	return (typeof window.innerHeight != 'undefined' ? window.innerHeight : document.body.offsetHeight);
}

// -- deprecations --
function deprecated(because) {
	Util.deprecated("use Util.deprecated");
	Util.deprecated(because);
}

function alterClass(elem,className,addOrRemove) {
	Util.deprecated('use Element.alterClass(String,String)');
	elem.alterClass(className,addOrRemove);
}

function util_replaceAll(instr,tofind,toreplace) {
	//return instr.replace(new RegExp(tofind,'g'),toreplace);
	Util.deprecated('Use String.replaceAll');
	return instr.replaceAll(tofind,toreplace);
}

function removeClass(elem,className) {
	Util.deprecated("Use Element.removeClass(String)");
	elem.removeClass(className);
}

function addClass(elem,className) {
	Util.deprecated('use Element.addClass(String)');
	elem.addClass(className);
}

function util_getById(name) {
	deprecated('use document.getElementById(String)');
	var elem=document.getElementById(name);
	if (!elem)
		alert('no elem with id: '+name);
	return elem;
}

function util_log(msg) {
	Util.deprecated("use Util.log(msg)");
	return Util.log(msg);
}

function util_setInnerHTML(elem,text) {
	deprecated("Use [Element].setInnerHTML(text)")
	elem.setInnerHTML(text);
}


