// JavaScript prototype extensions

Object.extend = function(dest, source, replace) {
	for(prop in source) {
		if(!replace && dest[prop] != null) continue;
		dest[prop] = source[prop];
	}
	return dest;
}

Object.extend(Function.prototype, {
	getArguments: function() {
		var args = [];
		for(var i=0; i<this.arguments.length; i++)
			args.push(this.arguments[i]);
		return args;
	},
	apply: function(o, a) {
		var r, x = "__fapply";
		if(typeof o != "object") o = {};
		o[x] = this;
		var s = "r = o." + x + "(";
		for(var i=0; i<a.length; i++) {
			if(i>0) s += ",";
			s += "a[" + i + "]";
		}
		s += ");";
		eval(s);
		delete o[x];
		return r;
	},
	bind: function(o) {
		if(!window.__objs) {
			window.__objs = [];
			window.__funcs = [];
		}

		var objId = o.__oid;
		if(!objId)
			__objs[objId = o.__oid = __objs.length] = o;

		var me = this;
		var funcId = me.__fid;
		if(!funcId)
			__funcs[funcId = me.__fid = __funcs.length] = me;

		if(!o.__closures)
			o.__closures = [];

		var closure = o.__closures[funcId];
		if(closure)
			return closure;

		o = null;
		me = null;

		return __objs[objId].__closures[funcId] = function() {
			return __funcs[funcId].apply(__objs[objId], arguments);
		};
	}
}, false);

Object.extend(Function, {
	isFunction: function(f) {
		if(f != null && typeof f == "function")
			return true;
		return false;
	}
}, false);

Object.extend(String.prototype, {
	endsWith: function(s) {
		return (this.substr(this.length - s.length) == s);
	},
	startsWith: function(s) {
		return (this.substr(0, s.length) == s);
	},
	trimLeft: function() {
		return this.replace(/^\s*/,"");
	},
	trimRight: function() {
		return this.replace(/\s*$/,"");
	},
	trim: function() {
		return this.trimRight().trimLeft();
	}
}, false);

Object.extend(String, {
	format: function(s) {
		for(var i=1; i<arguments.length; i++) {
			s = s.replace("{" + (i -1) + "}", arguments[i]);
		}
		return s;
	},
	isNullOrEmpty: function(s) {
		if(s == null || s.length == 0)
			return true;
		return false;
	}
}, false);

Object.extend(Array.prototype, {
	push: function(o) {
		this[this.length] = o;
	},
	clear: function() {
		this.length = 0;
	},
	shift: function() {
		if(this.length == 0) return null;
		var o = this[0];
		var a = this;
		this.clear();
		for(var i=1; i<a.length; i++) {
			this.push(a[i]);
		}
		delete a;
		return o;
	}
}, false);

// JavaScript namespaces

Object.extend(window, {
	addNamespace: function(ns) {
		var nsParts = ns.split(".");
		var root = window;
		for(var i=0; i<nsParts.length; i++) {
			if(typeof root[nsParts[i]] == "undefined")
				root[nsParts[i]] = {};
			root = root[nsParts[i]];
		}
	}
}, false);

// Browser related properties

addNamespace("MS.Browser");
MS.Browser.isIE = (window.navigator.appName.toLowerCase().indexOf('explorer') != -1 || window.navigator.appName.toLowerCase().indexOf('msie') != -1 );
if(window.navigator.userAgent.toLowerCase().indexOf('opera') != -1) MS.Browser.isIE = false;

// Debugging

addNamespace("MS.Debug");

Object.extend(MS.Debug, {
	enabled: false,
	trace: function(s) {
		window.status = s;
	}
}, false);

// DHTML related functions

addNamespace("MS.Position");

Object.extend(MS.Position, {
	getLocation: function(ele) {
		var x = 0;
		var y = 0;
		var p;
		for(p=ele; p; p=p.offsetParent) {
			if(p.offsetLeft && p.offsetTop) {
				x += p.offsetLeft;
				y += p.offsetTop;
			}
		}
		return {left:x,top:y};
	},
	getBounds: function(ele) {
		var offset = MS.Position.getLocation(ele);
		var width = ele.offsetWidth;
		var height = ele.offsetHeight;
		return {left:offset.left, top:offset.top, width:width, height:height};
	}
}, false);

// Event binding for DHTML

function addEvent(o, evType, f, capture) {
	if(o.addEventListener) {
		o.addEventListener(evType, f, capture);
		return true;
	} else if (o.attachEvent) {
		var r = o.attachEvent("on" + evType, f);
		return r;
	} else {
		// alert("Handler could not be attached");
	}
} 

function removeEvent(o, evType, f, capture) {
	if(o.removeEventListener) {
		o.removeEventListener(evType, f, capture);
		return true;
	} else if (o.detachEvent) {
		o.detachEvent("on" + evType, f);
	} else {
		// alert("Handler could not be removed");
	}
}

var Class = {
	create: function() {
		return function() {
			if(typeof this.initialize == "function")
				this.initialize.apply(this, arguments);
		}
	}
}

// Replacement for document.getElementById

function $() {
  var elements = new Array();
  for (var i = 0; i < arguments.length; i++) {
    var element = arguments[i];
    if (typeof element == 'string')
      element = document.getElementById(element);
    if (arguments.length == 1)
      return element;
    elements.push(element);
  }
  return elements;
}

// Helper classes (.NET style)

function StringBuilder() {
	this.s = [];
}

Object.extend(StringBuilder.prototype, {
	append: function(v) {
		this.s.push(v);
	},
	clear: function() {
		this.s.length = 0;
	},
	toString: function() {
		return this.s.join("");
	}
});
