Octeva = {
	version: '0.1'
};

$O = Octeva;

/*
 * Core
 */
/* from Ext */
Octeva.override = function(origclass, overrides){
    if(overrides){
        var p = origclass.prototype;
        for(var method in overrides){
            p[method] = overrides[method];
        }
    }
};
/* from Ext */
Octeva.extend = function(){
    // inline overrides
    var io = function(o){
        for(var m in o){
            this[m] = o[m];
        }
    };
    return function(sb, sp, overrides){
        if(typeof sp == 'object'){
            overrides = sp;
            sp = sb;
            sb = function(){sp.apply(this, arguments);};
        }
        var F = function(){}, sbp, spp = sp.prototype;
        F.prototype = spp;
        sbp = sb.prototype = new F();
        sbp.constructor=sb;
        sb.superclass=spp;
        if(spp.constructor == Object.prototype.constructor){
            spp.constructor=sp;
        }
        sb.override = function(o){
            Octeva.override(sb, o);
        };
        sbp.override = io;
        Octeva.override(sb, overrides);
        return sb;
    };
}();

Octeva.apply = function(des, obj) {
	for (var key in obj) {
		des[key] = obj[key];
	}
}

/**
 * @param obj	{object}	object to be cloned.
 * @param deep	{boolean}	whether the clone is deep or not.
 * @return {object} clone object.
 */
Octeva.clone = function(obj, deep) {
	if (deep) {
	    if (typeof obj == "string" ||
	        typeof obj == "number" ||
	        typeof obj =="boolean" ||
	        typeof obj == "function") {
	        dup = obj;
	    } else if (typeof obj == "undefined") {
	        return undefined;
	    } else if (typeof obj == null) {
	        return null;
	    } else { //object
	        var dup = {}, val;
	        for (var p in obj) {
	            val = obj[p];
	            if (typeof val == "object") {
	                if (typeof val == "string") {
	                    dup[p] = obj[p];
	
	                } else {
	                    if (typeof val.length == 'undefined') {
	                        dup[p] = Octeva.clone(val, true);
	                    } else {
	                        dup[p] = [];
	                        for (var i = 0; i < val.length; i ++) {
	                            dup[p][i] = Octeva.clone(val[i], true);
	                        }
	                    }
	                }
	            } else {
	                dup[p] = obj[p];
	            }
	        }
	    }
	    return dup;
	} else {
		var o = {};
		for (var key in obj) {
			o[key] = obj[key];
		}
		return o;
	}
};

Octeva.msg = {
	_createMessageBox: function(str) {
		var css = {
			'clear'			: 'both',
			'background'	: '#ADCAEB',
			'color' 		: 'white',
			'font-size' 	: '12px',
			'height'		: '20px',
			'padding-top' 	: '3px',
			'padding-left'	: '3px',
			'padding-right'	: '3px',
			'position'		: 'fixed',
			'right'			: '0px',
			'top'			: '0px'
		};
		if ($.browser.msie) {
			css['position'] = 'absolute';
		}
		var r = Math.random();
		var n = Math.floor(r*10000);
		var msg_id = 'message-' + n;
		return $($(document.createElement('div'))
			.attr('id', msg_id)
			.css(css)
			.text(str)
			.appendTo("body")[0]);
	},
	alert: function(str) {
		this._createMessageBox(str).fade('#FF0000');
	},
	warn: function(str) {
		this._createMessageBox(str).fade('#FF8000');
	},
 	message: function(str) {
		this._createMessageBox(str).fade('#00FF00');
	}
}

Octeva.template = {
	apply: function(tmpl, obj) {
		for (var p in obj) {
			tmpl = tmpl.replace(new RegExp("{" + p + "}", "ig"), obj[p])
				.replace(new RegExp("%7B" + p + "%7D", "ig"), obj[p]); 
		}
		return tmpl;
	}
}

Octeva.datePicker = {
	apply: function(el, date) {
		var _el = $(el);
		if (!date) {
			date = new Date();
		}
		_el.datePicker({startDate:'01/01/1970', verticalOffset: 24})
			.val(date.asString())
			.trigger('change');
		$('a.dp-choose-date').html('');
		_el.dblclick(
			function() {
				$(this).dpDisplay();
			}
		);
	}
}

