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