/**
 * Copyright (c) 2007, SoftAMIS, http://www.soft-amis.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * You are free to copy, modify, or redistribute code. 
 *
 * Author: Alexey Luchkovsky
 * E-mail: jsiner@soft-amis.com
 *
 * Version: 0.099
 * Last modified: 15/05/2007
 */

var COMMONS = {};

COMMONS.isObject = function(anObject){
	return anObject !== null && typeof(anObject) === "object";
};

COMMONS.isArray = function(anObject){
	return anObject instanceof Array;
};

COMMONS.isString = function(anObject){
	return typeof(anObject) === "string";
};

COMMONS.isFunction = function(anObject){
	return typeof(anObject) === "function";
};

COMMONS.isUndefined = function(anObject){
	return anObject === undefined;
};

COMMONS.isDefined = function(anObject){
	return anObject !== null && anObject !== undefined;
};


var JSINER = {

  scriptPrefix:"script/",

	scriptSuffix:".js",

	version: 0.98
};

JSINER.fDependency = {};

JSINER.getConstructor = function(anObject){
	var result = null;
	if (COMMONS.isString(anObject)){
		result = window[anObject];
	}
	else
	if (COMMONS.isFunction(anObject)){
		result = anObject;
	}
	else
	if (COMMONS.isObject(anObject)){
		result = anObject.constructor;
	}
	return result;
};

JSINER.getType = function(anObject){
	var result = (typeof anObject);
	if (COMMONS.isObject(anObject)){
		if ( COMMONS.isRegExp(anObject) ){
			result = "RegExp";
		} else {
			var constr = this.getConstructor(anObject);
			if (COMMONS.isDefined(constr)){
				var cons = constr.toString();
				var index = cons.indexOf('(');
				result = ((index > 0) ? cons.substring(cons.indexOf(' ') + 1, index) : cons);
			}
		}
	}
	return result;
};

JSINER.getScriptURI = function(aURL){
	throw {message:"The JSINER.getScriptURI must be implemented before"};
};

JSINER.isScriptLoaded = function(aKey){
	throw {message: "The JSINER.isScriptLoaded must be implemented before"};
};

JSINER.inject = function(aDependency, aCallBack){
	throw {message:"The JSINER.inject must be implemented before"};
};

JSINER.addDependency = function(aJson){
	if (COMMONS.isDefined(aJson)){
		for (var name in aJson){
			var dependency = this.fDependency[name];
			if ( !COMMONS.isArray(dependency) ){
				this.fDependency[name] = [];
				dependency = this.fDependency[name];
			}

			var value = aJson[name];
			if ( COMMONS.isArray(value) ){
				for( var i=0; i < value.length; i++){
					dependency.push( value[i] );
				}
			} else {
				dependency.push( value );
			}
		}
	}
};

JSINER.getDependency = function(anObject, aClass){
	var type = this.getType(anObject);
	return this.fDependency[type];
};

JSINER.createInstance = function(aConstructor, aClass){
	var result = null;
	if (COMMONS.isFunction(aConstructor) && COMMONS.isFunction(aClass) && aClass !== Object){
		var oldPrototype = aConstructor.prototype;        	// stores object prototype properties
		var oldToString = aConstructor.prototype.toString; 	// stores toString property for IE only

		aConstructor.prototype = new aClass(); 		          // overrides constuctor prototype
		aConstructor.prototype.constructor = aConstructor;	// restores class constructor
		aConstructor.superClass = aClass.prototype;         // adds superclass property

		for (var name in oldPrototype) //restores old prototype properties
		{
			aConstructor.prototype[name] = oldPrototype[name];
		}
		aConstructor.prototype.toString = oldToString;

		aConstructor.$extend = true; // sets flag that the class is already tied
		result = new aConstructor(); // creates new instance of the class
	}
	return result;
};

JSINER.extend = function(anObject, aClass){
	var doExtend = function(anObject, aClass){
		var result = anObject;
		var cons = anObject.constructor;
		if ( COMMONS.isUndefined(cons.$extend) ){
			var dependency = this.getDependency(anObject, aClass);
			if ( COMMONS.isArray(dependency) ){
				var toolkit = this;
				this.inject(dependency, function(){
					var inheritClass = toolkit.getConstructor(aClass);
					result = toolkit.createInstance(cons, inheritClass) || result;
				});
			} else {
				var inheritClass = this.getConstructor(aClass);
				result = this.createInstance(cons, inheritClass) || result;
			}
		}
		return result;
	};

	var result = doExtend.call(this, anObject, aClass);
	var inheritClass = this.getConstructor(aClass);
	if ( COMMONS.isFunction(inheritClass) ){
		inheritClass.call(result);
	}
	return result;
};