All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springmodules.validation.valang.javascript.valang_codebase.js Maven / Gradle / Ivy

There is a newer version: 0.8a
Show newest version
/*
 * Copyright 2004-2005 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author Oliver Hutchison
 */

//var globalErrorsBoxId = (deglobalErrorsId) ? globalErrorsId : 'global_errors';
var globalErrorsId = 'global_errors';

//var fieldErrorBoxIdSuffix = (fieldErrorIdSuffix) ? fieldErrorIdSuffix : '_error';
var fieldErrorIdSuffix = '_error';


if (!Array.prototype.push) {
    // Based on code from http://prototype.conio.net/
    Array.prototype.push = function() {
        var startLength = this.length
        for (var i = 0; i < arguments.length; i++) {
            this[startLength + i] = arguments[i]
        }
        return this.length
    }
}
if (!Function.prototype.apply) {
    // Based on code from http://prototype.conio.net/
    Function.prototype.apply = function(object, parameters) {
        var parameterStrings = new Array()
        if (!object) {
            object = window
        }
        if (!parameters) {
            parameters = new Array()
        }
        for (var i = 0; i < parameters.length; i++) {
            parameterStrings[i] = 'parameters[' + i + ']'
        }
        object.__apply__ = this
        var result = eval('object.__apply__(' + parameterStrings.join(', ') + ')')
        object.__apply__ = null
        return result
    }
}

/*
 * Core validation object.
 */
var ValangValidator = function(name, installSelfWithForm, rules) {
    this.name = name
    this.rules = rules
    this.form = this._findForm(name)
    if (installSelfWithForm) {
        this._installSelfWithForm()
    }
}
ValangValidator.prototype = {
    validate: function() {
        return this._validateAndReturnFailedRules().length > 0
    },
    validateAndShowFeedback: function() {
        var failedRules = this._validateAndReturnFailedRules()
        if (failedRules.length > 0) {
            this.showValidationFeedback(failedRules)
        }
        return failedRules.length === 0
    },
    showValidationFeedback: function(failedRules) {

        // first putting all the valiation error in field propriatery error place
        // holders, basically elements with id _error.
        var globalRules = new Array();
        for (var i = 0; i < failedRules.length; i++) {
            var errorBoxId = failedRules[i].field + fieldErrorIdSuffix;
            var errorBox = document.getElementById(errorBoxId);
            if (errorBox != null) {
                errorBox.innerHTML = failedRules[i].getErrorMessage();
            } else {
                globalRules.push(failedRules[i]);
            }
        }

        // all those errors that weren't put in a field propriatery error place holders
        // will be grouped together as global errors, either in a global error place holder
        // (element with id 'global_errors') or will be shown in an alert.
        var globalErrorsBox = document.getElementById(globalErrorsId);
        if (globalErrorsBox != null) {
            var ul = document.createElement('ul');
            for (var i = 0; i < globalRules.length; i++) {
                var li = document.createElement('li');
                li.innerHTML = globalRules[i].getErrorMessage();
                ul.appendChild(li);
            }
            globalErrorsBox.appendChild(ul);
        } else {
            var errors = ''
            for (var i = 0; i < globalRules.length; i++) {
                errors = errors + globalRules[i].getErrorMessage() + '\n'
            }
            // The following line is sometimes effected by Firefox Bug 236791. Please just ignore
            // the error or tell me how to fix it?
            // https://bugzilla.mozilla.org/show_bug.cgi?id=236791
            alert(errors)
        }

        var fields = this.form.getFieldsWithName(failedRules[0].field)
        if (fields.length > 0) {
            fields[0].activate()
        }
    },
    _findForm: function(name) {
        var element = document.getElementById(name)
        if (!element || element.tagName.toLowerCase() != 'form') {
            element = document.getElementById(name + 'ValangValidator')
        }
        if (!element || element.tagName.toLowerCase() != 'script') {
            throw 'unable to find form with ID \'' + name + '\' or script element with ID \'' + name + 'ValangValidator\''
        }
        var foundElement = element
        while (element && element.tagName.toLowerCase() != 'form') {
            element = element.parentNode
        }
        if (!element) {
            throw 'unable to find FORM element enclosing element with ID \'' + foundElement.id + '\''
        }
        return new ValangValidator.Form(element)
    },
    _installSelfWithForm: function() {
        var oldOnload = window.onload
        var oldOnsubmit = this.form.formElement.onsubmit
        var thisValidator = this
        // delay install until the page is 
        // fully loaded so that we can be 
        // (fairly) sure of being the last 
        // thing that tries to handle the
        // onload event
        window.onload = function() {
            ValangValidator.Logger.log('Installing ValangValidator \'' + thisValidator.name + '\' as onsubmit handler')
            try {
                if (oldOnload) {
                    oldOnload()
                }
            } finally {
                thisValidator.form.formElement.onsubmit = function() {
                    if (!oldOnsubmit || oldOnsubmit()) {
                        return thisValidator.validateAndShowFeedback()
                    }
                }
            }
        }
    },
    _validateAndReturnFailedRules: function() {
        this._clearGlobalErrors();
        ValangValidator.Logger.push('Starting validation')
        var failedRules = new Array()
        for (var i = 0; i < this.rules.length; i++) {
            var rule = this.rules[i]
            this._clearErrorIfExists(rule.field);
            //try {
            ValangValidator.Logger.push('Evaluating rule for field [' + rule.field + ']')
            rule.form = this.form
            if (!rule.validate()) {
                ValangValidator.Logger.pop('Failed')
                failedRules.push(rule)
            } else {
                ValangValidator.Logger.pop('Passed')
            }
            //} catch(ex) {
            //    ValangValidator.Logger.pop('Exception evaluating rule [' + ex + ']')
            //}
        }

        ValangValidator.Logger.pop('Finshed - ' + failedRules.length + ' failed rules')
        return this._giveRulesSameOrderAsFormFields(failedRules)
    },
    _clearErrorIfExists: function(field) {
        var errorBox = document.getElementById(field + fieldErrorIdSuffix);
        if (errorBox != null) {
            errorBox.innerHTML = '';
        }
    },
    _clearGlobalErrors: function() {
        var errorBox = document.getElementById(globalErrorsId);
        if (errorBox != null) {
            errorBox.innerHTML = '';
        }
    },
    _giveRulesSameOrderAsFormFields: function(failedRules) {
        var sortedFailedRules = new Array()
        var fields = this.form.getFields()
        for (var i = 0; i < fields.length; i++) {
            var fieldName = fields[i].name
            for (var j = 0; j < failedRules.length; j++) {
                if (failedRules[j] && failedRules[j].field == fieldName) {
                    sortedFailedRules.push(failedRules[j])
                    failedRules[j] = null
                }
            }
        }
        for (var i = 0; i < failedRules.length; i++) {
            if (failedRules[i]) {
                sortedFailedRules.push(failedRules[i])
            }
        }
        return sortedFailedRules
    }
}

