      1 /*
      2     Copyright 2008-2022
      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, document:true, jQuery:true, define: true, window: true*/
     34 /*jslint nomen: true, plusplus: true*/
     35
     36 /* depends:
     37  jxg
     38  utils/env
     39  utils/type
     40  base/board
     41  reader/file
     42  options
     43  renderer/svg
     44  renderer/vml
     45  renderer/canvas
     46  renderer/no
     47  */
     48
     49 /**
     50  * @fileoverview The JSXGraph object is defined in this file. JXG.JSXGraph controls all boards.
     51  * It has methods to create, save, load and free boards. Additionally some helper functions are
     52  * defined in this file directly in the JXG namespace.
     53  * @version 0.99
     54  */
     55
     56 define([
     57     'jxg', 'utils/env', 'utils/type', 'base/board', 'reader/file', 'options',
     58     'renderer/svg', 'renderer/vml', 'renderer/canvas', 'renderer/no'
     59 ], function (JXG, Env, Type, Board, FileReader, Options, SVGRenderer, VMLRenderer, CanvasRenderer, NoRenderer) {
     60
     61     "use strict";
     62
     63     /**
     64      * Constructs a new JSXGraph singleton object.
     65      * @class The JXG.JSXGraph singleton stores all properties required
     66      * to load, save, create and free a board.
     67      */
     68     JXG.JSXGraph = {
     69         /**
     70          * Stores the renderer that is used to draw the boards.
     71          * @type String
     72          */
     73         rendererType: (function () {
     74             Options.board.renderer = 'no';
     75
     76             if (Env.supportsVML()) {
     77                 Options.board.renderer = 'vml';
     78                 // Ok, this is some real magic going on here. IE/VML always was so
     79                 // terribly slow, except in one place: Examples placed in a moodle course
     80                 // was almost as fast as in other browsers. So i grabbed all the css and
     81                 // lib scripts from our moodle, added them to a jsxgraph example and it
     82                 // worked. next step was to strip all the css/lib code which didn't affect
     83                 // the VML update speed. The following five lines are what was left after
     84                 // the last step and yes - it basically does nothing but reads two
     85                 // properties of document.body on every mouse move. why? we don't know. if
     86                 // you know, please let us know.
     87                 //
     88                 // If we want to use the strict mode we have to refactor this a little bit. Let's
     89                 // hope the magic isn't gone now. Anywho... it's only useful in old versions of IE
     90                 // which should not be used anymore.
     91                 document.onmousemove = function () {
     92                     var t;
     93
     94                     if (document.body) {
     95                         t = document.body.scrollLeft;
     96                         t += document.body.scrollTop;
     97                     }
     98
     99                     return t;
    100                 };
    101             }
    102
    103             if (Env.supportsCanvas()) {
    104                 Options.board.renderer = 'canvas';
    105             }
    106
    107             if (Env.supportsSVG()) {
    108                 Options.board.renderer = 'svg';
    109             }
    110
    111             // we are inside node
    112             if (Env.isNode() && Env.supportsCanvas()) {
    113                 Options.board.renderer = 'canvas';
    114             }
    115
    116             if (Env.isNode() || Options.renderer === 'no') {
    117                 Options.text.display = 'internal';
    118                 Options.infobox.display = 'internal';
    119             }
    120
    121             return Options.board.renderer;
    122         }()),
    123
    124         /**
    125          * Initialize the rendering engine
    126          *
    127          * @param  {String} box        HTML id of the div-element which hosts the JSXGraph construction
    128          * @param  {Object} dim        The dimensions of the board
    129          * @param  {Object} doc        Usually, this is document object of the browser window.  If false or null, this defaults
    130          * to the document object of the browser.
    131          * @param  {Object} attrRenderer Attribute 'renderer', speficies the rendering engine. Possible values are 'auto', 'svg',
    132          *  'canvas', 'no', and 'vml'.
    133          * @returns {Object}           Reference to the rendering engine object.
    134          * @private
    135          */
    136         initRenderer: function (box, dim, doc, attrRenderer) {
    137             var boxid, renderer;
    138
    139             // Former version:
    140             // doc = doc || document
    141             if ((!Type.exists(doc) || doc === false) && typeof document === 'object') {
    142                 doc = document;
    143             }
    144
    145             if (typeof doc === 'object' && box !== null) {
    146                 boxid = doc.getElementById(box);
    147
    148                 // Remove everything from the container before initializing the renderer and the board
    149                 while (boxid.firstChild) {
    150                     boxid.removeChild(boxid.firstChild);
    151                 }
    152             } else {
    153                 boxid = box;
    154             }
    155
    156             // If attrRenderer is not supplied take the first available renderer
    157             if (attrRenderer === undefined || attrRenderer === 'auto') {
    158                 attrRenderer = this.rendererType;
    159             }
    160             // create the renderer
    161             if (attrRenderer === 'svg') {
    162                 renderer = new SVGRenderer(boxid, dim);
    163             } else if (attrRenderer === 'vml') {
    164                 renderer = new VMLRenderer(boxid);
    165             } else if (attrRenderer === 'canvas') {
    166                 renderer = new CanvasRenderer(boxid, dim);
    167             } else {
    168                 renderer = new NoRenderer();
    169             }
    170
    171             return renderer;
    172         },
    173
    174         /**
    175          * Merge the user supplied attributes with the attributes in options.js
    176          *
    177          * @param {Object} attributes User supplied attributes
    178          * @returns {Object} Merged attributes for the board
    179          *
    180          * @private
    181          */
    182         _setAttributes: function(attributes) {
    183             // merge attributes
    184             var attr = Type.copyAttributes(attributes, Options, 'board');
    185
    186             // The attributes which are objects have to be copied separately
    187             attr.zoom = Type.copyAttributes(attr, Options, 'board', 'zoom');
    188             attr.pan = Type.copyAttributes(attr, Options, 'board', 'pan');
    189             attr.drag = Type.copyAttributes(attr, Options, 'board', 'drag');
    190             attr.keyboard = Type.copyAttributes(attr, Options, 'board', 'keyboard');
    191             attr.selection = Type.copyAttributes(attr, Options, 'board', 'selection');
    192             attr.navbar = Type.copyAttributes(attr.navbar, Options, 'navbar');
    193             attr.screenshot = Type.copyAttributes(attr, Options, 'board', 'screenshot');
    194             attr.resize = Type.copyAttributes(attr, Options, 'board', 'resize');
    195             attr.fullscreen = Type.copyAttributes(attr, Options, 'board', 'fullscreen');
    196
    197             // Treat moveTarget separately, because deepCopy will not work here.
    198             // Reason: moveTarget will be an HTML node and it is prevented that Type.deepCopy will copy it.
    199             attr.movetarget = attributes.moveTarget || attributes.movetarget || Options.board.moveTarget;
    200
    201             return attr;
    202         },
    203
    204         /**
    205          * Further initialization of the board. Set some properties from attribute values.
    206          *
    207          * @param {JXG.Board} board
    208          * @param {Object} attr attributes object
    209          * @param {Object} dimensions Object containing dimensions of the canvas
    210          *
    211          * @private
    212          */
    213         _fillBoard: function(board, attr, dimensions) {
    214             board.initInfobox();
    215             board.maxboundingbox = attr.maxboundingbox;
    216             board.resizeContainer(dimensions.width, dimensions.height, true, true);
    217             board._createSelectionPolygon(attr);
    218             board.renderer.drawZoomBar(board, attr.navbar);
    219             JXG.boards[board.id] = board;
    220         },
    221
    222         /**
    223          *
    224          * @param {String} container HTML-ID to the HTML-element in which the board is painted.
    225          * @param {*} attr An object that sets some of the board properties.
    226          *
    227          * @private
    228          */
    229         _setARIA: function(container, attr) {
    230             var doc = attr.document || document,
    231                 node_jsx, newNode, parent,
    232                 id_label, id_description;
    233
    234             if (typeof doc !== 'object') {
    235                 return;
    236             }
    237
    238             node_jsx = doc.getElementById(container);
    239             parent = node_jsx.parentNode;
    240
    241             id_label = container + '_ARIAlabel';
    242             id_description = container + '_ARIAdescription';
    243
    244             newNode = doc.createElement('div');
    245             newNode.innerHTML = attr.title;
    246             newNode.setAttribute('id', id_label);
    247             newNode.style.display = 'none';
    248             parent.insertBefore(newNode, node_jsx);
    249
    250             newNode = doc.createElement('div');
    251             newNode.innerHTML = attr.description;
    252             newNode.setAttribute('id', id_description);
    253             newNode.style.display = 'none';
    254             parent.insertBefore(newNode, node_jsx);
    255
    256             node_jsx.setAttribute('aria-labelledby', id_label);
    257             node_jsx.setAttribute('aria-describedby', id_description);
    258         },
    259
    260         /**
    261          * Remove the two corresponding ARIA divs when freeing a board
    262          *
    263          * @param {JXG.Board} board
    264          *
    265          * @private
    266          */
    267         _removeARIANodes: function(board) {
    268             var node, id, doc;
    269
    270             doc = board.document || document;
    271             if (typeof doc !== 'object') {
    272                 return;
    273             }
    274
    275             id = board.containerObj.getAttribute('aria-labelledby');
    276             node = document.getElementById(id);
    277             if (node && node.parentNode) {
    278                 node.parentNode.removeChild(node);
    279             }
    280             id = board.containerObj.getAttribute('aria-describedby');
    281             node = document.getElementById(id);
    282             if (node && node.parentNode) {
    283                 node.parentNode.removeChild(node);
    284             }
    285         },
    286
    287         /**
    288          * Initialise a new board.
    289          * @param {String} box HTML-ID to the HTML-element in which the board is painted.
    290          * @param {Object} attributes An object that sets some of the board properties. Most of these properties can be set via JXG.Options.
    291          * @param {Array} [attributes.boundingbox=[-5, 5, 5, -5]] An array containing four numbers describing the left, top, right and bottom boundary of the board in user coordinates
    292          * @param {Boolean} [attributes.keepaspectratio=false] If <tt>true</tt>, the bounding box is adjusted to the same aspect ratio as the aspect ratio of the div containing the board.
    293          * @param {Boolean} [attributes.showCopyright=false] Show the copyright string in the top left corner.
    294          * @param {Boolean} [attributes.showNavigation=false] Show the navigation buttons in the bottom right corner.
    295          * @param {Object} [attributes.zoom] Allow the user to zoom with the mouse wheel or the two-fingers-zoom gesture.
    296          * @param {Object} [attributes.pan] Allow the user to pan with shift+drag mouse or two-fingers-pan gesture.
    297          * @param {Object} [attributes.drag] Allow the user to drag objects with a pointer device.
    298          * @param {Object} [attributes.keyboard] Allow the user to drag objects with arrow keys on keyboard.
    299          * @param {Boolean} [attributes.axis=false] If set to true, show the axis. Can also be set to an object that is given to both axes as an attribute object.
    300          * @param {Boolean|Object} [attributes.grid] If set to true, shows the grid. Can also be set to an object that is given to the grid as its attribute object.
    301          * @param {Boolean} [attributes.registerEvents=true] Register mouse / touch events.
    302          * @returns {JXG.Board} Reference to the created board.
    303          */
    304         initBoard: function (box, attributes) {
    305             var originX, originY, unitX, unitY,
    306                 renderer,
    307                 offX = 0,
    308                 offY = 0,
    309                 w, h, dimensions,
    310                 bbox, attr, axattr, axattr_x, axattr_y,
    311                 board;
    312
    313             attributes = attributes || {};
    314             attr = this._setAttributes(attributes);
    315
    316             dimensions = Env.getDimensions(box, attr.document);
    317
    318             if (attr.unitx || attr.unity) {
    319                 originX = Type.def(attr.originx, 150);
    320                 originY = Type.def(attr.originy, 150);
    321                 unitX = Type.def(attr.unitx, 50);
    322                 unitY = Type.def(attr.unity, 50);
    323             } else {
    324                 bbox = attr.boundingbox;
    325                 if (bbox[0] < attr.maxboundingbox[0]) { bbox[0] = attr.maxboundingbox[0]; }
    326                 if (bbox[1] > attr.maxboundingbox[1]) { bbox[1] = attr.maxboundingbox[1]; }
    327                 if (bbox[2] > attr.maxboundingbox[2]) { bbox[2] = attr.maxboundingbox[2]; }
    328                 if (bbox[3] < attr.maxboundingbox[3]) { bbox[3] = attr.maxboundingbox[3]; }
    329
    330                 w = parseInt(dimensions.width, 10);
    331                 h = parseInt(dimensions.height, 10);
    332
    333                 if (Type.exists(bbox) && attr.keepaspectratio) {
    334                     /*
    335                      * If the boundingbox attribute is given and the ratio of height and width of the
    336                      * sides defined by the bounding box and the ratio of the dimensions of the div tag
    337                      * which contains the board do not coincide, then the smaller side is chosen.
    338                      */
    339                     unitX = w / (bbox[2] - bbox[0]);
    340                     unitY = h / (bbox[1] - bbox[3]);
    341
    342                     if (Math.abs(unitX) < Math.abs(unitY)) {
    343                         unitY = Math.abs(unitX) * unitY / Math.abs(unitY);
    344                         // Add the additional units in equal portions above and below
    345                         offY = (h / unitY - (bbox[1] - bbox[3])) * 0.5;
    346                     } else {
    347                         unitX = Math.abs(unitY) * unitX / Math.abs(unitX);
    348                         // Add the additional units in equal portions left and right
    349                         offX = (w / unitX - (bbox[2] - bbox[0])) * 0.5;
    350                     }
    351                 } else {
    352                     unitX = w / (bbox[2] - bbox[0]);
    353                     unitY = h / (bbox[1] - bbox[3]);
    354                 }
    355                 originX = -unitX * (bbox[0] - offX);
    356                 originY = unitY * (bbox[1] + offY);
    357             }
    358
    359             renderer = this.initRenderer(box, dimensions, attr.document, attr.renderer);
    360             this._setARIA(box, attr);
    361
    362             // create the board
    363             board = new Board(box, renderer, attr.id, [originX, originY],
    364                         attr.zoomfactor * attr.zoomx,
    365                         attr.zoomfactor * attr.zoomy,
    366                         unitX, unitY,
    367                         dimensions.width, dimensions.height,
    368                         attr);
    369
    370             board.keepaspectratio = attr.keepaspectratio;
    371
    372             this._fillBoard(board, attr, dimensions);
    373
    374             // create elements like axes, grid, navigation, ...
    375             board.suspendUpdate();
    376             if (attr.axis) {
    377                 axattr = typeof attr.axis === 'object' ? attr.axis : {};
    378
    379                 // The defaultAxes attributes are overwritten by user supplied axis object.
    380                 axattr_x = Type.deepCopy(Options.board.defaultAxes.x, axattr);
    381                 axattr_y = Type.deepCopy(Options.board.defaultAxes.y, axattr);
    382                 // The user supplied defaultAxes attributes are merged in.
    383                 if (attr.defaultaxes.x) {
    384                     axattr_x = Type.deepCopy(axattr_x, attr.defaultaxes.x);
    385                 }
    386                 if (attr.defaultaxes.y) {
    387                     axattr_y = Type.deepCopy(axattr_y, attr.defaultaxes.y);
    388                 }
    389
    390                 board.defaultAxes = {};
    391                 board.defaultAxes.x = board.create('axis', [[0, 0], [1, 0]], axattr_x);
    392                 board.defaultAxes.y = board.create('axis', [[0, 0], [0, 1]], axattr_y);
    393             }
    394             if (attr.grid) {
    395                 board.create('grid', [], (typeof attr.grid === 'object' ? attr.grid : {}));
    396             }
    397             board.unsuspendUpdate();
    398
    399             return board;
    400         },
    401
    402         /**
    403          * Load a board from a file containing a construction made with either GEONExT,
    404          * Intergeo, Geogebra, or Cinderella.
    405          * @param {String} box HTML-ID to the HTML-element in which the board is painted.
    406          * @param {String} file base64 encoded string.
    407          * @param {String} format containing the file format: 'Geonext' or 'Intergeo'.
    408          * @param {Object} attributes Attributes for the board and 'encoding'.
    409          *  Compressed files need encoding 'iso-8859-1'. Otherwise it probably is 'utf-8'.
    410          * @param {Function} callback
    411          * @returns {JXG.Board} Reference to the created board.
    412          * @see JXG.FileReader
    413          * @see JXG.GeonextReader
    414          * @see JXG.GeogebraReader
    415          * @see JXG.IntergeoReader
    416          * @see JXG.CinderellaReader
    417          *
    418          * @example
    419          * // Uncompressed file
    420          * var board = JXG.JSXGraph.loadBoardFromFile('jxgbox', 'filename', 'geonext',
    421          *      {encoding: 'utf-8'},
    422          *      function (board) { console.log("Done loading"); }
    423          * );
    424          * // Compressed file
    425          * var board = JXG.JSXGraph.loadBoardFromFile('jxgbox', 'filename', 'geonext',
    426          *      {encoding: 'iso-8859-1'},
    427          *      function (board) { console.log("Done loading"); }
    428          * );
    429          *
    430          * @example
    431          * // From <input type="file" id="localfile" />
    432          * var file = document.getElementById('localfile').files[0];
    433          * JXG.JSXGraph.loadBoardFromFile('jxgbox', file, 'geonext',
    434          *      {encoding: 'utf-8'},
    435          *      function (board) { console.log("Done loading"); }
    436          * );
    437          */
    438         loadBoardFromFile: function (box, file, format, attributes, callback) {
    439             var attr, renderer, board, dimensions, encoding;
    440
    441             attributes = attributes || {};
    442             attr = this._setAttributes(attributes);
    443
    444             dimensions = Env.getDimensions(box, attr.document);
    445             renderer = this.initRenderer(box, dimensions, attr.document, attr.renderer);
    446             this._setARIA(box, attr);
    447
    448             /* User default parameters, in parse* the values in the gxt files are submitted to board */
    449             board = new Board(box, renderer, '', [150, 150], 1, 1, 50, 50, dimensions.width, dimensions.height, attr);
    450             this._fillBoard(board, attr, dimensions);
    451             encoding = attr.encoding || 'iso-8859-1';
    452             FileReader.parseFileContent(file, board, format, true, encoding, callback);
    453
    454             return board;
    455         },
    456
    457         /**
    458          * Load a board from a base64 encoded string containing a construction made with either GEONExT,
    459          * Intergeo, Geogebra, or Cinderella.
    460          * @param {String} box HTML-ID to the HTML-element in which the board is painted.
    461          * @param {String} string base64 encoded string.
    462          * @param {String} format containing the file format: 'Geonext', 'Intergeo', 'Geogebra'.
    463          * @param {Object} attributes Attributes for the board and 'encoding'.
    464          *  Compressed files need encoding 'iso-8859-1'. Otherwise it probably is 'utf-8'.
    465          * @param {Function} callback
    466          * @returns {JXG.Board} Reference to the created board.
    467          * @see JXG.FileReader
    468          * @see JXG.GeonextReader
    469          * @see JXG.GeogebraReader
    470          * @see JXG.IntergeoReader
    471          * @see JXG.CinderellaReader
    472          */
    473         loadBoardFromString: function (box, string, format, attributes, callback) {
    474             var attr, renderer, board, dimensions;
    475
    476             attributes = attributes || {};
    477             attr = this._setAttributes(attributes);
    478
    479             dimensions = Env.getDimensions(box, attr.document);
    480             renderer = this.initRenderer(box, dimensions, attr.document);
    481             this._setARIA(box, attr);
    482
    483             /* User default parameters, in parse* the values in the gxt files are submitted to board */
    484             board = new Board(box, renderer, '', [150, 150], 1.0, 1.0, 50, 50, dimensions.width, dimensions.height, attr);
    485             this._fillBoard(board, attr, dimensions);
    486             FileReader.parseString(string, board, format, true, callback);
    487
    488             return board;
    489         },
    490
    491         /**
    492          * Delete a board and all its contents.
    493          * @param {JXG.Board,String} board HTML-ID to the DOM-element in which the board is drawn.
    494          */
    495         freeBoard: function (board) {
    496             var el;
    497
    498             if (typeof board === 'string') {
    499                 board = JXG.boards[board];
    500             }
    501
    502             this._removeARIANodes(board);
    503             board.removeEventHandlers();
    504             board.suspendUpdate();
    505
    506             // Remove all objects from the board.
    507             for (el in board.objects) {
    508                 if (board.objects.hasOwnProperty(el)) {
    509                     board.objects[el].remove();
    510                 }
    511             }
    512
    513             // Remove all the other things, left on the board, XHTML save
    514             while (board.containerObj.firstChild) {
    515                 board.containerObj.removeChild(board.containerObj.firstChild);
    516             }
    517
    518             // Tell the browser the objects aren't needed anymore
    519             for (el in board.objects) {
    520                 if (board.objects.hasOwnProperty(el)) {
    521                     delete board.objects[el];
    522                 }
    523             }
    524
    525             // Free the renderer and the algebra object
    526             delete board.renderer;
    527
    528             // clear the creator cache
    529             board.jc.creator.clearCache();
    530             delete board.jc;
    531
    532             // Finally remove the board itself from the boards array
    533             delete JXG.boards[board.id];
    534         },
    535
    536         /**
    537          * @deprecated Use JXG#registerElement
    538          * @param element
    539          * @param creator
    540          */
    541         registerElement: function (element, creator) {
    542             JXG.deprecated('JXG.JSXGraph.registerElement()', 'JXG.registerElement()');
    543             JXG.registerElement(element, creator);
    544         }
    545     };
    546
    547     // JessieScript/JessieCode startup: Search for script tags of type text/jessiescript and interprete them.
    548     if (Env.isBrowser && typeof window === 'object' && typeof document === 'object') {
    549         Env.addEvent(window, 'load', function () {
    550             var type, i, j, div,
    551                 id, board, txt,
    552                 width, height, maxWidth, aspectRatio, cssClasses,
    553                 bbox, axis, grid, code,
    554                 src, request, postpone = false,
    555                 scripts = document.getElementsByTagName('script'),
    556                 init = function (code, type, bbox) {
    557                     var board = JXG.JSXGraph.initBoard(id, {boundingbox: bbox, keepaspectratio: true, grid: grid, axis: axis, showReload: true});
    558
    559                     if (type.toLowerCase().indexOf('script') > -1) {
    560                         board.construct(code);
    561                     } else {
    562                         try {
    563                             board.jc.parse(code);
    564                         } catch (e2) {
    565                             JXG.debug(e2);
    566                         }
    567                     }
    568
    569                     return board;
    570                 },
    571                 makeReload = function (board, code, type, bbox) {
    572                     return function () {
    573                         var newBoard;
    574
    575                         JXG.JSXGraph.freeBoard(board);
    576                         newBoard = init(code, type, bbox);
    577                         newBoard.reload = makeReload(newBoard, code, type, bbox);
    578                     };
    579                 };
    580
    581             for (i = 0; i < scripts.length; i++) {
    582                 type = scripts[i].getAttribute('type', false);
    583
    584                 if (Type.exists(type) &&
    585                     (type.toLowerCase() === 'text/jessiescript' || type.toLowerCase() === 'jessiescript' ||
    586                      type.toLowerCase() === 'text/jessiecode' || type.toLowerCase() === 'jessiecode')) {
    587                     cssClasses = scripts[i].getAttribute('class', false) || '';
    588                     width = scripts[i].getAttribute('width', false) || '';
    589                     height = scripts[i].getAttribute('height', false) || '';
    590                     maxWidth = scripts[i].getAttribute('maxwidth', false) || '100%';
    591                     aspectRatio = scripts[i].getAttribute('aspectratio', false) || '1/1';
    592                     bbox = scripts[i].getAttribute('boundingbox', false) || '-5, 5, 5, -5';
    593                     id = scripts[i].getAttribute('container', false);
    594                     src = scripts[i].getAttribute('src', false);
    595
    596                     bbox = bbox.split(',');
    597                     if (bbox.length !== 4) {
    598                         bbox = [-5, 5, 5, -5];
    599                     } else {
    600                         for (j = 0; j < bbox.length; j++) {
    601                             bbox[j] = parseFloat(bbox[j]);
    602                         }
    603                     }
    604                     axis = Type.str2Bool(scripts[i].getAttribute('axis', false) || 'false');
    605                     grid = Type.str2Bool(scripts[i].getAttribute('grid', false) || 'false');
    606
    607                     if (!Type.exists(id)) {
    608                         id = 'jessiescript_autgen_jxg_' + i;
    609                         div = document.createElement('div');
    610                         div.setAttribute('id', id);
    611
    612                         txt = (width !== '') ? ('width:' + width + ';') : '';
    613                         txt += (height !== '') ? ('height:' + height + ';') : '';
    614                         txt += (maxWidth !== '') ? ('max-width:' + maxWidth + ';') : '';
    615                         txt += (aspectRatio !== '') ? ('aspect-ratio:' + aspectRatio + ';') : '';
    616
    617                         div.setAttribute('style', txt);
    618                         div.setAttribute('class', 'jxgbox ' + cssClasses);
    619                         try {
    620                             document.body.insertBefore(div, scripts[i]);
    621                         } catch (e) {
    622                             // there's probably jquery involved...
    623                             if (typeof jQuery === 'object') {
    624                                 jQuery(div).insertBefore(scripts[i]);
    625                             }
    626                         }
    627                     } else {
    628                         div = document.getElementById(id);
    629                     }
    630
    631                     code = '';
    632
    633                     if (Type.exists(src)) {
    634                         postpone = true;
    635                         request = new XMLHttpRequest();
    636                         request.open("GET", src);
    637                         request.overrideMimeType("text/plain; charset=x-user-defined");
    638                         /* jshint ignore:start */
    639                         request.addEventListener("load", function() {
    640                             if (this.status < 400) {
    641                                 code = this.responseText + '\n' + code;
    642                                 board = init(code, type, bbox);
    643                                 board.reload = makeReload(board, code, type, bbox);
    644                             } else {
    645                                 throw new Error("\nJSXGraph: failed to load file", src, ":", this.responseText);
    646                             }
    647                         });
    648                         request.addEventListener("error", function(e) {
    649                             throw new Error("\nJSXGraph: failed to load file", src, ":", e);
    650                         });
    651                         /* jshint ignore:end */
    652                         request.send();
    653                     } else {
    654                         postpone = false;
    655                     }
    656
    657                     if (document.getElementById(id)) {
    658                         code = scripts[i].innerHTML;
    659                         code = code.replace(/<!\[CDATA\[/g, '').replace(/\]\]>/g, '');
    660                         scripts[i].innerHTML = code;
    661
    662                         if (!postpone) {
    663                             // Do no wait for data from "src" attribute
    664                             board = init(code, type, bbox);
    665                             board.reload = makeReload(board, code, type, bbox);
    666                         }
    667                     } else {
    668                         JXG.debug('JSXGraph: Apparently the div injection failed. Can\'t create a board, sorry.');
    669                     }
    670                 }
    671             }
    672         }, window);
    673     }
    674
    675     return JXG.JSXGraph;
    676 });
    677