//# ----------------| ================================== |--------------------- #\\
//# ----------------|                                    |--------------------- #\\
//# ----------------| The Universal functions or Classes |--------------------- #\\
//# ----------------|                                    |--------------------- #\\
//# ----------------| ================================== |--------------------- #\\


// Name: Get Browser and OS info.
// Description: Checks what browser and OS is. And set variables to TRUE or FALSE.
var USER_DATA = {

    Browser: {
        KHTML: /Konqueror|KHTML/.test(navigator.userAgent) &&
                !/Apple/.test(navigator.userAgent),
        Safari: /KHTML/.test(navigator.userAgent) &&
                /Apple/.test(navigator.userAgent),
        Opera: !!window.opera,
        MSIE: !!(window.attachEvent && !window.opera),
        Gecko: /Gecko/.test(navigator.userAgent) &&
                !/Konqueror|KHTML/.test(navigator.userAgent)
    },

    OS: {
        Windows: navigator.platform.indexOf("Win") > -1,
        Mac: navigator.platform.indexOf("Mac") > -1,
        Linux: navigator.platform.indexOf("Linux") > -1
    }
}

    var IS_IE = USER_DATA['Browser'].MSIE;
    var IS_SAFARI = USER_DATA['Browser'].Safari;
    var IS_OPERA = USER_DATA['Browser'].Opera;
    var IS_MOZILLA = USER_DATA['Browser'].Gecko;
    var IS_KHTML = USER_DATA['Browser'].KHTML;

    var IS_WIN = USER_DATA['OS'].Windows;
    var IS_MAC = USER_DATA['OS'].Mac;
    var IS_LIN = USER_DATA['OS'].Linux;


    var HEAD = document.getElementsByTagName("head")[0];
    //var BODY = document.getElementsByTagName("body")[0];





// Name: Get current style
// Description: CROSSbrowser function for getting current style of element( cs = getComputedStyle(toObject('img1'), null) ). 
//              (after, you must call cs.getPropertyValue("margin-top"))
// Used functions: toObject.
if (!window.getComputedStyle) {
    window.getComputedStyle = function(el, pseudo) {
        this.el = toObject(el);
        this.getPropertyValue = function(prop) {
            var re = /(\-([a-z]){1})/g;
            if (prop == 'float') prop = 'styleFloat';
            if (re.test(prop)) {
                prop = prop.replace(re, function () {
                    return arguments[2].toUpperCase();
                });
            }
            return el.currentStyle[prop] ? el.currentStyle[prop] : null;
        }
        return this;
    }
}





function empty(){}




// Name: Return object.
// Description: Cheks: if elem == object -> returns it, ELSE -> getElementByID and returns object, otherwise returns FALSE.
// Arguments: 
//              elem = STRIGN('id') or OBJECT;
function toObject(elem)
{
    var $elem = elem;
    if(typeof(elem) == "object")
    { 
        return elem;
    }
    else{
            if( elem=document.getElementById(elem))
            { return elem; }
            else { alert("Element with id='"+$elem+"' doesn't exist !"); return false; }
        }
}
//The same function
var $ = toObject;





// Name: Isset object.
// Description: Cheks: if exists object -> returns it, ELSE -> getElementByID and returns object, otherwise returns FALSE.
// Arguments: 
//              elem = STRIGN('id') or OBJECT;
function issetObj(elem)
{
    var $elem = elem;
    if(typeof(elem) == "object")
    { 
        return elem;
    }
    else{
            if( elem=document.getElementById(elem))
            { return elem; }
            else { return false; }
        }
}





// Name: Return array.
// Description: Cheks: if var_ is NOT an ARRAY -> returns Array whith one elem array[var_].
// Arguments: 
//              var_ = any;
function toArray(var_)
{
    if (var_.length == undefined || typeof(var_)=="string") { var_ = [var_]; }
    return var_;
}





