/*===========================================================================
 *  Longway JavaScript framework, version 0.0.2
 *  (c) 2007 Wayne Liu
 *
 *  Prototype is freely distributable under the terms of an MIT license.
 *  For details, see the Longway web site: http://www.longway.cc/
/*===========================================================================*/

/**
 * The root package of Longway javascript framework.
 */
var Longway = {
	/**
	 * Longway javascript framework version.
	 */
	Version: '0.0.2',

	/**
	 * Check dependency.
	 * @exception {PrototypeNotFound} If not found Prototype or Prototype Version < 1.5.
	 */
	load: function() {
		if((typeof Prototype == 'undefined') ||
		   (typeof Element == 'undefined') ||
		   (typeof Element.Methods == 'undefined') ||
		   parseFloat(Prototype.Version.split(".")[0] + "." +
		              Prototype.Version.split(".")[1]) < 1.5)
		   throw("Longway requires the Prototype JavaScript framework >= 1.5.0");
	},

	compare: function(op1, op2, operator) {
		if (op1 == null) {
			return false;
		}
		if ("DataTypeCheck" == operator) {
			return true;
		}
	    switch (operator) {
			case "!=":
	        case "NotEqual":
	            return (op1 != op2);
			case ">":
	        case "GreaterThan":
	            return (op1 > op2);
			case ">=":
	        case "GreaterThanEqual":
	            return (op1 >= op2);
			case "<":
	        case "LessThan":
	            return (op1 < op2);
			case "<=":
	        case "LessThanEqual":
	            return (op1 <= op2);
	        default:
	            return (op1 == op2);
	    }
	},

	convert: function(op, dataType, splitChar, format) {
		function getFullYear(year) {
        	return (year + parseInt(20)) - ((year < 40) ? 0 : 100);
    	}
	    var num, cleanInput, m, exp;
	    if (dataType == "Integer") {
	        exp = /^\s*[-\+]?\d+\s*$/;
	        if (op.match(exp) == null)
	            return null;
	        num = parseInt(op, 10);
	        return (isNaN(num) ? null : num);
	    }
	    else if(dataType == "Double") {
	        exp = new RegExp("^\\s*([-\\+])?(\\d+)?(\\" + val.decimalchar + "(\\d+))?\\s*$");
	        m = op.match(exp);
	        if (m == null)
	            return null;
	        cleanInput = m[1] + (m[2].length>0 ? m[2] : "0") + "." + m[4];
	        num = parseFloat(cleanInput);
	        return (isNaN(num) ? null : num);
	    }
	    else if (dataType == "Currency") {
			// TODO: implement currency converter.
			var groupChar = ",";
			var decimalchar = "";
			var digits = "";
	        exp = new RegExp("^\\s*([-\\+])?(((\\d+)\\" + "," + ")*)(\\d+)"
	                        + ((val.digits > 0) ? "(\\" + val.decimalchar + "(\\d{1," + val.digits + "}))?" : "")
	                        + "\\s*$");
	        m = op.match(exp);
	        if (m == null)
	            return null;
	        var intermed = m[2] + m[5] ;
	        cleanInput = m[1] + intermed.replace(new RegExp("(\\" + val.groupchar + ")", "g"), "") + ((val.digits > 0) ? "." + m[7] : 0);
	        num = parseFloat(cleanInput);
	        return (isNaN(num) ? null : num);
	    }
	    else if (dataType == "Date") {
	        var yearFirstExp = new RegExp("^\\s*((\\d{4})|(\\d{2}))([-/]|\\. ?)(\\d{1,2})\\4(\\d{1,2})\\s*$");
	        m = op.match(yearFirstExp);
	        var day, month, year;
	        if (m != null && (m[2].length == 4 || format == "ymd")) {
	            day = m[6];
	            month = m[5];
	            year = (m[2].length == 4) ? m[2] : GetFullYear(parseInt(m[3], 10))
	        }
	        else {
	            if (format == "ymd"){
	                return null;
	            }
	            var yearLastExp = new RegExp("^\\s*(\\d{1,2})([-/]|\\. ?)(\\d{1,2})\\2((\\d{4})|(\\d{2}))\\s*$");
	            m = op.match(yearLastExp);
	            if (m == null) {
	                return null;
	            }
	            if (val.dateorder == "mdy") {
	                day = m[3];
	                month = m[1];
	            }
	            else {
	                day = m[1];
	                month = m[3];
	            }
	            year = (m[5].length == 4) ? m[5] : getFullYear(parseInt(m[6], 10))
	        }
	        month -= 1;
	        var date = new Date(year, month, day);
	        return (typeof(date) == "object" && year == date.getFullYear() && month == date.getMonth() && day == date.getDate()) ? date.valueOf() : null;
	    }
	    else {
	        return op.toString();
	    }
	}
}
Longway.load();