Octeva.date = {	//date format is MM/dd/yyyy
	/**
	 * string like 'MM/dd/yyyy' to date object
	 */
	parse: function(localDate) {
		var d = new Date();
		var ds = localDate.split('/');
		d.setMonth(parseInt(ds[0], 10)-1);
		d.setDate(parseInt(ds[1], 10));
		d.setYear(parseInt(ds[2], 10));
		d.setHours(0);
		d.setMinutes(0);
		d.setSeconds(0);
		return d;
	},
	/**
	 * UTC string to local date object
	 */
	toLocal: function(utc, format) {
		var d = new Date();
		var p = {};
		if ('year' == format) {	//yyyy
			p = {	y: parseInt(utc), 
					M: 0, d: 1, h: 0, m: 0, s: 0, S: 0
				};
		} else if ('month' == format) {	//yyyyMM
			p = {	y: parseInt(utc.substring(0,4)), 
					M: parseInt(utc.substring(4))-1, 
					d: 1, h: 0, m: 0, s: 0, S: 0
				};
		} else if ('day' == format) {	//yyyyMMdd
			p = {	y: parseInt(utc.substring(0,4)), 
					M: parseInt(utc.substring(4,6))-1, 
					d: parseInt(utc.substring(6,8)), 
					h: 0, m: 0, s: 0, S: 0
				};
		} else if ('hour' == format) {	//yyyyMMddHH
			p = {	y: parseInt(utc.substring(0,4)), 
					M: parseInt(utc.substring(4,6))-1, 
					d: parseInt(utc.substring(6,8)), 
					h: parseInt(utc.substring(8,10)), 
					m: 0, s: 0, S: 0
				};
		} else if ('minute' == format) {//yyyyMMddHHmm
			p = {	y: parseInt(utc.substring(0,4)), 
					M: parseInt(utc.substring(4,6))-1, 
					d: parseInt(utc.substring(6,8)), 
					h: parseInt(utc.substring(8,10)), 
					m: parseInt(utc.substring(10,12)), 
					s: 0, S: 0
				};
		} else if ('second' == format) {//yyyyMMddHHmmss
			p = {	y: parseInt(utc.substring(0,4)), 
					M: parseInt(utc.substring(4,6))-1, 
					d: parseInt(utc.substring(6,8)), 
					h: parseInt(utc.substring(8,10)), 
					m: parseInt(utc.substring(10,12)), 
					s: parseInt(utc.substring(12,14)), 
					S: 0
				};
		} else if ('millisecond' == format) {//yyyyMMddHHmmssSSS
			p = {	y: parseInt(utc.substring(0,4)), 
					M: parseInt(utc.substring(4,6))-1, 
					d: parseInt(utc.substring(6,8)), 
					h: parseInt(utc.substring(8,10)), 
					m: parseInt(utc.substring(10,12)), 
					s: parseInt(utc.substring(12,14)), 
					S: parseInt(utc.substring(12,15))
				};
		}

		d.setUTCFullYear(p.y);
		d.setUTCMonth(p.M);
		d.setUTCDate(p.d);
		d.setUTCHours(p.h);
		d.setUTCMinutes(p.m);
		d.setUTCSeconds(p.s);
		d.setUTCMilliseconds(p.S);
		return d;
	},
	/**
	 * date object to UTC string.
	 */
	toUTC: function(d, format) {
		var s = '';
		if ('year' == format) {	//yyyy
			s = d.getUTCFullYear();
		} else if ('month' == format) {	//yyyyMM
			s = d.getUTCFullYear() 
				+ this._patch(d.getUTCMonth()+1, 2);
		} else if ('day' == format) {	//yyyyMMdd
			s = d.getUTCFullYear() 
				+ this._patch(d.getUTCMonth(), 2)
				+ this._patch(d.getUTCDate()+1, 2);
		} else if ('hour' == format) {	//yyyyMMddHH
			s = d.getUTCFullYear() 
				+ this._patch(d.getUTCMonth()+1, 2)
				+ this._patch(d.getUTCDate(), 2)
				+ this._patch(d.getUTCHours(), 2);
		} else if ('minute' == format) {//yyyyMMddHHmm
			s = d.getUTCFullYear() 
				+ this._patch(d.getUTCMonth()+1, 2)
				+ this._patch(d.getUTCDate(), 2)
				+ this._patch(d.getUTCHours(), 2)
				+ this._patch(d.getUTCMinutes(), 2);
		} else if ('second' == format) {//yyyyMMddHHmmss
			s = d.getUTCFullYear() 
				+ this._patch(d.getUTCMonth()+1, 2)
				+ this._patch(d.getUTCDate(), 2)
				+ this._patch(d.getUTCHours(), 2)
				+ this._patch(d.getUTCMinutes(), 2)
				+ this._patch(d.getUTCSeconds(), 2);
		} else if ('millisecond' == format) {//yyyyMMddHHmmssSSS
			s = d.getUTCFullYear() 
				+ this._patch(d.getUTCMonth()+1, 2)
				+ this._patch(d.getUTCDate(), 2)
				+ this._patch(d.getUTCHours(), 2)
				+ this._patch(d.getUTCMinutes(), 2)
				+ this._patch(d.getUTCSeconds(), 2)
				+ this._patch(d.getUTCMilliseconds(), 3);
		}
		return s;
	},
	_patch: function(s, len) {
		s = ''+s;
		if (s.length < len) {
			s = '0'+s;
		}
		return s;
	}
}

Octeva.pageToolbar = {
	update: function(config) {

		var c = parseInt(config.start/config.limit)+1;
		var s = (c<config.left+1)?1:c-config.left;
		var total_pages = Math.ceil(config.total/config.limit);
		var e = ((c+config.right)*config.limit>=config.total)?total_pages:c+config.right;
		//get html string
		var str = '';
		if (c>1 && config.first) {
			str += Octeva.template.apply(
				'<a href="javascript:{fn}(1)">&lt;&lt;</a>&nbsp;', 
				{fn: config.fn}
			);
		}
		if (c>config.left && config.prev) {
			str += Octeva.template.apply(
				'<a href="javascript:{fn}({i})">&lt;</a>&nbsp;', 
				{fn: config.fn, i: c-1}
			);
		}
		for (var i = s; i <= e; i ++) {
			if (i == c) {
				str += Octeva.template.apply(
					'<a href="javascript:{fn}({i})">' +
					'	<font style="font-size:12px;font-weight:900;">{i}</font>' +
					'</a>&nbsp;',
					{fn: config.fn, i: i}
				);
			} else {
				str += Octeva.template.apply(
					'<a href="javascript:{fn}({i})">{i}</a>&nbsp;',
					{fn: config.fn, i: i}
				);
			}
		}
		if ((c+config.right)*config.limit<config.total && config.next) {
			str += Octeva.template.apply(
				'<a href="javascript:{fn}({i})">&gt;</a>',
				{fn: config.fn, i: (c+1)}
			);
		}
		if (c < total_pages && config.last) {
			str += Octeva.template.apply(
				'<a href="javascript:{fn}({i})">&gt;&gt;</a>&nbsp;',
				{fn: config.fn, i: total_pages}
			);
		}
		//get message
		var msg = "";
		if (config.total) {
			msg = Octeva.template.apply(
				config.msg, 
				{current: c, total_pages: total_pages, total_count: config.total}
			);
		} else {
			msg = "No result found!";
		}
		$('[@name="page"]', config.el).html(str);
		$('[@name="msg"]', config.el).html(msg);
	}
}

