* Copyright 2017 Sam Thorogood. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
* @fileoverview Polyfill for TextEncoder and TextDecoder.
*
* You probably want `text.min.js`, and not this file directly.
*/
const global = (0,eval)("this");
(function(scope) {
'use strict';
if (scope['TextEncoder'] && scope['TextDecoder']) {
return false;
}
const validUtfLabels = ['utf-8', 'utf8', 'unicode-1-1-utf-8'];
* @constructor
*/
function FastTextEncoder() {
}
Object.defineProperty(FastTextEncoder.prototype, 'encoding', {value: 'utf-8'});
* @param {string} string
* @param {{stream: boolean}=} options
* @return {!Uint8Array}
*/
FastTextEncoder.prototype['encode'] = function(string, options={stream: false}) {
if (options.stream) {
throw new Error(`Failed to encode: the 'stream' option is unsupported.`);
}
let pos = 0;
const len = string.length;
let at = 0;
let tlen = Math.max(32, len + (len >>> 1) + 7);
let target = new Uint8Array((tlen >>> 3) << 3);
while (pos < len) {
let value = string.charCodeAt(pos++);
if (value >= 0xd800 && value <= 0xdbff) {
if (pos < len) {
const extra = string.charCodeAt(pos);
if ((extra & 0xfc00) === 0xdc00) {
++pos;
value = ((value & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000;
}
}
if (value >= 0xd800 && value <= 0xdbff) {
continue;
}
}
if (at + 4 > target.length) {
tlen += 8;
tlen *= (1.0 + (pos / string.length) * 2);
tlen = (tlen >>> 3) << 3;
const update = new Uint8Array(tlen);
update.set(target);
target = update;
}
if ((value & 0xffffff80) === 0) {
target[at++] = value;
continue;
} else if ((value & 0xfffff800) === 0) {
target[at++] = ((value >>> 6) & 0x1f) | 0xc0;
} else if ((value & 0xffff0000) === 0) {
target[at++] = ((value >>> 12) & 0x0f) | 0xe0;
target[at++] = ((value >>> 6) & 0x3f) | 0x80;
} else if ((value & 0xffe00000) === 0) {
target[at++] = ((value >>> 18) & 0x07) | 0xf0;
target[at++] = ((value >>> 12) & 0x3f) | 0x80;
target[at++] = ((value >>> 6) & 0x3f) | 0x80;
} else {
continue;
}
target[at++] = (value & 0x3f) | 0x80;
}
return target.slice ? target.slice(0, at) : target.subarray(0, at);
}
* @constructor
* @param {string=} utfLabel
* @param {{fatal: boolean}=} options
*/
function FastTextDecoder(utfLabel='utf-8', options={fatal: false}) {
if (validUtfLabels.indexOf(utfLabel.toLowerCase()) === -1) {
throw new RangeError(
`Failed to construct 'TextDecoder': The encoding label provided ('${utfLabel}') is invalid.`);
}
}
Object.defineProperty(FastTextDecoder.prototype, 'encoding', {value: 'utf-8'});
Object.defineProperty(FastTextDecoder.prototype, 'fatal', {value: false});
Object.defineProperty(FastTextDecoder.prototype, 'ignoreBOM', {value: false});
* @param {!Uint8Array} bytes
* @return {string}
*/
function decodeBuffer(bytes) {
return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString('utf-8');
}
* @param {!Uint8Array} bytes
* @return {string}
*/
function decodeSyncXHR(bytes) {
const b = new Blob([bytes], {type: 'text/plain;charset=UTF-8'});
const u = URL.createObjectURL(b);
try {
const x = new XMLHttpRequest();
x.open('GET', u, false);
x.send();
return x.responseText;
} catch (e) {
return decodeFallback(bytes);
} finally {
URL.revokeObjectURL(u);
}
}
* @param {!Uint8Array} bytes
* @return {string}
*/
function decodeFallback(bytes) {
let inputIndex = 0;
const pendingSize = Math.min(256 * 256, bytes.length + 1);
const pending = new Uint16Array(pendingSize);
const chunks = [];
let pendingIndex = 0;
for (;;) {
const more = inputIndex < bytes.length;
if (!more || (pendingIndex >= pendingSize - 1)) {
chunks.push(String.fromCharCode.apply(null, pending.subarray(0, pendingIndex)));
if (!more) {
return chunks.join('');
}
bytes = bytes.subarray(inputIndex);
inputIndex = 0;
pendingIndex = 0;
}
const byte1 = bytes[inputIndex++];
if ((byte1 & 0x80) === 0) {
pending[pendingIndex++] = byte1;
} else if ((byte1 & 0xe0) === 0xc0) {
const byte2 = bytes[inputIndex++] & 0x3f;
pending[pendingIndex++] = ((byte1 & 0x1f) << 6) | byte2;
} else if ((byte1 & 0xf0) === 0xe0) {
const byte2 = bytes[inputIndex++] & 0x3f;
const byte3 = bytes[inputIndex++] & 0x3f;
pending[pendingIndex++] = ((byte1 & 0x1f) << 12) | (byte2 << 6) | byte3;
} else if ((byte1 & 0xf8) === 0xf0) {
const byte2 = bytes[inputIndex++] & 0x3f;
const byte3 = bytes[inputIndex++] & 0x3f;
const byte4 = bytes[inputIndex++] & 0x3f;
let codepoint = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0c) | (byte3 << 0x06) | byte4;
if (codepoint > 0xffff) {
codepoint -= 0x10000;
pending[pendingIndex++] = (codepoint >>> 10) & 0x3ff | 0xd800;
codepoint = 0xdc00 | codepoint & 0x3ff;
}
pending[pendingIndex++] = codepoint;
} else {
}
}
}
let decodeImpl = decodeFallback;
if (typeof Buffer === 'function' && Buffer.from) {
decodeImpl = decodeBuffer;
} else if (typeof Blob === 'function' && typeof URL === 'function' && typeof URL.createObjectURL === 'function') {
decodeImpl = decodeSyncXHR;
}
* @param {(!ArrayBuffer|!ArrayBufferView)} buffer
* @param {{stream: boolean}=} options
* @return {string}
*/
FastTextDecoder.prototype['decode'] = function(buffer, options={stream: false}) {
if (options['stream']) {
throw new Error(`Failed to decode: the 'stream' option is unsupported.`);
}
if (!buffer)
return;
let bytes;
if (buffer instanceof Uint8Array) {
bytes = buffer;
} else if (buffer.buffer instanceof ArrayBuffer) {
bytes = new Uint8Array(buffer.buffer);
} else {
bytes = new Uint8Array(buffer);
}
return decodeImpl( (bytes));
}
scope['TextEncoder'] = FastTextEncoder;
scope['TextDecoder'] = FastTextDecoder;
}(typeof window !== 'undefined' ? window : (typeof global !== 'undefined' ? global : this)));