Object.extend(String.prototype, {
	trim: function() {
		var m = this.match(/^\s*(\S+(\s+\S+)*)\s*$/);
    	return (m == null) ? "" : m[1];
	}
});

/**
 * DOM implementation adapter.
 */
Longway.XmlDocument = {
	/**
	 * Get a DOM Document instance.
	 * @param async {boolean} Indicates whether or not load document asynchronously, default value is false.
	 * @return {Object} A DOM Document instance based on current browser.
	 * @exception {Error} If cannot create DOM Document.
	 */
	getInstance: function(async /* = false */) {
		var asyncValue = (async) ? async : false;
		if (document.implementation && document.implementation.createDocument) {
			var xmlDocument = document.implementation.createDocument("", "", null);
			xmlDocument.async = asyncValue;
			return xmlDocument;
		}
		if (window.ActiveXObject) {
			try {
				var prefix = ["MSXML2", "MSXML", "Microsoft", "MSXML3"];
				for (var i = 0; i < prefix.length; i++) {
					var obj = new ActiveXObject(prefix[i] + ".DomDocument");
					if (obj == null || typeof(obj) == 'undefined') {
						continue;
					} else {
						obj.async = asyncValue;
						return obj;
					}
				}
			} catch (e) {
				// TODO: Handle exception friendly.
				throw new Error(e);
			}
		}
		throw new Error("Cannot create DOM Document!");
	}
}
Longway.Control = {
	getValue: function(id) {
	    var control;
	    control = $(id);
	    if (typeof(control.value) == "string") {
	        return control.value;
	    }
	    if (typeof(control.tagName) == "undefined" && typeof(control.length) == "number") {
	        var j;
	        for (j=0; j < control.length; j++) {
	            var inner = control[j];
	            if (typeof(inner.value) == "string" && (inner.type != "radio" || inner.status == true)) {
	                return inner.value;
	            }
	        }
	    }
	    else {
	        return getValueRecursive(control);
	    }
	    return "";
	},
	getValueRecursive: function(control) {
	    if (typeof(control.value) == "string" && (control.type != "radio" || control.status == true)) {
	        return control.value;
	    }
	    var i, val;
	    for (i = 0; i < control.children.length; i++) {
	        val = getValueRecursive(control.children[i]);
	        if (val != "") return val;
	    }
	    return "";
	}
}


Longway.Control.DropDownList = Class.create();
Longway.Control.DropDownList.prototype = {
	DataSource: null,
	FrontObject: null,
	SelectedValue: null,
	SelectedIndex: -1,
	SelectedText: null,
	ValueMemberField: null,
	TextMemberField: null,
	HoldFirstOption: false,
	initialize: function() {

	},
	DataBind: function() {
		if (this.DataSource == null) {
			return;
		}
		if (this.TextMemberField == null) {
			return;
		}
		var dataSource = eval(this.DataSource);
		if (this.HoldFirstOption)
		{
			this.FrontObject.options.length = 1;
		} else {
			this.FrontObject.options.length = 0;
		}
		for (var i = 0; i < dataSource.length; i++) {
			var oOption = document.createElement("OPTION");
			oOption.text = eval("dataSource[i]." + this.TextMemberField);
			if (this.ValueMemberField)
			{
				oOption.value = eval("dataSource[i]." + this.ValueMemberField);
			}
			if (this.SelectedValue == oOption.value) {
				oOption.selected = true;
			}
			this.FrontObject.options.add(oOption);
		}
	}
}

