// create the main name space
var JSLIB = window.JSLIB || {};

// this function is used to pre-populate namespace
JSLIB.namespace = function(name)  {
    var names = name.split("."), o = JSLIB, i;
    if (names[0] != "JSLIB") {
        return;
    }
    for (i = 1; i < names.length; i ++) {
        if (typeof o[names[i]] == "undefined") {
            o[names[i]] = {};
        }
        o = o[names[i]];
    }
}

// http://phrogz.net/JS/Classes/OOPinJS.html
// http://www.coolpage.com/developer/javascript/Correct%20OOP%20for%20Javascript.html

// call parent constructor
JSLIB.callParentConstructor = function(parent, obj) {
	// Apply parent's constructor to this object
	if( arguments.length > 2 ) {
		// Note: 'arguments' is an Object, not an Array
		parent.apply( obj, Array.prototype.slice.call( arguments, 2 ) );
	}
	else {
		parent.call( obj );
	}
}

// child copies parent hierarchy
JSLIB.inherits = function(parent, child) {
	child.prototype = new parent();
	child.prototype.constructor = child;
}

// JSLIB.simpleObject class. base class for inheritance
JSLIB.namespace("JSLIB.simpleObject");
JSLIB.simpleObj = function () {
}
JSLIB.simpleObj.prototype.constructor = JSLIB.simpleObj;

// JSLIB.object class. base class for JSLIB classes
JSLIB.object = function () {
	JSLIB.callParentConstructor(JSLIB.simpleObj, this);
    this.properties = {};
}
JSLIB.inherits(JSLIB.simpleObj, JSLIB.object);

JSLIB.object.prototype.getProperty = function(name) {
    return this.properties[name];
}
JSLIB.object.prototype.setProperty = function(name, value) {
    this.properties[name] = value;
}

// browser sniffer
JSLIB.namespace("JSLIB.browser");
function JSLibGetBrowser() {
	var o = {
		"browser":"",
		"version":"",
		"os":"",
		"gecko":false,
		"geckoVersion":0,
		"dom1":false
		},
		agt=navigator.userAgent.toLowerCase();

	// following browser spoof other browsers. so detect them first.
	if(agt.indexOf("opera")!=-1){
		o.browser="opera";
		/opera( |\/)([0-9]+\.[0-9])/.exec(agt);
		o.version=RegExp.$2
	}
	else if(agt.indexOf("konqueror")!=-1){
		o.browser="konqueror";
		/konqueror( |\/)([0-9]+\.[0-9])/.exec(agt);
		o.version=RegExp.$2;
	}
	else if(agt.indexOf("safari")!=-1){
		o.browser="safari";
		/safari( |\/)([0-9]+\.[0-9])/.exec(agt);
		o.version=RegExp.$2;
	}
	else {
		o.gecko=agt.indexOf('gecko')!=-1;
		o.geckoVersion=agt.indexOf('gecko');
		if(o.geckoVersion>0)o.geckoVersion=parseInt(agt.substr(o.geckoVersion+6,8));

		if(agt.indexOf("msie")!=-1){
			o.browser="msie";
			/msie( |\/)([0-9]+\.[0-9])/.exec(agt);
			o.version=RegExp.$2;
		}
		else if(agt.indexOf("netscape")!=-1){
			o.browser="netscape";
			/netscape( |\/)([0-9]+\.[0-9])/.exec(agt);
			o.version=RegExp.$2;
		}
		else if(agt.indexOf("firefox")!=-1){
			o.browser="firefox";
			/firefox( |\/)([0-9]+\.[0-9])/.exec(agt);
			o.version=RegExp.$2;
		}
		else if(agt.indexOf("mozilla")!=-1){
			o.browser="mozilla";
			/rv:([0-9]+\.[0-9])/.exec(agt);
			o.version=RegExp.$1;
		}
		else{
			o.browser="other";
		}
	}

	if(agt.indexOf("win")>=0){
		if ((agt.indexOf("win16")!=-1) ||
		    (agt.indexOf("16bit")!=-1) || (agt.indexOf("windows 3.1")!=-1) ||
	    	(agt.indexOf("windows 16-bit")!=-1)){
			o.os="win16";
		}
		else{
			o.os="win32";
		}
	}
	else if(agt.indexOf("linux")>=0){
		o.os="linux";
	}
	else {
		o.os="other";
	}

	o.dom1=((document.getElementById) ? true : false);
	
	return o;
}
JSLIB.browser = JSLibGetBrowser();

