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