var easy = {};

// basic data input components
// can be used with <input type="text"> or <textarea/>
easy.TextInput = ajax.DomBean.SubClass();
Object.extend(easy.TextInput.prototype, {

	validate:true,// validate attributes
	required:	false,
	datatype:	null,	// null for string, others see easy.data.*
	regexp:	null,
	len:	null,
	jsexpr:	null,	// this.length < 6
	tips:	null,
	promptAt:	null,	// when changed, automate refresh these elements
	prompt:	null,	// prompt message
	_prompt:	null,	// dynamic prompt message
	

	initialize: function(element){
		this.datatype = new easy.data.String();
		ajax.DomBean.prototype.initialize.call(this, element);
		if(this.promptAt == null){
			var span = this.promptAt = document.createElement("span");
			span.className = "inputvalid";
			if(this.element.parentNode)
				this.element.parentNode.insertBefore(span, this.element.nextSibling);
			new ajax.ExprText(span);
		}
		Event.observe(element, "change", this._changed.bind(this) );
	},
	
	_changed: function(){

		this.validate= this.doValidate();

		var value = this.element.value;
		var dt = this.datatype;
		
		if(this.validate){
			var value = dt.fromExternal(value);	
			this.evaluateSetter("value", value);
		}
		else {
			this.evaluateSetter("value", null);
		} 
	},
	
	setPromptAt: function(e){
		this.promptAt = $(e);
	},

	setValue: function(text){
		if(text == null) text = "";
		this.element.value = text;
//		this.doValidate();
	},
	
	setDatatype: function(type){
		try {
			var cls = eval(type);	// easy.data.Email like
			if(cls != null){
				this.datatype = new cls();
			}
		}
		catch(ex){
			this.datatype = new easy.data.String();
		}
	},
	
	setJsexpr: function(expr){
		if(typeof expr == "function")
			this.jsexpr = expr;
		else if(typeof expr == "string"){
			this.jsexpr = new Function("it", "return (" + expr + ");");		
		}
	},
	
	setRegexp: function(expr){
		if(expr instanceof RegExp)
			this.regexp = expr;
		else 
			this.regexp = new  RegExp(expr);
	},

	doValidate: function(){
		
		this.validate = this._checkValidate();

		if(this.promptAt && this.promptAt.domBean){
			if(this.validate == false){
				this.promptAt.domBean.setExpr(this._prompt);
				this.promptAt.domBean.setVisible(true);
			}
			else {
				this.promptAt.domBean.setExpr(null);
				this.promptAt.domBean.setVisible(false);
			}
		}
		
		return this.validate;
	},
	
	_checkValidate: function(){

		var value = this.element.value;
		if(value == null || value == ""){
			if(this.required){
				this._prompt="必须填写";			
				return false;		// required
			}else
				return true;	// dont check others
		}
				
		try {
			this._checkLength(value);
			this._checkJsexpr(value);
			this._checkRegexpr(value);
		}
		catch(ex){
			this._prompt = (this.prompt == null)? ex.toString() : this.prompt;
			return false;
		}
		
		var check = this.datatype.checkExternalValid(this.element.value);
		if( check == false) {
			this._prompt = (this.prompt == null)? this.datatype.errMsg: this.prompt;
			return false;
		}
		else return true;
	},
		
	_checkLength: function(value){
		if(this.len != null && value.length != this.len)
			throw "length invalid";
	},
	
	_checkJsexpr: function(value){
		if(this.jsexpr != null){
			var result = this.jsexpr.call(this.element, value);
			if(result == false)
				throw "jsexpr invalid";
		}
	},
	
	_checkRegexpr: function(value){
		if(this.regexp == null)
			return;
		var valid = this.regexp.test(value);
		if(valid == false) 
			throw "regexpr invalid";
	}
		
});

// <select>
easy.SelectInput = ajax.DomBean.SubClass();

