/**
 * Runner main object. 
 * 
 * JS files include order of inheritance, for example:
 * 1. Runner.js (main functionality)
 * 2. Validate.js (validations utilities)
 * 3. ControlManager.js (global object, for controls manage)
 * 4. Control.js (base abstract class for all controls)
 * 5. All controls in any order.
 */
var Runner = {version: '2.0'};
/**
 * Copies all the properties of config to obj.
 * @param {Object} obj The receiver of the properties
 * @param {Object} config The source of the properties
 * @param {Object} defaults object literal that will be applied first
 * @return {Object} returns obj
 * @member Runner apply
 */
Runner.apply = function(obj, cfg, defaults){
	// third argument passed copy it first
    if(defaults){        
        Runner.apply(obj, defaults);
    }
    // copy config and override defaults if they cross
    if(obj && cfg && typeof cfg == 'object'){
        for(var prop in cfg){
            obj[prop] = cfg[prop];
        }
    }
    return obj;
};


/**
 * Reusable empty function
 */
Runner.emptyFn = function(){};
/**
 * Main RunnerJS functionality
 */
(function(){
	
	var idCounter = 0;
	var zIndexMax = 0;
    var userAgent = navigator.userAgent.toLowerCase();

    var isOpera = userAgent.indexOf("opera") > -1; 
    var isIE = (!isOpera && userAgent.indexOf("msie") > -1); 
	var isIE7 = (!isOpera && userAgent.indexOf("msie 7") > -1); 
	var isIE8 = (!isOpera && userAgent.indexOf("msie 8") > -1); 
	var isChrome = userAgent.indexOf("chrome") > -1; 
	var isSafari = (!isChrome && (/webkit|khtml/).test(userAgent)); 
	var isSafari3 = (isSafari && userAgent.indexOf('webkit/5') != -1); 
	var isGecko = (!isSafari && !isChrome && userAgent.indexOf("gecko") > -1); 
    var isGecko3 = (isGecko && userAgent.indexOf("rv:1.9") > -1);
    var isSecure = (window.location.href.toLowerCase().indexOf("https") === 0);
        
    // copy properties to main object
    Runner.apply(Runner, {
		/**
         * Implements inheritance, on class-based model. 
         * Inherites one class from another and optionally overrides properties with third argument - object literal.
         * Function support three or two arguments call. With two arguments pass superclass as first, and literal with properties to override as second.
         * In three arguments call pass subclass, parent and object literal to copy properties in subclass
         * Example of usage

	    Runner.controls.TextArea = Runner.extend(Runner.controls.Control,{
			constructor: function(cfg){		
				this.addEvent(["change", "keyup"]);		
				// call parent
				Runner.controls.TextArea.superclass.constructor.call(this, cfg);
				// change input type, because textarea don't have type attr
				this.inputType = "textarea";		
			},
			getForSubmit: function(){
				return [this.valueElem.clone().val(this.getValue())]
			}
		});

         * @param {Function} subclass The class which inheriting the functionality
         * @param {Function} superclass The class for extend
         * @param {Object} overrides (optional) A literal object with properties which are copied into the subclass's prototype
         * @return {Function} The subclass constructor.
         * @method extend
         */
		extend: function(){
		    // inline overrides function
		    var overrideFunc = function(obj){
		        for(var prop in obj){
		            this[prop] = obj[prop];
		        }
		    };
		    // constructor of simple Object class
		    var baseObjConstr = Object.prototype.constructor;
			// create closure function
		    return function(subBase, supPar, overrides){
		    	// if function called with 2 paramters, superclass and object literal
		        if(typeof supPar == 'object'){
		        	// change vars, because called with 2 params
		            overrides = supPar;		            
		            supPar = subBase;
		            // if contructor won't overriden, call parent with all passed args
		            subBase = (overrides.constructor != baseObjConstr) ? overrides.constructor : function(){supPar.apply(this, arguments);};
		        }
		        // create temp function and vars with prototypes
		        var F = function(){}, subBaseProt, supParProt = supPar.prototype;
		        // change temp functiion prototype
		        F.prototype = supParProt;
		        // create new incstance of prototype, this will solve problem of one prototype chain
		        subBaseProt = subBase.prototype = new F();
		        // take care of inheritance, reset constructor
		        subBaseProt.constructor=subBase;
		        // make link to parent contructor
		        subBase.superclass=supParProt;
		        // reset parent constructor, don't know for what
		        if(supParProt.constructor == baseObjConstr){
		            supParProt.constructor=supPar;
		        }
		        // add override function
		        subBase.override = function(obj){
		            Runner.override(subBase, obj);
		        };
		        // add override to prototype
		        subBaseProt.override = overrideFunc;
		        // copy properties
		        Runner.override(subBase, overrides);
		        // add extend function
		        subBase.extend = function(obj){Runner.extend(subBase, obj);};
		        // return new class (constructor function)
		        return subBase;
	    	};
        }(),
		/**
         * Copies and replaces properties of one object with another
         * @param {Object} baseclass
         * @param {Object} object literal
         * @method override
         */
		override: function(origClass, overrides){
		    if(overrides){
		        var origProt = origClass.prototype;
		        // copy all properties to prototype
		        for(var method in overrides){
		            origProt[method] = overrides[method];
		        }
		        
		        if(Runner.isIE && overrides.toString != origClass.toString){
		            origProt.toString = overrides.toString;
		        }
		    }
		},
		
		getControl: function(rowId, fName){
			return Runner.controls.ControlManager.getAt(false, rowId, fName);
		},
		/**
		 * Loads javascript from file
		 * fileName {string} name of file to be loaded
		 */
		loadJS: function(src, onload, scope){
			scope = scope || this;
			var js = document.createElement('script');
			js.setAttribute('type', 'text/javascript');
			js.setAttribute('src', src);
			if(onload && Runner.isIE){
				js.onreadystatechange = function(){			
					if (js.readyState == 'complete' || js.readyState == 'loaded'){
						onload.call(scope);
					}
				};
			}else if(onload){
				js.onload = function(){
					onload.call(scope);
				}
			}
			document.getElementsByTagName('HEAD')[0].appendChild(js);
		},	
		/**
		 * Decodes after encoded str_replace(array("&","<",">"),array("&amp;","&lt;","&gt;"),$jscode);
		 * @param {string}
		 * @return {string}
		 */
		htmlDecode: function(txt){
			txt = txt.replace(/&gt;/ig,"\>");
			txt = txt.replace(/&lt;/ig,"\<");
			txt = txt.replace(/&amp;/ig,"&");	
			return txt;
		},
		/**
		 * Creates namesapce
		 * @method
		 */
		namespace: function(name){
			var params = name.split('.'), current = Runner;
			for(var i=1;i<params.length;i++){
				if (!current[params[i]]){
					current[params[i]] = {};
				}
				current = current[params[i]];
			}
			
			return current;
		},
		/**
		 * Generates unique id
		 */
		genId: function(pref){
			return ++idCounter;
		},
		
		setIdCounter: function(num){
			if (typeof num != 'number'){
				return false;
			}
			idCounter += ++num;
		},
		
		getZindex: function(elObj){			
			if (elObj){
				$(elObj).css("z-index", ++zIndexMax);				
			}
			return zIndexMax;			
		},
		
		setZindex: function(counter){
			if (zIndexMax < counter){
				zIndexMax = ++counter;
			}
		},
		/**
		 * Replace all except numbers and strings into _
		 */
		goodFieldName: function(fName){	
			return fName.replace(/\W/g, '_');
		},
		
		getSearchController: function(id){
			return window['searchController'+id];
		},
		
		isArray: function(toCheck){
			return toCheck && typeof toCheck.splice == 'function' && typeof toCheck.length == 'number';
		},
		
		deepCopy: function(obj, cfg){
			// recursively copy objects
		    if(cfg && typeof cfg == 'object'){
		        for(var prop in cfg){
		        	if (typeof cfg[prop] == 'object' && !Runner.isArray(cfg[prop])){
		        		if (typeof obj[prop] != 'object'){
		        			obj[prop] = {};
		        		}
		        		Runner.deepCopy(obj[prop], cfg[prop]);
		        	}else{
		        		obj[prop] = cfg[prop];	
		        	}            
		        }
		    }
		    return obj;
		},
		/**
         * True if browser is Opera.
         * @type Boolean
         */
        isOpera : isOpera,
        /**
         * True if browser is Mozilla
         * @type Boolean
         */
        isGecko : isGecko,
        /**
         * True if browser is Firefox 2++
         * @type Boolean
         */
        isGecko2 : isGecko && !isGecko3,
        /**
         * True if browser is Firefox 3++
         * @type Boolean
         */
        isGecko3 : isGecko3,        
        /**
         * True if browser is Safari.
         * @type Boolean
         */
        isSafari : isSafari,
        /**
         * True if browser is Safari 3++
         * @type Boolean
         */
        isSafari3 : isSafari3,
        /**
         * True if browser is Safari 2++
         * @type Boolean
         */
        isSafari2 : isSafari && !isSafari3,
        /**
         * True if browser is Internet Explorer.
         * @type Boolean
         */
        isIE : isIE,
        /**
         * True if browser is Internet Explorer 6++
         * @type Boolean
         */
        isIE6 : isIE && !isIE7 && !isIE8,
        /**
         * True if browser is Internet Explorer 7++
         * @type Boolean
         */
        isIE7 : isIE7,
        /**
         * True if browser is Internet Explorer 8++
         * @type Boolean
         */
        isIE8 : isIE8,
        /**
         * True if browser is Chrome.
         * @type Boolean
         */
        isChrome : isChrome,        
        isSecure : isSecure,
        
        debugMode: false
        
	});
	
    Runner.ns = Runner.namespace;
})();


