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