      1 /*
      2     Copyright 2008,
      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: you can redistribute it and/or modify
     13     it under the terms of the GNU Lesser General Public License as published by
     14     the Free Software Foundation, either version 3 of the License, or
     15     (at your option) any later version.
     16
     17     JSXGraph is distributed in the hope that it will be useful,
     18     but WITHOUT ANY WARRANTY; without even the implied warranty of
     19     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     20     GNU Lesser General Public License for more details.
     21
     22     You should have received a copy of the GNU Lesser General Public License
     23     along with JSXGraph.  If not, see <http://www.gnu.org/licenses/>.
     24 */
     25
     26 /**
     27  * The FileReader object bundles the file input capabilities of JSXGraph.
     28  */
     29 JXG.FileReader = {
     30
     31     /**
     32      * Opens a file using the given URL and passes the contents to {@link JXG.FileReader#parseString}
     33      * @param {String} url
     34      * @param {JXG.Board|function} board Either a board or in case <tt>format</tt> equals 'raw' this has to be a callback function.
     35      * @param {String} format The expected file format. Possible values are <dl>
     36      * <dt>raw</dt><dd>Raw text file. In this case <tt>board</tt> has to be a callback function.</dd>
     37      * <dt>geonext</dt><dd>Geonext File <a href="http://www.geonext.de">http://www.geonext.de</a></dd>
     38      * <dt>intergeo</dt><dd>Intergeo file format <a href="http://www.i2geo.net">http://www.i2geo.net</a></dd>
     39      * <dt>tracenpoche</dt><dd>Tracenpoche construction <a href="http://www.tracenpoche.net">http://www.tracenpoche.net</a></dd>
     40      * <dt>graph</dt><dd>Graph file</dd>
     41      * <dt>digraph</dt><dd>DiGraph file</dd>
     42      * <dt>geogebra</dt><dd>Geogebra File <a href="http://www.geogebra.org">http://www.geogebra.org</a></dd>
     43      * <dl><dt>cdy or cinderella</dt><dd>Cinderella (<a href="http://www.cinderella.de/">http://www.cinderella.de</a></dd>
     44      * </dl>
     45      * @param {Boolean} async Call ajax asynchonously.
     46      */
     47     parseFileContent: function (url, board, format, async) {
     48         var request = false;
     49
     50         if(!JXG.exists(async)) {
     51             async = true;
     52         }
     53
     54         //this.request = false;
     55
     56         try {
     57             request = new XMLHttpRequest();
     58             if(format.toLowerCase()=='raw') {
     59                 request.overrideMimeType('text/plain; charset=iso-8859-1');
     60             } else {
     61                 request.overrideMimeType('text/xml; charset=iso-8859-1');
     62             }
     63         } catch (e) {
     64             try {
     65                 request = new ActiveXObject("Msxml2.XMLHTTP");
     66             } catch (e) {
     67                 try {
     68                     request = new ActiveXObject("Microsoft.XMLHTTP");
     69                 } catch (e) {
     70                     request = false;
     71                 }
     72             }
     73         }
     74
     75         if (!request) {
     76             alert("AJAX not activated!");
     77             return;
     78         }
     79
     80         request.open("GET", url, async);
     81
     82         if(format.toLowerCase() === 'raw') {
     83             this.cbp = function() {
     84                 var req = request;
     85                 if (req.readyState == 4) {
     86                     board(req.responseText);
     87                 }
     88             };
     89         } else {
     90             this.cbp = function() {
     91                 var req = request;
     92                 if (req.readyState == 4) {
     93                     var text = '';
     94
     95                     if (typeof req.responseStream!='undefined' &&
     96                         (req.responseText.slice(0,2) == "PK"                            // ZIP -> Geogebra
     97                             || JXG.Util.asciiCharCodeAt(req.responseText.slice(0,1),0)==31) // gzip -> Cinderella
     98                         ) {
     99                         text = JXG.Util.Base64.decode(BinFileReader(req)); // After this, text contains the base64 encoded, zip-compressed string
    100                     } else {
    101                         text = req.responseText;
    102                     }
    103                     this.parseString(text, board, format, false);
    104                 }
    105             };
    106         }
    107
    108         this.cb = JXG.bind(this.cbp, this);
    109         request.onreadystatechange = this.cb;
    110
    111         try {
    112             request.send(null);
    113         } catch (e) {
    114             throw new Error("JSXGraph: A problem occurred while trying to read '" + url + "'.");
    115         }
    116     },
    117
    118     /**
    119      * Cleans out unneccessary whitespaces in a chunk of xml.
    120      * @param {XMLElement} el
    121      */
    122     cleanWhitespace: function (el) {
    123         var cur = el.firstChild;
    124
    125         while (cur != null) {
    126             if (cur.nodeType == 3 && !/\S/.test(cur.nodeValue)) {
    127                 el.removeChild( cur );
    128             } else if ( cur.nodeType == 1 ) {
    129                 this.cleanWhitespace( cur );
    130             }
    131             cur = cur.nextSibling;
    132         }
    133     },
    134
    135     /**
    136      * Converts a given string into a XML tree.
    137      * @param {String} str
    138      * @returns {XMLElement} The xml tree represented by the root node.
    139      */
    140     stringToXMLTree: function (str) {
    141         // The string "str" is converted into a XML tree.
    142         if(typeof DOMParser === 'undefined') {
    143             // IE workaround, since there is no DOMParser
    144             DOMParser = function () {};
    145             DOMParser.prototype.parseFromString = function (str, contentType) {
    146                 if (typeof ActiveXObject !== 'undefined') {
    147                     var d = new ActiveXObject('MSXML.DomDocument');
    148                     d.loadXML(str);
    149                     return d;
    150                 }
    151             };
    152         }
    153         var parser = new DOMParser(),
    154             tree = parser.parseFromString(str, 'text/xml');
    155
    156         this.cleanWhitespace(tree);
    157         return tree;
    158     },
    159
    160     /**
    161      * Parses a given string according to the file format given in format.
    162      * @param {String} str Contents of the file.
    163      * @param {JXG.Board} board The board the construction in the file should be loaded in.
    164      * @param {String} format Possible values are <dl>
    165      * <dt>raw</dt><dd>Raw text file. In this case <tt>board</tt> has to be a callback function.</dd>
    166      * <dt>geonext</dt><dd>Geonext File <a href="http://www.geonext.de">http://www.geonext.de</a></dd>
    167      * <dt>intergeo</dt><dd>Intergeo file format <a href="http://www.i2geo.net">http://www.i2geo.net</a></dd>
    168      * <dt>tracenpoche</dt><dd>Tracenpoche construction <a href="http://www.tracenpoche.net">http://www.tracenpoche.net</a></dd>
    169      * <dt>graph</dt><dd>Graph file</dd>
    170      * <dt>digraph</dt><dd>DiGraph file</dd>
    171      * <dt>geogebra</dt><dd>Geogebra File <a href="http://www.geogebra.org">http://www.geogebra.org</a></dd>
    172      * <dl><dt>cdy or cinderella</dt><dd>Cinderella (<a href="http://www.cinderella.de/">http://www.cinderella.de</a></dd>
    173      * </dl>
    174      * @param {Boolean} isString Some file formats can be given as Base64 encoded strings or as plain xml, in both cases
    175      * they are given as strings. This flag is used to distinguish those cases: <tt>true</tt> means, it is given as a string,
    176      * no need to un-Base64 and unzip the file.
    177      */
    178     parseString: function (str, board, format, isString) {
    179         var tree, graph, xml;
    180
    181         format = format.toLowerCase();
    182
    183         switch (format) {
    184             case 'cdy':
    185             case 'cinderella':
    186                 // if isString is true, str is the base64 encoded zip file, otherwise it's just the zip file
    187                 if(isString) {
    188                     str = JXG.Util.Base64.decode(str);
    189                 }
    190
    191                 str = JXG.CinderellaReader.readCinderella(str, board);
    192                 board.xmlString = str;
    193
    194                 break;
    195             case 'tracenpoche':
    196                 board.xmlString = JXG.TracenpocheReader.readTracenpoche(str, board);
    197
    198                 break;
    199             case 'graph':
    200                 str = JXG.GraphReader.readGraph(str, board, false);
    201                 break;
    202             case 'digraph':
    203                 str = JXG.GraphReader.readGraph(str, updateboard, true);
    204                 break;
    205             case 'geonext':
    206                 // str is a string containing the XML code of the construction
    207                 str = JXG.GeonextReader.prepareString(str);
    208                 xml = true;
    209                 break;
    210             case 'geogebra':
    211                 isString = str.slice(0, 2) !== "PK";
    212
    213                 // if isString is true, str is a base64 encoded string, otherwise it's the zipped file
    214                 str = JXG.GeogebraReader.prepareString(str, isString);
    215                 xml = true;
    216                 break;
    217             case 'intergeo':
    218                 if(isString) {
    219                     str = JXG.Util.Base64.decode(str);
    220                 }
    221
    222                 str = JXG.IntergeoReader.prepareString(str);
    223                 xml = true;
    224                 break;
    225         }
    226
    227         if (xml) {
    228             board.xmlString = str;
    229             tree = this.stringToXMLTree(str);
    230             // Now, we can walk through the tree
    231             this.readElements(tree, board, format);
    232         }
    233     },
    234
    235     /**
    236      * Reading the elements of a geonext or geogebra file
    237      * @param {} tree expects the content of the parsed geonext file returned by function parseFromString
    238      * @param {Object} board board object
    239      */
    240     readElements: function (tree, board, format) {
    241         if (format.toLowerCase()=='geonext') {
    242             board.suspendUpdate();
    243             if(tree.getElementsByTagName('GEONEXT').length != 0) {
    244                 JXG.GeonextReader.readGeonext(tree, board);
    245             }
    246             board.unsuspendUpdate();
    247         }
    248         else if(tree.getElementsByTagName('geogebra').length != 0) {
    249             JXG.GeogebraReader.readGeogebra(tree, board);
    250         }
    251         else if(format.toLowerCase()=='intergeo') {
    252             JXG.IntergeoReader.readIntergeo(tree, board);
    253         }
    254     }
    255 };
    256
    257 // The following code is vbscript. This is a workaround to enable binary data downloads via AJAX in
    258 // Microsoft Internet Explorer.
    259
    260 if(/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent && document && document.write)) {
    261 document.write('<script type="text/vbscript">\n\
    262 Function Base64Encode(inData)\n\
    263   Const Base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"\n\
    264   Dim cOut, sOut, I\n\
    265   For I = 1 To LenB(inData) Step 3\n\
    266     Dim nGroup, pOut, sGroup\n\
    267     nGroup = &H10000 * AscB(MidB(inData, I, 1)) + _\n\
    268       &H100 * MyASC(MidB(inData, I + 1, 1)) + MyASC(MidB(inData, I + 2, 1))\n\
    269     nGroup = Oct(nGroup)\n\
    270     nGroup = String(8 - Len(nGroup), "0") & nGroup\n\
    271     pOut = Mid(Base64, CLng("&o" & Mid(nGroup, 1, 2)) + 1, 1) + _\n\
    272       Mid(Base64, CLng("&o" & Mid(nGroup, 3, 2)) + 1, 1) + _\n\
    273       Mid(Base64, CLng("&o" & Mid(nGroup, 5, 2)) + 1, 1) + _\n\
    274       Mid(Base64, CLng("&o" & Mid(nGroup, 7, 2)) + 1, 1)\n\
    275     sOut = sOut + pOut\n\
    276   Next\n\
    277   Select Case LenB(inData) Mod 3\n\
    278     Case 1: \'8 bit final\n\
    279       sOut = Left(sOut, Len(sOut) - 2) + "=="\n\
    280     Case 2: \'16 bit final\n\
    281       sOut = Left(sOut, Len(sOut) - 1) + "="\n\
    282   End Select\n\
    283   Base64Encode = sOut\n\
    284 End Function\n\
    285 \n\
    286 Function MyASC(OneChar)\n\
    287   If OneChar = "" Then MyASC = 0 Else MyASC = AscB(OneChar)\n\
    288 End Function\n\
    289 \n\
    290 Function BinFileReader(xhr)\n\
    291     Dim byteString\n\
    292     Dim b64String\n\
    293     Dim i\n\
    294     byteString = xhr.responseBody\n\
    295     ReDim byteArray(LenB(byteString))\n\
    296     For i = 1 To LenB(byteString)\n\
    297         byteArray(i-1) = AscB(MidB(byteString, i, 1))\n\
    298     Next\n\
    299     b64String = Base64Encode(byteString)\n\
    300     BinFileReader = b64String\n\
    301 End Function\n\
    302 </script>\n');
    303 }
    304