/**
 * Controls objects package
 * @type {object}
 */
Runner.namespace('Runner.controls');
/**
 * Search objects package
 * @type {object} 
 */
Runner.namespace('Runner.search');


/**
 * produces a string in which '<', '>', and '&' are replaced with their HTML entity equivalents. 
 * This is essential for placing arbitrary strings into HTML texts. So, "if (a < b && b > c) {".entityify()
 * produces
 * "if (a &lt; b &amp;&amp; b &gt; c) {"
 * @return {string}
 */
String.prototype.entityify = function () {
    return this.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace('"', '&quot;');
};
/**
 * produces a quoted string. 
 * This method returns a string that is like the original string except 
 * that all quote and backslash characters are preceded with backslash.
 * @return {string}
 */
String.prototype.quote = function () {
	return this.replace("\\","\\\\").replace("'","\\'");
};
/**
 * does variable substitution on the string. 
 * It scans through the string looking for expressions enclosed in { } braces. 
 * If an expression is found, use it as a key on the object, and if the key has a string value or number value, 
 * it is substituted for the bracket expression and it repeats. This is useful for automatically fixing URLs. So
 * param = {domain: 'lala.com', media: 'http://zhuzhu.com/'};
 * url = "{media}logo.gif".xTempl(param);
 * produces a url containing "http://zhuzhu.com/logo.gif".
 * @param {object} o
 * @return {string}
 */
