
/// Whether the str contains anything visible
/// very buggy implementation...
function ContainsVisibleChars(str)
{
	var strWithoutInvisibles = str.replace(new RegExp("<br ?/?>", "gi"), "");

	return strWithoutInvisibles.length > 0;

}



/// Append a eventhandler to the window.onload event
function AppendToWindowOnload(onloadDelegate)
{
	if(window.onload == undefined)
	{
		window.onload = onloadDelegate;
	}
	else
	{
		var tmp = window.onload;
		window.onload = function() { 
			tmp();
			onloadDelegate();
		}
	}
}

if(!(new Array()).push)
{
	Array.prototype.push = function(item)
	{
		this[this.length] = item;
	}

}



//
// dom stuff
//

/// Given a (default) event argument, return the source of the event
function GetSource(e)
{
	if (!e) var e = window.event;
	if (e.target) return e.target;
	else if (e.srcElement) return e.srcElement;
	if (targ.nodeType == 3) return targ.parentNode; // defeat Safari bug
	return null;		
}
function ClearChildren(source)
{
	while(source.childNodes.length > 0)
	{
		source.removeChild(source.childNodes[0]);
	}
}

/*
 * Very simple Label
 */
function CreateLabel(htmlContainer, text)
{
	htmlContainer.appendChild(document.createTextNode(text));
}
function CreateDiv(htmlContainer, text)
{
	var div = document.createElement('div');
	CreateLabel(div, text);
	htmlContainer.appendChild(div);
	return div;
}
/*
 * Very simple Label
 */
function CreateBrLabel(htmlContainer, text)
{
	var text_parts = text.split("\n");
	
	for(var i=0;i<text_parts.length; i++)
	{
		htmlContainer.appendChild(document.createTextNode(text_parts[i]));
		htmlContainer.appendChild(document.createElement("br"));
	}
}

function AppendHtml(htmlContainer, html)
{
	var div = document.createElement("div");
	div.innerHTML = html;
	htmlContainer.appendChild(div);
}


/// copied from http://www.howtocreate.co.uk/tutorials/javascript/eventinfo
function KeycodeFromEvent(e)
{
  if( typeof( e.keyCode ) == 'number'  ) {
    //DOM
    return e.keyCode;
  } else if( typeof( e.which ) == 'number' ) {
    //NS 4 compatible
    return e.which;
  } else if( typeof( e.charCode ) == 'number'  ) {
    //also NS 6+, Mozilla 0.9+
    return e.charCode;
  } else {
    return null;
  }
}		


//
// function binding
//

// Some browsers don't like expressions such as foo.concat(arguments)
// or arguments.concat(foo) --- presumably because argument objects are
// not really considered to be arrays --- so we'll just concatenate
// pseudoarrays manually.  Thanks to Chih-Chao Lam for pointing this out.
function concat() {
    var result = [];

    for (var i = 0; i < arguments.length; i++)
        for (var j = 0; j < arguments[i].length; j++)
            result.push(arguments[i][j]);

    return result;
}

function withoutFirst(sequence) {
    result = [];

    for (var i = 1; i < sequence.length; i++)
        result.push(sequence[i]);

    return result;
}

function cons(element, sequence) {
    return concat([element], sequence);
}

//
// returns a function pointer that is bound to the given object
// usage: someitem.someeventhandler = somemethod.bind(somemethod's object)
// this will ensure that you have access to somemethod's object when somemethod
// is called by the eventhandler
//
//
// Thanks to http://www.deepwood.net/writing/method-references.html.utf8
// for (what I'm guessing is) applied lisp knowledge
//
// Usage: someOtherObject.onSomeEvent = CreateMethodReference(this, this.someEventHandlerMethod);
//
function CreateMethodReference(object, method) {
    if (!(method instanceof Function))
        method = object[method];

    return function () {
        method.apply(object, arguments);
    };
};

Function.prototype.bind = function (object) {
    var method = this;
    var preappliedArguments = withoutFirst(arguments);
    return function () {
        return method.apply(object, concat(preappliedArguments, arguments));
    };
}

Function.prototype.bindAsEventListener = function (object) {
    var method = this;
    var preappliedArguments = withoutFirst(arguments);

    return function (event) {
        return method.apply(object, concat([event || window.event], preappliedArguments));
    };
}


/*****************************************************************************/
/*
 * An ApplicationEvent contains a set of listeners which can be notified
 * through a callback method
 */
function ApplicationEvent()
{
	this.listeners = new Array();
	this.activeNotificationQueue = null;
}

ApplicationEvent.prototype.AddListener = function(listener)
{
	this.listeners.push(listener);
}

ApplicationEvent.prototype.RemoveListener = function(listener)
{
	for(var i=this.listeners.length -1; i>=0; i--)
	{
		if(this.listeners[i] != listener) continue;
		
		this.listeners.splice(i, 1);
	}
}

ApplicationEvent.prototype.NotifyListeners = function() // any parameters will passthrough to the delegates
{
	if(this.activeNotificationQueue != null)
	{
		//
		// this.NotifyListeners is called recursively if the activeNotifaticationQueue != null
		// make sure the events are still chronological; append to the queue and exit
		//
		for(var i=0; i<this.listeners.length; i++)
		{
			this.activeNotificationQueue.push({ func : this.listeners[i], args : arguments });
		}
		return;
	}
	
	
	this.activeNotificationQueue = new Array();
	for(var i=0; i<this.listeners.length; i++)
	{
		this.activeNotificationQueue.push({ func : this.listeners[i], args : arguments });
	}
	for(var i=0; i<this.activeNotificationQueue.length; i++)
	{
		//
		// if you were to do this.listeners[i](arguments) then
		// your delegate would receive not a variable amount of arguments
		// but a single argument containing an array. Using .apply()
		// works around this. The first parameter to apply is the object
		// instance that the delegate should attach to. We don't use that here.
		//
		//this.activeNotificationQueue[i].apply(null, arguments);
		var funcObject = this.activeNotificationQueue[i];
		funcObject.func.apply(null, funcObject.args);
	}
	this.activeNotificationQueue = null;
	
}