Octeva.paramToJson = function(param) {

	var json = null;
	if (param) {
		if (typeof(param) == 'string') {	//string like 'a=1&b=2&b=3'
			json = {};
			var kvs = param.split('&');
			var kv, key, val;
			for (var i = 0; i < kvs.length;  i ++) {
				kv = kvs[i].split('=');
				key = kv[0];
				val = kv[1];
				
				if (json[key]) {
					if (!(json[key] instanceof Array)) {
						json[key] = [json[key]];
					}
					var len = json[key].length;
					json[key][len] = val;
				} else {
					json[key] = val;
				}
			}
		}
	}
	return json;
}

Octeva.setSelect = function(select, val) {
	var el = $(select)[0];
	if (!(val instanceof Array)) {
		val = [val];
	}

	for (var i = 0; el && i < el.options.length; i ++) {
		for (var j = 0; j < val.length; j ++) {
			if (el.options[i].value == val[j]) {
				el.options[i].selected = true;
				break;
			}
		}
	}
}

Octeva.setForm = function(form, json) {
	var uis = $(':input', form);
	for (var k = 0; k < uis.length; k ++) {
		var ui = uis[k];
		var tag = ui.tagName.toLowerCase();
		var type = ui.type;
		var key = $(ui).attr('name');
		var val = json[key];
		if (val) {
			if (tag == 'input') {
				if (type == 'checkbox') {
					var el = $(ui)[0];
					if (val && !(val instanceof Array)) {
						val = val.split(",");
					}
					for (var i = 0; i < (val instanceof Array) && val.length; i ++) {
						for (var j = 0; j < el.length; j ++) {
							if (el[j].value == val[i].id || el[j].value == val[i]) {
								el[j].checked = true;
								break;
							}
						}
					}
				} else if (type == 'radio') {
					alert('utils.js - TODO');
				} else {
					val = val.replace(/\++/ig, ' ');
					$(ui).val(val);
				}
			} else if (tag == 'select') {
				Octeva.setSelect(ui, val);
			} else if (tag == 'textarea') {
				$(ui).text(val);
			}
		}
	}
}
/*
 * form input/text/checkbox/radio values
 */
Octeva.getFormFields = function(form) {

	var fields = [];	
	var field = null;		
	// inputs
	var inputs = $(':input', form);
	for (var i = 0; i < inputs.length; i ++) {
		field = $(inputs[i]);
		if (field.attr('name') != undefined && field.attr('value') != undefined) {
			fields[fields.length] = field;
		}
	}
	// texts
	var texts = $(':text', form);
	for (var i = 0; i < texts.length; i ++) {
		field = $(texts[i]);
		if (field.attr('name') != undefined && field.attr('value') != undefined) {
			fields[fields.length] = field;
		}
	}
	// radios
	var radios = $(':radio', form);
	for (var i = 0; i < radios.length; i ++) {
		field = $(radios[i]);
		if (field.attr('name') != undefined && field.attr('value') != undefined) {
			fields[fields.length] = field;
		}
	}
	// checkboxes
	var checkboxes = $(':checkbox', form);
	for (var i = 0; i < checkboxes.length; i ++) {
		field = $(checkboxes[i]);
		if (field.attr('name') != undefined && field.attr('value') != undefined) {
			fields[fields.length] = field;
		}
	}
	return fields;
}
Octeva.getFormFieldValue = function(form) {
	
	var map = {};
	var key, value;
	var fields = this.getFormFields(form);
	for (var i = 0; i < fields.length; i ++) {
		key = fields[i].attr('name');
		value = fields[i].attr('value');
		map[key] = value;
	}
	return map;
}

Octeva.ui = {};

/**
 * @param config
 *	el			{jQuery Object}
 * 	clickHandler{function}
 * 	left 		{integer}
 * 	right 		{integer}
 *	pageTotal 	{integer}
 * 	pageSize	{integer}
 *
 */
Octeva.ui.PageBar = function(config) {
	Octeva.apply(this, config);
}
Octeva.ui.PageBar = Octeva.extend(Octeva.ui.PageBar, {

	updateTo: function(current) {
		this.current = current;
		//min
		var min = 1;
		if (current < this.left+2) {
			min = 1;
		} else {
		    min = current-this.left;
		}
		//max
		var max = 1;
		if (current + this.right <= this.pageTotal) {
			max = current + this.right;
		} else {
			max = this.pageTotal;
		}
		// calc table_page's content
		var str = '';
		if (current > 1) {
			str += '<li><a href="javascript:page(' + (current-1) + ')"><</a></li>';
		}
		for (var i = min; i <= max; i ++) {
			if (current == i) {
				str += 	'<li><a href="javascript:page(' + i + ')" class="selected">' + i + '</a></li>';
			} else {
				str += 	'<li><a href="javascript:page(' + i + ')">' + i + '</a></li>';
			}
		}
		if (current < this.pageTotal) {
			str += '<li><a href="javascript:page(' + (current+1) + ')">></a></li>';
		}
		this.el.html(str);
	}
});

/**
 * class: Editable Table 
 * configuration
 */