// Name: Convert to 'em'.
// Description: Convert params (width, height, ..) from 'px'->'em'. (Returns an Integer number).
// Arguments: 
//              value = NUMBER or STRING('10px');
function toEm(value)
{
    var em = 16;
    value+='';
    if(value.indexOf('em') == -1)
    {
        return parseFloat( parseInt(value)/em ).toFixed(2);
    }
    else
    {
        return parseFloat(value.replace(/em/,''));
    }
}





// Name: Get element's Height. (includes 2 functions)
// Description: Returns element's offsetHeight.
// Arguments: 
//              elem = STRIGN('id') or OBJECT;
function offsetH(elem) {
    elem = toObject(elem);
    return elem.offsetHeight ||
           elem.style.pixelHeight ||
           findOffsetHeight(elem);
}
function findOffsetHeight(elem) {
    var res = 0;
    while ((res == 0) && e.parentNode) {
        e = e.parentNode;
        res = e.offsetHeight;
    }
    return res;
}





// Name: Get position.
// Description: Get's object's position.
// Arguments: 
//              elem = STRIGN('id') or OBJECT;
function getPosition(elem){
    elem = toObject(elem);
    var currentStyle = getComputedStyle(elem, null);
    var x = parseInt(currentStyle.getPropertyValue("left"));
    var y = parseInt(currentStyle.getPropertyValue("top"));
    return {x:x, y:y};
}





// Name: Change position.
// Description: Change object's position.
// Arguments: 
//              obj = STRIGN('id') or OBJECT;
//              x = STRING('100px');
//              y = STRING('0px');
function changePosition(obj,x,y){
    obj = toObject(obj);
    if (y==0 || y) { obj.style.top = y; }
    if (x==0 || x) { obj.style.left = x; }
}





// Name: Create new element.
// Description: Creates new element and returns it.
// Arguments: 
//              elementName = STRING('div');
//              text = STRIGN('Some text in the element');
//              [attributes] = OBJECT( {className: 'important', title: 'summary'} );
function newElement(elementName,text,attributes)
{
	if(typeof elementName != "string" || ( attributes && typeof attributes != "object") ){
	    alert("Wrong type! of introduced variables in newElement() function");
	    return false;
	}
	// Check to prevent bug in IE with creating an INPUT element with Name and Checked attributes
	if(IS_IE && elementName == "input"){
		elementName = '<input name="" ';
		elementName += (attributes.checked) ? ' checked="" />' : '/>';
	}

	var element = document.createElement(elementName);
	
	if(text){
	    element.appendChild(document.createTextNode(text));
	}
	
	if(attributes){
	    for( var i in attributes ) {
		    element[i] = attributes[i];
		    /* 
		        # не забывать, что у обьекта могут быть прописану функции через prototype. Они тоже будут присваиваться 
		    */
	    }
	}
	return element;
}





// Name: Add script.
// Description: Adds javascript to <head> of the page. 
//    result will be: <head>...<script src=src type="text/javascript" id=id></script>...</head> 
// Arguments: 
//              src = STRIGN('all/script.js');
//              id = STRIGN('id');
function addScript(src,id) {
    if (issetObj(id)) { del( toObject(id) ); }
    var script = newElement("script",null,{id:id, type:"text/javascript", src:src});
    HEAD.appendChild(script);
}





// Name: Style element.
// Description: Appends style or an array of styles to an object.
// Arguments: 
//              elem = STRIGN('id') or OBJECT;
//              styles = STRING("color:blue;padding:5px;") or OBJECT( {color: 'blue',padding: '5px'} ) or ARRAY(['color','blue','padding','5px']);
function style(elem,styles){
	elem = toObject(elem);
	if (typeof styles == "string") {
		styles = styles.split(';');
		for (var i = 0; i < styles.length; ++i) {
			elem.style[styles[i].split(':')[0]] = styles[i].split(':')[1];
		}
	}
	else 
		if (typeof styles == "object") {
			if (styles[0]) {
				for (var i = 0; i < styles.length/2; ++i) {
					elem.style[styles[i]] = styles[++i];
				}
			} else {
				for (var i in styles) {
					elem.style[i] = styles[i];
				}
			}
		}
}





