1 /*
  2     Copyright 2008-2026
  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 
218         JXG.boards[board.id] = board;
219     },
220 
221     /**
222      *
223      * @param {String|Object} container id of or reference to the HTML element in which the board is painted.
224      * @param {Object} attr An object that sets some of the board properties.
225      *
226      * @private
227      */
228     _setARIA: function (container, attr) {
229         var doc = attr.document,
230             node_jsx;
231             // Unused variables, made obsolete in db3e50f4dfa8b86b1ff619b578e243a97b41151c
232             // doc_glob,
233             // newNode,
234             // parent,
235             // id_label,
236             // id_description;
237 
238             if (typeof doc !== 'object') {
239                 if (!Env.isBrowser) {
240                     return;
241                 }
242                 doc = document;
243             }
244 
245         node_jsx = (Type.isString(container)) ? doc.getElementById(container) : container;
246         node_jsx.setAttribute("role", 'region');
247         node_jsx.setAttribute("aria-label", attr.title);              // set by initBoard( {title:})
248 
249         // doc_glob = node_jsx.ownerDocument; // This is the window.document element, needed below.
250         // parent = node_jsx.parentNode;
251 
252     },
253 
254     /**
255      * Remove the two corresponding ARIA divs when freeing a board
256      *
257      * @param {JXG.Board} board
258      *
259      * @private
260      */
261     _removeARIANodes: function (board) {
262         var node, id, doc;
263 
264         doc = board.document || document;
265         if (typeof doc !== 'object') {
266             return;
267         }
268 
269         id = board.containerObj.getAttribute("aria-labelledby");
270         node = doc.getElementById(id);
271         if (node && node.parentNode) {
272             node.parentNode.removeChild(node);
273         }
274         id = board.containerObj.getAttribute("aria-describedby");
275         node = doc.getElementById(id);
276         if (node && node.parentNode) {
277             node.parentNode.removeChild(node);
278         }
279     },
280 
281     /**
282      * Initialize a new board.
283      *
284      * @param {String|Object} box id of or reference to the HTML element in which the board is painted.
285      * @param {Object} attributes An object that sets some of the board properties.
286      * See {@link JXG.Board} for a list of available attributes of the board.
287      * Most of these attributes can also be set via {@link JXG.Options},
288      *
289      * @returns {JXG.Board} Reference to the created board.
290      *
291      * @see JXG.AbstractRenderer#drawNavigationBar
292      * @example
293      * var board = JXG.JSXGraph.initBoard('jxgbox', {
294      *     boundingbox: [-10, 5, 10, -5],
295      *     keepaspectratio: false,
296      *     axis: true
297      * });
298      *
299      * </pre><div id="JXGc0f76e98-20bc-4224-9016-7ffa10770dff" class="jxgbox" style="width: 600px; height: 300px;"></div>
300      * <script type="text/javascript">
301      *     (function() {
302      *         var board = JXG.JSXGraph.initBoard('JXGc0f76e98-20bc-4224-9016-7ffa10770dff', {
303      *         boundingbox: [-10, 5, 10, -5],
304      *         keepaspectratio: false,
305      *         axis: true
306      *     });
307      *
308      *     })();
309      *
310      * </script><pre>
311      *
312      *
313      * @example
314      * const board = JXG.JSXGraph.initBoard('jxgbox', {
315      *   boundingbox: [-10, 10, 10, -10],
316      *   axis: true,
317      *   showCopyright: true,
318      *   showFullscreen: true,
319      *   showScreenshot: false,
320      *   showClearTraces: false,
321      *   showInfobox: false,
322      *   showNavigation: true,
323      *   grid: false,
324      *   defaultAxes: {
325      *     x: {
326      *       withLabel: true,
327      *       label: {
328      *         position: '95% left',
329      *         offset: [-10, 10]
330      *       },
331      *       lastArrow: {
332      *         type: 4,
333      *         size: 10
334      *       }
335      *     },
336      *     y: {
337      *       withLabel: true,
338      *       label: {
339      *         position: '0.90fr right',
340      *         offset: [6, -6]
341      *       },
342      *       lastArrow: {
343      *         type: 4,
344      *         size: 10
345      *       }
346      *     }
347      *   }
348      * });
349      *
350      * </pre><div id="JXG4ced167d-3235-48bc-84e9-1a28fce00f6a" class="jxgbox" style="width: 300px; height: 300px;"></div>
351      * <script type="text/javascript">
352      *     (function() {
353      *         var board = JXG.JSXGraph.initBoard('JXG4ced167d-3235-48bc-84e9-1a28fce00f6a', {
354      *       boundingbox: [-10, 10, 10, -10],
355      *       axis: true,
356      *       showCopyright: true,
357      *       showFullscreen: true,
358      *       showScreenshot: false,
359      *       showClearTraces: false,
360      *       showInfobox: false,
361      *       showNavigation: true,
362      *       grid: false,
363      *       defaultAxes: {
364      *         x: {
365      *           withLabel: true,
366      *           label: {
367      *             position: '95% left',
368      *             offset: [0, 0]
369      *           },
370      *           lastArrow: {
371      *             type: 4,
372      *             size: 10
373      *           }
374      *         },
375      *         y: {
376      *           withLabel: true,
377      *           label: {
378      *             position: '0.90fr right',
379      *             offset: [0, 0]
380      *           },
381      *           lastArrow: {
382      *             type: 4,
383      *             size: 10
384      *           }
385      *         }
386      *       }
387      *     });
388      *
389      *     })();
390      *
391      * </script><pre>
392      * @example
393      * const board = JXG.JSXGraph.initBoard('jxgbox', {
394      *     boundingbox: [-5, 5, 5, -5],
395      *     intl: {
396      *         enabled: false,
397      *         locale: 'en-EN'
398      *     },
399      *     keepaspectratio: true,
400      *     axis: true,
401      *     defaultAxes: {
402      *         x: {
403      *             ticks: {
404      *                 intl: {
405      *                         enabled: true,
406      *                         options: {
407      *                             style: 'unit',
408      *                             unit: 'kilometer-per-hour',
409      *                             unitDisplay: 'narrow'
410      *                         }
411      *                 }
412      *             }
413      *         },
414      *         y: {
415      *             ticks: {
416      *             }
417      *         }
418      *     },
419      *     infobox: {
420      *         fontSize: 20,
421      *         intl: {
422      *             enabled: true,
423      *             options: {
424      *                 minimumFractionDigits: 4,
425      *                 maximumFractionDigits: 5
426      *             }
427      *         }
428      *     }
429      * });
430      *
431      * </pre><div id="JXGdac54e59-f1e8-4fa6-bbcc-7486f7f6f960" class="jxgbox" style="width: 600px; height: 600px;"></div>
432      * <script type="text/javascript">
433      *     (function() {
434      *         var board = JXG.JSXGraph.initBoard('JXGdac54e59-f1e8-4fa6-bbcc-7486f7f6f960', {
435      *         boundingbox: [-5, 5, 5, -5],
436      *         intl: {
437      *             enabled: false,
438      *             locale: 'en-EN'
439      *         },
440      *         keepaspectratio: true,
441      *         axis: true,
442      *         defaultAxes: {
443      *             x: {
444      *                 ticks: {
445      *                     intl: {
446      *                             enabled: true,
447      *                             options: {
448      *                                 style: 'unit',
449      *                                 unit: 'kilometer-per-hour',
450      *                                 unitDisplay: 'narrow'
451      *                             }
452      *                     }
453      *                 }
454      *             },
455      *             y: {
456      *                 ticks: {
457      *                 }
458      *             }
459      *         },
460      *         infobox: {
461      *             fontSize: 20,
462      *             intl: {
463      *                 enabled: true,
464      *                 options: {
465      *                     minimumFractionDigits: 4,
466      *                     maximumFractionDigits: 5
467      *                 }
468      *             }
469      *         }
470      *     });
471      *
472      *     })();
473      *
474      * </script><pre>
475      *
476      *
477      */
478     //  *
479     //  * @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
480     //  * @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.
481     //  * @param {Boolean} [attributes.showCopyright=false] Show the copyright string in the top left corner.
482     //  * @param {Boolean} [attributes.showNavigation=false] Show the navigation buttons in the bottom right corner.
483     //  * @param {Object} [attributes.zoom] Allow the user to zoom with the mouse wheel or the two-fingers-zoom gesture.
484     //  * @param {Object} [attributes.pan] Allow the user to pan with shift+drag mouse or two-fingers-pan gesture.
485     //  * @param {Object} [attributes.drag] Allow the user to drag objects with a pointer device.
486     //  * @param {Object} [attributes.keyboard] Allow the user to drag objects with arrow keys on keyboard.
487     //  * @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.
488     //  * @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.
489     //  * @param {Boolean} [attributes.registerEvents=true] Register mouse / touch events.
490     initBoard: function (box, attributes) {
491         var originX, originY, unitX, unitY, w, h,
492             offX = 0, offY = 0,
493             renderer, dimensions, bbox,
494             attr, axattr, axattr_x, axattr_y,
495             options,
496             theme = {},
497             board;
498 
499         attributes = attributes || {}; // User supplied attributes
500         // Merge a possible theme
501         if (attributes.theme !== 'default' && Type.exists(JXG.themes[attributes.theme])) {
502             theme = JXG.themes[attributes.theme];
503         }
504         options = Type.deepCopy(Options, theme, true);    // Copy global options
505         attr = this._setAttributes(attributes, options);  // Merge user supplied attributes into global options
506 
507         dimensions = Env.getDimensions(box, attr.document);
508 
509         if (attr.unitx || attr.unity) {
510             originX = Type.def(attr.originx, 150);
511             originY = Type.def(attr.originy, 150);
512             unitX = Type.def(attr.unitx, 50);
513             unitY = Type.def(attr.unity, 50);
514         } else {
515             bbox = attr.boundingbox;
516             if (bbox[0] < attr.maxboundingbox[0]) {
517                 bbox[0] = attr.maxboundingbox[0];
518             }
519             if (bbox[1] > attr.maxboundingbox[1]) {
520                 bbox[1] = attr.maxboundingbox[1];
521             }
522             if (bbox[2] > attr.maxboundingbox[2]) {
523                 bbox[2] = attr.maxboundingbox[2];
524             }
525             if (bbox[3] < attr.maxboundingbox[3]) {
526                 bbox[3] = attr.maxboundingbox[3];
527             }
528 
529             // Size of HTML div.
530             // If zero, the size is set to a small value to avoid
531             // division by zero.
532             // w = Math.max(parseInt(dimensions.width, 10), Mat.eps);
533             // h = Math.max(parseInt(dimensions.height, 10), Mat.eps);
534             w = parseInt(dimensions.width, 10);
535             h = parseInt(dimensions.height, 10);
536 
537             if (Type.exists(bbox) && attr.keepaspectratio) {
538                 /*
539                  * If the boundingbox attribute is given and the ratio of height and width of the
540                  * sides defined by the bounding box and the ratio of the dimensions of the div tag
541                  * which contains the board do not coincide, then the smaller side is chosen.
542                  */
543                 unitX = w / (bbox[2] - bbox[0]);
544                 unitY = h / (bbox[1] - bbox[3]);
545 
546                 if (Math.abs(unitX) < Math.abs(unitY)) {
547                     unitY = (Math.abs(unitX) * unitY) / Math.abs(unitY);
548                     // Add the additional units in equal portions above and below
549                     offY = (h / unitY - (bbox[1] - bbox[3])) * 0.5;
550                 } else {
551                     unitX = (Math.abs(unitY) * unitX) / Math.abs(unitX);
552                     // Add the additional units in equal portions left and right
553                     offX = (w / unitX - (bbox[2] - bbox[0])) * 0.5;
554                 }
555             } else {
556                 unitX = w / (bbox[2] - bbox[0]);
557                 unitY = h / (bbox[1] - bbox[3]);
558             }
559             originX = -unitX * (bbox[0] - offX);
560             originY = unitY * (bbox[1] + offY);
561         }
562 
563         renderer = this.initRenderer(box, dimensions, attr.document, attr.renderer);
564         this._setARIA(box, attr);
565 
566         // Create the board.
567         // board.options will contain the user supplied board attributes
568         board = new Board(
569             box,
570             renderer,
571             attr.id,
572             [originX, originY],
573             /*attr.zoomfactor * */ attr.zoomx,
574             /*attr.zoomfactor * */ attr.zoomy,
575             unitX,
576             unitY,
577             dimensions.width,
578             dimensions.height,
579             attr
580         );
581 
582         board.keepaspectratio = attr.keepaspectratio;
583 
584         this._fillBoard(board, attr, dimensions);
585 
586         // Create elements like axes, grid, navigation, ...
587         board.suspendUpdate();
588         attr = board.attr;
589         if (attr.axis) {
590             axattr = typeof attr.axis === "object" ? attr.axis : {};
591 
592             // The defaultAxes attributes are overwritten by user supplied axis object.
593             axattr_x = Type.deepCopy(options.board.defaultaxes.x, axattr);
594             axattr_y = Type.deepCopy(options.board.defaultaxes.y, axattr);
595 
596             // The user supplied defaultAxes attributes are merged in.
597             if (attr.defaultaxes.x) {
598                 axattr_x = Type.deepCopy(axattr_x, attr.defaultaxes.x);
599             }
600             if (attr.defaultaxes.y) {
601                 axattr_y = Type.deepCopy(axattr_y, attr.defaultaxes.y);
602             }
603 
604             board.defaultAxes = {};
605             board.defaultAxes.x = board.create("axis", [[0, 0], [1, 0]], axattr_x);
606             board.defaultAxes.y = board.create("axis", [[0, 0], [0, 1]], axattr_y);
607         }
608         if (attr.grid) {
609             board.create("grid", [], typeof attr.grid === "object" ? attr.grid : {});
610         }
611         board.unsuspendUpdate();
612 
613         // Set CSS styles of JSXGraph div
614         board.setAttribute({cssStyle: attr.cssstyle}, true);
615 
616         return board;
617     },
618 
619     /**
620      * Load a board from a file containing a construction made with either GEONExT,
621      * Intergeo, Geogebra, or Cinderella.
622      * @param {String|Object} box id of or reference to the HTML element in which the board is painted.
623      * @param {String} file base64 encoded string.
624      * @param {String} format containing the file format: 'Geonext' or 'Intergeo'.
625      * @param {Object} attributes Attributes for the board and 'encoding'.
626      *  Compressed files need encoding 'iso-8859-1'. Otherwise it probably is 'utf-8'.
627      * @param {Function} callback
628      * @returns {JXG.Board} Reference to the created board.
629      * @see JXG.FileReader
630      * @see JXG.GeonextReader
631      * @see JXG.GeogebraReader
632      * @see JXG.IntergeoReader
633      * @see JXG.CinderellaReader
634      *
635      * @example
636      * // Uncompressed file
637      * var board = JXG.JSXGraph.loadBoardFromFile('jxgbox', 'filename', 'geonext',
638      *      {encoding: 'utf-8'},
639      *      function (board) { console.log("Done loading"); }
640      * );
641      * // Compressed file
642      * var board = JXG.JSXGraph.loadBoardFromFile('jxgbox', 'filename', 'geonext',
643      *      {encoding: 'iso-8859-1'},
644      *      function (board) { console.log("Done loading"); }
645      * );
646      *
647      * @example
648      * // From <input type="file" id="localfile" />
649      * var file = document.getElementById('localfile').files[0];
650      * JXG.JSXGraph.loadBoardFromFile('jxgbox', file, 'geonext',
651      *      {encoding: 'utf-8'},
652      *      function (board) { console.log("Done loading"); }
653      * );
654      */
655     loadBoardFromFile: function (box, file, format, attributes, callback) {
656         var attr, renderer, board, dimensions, encoding;
657 
658         attributes = attributes || {};
659         attr = this._setAttributes(attributes);
660 
661         dimensions = Env.getDimensions(box, attr.document);
662         renderer = this.initRenderer(box, dimensions, attr.document, attr.renderer);
663         this._setARIA(box, attr);
664 
665         /* User default parameters, in parse* the values in the gxt files are submitted to board */
666         board = new Board(
667             box,
668             renderer,
669             "",
670             [150, 150],
671             1,
672             1,
673             50,
674             50,
675             dimensions.width,
676             dimensions.height,
677             attr
678         );
679         this._fillBoard(board, attr, dimensions);
680         encoding = attr.encoding || "iso-8859-1";
681         FileReader.parseFileContent(file, board, format, true, encoding, callback);
682 
683         return board;
684     },
685 
686     /**
687      * Load a board from a base64 encoded string containing a construction made with either GEONExT,
688      * Intergeo, Geogebra, or Cinderella.
689      * @param {String|Object} box id of or reference to the HTML element in which the board is painted.
690      * @param {String} string base64 encoded string.
691      * @param {String} format containing the file format: 'Geonext', 'Intergeo', 'Geogebra'.
692      * @param {Object} attributes Attributes for the board and 'encoding'.
693      *  Compressed files need encoding 'iso-8859-1'. Otherwise it probably is 'utf-8'.
694      * @param {Function} callback
695      * @returns {JXG.Board} Reference to the created board.
696      * @see JXG.FileReader
697      * @see JXG.GeonextReader
698      * @see JXG.GeogebraReader
699      * @see JXG.IntergeoReader
700      * @see JXG.CinderellaReader
701      */
702     loadBoardFromString: function (box, string, format, attributes, callback) {
703         var attr, renderer, board, dimensions;
704 
705         attributes = attributes || {};
706         attr = this._setAttributes(attributes);
707 
708         dimensions = Env.getDimensions(box, attr.document);
709         renderer = this.initRenderer(box, dimensions, attr.document, attr.renderer);
710         this._setARIA(box, attr);
711 
712         /* User default parameters, in parse* the values in the gxt files are submitted to board */
713         board = new Board(
714             box,
715             renderer,
716             "",
717             [150, 150],
718             1.0,
719             1.0,
720             50,
721             50,
722             dimensions.width,
723             dimensions.height,
724             attr
725         );
726         this._fillBoard(board, attr, dimensions);
727         FileReader.parseString(string, board, format, true, callback);
728 
729         return board;
730     },
731 
732     /**
733      * Delete a board and all its contents.
734      * @param {JXG.Board|String} board id of or reference to the DOM element in which the board is drawn.
735      *
736      */
737     freeBoard: function (board) {
738         var el;
739 
740         if (typeof board === 'string') {
741             board = JXG.boards[board];
742         }
743 
744         this._removeARIANodes(board);
745         board.removeEventHandlers();
746         board.suspendUpdate();
747 
748         // Remove all objects from the board.
749         for (el in board.objects) {
750             if (board.objects.hasOwnProperty(el)) {
751                 board.objects[el].remove();
752             }
753         }
754 
755         // Remove all the other things, left on the board, XHTML save
756         while (board.containerObj.firstChild) {
757             board.containerObj.removeChild(board.containerObj.firstChild);
758         }
759 
760         // Tell the browser the objects aren't needed anymore
761         for (el in board.objects) {
762             if (board.objects.hasOwnProperty(el)) {
763                 delete board.objects[el];
764             }
765         }
766 
767         // Free the renderer and the algebra object
768         delete board.renderer;
769 
770         // clear the creator cache
771         board.jc.creator.clearCache();
772         delete board.jc;
773 
774         // Finally remove the board itself from the boards array
775         delete JXG.boards[board.id];
776     },
777 
778     /**
779      * @deprecated Use JXG#registerElement
780      * @param element
781      * @param creator
782      */
783     registerElement: function (element, creator) {
784         JXG.deprecated("JXG.JSXGraph.registerElement()", "JXG.registerElement()");
785         JXG.registerElement(element, creator);
786     }
787 };
788 
789 // JessieScript/JessieCode startup:
790 // Search for script tags of type text/jessiecode and execute them.
791 if (Env.isBrowser && typeof window === 'object' && typeof document === 'object') {
792     Env.addEvent(window, 'load',
793         function () {
794             var type, i, j, div, id,
795                 board, txt, width, height, maxWidth, aspectRatio,
796                 cssClasses, bbox, axis, grid, code, src, request,
797                 postpone = false,
798 
799                 scripts = document.getElementsByTagName('script'),
800                 init = function (code, type, bbox) {
801                     var board = JXG.JSXGraph.initBoard(id, {
802                         boundingbox: bbox,
803                         keepaspectratio: true,
804                         grid: grid,
805                         axis: axis,
806                         showReload: true
807                     });
808 
809                     if (type.toLowerCase().indexOf('script') > -1) {
810                         board.construct(code);
811                     } else {
812                         try {
813                             board.jc.parse(code);
814                         } catch (e2) {
815                             JXG.debug(e2);
816                         }
817                     }
818 
819                     return board;
820                 },
821                 makeReload = function (board, code, type, bbox) {
822                     return function () {
823                         var newBoard;
824 
825                         JXG.JSXGraph.freeBoard(board);
826                         newBoard = init(code, type, bbox);
827                         newBoard.reload = makeReload(newBoard, code, type, bbox);
828                     };
829                 };
830 
831             for (i = 0; i < scripts.length; i++) {
832                 type = scripts[i].getAttribute("type", false);
833 
834                 if (
835                     Type.exists(type) &&
836                     (type.toLowerCase() === "text/jessiescript" ||
837                         type.toLowerCase() === "jessiescript" ||
838                         type.toLowerCase() === "text/jessiecode" ||
839                         type.toLowerCase() === 'jessiecode')
840                 ) {
841                     cssClasses = scripts[i].getAttribute("class", false) || "";
842                     width = scripts[i].getAttribute("width", false) || "";
843                     height = scripts[i].getAttribute("height", false) || "";
844                     maxWidth = scripts[i].getAttribute("maxwidth", false) || "100%";
845                     aspectRatio = scripts[i].getAttribute("aspectratio", false) || "1/1";
846                     bbox = scripts[i].getAttribute("boundingbox", false) || "-5, 5, 5, -5";
847                     id = scripts[i].getAttribute("container", false);
848                     src = scripts[i].getAttribute("src", false);
849 
850                     bbox = bbox.split(",");
851                     if (bbox.length !== 4) {
852                         bbox = [-5, 5, 5, -5];
853                     } else {
854                         for (j = 0; j < bbox.length; j++) {
855                             bbox[j] = parseFloat(bbox[j]);
856                         }
857                     }
858                     axis = Type.str2Bool(scripts[i].getAttribute("axis", false) || 'false');
859                     grid = Type.str2Bool(scripts[i].getAttribute("grid", false) || 'false');
860 
861                     if (!Type.exists(id)) {
862                         id = "jessiescript_autgen_jxg_" + i;
863                         div = document.createElement('div');
864                         div.setAttribute("id", id);
865 
866                         txt = width !== "" ? "width:" + width + ";" : "";
867                         txt += height !== "" ? "height:" + height + ";" : "";
868                         txt += maxWidth !== "" ? "max-width:" + maxWidth + ";" : "";
869                         txt += aspectRatio !== "" ? "aspect-ratio:" + aspectRatio + ";" : "";
870 
871                         div.setAttribute("style", txt);
872                         div.setAttribute("class", "jxgbox " + cssClasses);
873                         try {
874                             document.body.insertBefore(div, scripts[i]);
875                         } catch (e) {
876                             // there's probably jquery involved...
877                             if (Type.exists(jQuery) && typeof jQuery === 'object') {
878                                 jQuery(div).insertBefore(scripts[i]);
879                             }
880                         }
881                     } else {
882                         div = document.getElementById(id);
883                     }
884 
885                     code = "";
886 
887                     if (Type.exists(src)) {
888                         postpone = true;
889                         request = new XMLHttpRequest();
890                         request.open("GET", src);
891                         request.overrideMimeType("text/plain; charset=x-user-defined");
892                         /* jshint ignore:start */
893                         request.addEventListener("load", function () {
894                             if (this.status < 400) {
895                                 code = this.responseText + "\n" + code;
896                                 board = init(code, type, bbox);
897                                 board.reload = makeReload(board, code, type, bbox);
898                             } else {
899                                 throw new Error(
900                                     "\nJSXGraph: failed to load file",
901                                     src,
902                                     ":",
903                                     this.responseText
904                                 );
905                             }
906                         });
907                         request.addEventListener("error", function (e) {
908                             throw new Error("\nJSXGraph: failed to load file", src, ":", e);
909                         });
910                         /* jshint ignore:end */
911                         request.send();
912                     } else {
913                         postpone = false;
914                     }
915 
916                     if (document.getElementById(id)) {
917                         code = scripts[i].innerHTML;
918                         code = code.replace(/<!\[CDATA\[/g, "").replace(/\]\]>/g, "");
919                         scripts[i].innerHTML = code;
920 
921                         if (!postpone) {
922                             // Do no wait for data from "src" attribute
923                             board = init(code, type, bbox);
924                             board.reload = makeReload(board, code, type, bbox);
925                         }
926                     } else {
927                         JXG.debug(
928                             "JSXGraph: Apparently the div injection failed. Can't create a board, sorry."
929                         );
930                     }
931                 }
932             }
933         },
934         window
935     );
936 }
937 
938 export default JXG.JSXGraph;
939