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