// Name: Toggle display.
// Description: Display/Hide object.
// Arguments: 
//              elem = STRING('id') or OBJECT or ARRAY(['id','id'] or ARRAY([object,object] );
function toggleDisplay(elem) {
    elem = $(elem);
    var array = [];

	//if insted of one element we get array of elements  
	if(elem.length>0){
		array = elem;
		array.each($,true);
	}else{
		   	array.push(elem);
	}

    for(var i=0; i<array.length; i++){
		var currentStyle = getComputedStyle(array[i], null);
	    var display = currentStyle.getPropertyValue("display");
		array[i].style.display = (display == "none") ? "block" : "none";
    }

}





// Name: Get elements by Class.
// Description: Returns an array of elements with class=searchClass (if element's class="clas1 searchClass clas2" - reg-test returns true).
// Arguments: 
//              searchClass = STRING('id') or OBJECT or Array(OBJECT,'id');
//              [node] = STRIGN('id') or OBJECT;     To search IN a concrete element
//              [tag] = STRIGN('div');              To search FOR a concrete element
function getElementsByClass(searchClass,node,tag) {
	var classElements = new Array();
	if ( node == null ){ node = document; }
	else{ node = toObject(node); }
		
	if ( tag == null ) { tag = '*'; }
	
	var els = node.getElementsByTagName(tag);
	var elsLen = els.length;
	var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)"); //^\s{0,2}Jekis\s{0,2}$
	for (i = 0, j = 0; i < elsLen; i++) {
		if ( pattern.test(els[i].className) ) {
			classElements[j] = els[i];
			j++;
		}
	}
	return (classElements.length == 0) ? false : classElements;
}

var $$ = getElementsByClass;



// Name: Switch link to active.
// Description: Sets active to current link and deactivate Other links that are childs of checked objects (parentsObjArray).
// Used functions: toArray, toObject.
// Arguments: 
//              parents = STRING('id') or OBJECT or Array(OBJECT,'id');
//              link    = STRIGN('id') or OBJECT;
function makeLocalLinkActive(link,parents,classSufix)
{
	classSufix = classSufix || "Active";
    classRegExp = new RegExp(classSufix+'(\s?)+$');
    parents = toArray(parents);
    link = toObject(link);
    
    if (!classRegExp.test(link.className))
    {// if link is NOT active we do this

            parents.each($,true);
            for (var i=0; i<parents.length; i++)
            {
                //parents[i] = toObject(parents[i]);
                var allLinks = parents[i].getElementsByTagName("a");
                for(var j=0; j<allLinks.length; j++)
                {
                    var currClassName = allLinks[j].className;
                    if(classRegExp.test(currClassName)){
						allLinks[j].className = currClassName.replace(classRegExp,"");
						// restore onclick event from our backup
						if(allLinks[j].onclick_copy){
							allLinks[j].onclick = allLinks[j].onclick_copy;
						}
                    }
                    
                }
                
            }
    //alert(1);
			link.className += classSufix;
            
            // we don't need onclick event, because link is active now and must do nothing
            link.onclick_copy = link.onclick;
			link.onclick = null; 
			//alert(link.onclick);
            
    }
    link.blur();
    
}

var LLA = makeLocalLinkActive;




// Name: Display object and Hide all same others
// Description: Make visible target object and hide all other objects with(class=Classname or that are childs of an object(array of objects) )
// Used functions: 
// Arguments: 
//              objectToDisplay = STRING('id') or OBJECT;
//              objectsToHide = STRING('className') or STRING('id') or OBJECT or Array of objects;

