      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