1 /*
  2     Copyright 2008-2021
  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 {
225                 throw new Error('JSXGraph: There is no reader available for \'' + format + '\'.');
226             }
227 
228             if (Type.isFunction(callback)) {
229                 callback(board);
230             }
231         }
232     };
233 
234     // The following code is vbscript. This is a workaround to enable binary data downloads via AJAX in
235     // Microsoft Internet Explorer.
236 
237     /*jslint evil:true, es5:true, white:true*/
238     /*jshint multistr:true*/
239     if (!Env.isMetroApp() && Env.isBrowser && typeof navigator === 'object' && /msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent) && document && document.write) {
240         document.write('<script type="text/vbscript">\n\
241 Function Base64Encode(inData)\n\
242   Const Base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"\n\
243   Dim cOut, sOut, I\n\
244   For I = 1 To LenB(inData) Step 3\n\
245     Dim nGroup, pOut, sGroup\n\
246     nGroup = &H10000 * AscB(MidB(inData, I, 1)) + _\n\
247       &H100 * MyASC(MidB(inData, I + 1, 1)) + MyASC(MidB(inData, I + 2, 1))\n\
248     nGroup = Oct(nGroup)\n\
249     nGroup = String(8 - Len(nGroup), "0") & nGroup\n\
250     pOut = Mid(Base64, CLng("&o" & Mid(nGroup, 1, 2)) + 1, 1) + _\n\
251       Mid(Base64, CLng("&o" & Mid(nGroup, 3, 2)) + 1, 1) + _\n\
252       Mid(Base64, CLng("&o" & Mid(nGroup, 5, 2)) + 1, 1) + _\n\
253       Mid(Base64, CLng("&o" & Mid(nGroup, 7, 2)) + 1, 1)\n\
254     sOut = sOut + pOut\n\
255   Next\n\
256   Select Case LenB(inData) Mod 3\n\
257     Case 1: \'8 bit final\n\
258       sOut = Left(sOut, Len(sOut) - 2) + "=="\n\
259     Case 2: \'16 bit final\n\
260       sOut = Left(sOut, Len(sOut) - 1) + "="\n\
261   End Select\n\
262   Base64Encode = sOut\n\
263 End Function\n\
264 \n\
265 Function MyASC(OneChar)\n\
266   If OneChar = "" Then MyASC = 0 Else MyASC = AscB(OneChar)\n\
267 End Function\n\
268 \n\
269 Function jxgBinFileReader(xhr)\n\
270     Dim byteString\n\
271     Dim b64String\n\
272     Dim i\n\
273     byteString = xhr.responseBody\n\
274     ReDim byteArray(LenB(byteString))\n\
275     For i = 1 To LenB(byteString)\n\
276         byteArray(i-1) = AscB(MidB(byteString, i, 1))\n\
277     Next\n\
278     b64String = Base64Encode(byteString)\n\
279     jxgBinFileReader = b64String\n\
280 End Function\n\
281 </script>\n');
282     }
283 
284     return JXG.FileReader;
285 });
286