text: // json-patch-duplex.js 0.3.5
  // (c) 2013 Joachim Wester
  // MIT license
  var jsonpatch;
  (function (jsonpatch) {
      var objOps = {
          add: function (obj, key) {
              obj[key] = this.value;
              return true;
          },
          remove: function (obj, key) {
              delete obj[key];
              return true;
          },
          replace: function (obj, key) {
              obj[key] = this.value;
              return true;
          },
          move: function (obj, key, tree) {
              var temp = { op: "_get", path: this.from };
              apply(tree, [temp]);
              apply(tree, [
                  { op: "remove", path: this.from }
              ]);
              apply(tree, [
                  { op: "add", path: this.path, value: temp.value }
              ]);
              return true;
          },
          copy: function (obj, key, tree) {
              var temp = { op: "_get", path: this.from };
              apply(tree, [temp]);
              apply(tree, [
                  { op: "add", path: this.path, value: temp.value }
              ]);
              return true;
          },
          test: function (obj, key) {
              return (JSON.stringify(obj[key]) === JSON.stringify(this.value));
          },
          _get: function (obj, key) {
              this.value = obj[key];
          }
      };
  
      var arrOps = {
          add: function (arr, i) {
              arr.splice(i, 0, this.value);
              return true;
          },
          remove: function (arr, i) {
              arr.splice(i, 1);
              return true;
          },
          replace: function (arr, i) {
              arr[i] = this.value;
              return true;
          },
          move: objOps.move,
          copy: objOps.copy,
          test: objOps.test,
          _get: objOps._get
      };
  
      var observeOps = {
          'new': function (patches, path) {
              var patch = {
                  op: "add",
                  path: path + escapePathComponent(this.name),
                  value: this.object[this.name]
              };
              patches.push(patch);
          },
          deleted: function (patches, path) {
              var patch = {
                  op: "remove",
                  path: path + escapePathComponent(this.name)
              };
              patches.push(patch);
          },
          updated: function (patches, path) {
              var patch = {
                  op: "replace",
                  path: path + escapePathComponent(this.name),
                  value: this.object[this.name]
              };
              patches.push(patch);
          }
      };
  
      function escapePathComponent(str) {
          if (str.indexOf('/') === -1 && str.indexOf('~') === -1)
              return str;
          return str.replace(/~/g, '~0').replace(/\//g, '~1');
      }
  
      function _getPathRecursive(root, obj) {
          var found;
          for (var key in root) {
              if (root.hasOwnProperty(key)) {
                  if (root[key] === obj) {
                      return escapePathComponent(key) + '/';
                  } else if (typeof root[key] === 'object') {
                      found = _getPathRecursive(root[key], obj);
                      if (found != '') {
                          return escapePathComponent(key) + '/' + found;
                      }
                  }
              }
          }
          return '';
      }
  
      function getPath(root, obj) {
          if (root === obj) {
              return '/';
          }
          var path = _getPathRecursive(root, obj);
          if (path === '') {
              throw new Error("Object not found in root");
          }
          return '/' + path;
      }
  
      var beforeDict = [];
  
      jsonpatch.intervals;
  
      var Mirror = (function () {
          function Mirror(obj) {
              this.observers = [];
              this.obj = obj;
          }
          return Mirror;
      })();
  
      var ObserverInfo = (function () {
          function ObserverInfo(callback, observer) {
              this.callback = callback;
              this.observer = observer;
          }
          return ObserverInfo;
      })();
  
      function getMirror(obj) {
          for (var i = 0, ilen = beforeDict.length; i < ilen; i++) {
              if (beforeDict[i].obj === obj) {
                  return beforeDict[i];
              }
          }
      }
  
      function getObserverFromMirror(mirror, callback) {
          for (var j = 0, jlen = mirror.observers.length; j < jlen; j++) {
              if (mirror.observers[j].callback === callback) {
                  return mirror.observers[j].observer;
              }
          }
      }
  
      function removeObserverFromMirror(mirror, observer) {
          for (var j = 0, jlen = mirror.observers.length; j < jlen; j++) {
              if (mirror.observers[j].observer === observer) {
                  mirror.observers.splice(j, 1);
                  return;
              }
          }
      }
  
      function unobserve(root, observer) {
          generate(observer);
          if (Object.observe) {
              _unobserve(observer, root);
          } else {
              clearTimeout(observer.next);
          }
  
          var mirror = getMirror(root);
          removeObserverFromMirror(mirror, observer);
      }
      jsonpatch.unobserve = unobserve;
  
      function observe(obj, callback) {
          var patches = [];
          var root = obj;
          var observer;
          var mirror = getMirror(obj);
  
          if (!mirror) {
              mirror = new Mirror(obj);
              beforeDict.push(mirror);
          } else {
              observer = getObserverFromMirror(mirror, callback);
          }
  
          if (observer) {
              return observer;
          }
  
          if (Object.observe) {
              observer = function (arr) {
                  //This "refresh" is needed to begin observing new object properties
                  _unobserve(observer, obj);
                  _observe(observer, obj);
  
                  var a = 0, alen = arr.length;
                  while (a < alen) {
                      if (!(arr[a].name === 'length' && _isArray(arr[a].object)) && !(arr[a].name === '__Jasmine_been_here_before__')) {
                          observeOps[arr[a].type].call(arr[a], patches, getPath(root, arr[a].object));
                      }
                      a++;
                  }
  
                  if (patches) {
                      if (callback) {
                          callback(patches);
                      }
                  }
                  observer.patches = patches;
                  patches = [];
              };
          } else {
              observer = {};
  
              mirror.value = JSON.parse(JSON.stringify(obj));
  
              if (callback) {
                  //callbacks.push(callback); this has no purpose
                  observer.callback = callback;
                  observer.next = null;
                  var intervals = this.intervals || [100, 1000, 10000, 60000];
                  var currentInterval = 0;
  
                  var dirtyCheck = function () {
                      generate(observer);
                  };
                  var fastCheck = function () {
                      clearTimeout(observer.next);
                      observer.next = setTimeout(function () {
                          dirtyCheck();
                          currentInterval = 0;
                          observer.next = setTimeout(slowCheck, intervals[currentInterval++]);
                      }, 0);
                  };
                  var slowCheck = function () {
                      dirtyCheck();
                      if (currentInterval == intervals.length)
                          currentInterval = intervals.length - 1;
                      observer.next = setTimeout(slowCheck, intervals[currentInterval++]);
                  };
                  if (typeof window !== 'undefined') {
                      if (window.addEventListener) {
                          window.addEventListener('mousedown', fastCheck);
                          window.addEventListener('mouseup', fastCheck);
                          window.addEventListener('keydown', fastCheck);
                      } else {
                          window.attachEvent('onmousedown', fastCheck);
                          window.attachEvent('onmouseup', fastCheck);
                          window.attachEvent('onkeydown', fastCheck);
                      }
                  }
                  observer.next = setTimeout(slowCheck, intervals[currentInterval++]);
              }
          }
          observer.patches = patches;
          observer.object = obj;
  
          mirror.observers.push(new ObserverInfo(callback, observer));
  
          return _observe(observer, obj);
      }
      jsonpatch.observe = observe;
  
      /// Listen to changes on an object tree, accumulate patches
      function _observe(observer, obj) {
          if (Object.observe) {
              Object.observe(obj, observer);
              for (var key in obj) {
                  if (obj.hasOwnProperty(key)) {
                      var v = obj[key];
                      if (v && typeof (v) === "object") {
                          _observe(observer, v);
                      }
                  }
              }
          }
          return observer;
      }
  
      function _unobserve(observer, obj) {
          if (Object.observe) {
              Object.unobserve(obj, observer);
              for (var key in obj) {
                  if (obj.hasOwnProperty(key)) {
                      var v = obj[key];
                      if (v && typeof (v) === "object") {
                          _unobserve(observer, v);
                      }
                  }
              }
          }
          return observer;
      }
  
      function generate(observer) {
          if (Object.observe) {
              Object.deliverChangeRecords(observer);
          } else {
              var mirror;
              for (var i = 0, ilen = beforeDict.length; i < ilen; i++) {
                  if (beforeDict[i].obj === observer.object) {
                      mirror = beforeDict[i];
                      break;
                  }
              }
              _generate(mirror.value, observer.object, observer.patches, "");
          }
          var temp = observer.patches;
          if (temp.length > 0) {
              observer.patches = [];
              if (observer.callback) {
                  observer.callback(temp);
              }
          }
          return temp;
      }
      jsonpatch.generate = generate;
  
      var _objectKeys;
      if (Object.keys) {
          _objectKeys = Object.keys;
      } else {
          _objectKeys = function (obj) {
              var keys = [];
              for (var o in obj) {
                  if (obj.hasOwnProperty(o)) {
                      keys.push(o);
                  }
              }
              return keys;
          };
      }
  
      // 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;
  
          for (var t = oldKeys.length - 1; t >= 0; t--) {
              var key = oldKeys[t];
              var oldVal = mirror[key];
              if (obj.hasOwnProperty(key)) {
                  var newVal = obj[key];
                  if (oldVal instanceof Object) {
                      _generate(oldVal, newVal, patches, path + "/" + escapePathComponent(key));
                  } else {
                      if (oldVal != newVal) {
                          changed = true;
                          patches.push({ op: "replace", path: path + "/" + escapePathComponent(key), value: newVal });
                          mirror[key] = newVal;
                      }
                  }
              } else {
                  patches.push({ op: "remove", path: path + "/" + escapePathComponent(key) });
                  delete mirror[key];
                  deleted = true;
              }
          }
  
          if (!deleted && newKeys.length == oldKeys.length) {
              return;
          }
  
          for (var t = 0; t < newKeys.length; t++) {
              var key = newKeys[t];
              if (!mirror.hasOwnProperty(key)) {
                  patches.push({ op: "add", path: path + "/" + escapePathComponent(key), value: obj[key] });
                  mirror[key] = JSON.parse(JSON.stringify(obj[key]));
              }
          }
      }
  
      var _isArray;
      if (Array.isArray) {
          _isArray = Array.isArray;
      } else {
          _isArray = function (obj) {
              return obj.push && typeof obj.length === 'number';
          };
      }
  
      /// Apply a json-patch operation on an object tree
      function apply(tree, patches) {
          var result = false, p = 0, plen = patches.length, patch;
          while (p < plen) {
              patch = patches[p];
  
              // Find the object
              var keys = patch.path.split('/');
              var obj = tree;
              var t = 1;
              var len = keys.length;
              while (true) {
                  if (_isArray(obj)) {
                      var index = parseInt(keys[t], 10);
                      t++;
                      if (t >= len) {
                          result = arrOps[patch.op].call(patch, obj, index, tree);
                          break;
                      }
                      obj = obj[index];
                  } else {
                      var key = keys[t];
                      if (key.indexOf('~') != -1)
                          key = key.replace(/~1/g, '/').replace(/~0/g, '~');
                      t++;
                      if (t >= len) {
                          result = objOps[patch.op].call(patch, obj, key, tree);
                          break;
                      }
                      obj = obj[key];
                  }
              }
              p++;
          }
          return result;
      }
      jsonpatch.apply = apply;
  })(jsonpatch || (jsonpatch = {}));
  
  if (typeof exports !== "undefined") {
      exports.apply = jsonpatch.apply;
      exports.observe = jsonpatch.observe;
      exports.unobserve = jsonpatch.unobserve;
      exports.generate = jsonpatch.generate;
  }
  