1 /* 2 Copyright 2008-2012 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: you can redistribute it and/or modify 13 it under the terms of the GNU Lesser General Public License as published by 14 the Free Software Foundation, either version 3 of the License, or 15 (at your option) any later version. 16 17 JSXGraph is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 GNU Lesser General Public License for more details. 21 22 You should have received a copy of the GNU Lesser General Public License 23 along with JSXGraph. If not, see <http://www.gnu.org/licenses/>. 24 */ 25 26 /** 27 * @fileoverview The JSXGraph object is defined in this file. JXG.JSXGraph controls all boards. 28 * It has methods to create, save, load and free boards. Additionally some helper functions are 29 * defined in this file directly in the JXG namespace. 30 * @version 0.83 31 */ 32 33 /** 34 * Constructs a new JSXGraph singleton object. 35 * @class The JXG.JSXGraph singleton stores all properties required 36 * to load, save, create and free a board. 37 */ 38 JXG.JSXGraph = { 39 40 /** 41 * The small gray version indicator in the top left corner of every JSXGraph board (if 42 * showCopyright is not set to false on board creation). 43 * @type String 44 */ 45 licenseText: 'JSXGraph v0.96 Copyright (C) see http://jsxgraph.org', 46 47 /** 48 * Associative array that keeps references to all boards. 49 * @type Object 50 */ 51 boards: {}, 52 53 /** 54 * Associative array that keeps track of all constructable elements registered 55 * via {@link JXG.JSXGraph.registerElement}. 56 * @type Object 57 */ 58 elements: {}, 59 60 /** 61 * Stores the renderer that is used to draw the boards. 62 * @type String 63 */ 64 rendererType: (function() { 65 var loadRenderer = function (renderer) { 66 var arr, i; 67 68 // Load the source files for the renderer 69 if (JXG.rendererFiles[renderer]) { 70 arr = JXG.rendererFiles[renderer].split(','); 71 72 for (i = 0; i < arr.length; i++) { 73 (function(include) { 74 JXG.require(JXG.requirePath + include + '.js'); 75 })(arr[i]); 76 } 77 78 delete(JXG.rendererFiles[renderer]); 79 } 80 }; 81 82 JXG.Options.renderer = 'no'; 83 84 if (JXG.supportsVML()) { 85 JXG.Options.renderer = 'vml'; 86 // Ok, this is some real magic going on here. IE/VML always was so 87 // terribly slow, except in one place: Examples placed in a moodle course 88 // was almost as fast as in other browsers. So i grabbed all the css and 89 // lib scripts from our moodle, added them to a jsxgraph example and it 90 // worked. next step was to strip all the css/lib code which didn't affect 91 // the VML update speed. The following five lines are what was left after 92 // the last step and yes - it basically does nothing but reads two 93 // properties of document.body on every mouse move. why? we don't know. if 94 // you know, please let us know. 95 function MouseMove() { 96 if (document.body) { 97 document.body.scrollLeft; 98 document.body.scrollTop; 99 } 100 } 101 document.onmousemove = MouseMove; 102 loadRenderer('vml'); 103 } 104 105 if (JXG.supportsCanvas()) { 106 JXG.Options.renderer = 'canvas'; 107 loadRenderer('canvas'); 108 } 109 110 if (JXG.supportsSVG()) { 111 JXG.Options.renderer = 'svg'; 112 loadRenderer('svg'); 113 } 114 115 // we are inside node 116 if (JXG.isNode()) { 117 JXG.Options.renderer = 'canvas'; 118 loadRenderer('canvas'); 119 } 120 121 return JXG.Options.renderer; 122 })(), 123 124 /** 125 * Initialise a new board. 126 * @param {String} box Html-ID to the Html-element in which the board is painted. 127 * @param {Object} attributes An object that sets some of the board properties. Most of these properties can be set via JXG.Options. Valid properties are 128 * <ul> 129 * <li><b>boundingbox</b>: An array containing four numbers describing the left, top, right and bottom boundary of the board in user coordinates</li> 130 * <li><b>keepaspectratio</b>: If <tt>true</tt>, the bounding box is adjusted to the same aspect ratio as the aspect ratio of the div containing the board.</li> 131 * <li><b>showCopyright</b>: Show the copyright string in the top left corner.</li> 132 * <li><b>showNavigation</b>: Show the navigation buttons in the bottom right corner.</li> 133 * <li><b>zoom</b>: Allow the user to zoom with the mouse wheel or the two-fingers-zoom gesture.</li> 134 * <li><b>pan</b>: Allow the user to pan with shift+drag mouse or two-fingers-pan gesture.</li> 135 * <li><b>axis</b>: If set to true, show the axis. Can also be set to an object that is given to both axes as an attribute object.</li> 136 * <li><b>grid</b>: If set to true, shows the grid. Can also bet set to an object that is given to the grid as its attribute object.</li> 137 * </ul> 138 * @returns {JXG.Board} Reference to the created board. 139 */ 140 initBoard: function (box, attributes) { 141 var renderer, 142 originX, originY, unitX, unitY, 143 w, h, dimensions, 144 bbox, 145 zoomfactor, zoomX, zoomY, 146 showCopyright, showNavi, 147 board, 148 wheelzoom, shiftpan, attr, boxid; 149 150 dimensions = JXG.getDimensions(box); 151 152 if (typeof document !== 'undefined' && box !== null) { 153 boxid = document.getElementById(box); 154 } else { 155 boxid = box; 156 } 157 158 // parse attributes 159 if (typeof attributes == 'undefined') { 160 attributes = {}; 161 } 162 163 if (typeof attributes["boundingbox"] != 'undefined') { 164 bbox = attributes["boundingbox"]; 165 w = parseInt(dimensions.width); 166 h = parseInt(dimensions.height); 167 168 if (attributes["keepaspectratio"]) { 169 /* 170 * If the boundingbox attribute is given and the ratio of height and width of the 171 * sides defined by the bounding box and the ratio of the dimensions of the div tag 172 * which contains the board do not coincide, then the smaller side is chosen. 173 */ 174 unitX = w/(bbox[2]-bbox[0]); 175 unitY = h/(-bbox[3]+bbox[1]); 176 if (Math.abs(unitX)<Math.abs(unitY)) { 177 unitY = Math.abs(unitX)*unitY/Math.abs(unitY); 178 } else { 179 unitX = Math.abs(unitY)*unitX/Math.abs(unitX); 180 } 181 } else { 182 unitX = w/(bbox[2]-bbox[0]); 183 unitY = h/(-bbox[3]+bbox[1]); 184 } 185 originX = -unitX*bbox[0]; 186 originY = unitY*bbox[1]; 187 } else { 188 originX = ( (typeof attributes["originX"]) == 'undefined' ? 150 : attributes["originX"]); 189 originY = ( (typeof attributes["originY"]) == 'undefined' ? 150 : attributes["originY"]); 190 unitX = ( (typeof attributes["unitX"]) == 'undefined' ? 50 : attributes["unitX"]); 191 unitY = ( (typeof attributes["unitY"]) == 'undefined' ? 50 : attributes["unitY"]); 192 } 193 194 zoomfactor = ( (typeof attributes["zoomfactor"]) == 'undefined' ? 1.0 : attributes["zoom"]); 195 zoomX = zoomfactor*( (typeof attributes["zoomX"]) == 'undefined' ? 1.0 : attributes["zoomX"]); 196 zoomY = zoomfactor*( (typeof attributes["zoomY"]) == 'undefined' ? 1.0 : attributes["zoomY"]); 197 198 showCopyright = ( (typeof attributes["showCopyright"]) == 'undefined' ? JXG.Options.showCopyright : attributes["showCopyright"]); 199 200 wheelzoom = ( (typeof attributes["zoom"]) == 'undefined' ? JXG.Options.zoom.wheel : attributes["zoom"]); 201 shiftpan = ( (typeof attributes["pan"]) == 'undefined' ? JXG.Options.pan : attributes["pan"]); 202 203 // create the renderer 204 if(JXG.Options.renderer == 'svg') { 205 renderer = new JXG.SVGRenderer(boxid); 206 } else if(JXG.Options.renderer == 'vml') { 207 renderer = new JXG.VMLRenderer(boxid); 208 } else if(JXG.Options.renderer == 'silverlight') { 209 renderer = new JXG.SilverlightRenderer(boxid, dimensions.width, dimensions.height); 210 } else if (JXG.Options.renderer == 'canvas') { 211 renderer = new JXG.CanvasRenderer(boxid); 212 } else { 213 renderer = new JXG.NoRenderer(); 214 } 215 216 // create the board 217 board = new JXG.Board(box, renderer, '', [originX, originY], zoomX, zoomY, unitX, unitY, dimensions.width, dimensions.height, showCopyright); 218 this.boards[board.id] = board; 219 220 board.keepaspectratio = attributes.keepaspectratio; 221 board.options.zoom.wheel = wheelzoom; 222 board.options.pan = shiftpan; 223 224 // create elements like axes, grid, navigation, ... 225 board.suspendUpdate(); 226 board.initInfobox(); 227 228 if(attributes["axis"]) { 229 attr = typeof attributes['axis'] === 'object' ? attributes['axis'] : {ticks: {drawZero: true}}; 230 board.defaultAxes = {}; 231 board.defaultAxes.x = board.create('axis', [[0,0], [1,0]], attr); 232 board.defaultAxes.y = board.create('axis', [[0,0], [0,1]], attr); 233 } 234 235 if(attributes["grid"]) { 236 board.create('grid', [], (typeof attributes["grid"] === 'object' ? attributes['grid'] : {})); 237 } 238 239 if (typeof attributes["shownavigation"] != 'undefined') attributes["showNavigation"] = attributes["shownavigation"]; 240 showNavi = ( (typeof attributes["showNavigation"]) == 'undefined' ? board.options.showNavigation : attributes["showNavigation"]); 241 if (showNavi) { 242 board.renderer.drawZoomBar(board); 243 } 244 board.unsuspendUpdate(); 245 246 return board; 247 }, 248 249 /** 250 * Load a board from a file containing a construction made with either GEONExT, 251 * Intergeo, Geogebra, or Cinderella. 252 * @param {String} box HTML-ID to the HTML-element in which the board is painted. 253 * @param {String} file base64 encoded string. 254 * @param {String} format containing the file format: 'Geonext' or 'Intergeo'. 255 * @returns {JXG.Board} Reference to the created board. 256 * 257 * @see JXG.FileReader 258 * @see JXG.GeonextReader 259 * @see JXG.GeogebraReader 260 * @see JXG.IntergeoReader 261 * @see JXG.CinderellaReader 262 */ 263 loadBoardFromFile: function (box, file, format) { 264 var renderer, board, dimensions; 265 266 if(JXG.Options.renderer == 'svg') { 267 renderer = new JXG.SVGRenderer(document.getElementById(box)); 268 } else if(JXG.Options.renderer == 'vml') { 269 renderer = new JXG.VMLRenderer(document.getElementById(box)); 270 } else if(JXG.Options.renderer == 'silverlight') { 271 renderer = new JXG.SilverlightRenderer(document.getElementById(box), dimensions.width, dimensions.height); 272 } else { 273 renderer = new JXG.CanvasRenderer(document.getElementById(box)); 274 } 275 276 //var dimensions = document.getElementById(box).getDimensions(); 277 dimensions = JXG.getDimensions(box); 278 279 /* User default parameters, in parse* the values in the gxt files are submitted to board */ 280 board = new JXG.Board(box, renderer, '', [150, 150], 1.0, 1.0, 50, 50, dimensions.width, dimensions.height); 281 board.initInfobox(); 282 283 JXG.FileReader.parseFileContent(file, board, format); 284 if(board.options.showNavigation) { 285 board.renderer.drawZoomBar(board); 286 } 287 this.boards[board.id] = board; 288 return board; 289 }, 290 291 /** 292 * Load a board from a base64 encoded string containing a construction made with either GEONExT, 293 * Intergeo, Geogebra, or Cinderella. 294 * @param {String} box HTML-ID to the HTML-element in which the board is painted. 295 * @param {String} string base64 encoded string. 296 * @param {String} format containing the file format: 'Geonext' or 'Intergeo'. 297 * @returns {JXG.Board} Reference to the created board. 298 * 299 * @see JXG.FileReader 300 * @see JXG.GeonextReader 301 * @see JXG.GeogebraReader 302 * @see JXG.IntergeoReader 303 * @see JXG.CinderellaReader 304 */ 305 loadBoardFromString: function(box, string, format) { 306 var renderer, dimensions, board; 307 308 if(JXG.Options.renderer == 'svg') { 309 renderer = new JXG.SVGRenderer(document.getElementById(box)); 310 } else if(JXG.Options.renderer == 'vml') { 311 renderer = new JXG.VMLRenderer(document.getElementById(box)); 312 } else if(JXG.Options.renderer == 'silverlight') { 313 renderer = new JXG.SilverlightRenderer(document.getElementById(box), dimensions.width, dimensions.height); 314 } else { 315 renderer = new JXG.CanvasRenderer(document.getElementById(box)); 316 } 317 //var dimensions = document.getElementById(box).getDimensions(); 318 dimensions = JXG.getDimensions(box); 319 320 /* User default parameters, in parse* the values in the gxt files are submitted to board */ 321 board = new JXG.Board(box, renderer, '', [150, 150], 1.0, 1.0, 50, 50, dimensions.width, dimensions.height); 322 board.initInfobox(); 323 324 JXG.FileReader.parseString(string, board, format, true); 325 if (board.options.showNavigation) { 326 board.renderer.drawZoomBar(board); 327 } 328 329 this.boards[board.id] = board; 330 return board; 331 }, 332 333 /** 334 * Delete a board and all its contents. 335 * @param {String} board HTML-ID to the DOM-element in which the board is drawn. 336 */ 337 freeBoard: function (board) { 338 var el, i; 339 340 if (typeof(board) == 'string') { 341 board = this.boards[board]; 342 } 343 344 board.removeEventHandlers(); 345 board.suspendUpdate(); 346 347 // Remove all objects from the board. 348 for(el in board.objects) { 349 //board.removeObject(board.objects[el]); 350 board.objects[el].remove(); 351 } 352 353 // Remove all the other things, left on the board, XHTML save 354 while (board.containerObj.firstChild) { 355 board.containerObj.removeChild(board.containerObj.firstChild); 356 } 357 // board.containerObj.innerHTML = ''; 358 359 // Tell the browser the objects aren't needed anymore 360 for(el in board.objects) { 361 delete(board.objects[el]); 362 } 363 364 // Free the renderer and the algebra object 365 delete(board.renderer); 366 delete(board.algebra); 367 368 // clear the creator cache 369 board.jc.creator.clearCache(); 370 delete(board.jc); 371 372 // Finally remove the board itself from the boards array 373 delete(this.boards[board.id]); 374 }, 375 376 /** 377 * This registers a new construction element to JSXGraph for the construction via the {@link JXG.Board.create} 378 * interface. 379 * @param {String} element The elements name. This is case-insensitive, existing elements with the same name 380 * will be overwritten. 381 * @param {Function} creator A reference to a function taking three parameters: First the board, the element is 382 * to be created on, a parent element array, and an attributes object. See {@link JXG.createPoint} or any other 383 * <tt>JXG.create...</tt> function for an example. 384 */ 385 registerElement: function (element, creator) { 386 element = element.toLowerCase(); 387 this.elements[element] = creator; 388 389 if(JXG.Board.prototype['_' + element]) 390 throw new Error("JSXGraph: Can't create wrapper method in JXG.Board because member '_" + element + "' already exists'"); 391 JXG.Board.prototype['_' + element] = function (parents, attributes) { 392 return this.create(element, parents, attributes); 393 }; 394 395 }, 396 397 /** 398 * The opposite of {@link JXG.JSXGraph.registerElement}, it removes a given element from 399 * the element list. You probably don't need this. 400 * @param {String} element The name of the element which is to be removed from the element list. 401 */ 402 unregisterElement: function (element) { 403 delete (this.elements[element.toLowerCase()]); 404 delete (JXG.Board.prototype['_' + element.toLowerCase()]); 405 } 406 }; 407