Longway.Control.DropDownList.Bind = function(controlId, option) {
	 var json = option.dataSource;
	 var ddl = new Longway.Control.DropDownList();
	 ddl.DataSource = json;
	 ddl.FrontObject = document.getElementById(controlId);
	 ddl.ValueMemberField = option.valueField;
	 ddl.TextMemberField = option.textField;
	 ddl.SelectedValue = option.selectedValue;
	 ddl.HoldFirstOption = option.holdFirst;
	 ddl.DataBind();
}
/**
 * ValidationRule holds a group of Validators.
 * @classDescription ValidationRule holds a group of Validators.
 */
Longway.ValidationRule = Class.create();
Longway.ValidationRule.prototype = {
	/**
	 * @constructor
	 * @param {Object} ruleNode an XML DOM node which contains the validator elements.
	 */
	initialize: function(ruleNode) {
		this._active = true;
		this._isValid = true;
		this._blockSubmit = false,
		this._validators = [];
		this._messageType = "Individual";
		this._headerText = "";
		this._messageContainer = "";
		this._htmlObject = null;
		this._displayMode = "BulletList";

		this._messageType = ruleNode.getAttribute("type");
		this._messageContainer = ruleNode.getAttribute("ctn");
		this._htmlObject = $(ruleNode.getAttribute("id"));
		this._displayMode = ruleNode.getAttribute("mode");
		this._headerText = ruleNode.getAttribute("header");

		var validatorNodes = ruleNode.getElementsByTagName("validator");

		for (var i = 0; i < validatorNodes.length; i++) {
			var validatorNode = validatorNodes.item(i);
			eval("var validator = new Longway." + validatorNode.getAttribute("impl") + "(this, validatorNode);");
			this._validators[this._validators.length] = validator;
		}
	},
	updateIsValid: function() {
    	for (var i = 0; i < this._validators.length; i++) {
        	if (!this._validators[i].isValid) {
            	this._isValid = false;
				//alert(this._validators[i].controlToValidate);
            	return;
        	}
   		}
   		this._isValid = true;
	},
	validate: function() {
		 for (var i = 0; i < this._validators.length; i++) {
			var val = this._validators[i];
			var control = $(val.controlToValidate);
			control.isValid = true;
		}
	    for (var i = 0; i < this._validators.length; i++) {
	        this._validators[i].validate();
	    }
	    this.updateIsValid();
	    this.showSummary();
	    this._blockSubmit = !this._isValid;
		return this._isValid;
	},
	showSummary: function() {
		if (this._messageType == "Individual") {
			return;
		}
	    if (this._validators.length == 0) {
	        return;
		}
		if ($(this._messageContainer))
			$(this._messageContainer).style.display = "none";
		if (this._isValid) {
			return;
		}
        if (typeof(this._displayMode) != "string") {
            this._displayMode = "BulletList";
        }
		var headerSep;
		var first;
		var pre;
		var post;
		var last;
        switch (this._displayMode) {
            case "List":
				if (this._messageType == "Alert") {
					headerSep = "\n";
				} else {
                	headerSep = "<br />";
				}
                first = "";
                pre = "";
				if (this._messageType == "Alert") {
					post = "\n";
				} else {
                	post = "<br />";
				}
                last = "";
                break;
            case "BulletList":
            default:
                if (this._messageType == "Alert") {
					headerSep = "\n";
					first = "";
					pre = "- ";
					post = "\n";
					last = "";
				} else {
                	headerSep = "";
					first = "<ul>";
					pre = "<li>";
					post = "</li>";
					last = "</ul>";
				}
                break;
            case "SingleParagraph":
                headerSep = " ";
                first = "";
                pre = "";
                post = " ";
				if (this._messageType == "Alert") {
                	last = "\n";
				} else {
					last = "<br />";
				}
                break;
        }
        var s = "";
        if (typeof(this._headerText) == "string" && this._headerText.length > 0) {
            s += this._headerText + headerSep;
        }
        s += first;
        for (i = 0; i < this._validators.length; i++) {
            if (!this._validators[i].isValid && typeof(this._validators[i].__errorMessage) == "string") {
                s += pre + this._validators[i].__errorMessage + post;
            }
        }
        s += last;
		if (this._messageType == "Alert") {
        	alert(s);
		} else {
			$(this._messageContainer).innerHTML = s;
			$(this._messageContainer).style.display = "";
		}
	}
}

