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