      1 /*
      2     Copyright 2008-2022
      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, ActiveXObject:true, jxgBinFileReader:true, DOMParser:true, XMLHttpRequest:true, document:true, navigator:true*/
     34 /*jslint nomen: true, plusplus: true*/
     35
     36 /* depends:
     37  jxg
     38  utils/env
     39  utils/type
     40  utils/encoding
     41  utils/base64
     42  */
     43
     44 define([
     45     'jxg', 'utils/env', 'utils/type', 'utils/encoding', 'utils/base64'
     46 ], function (JXG, Env, Type, Encoding, Base64) {
     47
     48     "use strict";
     49
     50     /**
     51      * The FileReader object bundles the file input capabilities of JSXGraph.
     52      */
     53     JXG.FileReader = {
     54         /**
     55          *
     56          * @param {String} url
     57          * @param {JXG.Board} board
     58          * @param {String} format
     59          * @param {Boolean} async
     60          * @param {Function} callback
     61          *
     62          * @private
     63          */
     64         handleRemoteFile: function(url, board, format, async, encoding, callback) {
     65             var request = false;
     66
     67             try {
     68                 request = new XMLHttpRequest();
     69                 if (format.toLowerCase() === 'raw') {
     70                     request.overrideMimeType('text/plain; charset=' + encoding);
     71                 } else {
     72                     request.overrideMimeType('text/xml; charset=' + encoding);
     73                 }
     74             } catch (e) {
     75                 try {
     76                     request = new ActiveXObject("Msxml2.XMLHTTP");
     77                 } catch (ex) {
     78                     try {
     79                         request = new ActiveXObject("Microsoft.XMLHTTP");
     80                     } catch (exc) {
     81                         request = false;
     82                     }
     83                 }
     84             }
     85             if (!request) {
     86                 JXG.debug("AJAX not activated!");
     87                 return;
     88             }
     89
     90             request.open("GET", url, async);
     91             if (format.toLowerCase() === 'raw') {
     92                 this.cbp = function () {
     93                     var req = request;
     94                     if (req.readyState === 4) {
     95                         board(req.responseText);
     96                     }
     97                 };
     98             } else {
     99                 this.cbp = function () {
    100                     var req = request,
    101                         text = '';
    102
    103                     if (req.readyState === 4) {
    104                         // Hack for ancient IEs:
    105                         // We use the Visual Basic stuff from below.
    106                         if (Type.exists(req.responseStream) &&
    107                                 // PK: zip, geogebra
    108                                 // 31: gzip, cinderella
    109                                 (req.responseText.slice(0, 2) === "PK" ||
    110                                 Encoding.asciiCharCodeAt(req.responseText.slice(0, 1), 0) === 31)) {
    111
    112                             // After this, text contains the binary? zip-compressed string
    113                             text = Base64.decode(jxgBinFileReader(req));
    114                         } else {
    115                             // This is for all browsers except ancient IEs.
    116                             text = req.responseText;
    117                             // console.log(text);
    118                         }
    119                         this.parseString(text, board, format, callback);
    120                     }
    121                 };
    122             }
    123
    124             this.cb = Type.bind(this.cbp, this);
    125             // Old style
    126             request.onreadystatechange = this.cb;
    127
    128             try {
    129                 request.send(null);
    130             } catch (ex2) {
    131                 throw new Error("JSXGraph: A problem occurred while trying to read remote file '" + url + "'.");
    132             }
    133         },
    134
    135         /**
    136          *
    137          * @param {Blob} url The Blob or File from which to read
    138          * @param {JXG.Board} board
    139          * @param {String} format
    140          * @param {Boolean} async
    141          * @param {Function} callback
    142          *
    143          * @private
    144          */
    145         handleLocalFile: function(url, board, format, async, encoding, callback) {
    146             if (!Type.exists(async)) {
    147                 async = true;
    148             }
    149
    150             if (format.toLowerCase() === 'raw') {
    151                 this.cbp = function (e) {
    152                     board(e.target.result);
    153                 };
    154             } else {
    155                 this.cbp = function (e) {
    156                     var text = e.target.result;
    157                     //console.log(text);
    158                     this.parseString(text, board, format, callback);
    159                 };
    160             }
    161
    162             this.cb = Type.bind(this.cbp, this);
    163
    164             var reader = new FileReader();
    165             reader.onload = this.cb;
    166             if (format.toLowerCase() === 'raw') {
    167                 reader.readAsText(url);
    168             } else {
    169                 reader.readAsText(url, encoding);
    170             }
    171         },
    172
    173         /**
    174          * Opens a file using the given URL and passes the contents to {@link JXG.FileReader#parseString}
    175          * @param {String} url
    176          * @param {JXG.Board|function} board Either a board or in case <tt>format</tt> equals 'raw' this has to be a callback function.
    177          * @param {String} format The expected file format. Possible values are <dl>
    178          * <dt>raw</dt><dd>Raw text file. In this case <tt>board</tt> has to be a callback function.</dd>
    179          * <dt>geonext</dt><dd>Geonext File <a href="http://www.geonext.de">http://www.geonext.de</a></dd>
    180          * <dt>intergeo</dt><dd>Intergeo file format <a href="http://www.i2geo.net">http://www.i2geo.net</a></dd>
    181          * <dt>tracenpoche</dt><dd>Tracenpoche construction <a href="http://www.tracenpoche.net">http://www.tracenpoche.net</a></dd>
    182          * <dt>graph</dt><dd>Graph file</dd>
    183          * <dt>digraph</dt><dd>DiGraph file</dd>
    184          * <dt>geogebra</dt><dd>Geogebra File <a href="http://www.geogebra.org">http://www.geogebra.org</a></dd>
    185          * <dl><dt>cdy or cinderella</dt><dd>Cinderella (<a href="http://www.cinderella.de/">http://www.cinderella.de</a></dd>
    186          * </dl>
    187          * @param {Boolean} async Call ajax asynchonously.
    188          * @param {function} callback A function that is run when the board is ready.
    189          */
    190         parseFileContent: function (url, board, format, async, encoding, callback) {
    191             if (Type.isString(url) || FileReader === undefined) {
    192                 this.handleRemoteFile(url, board, format, async, encoding, callback);
    193             } else {
    194                 this.handleLocalFile(url, board, format, async, encoding, callback);
    195             }
    196         },
    197
    198         /**
    199          * Parses a given string according to the file format given in format.
    200          * @param {String} str Contents of the file.
    201          * @param {JXG.Board} board The board the construction in the file should be loaded in.
    202          * @param {String} format Possible values are <dl>
    203          * <dt>raw</dt><dd>Raw text file. In this case <tt>board</tt> has to be a callback function.</dd>
    204          * <dt>geonext</dt><dd>Geonext File <a href="http://www.geonext.de">http://www.geonext.de</a></dd>
    205          * <dt>intergeo</dt><dd>Intergeo file format <a href="http://www.i2geo.net">http://www.i2geo.net</a></dd>
    206          * <dt>tracenpoche</dt><dd>Tracenpoche construction <a href="http://www.tracenpoche.net">http://www.tracenpoche.net</a></dd>
    207          * <dt>graph</dt><dd>Graph file</dd>
    208          * <dt>digraph</dt><dd>DiGraph file</dd>
    209          * <dt>geogebra</dt><dd>Geogebra File <a href="http://www.geogebra.org">http://www.geogebra.org</a></dd>
    210          * <dl><dt>cdy or cinderella</dt><dd>Cinderella (<a href="http://www.cinderella.de/">http://www.cinderella.de</a></dd>
    211          * </dl>
    212          * @param {function} callback
    213          */
    214         parseString: function (str, board, format, callback) {
    215             var Reader,
    216                 read;
    217
    218             format = format.toLowerCase();
    219             Reader = JXG.readers[format];
    220
    221             if (Type.exists(Reader)) {
    222                 read = new Reader(board, str);
    223                 read.read();
    224             } else if (format === 'jessiecode') {
    225
    226             } else {
    227                 throw new Error('JSXGraph: There is no reader available for \'' + format + '\'.');
    228             }
    229
    230             if (Type.isFunction(callback)) {
    231                 callback(board);
    232             }
    233         }
    234     };
    235
    236     // The following code is vbscript. This is a workaround to enable binary data downloads via AJAX in
    237     // Microsoft Internet Explorer.
    238
    239     /*jslint evil:true, es5:true, white:true*/
    240     /*jshint multistr:true*/
    241     if (!Env.isMetroApp() && Env.isBrowser && typeof navigator === 'object' && /msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent) && document && document.write) {
    242         document.write('<script type="text/vbscript">\n\
    243 Function Base64Encode(inData)\n\
    244   Const Base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"\n\
    245   Dim cOut, sOut, I\n\
    246   For I = 1 To LenB(inData) Step 3\n\
    247     Dim nGroup, pOut, sGroup\n\
    248     nGroup = &H10000 * AscB(MidB(inData, I, 1)) + _\n\
    249       &H100 * MyASC(MidB(inData, I + 1, 1)) + MyASC(MidB(inData, I + 2, 1))\n\
    250     nGroup = Oct(nGroup)\n\
    251     nGroup = String(8 - Len(nGroup), "0") & nGroup\n\
    252     pOut = Mid(Base64, CLng("&o" & Mid(nGroup, 1, 2)) + 1, 1) + _\n\
    253       Mid(Base64, CLng("&o" & Mid(nGroup, 3, 2)) + 1, 1) + _\n\
    254       Mid(Base64, CLng("&o" & Mid(nGroup, 5, 2)) + 1, 1) + _\n\
    255       Mid(Base64, CLng("&o" & Mid(nGroup, 7, 2)) + 1, 1)\n\
    256     sOut = sOut + pOut\n\
    257   Next\n\
    258   Select Case LenB(inData) Mod 3\n\
    259     Case 1: \'8 bit final\n\
    260       sOut = Left(sOut, Len(sOut) - 2) + "=="\n\
    261     Case 2: \'16 bit final\n\
    262       sOut = Left(sOut, Len(sOut) - 1) + "="\n\
    263   End Select\n\
    264   Base64Encode = sOut\n\
    265 End Function\n\
    266 \n\
    267 Function MyASC(OneChar)\n\
    268   If OneChar = "" Then MyASC = 0 Else MyASC = AscB(OneChar)\n\
    269 End Function\n\
    270 \n\
    271 Function jxgBinFileReader(xhr)\n\
    272     Dim byteString\n\
    273     Dim b64String\n\
    274     Dim i\n\
    275     byteString = xhr.responseBody\n\
    276     ReDim byteArray(LenB(byteString))\n\
    277     For i = 1 To LenB(byteString)\n\
    278         byteArray(i-1) = AscB(MidB(byteString, i, 1))\n\
    279     Next\n\
    280     b64String = Base64Encode(byteString)\n\
    281     jxgBinFileReader = b64String\n\
    282 End Function\n\
    283 </script>\n');
    284     }
    285
    286     return JXG.FileReader;
    287 });
    288