github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/public/libs/vue-1.0.24/src/parsers/path.js (about)

     1  import { parseExpression } from './expression'
     2  import {
     3    isLiteral,
     4    stripQuotes,
     5    isObject,
     6    isArray,
     7    warn,
     8    set
     9  } from '../util/index'
    10  import Cache from '../cache'
    11  
    12  var pathCache = new Cache(1000)
    13  
    14  // actions
    15  var APPEND = 0
    16  var PUSH = 1
    17  var INC_SUB_PATH_DEPTH = 2
    18  var PUSH_SUB_PATH = 3
    19  
    20  // states
    21  var BEFORE_PATH = 0
    22  var IN_PATH = 1
    23  var BEFORE_IDENT = 2
    24  var IN_IDENT = 3
    25  var IN_SUB_PATH = 4
    26  var IN_SINGLE_QUOTE = 5
    27  var IN_DOUBLE_QUOTE = 6
    28  var AFTER_PATH = 7
    29  var ERROR = 8
    30  
    31  var pathStateMachine = []
    32  
    33  pathStateMachine[BEFORE_PATH] = {
    34    'ws': [BEFORE_PATH],
    35    'ident': [IN_IDENT, APPEND],
    36    '[': [IN_SUB_PATH],
    37    'eof': [AFTER_PATH]
    38  }
    39  
    40  pathStateMachine[IN_PATH] = {
    41    'ws': [IN_PATH],
    42    '.': [BEFORE_IDENT],
    43    '[': [IN_SUB_PATH],
    44    'eof': [AFTER_PATH]
    45  }
    46  
    47  pathStateMachine[BEFORE_IDENT] = {
    48    'ws': [BEFORE_IDENT],
    49    'ident': [IN_IDENT, APPEND]
    50  }
    51  
    52  pathStateMachine[IN_IDENT] = {
    53    'ident': [IN_IDENT, APPEND],
    54    '0': [IN_IDENT, APPEND],
    55    'number': [IN_IDENT, APPEND],
    56    'ws': [IN_PATH, PUSH],
    57    '.': [BEFORE_IDENT, PUSH],
    58    '[': [IN_SUB_PATH, PUSH],
    59    'eof': [AFTER_PATH, PUSH]
    60  }
    61  
    62  pathStateMachine[IN_SUB_PATH] = {
    63    "'": [IN_SINGLE_QUOTE, APPEND],
    64    '"': [IN_DOUBLE_QUOTE, APPEND],
    65    '[': [IN_SUB_PATH, INC_SUB_PATH_DEPTH],
    66    ']': [IN_PATH, PUSH_SUB_PATH],
    67    'eof': ERROR,
    68    'else': [IN_SUB_PATH, APPEND]
    69  }
    70  
    71  pathStateMachine[IN_SINGLE_QUOTE] = {
    72    "'": [IN_SUB_PATH, APPEND],
    73    'eof': ERROR,
    74    'else': [IN_SINGLE_QUOTE, APPEND]
    75  }
    76  
    77  pathStateMachine[IN_DOUBLE_QUOTE] = {
    78    '"': [IN_SUB_PATH, APPEND],
    79    'eof': ERROR,
    80    'else': [IN_DOUBLE_QUOTE, APPEND]
    81  }
    82  
    83  /**
    84   * Determine the type of a character in a keypath.
    85   *
    86   * @param {Char} ch
    87   * @return {String} type
    88   */
    89  
    90  function getPathCharType (ch) {
    91    if (ch === undefined) {
    92      return 'eof'
    93    }
    94  
    95    var code = ch.charCodeAt(0)
    96  
    97    switch (code) {
    98      case 0x5B: // [
    99      case 0x5D: // ]
   100      case 0x2E: // .
   101      case 0x22: // "
   102      case 0x27: // '
   103      case 0x30: // 0
   104        return ch
   105  
   106      case 0x5F: // _
   107      case 0x24: // $
   108        return 'ident'
   109  
   110      case 0x20: // Space
   111      case 0x09: // Tab
   112      case 0x0A: // Newline
   113      case 0x0D: // Return
   114      case 0xA0:  // No-break space
   115      case 0xFEFF:  // Byte Order Mark
   116      case 0x2028:  // Line Separator
   117      case 0x2029:  // Paragraph Separator
   118        return 'ws'
   119    }
   120  
   121    // a-z, A-Z
   122    if (
   123      (code >= 0x61 && code <= 0x7A) ||
   124      (code >= 0x41 && code <= 0x5A)
   125    ) {
   126      return 'ident'
   127    }
   128  
   129    // 1-9
   130    if (code >= 0x31 && code <= 0x39) {
   131      return 'number'
   132    }
   133  
   134    return 'else'
   135  }
   136  
   137  /**
   138   * Format a subPath, return its plain form if it is
   139   * a literal string or number. Otherwise prepend the
   140   * dynamic indicator (*).
   141   *
   142   * @param {String} path
   143   * @return {String}
   144   */
   145  
   146  function formatSubPath (path) {
   147    var trimmed = path.trim()
   148    // invalid leading 0
   149    if (path.charAt(0) === '0' && isNaN(path)) {
   150      return false
   151    }
   152    return isLiteral(trimmed)
   153      ? stripQuotes(trimmed)
   154      : '*' + trimmed
   155  }
   156  
   157  /**
   158   * Parse a string path into an array of segments
   159   *
   160   * @param {String} path
   161   * @return {Array|undefined}
   162   */
   163  
   164  function parse (path) {
   165    var keys = []
   166    var index = -1
   167    var mode = BEFORE_PATH
   168    var subPathDepth = 0
   169    var c, newChar, key, type, transition, action, typeMap
   170  
   171    var actions = []
   172  
   173    actions[PUSH] = function () {
   174      if (key !== undefined) {
   175        keys.push(key)
   176        key = undefined
   177      }
   178    }
   179  
   180    actions[APPEND] = function () {
   181      if (key === undefined) {
   182        key = newChar
   183      } else {
   184        key += newChar
   185      }
   186    }
   187  
   188    actions[INC_SUB_PATH_DEPTH] = function () {
   189      actions[APPEND]()
   190      subPathDepth++
   191    }
   192  
   193    actions[PUSH_SUB_PATH] = function () {
   194      if (subPathDepth > 0) {
   195        subPathDepth--
   196        mode = IN_SUB_PATH
   197        actions[APPEND]()
   198      } else {
   199        subPathDepth = 0
   200        key = formatSubPath(key)
   201        if (key === false) {
   202          return false
   203        } else {
   204          actions[PUSH]()
   205        }
   206      }
   207    }
   208  
   209    function maybeUnescapeQuote () {
   210      var nextChar = path[index + 1]
   211      if ((mode === IN_SINGLE_QUOTE && nextChar === "'") ||
   212          (mode === IN_DOUBLE_QUOTE && nextChar === '"')) {
   213        index++
   214        newChar = '\\' + nextChar
   215        actions[APPEND]()
   216        return true
   217      }
   218    }
   219  
   220    while (mode != null) {
   221      index++
   222      c = path[index]
   223  
   224      if (c === '\\' && maybeUnescapeQuote()) {
   225        continue
   226      }
   227  
   228      type = getPathCharType(c)
   229      typeMap = pathStateMachine[mode]
   230      transition = typeMap[type] || typeMap['else'] || ERROR
   231  
   232      if (transition === ERROR) {
   233        return // parse error
   234      }
   235  
   236      mode = transition[0]
   237      action = actions[transition[1]]
   238      if (action) {
   239        newChar = transition[2]
   240        newChar = newChar === undefined
   241          ? c
   242          : newChar
   243        if (action() === false) {
   244          return
   245        }
   246      }
   247  
   248      if (mode === AFTER_PATH) {
   249        keys.raw = path
   250        return keys
   251      }
   252    }
   253  }
   254  
   255  /**
   256   * External parse that check for a cache hit first
   257   *
   258   * @param {String} path
   259   * @return {Array|undefined}
   260   */
   261  
   262  export function parsePath (path) {
   263    var hit = pathCache.get(path)
   264    if (!hit) {
   265      hit = parse(path)
   266      if (hit) {
   267        pathCache.put(path, hit)
   268      }
   269    }
   270    return hit
   271  }
   272  
   273  /**
   274   * Get from an object from a path string
   275   *
   276   * @param {Object} obj
   277   * @param {String} path
   278   */
   279  
   280  export function getPath (obj, path) {
   281    return parseExpression(path).get(obj)
   282  }
   283  
   284  /**
   285   * Warn against setting non-existent root path on a vm.
   286   */
   287  
   288  var warnNonExistent
   289  if (process.env.NODE_ENV !== 'production') {
   290    warnNonExistent = function (path, vm) {
   291      warn(
   292        'You are setting a non-existent path "' + path.raw + '" ' +
   293        'on a vm instance. Consider pre-initializing the property ' +
   294        'with the "data" option for more reliable reactivity ' +
   295        'and better performance.',
   296        vm
   297      )
   298    }
   299  }
   300  
   301  /**
   302   * Set on an object from a path
   303   *
   304   * @param {Object} obj
   305   * @param {String | Array} path
   306   * @param {*} val
   307   */
   308  
   309  export function setPath (obj, path, val) {
   310    var original = obj
   311    if (typeof path === 'string') {
   312      path = parse(path)
   313    }
   314    if (!path || !isObject(obj)) {
   315      return false
   316    }
   317    var last, key
   318    for (var i = 0, l = path.length; i < l; i++) {
   319      last = obj
   320      key = path[i]
   321      if (key.charAt(0) === '*') {
   322        key = parseExpression(key.slice(1)).get.call(original, original)
   323      }
   324      if (i < l - 1) {
   325        obj = obj[key]
   326        if (!isObject(obj)) {
   327          obj = {}
   328          if (process.env.NODE_ENV !== 'production' && last._isVue) {
   329            warnNonExistent(path, last)
   330          }
   331          set(last, key, obj)
   332        }
   333      } else {
   334        if (isArray(obj)) {
   335          obj.$set(key, val)
   336        } else if (key in obj) {
   337          obj[key] = val
   338        } else {
   339          if (process.env.NODE_ENV !== 'production' && obj._isVue) {
   340            warnNonExistent(path, obj)
   341          }
   342          set(obj, key, val)
   343        }
   344      }
   345    }
   346    return true
   347  }