String.prototype.xTempl = function (o) {
    return this.replace(/{([^{}]*)}/g,
        function (a, b) {
            var r = o[b];
            return typeof r === 'string' || typeof r === 'number' ? r : a;
        }
    );
};
/**
 * method removes whitespace characters from the beginning and end of the string.
 * @return {string}
 */
String.prototype.trim = function () {
    return this.replace(/^\s+|\s+$/g, "");
}; 

String.prototype.slashdecode = function(){
	var out = '';
	var pos = 0;
	for ( var i = 0; i < this.length - 1; i++ )
	{
		var c = this.charAt(i);
		if( c == '\\' )
		{
			out += this.substr(pos,i-pos);
			pos = i + 2;
			var c1 = this.charAt(i+1);
			i++;
			if ( c1 == '\\' ) {
				out += "\\";
			} else if ( c1 == 'r' ) {
				out += "\r";
			} else if ( c1 == 'n') {
				out += "\n";
			} else {
				i--;
				pos-=2;
			}
		}
	}
	if ( pos < this.length )
		out += this.substr(pos);
	
	return out;
};

/**
 * Checks if value exist in array
 * @param {mixed} value val to search
 * @param {bool} caseSensitive
 * @return {Boolean}
 */
Array.prototype.isInArray = function(value, caseSensitive){	
	for (var i=0; i < this.length; i++) {
		if (caseSensitive) {
			if(this[i] == value) { 
				return true; 
			}
		}else{			
			if(this[i].toString().toLowerCase() == value.toString().toLowerCase()) { 
				return true; 
			}
		}
	}
	return false;
};
/**
 * Counts elements in array
 * @param {mixed} value val to search
 * @param {bool} caseSensitive
 * @return {int}
 */
