1 /**
  2  * Copyright (C) 2012 KO GmbH <copyright@kogmbh.com>
  3  *
  4  * @licstart
  5  * This file is part of WebODF.
  6  *
  7  * WebODF is free software: you can redistribute it and/or modify it
  8  * under the terms of the GNU Affero General Public License (GNU AGPL)
  9  * as published by the Free Software Foundation, either version 3 of
 10  * the License, or (at your option) any later version.
 11  *
 12  * WebODF is distributed in the hope that it will be useful, but
 13  * WITHOUT ANY WARRANTY; without even the implied warranty of
 14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 15  * GNU Affero General Public License for more details.
 16  *
 17  * You should have received a copy of the GNU Affero General Public License
 18  * along with WebODF.  If not, see <http://www.gnu.org/licenses/>.
 19  * @licend
 20  *
 21  * @source: http://www.webodf.org/
 22  * @source: https://github.com/kogmbh/WebODF/
 23  */
 24 
 25 /*global core, runtime, ArrayBuffer, Uint8Array*/
 26 /*jslint bitwise: true, regexp: true, plusplus: true*/
 27 
 28 /*
 29  * $Id: base64.js,v 0.9 2009/03/01 20:51:18 dankogai Exp dankogai $
 30  */
 31 /**
 32  * @return {function(new:core.Base64):?}
 33  */
 34 function makeBase64() {
 35     "use strict";
 36     /**
 37      * @param {!string} bin
 38      * @return {!Object.<!string,!number>}
 39      */
 40     function makeB64tab(bin) {
 41         var /**@type{!Object.<!string,!number>}*/
 42             t = {},
 43             i, l;
 44         for (i = 0, l = bin.length; i < l; i += 1) {
 45             t[bin.charAt(i)] = i;
 46         }
 47         return t;
 48     }
 49     var /**@const@type{!string}*/
 50         b64chars
 51         = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
 52         /**@const@type{!Object.<!string,!number>}*/
 53         b64tab = makeB64tab(b64chars),
 54         /**@type{!function(!string):!string}*/
 55         convertUTF16StringToBase64,
 56         /**@type{!function(!string):!string}*/
 57         convertBase64ToUTF16String,
 58         /**@type{?Window}*/
 59         window = runtime.getWindow(),
 60         /**@type{!function(!string):!string}*/
 61         btoa,
 62         /**@type{!function(!string):!string}*/
 63         atob;
 64 
 65     /**
 66      * @param {!string} s
 67      * @return {!Uint8Array}
 68      */
 69     function stringToArray(s) {
 70         var i,
 71             l = s.length,
 72             a = new Uint8Array(new ArrayBuffer(l));
 73         for (i = 0; i < l; i += 1) {
 74             a[i] = s.charCodeAt(i) & 0xff;
 75         }
 76         return a;
 77     }
 78 
 79     /**
 80      * @param {!Uint8Array} bin
 81      * @return {!string}
 82      */
 83     function convertUTF8ArrayToBase64(bin) {
 84         var /**@type{!number}*/
 85             n,
 86             /**@type{!string}*/
 87             b64 = "",
 88             i,
 89             l = bin.length - 2;
 90         for (i = 0; i < l; i += 3) {
 91             n = (bin[i] << 16) | (bin[i + 1] << 8) | bin[i + 2];
 92             b64 += /**@type{!string}*/(b64chars[n >>> 18]);
 93             b64 += /**@type{!string}*/(b64chars[(n >>> 12) & 63]);
 94             b64 += /**@type{!string}*/(b64chars[(n >>>  6) & 63]);
 95             b64 += /**@type{!string}*/(b64chars[n          & 63]);
 96         }
 97         if (i === l + 1) { // 1 byte left
 98             n = bin[i] << 4;
 99             b64 += /**@type{!string}*/(b64chars[n >>> 6]);
100             b64 += /**@type{!string}*/(b64chars[n & 63]);
101             b64 += "==";
102         } else if (i === l) { // 2 bytes left
103             n = (bin[i] << 10) | (bin[i + 1] << 2);
104             b64 += /**@type{!string}*/(b64chars[n >>> 12]);
105             b64 += /**@type{!string}*/(b64chars[(n >>> 6) & 63]);
106             b64 += /**@type{!string}*/(b64chars[n & 63]);
107             b64 += "=";
108         }
109         return b64;
110     }
111     /**
112      * @param {!string} b64
113      * @return {!Uint8Array}
114      */
115     function convertBase64ToUTF8Array(b64) {
116         b64 = b64.replace(/[^A-Za-z0-9+\/]+/g, '');
117         var l = b64.length,
118             bin = new Uint8Array(new ArrayBuffer(3 * l)),
119             padlen = b64.length % 4,
120             o = 0,
121             i,
122             n,
123             a = [0, 0, 2, 1];
124         for (i = 0; i < l; i += 4) {
125             n = ((b64tab[b64.charAt(i)]     || 0) << 18) |
126                 ((b64tab[b64.charAt(i + 1)] || 0) << 12) |
127                 ((b64tab[b64.charAt(i + 2)] || 0) <<  6) |
128                 ((b64tab[b64.charAt(i + 3)] || 0));
129             bin[o]     =  n >> 16;
130             bin[o + 1] = (n >> 8) & 0xff;
131             bin[o + 2] =  n       & 0xff;
132             o += 3;
133         }
134         l = 3 * l - a[padlen];
135         return bin.subarray(0, l);
136     }
137     /**
138      * @param {!Uint8Array} uni
139      * @return {!Uint8Array}
140      */
141     function convertUTF16ArrayToUTF8Array(uni) {
142         var i, n,
143             l = uni.length,
144             o = 0,
145             bin = new Uint8Array(new ArrayBuffer(3 * l));
146         for (i = 0; i < l; i += 1) {
147             n = /**@type{!number}*/(uni[i]);
148             if (n < 0x80) {
149                 bin[o++] = n;
150             } else if (n < 0x800) {
151                 bin[o++] = 0xc0 | (n >>>  6);
152                 bin[o++] = 0x80 | (n & 0x3f);
153             } else {
154                 bin[o++] = 0xe0 | ((n >>> 12) & 0x0f);
155                 bin[o++] = 0x80 | ((n >>>  6) & 0x3f);
156                 bin[o++] = 0x80 |  (n         & 0x3f);
157             }
158         }
159         return bin.subarray(0, o);
160     }
161     /**
162      * @param {!Uint8Array} bin
163      * @return {!Uint8Array}
164      */
165     function convertUTF8ArrayToUTF16Array(bin) {
166         var i, c0, c1, c2,
167             l = bin.length,
168             uni = new Uint8Array(new ArrayBuffer(l)),
169             o = 0;
170         for (i = 0; i < l; i += 1) {
171             c0 = /**@type{!number}*/(bin[i]);
172             if (c0 < 0x80) {
173                 uni[o++] = c0;
174             } else {
175                 i += 1;
176                 c1 = /**@type{!number}*/(bin[i]);
177                 if (c0 < 0xe0) {
178                     uni[o++] = ((c0 & 0x1f) << 6) | (c1 & 0x3f);
179                 } else {
180                     i += 1;
181                     c2 = /**@type{!number}*/(bin[i]);
182                     uni[o++] = ((c0 & 0x0f) << 12) | ((c1 & 0x3f) << 6) |
183                             (c2 & 0x3f);
184                 }
185             }
186         }
187         return uni.subarray(0, o);
188     }
189     /**
190      * @param {!string} bin
191      * @return {!string}
192      */
193     function convertUTF8StringToBase64(bin) {
194         return convertUTF8ArrayToBase64(stringToArray(bin));
195     }
196     /**
197      * @param {!string} b64
198      * @return {!string}
199      */
200     function convertBase64ToUTF8String(b64) {
201         return String.fromCharCode.apply(String, convertBase64ToUTF8Array(b64));
202     }
203     /**
204      * @param {!string} bin
205      * @return {!Uint8Array}
206      */
207     function convertUTF8StringToUTF16Array(bin) {
208         return convertUTF8ArrayToUTF16Array(stringToArray(bin));
209     }
210     /**
211      * @param {!Uint8Array} bin
212      * @return {!string}
213      */
214     function convertUTF8ArrayToUTF16String(bin) {
215         // this conversion is done in chunks to avoid a stack overflow in
216         // apply()
217         var b = convertUTF8ArrayToUTF16Array(bin),
218             r = "",
219             i = 0,
220             chunksize = 45000;
221         while (i < b.length) {
222             r += String.fromCharCode.apply(String, b.subarray(i, i + chunksize));
223             i += chunksize;
224         }
225         return r;
226     }
227     /**
228      * @param {!Array.<number>|!string} bin
229      * @param {!number} i
230      * @param {!number} end
231      * @return {!string}
232      */
233     function convertUTF8StringToUTF16String_internal(bin, i, end) {
234         var c0, c1, c2, j,
235             str = "";
236         for (j = i; j < end; j += 1) {
237             c0 = bin.charCodeAt(j) & 0xff;
238             if (c0 < 0x80) {
239                 str += String.fromCharCode(c0);
240             } else {
241                 j += 1;
242                 c1 = bin.charCodeAt(j) & 0xff;
243                 if (c0 < 0xe0) {
244                     str += String.fromCharCode(((c0 & 0x1f) << 6) |
245                         (c1 & 0x3f));
246                 } else {
247                     j += 1;
248                     c2 = bin.charCodeAt(j) & 0xff;
249                     str += String.fromCharCode(((c0 & 0x0f) << 12) |
250                             ((c1 & 0x3f) << 6) | (c2 & 0x3f));
251                 }
252             }
253         }
254         return str;
255     }
256 
257     /**
258      * Convert a utf-8 array into a utf-16 string.
259      * The input array is treated as a list of values between 0 and 255.
260      * This function works with a callback and splits the work up in parts
261      * between which it yields to the main thread.
262      * After each part the progress is reported with the callback function that
263      * also passes a booleant that indicates if the job has finished.
264      * If the conversion should stop, the callback should return false.
265      *
266      * @param {!Array.<number>|!string} bin
267      * @param {!function(!string, boolean):boolean} callback
268      * @return {undefined}
269      */
270     function convertUTF8StringToUTF16String(bin, callback) {
271         var /**@const@type{!number}*/
272             partsize = 100000,
273             /**@type{!string}*/
274             str = "",
275             /**@type{!number}*/
276             pos = 0;
277         if (bin.length < partsize) {
278             callback(convertUTF8StringToUTF16String_internal(bin, 0,
279                     bin.length), true);
280             return;
281         }
282         // make a local copy if the input is a string, to avoid modification
283         if (typeof bin !== "string") {
284             bin = bin.slice();
285         }
286         function f() {
287             var end = pos + partsize;
288             if (end > bin.length) {
289                 end = bin.length;
290             }
291             str += convertUTF8StringToUTF16String_internal(bin, pos, end);
292             pos = end;
293             end = pos === bin.length;
294             if (callback(str, end) && !end) {
295                 runtime.setTimeout(f, 0);
296             }
297         }
298         f();
299     }
300     /**
301      * @param {!string} uni
302      * @return {!Uint8Array}
303      */
304     function convertUTF16StringToUTF8Array(uni) {
305         return convertUTF16ArrayToUTF8Array(stringToArray(uni));
306     }
307     /**
308      * @param {!Uint8Array} uni
309      * @return {!string}
310      */
311     function convertUTF16ArrayToUTF8String(uni) {
312         return String.fromCharCode.apply(String,
313                  convertUTF16ArrayToUTF8Array(uni));
314     }
315     /**
316      * @param {!string} uni
317      * @return {!string}
318      */
319     function convertUTF16StringToUTF8String(uni) {
320         return String.fromCharCode.apply(String,
321                  convertUTF16ArrayToUTF8Array(stringToArray(uni)));
322     }
323 
324     if (window && window.btoa) {
325         /**
326          * @param {!string} b
327          * @return {!string}
328          */
329         btoa = window.btoa;
330         /**
331          * @param {!string} uni
332          * @return {!string}
333          */
334         convertUTF16StringToBase64 = function (uni) {
335             return btoa(convertUTF16StringToUTF8String(uni));
336         };
337     } else {
338         btoa = convertUTF8StringToBase64;
339         /**
340          * @param {!string} uni
341          * @return {!string}
342          */
343         convertUTF16StringToBase64 = function (uni) {
344             return convertUTF8ArrayToBase64(convertUTF16StringToUTF8Array(uni));
345         };
346     }
347     if (window && window.atob) {
348         /**
349          * @param {!string} a
350          * @return {!string}
351          */
352         atob = window.atob;
353         /**
354          * @param {!string} b64
355          * @return {!string}
356          */
357         convertBase64ToUTF16String = function (b64) {
358             var /**@type{!string}*/
359                 b = atob(b64);
360             return convertUTF8StringToUTF16String_internal(b, 0, b.length);
361         };
362     } else {
363         atob = convertBase64ToUTF8String;
364         /**
365          * @param {!string} b64
366          * @return {!string}
367          */
368         convertBase64ToUTF16String = function (b64) {
369             return convertUTF8ArrayToUTF16String(convertBase64ToUTF8Array(b64));
370         };
371     }
372 
373     /**
374      * @constructor
375      * @struct
376      */
377     core.Base64 = function Base64() {
378         this.convertUTF8ArrayToBase64 = convertUTF8ArrayToBase64;
379         this.convertByteArrayToBase64 = convertUTF8ArrayToBase64;
380         this.convertBase64ToUTF8Array = convertBase64ToUTF8Array;
381         this.convertBase64ToByteArray = convertBase64ToUTF8Array;
382         this.convertUTF16ArrayToUTF8Array = convertUTF16ArrayToUTF8Array;
383         this.convertUTF16ArrayToByteArray = convertUTF16ArrayToUTF8Array;
384         this.convertUTF8ArrayToUTF16Array = convertUTF8ArrayToUTF16Array;
385         this.convertByteArrayToUTF16Array = convertUTF8ArrayToUTF16Array;
386         this.convertUTF8StringToBase64 = convertUTF8StringToBase64;
387         this.convertBase64ToUTF8String = convertBase64ToUTF8String;
388         this.convertUTF8StringToUTF16Array = convertUTF8StringToUTF16Array;
389         this.convertUTF8ArrayToUTF16String = convertUTF8ArrayToUTF16String;
390         this.convertByteArrayToUTF16String = convertUTF8ArrayToUTF16String;
391         this.convertUTF8StringToUTF16String = convertUTF8StringToUTF16String;
392         this.convertUTF16StringToUTF8Array = convertUTF16StringToUTF8Array;
393         this.convertUTF16StringToByteArray = convertUTF16StringToUTF8Array;
394         this.convertUTF16ArrayToUTF8String = convertUTF16ArrayToUTF8String;
395         this.convertUTF16StringToUTF8String = convertUTF16StringToUTF8String;
396         this.convertUTF16StringToBase64 = convertUTF16StringToBase64;
397         this.convertBase64ToUTF16String = convertBase64ToUTF16String;
398         this.fromBase64 = convertBase64ToUTF8String;
399         this.toBase64 = convertUTF8StringToBase64;
400         this.atob = atob;
401         this.btoa = btoa;
402         this.utob = convertUTF16StringToUTF8String;
403         this.btou = convertUTF8StringToUTF16String;
404         this.encode = convertUTF16StringToBase64;
405         /**
406          * @param {!string} u
407          * @return {!string}
408          */
409         this.encodeURI = function (u) {
410             return convertUTF16StringToBase64(u).replace(/[+\/]/g,
411                 /**
412                  * @param {!string} m0
413                  * @return {!string}
414                  */
415                 function (m0) {
416                     return m0 === '+' ? '-' : '_';
417                 }).replace(/\\=+$/, '');
418         };
419         /**
420          * @param {!string} a
421          * @return {!string}
422          */
423         this.decode = function (a) {
424             return convertBase64ToUTF16String(a.replace(/[\-_]/g,
425                 /**
426                  * @param {!string} m0
427                  * @return {!string}
428                  */
429                 function (m0) {
430                     return m0 === '-' ? '+' : '/';
431                 }));
432         };
433         return this;
434     };
435     return core.Base64;
436 }
437 /**
438  * @constructor
439  */
440 core.Base64 = makeBase64();
441