Octeva.ui.EditableTable = function(config) {
	
	Octeva.apply(this, config);
	//init consts
	this.OPTIONS_STATE = {
		"": 'Please select ...',
		"Non-US": 'Non-US',
		"AL": 'Alabama', 
		"AK": 'Alaska', 
		"AZ": 'Arizona', 
		"AR": 'Arkansas', 
		"CA": 'California', 
		"CO": 'Colorado', 
		"CT": 'Connecticut', 
		"DE": 'Delaware', 
		"DC": 'District Of Columbia', 
		"FL": 'Florida', 
		"GA": 'Georgia', 
		"HI": 'Hawaii', 
		"ID": 'Idaho', 
		"IL": 'Illinois', 
		"IN": 'Indiana', 
		"IA": 'Iowa', 
		"KS": 'Kansas', 
		"KY": 'Kentucky', 
		"LA": 'Louisiana', 
		"ME": 'Maine', 
		"MD": 'Maryland', 
		"MA": 'Massachusetts', 
		"MI": 'Michigan', 
		"MN": 'Minnesota', 
		"MS": 'Mississippi', 
		"MO": 'Missouri', 
		"MT": 'Montana', 
		"NE": 'Nebraska', 
		"NV": 'Nevada', 
		"NH": 'New Hampshire', 
		"NJ": 'New Jersey', 
		"NM": 'New Mexico', 
		"NY": 'New York', 
		"NC": 'North Carolina', 
		"ND": 'North Dakota', 
		"OH": 'Ohio', 
		"OK": 'Oklahoma', 
		"OR": 'Oregon', 
		"PA": 'Pennsylvania', 
		"RI": 'Rhode Island', 
		"SC": 'South Carolina', 
		"SD": 'South Dakota', 
		"TN": 'Tennessee', 
		"TX": 'Texas', 
		"UT": 'Utah', 
		"VT": 'Vermont', 
		"VA": 'Virginia', 
		"WA": 'Washington', 
		"WV": 'West Virginia', 
		"WI": 'Wisconsin', 
		"WY": 'Wyoming'
	};
	this.OPTIONS_COUNTRY = {
		"": 'Please select ...',
		"United States": 'United States',
		"Afghanistan": 'Afghanistan',
		"Albania": 'Albania',
		"Algeria": 'Algeria',
		"American Samoa": 'American Samoa',
		"Andorra": 'Andorra',
		"Angola": 'Angola',
		"Anguilla": 'Anguilla',
		"Antarctica": 'Antarctica',
		"Antigua and Barbuda": 'Antigua and Barbuda',
		"Argentina": 'Argentina',
		"Armenia": 'Armenia',
		"Aruba": 'Aruba',
		"Australia": 'Australia',
		"Austria": 'Austria',
		"Azerbaijan": 'Azerbaijan',
		"Bahamas": 'Bahamas',
		"Bahrain": 'Bahrain',
		"Bangladesh": 'Bangladesh',
		"Barbados": 'Barbados',
		"Belarus": 'Belarus',
		"Belgium": 'Belgium',
		"Belize": 'Belize',
		"Benin": 'Benin',
		"Bermuda": 'Bermuda',
		"Bhutan": 'Bhutan',
		"Bolivia": 'Bolivia',
		"Bosnia and Herzegovina": 'Bosnia and Herzegovina',
		"Botswana": 'Botswana',
		"Bouvet Island": 'Bouvet Island',
		"Brazil": 'Brazil',
		"British Indian Ocean Territory": 'British Indian Ocean Territory',
		"Brunei Darussalam": 'Brunei Darussalam',
		"Bulgaria": 'Bulgaria',
		"Burkina Faso": 'Burkina Faso',
		"Burundi": 'Burundi',
		"Cambodia": 'Cambodia',
		"Cameroon": 'Cameroon',
		"Canada": 'Canada',
		"Cape Verde": 'Cape Verde',
		"Cayman Islands": 'Cayman Islands',
		"Central African Republic": 'Central African Republic',
		"Chad": 'Chad',
		"Chile": 'Chile',
		"China": 'China',
		"Christmas Island": 'Christmas Island',
		"Cocos (Keeling) Islands": 'Cocos (Keeling) Islands',
		"Colombia": 'Colombia',
		"Comoros": 'Comoros',
		"Congo": 'Congo',
		"Cook Islands": 'Cook Islands',
		"Costa Rica": 'Costa Rica',
		"Cote D'ivoire": "Cote D'ivoire",
		"Croatia": 'Croatia',
		"Cuba": 'Cuba',
		"Cyprus": 'Cyprus',
		"Czech Republic": 'Czech Republic',
		"Denmark": 'Denmark',
		"Djibouti": 'Djibouti',
		"Dominica": 'Dominica',
		"Dominican Republic": 'Dominican Republic',
		"Ecuador": 'Ecuador',
		"Egypt": 'Egypt',
		"El Salvador": 'El Salvador',
		"Equatorial Guinea": 'Equatorial Guinea',
		"Eritrea": 'Eritrea',
		"Estonia": 'Estonia',
		"Ethiopia": 'Ethiopia',
		"Falkland Islands (Malvinas)": 'Falkland Islands (Malvinas)',
		"Faroe Islands": 'Faroe Islands',
		"Fiji": 'Fiji',
		"Finland": 'Finland',
		"France": 'France',
		"French Guiana": 'French Guiana',
		"French Polynesia": 'French Polynesia',
		"French Southern Territories": 'French Southern Territories',
		"Gabon": 'Gabon',
		"Gambia": 'Gambia',
		"Georgia": 'Georgia',
		"Germany": 'Germany',
		"Ghana": 'Ghana',
		"Gibraltar": 'Gibraltar',
		"Greece": 'Greece',
		"Greenland": 'Greenland',
		"Grenada": 'Grenada',
		"Guadeloupe": 'Guadeloupe',
		"Guam": 'Guam',
		"Guatemala": 'Guatemala',
		"Guinea": 'Guinea',
		"Guinea-bissau": 'Guinea-bissau',
		"Guyana": 'Guyana',
		"Haiti": 'Haiti',
		"Heard Island and Mcdonald Islands": 'Heard Island and Mcdonald Islands',
		"Holy See (Vatican City State)": 'Holy See (Vatican City State)',
		"Honduras": 'Honduras',
		"Hong Kong": 'Hong Kong',
		"Hungary": 'Hungary',
		"Iceland": 'Iceland',
		"India": 'India',
		"Indonesia": 'Indonesia',
		"Iran": 'Iran',
		"Iraq": 'Iraq',
		"Ireland": 'Ireland',
		"Israel": 'Israel',
		"Italy": 'Italy',
		"Jamaica": 'Jamaica',
		"Japan": 'Japan',
		"Jordan": 'Jordan',
		"Kazakhstan": 'Kazakhstan',
		"Kenya": 'Kenya',
		"Kiribati": 'Kiribati',
		"Korea, North": 'Korea, North',
		"Korea, South": 'Korea, South',
		"Kuwait": 'Kuwait',
		"Kyrgyzstan": 'Kyrgyzstan',
		"Lao People's Democratic Republic": "Lao People's Democratic Republic",
		"Latvia": 'Latvia',
		"Lebanon": 'Lebanon',
		"Lesotho": 'Lesotho',
		"Liberia": 'Liberia',
		"Libyan Arab Jamahiriya": 'Libyan Arab Jamahiriya',
		"Liechtenstein": 'Liechtenstein',
		"Lithuania": 'Lithuania',
		"Luxembourg": 'Luxembourg',
		"Macao": 'Macao',
		"Macedonia, The Former Yugoslav Republic of": 'Macedonia, The Former Yugoslav Republic of',
		"Madagascar": 'Madagascar',
		"Malawi": 'Malawi',
		"Malaysia": 'Malaysia',
		"Maldives": 'Maldives',
		"Mali": 'Mali',
		"Malta": 'Malta',
		"Marshall Islands": 'Marshall Islands',
		"Martinique": 'Martinique',
		"Mauritania": 'Mauritania',
		"Mauritius": 'Mauritius',
		"Mayotte": 'Mayotte',
		"Mexico": 'Mexico',
		"Micronesia, Federated States of": 'Micronesia, Federated States of',
		"Moldova, Republic of": 'Moldova, Republic of',
		"Monaco": 'Monaco',
		"Mongolia": 'Mongolia',
		"Montserrat": 'Montserrat',
		"Morocco": 'Morocco',
		"Mozambique": 'Mozambique',
		"Myanmar": 'Myanmar',
		"Namibia": 'Namibia',
		"Nauru": 'Nauru',
		"Nepal": 'Nepal',
		"Netherlands": 'Netherlands',
		"Netherlands Antilles": 'Netherlands Antilles',
		"New Caledonia": 'New Caledonia',
		"New Zealand": 'New Zealand',
		"Nicaragua": 'Nicaragua',
		"Niger": 'Niger',
		"Nigeria": 'Nigeria',
		"Niue": 'Niue',
		"Norfolk Island": 'Norfolk Island',
		"Northern Mariana Islands": 'Northern Mariana Islands',
		"Norway": 'Norway',
		"Oman": 'Oman',
		"Pakistan": 'Pakistan',
		"Palau": 'Palau',
		"Palestinian Territory, Occupied": 'Palestinian Territory, Occupied',
		"Panama": 'Panama',
		"Papua New Guinea": 'Papua New Guinea',
		"Paraguay": 'Paraguay',
		"Peru": 'Peru',
		"Philippines": 'Philippines',
		"Pitcairn": 'Pitcairn',
		"Poland": 'Poland',
		"Portugal": 'Portugal',
		"Puerto Rico": 'Puerto Rico',
		"Qatar": 'Qatar',
		"Reunion": 'Reunion',
		"Romania": 'Romania',
		"Russian Federation": 'Russian Federation',
		"Rwanda": 'Rwanda',
		"Saint Helena": 'Saint Helena',
		"Saint Kitts and Nevis": 'Saint Kitts and Nevis', 
		"Saint Lucia": 'Saint Lucia', 
		"Saint Pierre and Miquelon": 'Saint Pierre and Miquelon', 
		"Saint Vincent and The Grenadines": 'Saint Vincent and The Grenadines', 
		"Samoa": 'Samoa', 
		"San Marino": 'San Marino', 
		"Sao Tome and Principe": 'Sao Tome and Principe', 
		"Saudi Arabia": 'Saudi Arabia', 
		"Senegal": 'Senegal', 
		"Serbia and Montenegro": 'Serbia and Montenegro', 
		"Seychelles": 'Seychelles', 
		"Sierra Leone": 'Sierra Leone', 
		"Singapore": 'Singapore', 
		"Slovakia": 'Slovakia', 
		"Slovenia": 'Slovenia', 
		"Solomon Islands": 'Solomon Islands', 
		"Somalia": 'Somalia', 
		"South Africa": 'South Africa', 
		"South Georgia and The South Sandwich Islands": 'South Georgia and The South Sandwich Islands', 
		"Spain": 'Spain', 
		"Sri Lanka": 'Sri Lanka', 
		"Sudan": 'Sudan', 
		"Suriname": 'Suriname', 
		"Svalbard and Jan Mayen": 'Svalbard and Jan Mayen', 
		"Swaziland": 'Swaziland', 
		"Sweden": 'Sweden', 
		"Switzerland": 'Switzerland', 
		"Syrian Arab Republic": 'Syrian Arab Republic', 
		"Taiwan": 'Taiwan', 
		"Tajikistan": 'Tajikistan', 
		"Tanzania, United Republic of": 'Tanzania, United Republic of',
		"Thailand": 'Thailand',
		"Timor-leste": 'Timor-leste',
		"Togo": 'Togo',
		"Tokelau": 'Tokelau',
		"Tonga": 'Tonga',
		"Trinidad and Tobago": 'Trinidad and Tobago',
		"Tunisia": 'Tunisia',
		"Turkey": 'Turkey',
		"Turkmenistan": 'Turkmenistan',
		"Turks and Caicos Islands": 'Turks and Caicos Islands',
		"Tuvalu": 'Tuvalu',
		"Uganda": 'Uganda',
		"Ukraine": 'Ukraine',
		"United Arab Emirates": 'United Arab Emirates',
		"United Kingdom": 'United Kingdom',
		"United States Minor Outlying Islands": 'United States Minor Outlying Islands',
		"Uruguay": 'Uruguay',
		"Uzbekistan": 'Uzbekistan',
		"Vanuatu": 'Vanuatu',
		"Venezuela": 'Venezuela',
		"Viet Nam": 'Viet Nam',
		"Virgin Islands, British": 'Virgin Islands, British',
		"Virgin Islands, U.S.": 'Virgin Islands, U.S.',
		"Wallis and Futuna": 'Wallis and Futuna',
		"Western Sahara": 'Western Sahara',
		"Yemen": 'Yemen',
		"Zambia": 'Zambia',
		"Zimbabwe": 'Zimbabwe'
	}
	
	//initalize editableTable
	var editableTable = this;
	var confCols = this.columns;
	
	if (confCols) {
		$('tr.x_table_row', this.table[0]).each(function() {
			$('td', $(this)[0]).each(function(i) {
				var confCol = confCols[i];
				if (confCol && confCol.editable) {
					//modify current <td/>
					$(this).addClass('x_table_data_editable');
					var innerEl = $(this);
					while (innerEl.children().length > 0) innerEl = $(innerEl.children()[0]);
					innerEl.html('<span class="x_table_data_view">' + innerEl.text() + '</span>');
					//append value element
					if (confCol.element == 'input') {
						var tmp = {
							name	: confCol.name,
							size	: confCol.size,
							value	: ($(this).text() == editableTable.emptyText)?'':$(this).text()
						};
						var input = Octeva.template.apply('<input class="x_table_data_value" type="text" name="{name}" size="{size}" value="{value}" style="display:none;"/>', tmp);
						$(this).append(input);
					} else if (confCol.element == 'select') {
						var value = $(this).text();
						var options = null;
						if (typeof confCol.options == 'string') {
							options = editableTable[confCol.options];
							if (!options) {
								throw 'Octeva.ui.EditableTable: Options "' + confCol.options + '" not defined!';
							}
						} else {
							options = confCol.options;
						}
						
						var select = '<select class="x_table_data_value" name="' + confCol.name + '" style="display:none;">';
						for (var v in options) {
							if (value == v) {
								select += '<option value="' + v + '" selected="selected">' + options[v] + '</option>';
							} else {
								select += '<option value="' + v + '">' + options[v] + '</option>';
							}
						}
						select += '</select>';
						$(this).append(select);
					}
				}
			});
		});
	}
	/*
	 *bind events
	 */
	this.table.click(function(event) {
		var target = $(event.target);
		var td;
		if (target.is('td.x_table_data_editable')) {
			td = target;
		} else if (target.parents('td.x_table_data_editable').length) {
			td = target.parents('td.x_table_data_editable:first');
		}
		if (td) editableTable.showEditDataEl(td);
	});
	//input.x_table_data_value
	$('input.x_table_data_value').blur(function(event) {
		editableTable.hideEditDataEl($(this));
	}).keydown(function(event) {
		if (event.keyCode == 13) {	//KeyCode.Enter
			editableTable.hideEditDataEl($(this));
			return false;
		}
	});
	//select.x_table_data_value
	$('select.x_table_data_value').blur(function(event) {
		editableTable.hideEditDataEl($(this));
	}).change(function(event) {
		if ($.browser.msie) {
			editableTable.hideEditDataEl($(this));
		}
	}).mouseup(function(event) {
		if ($.browser.mozilla) {
			editableTable.hideEditDataEl($(this));
		}
	}).keydown(function(event) {
		if (event.keyCode == 13) {	//KeyCode.Enter
			editableTable.hideEditDataEl($(this));
			return false;
		}
	});
}

