1 /* 2 Copyright 2008-2024 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 <https://www.gnu.org/licenses/> 29 and <https://opensource.org/licenses/MIT/>. 30 */ 31 32 /*global JXG: true, define: true*/ 33 /*jslint nomen: true, plusplus: true, bitwise: true*/ 34 35 import JXG from "../jxg.js"; 36 import Encoding from "./encoding.js"; 37 38 var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 39 pad = "="; 40 41 // Util namespace 42 JXG.Util = JXG.Util || {}; 43 44 /** 45 * Base64 routines 46 * @namespace 47 */ 48 JXG.Util.Base64 = { 49 // Local helper functions 50 /** 51 * Extracts one byte from a string and ensures the result is less than or equal to 255. 52 * @param {String} s 53 * @param {Number} i 54 * @returns {Number} <= 255 55 * @private 56 */ 57 _getByte: function(s, i) { 58 return s.charCodeAt(i) & 0xff; 59 }, 60 61 /** 62 * Determines the index of a base64 character in the base64 alphabet. 63 * @param {String} s 64 * @param {Number} i 65 * @returns {Number} 66 * @throws {Error} If the character can not be found in the alphabet. 67 * @private 68 */ 69 _getIndex: function(s, i) { 70 return alphabet.indexOf(s.charAt(i)); 71 }, 72 73 /** 74 * Encode the given string. 75 * @param {String} input 76 * @returns {string} base64 encoded version of the input string. 77 */ 78 encode: function (input) { 79 var i, 80 bin, 81 len, 82 padLen, 83 encInput, 84 buffer = []; 85 86 encInput = Encoding.encode(input); 87 len = encInput.length; 88 padLen = len % 3; 89 90 for (i = 0; i < len - padLen; i += 3) { 91 bin = 92 (this._getByte(encInput, i) << 16) | 93 (this._getByte(encInput, i + 1) << 8) | 94 this._getByte(encInput, i + 2); 95 buffer.push( 96 alphabet.charAt(bin >> 18), 97 alphabet.charAt((bin >> 12) & 63), 98 alphabet.charAt((bin >> 6) & 63), 99 alphabet.charAt(bin & 63) 100 ); 101 } 102 103 switch (padLen) { 104 case 1: 105 bin = this._getByte(encInput, len - 1); 106 buffer.push( 107 alphabet.charAt(bin >> 2), 108 alphabet.charAt((bin << 4) & 63), 109 pad, 110 pad 111 ); 112 break; 113 case 2: 114 bin = (this._getByte(encInput, len - 2) << 8) | this._getByte(encInput, len - 1); 115 buffer.push( 116 alphabet.charAt(bin >> 10), 117 alphabet.charAt((bin >> 4) & 63), 118 alphabet.charAt((bin << 2) & 63), 119 pad 120 ); 121 break; 122 } 123 124 return buffer.join(""); 125 }, 126 127 /** 128 * Decode from Base64 129 * @param {String} input Base64 encoded data 130 * @param {Boolean} utf8 In case this parameter is true {@link JXG.Util.UTF8.decode} will be applied to 131 * the result of the base64 decoder. 132 * @throws {Error} If the string has the wrong length. 133 * @returns {String} 134 */ 135 decode: function (input, utf8) { 136 var encInput, 137 i, 138 len, 139 padLen, 140 bin, 141 output, 142 result = [], 143 buffer = []; 144 145 // deactivate regexp linting. Our regex is secure, because we replace everything with '' 146 /*jslint regexp:true*/ 147 encInput = input.replace(/[^A-Za-z0-9+/=]/g, ""); 148 /*jslint regexp:false*/ 149 150 len = encInput.length; 151 152 if (len % 4 !== 0) { 153 throw new Error( 154 "JSXGraph/utils/base64: Can't decode string (invalid input length)." 155 ); 156 } 157 158 if (encInput.charAt(len - 1) === pad) { 159 padLen = 1; 160 161 if (encInput.charAt(len - 2) === pad) { 162 padLen = 2; 163 } 164 165 // omit the last four bytes (taken care of after the for loop) 166 len -= 4; 167 } 168 169 for (i = 0; i < len; i += 4) { 170 bin = 171 (this._getIndex(encInput, i) << 18) | 172 (this._getIndex(encInput, i + 1) << 12) | 173 (this._getIndex(encInput, i + 2) << 6) | 174 this._getIndex(encInput, i + 3); 175 buffer.push(bin >> 16, (bin >> 8) & 255, bin & 255); 176 177 // flush the buffer, if it gets too big fromCharCode will crash 178 if (i % 10000 === 0) { 179 result.push(String.fromCharCode.apply(null, buffer)); 180 buffer = []; 181 } 182 } 183 184 switch (padLen) { 185 case 1: 186 bin = 187 (this._getIndex(encInput, len) << 12) | 188 (this._getIndex(encInput, len + 1) << 6) | 189 this._getIndex(encInput, len + 2); 190 buffer.push(bin >> 10, (bin >> 2) & 255); 191 break; 192 193 case 2: 194 bin = (this._getIndex(encInput, i) << 6) | this._getIndex(encInput, i + 1); 195 buffer.push(bin >> 4); 196 break; 197 } 198 199 result.push(String.fromCharCode.apply(null, buffer)); 200 output = result.join(""); 201 202 if (utf8) { 203 output = Encoding.decode(output); 204 } 205 206 return output; 207 }, 208 209 /** 210 * Decode the base64 input data as an array 211 * @param {string} input 212 * @returns {Array} 213 */ 214 decodeAsArray: function (input) { 215 var i, 216 dec = this.decode(input), 217 ar = [], 218 len = dec.length; 219 220 for (i = 0; i < len; i++) { 221 ar[i] = dec.charCodeAt(i); 222 } 223 224 return ar; 225 } 226 }; 227 228 export default JXG.Util.Base64; 229