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 }