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