You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
159 lines
3.4 KiB
JavaScript
159 lines
3.4 KiB
JavaScript
/**
|
|
* Based on Kendo UI Core expression code <https://github.com/telerik/kendo-ui-core#license-information>
|
|
*/
|
|
'use strict'
|
|
|
|
function Cache(maxSize) {
|
|
this._maxSize = maxSize
|
|
this.clear()
|
|
}
|
|
Cache.prototype.clear = function () {
|
|
this._size = 0
|
|
this._values = Object.create(null)
|
|
}
|
|
Cache.prototype.get = function (key) {
|
|
return this._values[key]
|
|
}
|
|
Cache.prototype.set = function (key, value) {
|
|
this._size >= this._maxSize && this.clear()
|
|
if (!(key in this._values)) this._size++
|
|
|
|
return (this._values[key] = value)
|
|
}
|
|
|
|
var SPLIT_REGEX = /[^.^\]^[]+|(?=\[\]|\.\.)/g,
|
|
DIGIT_REGEX = /^\d+$/,
|
|
LEAD_DIGIT_REGEX = /^\d/,
|
|
SPEC_CHAR_REGEX = /[~`!#$%\^&*+=\-\[\]\\';,/{}|\\":<>\?]/g,
|
|
CLEAN_QUOTES_REGEX = /^\s*(['"]?)(.*?)(\1)\s*$/,
|
|
MAX_CACHE_SIZE = 512
|
|
|
|
var pathCache = new Cache(MAX_CACHE_SIZE),
|
|
setCache = new Cache(MAX_CACHE_SIZE),
|
|
getCache = new Cache(MAX_CACHE_SIZE)
|
|
|
|
var config
|
|
|
|
module.exports = {
|
|
Cache: Cache,
|
|
|
|
split: split,
|
|
|
|
normalizePath: normalizePath,
|
|
|
|
setter: function (path) {
|
|
var parts = normalizePath(path)
|
|
|
|
return (
|
|
setCache.get(path) ||
|
|
setCache.set(path, function setter(obj, value) {
|
|
var index = 0
|
|
var len = parts.length
|
|
var data = obj
|
|
|
|
while (index < len - 1) {
|
|
var part = parts[index]
|
|
if (
|
|
part === '__proto__' ||
|
|
part === 'constructor' ||
|
|
part === 'prototype'
|
|
) {
|
|
return obj
|
|
}
|
|
|
|
data = data[parts[index++]]
|
|
}
|
|
data[parts[index]] = value
|
|
})
|
|
)
|
|
},
|
|
|
|
getter: function (path, safe) {
|
|
var parts = normalizePath(path)
|
|
return (
|
|
getCache.get(path) ||
|
|
getCache.set(path, function getter(data) {
|
|
var index = 0,
|
|
len = parts.length
|
|
while (index < len) {
|
|
if (data != null || !safe) data = data[parts[index++]]
|
|
else return
|
|
}
|
|
return data
|
|
})
|
|
)
|
|
},
|
|
|
|
join: function (segments) {
|
|
return segments.reduce(function (path, part) {
|
|
return (
|
|
path +
|
|
(isQuoted(part) || DIGIT_REGEX.test(part)
|
|
? '[' + part + ']'
|
|
: (path ? '.' : '') + part)
|
|
)
|
|
}, '')
|
|
},
|
|
|
|
forEach: function (path, cb, thisArg) {
|
|
forEach(Array.isArray(path) ? path : split(path), cb, thisArg)
|
|
},
|
|
}
|
|
|
|
function normalizePath(path) {
|
|
return (
|
|
pathCache.get(path) ||
|
|
pathCache.set(
|
|
path,
|
|
split(path).map(function (part) {
|
|
return part.replace(CLEAN_QUOTES_REGEX, '$2')
|
|
})
|
|
)
|
|
)
|
|
}
|
|
|
|
function split(path) {
|
|
return path.match(SPLIT_REGEX) || ['']
|
|
}
|
|
|
|
function forEach(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)
|
|
|
|
iter.call(thisArg, part, isBracket, isArray, idx, parts)
|
|
}
|
|
}
|
|
}
|
|
|
|
function isQuoted(str) {
|
|
return (
|
|
typeof str === 'string' && str && ["'", '"'].indexOf(str.charAt(0)) !== -1
|
|
)
|
|
}
|
|
|
|
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))
|
|
}
|