diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 80a172c..47533a5 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -1,5 +1,9 @@
 # Changelog
 
+## 1.0.2
+
+ - Update diff-match-patch to fix problem when encoding consecutive surrogate pairs
+
 ## 1.0.1
 
  - Disconnect from existing socket connection when connecting [#90](https://github.com/Simperium/node-simperium/pull/90)
diff --git a/package-lock.json b/package-lock.json
index 565642d..af2b448 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "simperium",
-  "version": "1.0.0",
+  "version": "1.0.2",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
diff --git a/package.json b/package.json
index 1097483..e34e82a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "simperium",
-  "version": "1.0.1",
+  "version": "1.0.2",
   "description": "A simperium client for node.js",
   "main": "./lib/simperium/index.js",
   "repository": {
diff --git a/src/simperium/jsondiff/diff_match_patch.js b/src/simperium/jsondiff/diff_match_patch.js
index 9bdccea..4c7435d 100644
--- a/src/simperium/jsondiff/diff_match_patch.js
+++ b/src/simperium/jsondiff/diff_match_patch.js
@@ -1,9 +1,7 @@
-
 /**
  * Diff Match and Patch
- *
- * Copyright 2006 Google Inc.
- * http://code.google.com/p/google-diff-match-patch/
+ * Copyright 2018 The diff-match-patch Authors.
+ * https://github.com/google/diff-match-patch
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -53,7 +51,7 @@ export default function diff_match_patch() {
 
   // The number of bits in an int.
   this.Match_MaxBits = 32;
-}
+};
 
 
 //  DIFF FUNCTIONS
@@ -68,8 +66,27 @@ export const DIFF_DELETE = -1;
 export const DIFF_INSERT = 1;
 export const DIFF_EQUAL = 0;
 
-/** @typedef {{0: number, 1: string}} */
-diff_match_patch.Diff;
+/**
+ * Class representing one diff tuple.
+ * Attempts to look like a two-element array (which is what this used to be).
+ * @param {number} op Operation, one of: DIFF_DELETE, DIFF_INSERT, DIFF_EQUAL.
+ * @param {string} text Text to be deleted, inserted, or retained.
+ * @constructor
+ */
+diff_match_patch.Diff = function(op, text) {
+  this[0] = op;
+  this[1] = text;
+};
+
+diff_match_patch.Diff.prototype.length = 2;
+
+/**
+ * Emulate the output of a two-element array.
+ * @return {string} Diff operation as a string.
+ */
+diff_match_patch.Diff.prototype.toString = function() {
+  return this[0] + ',' + this[1];
+};
 
 
 /**
@@ -80,7 +97,7 @@ diff_match_patch.Diff;
  * @param {boolean=} opt_checklines Optional speedup flag. If present and false,
  *     then don't run a line-level diff first to identify the changed areas.
  *     Defaults to true, which does a faster, slightly less optimal diff.
- * @param {number} opt_deadline Optional time when the diff should be complete
+ * @param {number=} opt_deadline Optional time when the diff should be complete
  *     by.  Used internally for recursive calls.  Users should set DiffTimeout
  *     instead.
  * @return {!Array.<!diff_match_patch.Diff>} Array of diff tuples.
@@ -105,7 +122,7 @@ diff_match_patch.prototype.diff_main = function(text1, text2, opt_checklines,
   // Check for equality (speedup).
   if (text1 == text2) {
     if (text1) {
-      return [[DIFF_EQUAL, text1]];
+      return [new diff_match_patch.Diff(DIFF_EQUAL, text1)];
     }
     return [];
   }
@@ -132,10 +149,10 @@ diff_match_patch.prototype.diff_main = function(text1, text2, opt_checklines,
 
   // Restore the prefix and suffix.
   if (commonprefix) {
-    diffs.unshift([DIFF_EQUAL, commonprefix]);
+    diffs.unshift(new diff_match_patch.Diff(DIFF_EQUAL, commonprefix));
   }
   if (commonsuffix) {
-    diffs.push([DIFF_EQUAL, commonsuffix]);
+    diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, commonsuffix));
   }
   this.diff_cleanupMerge(diffs);
   return diffs;
@@ -160,12 +177,12 @@ diff_match_patch.prototype.diff_compute_ = function(text1, text2, checklines,
 
   if (!text1) {
     // Just add some text (speedup).
-    return [[DIFF_INSERT, text2]];
+    return [new diff_match_patch.Diff(DIFF_INSERT, text2)];
   }
 
   if (!text2) {
     // Just delete some text (speedup).
-    return [[DIFF_DELETE, text1]];
+    return [new diff_match_patch.Diff(DIFF_DELETE, text1)];
   }
 
   var longtext = text1.length > text2.length ? text1 : text2;
@@ -173,9 +190,10 @@ diff_match_patch.prototype.diff_compute_ = function(text1, text2, checklines,
   var i = longtext.indexOf(shorttext);
   if (i != -1) {
     // Shorter text is inside the longer text (speedup).
-    diffs = [[DIFF_INSERT, longtext.substring(0, i)],
-             [DIFF_EQUAL, shorttext],
-             [DIFF_INSERT, longtext.substring(i + shorttext.length)]];
+    diffs = [new diff_match_patch.Diff(DIFF_INSERT, longtext.substring(0, i)),
+      new diff_match_patch.Diff(DIFF_EQUAL, shorttext),
+      new diff_match_patch.Diff(DIFF_INSERT,
+          longtext.substring(i + shorttext.length))];
     // Swap insertions for deletions if diff is reversed.
     if (text1.length > text2.length) {
       diffs[0][0] = diffs[2][0] = DIFF_DELETE;
@@ -186,7 +204,8 @@ diff_match_patch.prototype.diff_compute_ = function(text1, text2, checklines,
   if (shorttext.length == 1) {
     // Single character string.
     // After the previous speedup, the character can't be an equality.
-    return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
+    return [new diff_match_patch.Diff(DIFF_DELETE, text1),
+      new diff_match_patch.Diff(DIFF_INSERT, text2)];
   }
 
   // Check to see if the problem can be split in two.
@@ -202,7 +221,8 @@ diff_match_patch.prototype.diff_compute_ = function(text1, text2, checklines,
     var diffs_a = this.diff_main(text1_a, text2_a, checklines, deadline);
     var diffs_b = this.diff_main(text1_b, text2_b, checklines, deadline);
     // Merge the results.
-    return diffs_a.concat([[DIFF_EQUAL, mid_common]], diffs_b);
+    return diffs_a.concat([new diff_match_patch.Diff(DIFF_EQUAL, mid_common)],
+        diffs_b);
   }
 
   if (checklines && text1.length > 100 && text2.length > 100) {
@@ -239,7 +259,7 @@ diff_match_patch.prototype.diff_lineMode_ = function(text1, text2, deadline) {
 
   // Rediff any replacement blocks, this time character-by-character.
   // Add a dummy entry at the end.
-  diffs.push([DIFF_EQUAL, '']);
+  diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, ''));
   var pointer = 0;
   var count_delete = 0;
   var count_insert = 0;
@@ -260,13 +280,14 @@ diff_match_patch.prototype.diff_lineMode_ = function(text1, text2, deadline) {
         if (count_delete >= 1 && count_insert >= 1) {
           // Delete the offending records and add the merged ones.
           diffs.splice(pointer - count_delete - count_insert,
-                       count_delete + count_insert);
+              count_delete + count_insert);
           pointer = pointer - count_delete - count_insert;
-          var a = this.diff_main(text_delete, text_insert, false, deadline);
-          for (var j = a.length - 1; j >= 0; j--) {
-            diffs.splice(pointer, 0, a[j]);
+          var subDiff =
+              this.diff_main(text_delete, text_insert, false, deadline);
+          for (var j = subDiff.length - 1; j >= 0; j--) {
+            diffs.splice(pointer, 0, subDiff[j]);
           }
-          pointer = pointer + a.length;
+          pointer = pointer + subDiff.length;
         }
         count_insert = 0;
         count_delete = 0;
@@ -336,7 +357,7 @@ diff_match_patch.prototype.diff_bisect_ = function(text1, text2, deadline) {
       }
       var y1 = x1 - k1;
       while (x1 < text1_length && y1 < text2_length &&
-             text1.charAt(x1) == text2.charAt(y1)) {
+      text1.charAt(x1) == text2.charAt(y1)) {
         x1++;
         y1++;
       }
@@ -371,8 +392,8 @@ diff_match_patch.prototype.diff_bisect_ = function(text1, text2, deadline) {
       }
       var y2 = x2 - k2;
       while (x2 < text1_length && y2 < text2_length &&
-             text1.charAt(text1_length - x2 - 1) ==
-             text2.charAt(text2_length - y2 - 1)) {
+      text1.charAt(text1_length - x2 - 1) ==
+      text2.charAt(text2_length - y2 - 1)) {
         x2++;
         y2++;
       }
@@ -400,7 +421,8 @@ diff_match_patch.prototype.diff_bisect_ = function(text1, text2, deadline) {
   }
   // Diff took too long and hit the deadline or
   // number of diffs equals number of characters, no commonality at all.
-  return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
+  return [new diff_match_patch.Diff(DIFF_DELETE, text1),
+    new diff_match_patch.Diff(DIFF_INSERT, text2)];
 };
 
 
@@ -472,21 +494,29 @@ diff_match_patch.prototype.diff_linesToChars_ = function(text1, text2) {
         lineEnd = text.length - 1;
       }
       var line = text.substring(lineStart, lineEnd + 1);
-      lineStart = lineEnd + 1;
 
       if (lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) :
           (lineHash[line] !== undefined)) {
         chars += String.fromCharCode(lineHash[line]);
       } else {
+        if (lineArrayLength == maxLines) {
+          // Bail out at 65535 because
+          // String.fromCharCode(65536) == String.fromCharCode(0)
+          line = text.substring(lineStart);
+          lineEnd = text.length;
+        }
         chars += String.fromCharCode(lineArrayLength);
         lineHash[line] = lineArrayLength;
         lineArray[lineArrayLength++] = line;
       }
+      lineStart = lineEnd + 1;
     }
     return chars;
   }
-
+  // Allocate 2/3rds of the space for text1, the rest for text2.
+  var maxLines = 40000;
   var chars1 = diff_linesToCharsMunge_(text1);
+  maxLines = 65535;
   var chars2 = diff_linesToCharsMunge_(text2);
   return {chars1: chars1, chars2: chars2, lineArray: lineArray};
 };
@@ -500,13 +530,13 @@ diff_match_patch.prototype.diff_linesToChars_ = function(text1, text2) {
  * @private
  */
 diff_match_patch.prototype.diff_charsToLines_ = function(diffs, lineArray) {
-  for (var x = 0; x < diffs.length; x++) {
-    var chars = diffs[x][1];
+  for (var i = 0; i < diffs.length; i++) {
+    var chars = diffs[i][1];
     var text = [];
-    for (var y = 0; y < chars.length; y++) {
-      text[y] = lineArray[chars.charCodeAt(y)];
+    for (var j = 0; j < chars.length; j++) {
+      text[j] = lineArray[chars.charCodeAt(j)];
     }
-    diffs[x][1] = text.join('');
+    diffs[i][1] = text.join('');
   }
 };
 
@@ -524,7 +554,7 @@ diff_match_patch.prototype.diff_commonPrefix = function(text1, text2) {
     return 0;
   }
   // Binary search.
-  // Performance analysis: http://neil.fraser.name/news/2007/10/09/
+  // Performance analysis: https://neil.fraser.name/news/2007/10/09/
   var pointermin = 0;
   var pointermax = Math.min(text1.length, text2.length);
   var pointermid = pointermax;
@@ -556,7 +586,7 @@ diff_match_patch.prototype.diff_commonSuffix = function(text1, text2) {
     return 0;
   }
   // Binary search.
-  // Performance analysis: http://neil.fraser.name/news/2007/10/09/
+  // Performance analysis: https://neil.fraser.name/news/2007/10/09/
   var pointermin = 0;
   var pointermax = Math.min(text1.length, text2.length);
   var pointermid = pointermax;
@@ -605,7 +635,7 @@ diff_match_patch.prototype.diff_commonOverlap_ = function(text1, text2) {
 
   // Start by looking for a single character match
   // and increase length until no match is found.
-  // Performance analysis: http://neil.fraser.name/news/2010/11/04/
+  // Performance analysis: https://neil.fraser.name/news/2010/11/04/
   var best = 0;
   var length = 1;
   while (true) {
@@ -667,9 +697,9 @@ diff_match_patch.prototype.diff_halfMatch_ = function(text1, text2) {
     var best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b;
     while ((j = shorttext.indexOf(seed, j + 1)) != -1) {
       var prefixLength = dmp.diff_commonPrefix(longtext.substring(i),
-                                               shorttext.substring(j));
+          shorttext.substring(j));
       var suffixLength = dmp.diff_commonSuffix(longtext.substring(0, i),
-                                               shorttext.substring(0, j));
+          shorttext.substring(0, j));
       if (best_common.length < suffixLength + prefixLength) {
         best_common = shorttext.substring(j - suffixLength, j) +
             shorttext.substring(j, j + prefixLength);
@@ -681,7 +711,7 @@ diff_match_patch.prototype.diff_halfMatch_ = function(text1, text2) {
     }
     if (best_common.length * 2 >= longtext.length) {
       return [best_longtext_a, best_longtext_b,
-              best_shorttext_a, best_shorttext_b, best_common];
+        best_shorttext_a, best_shorttext_b, best_common];
     } else {
       return null;
     }
@@ -689,10 +719,10 @@ diff_match_patch.prototype.diff_halfMatch_ = function(text1, text2) {
 
   // First check if the second quarter is the seed for a half-match.
   var hm1 = diff_halfMatchI_(longtext, shorttext,
-                             Math.ceil(longtext.length / 4));
+      Math.ceil(longtext.length / 4));
   // Check again based on the third quarter.
   var hm2 = diff_halfMatchI_(longtext, shorttext,
-                             Math.ceil(longtext.length / 2));
+      Math.ceil(longtext.length / 2));
   var hm;
   if (!hm1 && !hm2) {
     return null;
@@ -732,7 +762,7 @@ diff_match_patch.prototype.diff_cleanupSemantic = function(diffs) {
   var equalities = [];  // Stack of indices where equalities are found.
   var equalitiesLength = 0;  // Keeping our own length var is faster in JS.
   /** @type {?string} */
-  var lastequality = null;
+  var lastEquality = null;
   // Always equal to diffs[equalities[equalitiesLength - 1]][1]
   var pointer = 0;  // Index of current position.
   // Number of characters that changed prior to the equality.
@@ -748,7 +778,7 @@ diff_match_patch.prototype.diff_cleanupSemantic = function(diffs) {
       length_deletions1 = length_deletions2;
       length_insertions2 = 0;
       length_deletions2 = 0;
-      lastequality = diffs[pointer][1];
+      lastEquality = diffs[pointer][1];
     } else {  // An insertion or deletion.
       if (diffs[pointer][0] == DIFF_INSERT) {
         length_insertions2 += diffs[pointer][1].length;
@@ -757,13 +787,13 @@ diff_match_patch.prototype.diff_cleanupSemantic = function(diffs) {
       }
       // Eliminate an equality that is smaller or equal to the edits on both
       // sides of it.
-      if (lastequality && (lastequality.length <=
+      if (lastEquality && (lastEquality.length <=
           Math.max(length_insertions1, length_deletions1)) &&
-          (lastequality.length <= Math.max(length_insertions2,
-                                           length_deletions2))) {
+          (lastEquality.length <= Math.max(length_insertions2,
+              length_deletions2))) {
         // Duplicate record.
         diffs.splice(equalities[equalitiesLength - 1], 0,
-                     [DIFF_DELETE, lastequality]);
+            new diff_match_patch.Diff(DIFF_DELETE, lastEquality));
         // Change second copy to insert.
         diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
         // Throw away the equality we just deleted.
@@ -775,7 +805,7 @@ diff_match_patch.prototype.diff_cleanupSemantic = function(diffs) {
         length_deletions1 = 0;
         length_insertions2 = 0;
         length_deletions2 = 0;
-        lastequality = null;
+        lastEquality = null;
         changes = true;
       }
     }
@@ -806,8 +836,8 @@ diff_match_patch.prototype.diff_cleanupSemantic = function(diffs) {
         if (overlap_length1 >= deletion.length / 2 ||
             overlap_length1 >= insertion.length / 2) {
           // Overlap found.  Insert an equality and trim the surrounding edits.
-          diffs.splice(pointer, 0,
-              [DIFF_EQUAL, insertion.substring(0, overlap_length1)]);
+          diffs.splice(pointer, 0, new diff_match_patch.Diff(DIFF_EQUAL,
+              insertion.substring(0, overlap_length1)));
           diffs[pointer - 1][1] =
               deletion.substring(0, deletion.length - overlap_length1);
           diffs[pointer + 1][1] = insertion.substring(overlap_length1);
@@ -818,8 +848,8 @@ diff_match_patch.prototype.diff_cleanupSemantic = function(diffs) {
             overlap_length2 >= insertion.length / 2) {
           // Reverse overlap found.
           // Insert an equality and swap and trim the surrounding edits.
-          diffs.splice(pointer, 0,
-              [DIFF_EQUAL, deletion.substring(0, overlap_length2)]);
+          diffs.splice(pointer, 0, new diff_match_patch.Diff(DIFF_EQUAL,
+              deletion.substring(0, overlap_length2)));
           diffs[pointer - 1][0] = DIFF_INSERT;
           diffs[pointer - 1][1] =
               insertion.substring(0, insertion.length - overlap_length2);
@@ -977,7 +1007,7 @@ diff_match_patch.prototype.diff_cleanupEfficiency = function(diffs) {
   var equalities = [];  // Stack of indices where equalities are found.
   var equalitiesLength = 0;  // Keeping our own length var is faster in JS.
   /** @type {?string} */
-  var lastequality = null;
+  var lastEquality = null;
   // Always equal to diffs[equalities[equalitiesLength - 1]][1]
   var pointer = 0;  // Index of current position.
   // Is there an insertion operation before the last equality.
@@ -996,11 +1026,11 @@ diff_match_patch.prototype.diff_cleanupEfficiency = function(diffs) {
         equalities[equalitiesLength++] = pointer;
         pre_ins = post_ins;
         pre_del = post_del;
-        lastequality = diffs[pointer][1];
+        lastEquality = diffs[pointer][1];
       } else {
         // Not a candidate, and can never become one.
         equalitiesLength = 0;
-        lastequality = null;
+        lastEquality = null;
       }
       post_ins = post_del = false;
     } else {  // An insertion or deletion.
@@ -1017,16 +1047,16 @@ diff_match_patch.prototype.diff_cleanupEfficiency = function(diffs) {
        * <ins>A</del>X<ins>C</ins><del>D</del>
        * <ins>A</ins><del>B</del>X<del>C</del>
        */
-      if (lastequality && ((pre_ins && pre_del && post_ins && post_del) ||
-                           ((lastequality.length < this.Diff_EditCost / 2) &&
-                            (pre_ins + pre_del + post_ins + post_del) == 3))) {
+      if (lastEquality && ((pre_ins && pre_del && post_ins && post_del) ||
+          ((lastEquality.length < this.Diff_EditCost / 2) &&
+              (pre_ins + pre_del + post_ins + post_del) == 3))) {
         // Duplicate record.
         diffs.splice(equalities[equalitiesLength - 1], 0,
-                     [DIFF_DELETE, lastequality]);
+            new diff_match_patch.Diff(DIFF_DELETE, lastEquality));
         // Change second copy to insert.
         diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
         equalitiesLength--;  // Throw away the equality we just deleted;
-        lastequality = null;
+        lastEquality = null;
         if (pre_ins && pre_del) {
           // No changes made which could affect previous entry, keep going.
           post_ins = post_del = true;
@@ -1055,7 +1085,8 @@ diff_match_patch.prototype.diff_cleanupEfficiency = function(diffs) {
  * @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.
  */
 diff_match_patch.prototype.diff_cleanupMerge = function(diffs) {
-  diffs.push([DIFF_EQUAL, '']);  // Add a dummy entry at the end.
+  // Add a dummy entry at the end.
+  diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, ''));
   var pointer = 0;
   var count_delete = 0;
   var count_insert = 0;
@@ -1087,8 +1118,8 @@ diff_match_patch.prototype.diff_cleanupMerge = function(diffs) {
                 diffs[pointer - count_delete - count_insert - 1][1] +=
                     text_insert.substring(0, commonlength);
               } else {
-                diffs.splice(0, 0, [DIFF_EQUAL,
-                                    text_insert.substring(0, commonlength)]);
+                diffs.splice(0, 0, new diff_match_patch.Diff(DIFF_EQUAL,
+                    text_insert.substring(0, commonlength)));
                 pointer++;
               }
               text_insert = text_insert.substring(commonlength);
@@ -1106,19 +1137,19 @@ diff_match_patch.prototype.diff_cleanupMerge = function(diffs) {
             }
           }
           // Delete the offending records and add the merged ones.
-          if (count_delete === 0) {
-            diffs.splice(pointer - count_insert,
-                count_delete + count_insert, [DIFF_INSERT, text_insert]);
-          } else if (count_insert === 0) {
-            diffs.splice(pointer - count_delete,
-                count_delete + count_insert, [DIFF_DELETE, text_delete]);
-          } else {
-            diffs.splice(pointer - count_delete - count_insert,
-                count_delete + count_insert, [DIFF_DELETE, text_delete],
-                [DIFF_INSERT, text_insert]);
+          pointer -= count_delete + count_insert;
+          diffs.splice(pointer, count_delete + count_insert);
+          if (text_delete.length) {
+            diffs.splice(pointer, 0,
+                new diff_match_patch.Diff(DIFF_DELETE, text_delete));
+            pointer++;
+          }
+          if (text_insert.length) {
+            diffs.splice(pointer, 0,
+                new diff_match_patch.Diff(DIFF_INSERT, text_insert));
+            pointer++;
           }
-          pointer = pointer - count_delete - count_insert +
-                    (count_delete ? 1 : 0) + (count_insert ? 1 : 0) + 1;
+          pointer++;
         } else if (pointer !== 0 && diffs[pointer - 1][0] == DIFF_EQUAL) {
           // Merge this equality with the previous one.
           diffs[pointer - 1][1] += diffs[pointer][1];
@@ -1152,7 +1183,7 @@ diff_match_patch.prototype.diff_cleanupMerge = function(diffs) {
         // Shift the edit over the previous equality.
         diffs[pointer][1] = diffs[pointer - 1][1] +
             diffs[pointer][1].substring(0, diffs[pointer][1].length -
-                                        diffs[pointer - 1][1].length);
+                diffs[pointer - 1][1].length);
         diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1];
         diffs.splice(pointer - 1, 1);
         changes = true;
@@ -1308,6 +1339,15 @@ diff_match_patch.prototype.diff_levenshtein = function(diffs) {
   return levenshtein;
 };
 
+diff_match_patch.prototype.isHighSurrogate = function(c) {
+  var v = c.charCodeAt(0);
+  return v >= 0xD800 && v <= 0xDBFF;
+}
+
+diff_match_patch.prototype.isLowSurrogate = function(c) {
+  var v = c.charCodeAt(0);
+  return v >= 0xDC00 && v <= 0xDFFF;
+}
 
 /**
  * Crush the diff into an encoded string which describes the operations
@@ -1319,7 +1359,26 @@ diff_match_patch.prototype.diff_levenshtein = function(diffs) {
  */
 diff_match_patch.prototype.diff_toDelta = function(diffs) {
   var text = [];
+  var lastEnd;
   for (var x = 0; x < diffs.length; x++) {
+
+    var thisDiff = diffs[x];
+    var thisTop = thisDiff[1][0];
+    var thisEnd = thisDiff[1][thisDiff[1].length - 1];
+
+    if (thisEnd && this.isHighSurrogate(thisEnd)) {
+      thisDiff[1] = thisDiff[1].slice(0, -1);
+    }
+
+    if (lastEnd && thisTop && this.isHighSurrogate(lastEnd) && this.isLowSurrogate(thisTop)) {
+      thisDiff[1] = lastEnd + thisDiff[1];
+    }
+
+    lastEnd = thisEnd;
+    if ( 0 === thisDiff[1].length ) {
+      continue;
+    }
+
     switch (diffs[x][0]) {
       case DIFF_INSERT:
         text[x] = '+' + encodeURI(diffs[x][1]);
@@ -1356,7 +1415,8 @@ diff_match_patch.prototype.diff_fromDelta = function(text1, delta) {
     switch (tokens[x].charAt(0)) {
       case '+':
         try {
-          diffs[diffsLength++] = [DIFF_INSERT, decodeURI(param)];
+          diffs[diffsLength++] =
+              new diff_match_patch.Diff(DIFF_INSERT, decodeURI(param));
         } catch (ex) {
           // Malformed URI sequence.
           throw new Error('Illegal escape in diff_fromDelta: ' + param);
@@ -1371,9 +1431,9 @@ diff_match_patch.prototype.diff_fromDelta = function(text1, delta) {
         }
         var text = text1.substring(pointer, pointer += n);
         if (tokens[x].charAt(0) == '=') {
-          diffs[diffsLength++] = [DIFF_EQUAL, text];
+          diffs[diffsLength++] = new diff_match_patch.Diff(DIFF_EQUAL, text);
         } else {
-          diffs[diffsLength++] = [DIFF_DELETE, text];
+          diffs[diffsLength++] = new diff_match_patch.Diff(DIFF_DELETE, text);
         }
         break;
       default:
@@ -1381,7 +1441,7 @@ diff_match_patch.prototype.diff_fromDelta = function(text1, delta) {
         // Anything else is an error.
         if (tokens[x]) {
           throw new Error('Invalid diff operation in diff_fromDelta: ' +
-                          tokens[x]);
+              tokens[x]);
         }
     }
   }
@@ -1513,8 +1573,8 @@ diff_match_patch.prototype.match_bitap_ = function(text, pattern, loc) {
         rd[j] = ((rd[j + 1] << 1) | 1) & charMatch;
       } else {  // Subsequent passes: fuzzy match.
         rd[j] = (((rd[j + 1] << 1) | 1) & charMatch) |
-                (((last_rd[j + 1] | last_rd[j]) << 1) | 1) |
-                last_rd[j + 1];
+            (((last_rd[j + 1] | last_rd[j]) << 1) | 1) |
+            last_rd[j + 1];
       }
       if (rd[j] & matchmask) {
         var score = match_bitapScore_(d, j - 1);
@@ -1576,17 +1636,20 @@ diff_match_patch.prototype.patch_addContext_ = function(patch, text) {
   if (text.length == 0) {
     return;
   }
+  if (patch.start2 === null) {
+    throw Error('patch not initialized');
+  }
   var pattern = text.substring(patch.start2, patch.start2 + patch.length1);
   var padding = 0;
 
   // Look for the first and last matches of pattern in text.  If two different
   // matches are found, increase the pattern length.
   while (text.indexOf(pattern) != text.lastIndexOf(pattern) &&
-         pattern.length < this.Match_MaxBits - this.Patch_Margin -
-         this.Patch_Margin) {
+  pattern.length < this.Match_MaxBits - this.Patch_Margin -
+  this.Patch_Margin) {
     padding += this.Patch_Margin;
     pattern = text.substring(patch.start2 - padding,
-                             patch.start2 + patch.length1 + padding);
+        patch.start2 + patch.length1 + padding);
   }
   // Add one chunk for good luck.
   padding += this.Patch_Margin;
@@ -1594,13 +1657,13 @@ diff_match_patch.prototype.patch_addContext_ = function(patch, text) {
   // Add the prefix.
   var prefix = text.substring(patch.start2 - padding, patch.start2);
   if (prefix) {
-    patch.diffs.unshift([DIFF_EQUAL, prefix]);
+    patch.diffs.unshift(new diff_match_patch.Diff(DIFF_EQUAL, prefix));
   }
   // Add the suffix.
   var suffix = text.substring(patch.start2 + patch.length1,
-                              patch.start2 + patch.length1 + padding);
+      patch.start2 + patch.length1 + padding);
   if (suffix) {
-    patch.diffs.push([DIFF_EQUAL, suffix]);
+    patch.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, suffix));
   }
 
   // Roll back the start points.
@@ -1628,9 +1691,9 @@ diff_match_patch.prototype.patch_addContext_ = function(patch, text) {
  *
  * @param {string|!Array.<!diff_match_patch.Diff>} a text1 (methods 1,3,4) or
  * Array of diff tuples for text1 to text2 (method 2).
- * @param {string|!Array.<!diff_match_patch.Diff>} opt_b text2 (methods 1,4) or
+ * @param {string|!Array.<!diff_match_patch.Diff>=} opt_b text2 (methods 1,4) or
  * Array of diff tuples for text1 to text2 (method 3) or undefined (method 2).
- * @param {string|!Array.<!diff_match_patch.Diff>} opt_c Array of diff tuples
+ * @param {string|!Array.<!diff_match_patch.Diff>=} opt_c Array of diff tuples
  * for text1 to text2 (method 4) or undefined (methods 1,2,3).
  * @return {!Array.<!diff_match_patch.patch_obj>} Array of Patch objects.
  */
@@ -1695,14 +1758,14 @@ diff_match_patch.prototype.patch_make = function(a, opt_b, opt_c) {
         patch.diffs[patchDiffLength++] = diffs[x];
         patch.length2 += diff_text.length;
         postpatch_text = postpatch_text.substring(0, char_count2) + diff_text +
-                         postpatch_text.substring(char_count2);
+            postpatch_text.substring(char_count2);
         break;
       case DIFF_DELETE:
         patch.length1 += diff_text.length;
         patch.diffs[patchDiffLength++] = diffs[x];
         postpatch_text = postpatch_text.substring(0, char_count2) +
-                         postpatch_text.substring(char_count2 +
-                             diff_text.length);
+            postpatch_text.substring(char_count2 +
+                diff_text.length);
         break;
       case DIFF_EQUAL:
         if (diff_text.length <= 2 * this.Patch_Margin &&
@@ -1719,7 +1782,7 @@ diff_match_patch.prototype.patch_make = function(a, opt_b, opt_c) {
             patch = new diff_match_patch.patch_obj();
             patchDiffLength = 0;
             // Unlike Unidiff, our patch lists have a rolling context.
-            // http://code.google.com/p/google-diff-match-patch/wiki/Unidiff
+            // https://github.com/google/diff-match-patch/wiki/Unidiff
             // Update prepatch text & pos to reflect the application of the
             // just completed patch.
             prepatch_text = postpatch_text;
@@ -1760,7 +1823,8 @@ diff_match_patch.prototype.patch_deepCopy = function(patches) {
     var patchCopy = new diff_match_patch.patch_obj();
     patchCopy.diffs = [];
     for (var y = 0; y < patch.diffs.length; y++) {
-      patchCopy.diffs[y] = patch.diffs[y].slice();
+      patchCopy.diffs[y] =
+          new diff_match_patch.Diff(patch.diffs[y][0], patch.diffs[y][1]);
     }
     patchCopy.start1 = patch.start1;
     patchCopy.start2 = patch.start2;
@@ -1807,7 +1871,7 @@ diff_match_patch.prototype.patch_apply = function(patches, text) {
       // patch_splitMax will only provide an oversized pattern in the case of
       // a monster delete.
       start_loc = this.match_main(text, text1.substring(0, this.Match_MaxBits),
-                                  expected_loc);
+          expected_loc);
       if (start_loc != -1) {
         end_loc = this.match_main(text,
             text1.substring(text1.length - this.Match_MaxBits),
@@ -1838,8 +1902,8 @@ diff_match_patch.prototype.patch_apply = function(patches, text) {
       if (text1 == text2) {
         // Perfect match, just shove the replacement text in.
         text = text.substring(0, start_loc) +
-               this.diff_text2(patches[x].diffs) +
-               text.substring(start_loc + text1.length);
+            this.diff_text2(patches[x].diffs) +
+            text.substring(start_loc + text1.length);
       } else {
         // Imperfect match.  Run a diff to get a framework of equivalent
         // indices.
@@ -1860,11 +1924,11 @@ diff_match_patch.prototype.patch_apply = function(patches, text) {
             }
             if (mod[0] === DIFF_INSERT) {  // Insertion
               text = text.substring(0, start_loc + index2) + mod[1] +
-                     text.substring(start_loc + index2);
+                  text.substring(start_loc + index2);
             } else if (mod[0] === DIFF_DELETE) {  // Deletion
               text = text.substring(0, start_loc + index2) +
-                     text.substring(start_loc + this.diff_xIndex(diffs,
-                         index1 + mod[1].length));
+                  text.substring(start_loc + this.diff_xIndex(diffs,
+                      index1 + mod[1].length));
             }
             if (mod[0] !== DIFF_DELETE) {
               index1 += mod[1].length;
@@ -1904,7 +1968,7 @@ diff_match_patch.prototype.patch_addPadding = function(patches) {
   var diffs = patch.diffs;
   if (diffs.length == 0 || diffs[0][0] != DIFF_EQUAL) {
     // Add nullPadding equality.
-    diffs.unshift([DIFF_EQUAL, nullPadding]);
+    diffs.unshift(new diff_match_patch.Diff(DIFF_EQUAL, nullPadding));
     patch.start1 -= paddingLength;  // Should be 0.
     patch.start2 -= paddingLength;  // Should be 0.
     patch.length1 += paddingLength;
@@ -1924,7 +1988,7 @@ diff_match_patch.prototype.patch_addPadding = function(patches) {
   diffs = patch.diffs;
   if (diffs.length == 0 || diffs[diffs.length - 1][0] != DIFF_EQUAL) {
     // Add nullPadding equality.
-    diffs.push([DIFF_EQUAL, nullPadding]);
+    diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, nullPadding));
     patch.length1 += paddingLength;
     patch.length2 += paddingLength;
   } else if (paddingLength > diffs[diffs.length - 1][1].length) {
@@ -1965,10 +2029,10 @@ diff_match_patch.prototype.patch_splitMax = function(patches) {
       patch.start2 = start2 - precontext.length;
       if (precontext !== '') {
         patch.length1 = patch.length2 = precontext.length;
-        patch.diffs.push([DIFF_EQUAL, precontext]);
+        patch.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, precontext));
       }
       while (bigpatch.diffs.length !== 0 &&
-             patch.length1 < patch_size - this.Patch_Margin) {
+      patch.length1 < patch_size - this.Patch_Margin) {
         var diff_type = bigpatch.diffs[0][0];
         var diff_text = bigpatch.diffs[0][1];
         if (diff_type === DIFF_INSERT) {
@@ -1978,13 +2042,13 @@ diff_match_patch.prototype.patch_splitMax = function(patches) {
           patch.diffs.push(bigpatch.diffs.shift());
           empty = false;
         } else if (diff_type === DIFF_DELETE && patch.diffs.length == 1 &&
-                   patch.diffs[0][0] == DIFF_EQUAL &&
-                   diff_text.length > 2 * patch_size) {
+            patch.diffs[0][0] == DIFF_EQUAL &&
+            diff_text.length > 2 * patch_size) {
           // This is a large deletion.  Let it pass in one chunk.
           patch.length1 += diff_text.length;
           start1 += diff_text.length;
           empty = false;
-          patch.diffs.push([diff_type, diff_text]);
+          patch.diffs.push(new diff_match_patch.Diff(diff_type, diff_text));
           bigpatch.diffs.shift();
         } else {
           // Deletion or equality.  Only take as much as we can stomach.
@@ -1998,7 +2062,7 @@ diff_match_patch.prototype.patch_splitMax = function(patches) {
           } else {
             empty = false;
           }
-          patch.diffs.push([diff_type, diff_text]);
+          patch.diffs.push(new diff_match_patch.Diff(diff_type, diff_text));
           if (diff_text == bigpatch.diffs[0][1]) {
             bigpatch.diffs.shift();
           } else {
@@ -2013,7 +2077,7 @@ diff_match_patch.prototype.patch_splitMax = function(patches) {
           precontext.substring(precontext.length - this.Patch_Margin);
       // Append the end context for this patch.
       var postcontext = this.diff_text1(bigpatch.diffs)
-                            .substring(0, this.Patch_Margin);
+          .substring(0, this.Patch_Margin);
       if (postcontext !== '') {
         patch.length1 += postcontext.length;
         patch.length2 += postcontext.length;
@@ -2021,7 +2085,7 @@ diff_match_patch.prototype.patch_splitMax = function(patches) {
             patch.diffs[patch.diffs.length - 1][0] === DIFF_EQUAL) {
           patch.diffs[patch.diffs.length - 1][1] += postcontext;
         } else {
-          patch.diffs.push([DIFF_EQUAL, postcontext]);
+          patch.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, postcontext));
         }
       }
       if (!empty) {
@@ -2100,13 +2164,13 @@ diff_match_patch.prototype.patch_fromText = function(textline) {
       }
       if (sign == '-') {
         // Deletion.
-        patch.diffs.push([DIFF_DELETE, line]);
+        patch.diffs.push(new diff_match_patch.Diff(DIFF_DELETE, line));
       } else if (sign == '+') {
         // Insertion.
-        patch.diffs.push([DIFF_INSERT, line]);
+        patch.diffs.push(new diff_match_patch.Diff(DIFF_INSERT, line));
       } else if (sign == ' ') {
         // Minor equality.
-        patch.diffs.push([DIFF_EQUAL, line]);
+        patch.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, line));
       } else if (sign == '@') {
         // Start of next patch.
         break;
@@ -2142,9 +2206,9 @@ diff_match_patch.patch_obj = function() {
 
 
 /**
- * Emmulate GNU diff's format.
+ * Emulate GNU diff's format.
  * Header: @@ -382,8 +481,9 @@
- * Indicies are printed as 1-based, not 0-based.
+ * Indices are printed as 1-based, not 0-based.
  * @return {string} The GNU diff string.
  */
 diff_match_patch.patch_obj.prototype.toString = function() {