alpaca
Version:
Alpaca provides the easiest and fastest way to generate interactive forms for the web and mobile devices. It runs simply as HTML5 or more elaborately using Bootstrap, jQuery Mobile or jQuery UI. Alpaca uses Handlebars to process JSON schema and provide
475 lines (408 loc) • 16.1 kB
JavaScript
(function($) {
var Alpaca = $.alpaca;
Alpaca.Fields.NumberField = Alpaca.Fields.TextField.extend(
/**
* @lends Alpaca.Fields.NumberField.prototype
*/
{
/**
* @see Alpaca.Fields.TextField#setup
*/
setup: function()
{
// default html5 input type = "number";
//this.inputType = "number";
// TODO: we can't do this because Chrome screws up it's handling of number type
// and prevents us from validating properly
// @see http://stackoverflow.com/questions/16420828/jquery-val-refuses-to-return-non-numeric-input-from-a-number-field-under-chrome
this.base();
if (typeof(this.options.numericEntry) === "undefined")
{
this.options.numericEntry = false;
}
},
/**
* @see Alpaca.Fields.TextField#getFieldType
*/
getFieldType: function() {
return "number";
},
/**
* @see Alpaca.ControlField#postRender
*/
postRender: function(callback) {
var self = this;
this.base(function() {
if (self.control)
{
self.on("keypress", function(e) {
var key = e.charCode || e.keyCode || 0;
var valid = true;
if (self.options.numericEntry) {
valid = valid && (key >= 48 && key <= 57);
}
if(!valid) {
// don't even allow entry of invalid characters
e.preventDefault();
e.stopImmediatePropagation();
}
return valid;
});
}
callback();
});
},
/**
* @see Alpaca.Fields.ControlField#getControlValue
*/
getControlValue: function()
{
var val = this._getControlVal(true);
if (typeof(val) == "undefined" || "" == val)
{
return val;
}
return parseFloat(val);
},
/**
* @see Alpaca.Fields.TextField#handleValidate
*/
handleValidate: function() {
var baseStatus = this.base();
var valInfo = this.validation;
var status = this._validateNumber();
valInfo["stringNotANumber"] = {
"message": status ? "" : this.getMessage("stringNotANumber"),
"status": status
};
status = this._validateDivisibleBy();
valInfo["stringDivisibleBy"] = {
"message": status ? "" : Alpaca.substituteTokens(this.getMessage("stringDivisibleBy"), [this.schema.divisibleBy]),
"status": status
};
status = this._validateMaximum();
valInfo["stringValueTooLarge"] = {
"message": "",
"status": status
};
if (!status) {
if (this.schema.exclusiveMaximum) {
valInfo["stringValueTooLarge"]["message"] = Alpaca.substituteTokens(this.getMessage("stringValueTooLargeExclusive"), [this.schema.maximum]);
} else {
valInfo["stringValueTooLarge"]["message"] = Alpaca.substituteTokens(this.getMessage("stringValueTooLarge"), [this.schema.maximum]);
}
}
status = this._validateMinimum();
valInfo["stringValueTooSmall"] = {
"message": "",
"status": status
};
if (!status) {
if (this.schema.exclusiveMinimum) {
valInfo["stringValueTooSmall"]["message"] = Alpaca.substituteTokens(this.getMessage("stringValueTooSmallExclusive"), [this.schema.minimum]);
} else {
valInfo["stringValueTooSmall"]["message"] = Alpaca.substituteTokens(this.getMessage("stringValueTooSmall"), [this.schema.minimum]);
}
}
status = this._validateMultipleOf();
valInfo["stringValueNotMultipleOf"] = {
"message": "",
"status": status
};
if (!status)
{
valInfo["stringValueNotMultipleOf"]["message"] = Alpaca.substituteTokens(this.getMessage("stringValueNotMultipleOf"), [this.schema.multipleOf]);
}
// hand back a true/false
return baseStatus && valInfo["stringNotANumber"]["status"] && valInfo["stringDivisibleBy"]["status"] && valInfo["stringValueTooLarge"]["status"] && valInfo["stringValueTooSmall"]["status"] && valInfo["stringValueNotMultipleOf"]["status"] && valInfo["invalidPattern"]["status"] && valInfo["stringTooLong"]["status"] && valInfo["stringTooShort"]["status"];
},
/**
* Validates against required property.
*
* @returns {Boolean} False if this field value is empty but required, true otherwise.
*/
_validateOptional: function() {
if (!this.isDisplayOnly())
{
if (this.isRequired() && Alpaca.isValEmpty($(this.control).val()))
{
return false;
}
}
return true;
},
/**
* Validates if it is a float number.
* @returns {Boolean} true if it is a float number
*/
_validateNumber: function() {
// get value as text
var textValue = this._getControlVal();
if (typeof(textValue) === "number")
{
textValue = "" + textValue;
}
// allow empty
if (Alpaca.isValEmpty(textValue)) {
return true;
}
// check if valid number format
var validNumber = Alpaca.testRegex(Alpaca.regexps.number, textValue);
if (!validNumber)
{
return false;
}
// quick check to see if what they entered was a number
var floatValue = this.getValue();
if (isNaN(floatValue)) {
return false;
}
return true;
},
/**
* Validates divisibleBy constraint.
* @returns {Boolean} true if it passes the divisibleBy constraint.
*/
_validateDivisibleBy: function() {
var floatValue = this.getValue();
if (!Alpaca.isEmpty(this.schema.divisibleBy)) {
// mod
if (floatValue % this.schema.divisibleBy !== 0)
{
return false;
}
}
return true;
},
/**
* Validates maximum constraint.
* @returns {Boolean} true if it passes the maximum constraint.
*/
_validateMaximum: function() {
var floatValue = this.getValue();
if (!Alpaca.isEmpty(this.schema.maximum)) {
if (floatValue > this.schema.maximum) {
return false;
}
if (!Alpaca.isEmpty(this.schema.exclusiveMaximum)) {
if (floatValue == this.schema.maximum && this.schema.exclusiveMaximum) { // jshint ignore:line
return false;
}
}
}
return true;
},
/**
* Validates maximum constraint.
* @returns {Boolean} true if it passes the minimum constraint.
*/
_validateMinimum: function() {
var floatValue = this.getValue();
if (!Alpaca.isEmpty(this.schema.minimum)) {
if (floatValue < this.schema.minimum) {
return false;
}
if (!Alpaca.isEmpty(this.schema.exclusiveMinimum)) {
if (floatValue == this.schema.minimum && this.schema.exclusiveMinimum) { // jshint ignore:line
return false;
}
}
}
return true;
},
/**
* Validates multipleOf constraint.
* @returns {Boolean} true if it passes the multipleOf constraint.
*/
_validateMultipleOf: function() {
var floatValue = this.getValue();
if (!Alpaca.isEmpty(this.schema.multipleOf)) {
if (floatValue && this.schema.multipleOf > 0)
{
return (floatValue % this.schema.multipleOf) === 0;
}
}
return true;
},
/**
* @see Alpaca.Fields.TextField#getType
*/
getType: function() {
return "number";
},
/**
* @see Alpaca.ControlField#onKeyPress
*/
onKeyPress: function(e)
{
var self = this;
// ignore tab and arrow keys
if (e.keyCode === 9 || e.keyCode === 37 || e.keyCode === 38 || e.keyCode === 39 || e.keyCode === 40 ) {
return;
}
if (e.keyCode === 8) // backspace
{
if (!Alpaca.isEmpty(self.schema.minLength) && (self.options.constrainLengths || self.options.constrainMinLength))
{
var newValue = self.getValue() || "";
if(Alpaca.isNumber(newValue)) {
newValue = newValue.toString();
}
if (newValue.length <= self.schema.minLength)
{
// kill event
e.preventDefault();
e.stopImmediatePropagation();
}
}
}
else
{
if (!Alpaca.isEmpty(self.schema.maxLength) && (self.options.constrainLengths || self.options.constrainMaxLength))
{
var newValue = self.getValue() || "";
if(Alpaca.isNumber(newValue)) {
newValue = newValue.toString();
}
if (newValue.length >= self.schema.maxLength)
{
// kill event
e.preventDefault();
e.stopImmediatePropagation();
}
}
}
if (e.keyCode === 32) // space
{
if (self.options.disallowEmptySpaces)
{
// kill event
e.preventDefault();
e.stopImmediatePropagation();
}
}
},
onKeyUp: function(e)
{
var self = this;
// if applicable, update the max length indicator
self.updateMaxLengthIndicator();
// trigger "fieldkeyup"
$(this.field).trigger("fieldkeyup");
},
/* builder_helpers */
/**
* @private
* @see Alpaca.Fields.TextField#getSchemaOfSchema
*/
getSchemaOfSchema: function() {
return Alpaca.merge(this.base(), {
"properties": {
"multipleOf": {
"title": "Multiple Of",
"description": "Property value must be a multiple of the multipleOf schema property such that division by this value yields an interger (mod zero).",
"type": "number"
},
"minimum": {
"title": "Minimum",
"description": "Minimum value of the property.",
"type": "number"
},
"maximum": {
"title": "Maximum",
"description": "Maximum value of the property.",
"type": "number"
},
"exclusiveMinimum": {
"title": "Exclusive Minimum",
"description": "Property value can not equal the number defined by the minimum schema property.",
"type": "boolean",
"default": false
},
"exclusiveMaximum": {
"title": "Exclusive Maximum",
"description": "Property value can not equal the number defined by the maximum schema property.",
"type": "boolean",
"default": false
}
}
});
},
/**
* @private
* @see Alpaca.Fields.TextField#getOptionsSchema
*/
getOptionsForSchema: function() {
return Alpaca.merge(this.base(), {
"fields": {
"multipleOf": {
"title": "Multiple Of",
"description": "The value must be a integral multiple of the property",
"type": "number"
},
"minimum": {
"title": "Minimum",
"description": "Minimum value of the property",
"type": "number"
},
"maximum": {
"title": "Maximum",
"description": "Maximum value of the property",
"type": "number"
},
"exclusiveMinimum": {
"rightLabel": "Exclusive minimum ?",
"helper": "Field value must be greater than but not equal to this number if checked",
"type": "checkbox"
},
"exclusiveMaximum": {
"rightLabel": "Exclusive Maximum ?",
"helper": "Field value must be less than but not equal to this number if checked",
"type": "checkbox"
}
}
});
},
/**
* @private
* @see Alpaca.Fields.NumberField#getSchemaOfOptions
*/
getSchemaOfOptions: function() {
return Alpaca.merge(this.base(), {
"properties": {
"numericEntry": {
"title": "Numeric Entry",
"description": "Whether to constrain data entry key presses to numeric values (0-9)",
"type": "boolean",
"default": false
}
}
});
},
/**
* @see Alpaca.Fields.TextField#getTitle
*/
getTitle: function() {
return "Number Field";
},
/**
* @see Alpaca.Fields.TextField#getDescription
*/
getDescription: function() {
return "Field for float numbers.";
}
/* end_builder_helpers */
});
// Additional Registrations
Alpaca.registerMessages({
"stringValueTooSmall": "The minimum value for this field is {0}",
"stringValueTooLarge": "The maximum value for this field is {0}",
"stringValueTooSmallExclusive": "Value of this field must be greater than {0}",
"stringValueTooLargeExclusive": "Value of this field must be less than {0}",
"stringDivisibleBy": "The value must be divisible by {0}",
"stringNotANumber": "This value is not a number.",
"stringValueNotMultipleOf": "This value is not a multiple of {0}"
});
Alpaca.registerFieldClass("number", Alpaca.Fields.NumberField);
Alpaca.registerDefaultSchemaFieldMapping("number", "number");
})(jQuery);