UNPKG

10.4 kBJavaScriptView Raw
1/**
2 * base64.ts
3 *
4 * Licensed under the BSD 3-Clause License.
5 * http://opensource.org/licenses/BSD-3-Clause
6 *
7 * References:
8 * http://en.wikipedia.org/wiki/Base64
9 *
10 * @author Dan Kogai (https://github.com/dankogai)
11 */
12const version = '3.7.2';
13/**
14 * @deprecated use lowercase `version`.
15 */
16const VERSION = version;
17const _hasatob = typeof atob === 'function';
18const _hasbtoa = typeof btoa === 'function';
19const _hasBuffer = typeof Buffer === 'function';
20const _TD = typeof TextDecoder === 'function' ? new TextDecoder() : undefined;
21const _TE = typeof TextEncoder === 'function' ? new TextEncoder() : undefined;
22const b64ch = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
23const b64chs = Array.prototype.slice.call(b64ch);
24const b64tab = ((a) => {
25 let tab = {};
26 a.forEach((c, i) => tab[c] = i);
27 return tab;
28})(b64chs);
29const b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/;
30const _fromCC = String.fromCharCode.bind(String);
31const _U8Afrom = typeof Uint8Array.from === 'function'
32 ? Uint8Array.from.bind(Uint8Array)
33 : (it, fn = (x) => x) => new Uint8Array(Array.prototype.slice.call(it, 0).map(fn));
34const _mkUriSafe = (src) => src
35 .replace(/=/g, '').replace(/[+\/]/g, (m0) => m0 == '+' ? '-' : '_');
36const _tidyB64 = (s) => s.replace(/[^A-Za-z0-9\+\/]/g, '');
37/**
38 * polyfill version of `btoa`
39 */
40const btoaPolyfill = (bin) => {
41 // console.log('polyfilled');
42 let u32, c0, c1, c2, asc = '';
43 const pad = bin.length % 3;
44 for (let i = 0; i < bin.length;) {
45 if ((c0 = bin.charCodeAt(i++)) > 255 ||
46 (c1 = bin.charCodeAt(i++)) > 255 ||
47 (c2 = bin.charCodeAt(i++)) > 255)
48 throw new TypeError('invalid character found');
49 u32 = (c0 << 16) | (c1 << 8) | c2;
50 asc += b64chs[u32 >> 18 & 63]
51 + b64chs[u32 >> 12 & 63]
52 + b64chs[u32 >> 6 & 63]
53 + b64chs[u32 & 63];
54 }
55 return pad ? asc.slice(0, pad - 3) + "===".substring(pad) : asc;
56};
57/**
58 * does what `window.btoa` of web browsers do.
59 * @param {String} bin binary string
60 * @returns {string} Base64-encoded string
61 */
62const _btoa = _hasbtoa ? (bin) => btoa(bin)
63 : _hasBuffer ? (bin) => Buffer.from(bin, 'binary').toString('base64')
64 : btoaPolyfill;
65const _fromUint8Array = _hasBuffer
66 ? (u8a) => Buffer.from(u8a).toString('base64')
67 : (u8a) => {
68 // cf. https://stackoverflow.com/questions/12710001/how-to-convert-uint8-array-to-base64-encoded-string/12713326#12713326
69 const maxargs = 0x1000;
70 let strs = [];
71 for (let i = 0, l = u8a.length; i < l; i += maxargs) {
72 strs.push(_fromCC.apply(null, u8a.subarray(i, i + maxargs)));
73 }
74 return _btoa(strs.join(''));
75 };
76/**
77 * converts a Uint8Array to a Base64 string.
78 * @param {boolean} [urlsafe] URL-and-filename-safe a la RFC4648 §5
79 * @returns {string} Base64 string
80 */
81const fromUint8Array = (u8a, urlsafe = false) => urlsafe ? _mkUriSafe(_fromUint8Array(u8a)) : _fromUint8Array(u8a);
82// This trick is found broken https://github.com/dankogai/js-base64/issues/130
83// const utob = (src: string) => unescape(encodeURIComponent(src));
84// reverting good old fationed regexp
85const cb_utob = (c) => {
86 if (c.length < 2) {
87 var cc = c.charCodeAt(0);
88 return cc < 0x80 ? c
89 : cc < 0x800 ? (_fromCC(0xc0 | (cc >>> 6))
90 + _fromCC(0x80 | (cc & 0x3f)))
91 : (_fromCC(0xe0 | ((cc >>> 12) & 0x0f))
92 + _fromCC(0x80 | ((cc >>> 6) & 0x3f))
93 + _fromCC(0x80 | (cc & 0x3f)));
94 }
95 else {
96 var cc = 0x10000
97 + (c.charCodeAt(0) - 0xD800) * 0x400
98 + (c.charCodeAt(1) - 0xDC00);
99 return (_fromCC(0xf0 | ((cc >>> 18) & 0x07))
100 + _fromCC(0x80 | ((cc >>> 12) & 0x3f))
101 + _fromCC(0x80 | ((cc >>> 6) & 0x3f))
102 + _fromCC(0x80 | (cc & 0x3f)));
103 }
104};
105const re_utob = /[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g;
106/**
107 * @deprecated should have been internal use only.
108 * @param {string} src UTF-8 string
109 * @returns {string} UTF-16 string
110 */
111const utob = (u) => u.replace(re_utob, cb_utob);
112//
113const _encode = _hasBuffer
114 ? (s) => Buffer.from(s, 'utf8').toString('base64')
115 : _TE
116 ? (s) => _fromUint8Array(_TE.encode(s))
117 : (s) => _btoa(utob(s));
118/**
119 * converts a UTF-8-encoded string to a Base64 string.
120 * @param {boolean} [urlsafe] if `true` make the result URL-safe
121 * @returns {string} Base64 string
122 */
123const encode = (src, urlsafe = false) => urlsafe
124 ? _mkUriSafe(_encode(src))
125 : _encode(src);
126/**
127 * converts a UTF-8-encoded string to URL-safe Base64 RFC4648 §5.
128 * @returns {string} Base64 string
129 */
130const encodeURI = (src) => encode(src, true);
131// This trick is found broken https://github.com/dankogai/js-base64/issues/130
132// const btou = (src: string) => decodeURIComponent(escape(src));
133// reverting good old fationed regexp
134const re_btou = /[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g;
135const cb_btou = (cccc) => {
136 switch (cccc.length) {
137 case 4:
138 var cp = ((0x07 & cccc.charCodeAt(0)) << 18)
139 | ((0x3f & cccc.charCodeAt(1)) << 12)
140 | ((0x3f & cccc.charCodeAt(2)) << 6)
141 | (0x3f & cccc.charCodeAt(3)), offset = cp - 0x10000;
142 return (_fromCC((offset >>> 10) + 0xD800)
143 + _fromCC((offset & 0x3FF) + 0xDC00));
144 case 3:
145 return _fromCC(((0x0f & cccc.charCodeAt(0)) << 12)
146 | ((0x3f & cccc.charCodeAt(1)) << 6)
147 | (0x3f & cccc.charCodeAt(2)));
148 default:
149 return _fromCC(((0x1f & cccc.charCodeAt(0)) << 6)
150 | (0x3f & cccc.charCodeAt(1)));
151 }
152};
153/**
154 * @deprecated should have been internal use only.
155 * @param {string} src UTF-16 string
156 * @returns {string} UTF-8 string
157 */
158const btou = (b) => b.replace(re_btou, cb_btou);
159/**
160 * polyfill version of `atob`
161 */
162const atobPolyfill = (asc) => {
163 // console.log('polyfilled');
164 asc = asc.replace(/\s+/g, '');
165 if (!b64re.test(asc))
166 throw new TypeError('malformed base64.');
167 asc += '=='.slice(2 - (asc.length & 3));
168 let u24, bin = '', r1, r2;
169 for (let i = 0; i < asc.length;) {
170 u24 = b64tab[asc.charAt(i++)] << 18
171 | b64tab[asc.charAt(i++)] << 12
172 | (r1 = b64tab[asc.charAt(i++)]) << 6
173 | (r2 = b64tab[asc.charAt(i++)]);
174 bin += r1 === 64 ? _fromCC(u24 >> 16 & 255)
175 : r2 === 64 ? _fromCC(u24 >> 16 & 255, u24 >> 8 & 255)
176 : _fromCC(u24 >> 16 & 255, u24 >> 8 & 255, u24 & 255);
177 }
178 return bin;
179};
180/**
181 * does what `window.atob` of web browsers do.
182 * @param {String} asc Base64-encoded string
183 * @returns {string} binary string
184 */
185const _atob = _hasatob ? (asc) => atob(_tidyB64(asc))
186 : _hasBuffer ? (asc) => Buffer.from(asc, 'base64').toString('binary')
187 : atobPolyfill;
188//
189const _toUint8Array = _hasBuffer
190 ? (a) => _U8Afrom(Buffer.from(a, 'base64'))
191 : (a) => _U8Afrom(_atob(a), c => c.charCodeAt(0));
192/**
193 * converts a Base64 string to a Uint8Array.
194 */
195const toUint8Array = (a) => _toUint8Array(_unURI(a));
196//
197const _decode = _hasBuffer
198 ? (a) => Buffer.from(a, 'base64').toString('utf8')
199 : _TD
200 ? (a) => _TD.decode(_toUint8Array(a))
201 : (a) => btou(_atob(a));
202const _unURI = (a) => _tidyB64(a.replace(/[-_]/g, (m0) => m0 == '-' ? '+' : '/'));
203/**
204 * converts a Base64 string to a UTF-8 string.
205 * @param {String} src Base64 string. Both normal and URL-safe are supported
206 * @returns {string} UTF-8 string
207 */
208const decode = (src) => _decode(_unURI(src));
209/**
210 * check if a value is a valid Base64 string
211 * @param {String} src a value to check
212 */
213const isValid = (src) => {
214 if (typeof src !== 'string')
215 return false;
216 const s = src.replace(/\s+/g, '').replace(/={0,2}$/, '');
217 return !/[^\s0-9a-zA-Z\+/]/.test(s) || !/[^\s0-9a-zA-Z\-_]/.test(s);
218};
219//
220const _noEnum = (v) => {
221 return {
222 value: v, enumerable: false, writable: true, configurable: true
223 };
224};
225/**
226 * extend String.prototype with relevant methods
227 */
228const extendString = function () {
229 const _add = (name, body) => Object.defineProperty(String.prototype, name, _noEnum(body));
230 _add('fromBase64', function () { return decode(this); });
231 _add('toBase64', function (urlsafe) { return encode(this, urlsafe); });
232 _add('toBase64URI', function () { return encode(this, true); });
233 _add('toBase64URL', function () { return encode(this, true); });
234 _add('toUint8Array', function () { return toUint8Array(this); });
235};
236/**
237 * extend Uint8Array.prototype with relevant methods
238 */
239const extendUint8Array = function () {
240 const _add = (name, body) => Object.defineProperty(Uint8Array.prototype, name, _noEnum(body));
241 _add('toBase64', function (urlsafe) { return fromUint8Array(this, urlsafe); });
242 _add('toBase64URI', function () { return fromUint8Array(this, true); });
243 _add('toBase64URL', function () { return fromUint8Array(this, true); });
244};
245/**
246 * extend Builtin prototypes with relevant methods
247 */
248const extendBuiltins = () => {
249 extendString();
250 extendUint8Array();
251};
252const gBase64 = {
253 version: version,
254 VERSION: VERSION,
255 atob: _atob,
256 atobPolyfill: atobPolyfill,
257 btoa: _btoa,
258 btoaPolyfill: btoaPolyfill,
259 fromBase64: decode,
260 toBase64: encode,
261 encode: encode,
262 encodeURI: encodeURI,
263 encodeURL: encodeURI,
264 utob: utob,
265 btou: btou,
266 decode: decode,
267 isValid: isValid,
268 fromUint8Array: fromUint8Array,
269 toUint8Array: toUint8Array,
270 extendString: extendString,
271 extendUint8Array: extendUint8Array,
272 extendBuiltins: extendBuiltins,
273};
274// makecjs:CUT //
275export { version };
276export { VERSION };
277export { _atob as atob };
278export { atobPolyfill };
279export { _btoa as btoa };
280export { btoaPolyfill };
281export { decode as fromBase64 };
282export { encode as toBase64 };
283export { utob };
284export { encode };
285export { encodeURI };
286export { encodeURI as encodeURL };
287export { btou };
288export { decode };
289export { isValid };
290export { fromUint8Array };
291export { toUint8Array };
292export { extendString };
293export { extendUint8Array };
294export { extendBuiltins };
295// and finally,
296export { gBase64 as Base64 };