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