function displayHideOthers(objectToDisplay,objectsToHide)
{
    var parent;
    var allObjects = getElementsByClass(objectsToHide);
    objectToDisplay = toObject(objectToDisplay);

    if (allObjects){
        for (var i=0; i<allObjects.length; i++)
        {
            allObjects[i].style.display = "none";
        }
    }
    else{
            objectsToHide = toArray(objectsToHide);
            for (var j=0; j<objectsToHide.length; j++)
            {
                    if ( parent = toObject(objectsToHide[j]) ){
                    
                        for (childItem in parent.childNodes) 
                        {
                            if (parent.childNodes[childItem].nodeType == 1){
                                parent.childNodes[childItem].style.display = "none";
                            }
                            
                        }// end for
                        
                    }// end if (parent..)
                    else{ alert("Object or id "+parent+" Does not exist !!!"); }
                    
            }// end for
            
        }// end else
        
    objectToDisplay.style.display = "block";
}





// Name: Parse HR string
// Description: Gets CSV string and Returns array of values
// Arguments: 
//              str = STRING('14.25::15000::10||15.55::30000::20||15.95::50000::30||16.55::70000::40');
function parseHRstring(str){
    array = str.split("||");
    for (var i=0; i<array.length; i++ ){
        if( array[i].indexOf("::") != -1 ){
            array[i] = array[i].split("::");
            array[i].each(parseFloat,true);
        }
        else{
            array[i] = parseFloat( array[i] );
        }
        
    }
    return array;
}

                                    // ============= Group: Work with Events ============ //


// Name: Mouse coordinates
// Description: returns object with mouse coordinates like ( mouse = mouseCoords(e); alert( mouse.x,mouse.y); )
// Arguments: 
//              e = ();
function mouseCoords(e){
    e = e || window.event;
    if(e.pageX || e.pageY){
	    return {x:e.pageX, y:e.pageY};
    }
    return {
	    x:e.clientX + document.body.scrollLeft - document.body.clientLeft,
	    y:e.clientY + document.body.scrollTop  - document.body.clientTop
    };
}





// Name: Set event to object
// Description: SETS(replace) to object or RETURN(if handler==undefined) event's handler
// Arguments: 
//              elem = STRING('id') or OBJECT;
//              event_ = STRIGN('onclick');
//              handler = STRING("alert('Hello World!')");
function setEvent(elem, event_, handler)
{
	var result;
	elem = toObject(elem);
    handler = handler ? new Function(""+handler+"") : false;
    
	switch(event_)
	{
	    case 'onload':      result = ( handler ? elem.onload = handler : elem.onload); break;
	    case 'onunload':    result = ( handler ? elem.onunload = handler : elem.onunload ); break;
	    case 'onclick':     result = ( handler ? elem.onclick = handler : elem.onclick ); break;
	    case 'ondblclick':  result = ( handler ? elem.ondblclick = handler : elem.ondblclick ); break;
	    case 'onmousedown': result = ( handler ? elem.onmousedown = handler : elem.onmousedown ); break;
	    case 'onmouseup':   result = ( handler ? elem.onmouseup = handler : elem.onmouseup ); break;
	    case 'onmouseover': result = ( handler ? elem.onmouseover = handler : elem.onmouseover ); break;
	    case 'onmouseout':  result = ( handler ? elem.onmouseout = handler : elem.onmouseout ); break;
	    case 'onmousemove': result = ( handler ? elem.onmousemove = handler : elem.onmousemove ); break;
	    case 'onkeypress':  result = ( handler ? elem.onkeypress = handler : elem.onkeypress ); break;
	    case 'onkeydown':   result = ( handler ? elem.onkeydown = handler : elem.onkeydown ); break;
	    case 'onkeyup':     result = ( handler ? elem.onkeyup = handler : elem.onkeyup ); break;
	    case 'oninit':      result = ( handler ? elem.oninit = handler : elem.oninit ); break;
	    case 'ondisposed':  result = ( handler ? elem.ondisposed = handler : elem.ondisposed ); break;
	    case 'onprerender': result = ( handler ? elem.onprerender = handler : elem.onprerender ); break;
	    case 'ondatabinding': result = ( handler ? elem.ondatabinding = handler : elem.ondatabinding ); break;
	}
    elem.removeAttribute(event_); // if isset attribute -> removes it;
	return result;
}





