/*
	<jsapi>
		<author>2006 Florian Hoech</author>
		<file>jsapi.js</file>
	</jsapi>
*/

jsapi = function() {
	return new jsapi.jsapi(arguments);
};
jsapi.constants = {
	OBJECTTYPES: {
		OBJECT: 0,
		ARRAY: 1,
		BOOLEAN: 2,
		DATE: 3,
		FUNCTION: 4,
		NUMBER: 5,
		REGEXP: 6,
		STRING: 7
	}
};
jsapi.extend = function(object, _this) {
	jsapi.jsapi.prototype.extend(object, _this)
};
jsapi.jsapi = function() {
	var objectType;
	if (!jsapi.initialized) {
		for (var propertyName in jsapi.dom) if (typeof jsapi.dom[propertyName] == "function" && !jsapi.dom[propertyName]._args) jsapi.dom[propertyName]._args = [jsapi.dom.isNode];
		for (var propertyName in jsapi.regexp) if (typeof jsapi.regexp[propertyName] == "function") jsapi.regexp[propertyName]._args = [function(argument) {
			return argument instanceof RegExp;
		}];
		for (var propertyName in jsapi.string) if (typeof jsapi.string[propertyName] == "function") jsapi.string[propertyName]._args = [function(argument) {
			return typeof argument == "string" || argument instanceof String;
		}];
		for (var propertyName in jsapi) jsapi.extend(jsapi[propertyName], jsapi[propertyName]);
		var arrayMethodNames = [
			//"concat",
			"every",
			"filter",
			"forEach",
			"indexOf",
			"join",
			"lastIndexOf",
			"map",
			"slice",
			"some",
			/* Mutator methods */
			"pop",
			"push",
			"reverse",
			"shift",
			"sort",
			"splice",
			"unshift"
		];
		for (var i = 0; i < arrayMethodNames.length; i ++) {
			(function (arrayMethodName) {
				jsapi.jsapi.prototype[arrayMethodName] = function () {
					var result = Array.prototype[arrayMethodName].apply(this, arguments);
					return typeof result == "object" ? jsapi(result) : (result != null || arrayMethodName == "pop" || arrayMethodName == "shift" ? result : this);
				}
			})(arrayMethodNames[i]);
		};
		jsapi.initialized = true;
	};
	if (this.length == null) this.length = 0;
	for (var i = 0; i < arguments[0].length; i ++) {
		var object = arguments[0][i];
		switch (typeof object) {
			case "function":
				objectType = jsapi.constants.OBJECTTYPES.FUNCTION;
				break;
			case "number":
				objectType = jsapi.constants.OBJECTTYPES.NUMBER;
				break;
			case "string":
				objectType = jsapi.constants.OBJECTTYPES.STRING;
				break;
			default:
				if (!object) continue; // null or undefined
				switch (object.constructor) {
					case Boolean:
						objectType = jsapi.constants.OBJECTTYPES.BOOLEAN;
						break;
					case Date:
						objectType = jsapi.constants.OBJECTTYPES.DATE;
						break;
					case Number:
						objectType = jsapi.constants.OBJECTTYPES.NUMBER;
						break;
					case RegExp:
						objectType = jsapi.constants.OBJECTTYPES.REGEXP;
						break;
					case String:
						objectType = jsapi.constants.OBJECTTYPES.STRING;
						break;
					default:
						objectType = jsapi.constants.OBJECTTYPES.OBJECT;
				}
		};
		switch (objectType) {
			case jsapi.constants.OBJECTTYPES.STRING:
				var expression = object.split(":");
				if (expression.length > 1) switch (expression[0]) {
					case "html":
					case "xhtml":
					case "xml":
						object = jsapi.dom.parseString(expression.slice(1).join(":"));
						break;
					case "xpath":
						object = jsapi.dom.getByXpath(expression.slice(1).join(":"));
						break;
				}
				else if (/<[^<]*>/.test(object)) object = jsapi.dom.parseString(object);
			default:
				if (object instanceof Array) Array.prototype.push.apply(this, object);
				else if (typeof object == "object" && typeof object.length == "number" && object.constructor != String && object.constructor != Function) 
					Array.prototype.push.apply(this, Array.prototype.slice.apply(object));
				else this[this.length ++] = object;
		}
	};
};
jsapi.jsapi.prototype = {
	concat: function(object) {
		var result = jsapi(this);
		jsapi.jsapi.apply(result, [object]);
		return result
	},
	extend: function(object, _this) {
		/*
			if _this evaluates to false, use the current result as "this"-object.
			otherwise, use _this as "this"-object and pass the current result as first argument.
		*/
		for (var propertyName in object) if (typeof object[propertyName] == "function" && 
			propertyName != "$" && 
			propertyName != "extend" && 
			propertyName != "toString" && 
			propertyName != "valueOf") {
			if (!this[propertyName]) {
				this[propertyName] = function() {
					if (this.length) {
						var _callbacks = arguments.callee._callbacks;
						for (var i = 0; i < this.length; i ++) {
							for (var n = 0; n < _callbacks.length; n ++) {
								var _this = _callbacks[n]._this || this[i],
									_arguments = _callbacks[n]._this ? [this[i]].concat(Array.prototype.slice.apply(arguments)) : arguments;
								if (_callbacks[n].hasValidArguments.apply(_callbacks[n], _arguments)) {
									this[i] = _callbacks[n].apply(_this, _arguments);
								}
							}
						}
					};
					return this;
				};
				this[propertyName]._callbacks = [];
			};
			if (!object[propertyName].hasValidArguments) {
				if (object[propertyName]._args) object[propertyName].hasValidArguments = function() {
					for (var i = 0; i < this._args.length; i ++) {
						if (!(typeof this._args[i] == "string" ? 
							typeof arguments[i] == this._args[i] : 
							(typeof this._args[i] == "function" ? 
								this._args[i](arguments[i]) : 
								arguments[i] == this._args[i]))) {
									/*
									alert("arguments[i] : " + (arguments[i]) + "\n" +
										"string : " + (typeof this._args[i] == "string") + " : " + (typeof this._args[i] == "string" ? 
											typeof arguments[i] == this._args[i] : 
											null) + "\n" +
										"function : " + (typeof this._args[i] == "function") + " : " + (typeof this._args[i] == "function" ? 
											this._args[i] + " : " + this._args[i](arguments[i]) : 
											null) + "\n" +
										"arguments[i] == this._args[i] : " + (arguments[i] == this._args[i]));
									*/
									return false;
						}
					};
					return true
				};
				else object[propertyName].hasValidArguments = function() { return true };
			};
			object[propertyName]._this = _this;
			if (this[propertyName]._callbacks.indexOf(object[propertyName]) < 0) this[propertyName]._callbacks.push(object[propertyName]);
		}
	},
	toString: function() {
		return Array.prototype.slice.apply(this).toString();
	}
};