Octeva.ui.EditableTable = Octeva.extend(Octeva.ui.EditableTable, {
	//private
	showEditDataEl: function(td) {
	
		var valueEl = null;
		if ($('input.x_table_data_value', td).length > 0) {
			valueEl = $('input.x_table_data_value', td);
		} else if ($('select.x_table_data_value', td).length > 0) {
			valueEl = $('select.x_table_data_value', td);
		}

		if (valueEl && valueEl.css('display') != '') {
			td.removeClass('changed');
			//save old value
			if (typeof td[0].oldValue == 'undefined') {
				var oldValue = valueEl.val();
				if (oldValue == this.emptyText) oldValue = null;
				td[0].oldValue = oldValue;
			}
			//toggle ui
			$('span.x_table_data_view', td).css('display', 'none');
			valueEl.css('display', '').focus();
		}
	},
	//private
	hideEditDataEl: function(valueEl) {

		if ((valueEl.is('input.x_table_data_value') || valueEl.is('select.x_table_data_value')) 
				&& valueEl.css('display') != 'none') {
				
			var td = valueEl.parent();
			var newValue = (valueEl.val() == this.emptyText)?null:valueEl.val();
			//toggle ui
			if (td[0].oldValue != newValue) {
				td.addClass('changed');
			} else {
				td.removeClass('changed');
			}
			if (newValue == null) {
				$('span.x_table_data_view', td).text(this.emptyText).css('display', '');
			} else {
				$('span.x_table_data_view', td).text(newValue).css('display', '');
			}
			valueEl.css('display', 'none');
			$('select.x_table_data_value').hide();
			if (valueEl.is('input.x_table_data_value')) {
				valueEl.attr('value', newValue);
			}
		} else {
			//throw 'Octeva.ui.EditableTable: Value Element is not found!';
		}
	},
	//public
	acceptChanges: function() {
		
		$('td.x_table_data_editable.changed', this.table).each(function() {
			
			var td = $(this);
			var valueEl = null;
			if ($('input.x_table_data_value', td).length > 0) {
				valueEl = $('input.x_table_data_value', td);
			} else if ($('select.x_table_data_value', td).length > 0) {
				valueEl = $('select.x_table_data_value', td);
			}
			//accept changes
			var oldValue = valueEl.val();
			if (oldValue == this.emptyText) oldValue = null;
			td[0].oldValue = oldValue;
			//udpate style
			$(this).toggleClass('changed');
		});
	}
});

