} return String.fromCharCode.apply(String, units);
};
+var adjust = function(mode, string, after) {
+ var before = after.slice();
+ var offset = 0, index = 0;
+
+ var check = function() {
+ var from = mode ? index : offset;
+ var to = mode ? offset : index;
+
+ for (var o = 0, l = after.length; o != l; ++o)
+ if (before[o] == from) {
+ before[o] = null;
+ after[o] = to;
+ }
+ };
+
+ for (var e = string.length; index != e; ++index) {
+ check();
+
+ var unit = string.charCodeAt(index);
+ var part = unit & 0xfc00;
+
+ if (part == 0xdc00)
+ return null;
+ else if (part != 0xd800);
+ else if (++index == e)
+ return null;
+ else {
+ var next = string.charCodeAt(index);
+ if ((next & 0xfc00) != 0xdc00)
+ return null;
+ }
+
+ ++offset;
+ }
+
+ check();
+ return after;
+};
+
+var point2unit = function(string, offsets) {
+ return adjust(false, string, offsets);
+};
+
+var unit2point = function(string, offsets) {
+ return adjust(true, string, offsets);
+};
+
+var charAt = function(string, offset) {
+ return substring(string, offset, offset + 1);
+};
+
+var length = function(string) {
+ return unit2point(string, [string.length])[0];
+};
+
+var substr = function(string, start, length) {
+ var stop;
+ if (typeof length !== "undefined")
+ stop = start + length;
+ return substring(string, start, stop);
+};
+
+var substring = function(string, start, stop) {
+ var range = point2unit(string, [start, stop]);
+ return string.substring(range[0], range[1]);
+};
+
return {
decode: decode,
encode: encode,
+
+ point2unit: point2unit,
+ unit2point: unit2point,
+
+ charAt: charAt,
+ length: length,
+ substr: substr,
+ substring: substring,
};
});