# all files / keystone/fields/types/select/ SelectType.js

93.1% Statements 81/87
78.82% Branches 67/85
87.5% Functions 14/16
93.1% Lines 81/87

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    1851×
    1×
    1×
    1×

    1×
    7×
    7×
    7×
    7×
    7×
    7×
    3×

    7×
    1×

    6×
    16×
    16×

    16×

    16×

    6×
    6×

    6×

    6×
    6×
    6×
    6×

    1×

    1×
    6×
    6×
    6×

    6×

    13×

    6×
    2×

    6×
    1×

    6×
    1×

    6×
    1×

    6×

    1×
    1×
    1×

    1×
    1×

    1×

    1×
    6×
    6×
    4×
    2×

    2×

    6×
    2×
    4×
    2×

    2×

    6×

    1×
    14×
    14×
    1×

    14×
    14×

    1×
    7×
    7×
    7×
    2×
    1×

    5×
    2×

    2×
    2×

    7×

    1×

    1×
    2×

    1×
     var _ = require('lodash');
    var FieldType = require('../Type');
    var util = require('util');
    var utils = require('keystone-utils');

    /**
     * Select FieldType Constructor
     * @extends Field
     * @api public
     */
    function select (list, path, options) {
    	this.ui = options.ui || 'select';
    	this.numeric = options.numeric ? true : false;
    	this._nativeType = (options.numeric) ? Number : String;
    	this._underscoreMethods = ['format', 'pluck'];
    	this._properties = ['ops', 'numeric'];
    	if (typeof options.options === 'string') {
    		options.options = options.options.split(',');
    	}
    	if (!Array.isArray(options.options)) {
    		throw new Error('Select fields require an options array.');
    	}
    	this.ops = options.options.map(function (i) {
    		var op = _.isString(i) ? { value: i.trim(), label: utils.keyToLabel(i) } : i;
    		Iif (!_.isObject(op)) {
    			op = { label: '' + i, value: '' + i };
    		}
    		Iif (options.numeric && !_.isNumber(op.value)) {
    			op.value = Number(op.value);
    		}
    		return op;
    	});
    	// undefined options.emptyOption defaults to true
    	Eif (options.emptyOption === undefined) {
    		options.emptyOption = true;
    	}
    	// ensure this.emptyOption is a boolean
    	this.emptyOption = options.emptyOption ? true : false;
    	// cached maps for options, labels and values
    	this.map = utils.optionsMap(this.ops);
    	this.labels = utils.optionsMap(this.ops, 'label');
    	this.values = _.map(this.ops, 'value');
    	select.super_.call(this, list, path, options);
    }
    util.inherits(select, FieldType);

    /**
     * Registers the field on the List's Mongoose Schema.
     *
     * Adds a virtual for accessing the label of the selected value,
     * and statics to the Schema for converting a value to a label,
     * and retrieving all of the defined options.
     */
    select.prototype.addToSchema = function () {
    	var field = this;
    	var schema = this.list.schema;
    	this.paths = {
    		data: this.options.dataPath || this._path.append('Data'),
    		label: this.options.labelPath || this._path.append('Label'),
    		options: this.options.optionsPath || this._path.append('Options'),
    		map: this.options.optionsMapPath || this._path.append('OptionsMap'),
    	};
    	schema.path(this.path, _.defaults({
    		type: this._nativeType,
    		enum: this.values,
    		set: function (val) {
    			return (val === '' || val === null || val === false) ? undefined : val;
    		},
    	}, this.options));
    	schema.virtual(this.paths.data).get(function () {
    		return field.map[this.get(field.path)];
    	});
    	schema.virtual(this.paths.label).get(function () {
    		return field.labels[this.get(field.path)];
    	});
    	schema.virtual(this.paths.options).get(function () {
    		return field.ops;
    	});
    	schema.virtual(this.paths.map).get(function () {
    		return field.map;
    	});
    	this.bindUnderscoreMethods();
    };

    /**
     * Returns a key value from the selected option
     */
    select.prototype.pluck = function (item, property, _default) {
    	var option = item.get(this.paths.data);
    	return (option) ? option[property] : _default;
    };

    /**
     * Retrieves a shallow clone of the options array
     */
    select.prototype.cloneOps = function () {
    	return _.map(this.ops, _.clone);
    };

    /**
     * Retrieves a shallow clone of the options map
     */
    select.prototype.cloneMap = function () {
    	return utils.optionsMap(this.ops, true);
    };

    /**
     * Add filters to a query
     */
    select.prototype.addFilterToQuery = function (filter, query) {
    	query = query || {};
    	if (!Array.isArray(filter.value)) {
    		if (filter.value) {
    			filter.value = [filter.value];
    		} else {
    			filter.value = [];
    		}
    	}
    	if (filter.value.length > 1) {
    		query[this.path] = (filter.inverted) ? { $nin: filter.value } : { $in: filter.value };
    	} else if (filter.value.length === 1) {
    		query[this.path] = (filter.inverted) ? { $ne: filter.value[0] } : filter.value[0];
    	} else {
    		query[this.path] = (filter.inverted) ? { $nin: ['', null] } : { $in: ['', null] };
    	}
    	return query;
    };

    /**
     * Asynchronously confirms that the provided value is valid
     */
    select.prototype.validateInput = function (data, callback) {
    	var value = this.getValueFromData(data);
    	if (typeof value === 'string' && this.numeric) {
    		value = utils.number(value);
    	}
    	var result = value === undefined || value === null || value === '' || (value in this.map) ? true : false;
    	utils.defer(callback, result);
    };

    /**
     * Asynchronously confirms that the provided value is present
     */
    select.prototype.validateRequiredInput = function (item, data, callback) {
    	var value = this.getValueFromData(data);
    	var result = false;
    	if (value === undefined) {
    		if (item.get(this.path)) {
    			result = true;
    		}
    	} else if (value) {
    		Eif (value !== '') {
    			// This is already checkind in validateInput, but it doesn't hurt
    			// to check again for security
    			Eif (value in this.map) {
    				result = true;
    			}
    		}
    	}
    	utils.defer(callback, result);
    };

    /**
     * Validates that a valid option has been provided in a data object
     *
     * Deprecated
     */
    select.prototype.inputIsValid = function (data, required, item) {
    	if (data[this.path]) {
    		return (data[this.path] in this.map) ? true : false;
    	} else {
    		return (!required || (!(this.path in data) && item && item.get(this.path))) ? true : false;
    	}
    };

    /**
     * Formats the field value
     */
    select.prototype.format = function (item) {
    	return this.labels[item.get(this.path)] || '';
    };

    /* Export Field Type */
    module.exports = select;