/**
 * Class AbstractValidator.
 */
Longway.AbstractValidator = Class.create();
Longway.AbstractValidator.prototype = {
	__active: true,
	__errorMessage: "Something wrong.",
	__messageContainer: "",
	isValid: true,
	rule: null,
	controlToValidate: "",

	initialize: function(ownerRule, validatorNode) {
		if (ownerRule) {
			this.rule = ownerRule;
		}
		if (validatorNode) {
			this.controlToValidate = validatorNode.getAttribute("ctrl");
			this.__errorMessage = validatorNode.getAttribute("msg");
			this.__messageContainer = validatorNode.getAttribute("ctn");
			if (this.rule && this.rule._messageType == "Individual")
				this._hookupControlId();
			this.__specialInit(ownerRule, validatorNode);
		}
	},
	__specialInit: function(ownerRule, validatorNode) {
		// noop;
	},
	_hookupControlId: function() {
		if (typeof(this.controlToValidate) != "string") {
        	return;
    	}
    	var ctrl = $(this.controlToValidate);
    	if (typeof(ctrl) != "undefined") {
        	this._hookupControl(ctrl);
    	} else {
        	this.isValid = true;
        	this.__active = false;
    	}
	},
	_hookupControl: function(control) {
	    if (typeof(control.tagName) == "undefined" && typeof(control.length) == "number") {
	        var i;
	        for (i = 0; i < control.length; i++) {
	            var inner = control[i];
	            if (typeof(inner.value) == "string") {
	                _hookupControl(inner, val);
	            }
	        }
	        return;
	    }
	    else if (control.tagName != "INPUT" && control.tagName != "TEXTAREA" && control.tagName != "SELECT") {
	        var i;
	        for (i = 0; i < control.children.length; i++) {
	            _hookupControl(control.children[i], val);
	        }
	        return;
	    }
	    else {
	        if (typeof(control.Validators) == "undefined") {
	            control.Validators = new Array;
	            var ev;
	            if (control.type == "radio") {
	                ev = control.onclick;
	            } else {
	                ev = control.onchange;
	            }
	            if (typeof(ev) == "function" ) {
	                ev = ev.toString();
	                ev = ev.substring(ev.indexOf("{") + 1, ev.lastIndexOf("}"));
	            }
	            else {
	                ev = "";
	            }
	            var func = new Function("ValidatorOnChange(EventUtil.getEvent()); " + ev);
	            if (control.type == "radio") {
					EventUtil.addEventHandler(control, "click", func);
	            } else {
	                EventUtil.addEventHandler(control, "change", func);
	            }
	        }
	        control.Validators[control.Validators.length] = this;
	    }
	},
	validate: function() {
		this.isValid = true;
		var control = $(this.controlToValidate);
		if (!control.isValid) {
			return;
		}

    	if (this.__active != false) {
        	if (typeof(this.doValidate) == "function") {
            	this.isValid = this.doValidate();
        	}
    	}
		if (!this.isValid) {
			control.isValid = false;
		}
		if (this.rule._messageType == "Individual")
    		this._updateDisplay();
	},
	_updateDisplay: function() {
		if (typeof(this.__messageContainer) == "string") {
			var messageContainer = $(this.__messageContainer);
	        messageContainer.style.display = this.isValid ? "none" : "inline";
			if (!this.isValid) {
				messageContainer.innerHTML = this.__errorMessage;
			}
	        return;
    	}
	}
};