Object.extend(easy.SelectInput.prototype, {
	validate:true,// validate attributes
	required:	false,
	items:	null,	// usage for select, either { value: label } or [ value, value ]
	selected:	null,	// used to save the radio/checkbox/select's current value
	initItems:null, //options contain in html
	tips:	null,
	promptAt:	null,	// when changed, automate refresh these elements
	prompt:	null,	// prompt message
	_prompt:	null,	// dynamic prompt message
	
	initialize: function(element){
		this.initItems=[];
		for (i=0;i<element.options.length;i++){
			this.initItems.push(element.options[i]);
		}

		ajax.DomBean.prototype.initialize.call(this, element);
		Event.observe(element, "change", this._changed.bind(this) );
		if(this.promptAt == null){
			var span = this.promptAt = document.createElement("span");
			span.className = "inputvalid";
			if(this.element.parentNode)
				this.element.parentNode.insertBefore(span, this.element.nextSibling);
			new ajax.ExprText(span);
		}		
	},
	doValidate: function(){
		var value = this.element.value;
		if(value == null || value == ""){
			if(this.required){
				this._prompt="必须填写";
				this.validate=false;// required
			}else{
				this.validate=true;// dont check others
			}	

		}
		if(this.promptAt && this.promptAt.domBean){
			if(this.validate == false){
				this.promptAt.domBean.setExpr(this._prompt);
				this.promptAt.domBean.setVisible(true);
			}
			else {
				this.promptAt.domBean.setExpr(null);
				this.promptAt.domBean.setVisible(false);
			}
		}
		return this.validate;		
	},
	setItems:	function(items){
		this.items = items;
		
		// clear old options
		while(this.element.options.length > 0)
			this.element.remove(0);
		for (i=0;i<this.initItems.length;i++){
			this.element.options.add(this.initItems[i]);
		}		
		if(items instanceof Array){	// [ "item0", "item1" ]
			for(var idx = 0; idx < items.length; idx++){
				var option = new Option( items[idx], idx );
				this.element.options.add(option);
				if (this.selected!=null&&option.value==this.selected){
					option.selected=true;				
					this.evaluateSetter("selected", option.value);	
					this.evaluateSetter("value", option.value);			
				}
			}
		}
		else for(var idx in items){
			var item = items[idx];
			if(typeof item == "string"){
				var option = new Option( item, idx );
				this.element.options.add(option); 
				if (this.selected!=null&&option.value==this.selected){
					option.selected=true;				
					this.evaluateSetter("selected", option.value);	
					this.evaluateSetter("value", option.value);			
				}
			}
		}
		
	},
	setValue: function(text){

		if(text == null) text = "";
		this.element.value = text;
		if (text!="" && this.selected==null)
			this.selected=text;
		if(this.element.value != ""){
			 this.element.checked = true;
			var options = this.element.options;
			
			for(var idx = 0; idx < options.length; idx++){
				var option = options[idx];
				if (option.selected)
						option.selected=false;
			}
			
			for(var idx = 0; idx < options.length; idx++){
				var option = options[idx];		
				if(text==option.value){
					option.selected=true;
									
			// added for notify that the value has changed
			
					if(this.element.fireEvent){
						this.element.fireEvent("onchange");
					}
					else if(this.element.dispatchEvent){
						var ev = document.createEvent("Events");
						ev.initEvent("change", true, true);			
						this.element.dispatchEvent(ev);
					}
				
						
				}				
			}
		}
	
	},
	_changed:	function(){
		var value = [];
		var options = this.element.options;
		for(var idx = 0; idx < options.length; idx++){
			var option = options[idx];
			if(option.selected){
				value[value.length] = option.value;
				this.selected=option.value;
				if(option.value != null && option.value != "")
					this.validate = true;
			}	
		}
		if(this.element.multiple == false){	// single
			value = (value.length > 0) ? value[0] : null;
		}
		this.evaluateSetter("selected", value);	
		this.evaluateSetter("value", value);	
		this.doValidate();
		
	}


});

