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, 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          * Opens a file using the given URL and passes the contents to {@link JXG.FileReader#parseString}
 57          * @param {String} url
 58          * @param {JXG.Board|function} board Either a board or in case <tt>format</tt> equals 'raw' this has to be a callback function.
 59          * @param {String} format The expected file format. Possible values are <dl>
 60          * <dt>raw</dt><dd>Raw text file. In this case <tt>board</tt> has to be a callback function.</dd>
 61          * <dt>geonext</dt><dd>Geonext File <a href="http://www.geonext.de">http://www.geonext.de</a></dd>
 62          * <dt>intergeo</dt><dd>Intergeo file format <a href="http://www.i2geo.net">http://www.i2geo.net</a></dd>
 63          * <dt>tracenpoche</dt><dd>Tracenpoche construction <a href="http://www.tracenpoche.net">http://www.tracenpoche.net</a></dd>
 64          * <dt>graph</dt><dd>Graph file</dd>
 65          * <dt>digraph</dt><dd>DiGraph file</dd>
 66          * <dt>geogebra</dt><dd>Geogebra File <a href="http://www.geogebra.org">http://www.geogebra.org</a></dd>
 67          * <dl><dt>cdy or cinderella</dt><dd>Cinderella (<a href="http://www.cinderella.de/">http://www.cinderella.de</a></dd>
 68          * </dl>
 69          * @param {Boolean} async Call ajax asynchonously.
 70          * @param {function} callback A function that is run when the board is ready.
 71          */
 72         parseFileContent: function (url, board, format, async, callback) {
 73             var request = false;
 74 
 75             if (!Type.exists(async)) {
 76                 async = true;
 77             }
 78 
 79             //this.request = false;
 80 
 81             try {
 82                 request = new XMLHttpRequest();
 83                 if (format.toLowerCase() === 'raw') {
 84                     request.overrideMimeType('text/plain; charset=iso-8859-1');
 85                 } else {
 86                     request.overrideMimeType('text/xml; charset=iso-8859-1');
 87                 }
 88             } catch (e) {
 89                 try {
 90                     request = new ActiveXObject("Msxml2.XMLHTTP");
 91                 } catch (ex) {
 92                     try {
 93                         request = new ActiveXObject("Microsoft.XMLHTTP");
 94                     } catch (exc) {
 95                         request = false;
 96                     }
 97                 }
 98             }
 99 
100             if (!request) {
101                 JXG.debug("AJAX not activated!");
102                 return;
103             }
104 
105             request.open("GET", url, async);
106 
107             if (format.toLowerCase() === 'raw') {
108                 this.cbp = function () {
109                     var req = request;
110                     if (req.readyState === 4) {
111                         board(req.responseText);
112                     }
113                 };
114             } else {
115                 this.cbp = function () {
116                     var req = request,
117                         text = '';
118 
119                     if (req.readyState === 4) {
120                         if (Type.exists(req.responseStream) &&
121                                 // PK: zip, geogebra
122                                 // 31: gzip, cinderella
123                                 (req.responseText.slice(0, 2) === "PK" ||
124                                 Encoding.asciiCharCodeAt(req.responseText.slice(0, 1), 0) === 31)) {
125 
126                             // After this, text contains the base64 encoded, zip-compressed string
127                             text = Base64.decode(jxgBinFileReader(req));
128                         } else {
129                             text = req.responseText;
130                         }
131                         this.parseString(text, board, format, callback);
132                     }
133                 };
134             }
135 
136             this.cb = Type.bind(this.cbp, this);
137             request.onreadystatechange = this.cb;
138 
139             try {
140                 request.send(null);
141             } catch (ex2) {
142                 throw new Error("JSXGraph: A problem occurred while trying to read '" + url + "'.");
143             }
144         },
145 
146         /**
147          * Parses a given string according to the file format given in format.
148          * @param {String} str Contents of the file.
149          * @param {JXG.Board} board The board the construction in the file should be loaded in.
150          * @param {String} format Possible values are <dl>
151          * <dt>raw</dt><dd>Raw text file. In this case <tt>board</tt> has to be a callback function.</dd>
152          * <dt>geonext</dt><dd>Geonext File <a href="http://www.geonext.de">http://www.geonext.de</a></dd>
153          * <dt>intergeo</dt><dd>Intergeo file format <a href="http://www.i2geo.net">http://www.i2geo.net</a></dd>
154          * <dt>tracenpoche</dt><dd>Tracenpoche construction <a href="http://www.tracenpoche.net">http://www.tracenpoche.net</a></dd>
155          * <dt>graph</dt><dd>Graph file</dd>
156          * <dt>digraph</dt><dd>DiGraph file</dd>
157          * <dt>geogebra</dt><dd>Geogebra File <a href="http://www.geogebra.org">http://www.geogebra.org</a></dd>
158          * <dl><dt>cdy or cinderella</dt><dd>Cinderella (<a href="http://www.cinderella.de/">http://www.cinderella.de</a></dd>
159          * </dl>
160          * @param {function} callback
161          */
162         parseString: function (str, board, format, callback) {
163             var Reader,
164                 read;
165 
166             format = format.toLowerCase();
167 
168             Reader = JXG.readers[format];
169 
170             if (Type.exists(Reader)) {
171                 read = new Reader(board, str);
172                 read.read();
173             } else {
174                 throw new Error('JSXGraph: There is no reader available for \'' + format + '\'.');
175             }
176 
177             if (typeof callback === 'function') {
178                 callback(board);
179             }
180         }
181     };
182 
183     // The following code is vbscript. This is a workaround to enable binary data downloads via AJAX in
184     // Microsoft Internet Explorer.
185 
186     /*jslint evil:true, es5:true, white:true*/
187     /*jshint multistr:true*/
188     if (!Env.isMetroApp() && Env.isBrowser && typeof navigator === 'object' && /msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent) && document && document.write) {
189         document.write('<script type="text/vbscript">\n\
190 Function Base64Encode(inData)\n\
191   Const Base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"\n\
192   Dim cOut, sOut, I\n\
193   For I = 1 To LenB(inData) Step 3\n\
194     Dim nGroup, pOut, sGroup\n\
195     nGroup = &H10000 * AscB(MidB(inData, I, 1)) + _\n\
196       &H100 * MyASC(MidB(inData, I + 1, 1)) + MyASC(MidB(inData, I + 2, 1))\n\
197     nGroup = Oct(nGroup)\n\
198     nGroup = String(8 - Len(nGroup), "0") & nGroup\n\
199     pOut = Mid(Base64, CLng("&o" & Mid(nGroup, 1, 2)) + 1, 1) + _\n\
200       Mid(Base64, CLng("&o" & Mid(nGroup, 3, 2)) + 1, 1) + _\n\
201       Mid(Base64, CLng("&o" & Mid(nGroup, 5, 2)) + 1, 1) + _\n\
202       Mid(Base64, CLng("&o" & Mid(nGroup, 7, 2)) + 1, 1)\n\
203     sOut = sOut + pOut\n\
204   Next\n\
205   Select Case LenB(inData) Mod 3\n\
206     Case 1: \'8 bit final\n\
207       sOut = Left(sOut, Len(sOut) - 2) + "=="\n\
208     Case 2: \'16 bit final\n\
209       sOut = Left(sOut, Len(sOut) - 1) + "="\n\
210   End Select\n\
211   Base64Encode = sOut\n\
212 End Function\n\
213 \n\
214 Function MyASC(OneChar)\n\
215   If OneChar = "" Then MyASC = 0 Else MyASC = AscB(OneChar)\n\
216 End Function\n\
217 \n\
218 Function jxgBinFileReader(xhr)\n\
219     Dim byteString\n\
220     Dim b64String\n\
221     Dim i\n\
222     byteString = xhr.responseBody\n\
223     ReDim byteArray(LenB(byteString))\n\
224     For i = 1 To LenB(byteString)\n\
225         byteArray(i-1) = AscB(MidB(byteString, i, 1))\n\
226     Next\n\
227     b64String = Base64Encode(byteString)\n\
228     jxgBinFileReader = b64String\n\
229 End Function\n\
230 </script>\n');
231     }
232 
233     return JXG.FileReader;
234 });
235