diff --git a/modules/system/assets/ui/js/inspector.editor.base.js b/modules/system/assets/ui/js/inspector.editor.base.js index f0f3c1a38..5ff780c67 100644 --- a/modules/system/assets/ui/js/inspector.editor.base.js +++ b/modules/system/assets/ui/js/inspector.editor.base.js @@ -29,7 +29,7 @@ this.parentGroup = group this.group = null // Group created by a grouped editor, for example by the set editor this.childInspector = null - this.validators = [] + this.validationSet = null Base.call(this) @@ -40,7 +40,7 @@ BaseEditor.prototype.constructor = Base BaseEditor.prototype.dispose = function() { - this.disposeValidators() + this.disposeValidation() if (this.childInspector) { this.childInspector.dispose() @@ -53,7 +53,7 @@ this.childInspector = null this.parentGroup = null this.group = null - this.validators = null + this.validationSet = null BaseProto.dispose.call(this) } @@ -61,7 +61,7 @@ BaseEditor.prototype.init = function() { this.build() this.registerHandlers() - this.createValidators() + this.initValidation() } BaseEditor.prototype.build = function() { @@ -104,77 +104,29 @@ // Validation // - BaseEditor.prototype.createValidators = function() { - // Handle legacy validation syntax properties: - // - // - required - // - validationPattern - // - validationMessage - - if ((this.propertyDefinition.required !== undefined || - this.propertyDefinition.validationPattern !== undefined || - this.propertyDefinition.validationMessage !== undefined) && - this.propertyDefinition.validation !== undefined) { - this.throwError('Legacy and new validation syntax should not be mixed.') - } - - if (this.propertyDefinition.required !== undefined) { - var validator = new $.oc.inspector.validators.required({ - message: this.propertyDefinition.validationMessage - }) - - this.validators.push(validator) - } - - if (this.propertyDefinition.validationPattern !== undefined) { - var validator = new $.oc.inspector.validators.regex({ - message: this.propertyDefinition.validationMessage, - pattern: this.propertyDefinition.validationPattern - }) - - this.validators.push(validator) - } - - // - // Handle new validation syntax - // - - if (this.propertyDefinition.validation === undefined) { - return - } - - for (var validatorName in this.propertyDefinition.validation) { - if ($.oc.inspector.validators[validatorName] == undefined) { - this.throwError('Inspector validator "' + validatorName + '" is not found in the $.oc.inspector.validators namespace.') - } - - var validator = new $.oc.inspector.validators[validatorName]( - this.propertyDefinition.validation[validatorName] - ) - - this.validators.push(validator) - } + BaseEditor.prototype.initValidation = function() { + this.validationSet = new $.oc.inspector.validationSet(this.propertyDefinition, this.propertyDefinition.property) } - BaseEditor.prototype.disposeValidators = function() { - for (var i = 0, len = this.validators.length; i < len; i++) { - this.validators[i].dispose() - } + BaseEditor.prototype.disposeValidation = function() { + this.validationSet.dispose() + } + + BaseEditor.prototype.getValueToValidate = function() { + return this.inspector.getPropertyValue(this.propertyDefinition.property) } BaseEditor.prototype.validate = function() { - for (var i = 0, len = this.validators.length; i < len; i++) { - var validator = this.validators[i], - value = this.inspector.getPropertyValue(this.propertyDefinition.property) + var value = this.getValueToValidate() - if (value === undefined) { - value = this.getUndefinedValue() - } + if (value === undefined) { + value = this.getUndefinedValue() + } - if (!validator.isValid(value)) { - $.oc.flashMsg({text: validator.getMessage(), 'class': 'error', 'interval': 5}) - return false - } + var validationResult = this.validationSet.validate(value) + if (validationResult !== null) { + $.oc.flashMsg({text: validationResult, 'class': 'error', 'interval': 5}) + return false } return true diff --git a/modules/system/assets/ui/js/inspector.editor.object.js b/modules/system/assets/ui/js/inspector.editor.object.js index e64b6c893..b949f5c46 100644 --- a/modules/system/assets/ui/js/inspector.editor.object.js +++ b/modules/system/assets/ui/js/inspector.editor.object.js @@ -104,7 +104,6 @@ return true } - ObjectEditor.prototype.getUndefinedValue = function() { var result = {} @@ -120,6 +119,19 @@ return this.getValueOrRemove(result) } + ObjectEditor.prototype.validate = function() { + var values = values = this.childInspector.getValues() + + if (this.cleanUpValue(values) === $.oc.inspector.removedProperty) { + // Ignore any validation rules if the object's required + // property is empty (ignoreIfPropertyEmpty) + + return true + } + + return this.childInspector.validate() + } + // // Event handlers // diff --git a/modules/system/assets/ui/js/inspector.editor.string.js b/modules/system/assets/ui/js/inspector.editor.string.js index 58a93849d..4d3f75b2e 100644 --- a/modules/system/assets/ui/js/inspector.editor.string.js +++ b/modules/system/assets/ui/js/inspector.editor.string.js @@ -75,7 +75,7 @@ } StringEditor.prototype.onInputKeyUp = function() { - var value = this.getInput().value + var value = $.trim(this.getInput().value) this.inspector.setPropertyValue(this.propertyDefinition.property, value) } diff --git a/modules/system/assets/ui/js/inspector.validationset.js b/modules/system/assets/ui/js/inspector.validationset.js new file mode 100644 index 000000000..8b80ff33c --- /dev/null +++ b/modules/system/assets/ui/js/inspector.validationset.js @@ -0,0 +1,119 @@ +/* + * Inspector validation set class. + */ ++function ($) { "use strict"; + + // NAMESPACES + // ============================ + + if ($.oc.inspector.validators === undefined) + $.oc.inspector.validators = {} + + // CLASS DEFINITION + // ============================ + + var Base = $.oc.foundation.base, + BaseProto = Base.prototype + + var ValidationSet = function(options, propertyName) { + this.validators = [] + + this.options = options + this.propertyName = propertyName + Base.call(this) + + this.createValidators() + } + + ValidationSet.prototype = Object.create(BaseProto) + ValidationSet.prototype.constructor = Base + + ValidationSet.prototype.dispose = function() { + this.disposeValidators() + this.validators = null + + BaseProto.dispose.call(this) + } + + ValidationSet.prototype.disposeValidators = function() { + for (var i = 0, len = this.validators.length; i < len; i++) { + this.validators[i].dispose() + } + } + + ValidationSet.prototype.throwError = function(errorMessage) { + throw new Error(errorMessage + ' Property: ' + this.propertyName) + } + + ValidationSet.prototype.createValidators = function() { + // Handle legacy validation syntax properties: + // + // - required + // - validationPattern + // - validationMessage + + if ((this.options.required !== undefined || + this.options.validationPattern !== undefined || + this.options.validationMessage !== undefined) && + this.options.validation !== undefined) { + this.throwError('Legacy and new validation syntax should not be mixed.') + } + + if (this.options.required !== undefined) { + var validator = new $.oc.inspector.validators.required({ + message: this.options.validationMessage + }) + + this.validators.push(validator) + } + + if (this.options.validationPattern !== undefined) { + var validator = new $.oc.inspector.validators.regex({ + message: this.options.validationMessage, + pattern: this.options.validationPattern + }) + + this.validators.push(validator) + } + + // + // Handle new validation syntax + // + + if (this.options.validation === undefined) { + return + } + + for (var validatorName in this.options.validation) { + if ($.oc.inspector.validators[validatorName] == undefined) { + this.throwError('Inspector validator "' + validatorName + '" is not found in the $.oc.inspector.validators namespace.') + } + + var validator = new $.oc.inspector.validators[validatorName]( + this.options.validation[validatorName] + ) + + this.validators.push(validator) + } + } + + ValidationSet.prototype.validate = function(value) { + try { + for (var i = 0, len = this.validators.length; i < len; i++) { + var validator = this.validators[i], + errorMessage = validator.isValid(value) + + if (typeof errorMessage === 'string') { + return errorMessage + } + } + + return null + } + catch (err) { + this.throwError(err) + } + } + + $.oc.inspector.validationSet = ValidationSet +}(window.jQuery); \ No newline at end of file diff --git a/modules/system/assets/ui/js/inspector.validator.base.js b/modules/system/assets/ui/js/inspector.validator.base.js index 9e5ad6b84..8d7d41a61 100644 --- a/modules/system/assets/ui/js/inspector.validator.base.js +++ b/modules/system/assets/ui/js/inspector.validator.base.js @@ -17,7 +17,9 @@ var BaseValidator = function(options) { this.options = options - this.defaultMessage = 'Invalid property value' + this.defaultMessage = 'Invalid property value.' + + Base.call(this) } BaseValidator.prototype = Object.create(BaseProto) @@ -29,9 +31,14 @@ BaseProto.dispose.call(this) } - BaseValidator.prototype.getMessage = function() { - if (this.options.message !== undefined) + BaseValidator.prototype.getMessage = function(defaultMessage) { + if (this.options.message !== undefined) { return this.options.message + } + + if (defaultMessage !== undefined) { + return defaultMessage + } return this.defaultMessage } @@ -49,7 +56,7 @@ } BaseValidator.prototype.isValid = function(value) { - return true + return null } $.oc.inspector.validators.base = BaseValidator diff --git a/modules/system/assets/ui/js/inspector.validator.basenumber.js b/modules/system/assets/ui/js/inspector.validator.basenumber.js new file mode 100644 index 000000000..c0074e408 --- /dev/null +++ b/modules/system/assets/ui/js/inspector.validator.basenumber.js @@ -0,0 +1,43 @@ +/* + * Base class for Inspector numeric validators. + */ ++function ($) { "use strict"; + + var Base = $.oc.inspector.validators.base, + BaseProto = Base.prototype + + var BaseNumber = function(options) { + Base.call(this, options) + } + + BaseNumber.prototype = Object.create(BaseProto) + BaseNumber.prototype.constructor = Base + + BaseNumber.prototype.doCommonChecks = function(value) { + if (this.options.min !== undefined || this.options.max !== undefined) { + if (this.options.min !== undefined) { + if (this.options.min.value === undefined) + throw new Error('The min.value parameter is not defined in the Inspector validator configuration') + + if (value < this.options.min.value) { + return this.options.min.message !== undefined ? + this.options.min.message : + "The value should not be less than " + this.options.min.value + } + } + + if (this.options.max !== undefined) { + if (this.options.max.value === undefined) + throw new Error('The max.value parameter is not defined in the table Inspector validator configuration') + + if (value > this.options.max.value) { + return this.options.max.message !== undefined ? + this.options.max.message : + "The value should not be greater than " + this.options.max.value + } + } + } + } + + $.oc.inspector.validators.baseNumber = BaseNumber +}(window.jQuery); \ No newline at end of file diff --git a/modules/system/assets/ui/js/inspector.validator.float.js b/modules/system/assets/ui/js/inspector.validator.float.js new file mode 100644 index 000000000..d1b97f62b --- /dev/null +++ b/modules/system/assets/ui/js/inspector.validator.float.js @@ -0,0 +1,47 @@ +/* + * Inspector float validator. + */ ++function ($) { "use strict"; + + var Base = $.oc.inspector.validators.baseNumber, + BaseProto = Base.prototype + + var FloatValidator = function(options) { + Base.call(this, options) + } + + FloatValidator.prototype = Object.create(BaseProto) + FloatValidator.prototype.constructor = Base + + FloatValidator.prototype.isValid = function(value) { + if (!this.isScalar(value) || typeof value == 'boolean') { + this.throwError('The Float Inspector validator can only be used with string values.') + } + + if (value === undefined || value === null) { + return null + } + + var string = $.trim(String(value)) + + if (string.length === 0) { + return null + } + + var testResult = this.options.allowNegative ? + /^[-]?([0-9]+\.[0-9]+|[0-9]+)$/.test(string) : + /^([0-9]+\.[0-9]+|[0-9]+)$/.test(string) + + if (!testResult) { + var defaultMessage = this.options.allowNegative ? + 'The value should be a floating point number.' : + 'The value should be a positive floating point number.'; + + return this.getMessage(defaultMessage) + } + + return this.doCommonChecks(parseFloat(string)) + } + + $.oc.inspector.validators.float = FloatValidator +}(window.jQuery); \ No newline at end of file diff --git a/modules/system/assets/ui/js/inspector.validator.integer.js b/modules/system/assets/ui/js/inspector.validator.integer.js new file mode 100644 index 000000000..2fbcc7f55 --- /dev/null +++ b/modules/system/assets/ui/js/inspector.validator.integer.js @@ -0,0 +1,47 @@ +/* + * Inspector integer validator. + */ ++function ($) { "use strict"; + + var Base = $.oc.inspector.validators.baseNumber, + BaseProto = Base.prototype + + var IntegerValidator = function(options) { + Base.call(this, options) + } + + IntegerValidator.prototype = Object.create(BaseProto) + IntegerValidator.prototype.constructor = Base + + IntegerValidator.prototype.isValid = function(value) { + if (!this.isScalar(value) || typeof value == 'boolean') { + this.throwError('The Integer Inspector validator can only be used with string values.') + } + + if (value === undefined || value === null) { + return null + } + + var string = $.trim(String(value)) + + if (string.length === 0) { + return null + } + + var testResult = this.options.allowNegative ? + /^\-?[0-9]*$/.test(string) : + /^[0-9]*$/.test(string) + + if (!testResult) { + var defaultMessage = this.options.allowNegative ? + 'The value should be an integer.' : + 'The value should be a positive integer.'; + + return this.getMessage(defaultMessage) + } + + return this.doCommonChecks(parseInt(string)) + } + + $.oc.inspector.validators.integer = IntegerValidator +}(window.jQuery); \ No newline at end of file diff --git a/modules/system/assets/ui/js/inspector.validator.length.js b/modules/system/assets/ui/js/inspector.validator.length.js new file mode 100644 index 000000000..ab2c0900c --- /dev/null +++ b/modules/system/assets/ui/js/inspector.validator.length.js @@ -0,0 +1,71 @@ +/* + * Inspector length validator. + */ ++function ($) { "use strict"; + + var Base = $.oc.inspector.validators.base, + BaseProto = Base.prototype + + var LengthValidator = function(options) { + Base.call(this, options) + } + + LengthValidator.prototype = Object.create(BaseProto) + LengthValidator.prototype.constructor = Base + + LengthValidator.prototype.isValid = function(value) { + if (value === undefined || value === null) { + return null + } + + if (typeof value == 'boolean') { + this.throwError('The Length Inspector validator cannot work with Boolean values.') + + } + + var length = null + + if(Object.prototype.toString.call(value) === '[object Array]' || typeof value === 'string') { + length = value.length + } + else if (typeof value === 'object') { + length = this.getObjectLength(value) + } + + if (this.options.min !== undefined || this.options.max !== undefined) { + if (this.options.min !== undefined) { + if (this.options.min.value === undefined) + throw new Error('The min.value parameter is not defined in the Length Inspector validator configuration.') + + if (length < this.options.min.value) { + return this.options.min.message !== undefined ? + this.options.min.message : + "The value should not be shorter than " + this.options.min.value + } + } + + if (this.options.max !== undefined) { + if (this.options.max.value === undefined) + throw new Error('The max.value parameter is not defined in the Length Inspector validator configuration.') + + if (length > this.options.max.value) { + return this.options.max.message !== undefined ? + this.options.max.message : + "The value should not be longer than " + this.options.max.value + } + } + } + } + + LengthValidator.prototype.getObjectLength = function(value) { + var result = 0 + + for (var key in value) { + result++ + } + + return result + } + + $.oc.inspector.validators.length = LengthValidator +}(window.jQuery); \ No newline at end of file diff --git a/modules/system/assets/ui/js/inspector.validator.regex.js b/modules/system/assets/ui/js/inspector.validator.regex.js index eb0a55e43..6585fa784 100644 --- a/modules/system/assets/ui/js/inspector.validator.regex.js +++ b/modules/system/assets/ui/js/inspector.validator.regex.js @@ -14,26 +14,27 @@ RegexValidator.prototype.constructor = Base RegexValidator.prototype.isValid = function(value) { + if (this.options.pattern === undefined) { + this.throwError('The pattern parameter is not defined in the Regex Inspector validator configuration.') + } + if (!this.isScalar(value)) { this.throwError('The Regex Inspector validator can only be used with string values.') } if (value === undefined || value === null) { - return true + return null } - var string = String(value) + var string = $.trim(String(value)) - if (string.length == 0) - return - - if (this.options.pattern === undefined) { - this.throwError('The pattern parameter is not defined in the Regex Inspector validator configuration.') + if (string.length === 0) { + return null } var regexObj = new RegExp(this.options.pattern, this.options.modifiers) - return regexObj.test(string) + return regexObj.test(string) ? null : this.getMessage() } $.oc.inspector.validators.regex = RegexValidator diff --git a/modules/system/assets/ui/js/inspector.validator.required.js b/modules/system/assets/ui/js/inspector.validator.required.js index c8825128b..08262b305 100644 --- a/modules/system/assets/ui/js/inspector.validator.required.js +++ b/modules/system/assets/ui/js/inspector.validator.required.js @@ -8,6 +8,8 @@ var RequiredValidator = function(options) { Base.call(this, options) + + this.defaultMessage = 'The property is required.' } RequiredValidator.prototype = Object.create(BaseProto) @@ -15,18 +17,18 @@ RequiredValidator.prototype.isValid = function(value) { if (value === undefined || value === null) { - return false + return this.getMessage() } if (typeof value === 'boolean') { - return value + return value ? null : this.getMessage() } if (typeof value === 'object') { - return !$.isEmptyObject(value) + return !$.isEmptyObject(value) ? null : this.getMessage() } - return $.trim(String(value)).length > 0 + return $.trim(String(value)).length > 0 ? null : this.getMessage() } $.oc.inspector.validators.required = RequiredValidator