UNPKG

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
(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);