// Name: Add event to object
// Description: ADDs event's handler to object and Preserve previous handlers
// Arguments: 
//              elem = STRING('id') or OBJECT;
//              event_ = STRIGN('onclick');
//              handler = STRING("alert('Hello World!')");
function addEvent(elem, event_, handler)
{
	elem = toObject(elem);
	
    prevHandlers = setEvent(elem, event_);

    if (prevHandlers)
    {
        prevHandlers += "";
        prevHandlers = prevHandlers.replace(/\n/g,"");                          // Deletes all "\n"
        prevHandlers = prevHandlers.replace(/^[a-z\s\(\)]+[\{]\s{0,10}/,"");    // Deletes "function onclick(event) { "
        prevHandlers = prevHandlers.replace(/[\};]+$/,"");                      // Deletes ";};"
    }
    else{ prevHandlers=""; }
    
    handler = ""+prevHandlers+";"+handler+"";
    setEvent(elem, event_, handler);
}
                                    // ============= END Group: Work with Events ============ //
                                    





// Name: Run function whith delay
// Description: Sets delay before running the handler. Функция (handler) не вызовется, если юзер не задержит мышь на объекте на время(delay)
// Arguments: 
//              elem = STRING('id') or OBJECT;
//              handler = STRING("alert('Hello World!')");
//              delay = INTEGER(2000) - time in mlsecnods;
function withDelay(elem,handler,delay)
{
    elem = toObject(elem);
    elem.timer = setTimeout(handler,delay);
}
function clearTimer(elem)
{
    elem = toObject(elem);
    clearTimeout(elem.timer);
}




// Name: Delete element
// Description: Deletes element from page's DOM model
// Arguments: 
//              elem = STRING('id') or OBJECT;
function del(elem)
{
    elem = toObject(elem);
    elem.parentNode.removeChild(elem);
}




//************************************************************************************************************
// ==================================================== Group: Work with OBJECT 
//************************************************************************************************************

// Name: Delete
// Description: Method deletes This object document.all
/*
Object.prototype.del = function(){
    var parent = this.parentNode;
    for(var i=0; i<parent.childNodes.length; i++){
        if(this.innerHTML == parent.childNodes[i].innerHTML){
            parent.removeChild(parent.childNodes[i]);
        }
    }

}
*/




// Name: Do for Each
// Description: Applies function (handler) for each array element. If (assign=true) handler returns result to the object element 
// Arguments: 
//              handler = FUNCTION( alert or function(a) {alert(a)} );
//				assign = BOOLEAN(true)
Object.prototype.each = function(handler,assign){
	assign = (assign==true) ? true : false;
	//alert(assign);
    var length = this.length;
    var i,j;
    for(i=0; i<length; i++){
    	
        if(j=this[i]){
        	
        	if(assign){ this[i] = handler(j) }
        	else{ handler(j) }
		}
    }

}




/*
Object.prototype.returnForEach = function(handler){
    var length = this.length;
    var i,j;
    for(i=0; i<length; i++){
    	
        if( j=this[i] ) { }
    }
};

*/


//* End Group




//************************************************************************************************************
// ==================================================== Group: Work with ARRAY 
//************************************************************************************************************

// Name: If in Array
// Description: Checks if isset value in array and returns array of their indexes. Has two modes: 
//      1.simple(byType=undefined) - checks only by value. (105(number) == '105'(string) returns true)
//      2.hardby(byType=true) - checks by value and by Type. (105(number) == '105'(string) returns false)
// Arguments: 
//              value = any type;
//              [byType] = BOOLEAN;(if true - compare values and by type)
Array.prototype.inArray = function(value,byType) 
{
	var indexes = [];
	for (var i=0; i < this.length; i++) 
	{
		if (this[i] == value) 
		{
			//alert(typeof(this[i])+" = "+typeof(value))
			if (byType)
			{
			    if (typeof(this[i])==typeof(value)){ indexes.push(i); }
			}
			else{ indexes.push(i); }
		}
		
	}
	return (indexes.length) ? indexes : false;
};