// create the util namespace
JSLIB.namespace("JSLIB.util");
JSLIB.util = {
    lTrim : function (str) {
        var s=new String(str); 
        return s.replace(/^\s*/,""); 
    },

    rTrim : function (str) {
        var s=new String(str); 
        return s.replace(/\s*$/,""); 
    },
    
    trim : function (str) {
        return this.lTrim(this.rTrim(str));
    },

    dirName : function (str) {
        var m=new String(str),arr=m.match(/(.*)\/([^\/]*)$/);
        return (arr==null||typeof(arr)=="undefined"||typeof(arr[1])=="undefined")?".":arr[1];
    },

    baseName : function (str) {
        var m=new String(str),arr = m.match(/(.*)\/([^\/]*)$/);
        return (arr==null||typeof(arr)=="undefined"||typeof(arr[2])=="undefined")?"":arr[2];
    },
    
    absUrl : function (str, win) {
		var loc, re;
        str=new String(str);
        if (typeof(win) == "undefined" || !win || !win.location) {
            win = window;
        }
        loc=win.location;
        
        // is it a abs url?
        re=/^(http|https|ftp):\/\//i;
        if(re.test(str)){
            return str;
        }
        else {
            // started with backslash?
            // use only protocol and hostname to construct abs url
            if (str.charAt(0) == "/") {
                return 
                    loc.protocol+"//"
                    +loc.hostname
                    +(loc.protocol!="http:"&&loc.port!=80?":"+loc.port:"")
                    +str;
            }
            // use existing location url and remove the file name,
            // then append file to its end
            else {
                return loc.href.substr(0,loc.href.lastIndexOf("/"))+"/"+str;
            }
        }    
    },
    
    // Description:
    //      encode characters used in HTML syntax to html entities
    // Arguments:
    //      str - string to be encoded
    //      flag - 
    //          undefined or 0 - escape double quote
    //          1 - do not escape double quote
    //          2 - quote single quote too
    // Return:
    //      encoded string
    htmlSpecialChars : function (str, flag) {
        var m = new String(str), re;
        if (typeof(flag) == "undefined") {
            flag = 0;
        }
        m=m.replace(/&/g,"&amp;");
        if (flag != 1) {
            m=m.replace(/\"/g,"&quot;");
        }
        if (flag == 2) {
            m=m.replace(/\'/g,"&#039;");
        }
        m=m.replace(/</g,"&lt;");
        m=m.replace(/>/g,"&gt;");
        // non-breakable space
        re=new RegExp(String.fromCharCode(160), "g")
        m=m.replace(re,"&nbsp;");
        return m;
    },

    plainTextToHtml : function (str){
        var m = this.htmlSpecialChars(str);
        m=m.replace(/\r\n/g,"<br />");
        m=m.replace(/\n/g,"<br />");
        m=m.replace(/\r/g,"<br />");
        m=m.replace(/  /g," &nbsp;");
        m=m.replace(/\t/g," &nbsp; &nbsp;");
        return m;
    },
	
    // Description:
    //      returns a string with backslashes before characters that need to be quoted 
    //      in javascript string
    // Arguments:
    //      str - string to be encoded
    // Return:
    //      string
    addSlashes : function (str) {
        var m = new String(str);
        m=m.replace(/\"/g,"\\\"");
        m=m.replace(/\'/g,"\\\'");
        m=m.replace(/\t/g,"\\t");
        m=m.replace(/\r/g,"\\r");
        m=m.replace(/\n/g,"\\n");
        return m;
    },
    
    // Description:
    //      determine whether input string looks like HTML code 
    // Arguments:
    //      str - string to be determined
    // Return:
    //      boolean
    //          - true: look like HTML
    //          - false: does not look like HTML
    isHtml : function (str) {
        var m = new String(str), re;
        re=/<(p|h1|h2|h3|h4|h5|h6|table|td|tr|ul|ol|li|b|i|u|strong|em|strike|super|sup|big|small|body|html|br|hr|font|blockquote|pre|tt|script|object|embed)/i;
        if(re.test(m)) {
			return true;
		}
        re=/(&[a-zA-Z]{2,5};|&#[0-9]{1,5};)/i;
        return (re.test(m)) ? true : false;
    },

	getArrayIndexByValue : function (a, v) {
		if (this.isArray(a)) {
			for (var i = 0; i < a.length; i ++) {
				if (a[i] == v) {
					return i;
				}
			}
		}
		return -1;
	},

    inArray : function(v, a) {
		var r=false;
		for(var i=0;i<a.length;i++){
			if(v == a[i]){
				r=true;
				break;
			}

		}
		return r;
	},
	
    // merge array and remove duplicated items
	mergeArray : function (a, b) {
		var i, j;
		o:for (j = 0; j < b.length; j ++) {
			for (i = 0; i < a.length; i ++) {
				if (a[i] == b[j]) { continue o; }
			}
			a.push(b[j]);
		}
		return a;
	},
	
	isArray : function(a) {
		return this.isObject(a) && a.constructor == Array;
	},
	
	isBoolean : function(a) {
		return typeof a == 'boolean';
	},

	// isEmpty(v) returns true if v is not a string or its length is zero
	isEmpty : function(a) {
		if (!this.isString(a)) {
			return true;
		}
		return a.length == 0;
	},
	
	// isEmptyObj(v) returns true if v is an object containing no enumerable members.
	isEmptyObj : function(o) {
	    var i, v;
	    if (this.isObject(o)) {
	        for (i in o) {
	            v = o[i];
	            if (v !== undefined && !this.isFunction(v)) {
	                return false;
	            }
	        }
	    }
	    return true;
	},
	
	isFunction : function(a) {
		return typeof a == 'function';
	},
	
	isNull : function(a) {
		return a === null;
	},
	
	isNumber : function(a) {
		return typeof a == 'number' && isFinite(a);
	},
	
	isObject : function(a) {
    	return (a && typeof a == 'object') || this.isFunction(a);
	},
	
	isString : function(a) {
		return typeof a == 'string';
	},
	
    objToString : function (a) {
		var str, key;
        if (this.isObject(a)) {
            str = "";
            for (key in a) {
                if (str.length > 0) {
                    str += ", ";
                }
                if (!this.isFunction(a[key])) {
                    str += key + ":" + a[key];
                }
                else {
                    str += key + ":FUNCTION";
                }
            }
            return "{" + str + "}";
        }
        else {
            return a;
        }
    },
	
	cloneObj : function (obj) {
		var o, prop;
		if (typeof (obj) != 'object') {
			return null;
		}
		if (obj.constructor == Array) {
			o = obj.concat();
		}
		else {
			o = {}; 
			for (prop in obj) {
				o[prop] = typeof (obj[prop]) == 'object' ? this.cloneObj(obj[prop]) : obj[prop];
			} 
		}
		return o;
	},
	
	// convert mysql date string (eg. '2008-04-16 13:10:01') to javascript date
	mysqlDateToJsDate : function (str) {
		var re = /^([0-9]{4})\-([0-9]{2})\-([0-9]{2})[ ]+([0-9]{2}):([0-9]{2}):([0-9]{2})$/, m;
		m = str.match(re);
		return m ? new Date(m[1], m[2] - 1, m[3], m[4], m[5], m[6]) : null;
	},
	
	// convert a 32bits number to 8 chars hex code
	number2hex : function (num) {
		var a=['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'], r="",n=num, i;
		for(i=0; i<4; i++) {
			r=a[(n>>4)&15]+a[n&15]+r;
			n=n>>8;
		}
		return r;
	},
	
	// get a 16 characters hash
	// adapted from JSHash()
	getHash : function (s) {
		var ms = "libmagicstr", hash1, hash2, i, s1 = ms + s + ms, s2 = ms + s + ms;
		hash2 = hash1 = 1315423911;
		for (i = 0; i < s1.length; i ++) {
			hash1 ^= (hash1 << 5) + s1.charCodeAt(i) + (hash1 >> 2);
		}
		for (i = s2.length - 1; i >= 0; i --) {
			hash2 ^= (hash2 << 5) + s2.charCodeAt(i) + (hash2 >> 2);
		}
		return this.number2hex(hash1) + this.number2hex(hash2);
	},
	
	// cloning PHP wordwrap but pCut is not respected now
	wordwrap : function (pStr, pWidth, pBreak, pCut) {
		var out = "", tmp = new String(pStr);
		do {
			if (out.length > 0) {
				out += pBreak;
			}
			if (tmp.length > pWidth) {
				out += tmp.substring(0, pWidth);
				tmp = tmp.substring(pWidth);
			}
			else {
				out += tmp;
				tmp = "";
			}
		} while (tmp.length > 0);
		return out;
	}
};