Array.prototype.countElems = function(value, caseSensitive){	
	var count = 0;
	for (var i=0; i < this.length; i++) {
		if (caseSensitive) {
			if(this[i] == value) { 
				count++;
			}
		}else{			
			if(this[i].toString().toLowerCase() == value.toString().toLowerCase()) { 
				count++; 
			}
		}
	}
	return count;
};

/**
 * Return index of element in array. If element doesn't exist in array, returns -1
 * @param {mixed} value value to search
 * @param {} callBack link to function that may used to comparison, 
 * accepts arguemnts:
 * 		value {mixed} value to search
 * 		elem {mixed} current array element in loop
 * 
 * @param {bool} caseSensitive doesn't work with callBack function
 * @return {int} index of element if found, or -1 if element doesn't exist in array
 */
Array.prototype.getIndexOfElem = function(value, callBack, caseSensitive){
	for (var i=0; i < this.length; i++) {
		if (callBack){
			if(callBack(value, this[i])){
				return i;
			}
		}else if (caseSensitive) {
			if (this[i] == value) {
				return i; 
			}
		}else{			
			if (this[i].toString().toLowerCase() == value.toString().toLowerCase()) {
				return i; 
			}
		}
	}
	return -1;
};

Array.prototype.removeElem = function(value, callBack, caseSensitive){
	for (var i=0; i < this.length; i++) {
		if(callBack){
			if(callBack(value, this[i])){
				this.splice(i, 1);
				return this;
			}
		}else if(caseSensitive) {
			if (this[i] == value) {
				this.splice(i, 1);
				return this;
			}
		}else{			
			if (this[i].toString().toLowerCase() == value.toString().toLowerCase()) {
				this.splice(i, 1);
				return this;
			}
		}
	}
	return this;
};

Array.prototype.copy = function(){
	var copy = [];
	for (var i=0; i < this.length; i++) {
		copy[i] = this[i];
	}
	return copy;
};

Runner.apply(Function.prototype, {
    createCallback : function(){
        var args = arguments;
        var func = this;
        return function() {
            return func.apply(window, args);
        };
    },

    createDelegate : function(obj, args, appendArgs){
        var func = this;
        return function() {
            var callArgs = args || arguments;
            if(appendArgs === true){
                callArgs = Array.prototype.slice.call(arguments, 0);
                callArgs = callArgs.concat(args);
            }else if(typeof appendArgs == "number"){
                callArgs = Array.prototype.slice.call(arguments, 0); 
                var applyArgs = [appendArgs, 0].concat(args); 
                Array.prototype.splice.apply(callArgs, applyArgs); 
            }
            return func.apply(obj || window, callArgs);
        };
    }
});
Runner.namespace('Runner.util');

