1 /* 2 Copyright 2008-2013 3 Matthias Ehmann, 4 Michael Gerhaeuser, 5 Carsten Miller, 6 Bianca Valentin, 7 Alfred Wassermann, 8 Peter Wilfahrt 9 10 This file is part of JSXGraph. 11 12 JSXGraph is free software dual licensed under the GNU LGPL or MIT License. 13 14 You can redistribute it and/or modify it under the terms of the 15 16 * GNU Lesser General Public License as published by 17 the Free Software Foundation, either version 3 of the License, or 18 (at your option) any later version 19 OR 20 * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT 21 22 JSXGraph is distributed in the hope that it will be useful, 23 but WITHOUT ANY WARRANTY; without even the implied warranty of 24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 GNU Lesser General Public License for more details. 26 27 You should have received a copy of the GNU Lesser General Public License and 28 the MIT License along with JSXGraph. If not, see <http://www.gnu.org/licenses/> 29 and <http://opensource.org/licenses/MIT/>. 30 */ 31 32 33 /*global JXG: true, define: true*/ 34 /*jslint nomen: true, plusplus: true, bitwise: true*/ 35 36 /* depends: 37 jxg 38 utils/encoding 39 */ 40 41 define(['jxg', 'utils/encoding'], function (JXG, Encoding) { 42 43 "use strict"; 44 45 var alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', 46 pad = '='; 47 48 // Util namespace 49 JXG.Util = JXG.Util || {}; 50 51 // Local helper functions 52 /** 53 * Extracts one byte from a string and ensures the result is less than or equal to 255. 54 * @param {String} s 55 * @param {Number} i 56 * @returns {Number} <= 255 57 * @private 58 */ 59 function _getByte(s, i) { 60 return s.charCodeAt(i) & 0xff; 61 } 62 63 /** 64 * Determines the index of a base64 character in the base64 alphabet. 65 * @param {String} s 66 * @param {Number} i 67 * @returns {Number} 68 * @throws {Error} If the character can not be found in the alphabet. 69 * @private 70 */ 71 function _getIndex(s, i) { 72 var idx = alphabet.indexOf(s.charAt(i)); 73 74 if (idx === -1) { 75 throw new Error('JSXGraph/utils/base64: Can\'t decode string (invalid character).'); 76 } 77 78 return idx; 79 } 80 81 /** 82 * Base64 routines 83 * @namespace 84 */ 85 JXG.Util.Base64 = { 86 /** 87 * Encode the given string. 88 * @param {String} input 89 * @returns {string} base64 encoded version of the input string. 90 */ 91 encode : function (input) { 92 var i, bin, len, padLen, encInput, 93 buffer = []; 94 95 encInput = Encoding.encode(input); 96 len = encInput.length; 97 padLen = len % 3; 98 99 for (i = 0; i < len - padLen; i += 3) { 100 bin = (_getByte(encInput, i) << 16) | (_getByte(encInput, i + 1) << 8) | (_getByte(encInput, i + 2)); 101 buffer.push( 102 alphabet.charAt(bin >> 18), 103 alphabet.charAt((bin >> 12) & 63), 104 alphabet.charAt((bin >> 6) & 63), 105 alphabet.charAt(bin & 63) 106 ); 107 } 108 109 switch (padLen) { 110 case 1: 111 bin = _getByte(encInput, len - 1); 112 buffer.push(alphabet.charAt(bin >> 2), alphabet.charAt((bin << 4) & 63), pad, pad); 113 break; 114 case 2: 115 bin = (_getByte(encInput, len - 2) << 8) | _getByte(encInput, len - 1); 116 buffer.push( 117 alphabet.charAt(bin >> 10), 118 alphabet.charAt((bin >> 4) & 63), 119 alphabet.charAt((bin << 2) & 63), 120 pad 121 ); 122 break; 123 } 124 125 return buffer.join(''); 126 }, 127 128 /** 129 * Decode from Base64 130 * @param {String} input Base64 encoded data 131 * @param {Boolean} utf8 In case this parameter is true {@link JXG.Util.UTF8.decode} will be applied to 132 * the result of the base64 decoder. 133 * @throws {Error} If the string has the wrong length. 134 * @returns {String} 135 */ 136 decode : function (input, utf8) { 137 var encInput, i, len, padLen, bin, output, 138 result = [], 139 buffer = []; 140 141 // deactivate regexp linting. Our regex is secure, because we replace everything with '' 142 /*jslint regexp:true*/ 143 encInput = input.replace(/[^A-Za-z0-9\+\/=]/g, ''); 144 /*jslint regexp:false*/ 145 146 len = encInput.length; 147 148 if (len % 4 !== 0) { 149 throw new Error('JSXGraph/utils/base64: Can\'t decode string (invalid input length).'); 150 } 151 152 if (encInput.charAt(len - 1) === pad) { 153 padLen = 1; 154 155 if (encInput.charAt(len - 2) === pad) { 156 padLen = 2; 157 } 158 159 // omit the last four bytes (taken care of after the for loop) 160 len -= 4; 161 } 162 163 for (i = 0; i < len; i += 4) { 164 bin = (_getIndex(encInput, i) << 18) | (_getIndex(encInput, i + 1) << 12) | (_getIndex(encInput, i + 2) << 6) | _getIndex(encInput, i + 3); 165 buffer.push(bin >> 16, (bin >> 8) & 255, bin & 255); 166 167 // flush the buffer, if it gets too big fromCharCode will crash 168 if (i % 10000 === 0) { 169 result.push(String.fromCharCode.apply(null, buffer)); 170 buffer = []; 171 } 172 } 173 174 switch (padLen) { 175 case 1: 176 bin = (_getIndex(encInput, len) << 12) | (_getIndex(encInput, len + 1) << 6) | (_getIndex(encInput, len + 2)); 177 buffer.push(bin >> 10, (bin >> 2) & 255); 178 break; 179 180 case 2: 181 bin = (_getIndex(encInput, i) << 6) | (_getIndex(encInput, i + 1)); 182 buffer.push(bin >> 4); 183 break; 184 } 185 186 result.push(String.fromCharCode.apply(null, buffer)); 187 output = result.join(''); 188 189 if (utf8) { 190 output = Encoding.decode(output); 191 } 192 193 return output; 194 }, 195 196 /** 197 * Decode the base64 input data as an array 198 * @param {string} input 199 * @return {Array} 200 */ 201 decodeAsArray: function (input) { 202 var i, 203 dec = this.decode(input), 204 ar = [], 205 len = dec.length; 206 207 for (i = 0; i < len; i++) { 208 ar[i] = dec.charCodeAt(i); 209 } 210 211 return ar; 212 } 213 }; 214 215 return JXG.Util.Base64; 216 }); 217