property-expr-csp
Version:
tiny util for getting and setting deep object props safely
127 lines (108 loc) • 2.87 kB
JavaScript
/**
* Based on Kendo UI Core expression code <https://github.com/telerik/kendo-ui-core#license-information>
* And on the original property-expr package <https://github.com/jquense/expr>
*/
var SPLIT_REGEX = /[^.^\]^[]+|(?=\[\]|\.\.)/g,
DIGIT_REGEX = /^\d+$/,
LEAD_DIGIT_REGEX = /^\d/,
SPEC_CHAR_REGEX = /[~`!#$%\^&*+=\-\[\]\\';,/{}|\\":<>\?]/g,
NO_QUOTES_REGEX = /^\s*(['"])(.*)(\1)\s*$/,
BREAK_FOR_EACH_SYMBOL = {}
module.exports = {
forEach: forEach,
setter: function(path) {
return function(data, value) {
return setter(path, data, value)
}
},
getter: function(path, safe) {
return function(data) {
return getter(path, data, safe)
}
},
split: function(path) {
return path.match(SPLIT_REGEX)
},
join: function(segments) {
return segments.reduce(function(path, part) {
return (
path +
(isQuoted(part) || DIGIT_REGEX.test(part)
? '[' + part + ']'
: (path ? '.' : '') + part)
)
}, '')
}
}
function setter(path, data, value) {
forEach(path, function(part, isBracket, isArray, idx, parts) {
part = normalizePropertyName(part, isBracket, isArray)
if (idx === parts.length - 1) {
data[part] = value
} else {
data = data[part]
}
})
}
function getter(path, data, safe) {
var value
forEach(path, function(part, isBracket, isArray, idx, parts) {
if (safe && !data) {
return BREAK_FOR_EACH_SYMBOL
}
part = normalizePropertyName(part, isBracket, isArray)
data = data[part]
if (idx === parts.length - 1) {
value = data
} else if (safe) {
data = data || {}
}
})
return value
}
function forEach(path, cb, thisArg) {
forEachPart(path.match(SPLIT_REGEX), cb, thisArg)
}
function forEachPart(parts, iter, thisArg) {
var len = parts.length,
part,
idx,
isArray,
isBracket
for (idx = 0; idx < len; idx++) {
part = parts[idx]
if (part) {
if (shouldBeQuoted(part)) {
part = '"' + part + '"'
}
isBracket = isQuoted(part)
isArray = !isBracket && /^\d+$/.test(part)
if (iter.call(thisArg, part, isBracket, isArray, idx, parts) === BREAK_FOR_EACH_SYMBOL) {
break
}
}
}
}
function isQuoted(str) {
return (
typeof str === 'string' && str && ["'", '"'].indexOf(str.charAt(0)) !== -1
)
}
function normalizePropertyName(part, isBracket, isArray) {
if (isBracket && !isArray) {
part = part.replace(NO_QUOTES_REGEX, '$2')
} else {
part = part.trim()
}
return part
}
function hasLeadingNumber(part) {
return part.match(LEAD_DIGIT_REGEX) && !part.match(DIGIT_REGEX)
}
function hasSpecialChars(part) {
return SPEC_CHAR_REGEX.test(part)
}
function shouldBeQuoted(part) {
return !isQuoted(part) && (hasLeadingNumber(part) || hasSpecialChars(part))
}