/**================================================ 
 * InspectForm version 1.0 (for Prototype version 1.6 and above)
 * Created: 11 December 2008
 * Created by: Zaenal - http://www.lokamaya.net
 * ================================================
 * Copyright (c) 2008 Zaenal - http://www.lokamaya.net 
 * Some rights reserved. This work is licensed under a Creative Commons Attribution-Noncommercial 3.0 Unported License.
 * ================================================
 * Demo: http://code.lokamaya.net/inpectform/
 * Blog: http://blog.lokamaya.net
 * ================================================
**/

var inspectForm = Class.create({
        forms:null,
        successElem: null,
        formsButton: $A(),
        error:false,
        errorText:{},
        hashElement: new Hash(),
        inspectMethod: {},
        initialize:  function(forms, successElem) {
            var that = this;
            this.forms = $(forms);
            if (successElem)
                this.successElem = $(successElem);
            if (this.successElem)
                (this.successElem).hide();

            this.forms.observe('submit', function(evt) {
                    that.inspect(evt);
            });
            this.resetObj = that.resetElement.bindAsEventListener(that);
            
            this.inspectMethod = inspectMethod;
            this.inspectMethod.field = {};
            this.inspectMethod.fieldAlternate = [];
            this.inspectMethod.trim = this.trim;
            this.inspectMethod.empty = this.empty;
        },
        register: function(elem, param) {
            var that = this;
            var found = true;
            if (!($(elem))) {
                if (this.forms[elem]) {
                    if ($(this.forms[elem]).id) {
                        elem = $(this.forms[elem]).id;
                    } else if (this.forms[elem].name) {
                        $(this.forms[elem]).id = elem;
                    } else {
                        found = false;
                    }
                }
                if (!found) {
                    return alert('Element "'+elem+'" not found');
                }
            }
            this.hashElement.set(elem,param);
            $(elem).observe('change', that.resetObj);
        },
        registerMethod: function(method, callback) {
            this.inspectMethod[method] = callback;
        },
        registerResetButton: function(elem) {
            var that = this;
            $(elem).observe('click', function(evt) {
                    that.error = false;
                    if (that.successElem) {
                        (that.successElem).update('');
                        (that.successElem).hide();
                    }
                    that.forms.reset();
                    that.errorCallbackResetAll();
                    Event.stop(evt);
            });
        },
        inspect: function(evt) {
            var that = this;
            this.error = false;
            this.errorCallbackResetAll();
            
            this.hashElement.each(function(elem){
                    that.inspectElement(elem);
            });
            if (this.error) {
                this.forms.submit = false;
                Event.stop(evt);
                return false;
            } else {
                this.submit();
                Event.stop(evt);
                return true;
            }
        },
        inspectElement: function(elem) {
            if (!($(elem.key).visible()) || $(elem.key).type=='hidden') return;
            var that = this;
            var valid = true;
            var param = false;
            
            var field = {}; field.id = elem.key; field.value = this.getValue(elem.key); 
            this.inspectMethod.field = field;
            this.inspectMethod.errorText = null;
            
            
            $H(elem.value).each(function(item){
                    this.inspectMethod.fieldAlternate = [];
                    if (valid) {
                        if (that.is_array(item.value)) param = item.value[0];
                        else param = item.value;

                        try {that.inspectMethod[item.key](param);}catch(e){alert(elem.key+': method "'+item.key+'" does not exist'); return;}
                        if (that.inspectMethod.errorText!==null) {
                            valid = false;
                            if (that.is_array(item.value) && item.value[1]) 
                                that.inspectMethod.errorText = item.value[1];
                                
                            if (that.is_array(that.inspectMethod.fieldAlternate) && (that.inspectMethod.fieldAlternate).length > 0) {
                                $(elem.key).addClassName('dependElementTo'+this.inspectMethod.field.id);
                                $(elem.key).addClassName('dependElement');
                                that.errorCallback(elem.key, that.inspectMethod.errorText, (that.error == false));
                                $A(that.inspectMethod.fieldAlternate).each(function(alternate) {
                                    $(alternate).stopObserving('change', that.resetObj);
                                    $(alternate).observe('change', that.resetObj);
                                    $(alternate).addClassName('dependElementTo'+that.inspectMethod.field.id);
                                    $(alternate).addClassName('dependElement');
                                    that.errorCallback(alternate, false);
                                });
                            } else {
                                that.errorCallback(elem.key, that.inspectMethod.errorText, false);
                            }

                            that.error = true;
                            throw $break;
                        }
                    }
            });
        },
        successCallback: function(msg, error) {
            if (this.successElem) {
                (this.successElem).update(msg);
                var that = this;
                $A((this.successElem).select('[href="#back"]')).each(function(item) {
                    that.registerResetButton(item);
                });
                (this.successElem).show();
            } else {
                alert(msg);
            }
            if (!error) (this.forms).addClassName("successForm");
        },
        resetElement: function(event) {
            var cleanup = Event.element(event);
            if ($(cleanup).hasClassName('dependElement')) {
                var that = this;
                $(cleanup).readAttribute('class').scan("[a-zA-Z0-9\\-_]+", function(cls) {
                    if ((cls+'').startsWith('dependElementTo')) {
                        $A((that.forms).select('*.'+cls)).each(function(cleanup) {
                            if (cleanup.hasClassName(cls+'')) {
                                cleanup.removeClassName('dependElement');
                                cleanup.removeClassName(cls+'');
                                that.errorCallbackReset(cleanup);
                            }
                        });
                        return;
                    }
                });
            } else {
                this.errorCallbackReset(cleanup);
            }
        },
        errorCallback: function(elem, msg, focused) {
            var that = this;
            $(elem).addClassName('errorElement');
            if (msg) $(elem).up().appendChild(new Element('label', {'for':'error-'+elem, 'class':'errorLabel'}).update(msg));
            if (focused===true) $(elem).focus();
        },
        errorCallbackReset: function(elem) {
            //alert($(elem).id);
            $(elem).removeClassName('errorElement');
            if ($(elem).up().down('label.errorLabel')) {
                $(elem).up().down('label.errorLabel').remove();
            }
        },
        errorCallbackResetAll: function() {
            var that = this;
            this.forms.getElements().each(function(elem) {
                    that.errorCallbackReset(elem);
            });
            (this.forms).removeClassName("successForm");
            if (this.successElem) {
                (this.successElem).update('');
                (this.successElem).hide();
            }
        },
        submit: function() {
            var that = this;
            (this.forms).request({
                onSuccess: function(transport) {
                        var docRoot = null;
                        var fields = [];
                        var error = false;
                        try {docRoot = transport.responseXML.documentElement;} catch(e){alert(e); return;};
                        try {fields = (docRoot.getElementsByTagName('messageError')[0]).getElementsByTagName('fields');} catch(e){fields=[]}
                        $A(fields).each(function(field, i) {
                            that.errorCallback(field.getAttribute('id'), that.getNodeValue(field), (i==0));
                            error = true;
                        });
                        if (docRoot.getElementsByTagName('message')) {
                            that.successCallback(that.getNodeValue(docRoot.getElementsByTagName('message')[0]), error);
                        }
                },
                onFailure: function() {
                    alert('Server down, please try to submit this form later!');
                }
            });
        },
        getNodeValue: function (oNode) {
            if (oNode.nodeValue)
                return oNode.nodeValue;
            else if (oNode.firstChild)
                return this.getNodeValue(oNode.firstChild);
            else
                return '';
        },
        is_array: function(mixed_var) {
            if (!mixed_var || mixed_var === null || mixed_var === false || mixed_var === true) return false;
            return (mixed_var.constructor.toString().indexOf("Array") != -1 || mixed_var instanceof Array || mixed_var instanceof Object);
        },
        getValue: function(elem) {
            return $(elem).getValue();
        },
        trim: function(str) {
            return str.strip();
        },
        empty: function() {
            return ((this.trim(this.field.value)).length == 0);
        }
});

