You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
125 lines
2.5 KiB
125 lines
2.5 KiB
'use strict'; |
|
|
|
function undefsafe(obj, path, value, __res) { |
|
// I'm not super keen on this private function, but it's because |
|
// it'll also be use in the browser and I wont *one* function exposed |
|
function split(path) { |
|
var res = []; |
|
var level = 0; |
|
var key = ''; |
|
|
|
for (var i = 0; i < path.length; i++) { |
|
var c = path.substr(i, 1); |
|
|
|
if (level === 0 && (c === '.' || c === '[')) { |
|
if (c === '[') { |
|
level++; |
|
i++; |
|
c = path.substr(i, 1); |
|
} |
|
|
|
if (key) { |
|
// the first value could be a string |
|
res.push(key); |
|
} |
|
key = ''; |
|
continue; |
|
} |
|
|
|
if (c === ']') { |
|
level--; |
|
key = key.slice(0, -1); |
|
continue; |
|
} |
|
|
|
key += c; |
|
} |
|
|
|
res.push(key); |
|
|
|
return res; |
|
} |
|
|
|
// bail if there's nothing |
|
if (obj === undefined || obj === null) { |
|
return undefined; |
|
} |
|
|
|
var parts = split(path); |
|
var key = null; |
|
var type = typeof obj; |
|
var root = obj; |
|
var parent = obj; |
|
|
|
var star = |
|
parts.filter(function(_) { |
|
return _ === '*'; |
|
}).length > 0; |
|
|
|
// we're dealing with a primitive |
|
if (type !== 'object' && type !== 'function') { |
|
return obj; |
|
} else if (path.trim() === '') { |
|
return obj; |
|
} |
|
|
|
key = parts[0]; |
|
var i = 0; |
|
for (; i < parts.length; i++) { |
|
key = parts[i]; |
|
parent = obj; |
|
|
|
if (key === '*') { |
|
// loop through each property |
|
var prop = ''; |
|
var res = __res || []; |
|
|
|
for (prop in parent) { |
|
var shallowObj = undefsafe( |
|
obj[prop], |
|
parts.slice(i + 1).join('.'), |
|
value, |
|
res |
|
); |
|
if (shallowObj && shallowObj !== res) { |
|
if ((value && shallowObj === value) || value === undefined) { |
|
if (value !== undefined) { |
|
return shallowObj; |
|
} |
|
|
|
res.push(shallowObj); |
|
} |
|
} |
|
} |
|
|
|
if (res.length === 0) { |
|
return undefined; |
|
} |
|
|
|
return res; |
|
} |
|
|
|
if (Object.getOwnPropertyNames(obj).indexOf(key) == -1) { |
|
return undefined; |
|
} |
|
|
|
obj = obj[key]; |
|
if (obj === undefined || obj === null) { |
|
break; |
|
} |
|
} |
|
|
|
// if we have a null object, make sure it's the one the user was after, |
|
// if it's not (i.e. parts has a length) then give undefined back. |
|
if (obj === null && i !== parts.length - 1) { |
|
obj = undefined; |
|
} else if (!star && value) { |
|
key = path.split('.').pop(); |
|
parent[key] = value; |
|
} |
|
return obj; |
|
} |
|
|
|
if (typeof module !== 'undefined') { |
|
module.exports = undefsafe; |
|
}
|
|
|