Octeva.encode = function(str) {
	return unescape(encodeURIComponent(str));
}
Octeva.decode = function(str) {
	return escape(decodeURIComponent(str));
}

/**
 * $O.ajax is wrapper of $.ajax
 */
Octeva.ajax = function(request) {

	Octeva.apply(request, {
	  	type: "POST",
	  	timeout: 30000,
	  	dataType: 'json',
	  	success: Octeva.ajax.callbackWrapper(request.success),
	  	error: function (xhr, status, error) {
	  		if (status == 'timeout') {
	  			alert('Server Timeout. Please contact administrator!');
	  		} else if (status == 'notmodified') {
	  			alert('Content not modified. Please try again later!');
	  		} else {	//error
	  			if (xhr.status) {
	  				alert('Internal Error [code=' + xhr.status + ']! Please try again.');
	  			} else {
//					alert('Internal Error! Please try again.');
	  			}
	  		}
		}
	});
	$.ajax(request);
}

Octeva.ajax.callbackWrapper = function(callback) {
	return function(response, status) {
		if (status == 'success') {
			if (callback.length == 1) {
				switch (response.code) {
					case 0:	//SUCCESS
						callback(response.results);
						break;
					case 1:	//INVALID_SESSION
						alert('Login again!');
						break;
					case 1023:	//SYSTEM_ERROR
					case 1024:	//UNKNOW_ERROR
					default:	//USER CUSTOMED ERRORS
						alert(response.message);
				}
			} else if (callback.length == 3) {
				callback(response.code, response.message, response.results);
			} else {
				throw 'Unsupported callback function "' + eval(callback) + '"';
			}
		} else {
//			alert('Internal Error! Please try again.');
		}
		return true;
	}
}