easy.CheckboxInput = ajax.DomBean.SubClass();

Object.extend(easy.CheckboxInput.prototype, {
		
	initialize: function(element){
		ajax.DomBean.prototype.initialize.call(this, element);
		Event.observe(element, "click", this._changed.bind(this) );			
	},
	
	setChecked: function(checked){
		var flag = false;
		if(typeof checked == "string"){
			switch(checked){
				case "checked":
				case "true":
					flag = true;
					break;
			}
		}
		else flag = checked ? true : false;
		this.element.checked = flag;
	},


	_changed : function(){
		var value = this.element.checked;
		this.evaluateSetter("checked", value);
	}
});


easy.RadioInput = ajax.DomBean.SubClass();

Object.extend(easy.RadioInput.prototype, {
	
	_group:	null,	// array of radios in the same group
	
	initialize: function(element){
		ajax.DomBean.prototype.initialize.call(this, element);
		Event.observe(element, "click", this._changed.bind(this) );
		
		var name = this.element.name;
		if(name == null || name == "")
			return;
			
		name = "__radio_group_" + name;
		if(window[name] == null) window[name] = [];
		this._group = window[name];
		this._group[this._group.length] = this.element;
			
	},
	
	setChecked: function(checked){
		var flag = false;
		if(typeof checked == "string"){
			switch(checked){
				case "checked":
				case "true":
					flag = true;
					break;
			}
		}
		else flag = checked ? true : false;
		this.element.checked = flag;
	},


	_changed : function(){
		for(var idx = 0; idx < this._group.length; idx++){
			var element = this._group[idx];
			if(element.domBean && element.domBean instanceof easy.RadioInput){
				var value = element.checked;
				element.domBean.evaluateSetter("checked", value);
			}
		}
	}
});

// data type support used in easy.TextInput/easy.SelectInput/easy.RadioInput/easy.CheckboxInput
easy.data = {};

easy.data.String = Class.create();
easy.data.String.prototype = {
	errMsg:"字符",
	isAdaptor:	true,
	
	initialize:	function(){
	},

	// checkExternalValid must be first checked before fromExternal	
	fromExternal:	function(ext){
		return ext;
	},
	toExternal:	function(obj){
		return obj;
	},
	checkExternalValid:	function(ext){
		return true;
	}
};

easy.data.Boolean = Class.create();
Object.extend(easy.data.Boolean.prototype, easy.data.String.prototype);
Object.extend(easy.data.Boolean.prototype,  {
	errMsg:"true/false",
	fromExternal:	function(ext){
		return ext == "true";
	},
	toExternal: function(obj){
		return obj ? "true" : "false";
	},
	checkExternalValid:	function(ext){
		return ext == "true" || ext == "false";
	}
});

easy.data.Number = Class.create();
Object.extend(easy.data.Number.prototype, easy.data.String.prototype);
Object.extend(easy.data.Number.prototype, {
	
	fromExternal:	function(ext){
		return new Number(ext).valueOf();
	},
	toExternal: function(obj){
		return new String(obj);
	},
	checkExternalValid:	function(ext){
		var num = new Number(ext);
		return isNaN(num) == false;
	}
});
easy.data.Integer = Class.create();
Object.extend(easy.data.Integer.prototype, easy.data.String.prototype);
Object.extend(easy.data.Integer.prototype, {
	errMsg:"错误整数",
	fromExternal:	function(ext){
		return new Number(ext).valueOf();
	},
	toExternal: function(obj){
		return new String(obj);
	},
	checkExternalValid:	function(ext){

		return this.isInteger(ext);
	},
	isInteger:	function (s) {
		var regexp = /^[+]?\d+$/;
		return this.isEmpty(s) || regexp.test(s);
	},
	isEmpty:function (s) {
	    var regexpWhitespace = /^\s+$/;
		return  ((s == null) || (s.length == 0) || regexpWhitespace.test(s));
	}
	
});
easy.data.Double = Class.create();
Object.extend(easy.data.Double.prototype, easy.data.String.prototype);
Object.extend(easy.data.Double.prototype, {
	errMsg:"错误小数",
	fromExternal:	function(ext){
		return new Number(ext).valueOf();
	},
	toExternal: function(obj){
		return new String(obj);
	},
	checkExternalValid:	function(ext){

		return this.isDouble(ext);
	},
	isDouble:	function (s) {
		var doubleExp=/^[0-9]*.[0-9]*/;
		return this.isEmpty(s) || (s.length==s.match(doubleExp)[0].length);
	},
    isEmpty:function (s) {
		var regexpWhitespace = /^\s+$/;
		return  ((s == null) || (s.length == 0) || regexpWhitespace.test(s));
	}
});