(function(){
	
	var createDelayed = function(hn, obj, scope){
        return function(){
            var argsArr = Array.prototype.slice.call(arguments, 0);
            setTimeout(function(){
                hn.apply(scope, argsArr);
            }, obj.delay || 10);
        };
    };
    
    var createSingle = function(hn, e, fn, scope){
        return function(){
            e.removeListener(fn, scope);
            return hn.apply(scope, arguments);
        };
    };    
    
    var createBuffered = function(hn, obj, scope){
        var task = new Runner.util.DelayedTask();
        return function(){
            task.delay(obj.buffer, hn, scope, Array.prototype.slice.call(arguments, 0));
        };
    };

    Runner.util.Event = function(obj, name){
        this.name = name;
        this.obj = obj;
        this.listeners = [];
    };

    Runner.util.Event.prototype = {
    	
    	createListener: function(fn, scope, obj){
            obj = obj || {};
            scope = scope || this.obj;
            var ls = {
            	fn: fn, 
            	scope: scope, 
            	options: obj
            };
            var hn = fn;
            if(obj.delay){
                hn = createDelayed(hn, obj, scope);
            }
            if(obj.single){
                hn = createSingle(hn, this, fn, scope);
            }
            if(obj.buffer){
                hn = createBuffered(hn, obj, scope);
            }
            ls.fireFn = hn;
            return ls;
        },

        getListenerIndex: function(fn, scope){
            scope = scope || this.obj;
            var length = this.listeners.length,
            	ls;
            for(var i = 0; i < length; i++){
                ls = this.listeners[i];
                if(ls.fn == fn && ls.scope == scope){
                    return i;
                }
            }
            return -1;
        },        
        
    	fire: function(){
            var scope,
            	ls,
            	length = this.listeners.length;
            	
            if(length > 0){
                this.firing = true;
                var argsArr = Array.prototype.slice.call(arguments, 0);
                for(var i = 0; i < length; i++){
                    ls = this.listeners[i];
                    if(ls.fireFn.apply(ls.scope || this.obj || window, arguments) === false){
                        this.firing = false;
                        return false;
                    }
                }
                this.firing = false;
            }
            return true;
        },
        
        on: function(fn, scope, options){
            scope = scope || this.obj;
            if(!this.isListening(fn, scope)){
                ls = this.createListener(fn, scope, options);
                if(!this.firing){
                    this.listeners.push(ls);
                }else{ 
                    this.listeners = this.listeners.slice(0);
                    this.listeners.push(ls);
                }
            }
        },       

        isListening: function(fn, scope){
            return this.getListenerIndex(fn, scope) != -1;
        },

        removeListener: function(fn, scope){
            var index;
            if((index = this.getListenerIndex(fn, scope)) != -1){
                if(!this.firing){
                    this.listeners.splice(index, 1);
                }else{
                    this.listeners = this.listeners.slice(0);
                    this.listeners.splice(index, 1);
                }
                return true;
            }
            return false;
        },

        clearListeners: function(){
            this.listeners = [];
        }        
    };
})();


/**
 * @class Runner.util.Observable
 * Observer-subscriber class
 */
Runner.util.Observable = Runner.extend(Runner.emptyFn, {
	
	filterOptRe: /^(?:scope|delay|buffer|single)$/,
	
	addEvents: function(obj){
        if(!this.events){
            this.events = {};
        }
        if(typeof obj == 'string'){
            for(var i = 0, a = arguments, v; v = a[i]; i++){
                if(!this.events[a[i]]){
                    this.events[a[i]] = true;
                }
            }
        }else{
            Runner.apply(this.events, obj);
        }
    },
	
    fireEvent: function(){
        if(this.eventsSuspended !== true){
            var ce = this.events[arguments[0].toLowerCase()];// || this.events[arguments[0]];
            if(typeof ce == "object"){
                return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
            }
        }
        return true;
    },

    on: function(evName, fn, scope, obj){
        if(typeof evName == "object"){
            obj = evName;
            for(var event in obj){
                if(this.filterOptRe.test(event)){
                    continue;
                }
                if(typeof obj[event] == "function"){
                    this.on(event, obj[event], obj.scope,  obj);
                }else{
                    this.on(event, obj[event].fn, obj[event].scope, obj[event]);
                }
            }
            return;
        }
        obj = (!obj || typeof obj == "boolean") ? {} : obj;
        evName = evName.toLowerCase();
        var ce = this.events[evName] || true;
        if(typeof ce == "boolean"){
            ce = new Runner.util.Event(this, evName);
            this.events[evName] = ce;
        }
        ce.on(fn, scope, obj);
    },

    un: function(evName, fn, scope){
        var ce = this.events[evName.toLowerCase()];
        if(typeof ce == "object"){
            ce.removeListener(fn, scope);
        }
    },

    purgeListeners: function(){
        for(var event in this.events){
            if(typeof this.events[event] == "object"){
                 this.events[event].clearListeners();
            }
        }
    },    
   
    suspendEvents: function(){
        this.eventsSuspended = true;
    },

    resumeEvents: function(){
        this.eventsSuspended = false;
    }
});


// register new namespace
Runner.namespace('Runner.util');

/**
 * @class Runner.util.DelayedTask
 * method for performing setTimeout where a new timeout cancels the old timeout. 
 * @param {Function} fn (optional) The default function to timeout
 * @param {Object} scope (optional) The default scope of that timeout
 * @param {Array} args (optional) The default Array of arguments
 */
