// Load modules
|
|
|
|
var Hoek = require('hoek');
|
|
var Ref = require('./ref');
|
|
var Errors = require('./errors');
|
|
var Alternatives = null; // Delay-loaded to prevent circular dependencies
|
|
var Cast = null;
|
|
|
|
|
|
// Declare internals
|
|
|
|
var internals = {};
|
|
|
|
|
|
internals.defaults = {
|
|
abortEarly: true,
|
|
convert: true,
|
|
allowUnknown: false,
|
|
skipFunctions: false,
|
|
stripUnknown: false,
|
|
language: {},
|
|
presence: 'optional',
|
|
raw: false,
|
|
strip: false,
|
|
noDefaults: false
|
|
|
|
// context: null
|
|
};
|
|
|
|
|
|
internals.checkOptions = function (options) {
|
|
|
|
var optionType = {
|
|
abortEarly: 'boolean',
|
|
convert: 'boolean',
|
|
allowUnknown: 'boolean',
|
|
skipFunctions: 'boolean',
|
|
stripUnknown: 'boolean',
|
|
language: 'object',
|
|
presence: ['string', 'required', 'optional', 'forbidden', 'ignore'],
|
|
raw: 'boolean',
|
|
context: 'object',
|
|
strip: 'boolean',
|
|
noDefaults: 'boolean'
|
|
};
|
|
|
|
var keys = Object.keys(options);
|
|
for (var k = 0, kl = keys.length; k < kl; ++k) {
|
|
var key = keys[k];
|
|
var opt = optionType[key];
|
|
var type = opt;
|
|
var values = null;
|
|
|
|
if (Array.isArray(opt)) {
|
|
type = opt[0];
|
|
values = opt.slice(1);
|
|
}
|
|
|
|
Hoek.assert(type, 'unknown key ' + key);
|
|
Hoek.assert(typeof options[key] === type, key + ' should be of type ' + type);
|
|
if (values) {
|
|
Hoek.assert(values.indexOf(options[key]) >= 0, key + ' should be one of ' + values.join(', '));
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
module.exports = internals.Any = function () {
|
|
|
|
Cast = Cast || require('./cast');
|
|
|
|
this.isJoi = true;
|
|
this._type = 'any';
|
|
this._settings = null;
|
|
this._valids = new internals.Set();
|
|
this._invalids = new internals.Set();
|
|
this._tests = [];
|
|
this._refs = [];
|
|
this._flags = { /*
|
|
presence: 'optional', // optional, required, forbidden, ignore
|
|
allowOnly: false,
|
|
allowUnknown: undefined,
|
|
default: undefined,
|
|
forbidden: false,
|
|
encoding: undefined,
|
|
insensitive: false,
|
|
trim: false,
|
|
case: undefined, // upper, lower
|
|
empty: undefined,
|
|
func: false
|
|
*/ };
|
|
|
|
this._description = null;
|
|
this._unit = null;
|
|
this._notes = [];
|
|
this._tags = [];
|
|
this._examples = [];
|
|
this._meta = [];
|
|
|
|
this._inner = {}; // Hash of arrays of immutable objects
|
|
};
|
|
|
|
|
|
internals.Any.prototype.isImmutable = true; // Prevents Hoek from deep cloning schema objects
|
|
|
|
|
|
internals.Any.prototype.clone = function () {
|
|
|
|
var obj = Object.create(Object.getPrototypeOf(this));
|
|
|
|
obj.isJoi = true;
|
|
obj._type = this._type;
|
|
obj._settings = internals.concatSettings(this._settings);
|
|
obj._valids = Hoek.clone(this._valids);
|
|
obj._invalids = Hoek.clone(this._invalids);
|
|
obj._tests = this._tests.slice();
|
|
obj._refs = this._refs.slice();
|
|
obj._flags = Hoek.clone(this._flags);
|
|
|
|
obj._description = this._description;
|
|
obj._unit = this._unit;
|
|
obj._notes = this._notes.slice();
|
|
obj._tags = this._tags.slice();
|
|
obj._examples = this._examples.slice();
|
|
obj._meta = this._meta.slice();
|
|
|
|
obj._inner = {};
|
|
var inners = Object.keys(this._inner);
|
|
for (var i = 0, il = inners.length; i < il; ++i) {
|
|
var key = inners[i];
|
|
obj._inner[key] = this._inner[key] ? this._inner[key].slice() : null;
|
|
}
|
|
|
|
return obj;
|
|
};
|
|
|
|
|
|
internals.Any.prototype.concat = function (schema) {
|
|
|
|
Hoek.assert(schema && schema.isJoi, 'Invalid schema object');
|
|
Hoek.assert(this._type === 'any' || schema._type === 'any' || schema._type === this._type, 'Cannot merge type', this._type, 'with another type:', schema._type);
|
|
|
|
var obj = this.clone();
|
|
|
|
if (this._type === 'any' && schema._type !== 'any') {
|
|
|
|
// Reset values as if we were "this"
|
|
var tmpObj = schema.clone();
|
|
var keysToRestore = ['_settings', '_valids', '_invalids', '_tests', '_refs', '_flags', '_description', '_unit',
|
|
'_notes', '_tags', '_examples', '_meta', '_inner'];
|
|
|
|
for (var j = 0, jl = keysToRestore.length; j < jl; ++j) {
|
|
tmpObj[keysToRestore[j]] = obj[keysToRestore[j]];
|
|
}
|
|
|
|
obj = tmpObj;
|
|
}
|
|
|
|
obj._settings = obj._settings ? internals.concatSettings(obj._settings, schema._settings) : schema._settings;
|
|
obj._valids.merge(schema._valids, schema._invalids);
|
|
obj._invalids.merge(schema._invalids, schema._valids);
|
|
obj._tests = obj._tests.concat(schema._tests);
|
|
obj._refs = obj._refs.concat(schema._refs);
|
|
Hoek.merge(obj._flags, schema._flags);
|
|
|
|
obj._description = schema._description || obj._description;
|
|
obj._unit = schema._unit || obj._unit;
|
|
obj._notes = obj._notes.concat(schema._notes);
|
|
obj._tags = obj._tags.concat(schema._tags);
|
|
obj._examples = obj._examples.concat(schema._examples);
|
|
obj._meta = obj._meta.concat(schema._meta);
|
|
|
|
var inners = Object.keys(schema._inner);
|
|
var isObject = obj._type === 'object';
|
|
for (var i = 0, il = inners.length; i < il; ++i) {
|
|
var key = inners[i];
|
|
var source = schema._inner[key];
|
|
if (source) {
|
|
var target = obj._inner[key];
|
|
if (target) {
|
|
if (isObject && key === 'children') {
|
|
var keys = {};
|
|
|
|
for (var k = 0, kl = target.length; k < kl; ++k) {
|
|
keys[target[k].key] = k;
|
|
}
|
|
|
|
for (k = 0, kl = source.length; k < kl; ++k) {
|
|
var sourceKey = source[k].key;
|
|
if (keys[sourceKey] >= 0) {
|
|
target[keys[sourceKey]] = {
|
|
key: sourceKey,
|
|
schema: target[keys[sourceKey]].schema.concat(source[k].schema)
|
|
};
|
|
}
|
|
else {
|
|
target.push(source[k]);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
obj._inner[key] = obj._inner[key].concat(source);
|
|
}
|
|
}
|
|
else {
|
|
obj._inner[key] = source.slice();
|
|
}
|
|
}
|
|
}
|
|
|
|
return obj;
|
|
};
|
|
|
|
|
|
internals.Any.prototype._test = function (name, arg, func) {
|
|
|
|
Hoek.assert(!this._flags.allowOnly, 'Cannot define rules when valid values specified');
|
|
|
|
var obj = this.clone();
|
|
obj._tests.push({ func: func, name: name, arg: arg });
|
|
return obj;
|
|
};
|
|
|
|
|
|
internals.Any.prototype.options = function (options) {
|
|
|
|
Hoek.assert(!options.context, 'Cannot override context');
|
|
internals.checkOptions(options);
|
|
|
|
var obj = this.clone();
|
|
obj._settings = internals.concatSettings(obj._settings, options);
|
|
return obj;
|
|
};
|
|
|
|
|
|
internals.Any.prototype.strict = function (isStrict) {
|
|
|
|
var obj = this.clone();
|
|
obj._settings = obj._settings || {};
|
|
obj._settings.convert = isStrict === undefined ? false : !isStrict;
|
|
return obj;
|
|
};
|
|
|
|
|
|
internals.Any.prototype.raw = function (isRaw) {
|
|
|
|
var obj = this.clone();
|
|
obj._settings = obj._settings || {};
|
|
obj._settings.raw = isRaw === undefined ? true : isRaw;
|
|
return obj;
|
|
};
|
|
|
|
|
|
internals.Any.prototype._allow = function () {
|
|
|
|
var values = Hoek.flatten(Array.prototype.slice.call(arguments));
|
|
for (var i = 0, il = values.length; i < il; ++i) {
|
|
var value = values[i];
|
|
|
|
Hoek.assert(value !== undefined, 'Cannot call allow/valid/invalid with undefined');
|
|
this._invalids.remove(value);
|
|
this._valids.add(value, this._refs);
|
|
}
|
|
};
|
|
|
|
|
|
internals.Any.prototype.allow = function () {
|
|
|
|
var obj = this.clone();
|
|
obj._allow.apply(obj, arguments);
|
|
return obj;
|
|
};
|
|
|
|
|
|
internals.Any.prototype.valid = internals.Any.prototype.only = internals.Any.prototype.equal = function () {
|
|
|
|
Hoek.assert(!this._tests.length, 'Cannot set valid values when rules specified');
|
|
|
|
var obj = this.allow.apply(this, arguments);
|
|
obj._flags.allowOnly = true;
|
|
return obj;
|
|
};
|
|
|
|
|
|
internals.Any.prototype.invalid = internals.Any.prototype.disallow = internals.Any.prototype.not = function (value) {
|
|
|
|
var obj = this.clone();
|
|
var values = Hoek.flatten(Array.prototype.slice.call(arguments));
|
|
for (var i = 0, il = values.length; i < il; ++i) {
|
|
value = values[i];
|
|
|
|
Hoek.assert(value !== undefined, 'Cannot call allow/valid/invalid with undefined');
|
|
obj._valids.remove(value);
|
|
obj._invalids.add(value, this._refs);
|
|
}
|
|
|
|
return obj;
|
|
};
|
|
|
|
|
|
internals.Any.prototype.required = internals.Any.prototype.exist = function () {
|
|
|
|
var obj = this.clone();
|
|
obj._flags.presence = 'required';
|
|
return obj;
|
|
};
|
|
|
|
|
|
internals.Any.prototype.optional = function () {
|
|
|
|
var obj = this.clone();
|
|
obj._flags.presence = 'optional';
|
|
return obj;
|
|
};
|
|
|
|
|
|
internals.Any.prototype.forbidden = function () {
|
|
|
|
var obj = this.clone();
|
|
obj._flags.presence = 'forbidden';
|
|
return obj;
|
|
};
|
|
|
|
|
|
internals.Any.prototype.strip = function () {
|
|
|
|
var obj = this.clone();
|
|
obj._flags.strip = true;
|
|
return obj;
|
|
};
|
|
|
|
|
|
internals.Any.prototype.applyFunctionToChildren = function (children, fn, args, root) {
|
|
|
|
children = [].concat(children);
|
|
|
|
if (children.length !== 1 || children[0] !== '') {
|
|
root = root ? (root + '.') : '';
|
|
|
|
var extraChildren = (children[0] === '' ? children.slice(1) : children).map(function (child) {
|
|
|
|
return root + child;
|
|
});
|
|
|
|
throw new Error('unknown key(s) ' + extraChildren.join(', '));
|
|
}
|
|
|
|
return this[fn].apply(this, args);
|
|
};
|
|
|
|
|
|
internals.Any.prototype.default = function (value, description) {
|
|
|
|
if (typeof value === 'function' &&
|
|
!Ref.isRef(value)) {
|
|
|
|
if (!value.description &&
|
|
description) {
|
|
|
|
value.description = description;
|
|
}
|
|
|
|
if (!this._flags.func) {
|
|
Hoek.assert(typeof value.description === 'string' && value.description.length > 0, 'description must be provided when default value is a function');
|
|
}
|
|
}
|
|
|
|
var obj = this.clone();
|
|
obj._flags.default = value;
|
|
Ref.push(obj._refs, value);
|
|
return obj;
|
|
};
|
|
|
|
|
|
internals.Any.prototype.empty = function (schema) {
|
|
|
|
var obj;
|
|
if (schema === undefined) {
|
|
obj = this.clone();
|
|
obj._flags.empty = undefined;
|
|
}
|
|
else {
|
|
schema = Cast.schema(schema);
|
|
|
|
obj = this.clone();
|
|
obj._flags.empty = schema;
|
|
}
|
|
|
|
return obj;
|
|
};
|
|
|
|
|
|
internals.Any.prototype.when = function (ref, options) {
|
|
|
|
Hoek.assert(options && typeof options === 'object', 'Invalid options');
|
|
Hoek.assert(options.then !== undefined || options.otherwise !== undefined, 'options must have at least one of "then" or "otherwise"');
|
|
|
|
var then = options.then ? this.concat(Cast.schema(options.then)) : this;
|
|
var otherwise = options.otherwise ? this.concat(Cast.schema(options.otherwise)) : this;
|
|
|
|
Alternatives = Alternatives || require('./alternatives');
|
|
var obj = Alternatives.when(ref, { is: options.is, then: then, otherwise: otherwise });
|
|
obj._flags.presence = 'ignore';
|
|
return obj;
|
|
};
|
|
|
|
|
|
internals.Any.prototype.description = function (desc) {
|
|
|
|
Hoek.assert(desc && typeof desc === 'string', 'Description must be a non-empty string');
|
|
|
|
var obj = this.clone();
|
|
obj._description = desc;
|
|
return obj;
|
|
};
|
|
|
|
|
|
internals.Any.prototype.notes = function (notes) {
|
|
|
|
Hoek.assert(notes && (typeof notes === 'string' || Array.isArray(notes)), 'Notes must be a non-empty string or array');
|
|
|
|
var obj = this.clone();
|
|
obj._notes = obj._notes.concat(notes);
|
|
return obj;
|
|
};
|
|
|
|
|
|
internals.Any.prototype.tags = function (tags) {
|
|
|
|
Hoek.assert(tags && (typeof tags === 'string' || Array.isArray(tags)), 'Tags must be a non-empty string or array');
|
|
|
|
var obj = this.clone();
|
|
obj._tags = obj._tags.concat(tags);
|
|
return obj;
|
|
};
|
|
|
|
internals.Any.prototype.meta = function (meta) {
|
|
|
|
Hoek.assert(meta !== undefined, 'Meta cannot be undefined');
|
|
|
|
var obj = this.clone();
|
|
obj._meta = obj._meta.concat(meta);
|
|
return obj;
|
|
};
|
|
|
|
|
|
internals.Any.prototype.example = function (value) {
|
|
|
|
Hoek.assert(arguments.length, 'Missing example');
|
|
var result = this._validate(value, null, internals.defaults);
|
|
Hoek.assert(!result.errors, 'Bad example:', result.errors && Errors.process(result.errors, value));
|
|
|
|
var obj = this.clone();
|
|
obj._examples = obj._examples.concat(value);
|
|
return obj;
|
|
};
|
|
|
|
|
|
internals.Any.prototype.unit = function (name) {
|
|
|
|
Hoek.assert(name && typeof name === 'string', 'Unit name must be a non-empty string');
|
|
|
|
var obj = this.clone();
|
|
obj._unit = name;
|
|
return obj;
|
|
};
|
|
|
|
|
|
internals._try = function (fn, arg) {
|
|
|
|
var err;
|
|
var result;
|
|
|
|
try {
|
|
result = fn.call(null, arg);
|
|
} catch (e) {
|
|
err = e;
|
|
}
|
|
|
|
return {
|
|
value: result,
|
|
error: err
|
|
};
|
|
};
|
|
|
|
|
|
internals.Any.prototype._validate = function (value, state, options, reference) {
|
|
|
|
var self = this;
|
|
var originalValue = value;
|
|
|
|
// Setup state and settings
|
|
|
|
state = state || { key: '', path: '', parent: null, reference: reference };
|
|
|
|
if (this._settings) {
|
|
options = internals.concatSettings(options, this._settings);
|
|
}
|
|
|
|
var errors = [];
|
|
var finish = function () {
|
|
|
|
var finalValue;
|
|
|
|
if (!self._flags.strip) {
|
|
if (value !== undefined) {
|
|
finalValue = options.raw ? originalValue : value;
|
|
}
|
|
else if (options.noDefaults) {
|
|
finalValue = originalValue;
|
|
}
|
|
else if (Ref.isRef(self._flags.default)) {
|
|
finalValue = self._flags.default(state.parent, options);
|
|
}
|
|
else if (typeof self._flags.default === 'function' &&
|
|
!(self._flags.func && !self._flags.default.description)) {
|
|
|
|
var arg;
|
|
|
|
if (state.parent !== null &&
|
|
self._flags.default.length > 0) {
|
|
|
|
arg = Hoek.clone(state.parent);
|
|
}
|
|
|
|
var defaultValue = internals._try(self._flags.default, arg);
|
|
finalValue = defaultValue.value;
|
|
if (defaultValue.error) {
|
|
errors.push(Errors.create('any.default', defaultValue.error, state, options));
|
|
}
|
|
}
|
|
else {
|
|
finalValue = Hoek.clone(self._flags.default);
|
|
}
|
|
}
|
|
|
|
return {
|
|
value: finalValue,
|
|
errors: errors.length ? errors : null
|
|
};
|
|
};
|
|
|
|
// Check presence requirements
|
|
|
|
var presence = this._flags.presence || options.presence;
|
|
if (presence === 'optional') {
|
|
if (value === undefined) {
|
|
var isDeepDefault = this._flags.hasOwnProperty('default') && this._flags.default === undefined;
|
|
if (isDeepDefault && this._type === 'object') {
|
|
value = {};
|
|
}
|
|
else {
|
|
return finish();
|
|
}
|
|
}
|
|
}
|
|
else if (presence === 'required' &&
|
|
value === undefined) {
|
|
|
|
errors.push(Errors.create('any.required', null, state, options));
|
|
return finish();
|
|
}
|
|
else if (presence === 'forbidden') {
|
|
if (value === undefined) {
|
|
return finish();
|
|
}
|
|
|
|
errors.push(Errors.create('any.unknown', null, state, options));
|
|
return finish();
|
|
}
|
|
|
|
if (this._flags.empty && !this._flags.empty._validate(value, null, internals.defaults).errors) {
|
|
value = undefined;
|
|
return finish();
|
|
}
|
|
|
|
// Check allowed and denied values using the original value
|
|
|
|
if (this._valids.has(value, state, options, this._flags.insensitive)) {
|
|
return finish();
|
|
}
|
|
|
|
if (this._invalids.has(value, state, options, this._flags.insensitive)) {
|
|
errors.push(Errors.create(value === '' ? 'any.empty' : 'any.invalid', null, state, options));
|
|
if (options.abortEarly ||
|
|
value === undefined) { // No reason to keep validating missing value
|
|
|
|
return finish();
|
|
}
|
|
}
|
|
|
|
// Convert value and validate type
|
|
|
|
if (this._base) {
|
|
var base = this._base.call(this, value, state, options);
|
|
if (base.errors) {
|
|
value = base.value;
|
|
errors = errors.concat(base.errors);
|
|
return finish(); // Base error always aborts early
|
|
}
|
|
|
|
if (base.value !== value) {
|
|
value = base.value;
|
|
|
|
// Check allowed and denied values using the converted value
|
|
|
|
if (this._valids.has(value, state, options, this._flags.insensitive)) {
|
|
return finish();
|
|
}
|
|
|
|
if (this._invalids.has(value, state, options, this._flags.insensitive)) {
|
|
errors.push(Errors.create('any.invalid', null, state, options));
|
|
if (options.abortEarly) {
|
|
return finish();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Required values did not match
|
|
|
|
if (this._flags.allowOnly) {
|
|
errors.push(Errors.create('any.allowOnly', { valids: this._valids.values({ stripUndefined: true }) }, state, options));
|
|
if (options.abortEarly) {
|
|
return finish();
|
|
}
|
|
}
|
|
|
|
// Helper.validate tests
|
|
|
|
for (var i = 0, il = this._tests.length; i < il; ++i) {
|
|
var test = this._tests[i];
|
|
var err = test.func.call(this, value, state, options);
|
|
if (err) {
|
|
errors.push(err);
|
|
if (options.abortEarly) {
|
|
return finish();
|
|
}
|
|
}
|
|
}
|
|
|
|
return finish();
|
|
};
|
|
|
|
|
|
internals.Any.prototype._validateWithOptions = function (value, options, callback) {
|
|
|
|
if (options) {
|
|
internals.checkOptions(options);
|
|
}
|
|
|
|
var settings = internals.concatSettings(internals.defaults, options);
|
|
var result = this._validate(value, null, settings);
|
|
var errors = Errors.process(result.errors, value);
|
|
|
|
if (callback) {
|
|
return callback(errors, result.value);
|
|
}
|
|
|
|
return { error: errors, value: result.value };
|
|
};
|
|
|
|
|
|
internals.Any.prototype.validate = function (value, callback) {
|
|
|
|
var result = this._validate(value, null, internals.defaults);
|
|
var errors = Errors.process(result.errors, value);
|
|
|
|
if (callback) {
|
|
return callback(errors, result.value);
|
|
}
|
|
|
|
return { error: errors, value: result.value };
|
|
};
|
|
|
|
|
|
internals.Any.prototype.describe = function () {
|
|
|
|
var description = {
|
|
type: this._type
|
|
};
|
|
|
|
var flags = Object.keys(this._flags);
|
|
if (flags.length) {
|
|
if (this._flags.empty) {
|
|
description.flags = {};
|
|
for (var f = 0, fl = flags.length; f < fl; ++f) {
|
|
var flag = flags[f];
|
|
description.flags[flag] = flag === 'empty' ? this._flags[flag].describe() : this._flags[flag];
|
|
}
|
|
}
|
|
else {
|
|
description.flags = this._flags;
|
|
}
|
|
}
|
|
|
|
if (this._description) {
|
|
description.description = this._description;
|
|
}
|
|
|
|
if (this._notes.length) {
|
|
description.notes = this._notes;
|
|
}
|
|
|
|
if (this._tags.length) {
|
|
description.tags = this._tags;
|
|
}
|
|
|
|
if (this._meta.length) {
|
|
description.meta = this._meta;
|
|
}
|
|
|
|
if (this._examples.length) {
|
|
description.examples = this._examples;
|
|
}
|
|
|
|
if (this._unit) {
|
|
description.unit = this._unit;
|
|
}
|
|
|
|
var valids = this._valids.values();
|
|
if (valids.length) {
|
|
description.valids = valids;
|
|
}
|
|
|
|
var invalids = this._invalids.values();
|
|
if (invalids.length) {
|
|
description.invalids = invalids;
|
|
}
|
|
|
|
description.rules = [];
|
|
|
|
for (var i = 0, il = this._tests.length; i < il; ++i) {
|
|
var validator = this._tests[i];
|
|
var item = { name: validator.name };
|
|
if (validator.arg !== void 0) {
|
|
item.arg = validator.arg;
|
|
}
|
|
description.rules.push(item);
|
|
}
|
|
|
|
if (!description.rules.length) {
|
|
delete description.rules;
|
|
}
|
|
|
|
var label = Hoek.reach(this._settings, 'language.label');
|
|
if (label) {
|
|
description.label = label;
|
|
}
|
|
|
|
return description;
|
|
};
|
|
|
|
internals.Any.prototype.label = function (name) {
|
|
|
|
Hoek.assert(name && typeof name === 'string', 'Label name must be a non-empty string');
|
|
|
|
var obj = this.clone();
|
|
var options = { language: { label: name } };
|
|
|
|
// If language.label is set, it should override this label
|
|
obj._settings = internals.concatSettings(options, obj._settings);
|
|
return obj;
|
|
};
|
|
|
|
|
|
// Set
|
|
|
|
internals.Set = function () {
|
|
|
|
this._set = [];
|
|
};
|
|
|
|
|
|
internals.Set.prototype.add = function (value, refs) {
|
|
|
|
Hoek.assert(value === null || value === undefined || value instanceof Date || Buffer.isBuffer(value) || Ref.isRef(value) || (typeof value !== 'function' && typeof value !== 'object'), 'Value cannot be an object or function');
|
|
|
|
if (typeof value !== 'function' &&
|
|
this.has(value, null, null, false)) {
|
|
|
|
return;
|
|
}
|
|
|
|
Ref.push(refs, value);
|
|
this._set.push(value);
|
|
};
|
|
|
|
|
|
internals.Set.prototype.merge = function (add, remove) {
|
|
|
|
for (var i = 0, il = add._set.length; i < il; ++i) {
|
|
this.add(add._set[i]);
|
|
}
|
|
|
|
for (i = 0, il = remove._set.length; i < il; ++i) {
|
|
this.remove(remove._set[i]);
|
|
}
|
|
};
|
|
|
|
|
|
internals.Set.prototype.remove = function (value) {
|
|
|
|
this._set = this._set.filter(function (item) {
|
|
|
|
return value !== item;
|
|
});
|
|
};
|
|
|
|
|
|
internals.Set.prototype.has = function (value, state, options, insensitive) {
|
|
|
|
for (var i = 0, il = this._set.length; i < il; ++i) {
|
|
var items = this._set[i];
|
|
|
|
if (Ref.isRef(items)) {
|
|
items = items(state.reference || state.parent, options);
|
|
}
|
|
|
|
if (!Array.isArray(items)) {
|
|
items = [items];
|
|
}
|
|
|
|
for (var j = 0, jl = items.length; j < jl; ++j) {
|
|
var item = items[j];
|
|
if (typeof value !== typeof item) {
|
|
continue;
|
|
}
|
|
|
|
if (value === item ||
|
|
(value instanceof Date && item instanceof Date && value.getTime() === item.getTime()) ||
|
|
(insensitive && typeof value === 'string' && value.toLowerCase() === item.toLowerCase()) ||
|
|
(Buffer.isBuffer(value) && Buffer.isBuffer(item) && value.length === item.length && value.toString('binary') === item.toString('binary'))) {
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
|
|
internals.Set.prototype.values = function (options) {
|
|
|
|
if (options && options.stripUndefined) {
|
|
var values = [];
|
|
|
|
for (var i = 0, il = this._set.length; i < il; ++i) {
|
|
var item = this._set[i];
|
|
if (item !== undefined) {
|
|
values.push(item);
|
|
}
|
|
}
|
|
|
|
return values;
|
|
}
|
|
|
|
return this._set.slice();
|
|
};
|
|
|
|
|
|
internals.concatSettings = function (target, source) {
|
|
|
|
// Used to avoid cloning context
|
|
|
|
if (!target &&
|
|
!source) {
|
|
|
|
return null;
|
|
}
|
|
|
|
var key, obj = {};
|
|
|
|
if (target) {
|
|
var tKeys = Object.keys(target);
|
|
for (var i = 0, il = tKeys.length; i < il; ++i) {
|
|
key = tKeys[i];
|
|
obj[key] = target[key];
|
|
}
|
|
}
|
|
|
|
if (source) {
|
|
var sKeys = Object.keys(source);
|
|
for (var j = 0, jl = sKeys.length; j < jl; ++j) {
|
|
key = sKeys[j];
|
|
if (key !== 'language' ||
|
|
!obj.hasOwnProperty(key)) {
|
|
|
|
obj[key] = source[key];
|
|
}
|
|
else {
|
|
obj[key] = Hoek.applyToDefaults(obj[key], source[key]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return obj;
|
|
};
|