
generic.forms = {
    init: function() {
        generic.forms.form.init();
        generic.forms.input.init();
        generic.forms.select.init();
        
    }   
} 

generic.forms.form = {
    init: function() { 
        $$("FORM.noSubmit").each(function($_) { 
            $_.observe("submit", function(e) {
                Event.stop(e);
            }) 
        })
    } 
}

generic.forms.input = {
    init: function() { 
        $$("INPUT.inline-label-field").each(function($_) { 
            var fieldlabel = new generic.forms.inlineLabelField({ field: $_ });         
        })
/* deprecated: use generic.forms.inlineFieldLabel instead
        $$("INPUT.clearField").each(function($_) { 
            $_.observe("focus", function() {
                this.value = "";    
            })
        })
*/
    }
}

generic.forms.select = {
    init: function() {
        $$("SELECT").each(function($_) {
            if ($_.hasClassName("dropDownSelect")) {
                
                 $_.dropDownSelect = new generic.forms.DropDownSelect($_);
            }
            else {
                $_.addOption = generic.forms.select.addOption;
                $_.onChangeCallback = generic.forms.select.onChangeCallBack;
                $_.setValue = generic.forms.select.setValue;
            }
        })
    },
    addOption:  function(args) {
         if (!args) return;
         var val = args.value;
         var label = args.label || val;
         var opt = '<option value="' + val + '">' + label + '</option>';
         this.insert(opt);
    },
    setValue: function(val) { 
        var idx = 0;
        for (var i=0;i<this.options.length;i++) { 
            if (val == this.options[i].value) {
                idx = i;
                break;
            }
        } 
        this.options.selectedIndex = idx;    
    },
    onChangeCallBack:  function(func) {
        this.observe("change", func.bind(this));
    }
    
}

//validation
generic.forms.isEmailAddress = function(s) {
    var filter  = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
    return (filter.test(s)); 
}

//utilities
generic.forms.limitTextLength = function(obj) {
    var field = obj.field;
    var max_length = obj.max_length;
    var display_element = obj.display_element;
    
    if (field.value.length > max_length) {
        field.value = field.value.slice(0, max_length);
    }   
    if (!display_element) return;
    
    var remaining_chars = max_length - field.value.length; 
    if (remaining_chars < max_length) {
        (remaining_chars != 1) ? plural = 's' : plural = '';
        display_element.innerHTML =  remaining_chars + ' character' + plural + ' left.';
    } else {
        display_element.innerHTML = '';
    }
}


