"use strict";12/**3* Implementation of atob() according to the HTML and Infra specs, except that4* instead of throwing INVALID_CHARACTER_ERR we return null.5*/6function atob(data) {7if (arguments.length === 0) {8throw new TypeError("1 argument required, but only 0 present.");9}1011// Web IDL requires DOMStrings to just be converted using ECMAScript12// ToString, which in our case amounts to using a template literal.13data = `${data}`;14// "Remove all ASCII whitespace from data."15data = data.replace(/[ \t\n\f\r]/g, "");16// "If data's length divides by 4 leaving no remainder, then: if data ends17// with one or two U+003D (=) code points, then remove them from data."18if (data.length % 4 === 0) {19data = data.replace(/==?$/, "");20}21// "If data's length divides by 4 leaving a remainder of 1, then return22// failure."23//24// "If data contains a code point that is not one of25//26// U+002B (+)27// U+002F (/)28// ASCII alphanumeric29//30// then return failure."31if (data.length % 4 === 1 || /[^+/0-9A-Za-z]/.test(data)) {32return null;33}34// "Let output be an empty byte sequence."35let output = "";36// "Let buffer be an empty buffer that can have bits appended to it."37//38// We append bits via left-shift and or. accumulatedBits is used to track39// when we've gotten to 24 bits.40let buffer = 0;41let accumulatedBits = 0;42// "Let position be a position variable for data, initially pointing at the43// start of data."44//45// "While position does not point past the end of data:"46for (let i = 0; i < data.length; i++) {47// "Find the code point pointed to by position in the second column of48// Table 1: The Base 64 Alphabet of RFC 4648. Let n be the number given in49// the first cell of the same row.50//51// "Append to buffer the six bits corresponding to n, most significant bit52// first."53//54// atobLookup() implements the table from RFC 4648.55buffer <<= 6;56buffer |= atobLookup(data[i]);57accumulatedBits += 6;58// "If buffer has accumulated 24 bits, interpret them as three 8-bit59// big-endian numbers. Append three bytes with values equal to those60// numbers to output, in the same order, and then empty buffer."61if (accumulatedBits === 24) {62output += String.fromCharCode((buffer & 0xff0000) >> 16);63output += String.fromCharCode((buffer & 0xff00) >> 8);64output += String.fromCharCode(buffer & 0xff);65buffer = accumulatedBits = 0;66}67// "Advance position by 1."68}69// "If buffer is not empty, it contains either 12 or 18 bits. If it contains70// 12 bits, then discard the last four and interpret the remaining eight as71// an 8-bit big-endian number. If it contains 18 bits, then discard the last72// two and interpret the remaining 16 as two 8-bit big-endian numbers. Append73// the one or two bytes with values equal to those one or two numbers to74// output, in the same order."75if (accumulatedBits === 12) {76buffer >>= 4;77output += String.fromCharCode(buffer);78} else if (accumulatedBits === 18) {79buffer >>= 2;80output += String.fromCharCode((buffer & 0xff00) >> 8);81output += String.fromCharCode(buffer & 0xff);82}83// "Return output."84return output;85}86/**87* A lookup table for atob(), which converts an ASCII character to the88* corresponding six-bit number.89*/9091const keystr =92"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";9394function atobLookup(chr) {95const index = keystr.indexOf(chr);96// Throw exception if character is not in the lookup string; should not be hit in tests97return index < 0 ? undefined : index;98}99100module.exports = atob;101102103