/**
 * Class RequiredFieldValidator.
 */
Longway.RequiredFieldValidator = Class.create();
Longway.RequiredFieldValidator.prototype = Object.extend(new Longway.AbstractValidator(), {
	doValidate: function() {
		return (Longway.Control.getValue(this.controlToValidate).trim() != "");
	}
});

/**
 * Class RangeValidator.


 */
Longway.RangeValidator = Class.create();
Longway.RangeValidator.prototype = Object.extend(new Longway.AbstractValidator(), {
	_maxValue: "",
	_minValue: "",
	_type: "",
	_splitChar: "",
	_format: "ymd",
	__specialInit: function(ownerRule, validatorNode) {
		this._maxValue = validatorNode.getAttribute("max");
		this._minValue = validatorNode.getAttribute("min");
		this._type = validatorNode.getAttribute("type");
		if (validatorNode.getAttribute("sprt")) {
			_splitChar = validatorNode.getAttribute("sprt");
		}
		if (validatorNode.getAttribute("fmt")) {
			_format = validatorNode.getAttribute("fmt");
		}
	},
	doValidate: function() {
		var value = Longway.Control.getValue(this.controlToValidate);
	    if (value.trim() == "")
	        return true;

		value = Longway.convert(value, this._type, this._splitChar);
		_maxValue = Longway.convert(this._maxValue, this._type, this._splitChar);
		_minValue = Longway.convert(this._minValue, this._type, this._splitChar);
	    return (Longway.compare(value, this._minValue, ">=") &&
	            Longway.compare(value, this._maxValue, "<="));
	}
});

/**
 * Class CompareValidator
 */
Longway.CompareValidator = Class.create();
Longway.CompareValidator.prototype = Object.extend(new Longway.AbstractValidator(), {
	_type: "",
	_controlToCompare: "",
	_valueToCompare: "",
	_operator: "",
	_splitChar: "",
	_format: "ymd",
	__specialInit: function(ownerRule, validatorNode) {
		this._type = validatorNode.getAttribute("type");
		this._controlToCompare = validatorNode.getAttribute("comp");
		this._valueToCompare = validatorNode.getAttribute("value");
		this._type = validatorNode.getAttribute("type");
		this._operator = validatorNode.getAttribute("op");
		if (validatorNode.getAttribute("sprt")) {
			_splitChar = validatorNode.getAttribute("sprt");
		}
		if (validatorNode.getAttribute("fmt")) {
			_format = validatorNode.getAttribute("fmt");
		}
	},
	doValidate: function() {
		var value = Longway.Control.getValue(this.controlToValidate);
	    if (value.trim() == "")
	        return true;

	    var compareTo = "";
	    if (null == $(this._controlToCompare)) {
	        if (typeof(this._valueToCompare) == "string") {
	            compareTo = this._valueToCompare;
	        }
	    } else {
	        compareTo = Longway.Control.getValue(this._controlToCompare);
	    }

		value = Longway.convert(value, this._type, this._splitChar, this._format);
		compareTo = Longway.convert(compareTo, this._type, this._splitChar, this._format);
	    return Longway.compare(value, compareTo, this._operator);
	}
});

/**
 * Class RegularExpressionValidator
 */
