@zxing/text-encoding
Version:
Polyfill for the Encoding Living Standard's API.
129 lines • 5.49 kB
JavaScript
import { DEFAULT_ENCODING } from "../encoding/defaultEncoding";
import { encoders } from "../encoding/encoding-factory";
import { getEncoding } from "../encoding/encodings";
import { finished } from "../encoding/finished";
import { end_of_stream } from "../encoding/terminology";
import { stringToCodePoints, ToDictionary } from "../encoding/utilities";
import { getGlobalScope } from "../helper/getGlobalScope";
import { Stream } from "./Stream";
/**
* @constructor
* @param {string=} label The label of the encoding. NONSTANDARD.
* @param {Object=} options NONSTANDARD.
*/
export class TextEncoder {
constructor(label, options) {
const optionsMap = ToDictionary(options);
// A TextEncoder object has an associated encoding and encoder.
/** @private */
this._encoding = null;
/** @private @type {?Encoder} */
this._encoder = null;
// Non-standard
/** @private @type {boolean} */
this._do_not_flush = false;
/** @private @type {string} */
this._fatal = Boolean(optionsMap['fatal']) ? 'fatal' : 'replacement';
// 1. Let enc be a new TextEncoder object.
// const enc = this;
// no need to do this as this is a proper class
// now and TSC will handle transpilation to older platforms
// 2. Set enc's encoding to UTF-8's encoder.
if (Boolean(optionsMap['NONSTANDARD_allowLegacyEncoding'])) {
// NONSTANDARD behavior.
label = !!label ? String(label) : DEFAULT_ENCODING;
const encoding = getEncoding(label);
if (encoding === null || encoding.name === 'replacement')
throw RangeError('Unknown encoding: ' + label);
if (!encoders[encoding.name]) {
throw Error('Encoder not present.' +
' Did you forget to include encoding-indexes.js first?');
}
this._encoding = encoding;
// EXPERIMENTAL_CODE
// } else if (["iso-8859-1", "ISO-8859-1", "latin-1", "latin1", "LATIN-1", "LATIN1"].indexOf(label) !== -1) {
// this._encoding = getEncoding('iso-8859-1');
}
else {
// Standard behavior.
this._encoding = getEncoding('utf-8');
const glo = getGlobalScope() || {};
if (label !== undefined && 'console' in glo) {
console.warn('TextEncoder constructor called with encoding label, '
+ 'which is ignored.');
}
}
// For pre-ES5 runtimes:
// if (!Object.defineProperty)
// this.encoding = enc._encoding.name.toLowerCase();
// 3. Return enc.
// return enc;
}
// if(Object.defineProperty) {
// // The encoding attribute's getter must return encoding's name.
// Object.defineProperty(TextEncoder.prototype, 'encoding', {
// /** @this {TextEncoder} */
// get: function () { return this._encoding.name.toLowerCase(); }
// });
// }
get encoding() {
return this._encoding.name.toLowerCase();
}
/**
* @param {string=} opt_string The string to encode.
* @param {Object=} options
* @return {!Uint8Array} Encoded bytes, as a Uint8Array.
*/
encode(opt_string, options) {
opt_string = opt_string === undefined ? '' : String(opt_string);
const optionsMap = ToDictionary(options);
// NOTE: This option is nonstandard. None of the encodings
// permitted for encoding (i.e. UTF-8, UTF-16) are stateful when
// the input is a USVString so streaming is not necessary.
if (!this._do_not_flush)
this._encoder = encoders[this._encoding.name]({
fatal: this._fatal === 'fatal'
});
this._do_not_flush = Boolean(optionsMap['stream']);
// 1. Convert input to a stream.
const input = new Stream(stringToCodePoints(opt_string));
// 2. Let output be a new stream
const output = [];
/** @type {?(number|!Array.<number>)} */
let result;
// 3. While true, run these substeps:
while (true) {
// 1. Let token be the result of reading from input.
const token = input.read();
if (token === end_of_stream)
break;
// 2. Let result be the result of processing token for encoder,
// input, output.
result = this._encoder.handler(input, token);
if (result === finished)
break;
if (Array.isArray(result))
output.push.apply(output, /**@type {!Array.<number>}*/ (result));
else
output.push(result);
}
// TODO: Align with spec algorithm.
if (!this._do_not_flush) {
while (true) {
result = this._encoder.handler(input, input.read());
if (result === finished)
break;
if (Array.isArray(result))
output.push.apply(output, /**@type {!Array.<number>}*/ (result));
else
output.push(result);
}
this._encoder = null;
}
// 3. If result is finished, convert output into a byte sequence,
// and then return a Uint8Array object wrapping an ArrayBuffer
// containing output.
return new Uint8Array(output);
}
}
//# sourceMappingURL=TextEncoder.js.map