// internal as string, not as javascript Date
easy.data.Date = Class.create();
Object.extend(easy.data.Date.prototype, easy.data.String.prototype);
Object.extend(easy.data.Date.prototype, {
	errMsg:"错误日期",
	_re:	/(\d{4})-(\d{1,2})-(\d{1,2})/,
	
	checkExternalValid:	function(ext){
		return this._re.test(ext);
	}
	
});

easy.data.Email = Class.create();
Object.extend(easy.data.Email.prototype, easy.data.String.prototype);
Object.extend(easy.data.Email.prototype, {
	errMsg:"错误email",
	_re:	/\w{1,}[@][\w\-]{1,}([.]([\w\-]{1,})){1,3}$/,

	checkExternalValid:	function(ext){
		return this._re.test(ext);
	},
	
	
	fromExternal: function(ext){
		return ext;
	}
	
});

easy.TabbedPanel = ajax.DomBean.SubClass();

Object.extend(easy.TabbedPanel.prototype, {
	
	pages:	null,
	currentPage: null,
	headerDiv: null,	// the created header area
	tabHeaderImgs: [],	// header images
	
	refresh: function(){
		if(this.pages == null){
			this._initPages();	
		}
		
		for(var i=this.element.childNodes.length-1; i>=0; i--){
			var e = this.element.childNodes[i];
			if(e.nodeType == 3)
				this.element.removeChild(e);
		}
		if(this.headerDiv == null){
			this.headerDiv = document.createElement("div");
			this.headerDiv.className = "tabHeader";
			this.element.insertBefore(this.headerDiv, this.pages[0]);
			for(var i=0; i<this.pages.length; i++){
				var image = this.pages[i].domBean.images[0];
				image["_index"] = i;
				image["_tabPanel"] = this.element;
				image.onclick = this._ClickOnImage;
				this.headerDiv.insertBefore(image, null);
				this.tabHeaderImgs[i] = image;
			}
		}
		
		this.selectPage(0);	

	},
	
	selectPage: function(index){
		this.currentPage = this.pages[index];
		for(var i=0; i<this.pages.length; i++){
			if(this.pages[i] == this.currentPage){
				// this.pages[i].domBean.setVisible(true);
				if(this.pages[i].parentNode != this.element)
					this.element.insertBefore(this.pages[i], this.headerDiv.nextSibling);

				var oldImg = this.tabHeaderImgs[i];
				var newImg = this.pages[i].domBean.images[1];
				if(oldImg != newImg){
					oldImg.parentNode.replaceChild(newImg, oldImg);
				}
				this.tabHeaderImgs[i] = newImg;
			} else {
				// this.pages[i].domBean.setVisible(false);
				if(this.pages[i].parentNode == this.element)
					this.element.removeChild(this.pages[i]);
				var oldImg = this.tabHeaderImgs[i];
				var newImg = this.pages[i].domBean.images[0];
				if(oldImg != newImg){
					oldImg.parentNode.replaceChild(newImg, oldImg);
				}
				this.tabHeaderImgs[i] = newImg;
			}
		}
	},
	
	_initPages: function(){
		var pages = [];
		for(var i = 0; i<this.element.childNodes.length; i++){
			var node = this.element.childNodes[i];
			if(node.nodeType == 1 && node.domBean != null && node.domBean instanceof easy.TabbedPage ){
				pages[pages.length] = node;
			}
		}
		this.pages = pages;
	},
	
	_ClickOnImage: function(evt){
		var img = (evt == null) ? event.srcElement : evt.target;
		var tabPanel = img["_tabPanel"];
		var index = img["_index"];
		tabPanel.domBean.selectPage(index);
	}
	
});