generic.forms.inlineLabelField = Class.create({
    // summary:
    //      Handles toggling of label display for fields that show label
    //      in the field itself (via value attribute)

    // field: DOM node
    field: null,
    
    // fieldPswdDisplay: DOM node
    // field which displays password label
    // (workaround for IE's inability to toggle field's "type" attribute)
    fieldPswdDisplay: null,
    
    // label: String
    // field label
    label: "",
    
    // ftype: String
    // field type attribute (text, password)
    ftype: "text",
    
    // hasValue: Boolean
    // flag is true when user has entered a value in field
    hasValue: false,

    // _hasMaxlengthDisplay: Boolean
    // item's label char count exceeds field maxlength property
    _hasMaxlengthDisplay: false,
    
    // _maxlength: Object
    // if hasMaxlengthDisplay, _maxlength.data = maxlength property of input data, _maxlength.display = maxlength count for label
    _maxlength: null,
    
    initialize: function(/* Object */args) {
        this.field = args.field;
        
        // optional label arg
        if (args.label) {
            this.label = args.label;
        } else if (this.field.title && this.field.title.length > 0) {
            this.label = this.field.title;
        } else {
            this.label = "";
        }
        
        var displayField = this.field;
        this.ftype = this.field.getAttribute("type");
        
        if (this.ftype === "password") {
            this.fieldPswdDisplay = $(this.field.id+".label");
            if (!this.fieldPswdDisplay) { return };
            displayField = this.fieldPswdDisplay;
        }
        
        // check for maxlength restrictions
        var flength = this.field.maxLength;
        var labelLength = this.label.length;
        if ((flength > 0) && (flength < labelLength)) {
            this._maxlength = {};
            this._hasMaxlengthDisplay = true;
            this._maxlength.data = flength;
            this._maxlength.display = labelLength;
        }
        var self = this;
        this.handlers = [
            displayField.observe("focus", self._onFocus.bind(self)),
            this.field.observe("blur",self._onBlur.bind(self))
        ];
        
        this.setLabelState();
    },

    setLabelState: function() {
        this.checkHasValue();
        // only clear field if there is no user-set value in field
        if (!this.hasValue) {
            this.showLabel();
        }
    },

    checkHasValue: function() {
        var val = this.field.value;
        // field has user-set value
        if ((val.length > 0) && (val !== " ") && (val !== this.label)) {
            this.hasValue = true;
        } else {
            this.hasValue = false;
        }
    },
    
    beforeSubmit: function() {
        // if value is still populated by label rather than user input, clear it out before form submission
        if (!this.hasValue) {
            this.checkHasValue();
            if (!this.hasValue) {
                this.field.value = "";
            }
        }
    },
    
    _onFocus: function() {
        // display label if there is no user-set value in field
        if (!this.hasValue) {
            this.clearField();
        }
    },
    
    _onBlur: function() {
        this.setLabelState();
    },
    
    showLabel: function() {
        if (this.ftype === "password") {
            this.field.style.display = "none";
            this.fieldPswdDisplay.style.display = "block";
        } else {
            // set maxlength to accomodate label
            if (this._hasMaxlengthDisplay) {
                this.field.maxLength = this._maxlength.display;
            }
            // show label as value
            this.field.value = this.label;
        }
    },
    
    clearField: function() {
        if (this.ftype === "password") {
            this.field.style.display = "block";
            this.field.focus(); // set focus before currently focused field is hidden to avoid FF cursor disappearance
            this.fieldPswdDisplay.style.display = "none";
        } else {
            // clear field
            this.field.value = "";
            // set maxlength for expected data
            if (this._hasMaxlengthDisplay) {
                this.field.maxLength = this._maxlength.data;
            }
        }
    }
});

generic.forms.contextualOptions = Class.create( { 
    srcSelect: null,
    targetSelect: null, 
    valueKey: null,
    labelKey: null, 
    targetSelectOptions: {}, 
    _selectsAreWidgets: false,
    
    initialize: function(/* Object */args) {
        srcSelect = $(args.srcSelectId);
        targetSelect = $(args.targetSelectId);
        if (srcSelect && targetSelect) {
            this.srcSelect.observe("change", this.onChange.bind(this));
        } else {
                console.log("generic.form.contextualOptions: select element not found");
                return false;
        } 
        this.srcSelect = srcSelect;
        this.targetSelect = targetSelect;
        this.targetSelectOptions = args.targetSelectOptions; 
        if (args.valueKey) { this.valueKey = args.valueKey; }
        if (args.labelKey) { this.labelKey = args.labelKey; }
    },
    
    onChange: function() {
        var srcval = this.srcSelect.value;
        var targetSelect = this.targetSelect;
        if (srcval === "" || srcval === null) { return false; } 
        this.removeOptions(targetSelect);
        this.addOptions(targetSelect);
        targetSelect.updateIndices();
    },
    
    getNewOptions: function() {
        // summary:
        // 
        // Defines default path to list of new options
        // Redefined to pass list from different array/object heirarchy
        var selected = this.srcSelect.value;
        return this.targetSelectOptions[selected];
    },

    addOptions: function(targetSelect) {
        var isWidget = this._selectsAreWidgets;
        var options = this.getNewOptions();
        
        // expect value, label pair stored in data, either as object (key id'ed) or in array (first 2 positions)        
        var labelKey = (this.labelKey ? this.labelKey : 0);
        var valueKey = (this.valueKey ? this.valueKey : 1);

        options.each(function(option, ix) {
            targetSelect.options[ix] = new Option(option[labelKey], option[valueKey]); 
        });

    },
    
    removeOptions: function(targetSelect) {
        var l = (targetSelect.options.length - 1);
        var i;
        for (i = l; i >= 0; i--) {
            if (targetSelect.options[i].value !== "") {
                targetSelect.options[i] = null;         
            }
            
        }    
    }
    
});