var inspectMethod = {
    field: {id:null,value:''},
    fieldAlternate: [],
    errorText: null,
    required:  function(param){
        if (this.empty()) {
            this.errorText = 'Required field';
        }
        return (this.errorText == null);
    },
    confirm: function(param) {
        if(this.field.value != Form.Element.getValue(param)) {
            this.errorText = 'Confirm value not macth'; 
            this.fieldAlternate = [param];
        }
        return (this.errorText == null);
    },
    alternate: function(param) {
        if((this.field.value).length==0) {
            var found = false;
            $A(param.split('|')).each(function(item) {
                if (($(item).getValue()).length > 0) {
                    if (!found) found = true;
                }
            });
            if (!found) {
                this.errorText = 'One of these fields must not empty';
                this.fieldAlternate = param.split('|');
            }
        };
        return (this.errorText == null);
    },
    radio: function(param) {
        if($(this.field.id).checked==false) {
            var found = false;
            $A(param.split('|')).each(function(item) {
                if ($(item).checked==true) {
                    if (!found) found = true;
                }
            });
            if (!found) {
                this.errorText = 'One of these fields must be checked';
                this.fieldAlternate = param.split('|');
            }
        };
        return (this.errorText == null);
    },
    checkbox: function(param) {
        if ($(this.field.id).checked==false) {
            this.errorText = 'Must be checked';
        }
        return (this.errorText == null);
    },
    not: function(param) {
        if (this.field.value==param) {
            this.errorText = 'Value "'+param+'" is not allowed';
        }
        return (this.errorText == null);
	},
    maxlength: function(param) {
        if (this.empty()) return;
        if (parseInt(param) > 0 && this.field.value.length > parseInt(param)) {
            this.errorText = 'Maximum ' + parseInt(param) + ' characters';
        }
        return (this.errorText == null);
    },
    minlength: function(param) {
        if (this.empty() || (parseInt(param) > 0 && this.field.value.length < parseInt(param))){
            this.errorText = 'Minimum ' + parseInt(param) + ' characters';
        }
        return (this.errorText == null);
    },
    decimal: function(param) {
        if (this.empty()) return;
        if((this.field.value).search("[^0-9\.,]") > 0 || (this.field.value).search("^(\.|,)")) { 
            this.errorText = 'Only decimal allowed'; 
        }
        return (this.errorText == null);
    },
    numeric: function(param) {
        if (this.empty()) return;
        if(!this.strsrc("[^0-9]")) { 
            this.errorText = 'Only integer number allowed'; 
        }
        return (this.errorText == null);
    },
    alphabet: function(param) {
        if (this.empty()) return;
        if(!this.strsrc("[^A-Za-z]")) { 
            this.errorText = 'Only alphabet characters are allowed'; 
        }
        return (this.errorText == null);
    },
    alphanumeric: function(param) {
        if (this.empty()) return;
        if(!this.strsrc("[^A-Za-z0-9]")) {
            this.errorText = 'Only alphabet and number characters are allowed'; 
        }
        return (this.errorText == null);
    },
    lessthan: function(param) {
        if (this.empty()) return;
        if (isNaN(this.field.value)){
            this.errorText = 'Only number allowed';
        } else if (parseFloat(this.field.value) >  parseFloat(param)){
            this.errorText = 'Value greater than ' + parseFloat(param);
        }
        return (this.errorText == null);
    },
    greaterthan: function(param) {
        if (this.empty()) return;
        if (isNaN(this.field.value)){
            this.errorText = 'Only number allowed';
        } else if (parseFloat(this.field.value) <  parseFloat(param)) {
            this.errorText = 'Value less than ' + parseFloat(param);
        }
        return (this.errorText == null);
    },
    email: function(param) {
        if (this.empty()) return;
        if(!(new RegExp("^([\\w-]+(?:\\.[\\w-]+)*)@((?:[\\w-]+\\.)*\\w[\\w-]{0,66})\\.([a-z]{2,6}(?:\\.[a-z]{2})?)$", "i")).test(this.trim(this.field.value))) { 
            this.errorText = 'Not valid email address';
        }
        return (this.errorText == null);
    },
    website: function(param) {
        if (this.empty()) return;
        if(!(new RegExp("^(?:https?):\\/\\/(?:(?:[\\w]+:)?\\w+@)?(?:(?:(?:[\\w-]+\\.)*\\w[\\w-]{0,66}\\.(?:[a-z]{2,6})(?:\\.[a-z]{2})?)|(?:(?:25[0-5]\\.|2[0-4][0-9]\\.|1[0-9]{2}\\.|[0-9]{1,2}\\.)(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\\.){2}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})))(?:\\:\\d{1,5})?(?:\\/(~[\\w-_.])?)?(?:(?:\\/[\\w-_]*)*)?$", "i")).test(this.trim(this.field.value))) { 
            this.errorText = 'Not valid website';
        }
        return (this.errorText == null);
    },
    url: function(param) {
        if (this.empty()) return;
        if(!(new RegExp("^(?:https?):\\/\\/(?:(?:[\\w]+:)?\\w+@)?(?:(?:(?:[\\w-]+\\.)*\\w[\\w-]{0,66}\\.(?:[a-z]{2,6})(?:\\.[a-z]{2})?)|(?:(?:25[0-5]\\.|2[0-4][0-9]\\.|1[0-9]{2}\\.|[0-9]{1,2}\\.)(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\\.){2}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})))(?:\\:\\d{1,5})?(?:\\/(~[\\w-_.])?)?(?:(?:\\/[\\w-_.#]*)*)?(\\?)?(?:(?:[\\w-_.#]+\\=[\\w-_.#]+&?)*)?$", "i")).test(this.trim(this.field.value))) { 
            this.errorText = 'Not valid url address';
        }
        return (this.errorText == null);
    },
    file: function(param) {
        if (this.empty() || !param) return;
        if (!(new RegExp("\.("+param+")$", "i")).test(this.field.value)) {
                this.errorText = 'File extension not allowed';
        }
        return (this.errorText == null);
    },
    date: function(param) {
        if (this.empty()) return;
        param = param.toLowerCase();
        var oRegex = ((((((param.replace(new RegExp('\\W+', "gi"), "##")).replace('dd', "(0[1-9]|1[0-9]|2[0-9]|3[0-1])")).replace('d', "([1-9]|1[0-9]|2[0-9]|3[0-1])")).replace('mm', "(0[1-9]|1[0-2])")).replace('m', "([1-9]|1[0-2])")).replace('yyyy', "([0-9]{4})")).replace('yy', "([0-9]{2})");
        var oValue = (this.field.value).replace(new RegExp('\\W+', "gi"), "##");
        if (!(new RegExp("^"+oRegex+"$")).match(oValue)) {
            this.errorText = 'Invalid date format';
        }
        return (this.errorText == null);
    },
    regexp: function(param) {
        if (this.empty()) return;
        if(!(param).test(this.field.value)) {
            this.errorText = 'Error regex';
        }
        return (this.errorText == null);
    },
    strsrc: function(param) {
        if (this.empty()) return;
        if((this.field.value).search(param) > 0) { 
            this.errorText = 'Error: characters found'; 
        }
        return (this.errorText == null);
    }
};