management/front/dkha-web-sz-main/node_modules/vue-i18n/src/path.js

302 lines
6.1 KiB
JavaScript

/* @flow */
import { isObject } from './util'
/**
* Path parser
* - Inspired:
* Vue.js Path parser
*/
// actions
const APPEND = 0
const PUSH = 1
const INC_SUB_PATH_DEPTH = 2
const PUSH_SUB_PATH = 3
// states
const BEFORE_PATH = 0
const IN_PATH = 1
const BEFORE_IDENT = 2
const IN_IDENT = 3
const IN_SUB_PATH = 4
const IN_SINGLE_QUOTE = 5
const IN_DOUBLE_QUOTE = 6
const AFTER_PATH = 7
const ERROR = 8
const pathStateMachine: any = []
pathStateMachine[BEFORE_PATH] = {
'ws': [BEFORE_PATH],
'ident': [IN_IDENT, APPEND],
'[': [IN_SUB_PATH],
'eof': [AFTER_PATH]
}
pathStateMachine[IN_PATH] = {
'ws': [IN_PATH],
'.': [BEFORE_IDENT],
'[': [IN_SUB_PATH],
'eof': [AFTER_PATH]
}
pathStateMachine[BEFORE_IDENT] = {
'ws': [BEFORE_IDENT],
'ident': [IN_IDENT, APPEND],
'0': [IN_IDENT, APPEND],
'number': [IN_IDENT, APPEND]
}
pathStateMachine[IN_IDENT] = {
'ident': [IN_IDENT, APPEND],
'0': [IN_IDENT, APPEND],
'number': [IN_IDENT, APPEND],
'ws': [IN_PATH, PUSH],
'.': [BEFORE_IDENT, PUSH],
'[': [IN_SUB_PATH, PUSH],
'eof': [AFTER_PATH, PUSH]
}
pathStateMachine[IN_SUB_PATH] = {
"'": [IN_SINGLE_QUOTE, APPEND],
'"': [IN_DOUBLE_QUOTE, APPEND],
'[': [IN_SUB_PATH, INC_SUB_PATH_DEPTH],
']': [IN_PATH, PUSH_SUB_PATH],
'eof': ERROR,
'else': [IN_SUB_PATH, APPEND]
}
pathStateMachine[IN_SINGLE_QUOTE] = {
"'": [IN_SUB_PATH, APPEND],
'eof': ERROR,
'else': [IN_SINGLE_QUOTE, APPEND]
}
pathStateMachine[IN_DOUBLE_QUOTE] = {
'"': [IN_SUB_PATH, APPEND],
'eof': ERROR,
'else': [IN_DOUBLE_QUOTE, APPEND]
}
/**
* Check if an expression is a literal value.
*/
const literalValueRE: RegExp = /^\s?(?:true|false|-?[\d.]+|'[^']*'|"[^"]*")\s?$/
function isLiteral (exp: string): boolean {
return literalValueRE.test(exp)
}
/**
* Strip quotes from a string
*/
function stripQuotes (str: string): string | boolean {
const a: number = str.charCodeAt(0)
const b: number = str.charCodeAt(str.length - 1)
return a === b && (a === 0x22 || a === 0x27)
? str.slice(1, -1)
: str
}
/**
* Determine the type of a character in a keypath.
*/
function getPathCharType (ch: ?string): string {
if (ch === undefined || ch === null) { return 'eof' }
const code: number = ch.charCodeAt(0)
switch (code) {
case 0x5B: // [
case 0x5D: // ]
case 0x2E: // .
case 0x22: // "
case 0x27: // '
return ch
case 0x5F: // _
case 0x24: // $
case 0x2D: // -
return 'ident'
case 0x09: // Tab
case 0x0A: // Newline
case 0x0D: // Return
case 0xA0: // No-break space
case 0xFEFF: // Byte Order Mark
case 0x2028: // Line Separator
case 0x2029: // Paragraph Separator
return 'ws'
}
return 'ident'
}
/**
* Format a subPath, return its plain form if it is
* a literal string or number. Otherwise prepend the
* dynamic indicator (*).
*/
function formatSubPath (path: string): boolean | string {
const trimmed: string = path.trim()
// invalid leading 0
if (path.charAt(0) === '0' && isNaN(path)) { return false }
return isLiteral(trimmed) ? stripQuotes(trimmed) : '*' + trimmed
}
/**
* Parse a string path into an array of segments
*/
function parse (path: Path): ?Array<string> {
const keys: Array<string> = []
let index: number = -1
let mode: number = BEFORE_PATH
let subPathDepth: number = 0
let c: ?string
let key: any
let newChar: any
let type: string
let transition: number
let action: Function
let typeMap: any
const actions: Array<Function> = []
actions[PUSH] = function () {
if (key !== undefined) {
keys.push(key)
key = undefined
}
}
actions[APPEND] = function () {
if (key === undefined) {
key = newChar
} else {
key += newChar
}
}
actions[INC_SUB_PATH_DEPTH] = function () {
actions[APPEND]()
subPathDepth++
}
actions[PUSH_SUB_PATH] = function () {
if (subPathDepth > 0) {
subPathDepth--
mode = IN_SUB_PATH
actions[APPEND]()
} else {
subPathDepth = 0
key = formatSubPath(key)
if (key === false) {
return false
} else {
actions[PUSH]()
}
}
}
function maybeUnescapeQuote (): ?boolean {
const nextChar: string = path[index + 1]
if ((mode === IN_SINGLE_QUOTE && nextChar === "'") ||
(mode === IN_DOUBLE_QUOTE && nextChar === '"')) {
index++
newChar = '\\' + nextChar
actions[APPEND]()
return true
}
}
while (mode !== null) {
index++
c = path[index]
if (c === '\\' && maybeUnescapeQuote()) {
continue
}
type = getPathCharType(c)
typeMap = pathStateMachine[mode]
transition = typeMap[type] || typeMap['else'] || ERROR
if (transition === ERROR) {
return // parse error
}
mode = transition[0]
action = actions[transition[1]]
if (action) {
newChar = transition[2]
newChar = newChar === undefined
? c
: newChar
if (action() === false) {
return
}
}
if (mode === AFTER_PATH) {
return keys
}
}
}
export type PathValue = PathValueObject | PathValueArray | string | number | boolean | null
export type PathValueObject = { [key: string]: PathValue }
export type PathValueArray = Array<PathValue>
export default class I18nPath {
_cache: Object
constructor () {
this._cache = Object.create(null)
}
/**
* External parse that check for a cache hit first
*/
parsePath (path: Path): Array<string> {
let hit: ?Array<string> = this._cache[path]
if (!hit) {
hit = parse(path)
if (hit) {
this._cache[path] = hit
}
}
return hit || []
}
/**
* Get path value from path string
*/
getPathValue (obj: mixed, path: Path): PathValue {
if (!isObject(obj)) { return null }
const paths: Array<string> = this.parsePath(path)
if (paths.length === 0) {
return null
} else {
const length: number = paths.length
let last: any = obj
let i: number = 0
while (i < length) {
const value: any = last[paths[i]]
if (value === undefined) {
return null
}
last = value
i++
}
return last
}
}
}