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