easy.TabbedPage = ajax.DomBean.SubClass();

Object.extend(easy.TabbedPage.prototype, {
	
	images: null,	//	[0] normal, [1] highlight
	
	setImages: function(images){
		var imageURLs = images.split(/[\s|;]/);
		this.images = new Array(2);
		var i = 0;
		for(i=0; i<imageURLs.length; i++){
			var img = document.createElement("IMG");
			img.src= imageURLs[i];
			this.images[i] = img;
		}
		if(i == 1)
			this.images[1] = this.images[0];
	}
	
});


easy.MenuBar = ajax.DomBean.SubClass();

Object.extend(easy.MenuBar.prototype, {
	
	initialize: function(element){
		ajax.DomBean.prototype.initialize.call(this, element);
		this._loadStyleSheet();
		var emptyDiv = document.createElement("DIV");
		emptyDiv.style.clear = "left";
		if(document.all == null) // firefox
			emptyDiv.style.height = "1px";
		element.parentNode.insertBefore(emptyDiv, element.nextSibling);
	},
	
	_loadStyleSheet: function(){
		Element.addClassName(this.element, "menubar");
		if(document.styleSheets){
			var loaded = false;
			for(var i = 0; i<document.styleSheets.length; i++){
				 var style = document.styleSheets[i];
				 if(style.title == "easymenu.css")	{ // already defined
				 	loaded = true;
				 	break;
				}
			}
			if(loaded == false){
				var head = document.getElementsByTagName("HEAD")[0];
				var link = document.createElement("LINK");
				link.href = ajax.baseURI + "/css/easymenu.css";
				link.rel = "stylesheet";
				link.type = "text/css";
				link.title = "easymenu.css";
				head.insertBefore(link, null);
			}
		}
		
		if(document.all){ // for IE
			var sfEls = this.element.getElementsByTagName("LI");
			for (var i=0; i<sfEls.length; i++) {
				sfEls[i].onmouseover=function() {
					this.className+=" sfhover";
				}
				sfEls[i].onmouseout=function() {
					this.className=this.className.replace(new RegExp(" sfhover\\b"), "");
				}
			}
		}
	}
	
});

easy.Decorator = ajax.DomBean.SubClass();

