var check = require('./check');
var eachProperty = require('./eachProperty');
var assign = require('./assign');
/**
* Defaults to <var>defaultValue</var> if <var>value</var> looks like
* <var>defaultValue</var>.
*
* @namespace
* @memberof just
* @param {*} value - Any value.
* @param {*} [defaultValue] - A value with a desired type for <var>value</var>.
* If an object literal is given, all the keys of <var>value</var> will <var>default</var>
* to his corresponding key in this object.
* @param {object} opts - Some options.
* @param {boolean} [opts.ignoreDefaultKeys=false] - If `false` and <var>defaultValue</var>
* is an object literal, the default keys will be added to <var>value</var>
* or checked against this function for each repeated key.
* @param {boolean} [opts.checkLooks=true]
* If `true`:
* `[]` will match ONLY with another Array.
* `{}` will match ONLY with another object literal.
* If `false`
* `[]` and `{}` will match with any other object.
* @param {boolean} [opts.checkDeepLooks=true]
* Same as <var>checkLooks</var> but it works with the inner values
* of the objects.
* @param {boolean} [opts.ignoreNull=false]
* If `true`, <var>defaultValue</var>s with null as a value won't be checked
* and any <var>value</var> (except `undefined`) will be allowed.
*
* @example
* just.defaults([1, 2], {a: 1}); // {a: 1}
*
* @example
* just.defaults({}, null); // null: null is not an object literal.
* just.defaults([], null, {'checkLooks': false}); // []: null is an object.
* just.defaults(null, {}); // {}: null is not an object literal.
* just.defaults(null, []); // []: null is not an Array.
*
* @example
* just.defaults(1, NaN); // 1 (NaN is an instance of a Number)
*
* @example
* just.defaults({'a': 1, 'b': 2}, {'a': 'some string'}, {'ignoreDefaultKeys': false}); // {'a': 'some string', 'b': 2}
*
* @example
* just.defaults({'a': 1}, {'b': 2}, {'ignoreDefaultKeys': false}); // {'a': 1, 'b': 2}
* just.defaults({'a': 1}, {'b': 2}, {'ignoreDefaultKeys': true}); // {'a': 1}
*
* @example
* just.defaults(1, null, {'ignoreNull': false}) // null (1 is not an object)
* just.defaults(1, null, {'ignoreNull': true}) // 1
* just.defaults(undefined, null, {'ignoreNull': true}) // null
* just.defaults({a: 1}, {a: null}, {'ignoreNull': true}) // {a: 1}
*
* @returns {value} <var>value</var> if it looks like <var>defaultValue</var> or <var>defaultValue</var> otherwise.
*/
function defaults (value, defaultValue, opts) {
var options = assign({
'ignoreDefaultKeys': false,
'checkLooks': true,
'checkDeepLooks': true,
'ignoreNull': false
}, opts);
if (options.ignoreNull && defaultValue === null && value !== void 0) { return value; }
if (options.checkLooks) {
if (!check(value, defaultValue)) { return defaultValue; }
if (check(value, {}) && options.checkDeepLooks) {
eachProperty(defaultValue, function (v, k) {
if (typeof value[k] !== 'undefined') {
value[k] = defaults(value[k], v, options);
}
else if (!options.ignoreDefaultKeys) {
value[k] = v;
}
});
}
return value;
}
return (typeof value === typeof defaultValue
? value
: defaultValue
);
}
module.exports = defaults;