/*
 * Simple static logger implementation; by default attempts to log output
 * into a div with ID = 'valangLogDiv'.
 * If you wish to provide an alternative location for the log output you 
 * must overide the ValangValidator.Logger.log function.
 */
ValangValidator.Logger = {
    log: function(msg) {
        var logDiv = document.getElementById('valangLogDiv')
        if (logDiv) {
            var oldHtml = logDiv.innerHTML
            logDiv.innerHTML = this._indentString(' ') + msg + (oldHtml.length > 0 ? '
' + oldHtml : '') } }, push: function(msg) { this.log(msg) this._indent++ }, pop: function(msg) { this._indent-- this.log(msg) }, logFunctionCalls: function(object) { for (var elementName in object) { var theElement = object[elementName] if (typeof theElement == 'function') { object[elementName] = this._wrapFunctionCallWithLog(elementName, theElement) } } }, _indent: 0, _indentString: function(filler) { var result = '' for (var i = 0; i < this._indent * 5; i++) { result += filler } return result }, _wrapFunctionCallWithLog: function(functionName, theFunction) { return function() { ValangValidator.Logger.push('calling ' + functionName + '(' + arguments[0] + ', ' + arguments[1] + ')') try { var result = theFunction.apply(this, arguments) } catch(ex) { ValangValidator.Logger.pop('threw ' + ex) throw ex } ValangValidator.Logger.pop('result = ' + result) return result } } } /* * Encapsulates a HTML form * * Based on code from http://prototype.conio.net/ */ ValangValidator.Form = function(formElement) { this.formElement = formElement } ValangValidator.Form.prototype = { getValue: function(fieldName) { var fields = this.getFieldsWithName(fieldName) var value = new Array() for (var i = 0; i < fields.length; i++) { if (fields[i].getValue()) { value.push(fields[i].getValue()) } } if (value.length == 1) { return value[0] } else if (value.length > 1) { return value } }, getFieldsWithName: function(fieldName) { var matchingFields = new Array() var fields = this.getFields() for (var i = 0; i < fields.length; i++) { var field = fields[i] if (field.name == fieldName) { matchingFields.push(field) } } return matchingFields }, getFields: function() { var fields = new Array() var tagElements = this.formElement.elements for (var i = 0; i < tagElements.length; i++) { fields.push(new ValangValidator.Field(tagElements[i])) } return fields }, disable: function() { var fields = this.getFields() for (var i = 0; i < fields.length; i++) { fields[i].disable() } }, enable: function() { var fields = this.getFields() for (var i = 0; i < fields.length; i++) { fields[i].enable() } }, focusFirstElement: function(form) { var fields = this.getFields() for (var i = 0; i < fields.length; i++) { var field = fields[i] if (field.type != 'hidden' && !field.isDisabled()) { field.activate() break; } } } } /* * Encapsulates a HTML form field * * Based on code from http://prototype.conio.net/ */ ValangValidator.Field = function(fieldElement) { this.id = fieldElement.id this.name = fieldElement.name this.type = fieldElement.type.toLowerCase() this.tagName = fieldElement.tagName.toLowerCase() this.fieldElement = fieldElement if (ValangValidator.Field.ValueGetters[this.tagName]) { this.getValue = ValangValidator.Field.ValueGetters[this.tagName] } else if (this.tagName == 'input') { switch (this.type) { case 'submit': case 'hidden': case 'password': case 'text': this.getValue = ValangValidator.Field.ValueGetters['textarea'] break case 'checkbox': case 'radio': this.getValue = ValangValidator.Field.ValueGetters['inputSelector'] break default: throw 'unexpected input field type \'' + this.type + '\'' } } else { throw 'unexpected form field tag name \'' + this.tagName + '\'' } } ValangValidator.Field.prototype = { clear: function() { this.fieldElement.value = '' }, focus: function() { // The following line is sometimes effected by Firefox Bug 236791. Please just ignore // the error or tell me how to fix it? // https://bugzilla.mozilla.org/show_bug.cgi?id=236791 this.fieldElement.focus() }, select: function() { if (this.fieldElement.select) { this.fieldElement.select() } }, activate: function() { this.focus() this.select() }, isDisabled : function() { return element.disabled }, disable: function() { element.blur() element.disabled = 'true' }, enable: function() { element.disabled = '' } } ValangValidator.Field.ValueGetters = { inputSelector: function() { if (this.fieldElement.checked) { return this.fieldElement.value } }, textarea: function() { return this.fieldElement.value }, select: function() { var value = '' if (this.fieldElement.type == 'select-one') { var index = this.fieldElement.selectedIndex if (index >= 0) { value = this.fieldElement.options[index].value } } else { value = new Array() for (var i = 0; i < element.length; i++) { var option = this.fieldElement.options[i] if (option.selected) { value.push(option.value) } } } return value } } /* * Represents a single valang validation rule and the functions needed * to evaluate that rule. */ ValangValidator.Rule = function(field, valang, errorMessage, validationFunction) { this.field = field this.valang = valang this.errorMessage = errorMessage this.validate = validationFunction } ValangValidator.Rule.prototype = { getErrorMessage: function() { return this.errorMessage }, // Property Accessor getPropertyValue: function(propertyName, expectedType) { return this.form.getValue(propertyName) }, // Assertions _assertHasLength: function(value) { if (!value.length) { throw 'value \'' + value + '\' does not have length' } }, _assertLength: function(value, length) { this._assertHasLength(value) if (value.length != length) { throw 'value\'s length != \'' + length + '\'' } }, _throwError: function(msg) { throw msg }, // Type safety checks // This function tries to convert the lhs into a type // that are compatible with the rhs for the various // JS compare operations. When there is a choice between // converting to a string or a number; number is always // favoured. _makeCompatible: function(lhs, rhs) { try { this._forceNumber(rhs) return this._forceNumber(lhs) } catch(ex) { } var lhsType = typeof lhs var rhsType = typeof rhs if (lhsType == rhsType) { return lhs } else if (lhsType == 'number' || rhsType == 'number') { return this._forceNumber(lhs) } else { throw 'unable to convert [' + lhs + '] and [' + rhs + '] to compatible types' } }, _forceNumber: function(value) { if (typeof value != 'number') { try { var newValue = eval(value.toString()) } catch(ex) { } if (newValue && typeof newValue == 'number') { return newValue } throw 'unable to convert value [' + value + '] to number' } return value }, // Unary Operators lengthOf: function(value) { return (value != null) ? value.length : 0; }, lowerCase: function(value) { return (value != null) ? value.toLowerCase(): null }, upperCase: function(value) { return (value != null) ? value.toUpperCase(): null }, // Binary Operators equals: function(lhs, rhs) { if ((lhs == null && rhs != null) || (rhs == null && lhs != null)) { return false; } if (lhs == rhs) { return true; } lhs = this._makeCompatible(lhs, rhs) rhs = this._makeCompatible(rhs, lhs) return lhs === rhs }, lessThan: function(lhs, rhs) { lhs = this._makeCompatible(lhs, rhs) rhs = this._makeCompatible(rhs, lhs) return lhs < rhs }, lessThanOrEquals: function(lhs, rhs) { lhs = this._makeCompatible(lhs, rhs) rhs = this._makeCompatible(rhs, lhs) return lhs <= rhs }, moreThan: function(lhs, rhs) { lhs = this._makeCompatible(lhs, rhs) rhs = this._makeCompatible(rhs, lhs) return lhs > rhs }, moreThanOrEquals: function(lhs, rhs) { lhs = this._makeCompatible(lhs, rhs) rhs = this._makeCompatible(rhs, lhs) return lhs >= rhs }, inFunc: function(lhs, rhs) { for (var i = 0; i < rhs.length; i++) { var value = rhs[i] if (lhs == value) { return true } } return false }, between: function(lhs, rhs) { this._assertLength(rhs, 2) lhs = this._makeCompatible(lhs, rhs[0]) rhs[0] = this._makeCompatible(rhs[0], lhs) rhs[1] = this._makeCompatible(rhs[1], lhs) return lhs >= rhs[0] && lhs <= rhs[1] }, nullFunc: function(lhs, rhs) { return lhs === null || typeof lhs == 'undefined' }, hasText: function(lhs, rhs) { return lhs && lhs.replace(/\s/g, '').length > 0 }, hasLength: function(lhs, rhs) { return lhs && lhs.length > 0 }, isBlank: function(lhs, rhs) { return !lhs || lhs.length === 0 }, isWord: function(lhs, rhs) { return lhs && lhs.replace(/\s/g, '') == lhs }, isUpper: function(lhs, rhs) { return lhs && lhs.toUpperCase() == lhs }, isLower: function(lhs, rhs) { return lhs && lhs.toLowerCase() == lhs }, // Math operators add: function(lhs, rhs) { return this._forceNumber(lhs) + this._forceNumber(rhs) }, divide: function(lhs, rhs) { return this._forceNumber(lhs) / this._forceNumber(rhs) }, modulo: function(lhs, rhs) { return this._forceNumber(lhs) % this._forceNumber(rhs) }, multiply: function(lhs, rhs) { return this._forceNumber(lhs) * this._forceNumber(rhs) }, subtract: function(lhs, rhs) { return this._forceNumber(lhs) - this._forceNumber(rhs) }, // Custom Function RegExFunction: function(pattern, value) { if (!value.match) { throw 'don\'t know how to apply regexp to value \'' + value + '\'' } return value.match(pattern)[0] == value }, EmailFunction: function(value) { var filter = /^(([A-Za-z0-9]+_+)|([A-Za-z0-9]+\-+)|([A-Za-z0-9]+\.+)|([A-Za-z0-9]+\++))*[A-Za-z0-9]+@((\w+\-+)|(\w+\.))*\w{1,63}\.[a-zA-Z]{2,6}$/; return filter.test(value); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy