diff --git a/dist/json-patch-duplex.min.js b/dist/json-patch-duplex.min.js index b5b12af0..57c6ee6c 100644 --- a/dist/json-patch-duplex.min.js +++ b/dist/json-patch-duplex.min.js @@ -1,7 +1,7 @@ /*! -* https://github.com/Starcounter-Jack/JSON-Patch -* json-patch-duplex.js version: 0.5.5 -* (c) 2013 Joachim Wester -* MIT license -*/ -var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},OriginalError=Error,jsonpatch;!function(a){function b(a,c){switch(typeof a){case"undefined":case"boolean":case"string":case"number":return a===c;case"object":if(null===a)return null===c;if(y(a)){if(!y(c)||a.length!==c.length)return!1;for(var d=0,e=a.length;e>d;d++)if(!b(a[d],c[d]))return!1;return!0}var f=r(c),g=f.length;if(r(a).length!==g)return!1;for(var d=0;g>d;d++)if(!b(a[d],c[d]))return!1;return!0;default:return!1}}function c(a){return-1===a.indexOf("/")&&-1===a.indexOf("~")?a:a.replace(/~/g,"~0").replace(/\//g,"~1")}function d(a){for(var b=0,c=v.length;c>b;b++)if(v[b].obj===a)return v[b]}function e(a,b){for(var c=0,d=a.observers.length;d>c;c++)if(a.observers[c].callback===b)return a.observers[c].observer}function f(a,b){for(var c=0,d=a.observers.length;d>c;c++)if(a.observers[c].observer===b)return void a.observers.splice(c,1)}function g(a,b){j(b),clearTimeout(b.next);var c=d(a);f(c,b)}function h(a){return"object"==typeof a?JSON.parse(JSON.stringify(a)):a}function i(a,b){var c,f=[],g=d(a);if(g?c=e(g,b):(g=new w(a),v.push(g)),c)return c;if(c={},g.value=h(a),b){c.callback=b,c.next=null;var i=this.intervals||[100,1e3,1e4,6e4];if(void 0===i.push)throw new OriginalError("jsonpatch.intervals must be an array");var k=0,l=function(){j(c)},m=function(){clearTimeout(c.next),c.next=setTimeout(function(){l(),k=0,c.next=setTimeout(n,i[k++])},0)},n=function(){l(),k==i.length&&(k=i.length-1),c.next=setTimeout(n,i[k++])};"undefined"!=typeof window&&(window.addEventListener?(window.addEventListener("mousedown",m),window.addEventListener("mouseup",m),window.addEventListener("keydown",m)):(document.documentElement.attachEvent("onmousedown",m),document.documentElement.attachEvent("onmouseup",m),document.documentElement.attachEvent("onkeydown",m))),c.next=setTimeout(n,i[k++])}return c.patches=f,c.object=a,g.observers.push(new x(b,c)),c}function j(a){for(var b,c=0,d=v.length;d>c;c++)if(v[c].obj===a.object){b=v[c];break}k(b.value,a.object,a.patches,""),a.patches.length&&m(b.value,a.patches);var e=a.patches;return e.length>0&&(a.patches=[],a.callback&&a.callback(e)),e}function k(a,b,d,e){for(var f=r(b),g=r(a),i=!1,j=!1,l=g.length-1;l>=0;l--){var m=g[l],n=a[m];if(b.hasOwnProperty(m)){var o=b[m];"object"==typeof n&&null!=n&&"object"==typeof o&&null!=o?k(n,o,d,e+"/"+c(m)):n!=o&&(i=!0,d.push({op:"replace",path:e+"/"+c(m),value:h(o)}))}else d.push({op:"remove",path:e+"/"+c(m)}),j=!0}if(j||f.length!=g.length)for(var l=0;lc;){b=a.charCodeAt(c);{if(!(b>=48&&57>=b))return!1;c++}}return!0}function m(a,b,c){for(var d,e,f=!1,g=0,h=b.length;h>g;){d=b[g],g++;for(var i=d.path||"",j=i.split("/"),k=a,m=1,n=j.length,o=void 0;;){if(e=j[m],c&&void 0===o&&(void 0===k[e]?o=j.slice(0,m).join("/"):m==n-1&&(o=d.path),void 0!==o&&this.validator(d,g-1,a,o)),m++,void 0===e&&m>=n){f=u[d.op].call(d,k,e,a);break}if(y(k)){if("-"===e)e=k.length;else{if(c&&!l(e))throw new z("Expected an unsigned base-10 integer value, making the new referenced value the array element with the zero-based index","OPERATION_PATH_ILLEGAL_ARRAY_INDEX",g-1,d.path,d);e=parseInt(e,10)}if(m>=n){if(c&&"add"===d.op&&e>k.length)throw new z("The specified index MUST NOT be greater than the number of elements in the array","OPERATION_VALUE_OUT_OF_BOUNDS",g-1,d.path,d);f=t[d.op].call(d,k,e,a);break}}else if(e&&-1!=e.indexOf("~")&&(e=e.replace(/~1/g,"/").replace(/~0/g,"~")),m>=n){f=s[d.op].call(d,k,e,a);break}k=k[e]}}return f}function n(a,b){var c=[];return k(a,b,c,""),c}function o(a){if(void 0===a)return!0;if("array"==typeof a||"object"==typeof a)for(var b in a)if(o(a[b]))return!0;return!1}function p(b,c,d,e){if("object"!=typeof b||null===b||y(b))throw new z("Operation is not an object","OPERATION_NOT_AN_OBJECT",c,b,d);if(!s[b.op])throw new z("Operation `op` property is not one of operations defined in RFC-6902","OPERATION_OP_INVALID",c,b,d);if("string"!=typeof b.path)throw new z("Operation `path` property is not a string","OPERATION_PATH_INVALID",c,b,d);if(("move"===b.op||"copy"===b.op)&&"string"!=typeof b.from)throw new z("Operation `from` property is not present (applicable in `move` and `copy` operations)","OPERATION_FROM_REQUIRED",c,b,d);if(("add"===b.op||"replace"===b.op||"test"===b.op)&&void 0===b.value)throw new z("Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)","OPERATION_VALUE_REQUIRED",c,b,d);if(("add"===b.op||"replace"===b.op||"test"===b.op)&&o(b.value))throw new z("Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)","OPERATION_VALUE_CANNOT_CONTAIN_UNDEFINED",c,b,d);if(d)if("add"==b.op){var f=b.path.split("/").length,g=e.split("/").length;if(f!==g+1&&f!==g)throw new z("Cannot perform an `add` operation at the desired path","OPERATION_PATH_CANNOT_ADD",c,b,d)}else if("replace"===b.op||"remove"===b.op||"_get"===b.op){if(b.path!==e)throw new z("Cannot perform the operation at a path that does not exist","OPERATION_PATH_UNRESOLVABLE",c,b,d)}else if("move"===b.op||"copy"===b.op){var h={op:"_get",path:b.from,value:void 0},i=a.validate([h],d);if(i&&"OPERATION_PATH_UNRESOLVABLE"===i.name)throw new z("Cannot perform the operation from a path that does not exist","OPERATION_FROM_UNRESOLVABLE",c,b,d)}}function q(a,b){try{if(!y(a))throw new z("Patch sequence must be an array","SEQUENCE_NOT_AN_ARRAY");if(b)b=JSON.parse(JSON.stringify(b)),m.call(this,b,a,!0);else for(var c=0;cd;d++)if(!b(a[d],c[d]))return!1;return!0}var f=r(c),g=f.length;if(r(a).length!==g)return!1;for(var d=0;g>d;d++)if(!b(a[d],c[d]))return!1;return!0;default:return!1}}function c(a){return-1===a.indexOf("/")&&-1===a.indexOf("~")?a:a.replace(/~/g,"~0").replace(/\//g,"~1")}function d(a){for(var b=0,c=v.length;c>b;b++)if(v[b].obj===a)return v[b]}function e(a,b){for(var c=0,d=a.observers.length;d>c;c++)if(a.observers[c].callback===b)return a.observers[c].observer}function f(a,b){for(var c=0,d=a.observers.length;d>c;c++)if(a.observers[c].observer===b)return void a.observers.splice(c,1)}function g(a,b){j(b),clearTimeout(b.next);var c=d(a);f(c,b)}function h(a){return"object"==typeof a?JSON.parse(JSON.stringify(a)):a}function i(a,b){var c,f=[],g=d(a);if(g?c=e(g,b):(g=new w(a),v.push(g)),c)return c;if(c={},g.value=h(a),b){c.callback=b,c.next=null;var i=this.intervals||[100,1e3,1e4,6e4];if(void 0===i.push)throw new OriginalError("jsonpatch.intervals must be an array");var k=0,l=function(){j(c)},m=function(){clearTimeout(c.next),c.next=setTimeout(function(){l(),k=0,c.next=setTimeout(n,i[k++])},0)},n=function(){l(),k==i.length&&(k=i.length-1),c.next=setTimeout(n,i[k++])};"undefined"!=typeof window&&(window.addEventListener?(window.addEventListener("mousedown",m),window.addEventListener("mouseup",m),window.addEventListener("keydown",m)):(document.documentElement.attachEvent("onmousedown",m),document.documentElement.attachEvent("onmouseup",m),document.documentElement.attachEvent("onkeydown",m))),c.next=setTimeout(n,i[k++])}return c.patches=f,c.object=a,g.observers.push(new x(b,c)),c}function j(a){for(var b,c=0,d=v.length;d>c;c++)if(v[c].obj===a.object){b=v[c];break}k(b.value,a.object,a.patches,""),a.patches.length&&m(b.value,a.patches);var e=a.patches;return e.length>0&&(a.patches=[],a.callback&&a.callback(e)),e}function k(a,b,d,e){for(var f=r(b),g=r(a),i=!1,j=!1,l=g.length-1;l>=0;l--){var m=g[l],n=a[m];if(b.hasOwnProperty(m)){var o=b[m];"object"==typeof n&&null!=n&&"object"==typeof o&&null!=o?k(n,o,d,e+"/"+c(m)):n!=o&&(i=!0,d.push({op:"replace",path:e+"/"+c(m),value:h(o)}))}else d.push({op:"remove",path:e+"/"+c(m)}),j=!0}if(j||f.length!=g.length)for(var l=0;lc;){b=a.charCodeAt(c);{if(!(b>=48&&57>=b))return!1;c++}}return!0}function m(a,b,c){for(var d,e,f=!1,g=0,h=b.length;h>g;){d=b[g],g++;for(var i=d.path||"",j=i.split("/"),k=a,m=1,n=j.length,o=void 0;;){if(e=j[m],c&&void 0===o&&(void 0===k[e]?o=j.slice(0,m).join("/"):m==n-1&&(o=d.path),void 0!==o&&this.validator(d,g-1,a,o)),m++,void 0===e&&m>=n){f=u[d.op].call(d,k,e,a);break}if(y(k)){if("-"===e)e=k.length;else{if(c&&!l(e))throw new z("Expected an unsigned base-10 integer value, making the new referenced value the array element with the zero-based index","OPERATION_PATH_ILLEGAL_ARRAY_INDEX",g-1,d.path,d);e=parseInt(e,10)}if(m>=n){if(c&&"add"===d.op&&e>k.length)throw new z("The specified index MUST NOT be greater than the number of elements in the array","OPERATION_VALUE_OUT_OF_BOUNDS",g-1,d.path,d);f=t[d.op].call(d,k,e,a);break}}else if(e&&-1!=e.indexOf("~")&&(e=e.replace(/~1/g,"/").replace(/~0/g,"~")),m>=n){f=s[d.op].call(d,k,e,a);break}k=k[e]}}return f}function n(a,b){var c=[];return k(a,b,c,""),c}function o(a){if(void 0===a)return!0;if("array"==typeof a||"object"==typeof a)for(var b in a)if(o(a[b]))return!0;return!1}function p(b,c,d,e){if("object"!=typeof b||null===b||y(b))throw new z("Operation is not an object","OPERATION_NOT_AN_OBJECT",c,b,d);if(!s[b.op])throw new z("Operation `op` property is not one of operations defined in RFC-6902","OPERATION_OP_INVALID",c,b,d);if("string"!=typeof b.path)throw new z("Operation `path` property is not a string","OPERATION_PATH_INVALID",c,b,d);if(("move"===b.op||"copy"===b.op)&&"string"!=typeof b.from)throw new z("Operation `from` property is not present (applicable in `move` and `copy` operations)","OPERATION_FROM_REQUIRED",c,b,d);if(("add"===b.op||"replace"===b.op||"test"===b.op)&&void 0===b.value)throw new z("Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)","OPERATION_VALUE_REQUIRED",c,b,d);if(("add"===b.op||"replace"===b.op||"test"===b.op)&&o(b.value))throw new z("Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)","OPERATION_VALUE_CANNOT_CONTAIN_UNDEFINED",c,b,d);if(d)if("add"==b.op){var f=b.path.split("/").length,g=e.split("/").length;if(f!==g+1&&f!==g)throw new z("Cannot perform an `add` operation at the desired path","OPERATION_PATH_CANNOT_ADD",c,b,d)}else if("replace"===b.op||"remove"===b.op||"_get"===b.op){if(b.path!==e)throw new z("Cannot perform the operation at a path that does not exist","OPERATION_PATH_UNRESOLVABLE",c,b,d)}else if("move"===b.op||"copy"===b.op){var h={op:"_get",path:b.from,value:void 0},i=a.validate([h],d);if(i&&"OPERATION_PATH_UNRESOLVABLE"===i.name)throw new z("Cannot perform the operation from a path that does not exist","OPERATION_FROM_UNRESOLVABLE",c,b,d)}}function q(a,b){try{if(!y(a))throw new z("Patch sequence must be an array","SEQUENCE_NOT_AN_ARRAY");if(b)b=JSON.parse(JSON.stringify(b)),m.call(this,b,a,!0);else for(var c=0;c 0) { observer.patches = []; @@ -399,14 +365,13 @@ var jsonpatch; return temp; } jsonpatch.generate = generate; - // Dirty check if obj is different from mirror, generate patches and update mirror function _generate(mirror, obj, patches, path) { var newKeys = _objectKeys(obj); var oldKeys = _objectKeys(mirror); var changed = false; var deleted = false; - + //if ever "move" operation is implemented here, make sure this test runs OK: "should not generate the same patch twice (move)" for (var t = oldKeys.length - 1; t >= 0; t--) { var key = oldKeys[t]; var oldVal = mirror[key]; @@ -414,22 +379,22 @@ var jsonpatch; var newVal = obj[key]; if (typeof oldVal == "object" && oldVal != null && typeof newVal == "object" && newVal != null) { _generate(oldVal, newVal, patches, path + "/" + escapePathComponent(key)); - } else { + } + else { if (oldVal != newVal) { changed = true; patches.push({ op: "replace", path: path + "/" + escapePathComponent(key), value: deepClone(newVal) }); } } - } else { + } + else { patches.push({ op: "remove", path: path + "/" + escapePathComponent(key) }); deleted = true; // property has been deleted } } - if (!deleted && newKeys.length == oldKeys.length) { return; } - for (var t = 0; t < newKeys.length; t++) { var key = newKeys[t]; if (!mirror.hasOwnProperty(key)) { @@ -437,16 +402,15 @@ var jsonpatch; } } } - var _isArray; if (Array.isArray) { _isArray = Array.isArray; - } else { + } + else { _isArray = function (obj) { return obj.push && typeof obj.length === 'number'; }; } - //3x faster than cached /^\d+$/.test(str) function isInteger(str) { var i = 0; @@ -462,30 +426,27 @@ var jsonpatch; } return true; } - /// Apply a json-patch operation on an object tree function apply(tree, patches, validate) { var result = false, p = 0, plen = patches.length, patch, key; while (p < plen) { patch = patches[p]; p++; - // Find the object var path = patch.path || ""; var keys = path.split('/'); var obj = tree; - var t = 1; + var t = 1; //skip empty element - http://jsperf.com/to-shift-or-not-to-shift var len = keys.length; var existingPathFragment = undefined; - while (true) { key = keys[t]; - if (validate) { if (existingPathFragment === undefined) { if (obj[key] === undefined) { existingPathFragment = keys.slice(0, t).join('/'); - } else if (t == len - 1) { + } + else if (t == len - 1) { existingPathFragment = patch.path; } if (existingPathFragment !== undefined) { @@ -493,7 +454,6 @@ var jsonpatch; } } } - t++; if (key === undefined) { if (t >= len) { @@ -504,7 +464,8 @@ var jsonpatch; if (_isArray(obj)) { if (key === '-') { key = obj.length; - } else { + } + else { if (validate && !isInteger(key)) { throw new JsonPatchError("Expected an unsigned base-10 integer value, making the new referenced value the array element with the zero-based index", "OPERATION_PATH_ILLEGAL_ARRAY_INDEX", p - 1, patch.path, patch); } @@ -517,7 +478,8 @@ var jsonpatch; result = arrOps[patch.op].call(patch, obj, key, tree); // Apply patch break; } - } else { + } + else { if (key && key.indexOf('~') != -1) key = key.replace(/~1/g, '/').replace(/~0/g, '~'); // escape chars if (t >= len) { @@ -531,14 +493,12 @@ var jsonpatch; return result; } jsonpatch.apply = apply; - function compare(tree1, tree2) { var patches = []; _generate(tree1, tree2, patches, ''); return patches; } jsonpatch.compare = compare; - var JsonPatchError = (function (_super) { __extends(JsonPatchError, _super); function JsonPatchError(message, name, index, operation, tree) { @@ -552,17 +512,14 @@ var jsonpatch; return JsonPatchError; })(OriginalError); jsonpatch.JsonPatchError = JsonPatchError; - jsonpatch.Error = JsonPatchError; - /** - * Recursively checks whether an object has any undefined values inside. - */ + * Recursively checks whether an object has any undefined values inside. + */ function hasUndefined(obj) { if (obj === undefined) { return true; } - if (typeof obj == "array" || typeof obj == "object") { for (var i in obj) { if (hasUndefined(obj[i])) { @@ -570,42 +527,49 @@ var jsonpatch; } } } - return false; } - + jsonpatch.hasUndefined = hasUndefined; /** - * Validates a single operation. Called from `jsonpatch.validate`. Throws `JsonPatchError` in case of an error. - * @param {object} operation - operation object (patch) - * @param {number} index - index of operation in the sequence - * @param {object} [tree] - object where the operation is supposed to be applied - * @param {string} [existingPathFragment] - comes along with `tree` - */ + * Validates a single operation. Called from `jsonpatch.validate`. Throws `JsonPatchError` in case of an error. + * @param {object} operation - operation object (patch) + * @param {number} index - index of operation in the sequence + * @param {object} [tree] - object where the operation is supposed to be applied + * @param {string} [existingPathFragment] - comes along with `tree` + */ function validator(operation, index, tree, existingPathFragment) { if (typeof operation !== 'object' || operation === null || _isArray(operation)) { throw new JsonPatchError('Operation is not an object', 'OPERATION_NOT_AN_OBJECT', index, operation, tree); - } else if (!objOps[operation.op]) { + } + else if (!objOps[operation.op]) { throw new JsonPatchError('Operation `op` property is not one of operations defined in RFC-6902', 'OPERATION_OP_INVALID', index, operation, tree); - } else if (typeof operation.path !== 'string') { + } + else if (typeof operation.path !== 'string') { throw new JsonPatchError('Operation `path` property is not a string', 'OPERATION_PATH_INVALID', index, operation, tree); - } else if ((operation.op === 'move' || operation.op === 'copy') && typeof operation.from !== 'string') { + } + else if ((operation.op === 'move' || operation.op === 'copy') && typeof operation.from !== 'string') { throw new JsonPatchError('Operation `from` property is not present (applicable in `move` and `copy` operations)', 'OPERATION_FROM_REQUIRED', index, operation, tree); - } else if ((operation.op === 'add' || operation.op === 'replace' || operation.op === 'test') && operation.value === undefined) { + } + else if ((operation.op === 'add' || operation.op === 'replace' || operation.op === 'test') && operation.value === undefined) { throw new JsonPatchError('Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)', 'OPERATION_VALUE_REQUIRED', index, operation, tree); - } else if ((operation.op === 'add' || operation.op === 'replace' || operation.op === 'test') && hasUndefined(operation.value)) { + } + else if ((operation.op === 'add' || operation.op === 'replace' || operation.op === 'test') && hasUndefined(operation.value)) { throw new JsonPatchError('Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)', 'OPERATION_VALUE_CANNOT_CONTAIN_UNDEFINED', index, operation, tree); - } else if (tree) { + } + else if (tree) { if (operation.op == "add") { var pathLen = operation.path.split("/").length; var existingPathLen = existingPathFragment.split("/").length; if (pathLen !== existingPathLen + 1 && pathLen !== existingPathLen) { throw new JsonPatchError('Cannot perform an `add` operation at the desired path', 'OPERATION_PATH_CANNOT_ADD', index, operation, tree); } - } else if (operation.op === 'replace' || operation.op === 'remove' || operation.op === '_get') { + } + else if (operation.op === 'replace' || operation.op === 'remove' || operation.op === '_get') { if (operation.path !== existingPathFragment) { throw new JsonPatchError('Cannot perform the operation at a path that does not exist', 'OPERATION_PATH_UNRESOLVABLE', index, operation, tree); } - } else if (operation.op === 'move' || operation.op === 'copy') { + } + else if (operation.op === 'move' || operation.op === 'copy') { var existingValue = { op: "_get", path: operation.from, value: undefined }; var error = jsonpatch.validate([existingValue], tree); if (error && error.name === 'OPERATION_PATH_UNRESOLVABLE') { @@ -615,39 +579,39 @@ var jsonpatch; } } jsonpatch.validator = validator; - /** - * Validates a sequence of operations. If `tree` parameter is provided, the sequence is additionally validated against the object tree. - * If error is encountered, returns a JsonPatchError object - * @param sequence - * @param tree - * @returns {JsonPatchError|undefined} - */ + * Validates a sequence of operations. If `tree` parameter is provided, the sequence is additionally validated against the object tree. + * If error is encountered, returns a JsonPatchError object + * @param sequence + * @param tree + * @returns {JsonPatchError|undefined} + */ function validate(sequence, tree) { - try { + try { if (!_isArray(sequence)) { throw new JsonPatchError('Patch sequence must be an array', 'SEQUENCE_NOT_AN_ARRAY'); } - if (tree) { tree = JSON.parse(JSON.stringify(tree)); //clone tree so that we can safely try applying operations apply.call(this, tree, sequence, true); - } else { + } + else { for (var i = 0; i < sequence.length; i++) { this.validator(sequence[i], i); } } - } catch (e) { + } + catch (e) { if (e instanceof JsonPatchError) { return e; - } else { + } + else { throw e; } } } jsonpatch.validate = validate; })(jsonpatch || (jsonpatch = {})); - if (typeof exports !== "undefined") { exports.apply = jsonpatch.apply; exports.observe = jsonpatch.observe;