1 /*
  2     Copyright 2008-2012
  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  * @fileoverview The JSXGraph object is defined in this file. JXG.JSXGraph controls all boards.
 28  * It has methods to create, save, load and free boards. Additionally some helper functions are
 29  * defined in this file directly in the JXG namespace.
 30  * @version 0.83
 31  */
 32 
 33 /**
 34  * Constructs a new JSXGraph singleton object.
 35  * @class The JXG.JSXGraph singleton stores all properties required
 36  * to load, save, create and free a board.
 37  */
 38 JXG.JSXGraph = {
 39 
 40     /**
 41      * The small gray version indicator in the top left corner of every JSXGraph board (if
 42      * showCopyright is not set to false on board creation).
 43      * @type String
 44      */
 45     licenseText: 'JSXGraph v0.94 Copyright (C) see http://jsxgraph.org',
 46 
 47     /**
 48      * Associative array that keeps references to all boards.
 49      * @type Object
 50      */
 51     boards: {},
 52 
 53     /**
 54      * Associative array that keeps track of all constructable elements registered
 55      * via {@link JXG.JSXGraph.registerElement}.
 56      * @type Object
 57      */
 58     elements: {},
 59 
 60     /**
 61      * Stores the renderer that is used to draw the boards.
 62      * @type String
 63      */
 64     rendererType: (function() {
 65         var i, arr;
 66 
 67         if (JXG.supportsSVG()) {
 68             JXG.Options.renderer = 'svg';
 69         } else if (JXG.supportsVML()) {
 70             JXG.Options.renderer = 'vml';
 71             // Ok, this is some real magic going on here. IE/VML always was so
 72             // terribly slow, except in one place: Examples placed in a moodle course
 73             // was almost as fast as in other browsers. So i grabbed all the css and
 74             // js scripts from our moodle, added them to a jsxgraph example and it
 75             // worked. next step was to strip all the css/js code which didn't affect
 76             // the VML update speed. The following five lines are what was left after
 77             // the last step and yes - it basically does nothing but reads two
 78             // properties of document.body on every mouse move. why? we don't know. if
 79             // you know, please let us know.
 80             function MouseMove() {
 81                 document.body.scrollLeft;
 82                 document.body.scrollTop;
 83             }
 84             document.onmousemove = MouseMove;
 85         } else {
 86             JXG.Options.renderer = 'canvas';
 87         }
 88 
 89         // Load the source files for the renderer
 90         arr = JXG.rendererFiles[JXG.Options.renderer].split(',');
 91         for (i = 0; i < arr.length; i++) ( function(include) {
 92             JXG.require(JXG.requirePath + include + '.js');
 93         } )(arr[i]);
 94 
 95         return JXG.Options.renderer;
 96     })(),
 97 
 98     /**
 99      * Initialise a new board.
100      * @param {String} box Html-ID to the Html-element in which the board is painted.
101      * @returns {JXG.Board} Reference to the created board.
102      */
103     initBoard: function (box, attributes) {
104         var renderer,
105             originX, originY, unitX, unitY,
106             w, h, dimensions,
107             bbox,
108             zoomfactor, zoomX, zoomY,
109             showCopyright, showNavi,
110             board,
111             wheelzoom, shiftpan;
112 
113         dimensions = JXG.getDimensions(box);
114 
115         // parse attributes
116         if (typeof attributes == 'undefined') {
117             attributes = {};
118         }
119 
120         if (typeof attributes["boundingbox"] != 'undefined') {
121             bbox = attributes["boundingbox"];
122             w = parseInt(dimensions.width);
123             h = parseInt(dimensions.height);
124 
125             if (attributes["keepaspectratio"]) {
126                 /*
127                  * If the boundingbox attribute is given and the ratio of height and width of the
128                  * sides defined by the bounding box and the ratio of the dimensions of the div tag
129                  * which contains the board do not coincide, then the smaller side is chosen.
130                  */
131                 unitX = w/(bbox[2]-bbox[0]);
132                 unitY = h/(-bbox[3]+bbox[1]);
133                 if (Math.abs(unitX)<Math.abs(unitY)) {
134                     unitY = Math.abs(unitX)*unitY/Math.abs(unitY);
135                 } else {
136                     unitX = Math.abs(unitY)*unitX/Math.abs(unitX);
137                 }
138             } else {
139                 unitX = w/(bbox[2]-bbox[0]);
140                 unitY = h/(-bbox[3]+bbox[1]);
141             }
142             originX = -unitX*bbox[0];
143             originY = unitY*bbox[1];
144         } else {
145             originX = ( (typeof attributes["originX"]) == 'undefined' ? 150 : attributes["originX"]);
146             originY = ( (typeof attributes["originY"]) == 'undefined' ? 150 : attributes["originY"]);
147             unitX = ( (typeof attributes["unitX"]) == 'undefined' ? 50 : attributes["unitX"]);
148             unitY = ( (typeof attributes["unitY"]) == 'undefined' ? 50 : attributes["unitY"]);
149         }
150 
151         zoomfactor = ( (typeof attributes["zoomfactor"]) == 'undefined' ? 1.0 : attributes["zoom"]);
152         zoomX = zoomfactor*( (typeof attributes["zoomX"]) == 'undefined' ? 1.0 : attributes["zoomX"]);
153         zoomY = zoomfactor*( (typeof attributes["zoomY"]) == 'undefined' ? 1.0 : attributes["zoomY"]);
154 
155         showCopyright = ( (typeof attributes["showCopyright"]) == 'undefined' ? JXG.Options.showCopyright : attributes["showCopyright"]);
156 
157         wheelzoom = ( (typeof attributes["zoom"]) == 'undefined' ? JXG.Options.zoom.wheel : attributes["zoom"]);
158         shiftpan = ( (typeof attributes["pan"]) == 'undefined' ? JXG.Options.pan : attributes["pan"]);
159 
160         // create the renderer
161         if(JXG.Options.renderer == 'svg') {
162             renderer = new JXG.SVGRenderer(document.getElementById(box));
163         } else if(JXG.Options.renderer == 'vml') {
164             renderer = new JXG.VMLRenderer(document.getElementById(box));
165         } else if(JXG.Options.renderer == 'silverlight') {
166             renderer = new JXG.SilverlightRenderer(document.getElementById(box), dimensions.width, dimensions.height);
167         } else {
168             renderer = new JXG.CanvasRenderer(document.getElementById(box));
169         }
170 
171         // create the board
172         board = new JXG.Board(box, renderer, '', [originX, originY], zoomX, zoomY, unitX, unitY, dimensions.width, dimensions.height, showCopyright);
173         this.boards[board.id] = board;
174 
175         board.keepaspectratio = attributes.keepaspectratio;
176         board.options.zoom.wheel = wheelzoom;
177         board.options.pan = shiftpan;
178 
179 
180         // create elements like axes, grid, navigation, ...
181         board.suspendUpdate();
182         board.initInfobox();
183         
184         if(attributes["axis"]) {
185         	board.defaultAxes = {};
186             board.defaultAxes.x = board.create('axis', [[0,0], [1,0]], {ticks:{drawZero:true}});
187             board.defaultAxes.y = board.create('axis', [[0,0], [0,1]], {ticks:{drawZero:board.options.axis.ticks.drawZero}});
188         }
189 
190         if(attributes["grid"]) {
191             board.create('grid', []);
192         }
193 
194         if (typeof attributes["shownavigation"] != 'undefined') attributes["showNavigation"] = attributes["shownavigation"];
195         showNavi = ( (typeof attributes["showNavigation"]) == 'undefined' ? board.options.showNavigation : attributes["showNavigation"]);
196         if (showNavi) {
197             board.renderer.drawZoomBar(board);
198         }
199         board.unsuspendUpdate();
200 
201         return board;
202     },
203 
204     /**
205      * Load a board from a file containing a construction made with either GEONExT,
206      * Intergeo, Geogebra, or Cinderella.
207      * @param {String} box HTML-ID to the HTML-element in which the board is painted.
208      * @param {String} file base64 encoded string.
209      * @param {String} format containing the file format: 'Geonext' or 'Intergeo'.
210      * @returns {JXG.Board} Reference to the created board.
211      *
212      * @see JXG.FileReader
213      * @see JXG.GeonextReader
214      * @see JXG.GeogebraReader
215      * @see JXG.IntergeoReader
216      * @see JXG.CinderellaReader
217      */
218     loadBoardFromFile: function (box, file, format) {
219         var renderer, board, dimensions;
220 
221         if(JXG.Options.renderer == 'svg') {
222             renderer = new JXG.SVGRenderer(document.getElementById(box));
223         } else if(JXG.Options.renderer == 'vml') {
224             renderer = new JXG.VMLRenderer(document.getElementById(box));
225         } else if(JXG.Options.renderer == 'silverlight') {
226             renderer = new JXG.SilverlightRenderer(document.getElementById(box), dimensions.width, dimensions.height);
227         } else {
228             renderer = new JXG.CanvasRenderer(document.getElementById(box));
229         }
230         
231         //var dimensions = document.getElementById(box).getDimensions();
232         dimensions = JXG.getDimensions(box);
233 
234         /* User default parameters, in parse* the values in the gxt files are submitted to board */
235         board = new JXG.Board(box, renderer, '', [150, 150], 1.0, 1.0, 50, 50, dimensions.width, dimensions.height);
236         board.initInfobox();
237 
238         JXG.FileReader.parseFileContent(file, board, format);
239         if(board.options.showNavigation) {
240             board.renderer.drawZoomBar(board);
241         }
242         this.boards[board.id] = board;
243         return board;
244     },
245 
246     /**
247      * Load a board from a base64 encoded string containing a construction made with either GEONExT,
248      * Intergeo, Geogebra, or Cinderella.
249      * @param {String} box HTML-ID to the HTML-element in which the board is painted.
250      * @param {String} string base64 encoded string.
251      * @param {String} format containing the file format: 'Geonext' or 'Intergeo'.
252      * @returns {JXG.Board} Reference to the created board.
253      *
254      * @see JXG.FileReader
255      * @see JXG.GeonextReader
256      * @see JXG.GeogebraReader
257      * @see JXG.IntergeoReader
258      * @see JXG.CinderellaReader
259      */
260     loadBoardFromString: function(box, string, format) {
261         var renderer, dimensions, board;
262 
263         if(JXG.Options.renderer == 'svg') {
264             renderer = new JXG.SVGRenderer(document.getElementById(box));
265         } else if(JXG.Options.renderer == 'vml') {
266             renderer = new JXG.VMLRenderer(document.getElementById(box));
267         } else if(JXG.Options.renderer == 'silverlight') {
268             renderer = new JXG.SilverlightRenderer(document.getElementById(box), dimensions.width, dimensions.height);
269         } else {
270             renderer = new JXG.CanvasRenderer(document.getElementById(box));
271         }
272         //var dimensions = document.getElementById(box).getDimensions();
273         dimensions = JXG.getDimensions(box);
274 
275         /* User default parameters, in parse* the values in the gxt files are submitted to board */
276         board = new JXG.Board(box, renderer, '', [150, 150], 1.0, 1.0, 50, 50, dimensions.width, dimensions.height);
277         board.initInfobox();
278 
279         JXG.FileReader.parseString(string, board, format, true);
280         if (board.options.showNavigation) {
281             board.renderer.drawZoomBar(board);
282         }
283 
284         this.boards[board.id] = board;
285         return board;
286     },
287 
288     /**
289      * Delete a board and all its contents.
290      * @param {String} board HTML-ID to the DOM-element in which the board is drawn.
291      */
292     freeBoard: function (board) {
293         var el, i;
294 
295         if (typeof(board) == 'string') {
296             board = this.boards[board];
297         }
298 
299         board.removeEventHandlers();
300 
301         // Remove all objects from the board.
302         for(el in board.objects) {
303             board.removeObject(board.objects[el]);
304         }
305 
306         // Remove all the other things, left on the board, XHTML save
307         while (board.containerObj.firstChild) {
308             board.containerObj.removeChild(board.containerObj.firstChild);
309         }
310         // board.containerObj.innerHTML = '';
311 
312         // Tell the browser the objects aren't needed anymore
313         for(el in board.objects) {
314             delete(board.objects[el]);
315         }
316 
317         // Free the renderer and the algebra object
318         delete(board.renderer);
319         delete(board.algebra);
320 
321         // clear the creator cache
322         board.jc.creator.clearCache();
323         delete(board.jc);
324 
325         // Finally remove the board itself from the boards array
326         delete(this.boards[board.id]);
327     },
328 
329     /**
330      * This registers a new construction element to JSXGraph for the construction via the {@link JXG.Board.create}
331      * interface.
332      * @param {String} element The elements name. This is case-insensitive, existing elements with the same name
333      * will be overwritten.
334      * @param {Function} creator A reference to a function taking three parameters: First the board, the element is
335      * to be created on, a parent element array, and an attributes object. See {@link JXG.createPoint} or any other
336      * <tt>JXG.create...</tt> function for an example.
337      */
338     registerElement: function (element, creator) {
339         element = element.toLowerCase();
340         this.elements[element] = creator;
341 
342         if(JXG.Board.prototype['_' + element])
343         	throw new Error("JSXGraph: Can't create wrapper method in JXG.Board because member '_" + element + "' already exists'");
344         JXG.Board.prototype['_' + element] = function (parents, attributes) {
345         	return this.create(element, parents, attributes);
346         };
347 
348     },
349 
350     /**
351      * The opposite of {@link JXG.JSXGraph.registerElement}, it removes a given element from
352      * the element list. You probably don't need this.
353      * @param {String} element The name of the element which is to be removed from the element list.
354      */
355     unregisterElement: function (element) {
356         delete (this.elements[element.toLowerCase()]);
357         delete (JXG.Board.prototype['_' + element.toLowerCase()]);
358     }
359 };
360