+ // Matches if a string contains combining characters or astral
+ // codepoints (technically, the first half surrogate of an astral
+ // codepoint).
+ var MULTI = /[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uD800-\uDBFF\uFE20-\uFE2F]/;
+
+ // Match an entire (potentially astral) codepoint and any
+ // combining characters following it.
+ var GLYPH = /[\0-\u02FF\u0370-\u1DBF\u1E00-\u20CF\u2100-\uD7FF\uD800-\uFE1F\uFE30-\uFFFF][\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uDC00-\uDFFF\uFE20-\uFE2F]*/g;
+
+ function diffLerpAstral(source, target, amount) {
+ // This split is not perfect for all languages, but at least
+ // it won't create invalid surrogate pairs or orphaned
+ // combining characters.
+ var sourceGlyphs = source.match(GLYPH) || [];
+ var targetGlyphs = target.match(GLYPH) || [];
+ var edits = diff(targetGlyphs, sourceGlyphs, 2, 2, 3);
+ // The edit path works from the string end, forwards, because
+ // that's how Levenshtein edits work. To match LTR reading
+ // direction (and the behavior of fastLerp), swap the strings
+ // and invert the parameter when editing.
+ var partial = edits.slice(0, Math.round((1 - amount) * edits.length));
+ return patchArray(partial, targetGlyphs).join("");
+ }
+
+ function diffLerpBasic(source, target, amount) {
+ var edits = diff(target, source, 2, 2, 3);
+ // The edit path works from the string end, forwards, because
+ // that's how Levenshtein edits work. To match LTR reading
+ // direction (and the behavior of fastLerp), swap the strings
+ // and invert the parameter when editing.
+ var partial = edits.slice(0, Math.round((1 - amount) * edits.length));
+ return patchString(partial, target);
+ }
+
+ function diffLerp(source, target, amount) {
+ /** Interpolate between two strings using edit operations