Object.extend(easy.Decorator.prototype, {
	
	url:	null,	//
	
	_iframe:	null,	// the iframe used to load the template
	_intervalId:	null,
	_count:			null,
	_savedChildren:	null,
	_prompt:	null,
	
	// 1. use an iframe to load the template
	// 2. decorate the element using the template
	// 3. dont initialize the children util decorate complete
	initialize:	function(el){
		ajax.DomBean.prototype.initialize.call(this, el);
		
		this._savedChildren = new Array();
		while(el.firstChild != null){
			this._savedChildren[this._savedChildren.length] = el.firstChild;
			el.removeChild(el.firstChild);
		}

		var iframe = document.createElement("iframe");
		iframe.src = this.url;
		iframe.style.height = "0px"; iframe.style.width="0px";
		//iframe.height = -1; iframe.width = -1;
		// iframe.style.display = "none"; // not works in opera 
		this.element.insertBefore(iframe, null);
		
		var prompt = this._prompt = document.createElement("div");
		prompt.className = "asyn-msg";
		prompt.insertBefore( document.createTextNode("Loading..."), null);
		this.element.insertBefore(prompt, null);
				
		this._iframe = iframe;
		this._count = 0;
		this._intervalId = setInterval( this._checkLoaded.bind(this), 100);
		
		return false;
	},
	
	_clear: function(){
		
		clearInterval(this._intervalId); 
		delete(this._intervalId);
		delete(this._count);
		delete(this._iframe);	// already removed from DOM tree
		delete(this._prompt);
	},
	
	_checkLoaded: function(){
		
		this._count++;
		if(this._count == 600){	// 60s
			this._clear();
			return;
		}
		var iframe = this._iframe;
		
		var win = iframe.contentWindow;
		if(win == null)
			return;
		
		// when using firefox, be sure to set readyState in the template html
		if(win.readyState != "complete")
			return;

		var doc = win.document;	
			
		var content = doc.getElementById("TemplateBody");
		if(content == null)
			content = doc.getElementsByTagName("body")[0];
			
		this.element.innerHTML = content.innerHTML;

		this._clear();
		
		var insertDecorated = null;
		ajax.DomBean.TopDownIterateElements(this.element, 
			function(el){
				if(el.id == "InsertDecorated"){
					insertDecorated = el;
				}
			}, 
			null);
		
		if(insertDecorated != null){
			while(insertDecorated.firstChild != null)
				insertDecorated.removeChild(insertDecorated.firstChild);
				
			for(var i=0; i<this._savedChildren.length; i++){
				insertDecorated.insertBefore(this._savedChildren[i], null);	
			}
		}
		
		this.element.domBean = null;
		this.element.removeAttribute("jsclass");
		
		ajax.DomBean.InitElement(this.element);
		ajax.DomBean.RefreshElement(this.element);
	}	
});

/*
 * easy.RollableDiv
 */ 
easy.RollableDiv = ajax.DomBean.SubClass();
Object.extend(easy.RollableDiv.prototype, {
	
	headE:	null,
	contentE:	null,
	toggleE:	null,
	
	showContent: true,		//an watchable property
	
	initialize:	function(e){
		
		ajax.DomBean.prototype.initialize.call(this, e);
		
		ajax.lang.System.addWatchableProperty(this, "showContent");
		
		// find headE/contentE/toggleE
		var children = e.childNodes;
		for(var i=0; i<children.length; i++){
			var child = children[i];
			if(child.nodeType == 1){
				 if(Element.hasClassName(child, "easy.RollableDiv.header")) this.headE = child;
				 if(Element.hasClassName(child, "easy.RollableDiv.content")) this.contentE = child;
			}
		}
		
		var gThis = this;
		if(this.headE != null){
			ajax.lang.XmlUtils.topDownIterateElements(this.headE, function(e){
				if(Element.hasClassName(e, "easy.RollableDiv.toggler")){
					gThis.toggleE = e;
					//return ajax.lang.XmlUtils.DOM_ITERATE_STOP;
				}
			});
		}
		
		if(this.headE == null || this.contentE == null ){
			throw "invalid usage of easy.RollableDiv, missing element header/content";
		}
		
		if(this.toggleE != null)
			Event.observe(this.toggleE, "click", this.toggle.bind(this));
	},
		
	toggle:	function(){
		var showContent = this.getShowContent() ? false: true;
		this.setShowContent(showContent);	//
		
//		if(showContent) Element.show(this.contentE);
//		else Element.hide(this.contentE);
	}
	
});

easy.Effect=ajax.DomBean.SubClass();
Object.extend(easy.Effect.prototype,{
	frequency:100,
	repeat:5,
	e:null,
	initialize: function(e){
		this.e=e;
		ajax.DomBean.prototype.initialize.call(this, e);
		Event.observe(e, "click", this._clickEffect.bindAsEventListener(this) );  
		new PeriodicalExecuter(this._clickEffect,this.frequency);
	},
	setFrequency:function(milsecond){
		this.frequency=milsecond;
	},
	setRepeat:function(repeat){
		this.repeat=repeat;
	},
	_clickEffect:function(){
		Element.toggle(this.e);
	}
});