keystone
Version:
Web Application Framework and Admin GUI / Content Management System built on Express.js and Mongoose
237 lines (227 loc) • 6.72 kB
JavaScript
var FieldType = require('../Type');
var numeral = require('numeral');
var util = require('util');
var utils = require('keystone-utils');
var addPresenceToQuery = require('../../utils/addPresenceToQuery');
/**
* Number FieldType Constructor
* @extends Field
* @api public
*/
function numberarray (list, path, options) {
this._nativeType = [Number];
this._underscoreMethods = ['format'];
this._formatString = (options.format === false) ? false : (options.format || '0,0[.][000000000000]');
this._defaultSize = 'small';
if (this._formatString && typeof this._formatString !== 'string') {
throw new Error('FieldType.NumberArray: options.format must be a string.');
}
this.separator = options.separator || ' | ';
numberarray.super_.call(this, list, path, options);
}
numberarray.properName = 'NumberArray';
util.inherits(numberarray, FieldType);
/**
* Formats the field value
*/
numberarray.prototype.format = function (item, format, separator) {
var value = item.get(this.path);
if (format || this._formatString) {
value = value.map(function (n) {
return numeral(n).format(format || this._formatString);
});
}
return value.join(separator || this.separator);
};
/**
* Checks if a value is a valid number
*/
function isValidNumber (value) {
return !Number.isNaN(utils.number(value));
}
/**
* Asynchronously confirms that the provided value is valid
*/
numberarray.prototype.validateInput = function (data, callback) {
var value = this.getValueFromData(data);
var result = true;
// Let undefined, empty string and null pass
if (value !== undefined && value !== '' && value !== null) {
// Coerce a single value to an array
if (!Array.isArray(value)) {
value = [value];
}
for (var i = 0; i < value.length; i++) {
var thisValue = value[i];
// If it's a string, check if there's a number in the string
if (typeof thisValue === 'string') {
thisValue = utils.number(thisValue);
}
// If it's not a number or NaN invalidate
if (typeof thisValue !== 'number' || Number.isNaN(thisValue)) {
result = false;
break;
}
}
}
utils.defer(callback, result);
};
/**
* Asynchronously confirms that the a value is present
*/
numberarray.prototype.validateRequiredInput = function (item, data, callback) {
var value = this.getValueFromData(data);
var result = false;
// If the field is undefined but has a value saved already, validate
if (value === undefined) {
if (item.get(this.path) && item.get(this.path).length) {
result = true;
}
}
// If it's a string that's not empty, validate
if (typeof value === 'string' && value !== '') {
result = true;
// If it's an array of only numbers and/or numberify-able data, validate
} else if (Array.isArray(value)) {
var invalidContent = false;
for (var i = 0; i < value.length; i++) {
var thisValue = value[i];
// If it's a string, check if there's a number in the string
if (typeof thisValue === 'string') {
thisValue = utils.number(thisValue);
}
// If even a single item is not a number or NaN, invalidate
if (typeof thisValue !== 'number' || Number.isNaN(thisValue)) {
invalidContent = true;
break;
}
}
if (invalidContent === false) {
result = true;
}
}
utils.defer(callback, result);
};
/**
* Add filters to a query
*
* @param {Object} filter The data from the frontend
* @param {String} filter.mode The filter mode, either one of
* "between", "gt" or "lt"
* @param {String} [filter.presence='some'] The presence mode, either on of
* "none" and "some". Default: 'some'
* @param {String|Object} filter.value The value that is filtered for
*/
numberarray.prototype.addFilterToQuery = function (filter) {
var query = {};
var presence = filter.presence || 'some';
// Filter empty/non-empty arrays (copied from textarray)
if (filter.value === undefined
|| filter.value === null
|| filter.value === '') {
// "At least one element contains nothing"
// This isn't 100% accurate because this will only return arrays that
// don't have elements, not ones that have empty elements, but it works
// fine for 99% of the usecase
query[this.path] = presence === 'some' ? {
$size: 0,
// "No elements contain nothing"
} : {
$not: {
$size: 0,
},
};
return query;
}
// Filter between two numbers
if (filter.mode === 'between') {
var min = utils.number(filter.value.min);
var max = utils.number(filter.value.max);
if (!isNaN(min) && !isNaN(max)) {
query[this.path] = {
$gte: min,
$lte: max,
};
query[this.path] = addPresenceToQuery(presence, query[this.path]);
}
return query;
}
var value = utils.number(filter.value);
// Filter greater than, less than and equals
if (!isNaN(value)) {
if (filter.mode === 'gt') {
query[this.path] = {
$gt: value,
};
}
else if (filter.mode === 'lt') {
query[this.path] = {
$lt: value,
};
}
else {
query[this.path] = {
$eq: value,
};
}
query[this.path] = addPresenceToQuery(presence, query[this.path]);
}
return query;
};
/**
* Checks that a valid array of number has been provided in a data object
* An empty value clears the stored value and is considered valid
*
* Deprecated
*/
numberarray.prototype.inputIsValid = function (data, required, item) {
var value = this.getValueFromData(data);
if (required) {
if (value === undefined && item && item.get(this.path) && item.get(this.path).length) {
return true;
}
if (value === undefined || !Array.isArray(value) || (typeof value !== 'string') || (typeof value !== 'number')) {
return false;
}
if (Array.isArray(value) && !value.length) {
return false;
}
}
if (typeof value === 'string') {
if (!isValidNumber(value)) {
return false;
}
}
if (Array.isArray(value)) {
for (var index = 0; index < value.length; index++) {
if (!isValidNumber(value[index])) {
return false;
}
}
}
return (value === undefined || Array.isArray(value) || (typeof value === 'string') || (typeof value === 'number'));
};
/**
* Updates the value for this field in the item from a data object
*/
numberarray.prototype.updateItem = function (item, data, callback) {
var value = this.getValueFromData(data);
if (value === undefined || value === null || value === '') {
value = [];
}
if (!Array.isArray(value)) {
value = [value];
}
value = value.map(function (num) {
if (typeof num !== 'number') {
num = utils.number(num);
}
return num;
}).filter(function (num) {
return !Number.isNaN(num);
});
item.set(this.path, value);
process.nextTick(callback);
};
/* Export Field Type */
module.exports = numberarray;