      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