/**
 * support multiple actions in hash, like '#load_user/familyName/asc/1&a=xx#load_event/2005/01/01'
 * 
 * @param config {object}
 *  - defaultParam		{map}		[required]
 *		if we can not get parameter from hash in url, then use this default parameter.
 *  NOTE: Not only the parameters and their values are important in defaultParam, but also their orders.
 *  The parameters in defaultParam, will separated by slash ('/') in url hash. and only values, no parameter
 *  names. and other parameters will shown as 'a=1&b=2'.
 *  
 *  For Eaxample: defaultParam = {'action1', {sort:'title', dir:'asc'}}
 *      call $h.call('action1', null), the hash is '#action1/title/asc';
 *      call : $h.call('action1', {sort: 'title', dir:'desc', p1:'v1', p2:'v2'}),
 *      the hash will be '#action1/title/asc/p1=v1&p2=v2'. 
 *
 * 	- toHash 			{function}	[optional]
 *		toHash = function(action, param) { do something and return hash }
 *		this function is used to convert (action, parameter} to a hash string.
 *		NOTE: the string must start with action.
 *
 *  - toOperation 		{function}	[optional]
 *		toOperation: function(hash) { do something and return {action: action, param: param} }
 *		this function is used to convert a hash string to object (action, parameter}.
 *
 * 	- doAction			{function} 	[optional]
 *		doAction(operation) { do something }
 *		operation is a object like {action: action, param: param}.
 *		if user did not define this function, we will call method 'action(param)'.
 *		for example, if the operation is {action: 'load_user', param: {id: [1,2,3]}}
 *		then, it will execute 'load_user({id:[1,2,3]});'  
 */
Octeva.ajax.History = function(config) {
	Octeva.apply(this, config);
}

