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.
		
		
		
		
			
				
					206 lines
				
				4.3 KiB
			
		
		
			
		
	
	
					206 lines
				
				4.3 KiB
			| 
								 
											4 years ago
										 
									 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * 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 = {}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								Cache.prototype.get = function(key) {
							 | 
						||
| 
								 | 
							
								  return this._values[key]
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								Cache.prototype.set = function(key, value) {
							 | 
						||
| 
								 | 
							
								  this._size >= this._maxSize && this.clear()
							 | 
						||
| 
								 | 
							
								  if (!this._values.hasOwnProperty(key)) {
							 | 
						||
| 
								 | 
							
								    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 contentSecurityPolicy = false,
							 | 
						||
| 
								 | 
							
								  pathCache = new Cache(MAX_CACHE_SIZE),
							 | 
						||
| 
								 | 
							
								  setCache = new Cache(MAX_CACHE_SIZE),
							 | 
						||
| 
								 | 
							
								  getCache = new Cache(MAX_CACHE_SIZE)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								try {
							 | 
						||
| 
								 | 
							
								  new Function('')
							 | 
						||
| 
								 | 
							
								} catch (error) {
							 | 
						||
| 
								 | 
							
								  contentSecurityPolicy = true
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports = {
							 | 
						||
| 
								 | 
							
								  Cache: Cache,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  expr: expr,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  split: split,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  normalizePath: normalizePath,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  setter: contentSecurityPolicy
							 | 
						||
| 
								 | 
							
								    ? function(path) {
							 | 
						||
| 
								 | 
							
								      var parts = normalizePath(path)
							 | 
						||
| 
								 | 
							
								      return function(data, value) {
							 | 
						||
| 
								 | 
							
								        return setterFallback(parts, data, value)
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    : function(path) {
							 | 
						||
| 
								 | 
							
								      return setCache.get(path) || setCache.set(
							 | 
						||
| 
								 | 
							
								        path,
							 | 
						||
| 
								 | 
							
								        new Function(
							 | 
						||
| 
								 | 
							
								          'data, value',
							 | 
						||
| 
								 | 
							
								          expr(path, 'data') + ' = value'
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								      )
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  getter: contentSecurityPolicy
							 | 
						||
| 
								 | 
							
								    ? function(path, safe) {
							 | 
						||
| 
								 | 
							
								      var parts = normalizePath(path)
							 | 
						||
| 
								 | 
							
								      return function(data) {
							 | 
						||
| 
								 | 
							
								        return getterFallback(parts, safe, data)
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    : function(path, safe) {
							 | 
						||
| 
								 | 
							
								      var key = path + '_' + safe
							 | 
						||
| 
								 | 
							
								      return getCache.get(key) || getCache.set(
							 | 
						||
| 
								 | 
							
								        key,
							 | 
						||
| 
								 | 
							
								        new Function('data', 'return ' + expr(path, safe, '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(split(path), cb, thisArg)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function setterFallback(parts, data, value) {
							 | 
						||
| 
								 | 
							
								  var index = 0,
							 | 
						||
| 
								 | 
							
								    len = parts.length
							 | 
						||
| 
								 | 
							
								  while (index < len - 1) {
							 | 
						||
| 
								 | 
							
								    data = data[parts[index++]]
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  data[parts[index]] = value
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function getterFallback(parts, safe, data) {
							 | 
						||
| 
								 | 
							
								  var index = 0,
							 | 
						||
| 
								 | 
							
								    len = parts.length
							 | 
						||
| 
								 | 
							
								  while (index < len) {
							 | 
						||
| 
								 | 
							
								    if (data != null || !safe) {
							 | 
						||
| 
								 | 
							
								      data = data[parts[index++]]
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      return
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return data
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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 expr(expression, safe, param) {
							 | 
						||
| 
								 | 
							
								  expression = expression || ''
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (typeof safe === 'string') {
							 | 
						||
| 
								 | 
							
								    param = safe
							 | 
						||
| 
								 | 
							
								    safe = false
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  param = param || 'data'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (expression && expression.charAt(0) !== '[') expression = '.' + expression
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return safe ? makeSafe(expression, param) : param + expression
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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 makeSafe(path, param) {
							 | 
						||
| 
								 | 
							
								  var result = param,
							 | 
						||
| 
								 | 
							
								    parts = split(path),
							 | 
						||
| 
								 | 
							
								    isLast
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  forEach(parts, function(part, isBracket, isArray, idx, parts) {
							 | 
						||
| 
								 | 
							
								    isLast = idx === parts.length - 1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    part = isBracket || isArray ? '[' + part + ']' : '.' + part
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    result += part + (!isLast ? ' || {})' : ')')
							 | 
						||
| 
								 | 
							
								  })
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return new Array(parts.length + 1).join('(') + result
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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))
							 | 
						||
| 
								 | 
							
								}
							 |