      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         return alphabet.indexOf(s.charAt(i));
     73     }
     74
     75     /**
     76      * Base64 routines
     77      * @namespace
     78      */
     79     JXG.Util.Base64 = {
     80         /**
     81          * Encode the given string.
     82          * @param {String} input
     83          * @returns {string} base64 encoded version of the input string.
     84          */
     85         encode : function (input) {
     86             var i, bin, len, padLen, encInput,
     87                 buffer = [];
     88
     89             encInput =  Encoding.encode(input);
     90             len = encInput.length;
     91             padLen = len % 3;
     92
     93             for (i = 0; i < len - padLen; i += 3) {
     94                 bin = (_getByte(encInput, i) << 16) | (_getByte(encInput, i + 1) << 8) | (_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 = _getByte(encInput, len - 1);
    106                 buffer.push(alphabet.charAt(bin >> 2), alphabet.charAt((bin << 4) & 63), pad, pad);
    107                 break;
    108             case 2:
    109                 bin = (_getByte(encInput, len - 2) << 8) | _getByte(encInput, len - 1);
    110                 buffer.push(
    111                     alphabet.charAt(bin >> 10),
    112                     alphabet.charAt((bin >> 4) & 63),
    113                     alphabet.charAt((bin << 2) & 63),
    114                     pad
    115                 );
    116                 break;
    117             }
    118
    119             return buffer.join('');
    120         },
    121
    122         /**
    123          * Decode from Base64
    124          * @param {String} input Base64 encoded data
    125          * @param {Boolean} utf8 In case this parameter is true {@link JXG.Util.UTF8.decode} will be applied to
    126          * the result of the base64 decoder.
    127          * @throws {Error} If the string has the wrong length.
    128          * @returns {String}
    129          */
    130         decode : function (input, utf8) {
    131             var encInput, i, len, padLen, bin, output,
    132                 result = [],
    133                 buffer = [];
    134
    135             // deactivate regexp linting. Our regex is secure, because we replace everything with ''
    136             /*jslint regexp:true*/
    137             encInput = input.replace(/[^A-Za-z0-9\+\/=]/g, '');
    138             /*jslint regexp:false*/
    139
    140             len = encInput.length;
    141
    142             if (len % 4 !== 0) {
    143                 throw new Error('JSXGraph/utils/base64: Can\'t decode string (invalid input length).');
    144             }
    145
    146             if (encInput.charAt(len - 1) === pad) {
    147                 padLen = 1;
    148
    149                 if (encInput.charAt(len - 2) === pad) {
    150                     padLen = 2;
    151                 }
    152
    153                 // omit the last four bytes (taken care of after the for loop)
    154                 len -= 4;
    155             }
    156
    157             for (i = 0; i < len; i += 4) {
    158                 bin = (_getIndex(encInput, i) << 18) | (_getIndex(encInput, i + 1) << 12) | (_getIndex(encInput, i + 2) << 6) | _getIndex(encInput, i + 3);
    159                 buffer.push(bin >> 16, (bin >> 8) & 255, bin & 255);
    160
    161                 // flush the buffer, if it gets too big fromCharCode will crash
    162                 if (i % 10000 === 0) {
    163                     result.push(String.fromCharCode.apply(null, buffer));
    164                     buffer = [];
    165                 }
    166             }
    167
    168             switch (padLen) {
    169             case 1:
    170                 bin = (_getIndex(encInput, len) << 12) | (_getIndex(encInput, len + 1) << 6) | (_getIndex(encInput, len + 2));
    171                 buffer.push(bin >> 10, (bin >> 2) & 255);
    172                 break;
    173
    174             case 2:
    175                 bin = (_getIndex(encInput, i) << 6) | (_getIndex(encInput, i + 1));
    176                 buffer.push(bin >> 4);
    177                 break;
    178             }
    179
    180             result.push(String.fromCharCode.apply(null, buffer));
    181             output = result.join('');
    182
    183             if (utf8) {
    184                 output = Encoding.decode(output);
    185             }
    186
    187             return output;
    188         },
    189
    190         /**
    191          * Decode the base64 input data as an array
    192          * @param {string} input
    193          * @returns {Array}
    194          */
    195         decodeAsArray: function (input) {
    196             var i,
    197                 dec = this.decode(input),
    198                 ar = [],
    199                 len = dec.length;
    200
    201             for (i = 0; i < len; i++) {
    202                 ar[i] = dec.charCodeAt(i);
    203             }
    204
    205             return ar;
    206         }
    207     };
    208
    209     return JXG.Util.Base64;
    210 });
    211