(function (root, factory) {
    if (typeof define === "function" && define.amd) {
        define([], factory);
    } else if (typeof exports === "object") {
        module.exports = factory();
    } else {
		var textEncoding = factory();
        root.TextEncoder = textEncoding.TextEncoder;
		root.TextDecoder = textEncoding.TextDecoder;
    }
}(this, function () {
	"use strict";
	// return native implementation if available
	var g = typeof global !== 'undefined' ? global : self;
	if (typeof g.TextEncoder !== 'undefined' && typeof g.TextDecoder !== 'undefined') {
		return {'TextEncoder': g.TextEncoder, 'TextDecoder': g.TextDecoder};
	}

	// allowed encoding strings for utf-8
	var utf8Encodings = [
		'utf8',
		'utf-8',
		'unicode-1-1-utf-8'
	];

	var TextEncoder = function(encoding) {
		if (utf8Encodings.indexOf(encoding) < 0 && typeof encoding !== 'undefined' && encoding !== null) {
			throw new RangeError('Invalid encoding type. Only utf-8 is supported');
		} else {
			this.encoding = 'utf-8';
			this.encode = function(str) {
				if (typeof str !== 'string') {
					throw new TypeError('passed argument must be of type string');
				}
				var binstr = unescape(encodeURIComponent(str)),
					arr = new Uint8Array(binstr.length);
				binstr.split('').forEach(function(char, i) {
					arr[i] = char.charCodeAt(0);
				});
				return arr;
			};
		}
	};

	var TextDecoder = function(encoding, options) {
		if (utf8Encodings.indexOf(encoding) < 0 && typeof encoding !== 'undefined' && encoding !== null) {
			throw new RangeError('Invalid encoding type. Only utf-8 is supported');
		}
		this.encoding = 'utf-8';
		this.ignoreBOM = false;
		this.fatal = (typeof options !== 'undefined' && 'fatal' in options) ? options.fatal : false;
		if (typeof this.fatal !== 'boolean') {
			throw new TypeError('fatal flag must be boolean');
		}
		this.decode = function (view, options) {
			if (typeof view === 'undefined') {
				return '';
			}

			var stream = (typeof options !== 'undefined' && 'stream' in options) ? options.stream : false;
			if (typeof stream !== 'boolean') {
				throw new TypeError('stream option must be boolean');
			}

			if (!ArrayBuffer.isView(view)) {
				throw new TypeError('passed argument must be an array buffer view');
			} else {
				var arr = new Uint8Array(view.buffer, view.byteOffset, view.byteLength),
					charArr = new Array(arr.length);
				arr.forEach(function(charcode, i) {
					charArr[i] = String.fromCharCode(charcode);
				});
				return decodeURIComponent(escape(charArr.join('')));
			}
		};
	};
	return {'TextEncoder': TextEncoder, 'TextDecoder': TextDecoder};
}));
