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         for (i = 0; i < l; i += 4) {
124             n = ((b64tab[b64.charAt(i)]     || 0) << 18) |
125                 ((b64tab[b64.charAt(i + 1)] || 0) << 12) |
126                 ((b64tab[b64.charAt(i + 2)] || 0) <<  6) |
127                 ((b64tab[b64.charAt(i + 3)] || 0));
128             bin[o]     =  n >> 16;
129             bin[o + 1] = (n >> 8) & 0xff;
130             bin[o + 2] =  n       & 0xff;
131             o += 3;
132         }
133         l = 3 * l - [0, 0, 2, 1][padlen];
134         return bin.subarray(0, l);
135     }
136     /**
137      * @param {!Uint8Array} uni
138      * @return {!Uint8Array}
139      */
140     function convertUTF16ArrayToUTF8Array(uni) {
141         var i, n,
142             l = uni.length,
143             o = 0,
144             bin = new Uint8Array(new ArrayBuffer(3 * l));
145         for (i = 0; i < l; i += 1) {
146             n = /**@type{!number}*/(uni[i]);
147             if (n < 0x80) {
148                 bin[o++] = n;
149             } else if (n < 0x800) {
150                 bin[o++] = 0xc0 | (n >>>  6);
151                 bin[o++] = 0x80 | (n & 0x3f);
152             } else {
153                 bin[o++] = 0xe0 | ((n >>> 12) & 0x0f);
154                 bin[o++] = 0x80 | ((n >>>  6) & 0x3f);
155                 bin[o++] = 0x80 |  (n         & 0x3f);
156             }
157         }
158         return bin.subarray(0, o);
159     }
160     /**
161      * @param {!Uint8Array} bin
162      * @return {!Uint8Array}
163      */
164     function convertUTF8ArrayToUTF16Array(bin) {
165         var i, c0, c1, c2,
166             l = bin.length,
167             uni = new Uint8Array(new ArrayBuffer(l)),
168             o = 0;
169         for (i = 0; i < l; i += 1) {
170             c0 = /**@type{!number}*/(bin[i]);
171             if (c0 < 0x80) {
172                 uni[o++] = c0;
173             } else {
174                 i += 1;
175                 c1 = /**@type{!number}*/(bin[i]);
176                 if (c0 < 0xe0) {
177                     uni[o++] = ((c0 & 0x1f) << 6) | (c1 & 0x3f);
178                 } else {
179                     i += 1;
180                     c2 = /**@type{!number}*/(bin[i]);
181                     uni[o++] = ((c0 & 0x0f) << 12) | ((c1 & 0x3f) << 6) |
182                             (c2 & 0x3f);
183                 }
184             }
185         }
186         return uni.subarray(0, o);
187     }
188     /**
189      * @param {!string} bin
190      * @return {!string}
191      */
192     function convertUTF8StringToBase64(bin) {
193         return convertUTF8ArrayToBase64(stringToArray(bin));
194     }
195     /**
196      * @param {!string} b64
197      * @return {!string}
198      */
199     function convertBase64ToUTF8String(b64) {
200         return String.fromCharCode.apply(String, convertBase64ToUTF8Array(b64));
201     }
202     /**
203      * @param {!string} bin
204      * @return {!Uint8Array}
205      */
206     function convertUTF8StringToUTF16Array(bin) {
207         return convertUTF8ArrayToUTF16Array(stringToArray(bin));
208     }
209     /**
210      * @param {!Uint8Array} bin
211      * @return {!string}
212      */
213     function convertUTF8ArrayToUTF16String(bin) {
214         // this conversion is done in chunks to avoid a stack overflow in
215         // apply()
216         var b = convertUTF8ArrayToUTF16Array(bin),
217             r = "",
218             i = 0,
219             chunksize = 45000;
220         while (i < b.length) {
221             r += String.fromCharCode.apply(String, b.subarray(i, i + chunksize));
222             i += chunksize;
223         }
224         return r;
225     }
226     /**
227      * @param {!Array.<number>|!string} bin
228      * @param {!number} i
229      * @param {!number} end
230      * @return {!string}
231      */
232     function convertUTF8StringToUTF16String_internal(bin, i, end) {
233         var c0, c1, c2, j,
234             str = "";
235         for (j = i; j < end; j += 1) {
236             c0 = bin.charCodeAt(j) & 0xff;
237             if (c0 < 0x80) {
238                 str += String.fromCharCode(c0);
239             } else {
240                 j += 1;
241                 c1 = bin.charCodeAt(j) & 0xff;
242                 if (c0 < 0xe0) {
243                     str += String.fromCharCode(((c0 & 0x1f) << 6) |
244                         (c1 & 0x3f));
245                 } else {
246                     j += 1;
247                     c2 = bin.charCodeAt(j) & 0xff;
248                     str += String.fromCharCode(((c0 & 0x0f) << 12) |
249                             ((c1 & 0x3f) << 6) | (c2 & 0x3f));
250                 }
251             }
252         }
253         return str;
254     }
255 
256     /**
257      * Convert a utf-8 array into a utf-16 string.
258      * The input array is treated as a list of values between 0 and 255.
259      * This function works with a callback and splits the work up in parts
260      * between which it yields to the main thread.
261      * After each part the progress is reported with the callback function that
262      * also passes a booleant that indicates if the job has finished.
263      * If the conversion should stop, the callback should return false.
264      *
265      * @param {!Array.<number>|!string} bin
266      * @param {!function(!string, boolean):boolean} callback
267      * @return {undefined}
268      */
269     function convertUTF8StringToUTF16String(bin, callback) {
270         var /**@const@type{!number}*/
271             partsize = 100000,
272             /**@type{!string}*/
273             str = "",
274             /**@type{!number}*/
275             pos = 0;
276         if (bin.length < partsize) {
277             callback(convertUTF8StringToUTF16String_internal(bin, 0,
278                     bin.length), true);
279             return;
280         }
281         // make a local copy if the input is a string, to avoid modification
282         if (typeof bin !== "string") {
283             bin = bin.slice();
284         }
285         function f() {
286             var end = pos + partsize;
287             if (end > bin.length) {
288                 end = bin.length;
289             }
290             str += convertUTF8StringToUTF16String_internal(bin, pos, end);
291             pos = end;
292             end = pos === bin.length;
293             if (callback(str, end) && !end) {
294                 runtime.setTimeout(f, 0);
295             }
296         }
297         f();
298     }
299     /**
300      * @param {!string} uni
301      * @return {!Uint8Array}
302      */
303     function convertUTF16StringToUTF8Array(uni) {
304         return convertUTF16ArrayToUTF8Array(stringToArray(uni));
305     }
306     /**
307      * @param {!Uint8Array} uni
308      * @return {!string}
309      */
310     function convertUTF16ArrayToUTF8String(uni) {
311         return String.fromCharCode.apply(String,
312                  convertUTF16ArrayToUTF8Array(uni));
313     }
314     /**
315      * @param {!string} uni
316      * @return {!string}
317      */
318     function convertUTF16StringToUTF8String(uni) {
319         return String.fromCharCode.apply(String,
320                  convertUTF16ArrayToUTF8Array(stringToArray(uni)));
321     }
322 
323     if (window && window.btoa) {
324         /**
325          * @param {!string} b
326          * @return {!string}
327          */
328         btoa = window.btoa;
329         /**
330          * @param {!string} uni
331          * @return {!string}
332          */
333         convertUTF16StringToBase64 = function (uni) {
334             return btoa(convertUTF16StringToUTF8String(uni));
335         };
336     } else {
337         btoa = convertUTF8StringToBase64;
338         /**
339          * @param {!string} uni
340          * @return {!string}
341          */
342         convertUTF16StringToBase64 = function (uni) {
343             return convertUTF8ArrayToBase64(convertUTF16StringToUTF8Array(uni));
344         };
345     }
346     if (window && window.atob) {
347         /**
348          * @param {!string} a
349          * @return {!string}
350          */
351         atob = window.atob;
352         /**
353          * @param {!string} b64
354          * @return {!string}
355          */
356         convertBase64ToUTF16String = function (b64) {
357             var /**@type{!string}*/
358                 b = atob(b64);
359             return convertUTF8StringToUTF16String_internal(b, 0, b.length);
360         };
361     } else {
362         atob = convertBase64ToUTF8String;
363         /**
364          * @param {!string} b64
365          * @return {!string}
366          */
367         convertBase64ToUTF16String = function (b64) {
368             return convertUTF8ArrayToUTF16String(convertBase64ToUTF8Array(b64));
369         };
370     }
371 
372     /**
373      * @constructor
374      * @struct
375      */
376     core.Base64 = function Base64() {
377         this.convertUTF8ArrayToBase64 = convertUTF8ArrayToBase64;
378         this.convertByteArrayToBase64 = convertUTF8ArrayToBase64;
379         this.convertBase64ToUTF8Array = convertBase64ToUTF8Array;
380         this.convertBase64ToByteArray = convertBase64ToUTF8Array;
381         this.convertUTF16ArrayToUTF8Array = convertUTF16ArrayToUTF8Array;
382         this.convertUTF16ArrayToByteArray = convertUTF16ArrayToUTF8Array;
383         this.convertUTF8ArrayToUTF16Array = convertUTF8ArrayToUTF16Array;
384         this.convertByteArrayToUTF16Array = convertUTF8ArrayToUTF16Array;
385         this.convertUTF8StringToBase64 = convertUTF8StringToBase64;
386         this.convertBase64ToUTF8String = convertBase64ToUTF8String;
387         this.convertUTF8StringToUTF16Array = convertUTF8StringToUTF16Array;
388         this.convertUTF8ArrayToUTF16String = convertUTF8ArrayToUTF16String;
389         this.convertByteArrayToUTF16String = convertUTF8ArrayToUTF16String;
390         this.convertUTF8StringToUTF16String = convertUTF8StringToUTF16String;
391         this.convertUTF16StringToUTF8Array = convertUTF16StringToUTF8Array;
392         this.convertUTF16StringToByteArray = convertUTF16StringToUTF8Array;
393         this.convertUTF16ArrayToUTF8String = convertUTF16ArrayToUTF8String;
394         this.convertUTF16StringToUTF8String = convertUTF16StringToUTF8String;
395         this.convertUTF16StringToBase64 = convertUTF16StringToBase64;
396         this.convertBase64ToUTF16String = convertBase64ToUTF16String;
397         this.fromBase64 = convertBase64ToUTF8String;
398         this.toBase64 = convertUTF8StringToBase64;
399         this.atob = atob;
400         this.btoa = btoa;
401         this.utob = convertUTF16StringToUTF8String;
402         this.btou = convertUTF8StringToUTF16String;
403         this.encode = convertUTF16StringToBase64;
404         /**
405          * @param {!string} u
406          * @return {!string}
407          */
408         this.encodeURI = function (u) {
409             return convertUTF16StringToBase64(u).replace(/[+\/]/g,
410                 /**
411                  * @param {!string} m0
412                  * @return {!string}
413                  */
414                 function (m0) {
415                     return m0 === '+' ? '-' : '_';
416                 }).replace(/\\=+$/, '');
417         };
418         /**
419          * @param {!string} a
420          * @return {!string}
421          */
422         this.decode = function (a) {
423             return convertBase64ToUTF16String(a.replace(/[\-_]/g,
424                 /**
425                  * @param {!string} m0
426                  * @return {!string}
427                  */
428                 function (m0) {
429                     return m0 === '-' ? '+' : '/';
430                 }));
431         };
432         return this;
433     };
434     return core.Base64;
435 }
436 /**
437  * @constructor
438  */
439 core.Base64 = makeBase64();
440