Longway.RegularExpressionValidator = Class.create();
Longway.RegularExpressionValidator.prototype = Object.extend(new Longway.AbstractValidator(), {
	_validationExpression: "",
	__specialInit: function(ownerRule, validatorNode) {
		this._validationExpression = validatorNode.getAttribute("regex");
	},
	doValidate: function() {
		var value = Longway.Control.getValue(this.controlToValidate);
	    if (value.trim() == "")
	        return true;
	    var rx = new RegExp(this._validationExpression);
	    var matches = rx.exec(value);
	    return (matches != null && value == matches[0]);
	}
});

/**
 * CustomValidator, validate the control with the method provied by user.
 * @extends {AbstractValidator}
 */
Longway.CustomValidator = Class.create();
Longway.CustomValidator.prototype = Object.extend(new Longway.AbstractValidator(), {
	_clientValidationFunction: "",
	__specialInit: function(ownerRule, validatorNode) {
		this._clientValidationFunction = validatorNode.getAttribute("func");
	},
	doValidate: function() {
		var value = "";
	    if (typeof(this.controlToValidate) == "string") {
	        value = Longway.Control.getValue(this.controlToValidate);
	        if (value.trim() == "")
	            return true;
	    }
	    var args = { value: value, isValid: true };
	    if (typeof(this._clientValidationFunction) == "string") {
	        eval(this._clientValidationFunction + "(this, args) ;");
	    }
	    return args.isValid;
	}
});

function ValidatorOnChange(oEvent) {
	oEvent.target.isValid = true;
    var vals = oEvent.target.Validators;
    for (var i = 0; i < vals.length; i++) {
        vals[i].validate();
		if (!vals[i].isValid) {
			break;
		}
    }
	if (vals.length > 0) {
		vals[0].rule.updateIsValid();
	}
}
Longway.ValidationRuleFactory = {
	/**
	 * @type {String} Gets or sets the path of validation rules configuration file.
	 */
	_defaultConfigFiles: ["validation-config.xml"],

	_validationConfigFiles: ["/js/admin/articleNew/validation.xml","/js/admin/template/validation.xml","/js/admin/pageTemplate/validation.xml","/js/admin/administrator/validation.xml","/js/admin/circle/funCategory/validation.xml" ,"/js/admin/notice/validation.xml","/js/admin/dictionary/validation.xml","/js/admin/dictionaryValue/validation.xml","/js/user/validation.xml","/js/admin/message/validation.xml"],
	_validationCache: [],


	_getDocumentElements: function() {
		if (this._validationCache.length > 0) {
			return this._validationCache;
		}

		var files;
		if (this._validationConfigFiles.length > 0) {
			files = this._validationConfigFiles;
		} else {
			files = this._defaultConfigFiles;
		}


		for (var i = 0; i < files.length; i++) {
			var xmlDoc = Longway.XmlDocument.getInstance(false);
			xmlDoc.load(files[i]);
			if (xmlDoc.documentElement == null) {
				throw new Error("Cannot load " + files[i]);
			}
			// TODO: parse the document if it's a valid document.
			this._validationCache[i] = xmlDoc.documentElement;
		}
		return this._validationCache;
	},

	_pickupValidatorRule: function(doms, ruleId) {
		for (var i = 0; i < doms.length; i++) {
			var dom = doms[i];
			var ruleElements = dom.getElementsByTagName("rule");
			var ruleElement;
			for (var j = 0; j < ruleElements.length; j++) {
				if (ruleElements[j].getAttribute("id") == ruleId) {
					ruleElement = ruleElements[j];
					break;
				}
			}
			if (ruleElement && ruleElement.hasChildNodes()) {
				return new Longway.ValidationRule(ruleElement);
			}
		}
		return null;
	},

	initValidatorRule:	function(ruleId) {
		var doms = this._getDocumentElements();
		var cc = $(ruleId);
		cc.ValidationRule = this._pickupValidatorRule(doms, ruleId);
	},
	validate: function(ruleId) {
		var cc = $(ruleId);
		
		if (typeof cc.ValidationRule == "undefined") {
			return true;
		}
		if (cc.ValidationRule == null) {
			return true;
		}
	    return cc.ValidationRule.validate();
	}
}