Octeva.ajax.History = Octeva.extend(Octeva.ajax.History, {
	/**
	 * public
	 * initialize history callback
	 */
	initialize: function() {
		$.history.init(this._historyCallback_());
	},
	/**
	 * public 
	 * update url, this will call 'history callback' function.
	 * and related actions will execute.
	 * here, support multiple actions
	 * 
	 * if only one argument, it is [op1, op2, ..., opn];
	 * if two arguments, it is action and parameter;
	 */
	call: function() {
		var history = this;
		var _update_hash_array_ = function(strHashes, action, param) {
			var index = -1;
			for (var i = 0; i < strHashes.length; i ++) {
				var hash = strHashes[i];
				if (hash.indexOf(action) == 0) {
					index = i; 
				} else {
					strHashes[i] = '#' + hash;
				}
			}
			var new_hash = history.toHash(action, param);
			if (new_hash) {
				if (index > -1) {
					strHashes[index] = '#' + new_hash;
				} else {
					strHashes[strHashes.length] = '#' + new_hash;
				}
			}
		}
		
		var strHashes = this._getHashes();
		if (arguments.length == 1) {
			var operations = arguments[0];
			for (var i = 0; i < operations.length; i ++) {
				var op = operations[i];
				_update_hash_array_(strHashes, op.action, op.param);
			}
		} else if (arguments.length == 2){
			var action = arguments[0];
			var param = arguments[1];
			_update_hash_array_(strHashes, action, param);
		}
		window.location.hash = strHashes.join('').substring(1);
	},
	/**
	 * public
	 * @return refresh the page by url
	 */
	refresh: function() {
		var strHashes = this._getHashes();
		$.history._curHash = '';//make sure the data will reloaded
		var operations = []; 
		for (var i = 0; i < strHashes.length; i ++) {
			var hash = strHashes[i];
			operations[operations.length] = $h.toOperation(hash);
		}
		this.call(operations);
	},
	/**
	 * public
	 * @return get param of an action
	 */
	getParam: function(action) {
		var param = null;
		var strHashes = this._getHashes();
		for (var i = 0; i < strHashes.length; i ++) {
			var hash = strHashes[i];
			if (hash.indexOf(action) == 0) {
				var op = this.toOperation(hash);
				param = op.param;
				break;
			}
		}
		if (!param) {
			param = this.defaultParam[action];
		}
		return param;
	},
	/**
	 * default doAction
	 */
	doAction: function(operation) {
		if (operation && operation.action) {
			var action = operation.action;
			var param = operation.param;
			if (typeof(eval(action)) == 'function')  {
				var func = eval(action);
				func.call(this, param);
			} else {
				throw 'Not found GLOBAL function : ' + action;
			}
		}
	},
	/**
	 * default toHash
	 */
	toHash: function(action, param) {
		var default_param = this.defaultParam[action];
		if (!default_param) {
			throw 'Unknow action: ' + action;
		} else {
			var hash = $O.encode(action) + '/';
			//append default param first
			for (var p in default_param) {
				var value = param[p];
				if (value instanceof Array) {
					var tmp = [];
					for (var i = 0; i < value.length; i ++) {
						tmp[tmp.length] = $O.encode(value[i]);
					}
					hash += tmp.join(',') + '/';
				} else {	//string
					hash += $O.encode(value) + '/';
				}
			}
			//append other param
			var c = 0;
			for (var p in param) {
				if (typeof(default_param[p]) == 'undefined') {	//p is not in default parameters
					var value = param[p];
					var tmp = [];
					if (value instanceof Array) {
						for (var i = 0; i < value.length; i ++) {
							tmp[tmp.length] = $O.encode(value[i]);
						}
					} else {	//string
						tmp = [$O.encode(value)];
					}
					for (var i = 0; i < tmp.length; i ++) {
						if (c > 0) {
							hash += '&' + p + '=' + tmp[i];
						} else {
							hash += p + '=' + tmp[i];
						}
						c ++;
					}
				}
			}
			return hash; 
		}
	},
	/**
	 * default toOperation
	 */
	toOperation: function(hash) {
		var action = null;
		var param = {};
		if (hash) {
			var ps = hash.split('/');
			action = $O.decode(ps[0]);
			var default_param = this.defaultParam[action];
			if (!default_param) {
				throw 'Unknow action: ' + action;
			} else {
				//add default parameters
				var index = 1;
				for (var p in default_param) {
					var type = typeof(default_param[p]);
					var str_value = $O.decode(ps[index]);
					if (str_value == '') {
						param[p] = default_param[p];
					} else {
						var value = null;
						if (type == 'object') {
							if (default_param[p] instanceof Array) {
								value = str_value.split('%2C');
							} else {
								value = str_value;
							}
						} else if (type == 'number') {
							value = parseFloat(str_value, 10);
						} else {
							value = str_value;
						}
						param[p] = value;
					}
					index ++;
				}
				//add other parameters
				var other_param = ps[ps.length-1];
				if (other_param.length > 0) {
					var pairs = other_param.split('&');
					for (var i = 0; i < pairs.length; i ++) {
						var tmp = pairs[i].split('=');
						var key = tmp[0];
						var val = $O.decode(tmp[1]);
						if (param[key]) {
							var array = param[key];
							if (!(array instanceof Array)) {
								param[key] = [array];
							}
							param[key][param[key].length] = val;
						} else {
							param[key] = val;
						}
					}
				}
			}
		}
		return {action: action, param: param};
	},
	/**
	 * private
	 */
	_historyCallback_: function() {
		var history = this;
		return function() {
			var strHashes = history._getHashes();
			for (var i = 0; i < strHashes.length; i ++) {
				var hash = strHashes[i];
				var op = $h.toOperation(hash);
				history.doAction(op);
			}
		}
	},
	/**
	 * private
	 */
	_getHashes: function() {
		var tmp = null;
		var hash = window.location.hash;
		if (hash == '' || hash == '#') {
			for (var action in this.defaultParam) {
				var param = this.defaultParam[action];
				hash += '#' + this.toHash(action, param);
			}
		}
		tmp = hash.split('#');
		var hashes = [];
		for (var i = 0; i < tmp.length; i ++) {
			if (tmp[i] && tmp[i].length > 0) {
				hashes[hashes.length] = tmp[i];
			}
		}
		return hashes;
	}
});