2016-11-01 00:22:38 +01:00
|
|
|
/**
|
|
|
|
* the type for base 64
|
|
|
|
*/
|
2022-09-13 19:13:04 +02:00
|
|
|
export type TStringInputType = 'string' | 'base64' | 'base64uri';
|
2016-11-01 00:22:38 +01:00
|
|
|
|
2025-09-12 18:57:31 +00:00
|
|
|
/**
|
|
|
|
* Cross-platform base64 implementation
|
|
|
|
* Works in both Node.js and browser environments
|
|
|
|
*/
|
|
|
|
const universalBase64 = {
|
|
|
|
encode: (str: string): string => {
|
|
|
|
if (typeof Buffer !== 'undefined') {
|
|
|
|
// Node.js environment
|
|
|
|
return Buffer.from(str, 'utf8').toString('base64');
|
|
|
|
} else if (typeof btoa !== 'undefined') {
|
|
|
|
// Browser environment
|
|
|
|
// Handle Unicode properly
|
|
|
|
const utf8Bytes = new TextEncoder().encode(str);
|
|
|
|
const binaryString = Array.from(utf8Bytes, byte => String.fromCharCode(byte)).join('');
|
|
|
|
return btoa(binaryString);
|
|
|
|
} else {
|
|
|
|
// Fallback pure JS implementation
|
|
|
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
|
|
const bytes = new TextEncoder().encode(str);
|
|
|
|
let result = '';
|
|
|
|
let i = 0;
|
|
|
|
|
|
|
|
while (i < bytes.length) {
|
|
|
|
const a = bytes[i++];
|
|
|
|
const b = i < bytes.length ? bytes[i++] : 0;
|
|
|
|
const c = i < bytes.length ? bytes[i++] : 0;
|
|
|
|
|
|
|
|
const bitmap = (a << 16) | (b << 8) | c;
|
|
|
|
|
|
|
|
result += chars.charAt((bitmap >> 18) & 63);
|
|
|
|
result += chars.charAt((bitmap >> 12) & 63);
|
|
|
|
result += i - 2 < bytes.length ? chars.charAt((bitmap >> 6) & 63) : '=';
|
|
|
|
result += i - 1 < bytes.length ? chars.charAt(bitmap & 63) : '=';
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
decode: (str: string): string => {
|
|
|
|
// Handle base64uri by converting back to standard base64
|
|
|
|
const base64String = str
|
|
|
|
.replace(/-/g, '+')
|
|
|
|
.replace(/_/g, '/')
|
|
|
|
.padEnd(str.length + ((4 - (str.length % 4)) % 4), '=');
|
|
|
|
|
|
|
|
if (typeof Buffer !== 'undefined') {
|
|
|
|
// Node.js environment
|
|
|
|
return Buffer.from(base64String, 'base64').toString('utf8');
|
|
|
|
} else if (typeof atob !== 'undefined') {
|
|
|
|
// Browser environment
|
|
|
|
const binaryString = atob(base64String);
|
|
|
|
const bytes = new Uint8Array(binaryString.length);
|
|
|
|
for (let i = 0; i < binaryString.length; i++) {
|
|
|
|
bytes[i] = binaryString.charCodeAt(i);
|
|
|
|
}
|
|
|
|
return new TextDecoder().decode(bytes);
|
|
|
|
} else {
|
|
|
|
// Fallback pure JS implementation
|
|
|
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
|
|
let bytes: number[] = [];
|
|
|
|
let i = 0;
|
|
|
|
|
|
|
|
while (i < base64String.length) {
|
|
|
|
const encoded1 = chars.indexOf(base64String.charAt(i++));
|
|
|
|
const encoded2 = chars.indexOf(base64String.charAt(i++));
|
|
|
|
const encoded3 = chars.indexOf(base64String.charAt(i++));
|
|
|
|
const encoded4 = chars.indexOf(base64String.charAt(i++));
|
|
|
|
|
|
|
|
const bitmap = (encoded1 << 18) | (encoded2 << 12) | (encoded3 << 6) | encoded4;
|
|
|
|
|
|
|
|
bytes.push((bitmap >> 16) & 255);
|
|
|
|
if (encoded3 !== 64) bytes.push((bitmap >> 8) & 255);
|
|
|
|
if (encoded4 !== 64) bytes.push(bitmap & 255);
|
|
|
|
}
|
|
|
|
|
|
|
|
return new TextDecoder().decode(new Uint8Array(bytes));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-11-01 00:22:38 +01:00
|
|
|
/**
|
|
|
|
* handle base64 strings
|
|
|
|
*/
|
|
|
|
export class Base64 {
|
2018-07-21 14:37:39 +02:00
|
|
|
private refString: string;
|
2022-09-13 19:13:04 +02:00
|
|
|
constructor(inputStringArg, typeArg: TStringInputType) {
|
2017-10-05 15:55:59 +02:00
|
|
|
switch (typeArg) {
|
|
|
|
case 'string': // easiest case
|
2018-07-21 14:37:39 +02:00
|
|
|
this.refString = inputStringArg;
|
|
|
|
break;
|
2017-10-05 15:55:59 +02:00
|
|
|
case 'base64':
|
2018-07-21 14:37:39 +02:00
|
|
|
this.refString = base64.decode(inputStringArg);
|
|
|
|
break;
|
2017-10-05 15:55:59 +02:00
|
|
|
case 'base64uri':
|
2018-07-21 14:37:39 +02:00
|
|
|
this.refString = base64.decode(inputStringArg);
|
2016-11-01 00:22:38 +01:00
|
|
|
}
|
2017-10-05 15:55:59 +02:00
|
|
|
}
|
2016-11-01 00:22:38 +01:00
|
|
|
|
2017-10-05 15:55:59 +02:00
|
|
|
/**
|
|
|
|
* the simple string (unencoded)
|
|
|
|
*/
|
2018-07-21 14:37:39 +02:00
|
|
|
get simpleString() {
|
|
|
|
return this.refString;
|
2017-10-05 15:55:59 +02:00
|
|
|
}
|
2016-11-01 00:22:38 +01:00
|
|
|
|
2017-10-05 15:55:59 +02:00
|
|
|
/**
|
|
|
|
* the base64 encoded version of the original string
|
|
|
|
*/
|
2018-07-21 14:37:39 +02:00
|
|
|
get base64String() {
|
|
|
|
return base64.encode(this.refString);
|
2017-10-05 15:55:59 +02:00
|
|
|
}
|
2016-11-01 00:22:38 +01:00
|
|
|
|
2017-10-05 15:55:59 +02:00
|
|
|
/**
|
|
|
|
* the base64uri encoded version of the original string
|
|
|
|
*/
|
2018-07-21 14:37:39 +02:00
|
|
|
get base64UriString() {
|
|
|
|
return base64.encodeUri(this.refString);
|
2017-10-05 15:55:59 +02:00
|
|
|
}
|
2016-11-01 18:59:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
export let base64 = {
|
2017-10-05 15:55:59 +02:00
|
|
|
/**
|
|
|
|
* encodes the string
|
|
|
|
*/
|
|
|
|
encode: (stringArg: string) => {
|
2025-09-12 18:57:31 +00:00
|
|
|
return universalBase64.encode(stringArg);
|
2017-10-05 15:55:59 +02:00
|
|
|
},
|
2016-11-01 18:59:43 +01:00
|
|
|
|
2017-10-05 15:55:59 +02:00
|
|
|
/**
|
|
|
|
* encodes a stringArg to base64 uri style
|
|
|
|
*/
|
|
|
|
encodeUri: (stringArg: string) => {
|
2025-09-12 18:57:31 +00:00
|
|
|
return universalBase64.encode(stringArg)
|
|
|
|
.replace(/\+/g, '-')
|
|
|
|
.replace(/\//g, '_')
|
|
|
|
.replace(/=/g, '');
|
2017-10-05 15:55:59 +02:00
|
|
|
},
|
2016-11-01 18:59:43 +01:00
|
|
|
|
2017-10-05 15:55:59 +02:00
|
|
|
/**
|
|
|
|
* decodes a base64 encoded string
|
|
|
|
*/
|
|
|
|
decode: (stringArg: string) => {
|
2025-09-12 18:57:31 +00:00
|
|
|
return universalBase64.decode(stringArg);
|
2020-12-31 03:56:40 +00:00
|
|
|
},
|
2022-09-13 19:23:11 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param stringArg
|
|
|
|
* checks wether the string is base64 encoded
|
|
|
|
*/
|
|
|
|
isBase64: (stringArg: string) => {
|
|
|
|
const regex = /^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?$/;
|
|
|
|
return regex.test(stringArg);
|
|
|
|
},
|
2018-07-21 14:37:39 +02:00
|
|
|
};
|