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