1
+ // Adapted from abab
2
+ //
3
+
4
+ const keystr =
5
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' ;
6
+
7
+ /**
8
+ * Implementation of atob() according to the HTML and Infra specs, except that
9
+ * instead of throwing INVALID_CHARACTER_ERR we return null.
10
+ */
11
+ function atob ( data ) {
12
+ if ( arguments . length === 0 ) {
13
+ throw new TypeError ( '1 argument required, but only 0 present.' ) ;
14
+ }
15
+
16
+ // Web IDL requires DOMStrings to just be converted using ECMAScript
17
+ // ToString, which in our case amounts to using a template literal.
18
+ data = `${ data } ` ;
19
+ // "Remove all ASCII whitespace from data."
20
+ data = data . replace ( / [ \t \n \f \r ] / g, '' ) ;
21
+
22
+ // "If data's length divides by 4 leaving no remainder, then: if data ends
23
+ // with one or two U+003D (=) code points, then remove them from data."
24
+ if ( data . length % 4 === 0 ) {
25
+ data = data . replace ( / = = ? $ / , '' ) ;
26
+ }
27
+
28
+ // "If data's length divides by 4 leaving a remainder of 1, then return
29
+ // failure."
30
+ //
31
+ // "If data contains a code point that is not one of
32
+ //
33
+ // U+002B (+)
34
+ // U+002F (/)
35
+ // ASCII alphanumeric
36
+ //
37
+ // then return failure."
38
+ if ( data . length % 4 === 1 || / [ ^ + / 0 - 9 A - Z a - z ] / . test ( data ) ) {
39
+ throw new DOMException (
40
+ 'Failed to decode base64: invalid character' ,
41
+ 'InvalidCharacterError'
42
+ ) ;
43
+ }
44
+
45
+ // "Let output be an empty byte sequence."
46
+ let output = '' ;
47
+ // "Let buffer be an empty buffer that can have bits appended to it."
48
+ //
49
+ // We append bits via left-shift and or. accumulatedBits is used to track
50
+ // when we've gotten to 24 bits.
51
+ let buffer = 0 ;
52
+ let accumulatedBits = 0 ;
53
+
54
+ // "Let position be a position variable for data, initially pointing at the
55
+ // start of data."
56
+ //
57
+ // "While position does not point past the end of data:"
58
+ for ( let i = 0 ; i < data . length ; i ++ ) {
59
+ // "Find the code point pointed to by position in the second column of
60
+ // Table 1: The Base 64 Alphabet of RFC 4648. Let n be the number given in
61
+ // the first cell of the same row.
62
+ //
63
+ // "Append to buffer the six bits corresponding to n, most significant bit
64
+ // first."
65
+ //
66
+ // atobLookup() implements the table from RFC 4648.
67
+ buffer <<= 6 ;
68
+ buffer |= atobLookup ( data [ i ] ) ;
69
+ accumulatedBits += 6 ;
70
+
71
+ // "If buffer has accumulated 24 bits, interpret them as three 8-bit
72
+ // big-endian numbers. Append three bytes with values equal to those
73
+ // numbers to output, in the same order, and then empty buffer."
74
+ if ( accumulatedBits === 24 ) {
75
+ output += String . fromCharCode ( ( buffer & 0xff0000 ) >> 16 ) ;
76
+ output += String . fromCharCode ( ( buffer & 0xff00 ) >> 8 ) ;
77
+ output += String . fromCharCode ( buffer & 0xff ) ;
78
+ buffer = accumulatedBits = 0 ;
79
+ }
80
+ // "Advance position by 1."
81
+ }
82
+
83
+ // "If buffer is not empty, it contains either 12 or 18 bits. If it contains
84
+ // 12 bits, then discard the last four and interpret the remaining eight as
85
+ // an 8-bit big-endian number. If it contains 18 bits, then discard the last
86
+ // two and interpret the remaining 16 as two 8-bit big-endian numbers. Append
87
+ // the one or two bytes with values equal to those one or two numbers to
88
+ // output, in the same order."
89
+ if ( accumulatedBits === 12 ) {
90
+ buffer >>= 4 ;
91
+ output += String . fromCharCode ( buffer ) ;
92
+ } else if ( accumulatedBits === 18 ) {
93
+ buffer >>= 2 ;
94
+ output += String . fromCharCode ( ( buffer & 0xff00 ) >> 8 ) ;
95
+ output += String . fromCharCode ( buffer & 0xff ) ;
96
+ }
97
+
98
+ // "Return output."
99
+ return output ;
100
+ }
101
+
102
+ /**
103
+ * A lookup table for atob(), which converts an ASCII character to the
104
+ * corresponding six-bit number.
105
+ */
106
+ function atobLookup ( chr ) {
107
+ const index = keystr . indexOf ( chr ) ;
108
+
109
+ // Throw exception if character is not in the lookup string; should not be hit in tests
110
+ return index < 0 ? undefined : index ;
111
+ }
112
+
113
+
114
+ /**
115
+ * btoa() as defined by the HTML and Infra specs, which mostly just references
116
+ * RFC 4648.
117
+ */
118
+ function btoa ( s ) {
119
+ if ( arguments . length === 0 ) {
120
+ throw new TypeError ( '1 argument required, but only 0 present.' ) ;
121
+ }
122
+
123
+ let i ;
124
+
125
+ // String conversion as required by Web IDL.
126
+ s = `${ s } ` ;
127
+
128
+ // "The btoa() method must throw an "InvalidCharacterError" DOMException if
129
+ // data contains any character whose code point is greater than U+00FF."
130
+ for ( i = 0 ; i < s . length ; i ++ ) {
131
+ if ( s . charCodeAt ( i ) > 255 ) {
132
+ throw new DOMException (
133
+ 'The string to be encoded contains characters outside of the Latin1 range.' ,
134
+ 'InvalidCharacterError'
135
+ ) ;
136
+ }
137
+ }
138
+
139
+ let out = '' ;
140
+
141
+ for ( i = 0 ; i < s . length ; i += 3 ) {
142
+ const groupsOfSix = [ undefined , undefined , undefined , undefined ] ;
143
+
144
+ groupsOfSix [ 0 ] = s . charCodeAt ( i ) >> 2 ;
145
+ groupsOfSix [ 1 ] = ( s . charCodeAt ( i ) & 0x03 ) << 4 ;
146
+
147
+ if ( s . length > i + 1 ) {
148
+ groupsOfSix [ 1 ] |= s . charCodeAt ( i + 1 ) >> 4 ;
149
+ groupsOfSix [ 2 ] = ( s . charCodeAt ( i + 1 ) & 0x0f ) << 2 ;
150
+ }
151
+
152
+ if ( s . length > i + 2 ) {
153
+ groupsOfSix [ 2 ] |= s . charCodeAt ( i + 2 ) >> 6 ;
154
+ groupsOfSix [ 3 ] = s . charCodeAt ( i + 2 ) & 0x3f ;
155
+ }
156
+
157
+ for ( let j = 0 ; j < groupsOfSix . length ; j ++ ) {
158
+ if ( typeof groupsOfSix [ j ] === 'undefined' ) {
159
+ out += '=' ;
160
+ } else {
161
+ out += btoaLookup ( groupsOfSix [ j ] ) ;
162
+ }
163
+ }
164
+ }
165
+
166
+ return out ;
167
+ }
168
+
169
+ /**
170
+ * Lookup table for btoa(), which converts a six-bit number into the
171
+ * corresponding ASCII character.
172
+ */
173
+ function btoaLookup ( index ) {
174
+ if ( index >= 0 && index < 64 ) {
175
+ return keystr [ index ] ;
176
+ }
177
+
178
+ // Throw INVALID_CHARACTER_ERR exception here -- won't be hit in the tests.
179
+ return undefined ;
180
+ }
181
+
182
+
183
+ globalThis . atob = atob ;
184
+ globalThis . btoa = btoa ;
0 commit comments