387 lines
14 KiB
JavaScript
387 lines
14 KiB
JavaScript
'use strict';
|
|
// Async base64 encoding and decoding
|
|
// Bundles TextEncoderLite and b64.js for utf8 and typed array support.
|
|
// See: https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding
|
|
|
|
function asyncToBase64(value, encoding, onSuccess, onError) {
|
|
try {
|
|
switch (encoding) {
|
|
case 'ascii':
|
|
var result = window.btoa(value);
|
|
onSuccess(result);
|
|
break;
|
|
case 'utf8':
|
|
value = Base64Utils.encodeToUtf8Array(value);
|
|
// Fall through into byte array case.
|
|
case undefined:
|
|
var result = Base64Utils.uint8ToBase64(value);
|
|
onSuccess(result);
|
|
break;
|
|
default:
|
|
onError('Unknown encoding \'' + encoding + '\'.');
|
|
}
|
|
} catch (error) {
|
|
if (error.name === 'InvalidCharacterError') {
|
|
onError('Couldn\'t convert non-latin1 characters to base64.');
|
|
} else if (error.message) {
|
|
onError('Couldn\'t convert to base64 because ' + error.message + '.');
|
|
} else {
|
|
onError('Couldn\'t convert to base64 because ' + error + '.');
|
|
}
|
|
}
|
|
}
|
|
|
|
function asyncFromBase64(value, encoding, onSuccess, onError) {
|
|
try {
|
|
switch (encoding) {
|
|
case 'ascii':
|
|
var result = window.atob(Base64Utils.cleanupBase64(value));
|
|
onSuccess(result);
|
|
break;
|
|
case 'utf8':
|
|
var utf8ByteArray = Base64Utils.b64ToByteArray(Base64Utils.cleanupBase64(value));
|
|
var result = Base64Utils.decodeFromUtf8Array(utf8ByteArray);
|
|
onSuccess(result);
|
|
break;
|
|
default:
|
|
onError('Unknown encoding \'' + encoding + '\'.');
|
|
}
|
|
} catch (error) {
|
|
if (error.name == 'InvalidCharacterError') {
|
|
onError('Couldn\'t convert because the base64 decodes to non-latin1 characters.');
|
|
} else if (error.message) {
|
|
onError('Couldn\'t convert from base64 because ' + error.message + '.');
|
|
} else {
|
|
onError('Couldn\'t convert from base64 because ' + error + '.');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Utility helper functions:
|
|
// cleanupBase64, uint8ToBase64, b64ToByteArray, encodeToUtf8Array, decodeFromUtf8Array
|
|
function Base64Utils() {
|
|
}
|
|
|
|
// Remove invalid characters and correct padding to accept more liberal input.
|
|
// This is modified from https://github.com/feross/buffer/blob/master/index.js by feross et. al.
|
|
Base64Utils.cleanupBase64 = function(dirty) {
|
|
// Trim whitespace.
|
|
dirty = dirty.trim ? dirty.trim() : dirty.replace(/^\s+|\s+$/g, '');
|
|
|
|
// Remove anything outside our range.
|
|
var INVALID_BASE64_RE = /[^+\/0-9A-Za-z-_]/g;
|
|
dirty = dirty.replace(INVALID_BASE64_RE, '');
|
|
|
|
if (dirty.length < 2)
|
|
return '';
|
|
|
|
// Ensure padding is correct.
|
|
while (dirty.length % 4 !== 0)
|
|
dirty = dirty + '=';
|
|
|
|
return dirty;
|
|
}
|
|
|
|
// b64.js from https://github.com/beatgammit/base64-js by beatgammit, feross, and others.
|
|
// Modified to only export uint8ToBase64 and b64ToByteArray.
|
|
//
|
|
// The MIT License (MIT)
|
|
//
|
|
// Copyright (c) 2014
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
;(function (exports) {
|
|
var i, code, lookup, revLookup, Arr;
|
|
|
|
function initIfNeeded() {
|
|
if (code !== undefined)
|
|
return;
|
|
|
|
code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
lookup = [];
|
|
for (i = 0; i < code.length; i++)
|
|
lookup[i] = code[i];
|
|
revLookup = [];
|
|
for (i = 0; i < code.length; ++i)
|
|
revLookup[code.charCodeAt(i)] = i;
|
|
revLookup['-'.charCodeAt(0)] = 62;
|
|
revLookup['_'.charCodeAt(0)] = 63;
|
|
Arr = (typeof Uint8Array !== 'undefined') ? Uint8Array : Array;
|
|
}
|
|
|
|
function decode(elt) {
|
|
var v = revLookup[elt.charCodeAt(0)];
|
|
return v !== undefined ? v : -1;
|
|
}
|
|
|
|
function b64ToByteArray(b64) {
|
|
initIfNeeded();
|
|
|
|
var i, j, l, tmp, placeHolders, arr;
|
|
|
|
if (b64.length % 4 > 0)
|
|
throw new Error('the length is not a multiple of 4');
|
|
|
|
// the number of equal signs (place holders)
|
|
// if there are two placeholders, than the two characters before it
|
|
// represent one byte
|
|
// if there is only one, then the three characters before it represent 2 bytes
|
|
// this is just a cheap hack to not do indexOf twice
|
|
var len = b64.length;
|
|
placeHolders = b64.charAt(len - 2) === '=' ? 2 : b64.charAt(len - 1) === '=' ? 1 : 0;
|
|
|
|
// base64 is 4/3 + up to two characters of the original data
|
|
arr = new Arr(b64.length * 3 / 4 - placeHolders);
|
|
|
|
// if there are placeholders, only get up to the last complete 4 chars
|
|
l = placeHolders > 0 ? b64.length - 4 : b64.length;
|
|
|
|
var L = 0;
|
|
|
|
function push(v) {
|
|
arr[L++] = v;
|
|
}
|
|
|
|
for (i = 0, j = 0; i < l; i += 4, j += 3) {
|
|
tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3));
|
|
push((tmp & 0xFF0000) >> 16);
|
|
push((tmp & 0xFF00) >> 8);
|
|
push(tmp & 0xFF);
|
|
}
|
|
|
|
if (placeHolders === 2) {
|
|
tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4);
|
|
push(tmp & 0xFF);
|
|
} else if (placeHolders === 1) {
|
|
tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2);
|
|
push((tmp >> 8) & 0xFF);
|
|
push(tmp & 0xFF);
|
|
}
|
|
return arr;
|
|
}
|
|
|
|
function encode(num) {
|
|
return lookup[num];
|
|
}
|
|
|
|
function tripletToBase64(num) {
|
|
return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F);
|
|
}
|
|
|
|
function encodeChunk(uint8, start, end) {
|
|
var temp;
|
|
var output = [];
|
|
for (var i = start; i < end; i += 3) {
|
|
temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]);
|
|
output.push(tripletToBase64(temp));
|
|
}
|
|
return output.join('');
|
|
}
|
|
|
|
function uint8ToBase64(uint8) {
|
|
initIfNeeded();
|
|
|
|
var i;
|
|
var extraBytes = uint8.length % 3; // if we have 1 byte left, pad 2 bytes
|
|
var output = '';
|
|
var parts = [];
|
|
var temp, length;
|
|
var maxChunkLength = 16383; // must be multiple of 3
|
|
|
|
// go through the array every three bytes, we'll deal with trailing stuff later
|
|
for (i = 0, length = uint8.length - extraBytes; i < length; i += maxChunkLength)
|
|
parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > length ? length : (i + maxChunkLength)));
|
|
|
|
// pad the end with zeros, but make sure to not forget the extra bytes
|
|
switch (extraBytes) {
|
|
case 1:
|
|
temp = uint8[uint8.length - 1];
|
|
output += encode(temp >> 2);
|
|
output += encode((temp << 4) & 0x3F);
|
|
output += '==';
|
|
break;
|
|
case 2:
|
|
temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1]);
|
|
output += encode(temp >> 10);
|
|
output += encode((temp >> 4) & 0x3F);
|
|
output += encode((temp << 2) & 0x3F);
|
|
output += '=';
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
parts.push(output);
|
|
return parts.join('');
|
|
}
|
|
|
|
exports.uint8ToBase64 = uint8ToBase64;
|
|
exports.b64ToByteArray = b64ToByteArray;
|
|
}(Base64Utils));
|
|
|
|
// TextEncoderLite from https://github.com/coolaj86/TextEncoderLite/blob/master/index.js by
|
|
// coolaj86, feross, and others.
|
|
// Modified to only export encodeToUtf8Array and decodeFromUtf8Array.
|
|
//
|
|
// The MIT License (MIT)
|
|
//
|
|
// Copyright (c) Feross Aboukhadijeh, and other contributors.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
;(function (exports) {
|
|
function utf8ToBytes(string, units) {
|
|
units = units || Infinity;
|
|
var codePoint;
|
|
var length = string.length;
|
|
var leadSurrogate = null;
|
|
var bytes = [];
|
|
|
|
for (var i = 0; i < length; i++) {
|
|
codePoint = string.charCodeAt(i);
|
|
|
|
// is surrogate component
|
|
if (codePoint > 0xD7FF && codePoint < 0xE000) {
|
|
// last char was a lead
|
|
if (leadSurrogate) {
|
|
// 2 leads in a row
|
|
if (codePoint < 0xDC00) {
|
|
if ((units -= 3) > -1)
|
|
bytes.push(0xEF, 0xBF, 0xBD);
|
|
leadSurrogate = codePoint;
|
|
continue;
|
|
} else {
|
|
// valid surrogate pair
|
|
codePoint = leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00 | 0x10000;
|
|
leadSurrogate = null;
|
|
}
|
|
} else {
|
|
// no lead yet
|
|
if (codePoint > 0xDBFF) {
|
|
// unexpected trail
|
|
if ((units -= 3) > -1)
|
|
bytes.push(0xEF, 0xBF, 0xBD);
|
|
continue;
|
|
} else if (i + 1 === length) {
|
|
// unpaired lead
|
|
if ((units -= 3) > -1)
|
|
bytes.push(0xEF, 0xBF, 0xBD);
|
|
continue;
|
|
} else {
|
|
// valid lead
|
|
leadSurrogate = codePoint;
|
|
continue;
|
|
}
|
|
}
|
|
} else if (leadSurrogate) {
|
|
// valid bmp char, but last char was a lead
|
|
if ((units -= 3) > -1)
|
|
bytes.push(0xEF, 0xBF, 0xBD);
|
|
leadSurrogate = null;
|
|
}
|
|
|
|
// encode utf8
|
|
if (codePoint < 0x80) {
|
|
if ((units -= 1) < 0)
|
|
break;
|
|
bytes.push(codePoint);
|
|
} else if (codePoint < 0x800) {
|
|
if ((units -= 2) < 0)
|
|
break;
|
|
bytes.push(
|
|
codePoint >> 0x6 | 0xC0,
|
|
codePoint & 0x3F | 0x80
|
|
);
|
|
} else if (codePoint < 0x10000) {
|
|
if ((units -= 3) < 0)
|
|
break;
|
|
bytes.push(
|
|
codePoint >> 0xC | 0xE0,
|
|
codePoint >> 0x6 & 0x3F | 0x80,
|
|
codePoint & 0x3F | 0x80
|
|
);
|
|
} else if (codePoint < 0x200000) {
|
|
if ((units -= 4) < 0)
|
|
break;
|
|
bytes.push(
|
|
codePoint >> 0x12 | 0xF0,
|
|
codePoint >> 0xC & 0x3F | 0x80,
|
|
codePoint >> 0x6 & 0x3F | 0x80,
|
|
codePoint & 0x3F | 0x80
|
|
);
|
|
} else {
|
|
throw new Error('Invalid code point');
|
|
}
|
|
}
|
|
return bytes;
|
|
}
|
|
|
|
function utf8Slice(buf, start, end) {
|
|
var res = '';
|
|
var tmp = '';
|
|
end = Math.min(buf.length, end || Infinity);
|
|
start = start || 0;
|
|
for (var i = start; i < end; i++) {
|
|
if (buf[i] <= 0x7F) {
|
|
res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i]);
|
|
tmp = '';
|
|
} else {
|
|
tmp += '%' + buf[i].toString(16);
|
|
}
|
|
}
|
|
return res + decodeUtf8Char(tmp);
|
|
}
|
|
|
|
function decodeUtf8Char(str) {
|
|
try {
|
|
return decodeURIComponent(str);
|
|
} catch (err) {
|
|
return String.fromCharCode(0xFFFD); // UTF 8 invalid char
|
|
}
|
|
}
|
|
|
|
exports.encodeToUtf8Array = function(str) {
|
|
var result;
|
|
if ('undefined' === typeof Uint8Array)
|
|
result = utf8ToBytes(str);
|
|
else
|
|
result = new Uint8Array(utf8ToBytes(str));
|
|
return result;
|
|
}
|
|
|
|
exports.decodeFromUtf8Array = function(bytes) {
|
|
return utf8Slice(bytes, 0, bytes.length);
|
|
}
|
|
}(Base64Utils));
|