| var hasExcape = /~/ |
| var escapeMatcher = /~[01]/g |
| function escapeReplacer (m) { |
| switch (m) { |
| case '~1': return '/' |
| case '~0': return '~' |
| } |
| throw new Error('Invalid tilde escape: ' + m) |
| } |
| |
| function untilde (str) { |
| if (!hasExcape.test(str)) return str |
| return str.replace(escapeMatcher, escapeReplacer) |
| } |
| |
| function setter (obj, pointer, value) { |
| var part |
| var hasNextPart |
| |
| for (var p = 1, len = pointer.length; p < len;) { |
| part = untilde(pointer[p++]) |
| hasNextPart = len > p |
| |
| if (typeof obj[part] === 'undefined') { |
| // support setting of /- |
| if (Array.isArray(obj) && part === '-') { |
| part = obj.length |
| } |
| |
| // support nested objects/array when setting values |
| if (hasNextPart) { |
| if ((pointer[p] !== '' && pointer[p] < Infinity) || pointer[p] === '-') obj[part] = [] |
| else obj[part] = {} |
| } |
| } |
| |
| if (!hasNextPart) break |
| obj = obj[part] |
| } |
| |
| var oldValue = obj[part] |
| if (value === undefined) delete obj[part] |
| else obj[part] = value |
| return oldValue |
| } |
| |
| function compilePointer (pointer) { |
| if (typeof pointer === 'string') { |
| pointer = pointer.split('/') |
| if (pointer[0] === '') return pointer |
| throw new Error('Invalid JSON pointer.') |
| } else if (Array.isArray(pointer)) { |
| return pointer |
| } |
| |
| throw new Error('Invalid JSON pointer.') |
| } |
| |
| function get (obj, pointer) { |
| if (typeof obj !== 'object') throw new Error('Invalid input object.') |
| pointer = compilePointer(pointer) |
| var len = pointer.length |
| if (len === 1) return obj |
| |
| for (var p = 1; p < len;) { |
| obj = obj[untilde(pointer[p++])] |
| if (len === p) return obj |
| if (typeof obj !== 'object') return undefined |
| } |
| } |
| |
| function set (obj, pointer, value) { |
| if (typeof obj !== 'object') throw new Error('Invalid input object.') |
| pointer = compilePointer(pointer) |
| if (pointer.length === 0) throw new Error('Invalid JSON pointer for set.') |
| return setter(obj, pointer, value) |
| } |
| |
| function compile (pointer) { |
| var compiled = compilePointer(pointer) |
| return { |
| get: function (object) { |
| return get(object, compiled) |
| }, |
| set: function (object, value) { |
| return set(object, compiled, value) |
| } |
| } |
| } |
| |
| exports.get = get |
| exports.set = set |
| exports.compile = compile |