// Name: Array's dump
// Description: Returns string with multilevel array content ( [key] => value )
Array.prototype.dump = function(){
    var str="";
    var lineBreak = "\n";
    var startArr = "Array('"+lineBreak;
    var endArr = "')";

    str += startArr;
    str = levelDump(str,this,0);
    str += endArr;

    return str;
};
function levelDump(str,array,level){
    var tab = "\t";
    var lineBreak = "\n";
    var value;
    var regExp = /function/;
    
    ++level;
    //str+= "level"+level;
    for (var key in array) {
        value = array[key];
        if(typeof value == "object"){
            str += tab.multiply(level)+"['"+key+"'] => "+lineBreak;
            str = levelDump(str,array[key],level)+lineBreak;
        }
        else{
                if ( !regExp.test(value) ){ // if we don't wont to type array methods (array.inArray = function(){...})
                    value = (typeof array[key] == "string") ? "\""+array[key]+"\"" : " "+array[key];
                    str += tab.multiply(level)+"['"+key+"'] => "+value+","+lineBreak;
                }
        }
    }
    return str;
}
//* End Group





//************************************************************************************************************
// ==================================================== Group: Work with STRING 
//************************************************************************************************************

// Name: Multiply string
// Description: Returns string multiplied on number ( "abc".multiply(2,"|") //returns "abc|abc")
// Arguments: 
//              numbe = NUMBER(5);
//              [delim] = STRING("any chars");
String.prototype.multiply = function(number,delim){
    number = Math.round(number);
    if (number > 1){
    
        var item = this;
        var string = this;
        for(var i=0; i<number-1; i++){
            string += (delim) ? delim + item : item;
        }
        return string;
    }
    else if(number == 1) { return this; }
    
    return "";

}
//* End Group





//************************************************************************************************************
// ==================================================== Group: Work with NUMBER 
//************************************************************************************************************

Number.prototype.div = function(x){
    return( Math.round(this/x) );
}
Number.prototype.roundTo = function(x){
    return( Math.round(this/x)*x);
}
//* End Group





//************************************************************************************************************
// ==================================================== Group: Work with FORMS 

function getCheckedValue(formWithRadio,radioName) {
    formWithRadio = toObject(formWithRadio);
	if(!formWithRadio)	return 0;
	
	var radioLength = formWithRadio.length;
	if(radioLength == undefined)
		if(formWithRadio.checked && formWithRadio.name == radioName)
			return formWithRadio.value;
		else
			return 0;
	for(var i=0; i<radioLength; i++) {
		if(formWithRadio[i].checked && formWithRadio[i].name == radioName) {
			return formWithRadio[i].value;
		}
	}
	return 0;
}

function disableInput(input){
	input = $(input);
	input.style.cursor = 'default';
	input.onfocus = function(){ this.blur() };
	input.onclick = function(){ this.blur() };
	input.onselectstart = function(){ return false };
	input.onkeydown = function(){ this.blur() };
}
//************************************************************************************************************

// Name: Test key (Filter for inroduced keys)
// Description: Tests introduced key and compares it with regExp (/[\w\s]/).If key is out of this range returns false and you won't may introduce other keys
//              <input type="text" onkeypress="return testKey(event)"> 
// Arguments: 
//              e = event;
function testKey(e)
{
  // Make sure to use event.charCode if available
  var key = (typeof e.charCode == 'undefined' ? e.keyCode : e.charCode);

  // Ignore special keys
  if (e.ctrlKey || e.altKey || key < 32)
    return true;

  key = String.fromCharCode(key);
  return /[\w\s]/.test(key);
}       
//* End Group





                                    // ============= Prototypes ============ //