Runner.util.DelayedTask = function(fn, scope, args){
    var id = null, delay, time;

    var call = function(){
        var now = new Date().getTime();
        if(now - time >= delay){
            clearInterval(id);
            id = null;
            fn.apply(scope, args || []);
        }
    };
    /**
     * Cancels any pending timeout and queues a new one
     * @param {Number} delay The milliseconds to delay
     * @param {Function} newFn (optional) Overrides function passed to constructor
     * @param {Object} newScope (optional) Overrides scope passed to constructor
     * @param {Array} newArgs (optional) Overrides args passed to constructor
     */
    this.delay = function(newDelay, newFn, newScope, newArgs){
        if(id && delay != newDelay){
            this.cancel();
        }
        delay = newDelay;
        time = new Date().getTime();
        fn = newFn || fn;
        scope = newScope || scope;
        args = newArgs || args;
        if(!id){
            id = setInterval(call, delay);
        }
    };

    /**
     * Cancel the last queued timeout
     */
    this.cancel = function(){
        if(id){
            clearInterval(id);
            id = null;
        }
    };
};





/**
 * Global object for loading scripts and css files
 * @object
 */
Runner.util.ScriptLoader = Runner.extend(Runner.util.Observable, {
	
	
	/**
	 * Array of CSS files for loading
	 * @type 
	 */
	cssFiles: [],
	/**
	 * Array of file names for load
	 * @type {array}
	 */
	jsFiles: [],
	
	constructor: function(cfg){
		Runner.util.ScriptLoader.superclass.constructor.call(this, cfg);
		this.addEvents('filesLoaded');
		
		this.on('filesLoaded', function(){
			if (Runner.pages){
				Runner.pages.PageManager.initPages();
			}
		}, this, {single: true});				
		
	},
	/**
	 * Add js file to load queue
	 * @param {array} files
	 * @param any param except first will be added for requirements array
	 */
	addJS: function(files){
		var isAdded = false;
		// loop through all files to add
		for (var i=0;i<files.length;i++){
			// check if such file was added before
			for (var j=0;j<this.jsFiles.length;j++){
				if (this.jsFiles[j].name == files[i]){
					isAdded = true;
					break;
				}					
			}
			// add only new files
			if(!isAdded){
				// add files to array of file names
				this.jsFiles.push({
					name: files[i],
					isLoaded: false,
					//	add requirements, all passed arguments, except first
					requirements: Array.prototype.slice.call(arguments, 1)
				});
			}
			// reinit var
			isAdded = false;
		}
		
	},
	
	/**
	 * Method for load CSS files
	 * @param files {array}
	 */
	loadCSS: function (files){
		for (var i=0;i<files.length; i++){			
			// check if file exist in array of CSS files, try to get it's index
			var idx = this.cssFiles.getIndexOfElem(files[i], function(val, arrElem){
				if (val == arrElem.name){
					return true;
				}
			});
			// if file already added and it was loaded, than return true
			if (idx!=-1 && this.cssFiles[idx].isLoaded){
				continue;
			}			
			this.cssFiles.push({
				name: files[i],
				isLoaded: true
			})
			// load file			
			var head = $(document).find('head')[0];
			var css = document.createElement('link');
			css.setAttribute('rel', 'stylesheet');
			css.setAttribute('type', 'text/css');
			css.setAttribute('href', files[i]+".css");
			head.appendChild(css);
		}
	},
	
	load: function(){
		// load all js files
		for(var i=0;i<this.jsFiles.length;i++){
			this.loadJS(i);		
		}	
	},
	/**
	 * Load file from queue
	 * @param {int} idx file index
	 * @return {bool} true if success
	 * @method
	 * @private
	 */
	loadJS: function(idx){
		// return if no file obj for this file
		if (!this.jsFiles[idx]){
			return false;
		}
		// if loaded, load dependent files
		if(this.jsFiles[idx].isLoaded){
			this.postLoad(idx);
			return true;
		}
		// check requirements
		if (!this.checkReq(this.jsFiles[idx])){
			return false;
		}
		// file loading started already
		if(this.jsFiles[idx].isStarted){
			return false;
		}
		// load file
		this.jsFiles[idx].isStarted = true;
		var js = document.createElement('script');
		js.setAttribute('type', 'text/javascript');
		js.setAttribute('src', this.jsFiles[idx].name);
		var sl = this;
		//	add onload event handler
		if(Runner.isIE){
			js.onreadystatechange = function(){			
				if (js.readyState == 'complete' || js.readyState == 'loaded'){
					sl.postLoad(idx);
				}
			};
		}else{
			js.onload = function() {	
				sl.postLoad(idx);
			};
		}
		document.getElementsByTagName('HEAD')[0].appendChild(js);
		return true;
	},
	
		
	/**
	 * Checks is required files are loaded
	 * @param {object} fileObj
	 * @return {Boolean}
	 */
	checkReq: function(fileObj){	
		// loop through all files
		for(var i=0;i<fileObj.requirements.length;i++){
			// loop through all req
			for(var j=0;j<this.jsFiles.length;j++){
				// if req cotains loaded file, than try to load it
				if (fileObj.requirements[i] == this.jsFiles[j].name && !this.jsFiles[j].isLoaded){ 
					return false;
				}
			}
		}
		return true;			
	},
	/**
	 * After event handler. Called after file loaded.
	 * @method
	 */
	postLoad: function(idx){
		this.jsFiles[idx].isLoaded = true;			
		this.loadDependent(idx);
		var loadedAll = true;
		
		for(var i=0;i<this.jsFiles.length;i++){
			if (!this.jsFiles[i].isLoaded){
				loadedAll = false;
				break;
			}
		}
		if (loadedAll){
			this.fireEvent('filesLoaded');
		}
	},
	/**
	 * Call load for files, which are dependent to file with index = idx
	 * @param {int} idx
	 */
	loadDependent: function(idx){
		// loop through all files
		for(var i=0;i<this.jsFiles.length;i++){
			// loop through all req
			for(var j=0;j<this.jsFiles[i].requirements.length;j++){
				// if req cotains loaded file, than try to load it
				if (i != idx && this.jsFiles[i].requirements[j] == this.jsFiles[idx].name){ 
					this.loadJS(i);
				}
			}
		}
	}	
});
Runner.util.ScriptLoader = new Runner.util.ScriptLoader(); 
// create these classes only for IE6
//if (Runner.isIE6){
	/**
	 * IE utils objects package
	 * @type {object}
	 */
	Runner.namespace('Runner.util.IEHelper');
	/**
	 * iframe class. Used to cover select tags in IE6
	 * for correct work, el wich need to be covered should be child of document body 
	 * and have position absolute and method getPos should work with findPos, not with getAbsolutePosition
	 * 
	 * 
	 * @cfg {int} x
	 * @cfg {int} y
	 * @cfg {int} w
	 * @cfg {int} h
	 * @cfg {int} id options, if not passed, 
	 * @return {obj} iframe obj
	 */
	Runner.util.IEHelper.iframe = function(cfg){
		// init params		
		var cfg = cfg || {}, id;
		// if passed cfg object literal
		if (cfg.constructor === Object){
			cfg.w = cfg.w || 0, cfg.h = cfg.h || 0, cfg.t = cfg.t || 0, cfg.l = cfg.l || 0, id = cfg.id || Runner.genId();
		// deal with DOM element
		}else{
			var el = cfg;
			id = Runner.genId();			
		}
			
		// create iframe jQuery obj
		var iframe = $('#'+id);		
		if (!iframe.length){
			$(document).find('body').append('<iframe src="javascript:false;" id="'+id+'" frameborder="1" vspace="0" hspace="0" marginwidth="0" marginheight="0" scrolling="no" style="background:white;position:absolute;display:block;opacity:0;filter:alpha(opacity=0);"></iframe>');
			//$(document).find('body').append('<iframe id="'+id+'" frameborder="1" vspace="0" hspace="0" marginwidth="0" marginheight="0" scrolling="no" style="border: 1px solid red;background:white;position:absolute;display:block;"></iframe>');			
			iframe = $('#'+id);
		}	
		// obj with methods
		var iframeObj = {
			/**
			 * Move iframe to coordinates
			 * @param {int} l
			 * @param {int} t
			 * @return {obj} iframe obj
			 */
			move: function(t, l){
				if (t !== undefined && l !== undefined){
					iframe.css('top', t+'px').css('left', l+'px');
				}
				return this;
			},
			/**
			 * Completely removes iframe from DOM
			 */
			destroy: function(){
				iframe.remove();
				return this;
			},
			/**
			 * Move iframe to coordinates
			 * @param {int} w
			 * @param {int} h
			 * @return {obj} iframe obj
			 */
			resize: function(w, h){
				if (w !== undefined && h !== undefined){
					iframe.css('height', h).css('width', w);
				}
				return this;
			},
			/**
			 * Hide iframe, for next use
			 */
			hide: function(){
				iframe.hide();
				return this;
			},
			/**
			 * Show iframe, with old coors
			 */
			show: function(){
				iframe.show();
				return this;
			},
			/**
			 * Move iframe to coordinates
			 * @cfg {int} l
			 * @cfg {int} t
			 * @cfg {int} w
			 * @cfg {int} h
			 * @return {obj} iframe obj
			 */
			reset: function(coors){	
				// use old coors, if not passed new
				coors = coors || this.getPos() || cfg;
				// set postion and size and show iframe
				this.move(coors.t, coors.l).resize(coors.w, coors.h).show();
				// add z-index for iframe
				Runner.getZindex(iframe);
				return this;
			},
			/**
			 * Calculates postion of iframe, when DOM element passed to constructor
			 * @method
			 * @return {obj} literal with coordinates
			 */
			getPos: function(){	
				// lazy init func
				if (!el){
					this.getPos = function(){return false;}
				}else{
					this.getPos = function(){
						var posObj = getAbsolutePosition(el), coors = {};
						coors.w = el.offsetWidth, coors.h = el.offsetHeight, coors.t = posObj.t, coors.l = posObj.l;
						return coors;
					}
				}
				this.getPos();
			}
		}
		// return object
		return iframeObj.reset();
	}	
	/**
	 * Another way to solve IE select element coverage.
	 * 
	 * @param {DOM} el
	 * @return {object}
	 */
	Runner.util.IEHelper.selectsHider = function(el){
		// init private vars
		var selToHide = [], elem = el;
		
		return {
			/**
			 * Method checks intersection of two element by there center coordinates and dimensions
			 */
			checkIntersection: function(selCoors, elCoors){
				if ((Math.abs((elCoors.x-selCoors.x))<=(elCoors.w+selCoors.w)/2) 
					&& (Math.abs((elCoors.y-selCoors.y))<=(elCoors.h+selCoors.h)/2)){
						return true; 
				}
			},
			/**
			 * Calcs center of element
			 * @param {object} coors literal with coordinates
			 */
			getCenter: function(coors){
				coors.x = coors.l+coors.w/2;
				coors.y = coors.t+coors.h/2;
				return coors;
			},
			/**
			 * Hide select that were found in last getSelects call
			 */
			hideSels: function(){
				for(var i=0;i<selToHide.length;i++){
					$(selToHide[i]).hide();
				}
				return this;
			},
			/**
			 * Show hidden selects that were found in last getSelects call
			 */
			showSels: function(){				
				for(var i=0;i<selToHide.length;i++){
					$(selToHide[i]).show();
				}
				return this;
			},
			/**
			 * Check all select for intersection
			 */
			getSelects: function(elPos){
				// init vars
				var elCoors = elPos || {}, selToCheck = $('select');
				// clear old array with selects
				selToHide = [];
				// if element coords not passed
				if (!elPos){
					var pos = findPos(el);
					// add 10px for better coverage 
					elCoors.l = pos[0];
					elCoors.t = pos[1];
					elCoors.w = el.offsetWidth;
					elCoors.h = el.offsetHeight;
				}
				// get center of element
				elCoors = this.getCenter(elCoors);
				// check each select
				var coors = {};
				for(var i=0; i<selToCheck.length;i++){
					// get select position, coordinates and dimension
					pos = findPos(selToCheck[i]);
					coors.l = pos[0];
					coors.t = pos[1];
					coors.w = selToCheck[i].offsetWidth;
					coors.h = selToCheck[i].offsetHeight;
					coors = this.getCenter(coors);
					// check intersection
					if (this.checkIntersection(coors, elCoors)){
						selToHide.push(selToCheck[i]);
					}
				}
				// return array to hide
				return selToHide;
			}			
		}	
	}
//} 
