1 /* 2 Copyright 2008-2013 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, define: true, AMprocessNode: true, MathJax: true, document: true, window: true */ 34 35 /* 36 nomen: Allow underscores to indicate private class members. Might be replaced by local variables. 37 plusplus: Only allowed in for-loops 38 newcap: AsciiMathMl exposes non-constructor functions beginning with upper case letters 39 */ 40 /*jslint nomen: true, plusplus: true, newcap:true*/ 41 42 /* depends: 43 jxg 44 options 45 base/coords 46 base/constants 47 math/math 48 math/geometry 49 utils/type 50 utils/env 51 */ 52 53 /** 54 * @fileoverview JSXGraph can use various technologies to render the contents of a construction, e.g. 55 * SVG, VML, and HTML5 Canvas. To accomplish this, The rendering and the logic and control mechanisms 56 * are completely separated from each other. Every rendering technology has it's own class, called 57 * Renderer, e.g. SVGRenderer for SVG, the same for VML and Canvas. The common base for all available 58 * renderers is the class AbstractRenderer defined in this file. 59 */ 60 61 define([ 62 'jxg', 'options', 'base/coords', 'base/constants', 'math/math', 'math/geometry', 'utils/type', 'utils/env' 63 ], function (JXG, Options, Coords, Const, Mat, Geometry, Type, Env) { 64 65 "use strict"; 66 67 /** 68 * <p>This class defines the interface to the graphics part of JSXGraph. This class is an abstract class, it 69 * actually does not render anything. This is up to the {@link JXG.SVGRenderer}, {@link JXG.VMLRenderer}, 70 * and {@link JXG.CanvasRenderer} classes. We strongly discourage you from using the methods in these classes 71 * directly. Only the methods which are defined in this class and are not marked as private are guaranteed 72 * to exist in any renderer instance you can access via {@link JXG.Board#renderer}. But not all methods may 73 * work as expected.</p> 74 * <p>The methods of this renderer can be divided into different categories: 75 * <dl> 76 * <dt>Draw basic elements</dt> 77 * <dd>In this category we find methods to draw basic elements like {@link JXG.Point}, {@link JXG.Line}, 78 * and {@link JXG.Curve} as well as assisting methods tightly bound to these basic painters. You do not 79 * need to implement these methods in a descendant renderer but instead implement the primitive drawing 80 * methods described below. This approach is encouraged when you're using a XML based rendering engine 81 * like VML and SVG. If you want to use a bitmap based rendering technique you are supposed to override 82 * these methods instead of the primitive drawing methods.</dd> 83 * <dt>Draw primitives</dt> 84 * <dd>This category summarizes methods to handle primitive nodes. As creation and management of these nodes 85 * is different among different the rendering techniques most of these methods are purely virtual and need 86 * proper implementation if you choose to not overwrite the basic element drawing methods.</dd> 87 * <dt>Attribute manipulation</dt> 88 * <dd>In XML based renders you have to manipulate XML nodes and their attributes to change the graphics. 89 * For that purpose attribute manipulation methods are defined to set the color, opacity, and other things. 90 * Please note that some of these methods are required in bitmap based renderers, too, because some elements 91 * like {@link JXG.Text} can be HTML nodes floating over the construction.</dd> 92 * <dt>Renderer control</dt> 93 * <dd>Methods to clear the drawing board or to stop and to resume the rendering engine.</dd> 94 * </dl></p> 95 * @class JXG.AbstractRenderer 96 * @constructor 97 * @see JXG.SVGRenderer 98 * @see JXG.VMLRenderer 99 * @see JXG.CanvasRenderer 100 */ 101 JXG.AbstractRenderer = function () { 102 103 // WHY THIS IS A CLASS INSTEAD OF A SINGLETON OBJECT: 104 // 105 // The renderers need to keep track of some stuff which is not always the same on different boards, 106 // like enhancedRendering, reference to the container object, and resolution in VML. Sure, those 107 // things could be stored in board. But they are rendering related and JXG.Board is already very 108 // very big. 109 // 110 // And we can't save the rendering related data in {SVG,VML,Canvas}Renderer and make only the 111 // JXG.AbstractRenderer a singleton because of that: 112 // 113 // Given an object o with property a set to true 114 // var o = {a: true}; 115 // and a class c doing nothing 116 // c = function() {}; 117 // Set c's prototype to o 118 // c.prototype = o; 119 // and create an instance of c we get i.a to be true 120 // i = new c(); 121 // i.a; 122 // > true 123 // But we can overwrite this property via 124 // c.prototype.a = false; 125 // i.a; 126 // > false 127 128 /** 129 * The vertical offset for {@link Text} elements. Every {@link Text} element will 130 * be placed this amount of pixels below the user given coordinates. 131 * @type number 132 * @default 8 133 */ 134 this.vOffsetText = 0; 135 136 /** 137 * If this property is set to <tt>true</tt> the visual properties of the elements are updated 138 * on every update. Visual properties means: All the stuff stored in the 139 * {@link JXG.GeometryElement#visProp} property won't be set if enhancedRendering is <tt>false</tt> 140 * @type Boolean 141 * @default true 142 */ 143 this.enhancedRendering = true; 144 145 /** 146 * The HTML element that stores the JSXGraph board in it. 147 * @type Node 148 */ 149 this.container = null; 150 151 /** 152 * This is used to easily determine which renderer we are using 153 * @example if (board.renderer.type === 'vml') { 154 * // do something 155 * } 156 * @type String 157 */ 158 this.type = ''; 159 }; 160 161 JXG.extend(JXG.AbstractRenderer.prototype, /** @lends JXG.AbstractRenderer.prototype */ { 162 163 /* ******************************** * 164 * private methods * 165 * should not be called from * 166 * outside AbstractRenderer * 167 * ******************************** */ 168 169 /** 170 * Update visual properties, but only if {@link JXG.AbstractRenderer#enhancedRendering} or <tt>enhanced</tt> is set to true. 171 * @param {JXG.GeometryElement} element The element to update 172 * @param {Object} [not={}] Select properties you don't want to be updated: <tt>{fill: true, dash: true}</tt> updates 173 * everything except for fill and dash. Possible values are <tt>stroke, fill, dash, shadow, gradient</tt>. 174 * @param {Boolean} [enhanced=false] If true, {@link JXG.AbstractRenderer#enhancedRendering} is assumed to be true. 175 * @private 176 */ 177 _updateVisual: function (element, not, enhanced) { 178 var rgbo; 179 180 if (enhanced || this.enhancedRendering) { 181 not = not || {}; 182 183 if (!element.visProp.draft) { 184 if (!not.stroke) { 185 this.setObjectStrokeColor(element, element.visProp.strokecolor, element.visProp.strokeopacity); 186 this.setObjectStrokeWidth(element, element.visProp.strokewidth); 187 } 188 189 if (!not.fill) { 190 this.setObjectFillColor(element, element.visProp.fillcolor, element.visProp.fillopacity); 191 } 192 193 if (!not.dash) { 194 this.setDashStyle(element, element.visProp); 195 } 196 197 if (!not.shadow) { 198 this.setShadow(element); 199 } 200 201 if (!not.gradient) { 202 this.setShadow(element); 203 } 204 } else { 205 this.setDraft(element); 206 } 207 } 208 }, 209 210 211 /* ******************************** * 212 * Point drawing and updating * 213 * ******************************** */ 214 215 /** 216 * Draws a point on the {@link JXG.Board}. 217 * @param {JXG.Point} element Reference to a {@link JXG.Point} object that has to be drawn. 218 * @see Point 219 * @see JXG.Point 220 * @see JXG.AbstractRenderer#updatePoint 221 * @see JXG.AbstractRenderer#changePointStyle 222 */ 223 drawPoint: function (element) { 224 var prim, 225 // sometimes element is not a real point and lacks the methods of a JXG.Point instance, 226 // in these cases to not use element directly. 227 face = Options.normalizePointFace(element.visProp.face); 228 229 // determine how the point looks like 230 if (face === 'o') { 231 prim = 'ellipse'; 232 } else if (face === '[]') { 233 prim = 'rect'; 234 } else { 235 // cross/x, diamond/<>, triangleup/a/^, triangledown/v, triangleleft/<, 236 // triangleright/>, plus/+, 237 prim = 'path'; 238 } 239 240 element.rendNode = this.appendChildPrim(this.createPrim(prim, element.id), element.visProp.layer); 241 this.appendNodesToElement(element, prim); 242 243 // adjust visual propertys 244 this._updateVisual(element, {dash: true, shadow: true}, true); 245 246 247 // By now we only created the xml nodes and set some styles, in updatePoint 248 // the attributes are filled with data. 249 this.updatePoint(element); 250 }, 251 252 /** 253 * Updates visual appearance of the renderer element assigned to the given {@link JXG.Point}. 254 * @param {JXG.Point} element Reference to a {@link JXG.Point} object, that has to be updated. 255 * @see Point 256 * @see JXG.Point 257 * @see JXG.AbstractRenderer#drawPoint 258 * @see JXG.AbstractRenderer#changePointStyle 259 */ 260 updatePoint: function (element) { 261 var size = element.visProp.size, 262 // sometimes element is not a real point and lacks the methods of a JXG.Point instance, 263 // in these cases to not use element directly. 264 face = Options.normalizePointFace(element.visProp.face); 265 266 if (!isNaN(element.coords.scrCoords[2] + element.coords.scrCoords[1])) { 267 this._updateVisual(element, {dash: false, shadow: false}); 268 size *= ((!element.board || !element.board.options.point.zoom) ? 1.0 : Math.sqrt(element.board.zoomX * element.board.zoomY)); 269 270 if (face === 'o') { // circle 271 this.updateEllipsePrim(element.rendNode, element.coords.scrCoords[1], element.coords.scrCoords[2], size + 1, size + 1); 272 } else if (face === '[]') { // rectangle 273 this.updateRectPrim(element.rendNode, element.coords.scrCoords[1] - size, element.coords.scrCoords[2] - size, size * 2, size * 2); 274 } else { // x, +, <>, ^, v, <, > 275 this.updatePathPrim(element.rendNode, this.updatePathStringPoint(element, size, face), element.board); 276 } 277 this.setShadow(element); 278 } 279 }, 280 281 /** 282 * Changes the style of a {@link JXG.Point}. This is required because the point styles differ in what 283 * elements have to be drawn, e.g. if the point is marked by a "x" or a "+" two lines are drawn, if 284 * it's marked by spot a circle is drawn. This method removes the old renderer element(s) and creates 285 * the new one(s). 286 * @param {JXG.Point} element Reference to a {@link JXG.Point} object, that's style is changed. 287 * @see Point 288 * @see JXG.Point 289 * @see JXG.AbstractRenderer#updatePoint 290 * @see JXG.AbstractRenderer#drawPoint 291 */ 292 changePointStyle: function (element) { 293 var node = this.getElementById(element.id); 294 295 // remove the existing point rendering node 296 if (Type.exists(node)) { 297 this.remove(node); 298 } 299 300 // and make a new one 301 this.drawPoint(element); 302 Type.clearVisPropOld(element); 303 304 if (!element.visProp.visible) { 305 this.hide(element); 306 } 307 308 if (element.visProp.draft) { 309 this.setDraft(element); 310 } 311 }, 312 313 /* ******************************** * 314 * Lines * 315 * ******************************** */ 316 317 /** 318 * Draws a line on the {@link JXG.Board}. 319 * @param {JXG.Line} element Reference to a line object, that has to be drawn. 320 * @see Line 321 * @see JXG.Line 322 * @see JXG.AbstractRenderer#updateLine 323 */ 324 drawLine: function (element) { 325 element.rendNode = this.appendChildPrim(this.createPrim('line', element.id), element.visProp.layer); 326 this.appendNodesToElement(element, 'lines'); 327 this.updateLine(element); 328 }, 329 330 /** 331 * Updates visual appearance of the renderer element assigned to the given {@link JXG.Line}. 332 * @param {JXG.Line} element Reference to the {@link JXG.Line} object that has to be updated. 333 * @see Line 334 * @see JXG.Line 335 * @see JXG.AbstractRenderer#drawLine 336 */ 337 updateLine: function (element) { 338 var s, d, d1x, d1y, d2x, d2y, 339 c1 = new Coords(Const.COORDS_BY_USER, element.point1.coords.usrCoords, element.board), 340 c2 = new Coords(Const.COORDS_BY_USER, element.point2.coords.usrCoords, element.board), 341 minlen = 10, 342 margin = null; 343 344 if (element.visProp.firstarrow || element.visProp.lastarrow) { 345 margin = -4; 346 } 347 Geometry.calcStraight(element, c1, c2, margin); 348 349 d1x = d1y = d2x = d2y = 0.0; 350 /* 351 Handle arrow heads. 352 353 The arrow head is an equilateral triangle with base length 10 and height 10. 354 These 10 units are scaled to strokeWidth*3 pixels or minlen pixels. 355 */ 356 s = Math.max(parseInt(element.visProp.strokewidth, 10) * 3, minlen); 357 d = c1.distance(Const.COORDS_BY_SCREEN, c2); 358 if (element.visProp.lastarrow && element.board.renderer.type !== 'vml' && d >= minlen/*Mat.eps*/) { 359 d2x = (c2.scrCoords[1] - c1.scrCoords[1]) * s / d; 360 d2y = (c2.scrCoords[2] - c1.scrCoords[2]) * s / d; 361 } 362 if (element.visProp.firstarrow && element.board.renderer.type !== 'vml' && d >= minlen /* Mat.eps*/) { 363 d1x = (c2.scrCoords[1] - c1.scrCoords[1]) * s / d; 364 d1y = (c2.scrCoords[2] - c1.scrCoords[2]) * s / d; 365 } 366 367 this.updateLinePrim(element.rendNode, 368 c1.scrCoords[1] + d1x, c1.scrCoords[2] + d1y, 369 c2.scrCoords[1] - d2x, c2.scrCoords[2] - d2y, element.board); 370 371 this.makeArrows(element); 372 this._updateVisual(element); 373 }, 374 375 /** 376 * Creates a rendering node for ticks added to a line. 377 * @param {JXG.Line} element A arbitrary line. 378 * @see Line 379 * @see Ticks 380 * @see JXG.Line 381 * @see JXG.Ticks 382 * @see JXG.AbstractRenderer#updateTicks 383 */ 384 drawTicks: function (element) { 385 element.rendNode = this.appendChildPrim(this.createPrim('path', element.id), element.visProp.layer); 386 this.appendNodesToElement(element, 'path'); 387 }, 388 389 /** 390 * Update {@link Ticks} on a {@link JXG.Line}. This method is only a stub and has to be implemented 391 * in any descendant renderer class. 392 * @param {JXG.Ticks} element Reference of a ticks object that has to be updated. 393 * @see Line 394 * @see Ticks 395 * @see JXG.Line 396 * @see JXG.Ticks 397 * @see JXG.AbstractRenderer#drawTicks 398 */ 399 updateTicks: function (element) { /* stub */ }, 400 401 /* ************************** 402 * Curves 403 * **************************/ 404 405 /** 406 * Draws a {@link JXG.Curve} on the {@link JXG.Board}. 407 * @param {JXG.Curve} element Reference to a graph object, that has to be plotted. 408 * @see Curve 409 * @see JXG.Curve 410 * @see JXG.AbstractRenderer#updateCurve 411 */ 412 drawCurve: function (element) { 413 element.rendNode = this.appendChildPrim(this.createPrim('path', element.id), element.visProp.layer); 414 this.appendNodesToElement(element, 'path'); 415 this._updateVisual(element, {shadow: true}, true); 416 this.updateCurve(element); 417 }, 418 419 /** 420 * Updates visual appearance of the renderer element assigned to the given {@link JXG.Curve}. 421 * @param {JXG.Curve} element Reference to a {@link JXG.Curve} object, that has to be updated. 422 * @see Curve 423 * @see JXG.Curve 424 * @see JXG.AbstractRenderer#drawCurve 425 */ 426 updateCurve: function (element) { 427 this._updateVisual(element); 428 if (element.visProp.handdrawing) { 429 this.updatePathPrim(element.rendNode, this.updatePathStringBezierPrim(element), element.board); 430 } else { 431 this.updatePathPrim(element.rendNode, this.updatePathStringPrim(element), element.board); 432 } 433 if (element.numberPoints > 1) { 434 this.makeArrows(element); 435 } 436 }, 437 438 /* ************************** 439 * Circle related stuff 440 * **************************/ 441 442 /** 443 * Draws a {@link JXG.Circle} 444 * @param {JXG.Circle} element Reference to a {@link JXG.Circle} object that has to be drawn. 445 * @see Circle 446 * @see JXG.Circle 447 * @see JXG.AbstractRenderer#updateEllipse 448 */ 449 drawEllipse: function (element) { 450 element.rendNode = this.appendChildPrim(this.createPrim('ellipse', element.id), element.visProp.layer); 451 this.appendNodesToElement(element, 'ellipse'); 452 this.updateEllipse(element); 453 }, 454 455 /** 456 * Updates visual appearance of a given {@link JXG.Circle} on the {@link JXG.Board}. 457 * @param {JXG.Circle} element Reference to a {@link JXG.Circle} object, that has to be updated. 458 * @see Circle 459 * @see JXG.Circle 460 * @see JXG.AbstractRenderer#drawEllipse 461 */ 462 updateEllipse: function (element) { 463 this._updateVisual(element); 464 465 var radius = element.Radius(); 466 467 if (radius > 0.0 && 468 Math.abs(element.center.coords.usrCoords[0]) > Mat.eps && 469 !isNaN(radius + element.center.coords.scrCoords[1] + element.center.coords.scrCoords[2]) && 470 radius * element.board.unitX < 2000000) { 471 this.updateEllipsePrim(element.rendNode, element.center.coords.scrCoords[1], 472 element.center.coords.scrCoords[2], (radius * element.board.unitX), (radius * element.board.unitY)); 473 } 474 }, 475 476 477 /* ************************** 478 * Polygon related stuff 479 * **************************/ 480 481 /** 482 * Draws a {@link JXG.Polygon} on the {@link JXG.Board}. 483 * @param {JXG.Polygon} element Reference to a Polygon object, that is to be drawn. 484 * @see Polygon 485 * @see JXG.Polygon 486 * @see JXG.AbstractRenderer#updatePolygon 487 */ 488 drawPolygon: function (element) { 489 element.rendNode = this.appendChildPrim(this.createPrim('polygon', element.id), element.visProp.layer); 490 this.appendNodesToElement(element, 'polygon'); 491 this.updatePolygon(element); 492 }, 493 494 /** 495 * Updates properties of a {@link JXG.Polygon}'s rendering node. 496 * @param {JXG.Polygon} element Reference to a {@link JXG.Polygon} object, that has to be updated. 497 * @see Polygon 498 * @see JXG.Polygon 499 * @see JXG.AbstractRenderer#drawPolygon 500 */ 501 updatePolygon: function (element) { 502 // here originally strokecolor wasn't updated but strokewidth was 503 // but if there's no strokecolor i don't see why we should update strokewidth. 504 this._updateVisual(element, {stroke: true, dash: true}); 505 this.updatePolygonPrim(element.rendNode, element); 506 }, 507 508 /* ************************** 509 * Text related stuff 510 * **************************/ 511 512 /** 513 * Shows a small copyright notice in the top left corner of the board. 514 * @param {String} str The copyright notice itself 515 * @param {Number} fontsize Size of the font the copyright notice is written in 516 */ 517 displayCopyright: function (str, fontsize) { /* stub */ }, 518 519 /** 520 * An internal text is a {@link JXG.Text} element which is drawn using only 521 * the given renderer but no HTML. This method is only a stub, the drawing 522 * is done in the special renderers. 523 * @param {JXG.Text} element Reference to a {@link JXG.Text} object 524 * @see Text 525 * @see JXG.Text 526 * @see JXG.AbstractRenderer#updateInternalText 527 * @see JXG.AbstractRenderer#drawText 528 * @see JXG.AbstractRenderer#updateText 529 * @see JXG.AbstractRenderer#updateTextStyle 530 */ 531 drawInternalText: function (element) { /* stub */ }, 532 533 /** 534 * Updates visual properties of an already existing {@link JXG.Text} element. 535 * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be updated. 536 * @see Text 537 * @see JXG.Text 538 * @see JXG.AbstractRenderer#drawInternalText 539 * @see JXG.AbstractRenderer#drawText 540 * @see JXG.AbstractRenderer#updateText 541 * @see JXG.AbstractRenderer#updateTextStyle 542 */ 543 updateInternalText: function (element) { /* stub */ }, 544 545 /** 546 * Displays a {@link JXG.Text} on the {@link JXG.Board} by putting a HTML div over it. 547 * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be displayed 548 * @see Text 549 * @see JXG.Text 550 * @see JXG.AbstractRenderer#drawInternalText 551 * @see JXG.AbstractRenderer#updateText 552 * @see JXG.AbstractRenderer#updateInternalText 553 * @see JXG.AbstractRenderer#updateTextStyle 554 */ 555 drawText: function (element) { 556 var node, z; 557 558 if (element.visProp.display === 'html' && Env.isBrowser) { 559 node = this.container.ownerDocument.createElement('div'); 560 node.style.position = 'absolute'; 561 562 node.className = element.visProp.cssclass; 563 if (this.container.style.zIndex === '') { 564 z = 0; 565 } else { 566 z = parseInt(this.container.style.zIndex, 10); 567 } 568 569 node.style.zIndex = z + element.board.options.layer.text; 570 this.container.appendChild(node); 571 node.setAttribute('id', this.container.id + '_' + element.id); 572 } else { 573 node = this.drawInternalText(element); 574 } 575 576 element.rendNode = node; 577 element.htmlStr = ''; 578 this.updateText(element); 579 }, 580 581 /** 582 * Updates visual properties of an already existing {@link JXG.Text} element. 583 * @param {JXG.Text} el Reference to an {@link JXG.Text} object, that has to be updated. 584 * @see Text 585 * @see JXG.Text 586 * @see JXG.AbstractRenderer#drawText 587 * @see JXG.AbstractRenderer#drawInternalText 588 * @see JXG.AbstractRenderer#updateInternalText 589 * @see JXG.AbstractRenderer#updateTextStyle 590 */ 591 updateText: function (el) { 592 var content = el.plaintext, v, c; 593 594 if (el.visProp.visible) { 595 this.updateTextStyle(el, false); 596 597 if (el.visProp.display === 'html') { 598 // Set the position 599 if (!isNaN(el.coords.scrCoords[1] + el.coords.scrCoords[2])) { 600 601 // Horizontal 602 c = el.coords.scrCoords[1]; 603 // webkit seems to fail for extremely large values for c. 604 c = Math.abs(c) < 1000000 ? c : 1000000; 605 606 if (el.visProp.anchorx === 'right') { 607 v = Math.floor(el.board.canvasWidth - c); 608 } else if (el.visProp.anchorx === 'middle') { 609 v = Math.floor(c - 0.5 * el.size[0]); 610 } else { // 'left' 611 v = Math.floor(c); 612 } 613 614 if (el.visPropOld.left !== (el.visProp.anchorx + v)) { 615 if (el.visProp.anchorx === 'right') { 616 el.rendNode.style.right = v + 'px'; 617 el.rendNode.style.left = 'auto'; 618 } else { 619 el.rendNode.style.left = v + 'px'; 620 el.rendNode.style.right = 'auto'; 621 } 622 el.visPropOld.left = el.visProp.anchorx + v; 623 } 624 625 // Vertical 626 c = el.coords.scrCoords[2] + this.vOffsetText; 627 c = Math.abs(c) < 1000000 ? c : 1000000; 628 629 if (el.visProp.anchory === 'bottom') { 630 v = Math.floor(el.board.canvasHeight - c); 631 } else if (el.visProp.anchory === 'middle') { 632 v = Math.floor(c - 0.5 * el.size[1]); 633 } else { // top 634 v = Math.floor(c); 635 } 636 637 if (el.visPropOld.top !== (el.visProp.anchory + v)) { 638 if (el.visProp.anchory === 'bottom') { 639 el.rendNode.style.top = 'auto'; 640 el.rendNode.style.bottom = v + 'px'; 641 } else { 642 el.rendNode.style.bottom = 'auto'; 643 el.rendNode.style.top = v + 'px'; 644 } 645 el.visPropOld.top = el.visProp.anchory + v; 646 } 647 } 648 649 // Set the content 650 if (el.htmlStr !== content) { 651 el.rendNode.innerHTML = content; 652 el.htmlStr = content; 653 654 if (el.visProp.usemathjax) { 655 // typesetting directly might not work because mathjax was not loaded completely 656 // see http://www.mathjax.org/docs/1.1/typeset.html 657 MathJax.Hub.Queue(['Typeset', MathJax.Hub, el.rendNode]); 658 } else if (el.visProp.useasciimathml) { 659 // This is not a constructor. 660 // See http://www1.chapman.edu/~jipsen/mathml/asciimath.html for more information 661 // about AsciiMathML and the project's source code. 662 AMprocessNode(el.rendNode, false); 663 } 664 } 665 this.transformImage(el, el.transformations); 666 } else { 667 this.updateInternalText(el); 668 } 669 } 670 }, 671 672 /** 673 * Updates font-size, color and opacity propertiey and CSS style properties of a {@link JXG.Text} node. 674 * This function is also called by highlight() and nohighlight(). 675 * @param {JXG.Text} element Reference to the {@link JXG.Text} object, that has to be updated. 676 * @param {Boolean} doHighlight 677 * @see Text 678 * @see JXG.Text 679 * @see JXG.AbstractRenderer#drawText 680 * @see JXG.AbstractRenderer#drawInternalText 681 * @see JXG.AbstractRenderer#updateText 682 * @see JXG.AbstractRenderer#updateInternalText 683 * @see JXG.AbstractRenderer#updateInternalTextStyle 684 */ 685 updateTextStyle: function (element, doHighlight) { 686 var fs, so, sc, css, 687 ev = element.visProp, 688 display = Env.isBrowser ? ev.display : 'internal'; 689 690 if (doHighlight) { 691 sc = ev.highlightstrokecolor; 692 so = ev.highlightstrokeopacity; 693 css = ev.highlightcssclass; 694 } else { 695 sc = ev.strokecolor; 696 so = ev.strokeopacity; 697 css = ev.cssclass; 698 } 699 700 // This part is executed for all text elements except internal texts in canvas. 701 if (display === 'html' || (this.type !== 'canvas' && this.type !== 'no')) { 702 fs = Type.evaluate(element.visProp.fontsize); 703 if (element.visPropOld.fontsize !== fs) { 704 element.needsSizeUpdate = true; 705 try { 706 element.rendNode.style.fontSize = fs + 'px'; 707 } catch (e) { 708 // IE needs special treatment. 709 element.rendNode.style.fontSize = fs; 710 } 711 element.visPropOld.fontsize = fs; 712 } 713 714 } 715 716 if (display === 'html') { 717 if (element.visPropOld.cssclass !== css) { 718 element.rendNode.className = css; 719 element.visPropOld.cssclass = css; 720 element.needsSizeUpdate = true; 721 } 722 this.setObjectStrokeColor(element, sc, so); 723 } else { 724 this.updateInternalTextStyle(element, sc, so); 725 } 726 return this; 727 }, 728 729 /** 730 * Set color and opacity of internal texts. 731 * This method is used for Canvas and VML. 732 * SVG needs its own version. 733 * @private 734 * @see JXG.AbstractRenderer#updateTextStyle 735 * @see JXG.SVGRenderer#updateInternalTextStyle 736 */ 737 updateInternalTextStyle: function (element, strokeColor, strokeOpacity) { 738 this.setObjectStrokeColor(element, strokeColor, strokeOpacity); 739 }, 740 741 /* ************************** 742 * Image related stuff 743 * **************************/ 744 745 /** 746 * Draws an {@link JXG.Image} on a board; This is just a template that has to be implemented by special 747 * renderers. 748 * @param {JXG.Image} element Reference to the image object that is to be drawn 749 * @see Image 750 * @see JXG.Image 751 * @see JXG.AbstractRenderer#updateImage 752 */ 753 drawImage: function (element) { /* stub */ }, 754 755 /** 756 * Updates the properties of an {@link JXG.Image} element. 757 * @param {JXG.Image} element Reference to an {@link JXG.Image} object, that has to be updated. 758 * @see Image 759 * @see JXG.Image 760 * @see JXG.AbstractRenderer#drawImage 761 */ 762 updateImage: function (element) { 763 this.updateRectPrim(element.rendNode, element.coords.scrCoords[1], 764 element.coords.scrCoords[2] - element.size[1], element.size[0], element.size[1]); 765 766 this.updateImageURL(element); 767 this.transformImage(element, element.transformations); 768 this._updateVisual(element, {stroke: true, dash: true}, true); 769 }, 770 771 /** 772 * Multiplication of transformations without updating. That means, at that point it is expected that the 773 * matrices contain numbers only. First, the origin in user coords is translated to <tt>(0,0)</tt> in screen 774 * coords. Then, the stretch factors are divided out. After the transformations in user coords, the stretch 775 * factors are multiplied in again, and the origin in user coords is translated back to its position. This 776 * method does not have to be implemented in a new renderer. 777 * @param {JXG.GeometryElement} element A JSXGraph element. We only need its board property. 778 * @param {Array} transformations An array of JXG.Transformations. 779 * @returns {Array} A matrix represented by a two dimensional array of numbers. 780 * @see JXG.AbstractRenderer#transformImage 781 */ 782 joinTransforms: function (element, transformations) { 783 var i, 784 m = [[1, 0, 0], [0, 1, 0], [0, 0, 1]], 785 ox = element.board.origin.scrCoords[1], 786 oy = element.board.origin.scrCoords[2], 787 ux = element.board.unitX, 788 uy = element.board.unitY, 789 // Translate to 0,0 in screen coords 790 mpre1 = [[1, 0, 0], 791 [-ox, 1, 0], 792 [-oy, 0, 1]], 793 // Scale 794 mpre2 = [[1, 0, 0], 795 [0, 1 / ux, 0], 796 [0, 0, -1 / uy]], 797 // Scale back 798 mpost2 = [[1, 0, 0], 799 [0, ux, 0], 800 [0, 0, -uy]], 801 // Translate back 802 mpost1 = [[1, 0, 0], 803 [ox, 1, 0], 804 [oy, 0, 1]], 805 len = transformations.length; 806 807 for (i = 0; i < len; i++) { 808 m = Mat.matMatMult(mpre1, m); 809 m = Mat.matMatMult(mpre2, m); 810 m = Mat.matMatMult(transformations[i].matrix, m); 811 m = Mat.matMatMult(mpost2, m); 812 m = Mat.matMatMult(mpost1, m); 813 } 814 return m; 815 }, 816 817 /** 818 * Applies transformations on images and text elements. This method is just a stub and has to be implemented in 819 * all descendant classes where text and image transformations are to be supported. 820 * @param {JXG.Image|JXG.Text} element A {@link JXG.Image} or {@link JXG.Text} object. 821 * @param {Array} transformations An array of {@link JXG.Transformation} objects. This is usually the 822 * transformations property of the given element <tt>el</tt>. 823 */ 824 transformImage: function (element, transformations) { /* stub */ }, 825 826 /** 827 * If the URL of the image is provided by a function the URL has to be updated during updateImage() 828 * @param {JXG.Image} element Reference to an image object. 829 * @see JXG.AbstractRenderer#updateImage 830 */ 831 updateImageURL: function (element) { /* stub */ }, 832 833 /** 834 * Updates CSS style properties of a {@link JXG.Image} node. 835 * In SVGRenderer opacity is the only available style element. 836 * This function is called by highlight() and nohighlight(). 837 * This function works for VML. 838 * It does not work for Canvas. 839 * SVGRenderer overwrites this method. 840 * @param {JXG.Text} el Reference to the {@link JXG.Image} object, that has to be updated. 841 * @param {Boolean} doHighlight 842 * @see Image 843 * @see JXG.Image 844 * @see JXG.AbstractRenderer#highlight 845 * @see JXG.AbstractRenderer#noHighlight 846 */ 847 updateImageStyle: function (el, doHighlight) { 848 el.rendNode.className = doHighlight ? el.visProp.highlightcssclass : el.visProp.cssclass; 849 }, 850 851 852 /* ************************** 853 * Render primitive objects 854 * **************************/ 855 856 /** 857 * Appends a node to a specific layer level. This is just an abstract method and has to be implemented 858 * in all renderers that want to use the <tt>createPrim</tt> model to draw. 859 * @param {Node} node A DOM tree node. 860 * @param {Number} level The layer the node is attached to. This is the index of the layer in 861 * {@link JXG.SVGRenderer#layer} or the <tt>z-index</tt> style property of the node in VMLRenderer. 862 */ 863 appendChildPrim: function (node, level) { /* stub */ }, 864 865 /** 866 * Stores the rendering nodes. This is an abstract method which has to be implemented in all renderers that use 867 * the <tt>createPrim</tt> method. 868 * @param {JXG.GeometryElement} element A JSXGraph element. 869 * @param {String} type The XML node name. Only used in VMLRenderer. 870 */ 871 appendNodesToElement: function (element, type) { /* stub */ }, 872 873 /** 874 * Creates a node of a given type with a given id. 875 * @param {String} type The type of the node to create. 876 * @param {String} id Set the id attribute to this. 877 * @returns {Node} Reference to the created node. 878 */ 879 createPrim: function (type, id) { 880 /* stub */ 881 return null; 882 }, 883 884 /** 885 * Removes an element node. Just a stub. 886 * @param {Node} node The node to remove. 887 */ 888 remove: function (node) { /* stub */ }, 889 890 /** 891 * Can be used to create the nodes to display arrows. This is an abstract method which has to be implemented 892 * in any descendant renderer. 893 * @param {JXG.GeometryElement} element The element the arrows are to be attached to. 894 */ 895 makeArrows: function (element) { /* stub */ }, 896 897 /** 898 * Updates an ellipse node primitive. This is an abstract method which has to be implemented in all renderers 899 * that use the <tt>createPrim</tt> method. 900 * @param {Node} node Reference to the node. 901 * @param {Number} x Centre X coordinate 902 * @param {Number} y Centre Y coordinate 903 * @param {Number} rx The x-axis radius. 904 * @param {Number} ry The y-axis radius. 905 */ 906 updateEllipsePrim: function (node, x, y, rx, ry) { /* stub */ }, 907 908 /** 909 * Refreshes a line node. This is an abstract method which has to be implemented in all renderers that use 910 * the <tt>createPrim</tt> method. 911 * @param {Node} node The node to be refreshed. 912 * @param {Number} p1x The first point's x coordinate. 913 * @param {Number} p1y The first point's y coordinate. 914 * @param {Number} p2x The second point's x coordinate. 915 * @param {Number} p2y The second point's y coordinate. 916 * @param {JXG.Board} board 917 */ 918 updateLinePrim: function (node, p1x, p1y, p2x, p2y, board) { /* stub */ }, 919 920 /** 921 * Updates a path element. This is an abstract method which has to be implemented in all renderers that use 922 * the <tt>createPrim</tt> method. 923 * @param {Node} node The path node. 924 * @param {String} pathString A string formatted like e.g. <em>'M 1,2 L 3,1 L5,5'</em>. The format of the string 925 * depends on the rendering engine. 926 * @param {JXG.Board} board Reference to the element's board. 927 */ 928 updatePathPrim: function (node, pathString, board) { /* stub */ }, 929 930 /** 931 * Builds a path data string to draw a point with a face other than <em>rect</em> and <em>circle</em>. Since 932 * the format of such a string usually depends on the renderer this method 933 * is only an abstract method. Therefore, it has to be implemented in the descendant renderer itself unless 934 * the renderer does not use the createPrim interface but the draw* interfaces to paint. 935 * @param {JXG.Point} element The point element 936 * @param {Number} size A positive number describing the size. Usually the half of the width and height of 937 * the drawn point. 938 * @param {String} type A string describing the point's face. This method only accepts the shortcut version of 939 * each possible face: <tt>x, +, <>, ^, v, >, < 940 */ 941 updatePathStringPoint: function (element, size, type) { /* stub */ }, 942 943 /** 944 * Builds a path data string from a {@link JXG.Curve} element. Since the path data strings heavily depend on the 945 * underlying rendering technique this method is just a stub. Although such a path string is of no use for the 946 * CanvasRenderer, this method is used there to draw a path directly. 947 * @param element 948 */ 949 updatePathStringPrim: function (element) { /* stub */ }, 950 951 /** 952 * Builds a path data string from a {@link JXG.Curve} element such that the curve looks like hand drawn. Since 953 * the path data strings heavily depend on the underlying rendering technique this method is just a stub. 954 * Although such a path string is of no use for the CanvasRenderer, this method is used there to draw a path 955 * directly. 956 * @param element 957 */ 958 updatePathStringBezierPrim: function (element) { /* stub */ }, 959 960 961 /** 962 * Update a polygon primitive. 963 * @param {Node} node 964 * @param {JXG.Polygon} element A JSXGraph element of type {@link JXG.Polygon} 965 */ 966 updatePolygonPrim: function (node, element) { /* stub */ }, 967 968 /** 969 * Update a rectangle primitive. This is used only for points with face of type 'rect'. 970 * @param {Node} node The node yearning to be updated. 971 * @param {Number} x x coordinate of the top left vertex. 972 * @param {Number} y y coordinate of the top left vertex. 973 * @param {Number} w Width of the rectangle. 974 * @param {Number} h The rectangle's height. 975 */ 976 updateRectPrim: function (node, x, y, w, h) { /* stub */ }, 977 978 /* ************************** 979 * Set Attributes 980 * **************************/ 981 982 /** 983 * Sets a node's attribute. 984 * @param {Node} node The node that is to be updated. 985 * @param {String} key Name of the attribute. 986 * @param {String} val New value for the attribute. 987 */ 988 setPropertyPrim: function (node, key, val) { /* stub */ }, 989 990 /** 991 * Shows a hidden element on the canvas; Only a stub, requires implementation in the derived renderer. 992 * @param {JXG.GeometryElement} element Reference to the object that has to appear. 993 * @see JXG.AbstractRenderer#hide 994 */ 995 show: function (element) { /* stub */ }, 996 997 /** 998 * Hides an element on the canvas; Only a stub, requires implementation in the derived renderer. 999 * @param {JXG.GeometryElement} element Reference to the geometry element that has to disappear. 1000 * @see JXG.AbstractRenderer#show 1001 */ 1002 hide: function (element) { /* stub */ }, 1003 1004 /** 1005 * Sets the buffering as recommended by SVGWG. Until now only Opera supports this and will be ignored by other 1006 * browsers. Although this feature is only supported by SVG we have this method in {@link JXG.AbstractRenderer} 1007 * because it is called from outside the renderer. 1008 * @param {Node} node The SVG DOM Node which buffering type to update. 1009 * @param {String} type Either 'auto', 'dynamic', or 'static'. For an explanation see 1010 * {@link http://www.w3.org/TR/SVGTiny12/painting.html#BufferedRenderingProperty}. 1011 */ 1012 setBuffering: function (node, type) { /* stub */ }, 1013 1014 /** 1015 * Sets an element's dash style. 1016 * @param {JXG.GeometryElement} element An JSXGraph element. 1017 */ 1018 setDashStyle: function (element) { /* stub */ }, 1019 1020 /** 1021 * Puts an object into draft mode, i.e. it's visual appearance will be changed. For GEONE<sub>x</sub>T backwards 1022 * compatibility. 1023 * @param {JXG.GeometryElement} element Reference of the object that is in draft mode. 1024 */ 1025 setDraft: function (element) { 1026 if (!element.visProp.draft) { 1027 return; 1028 } 1029 var draftColor = element.board.options.elements.draft.color, 1030 draftOpacity = element.board.options.elements.draft.opacity; 1031 1032 if (element.type === Const.OBJECT_TYPE_POLYGON) { 1033 this.setObjectFillColor(element, draftColor, draftOpacity); 1034 } else { 1035 if (element.elementClass === Const.OBJECT_CLASS_POINT) { 1036 this.setObjectFillColor(element, draftColor, draftOpacity); 1037 } else { 1038 this.setObjectFillColor(element, 'none', 0); 1039 } 1040 this.setObjectStrokeColor(element, draftColor, draftOpacity); 1041 this.setObjectStrokeWidth(element, element.board.options.elements.draft.strokeWidth); 1042 } 1043 }, 1044 1045 /** 1046 * Puts an object from draft mode back into normal mode. 1047 * @param {JXG.GeometryElement} element Reference of the object that no longer is in draft mode. 1048 */ 1049 removeDraft: function (element) { 1050 if (element.type === Const.OBJECT_TYPE_POLYGON) { 1051 this.setObjectFillColor(element, element.visProp.fillcolor, element.visProp.fillopacity); 1052 } else { 1053 if (element.type === Const.OBJECT_CLASS_POINT) { 1054 this.setObjectFillColor(element, element.visProp.fillcolor, element.visProp.fillopacity); 1055 } 1056 this.setObjectStrokeColor(element, element.visProp.strokecolor, element.visProp.strokeopacity); 1057 this.setObjectStrokeWidth(element, element.visProp.strokewidth); 1058 } 1059 }, 1060 1061 /** 1062 * Sets up nodes for rendering a gradient fill. 1063 * @param element 1064 */ 1065 setGradient: function (element) { /* stub */ }, 1066 1067 /** 1068 * Updates the gradient fill. 1069 * @param {JXG.GeometryElement} element An JSXGraph element with an area that can be filled. 1070 */ 1071 updateGradient: function (element) { /* stub */ }, 1072 1073 /** 1074 * Sets an objects fill color. 1075 * @param {JXG.GeometryElement} element Reference of the object that wants a new fill color. 1076 * @param {String} color Color in a HTML/CSS compatible format. If you don't want any fill color at all, choose 1077 * 'none'. 1078 * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1. 1079 */ 1080 setObjectFillColor: function (element, color, opacity) { /* stub */ }, 1081 1082 /** 1083 * Changes an objects stroke color to the given color. 1084 * @param {JXG.GeometryElement} element Reference of the {@link JXG.GeometryElement} that gets a new stroke 1085 * color. 1086 * @param {String} color Color value in a HTML compatible format, e.g. <strong>#00ff00</strong> or 1087 * <strong>green</strong> for green. 1088 * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1. 1089 */ 1090 setObjectStrokeColor: function (element, color, opacity) { /* stub */ }, 1091 1092 /** 1093 * Sets an element's stroke width. 1094 * @param {JXG.GeometryElement} element Reference to the geometry element. 1095 * @param {Number} width The new stroke width to be assigned to the element. 1096 */ 1097 setObjectStrokeWidth: function (element, width) { /* stub */ }, 1098 1099 /** 1100 * Sets the shadow properties to a geometry element. This method is only a stub, it is implemented in the actual 1101 * renderers. 1102 * @param {JXG.GeometryElement} element Reference to a geometry object, that should get a shadow 1103 */ 1104 setShadow: function (element) { /* stub */ }, 1105 1106 /** 1107 * Highlights an object, i.e. changes the current colors of the object to its highlighting colors 1108 * @param {JXG.GeometryElement} element Reference of the object that will be highlighted. 1109 * @returns {JXG.AbstractRenderer} Reference to the renderer 1110 * @see JXG.AbstractRenderer#updateTextStyle 1111 */ 1112 highlight: function (element) { 1113 var i, ev = element.visProp; 1114 1115 if (!ev.draft) { 1116 if (element.type === Const.OBJECT_TYPE_POLYGON) { 1117 this.setObjectFillColor(element, ev.highlightfillcolor, ev.highlightfillopacity); 1118 for (i = 0; i < element.borders.length; i++) { 1119 this.setObjectStrokeColor(element.borders[i], element.borders[i].visProp.highlightstrokecolor, 1120 element.borders[i].visProp.highlightstrokeopacity); 1121 } 1122 } else { 1123 if (element.type === Const.OBJECT_TYPE_TEXT) { 1124 this.updateTextStyle(element, true); 1125 } else if (element.type === Const.OBJECT_TYPE_IMAGE) { 1126 this.updateImageStyle(element, true); 1127 } else { 1128 this.setObjectStrokeColor(element, ev.highlightstrokecolor, ev.highlightstrokeopacity); 1129 this.setObjectFillColor(element, ev.highlightfillcolor, ev.highlightfillopacity); 1130 } 1131 } 1132 if (ev.highlightstrokewidth) { 1133 this.setObjectStrokeWidth(element, Math.max(ev.highlightstrokewidth, ev.strokewidth)); 1134 } 1135 } 1136 1137 return this; 1138 }, 1139 1140 /** 1141 * Uses the normal colors of an object, i.e. the opposite of {@link JXG.AbstractRenderer#highlight}. 1142 * @param {JXG.GeometryElement} element Reference of the object that will get its normal colors. 1143 * @returns {JXG.AbstractRenderer} Reference to the renderer 1144 * @see JXG.AbstractRenderer#updateTextStyle 1145 */ 1146 noHighlight: function (element) { 1147 var i, ev = element.visProp; 1148 1149 if (!element.visProp.draft) { 1150 if (element.type === Const.OBJECT_TYPE_POLYGON) { 1151 this.setObjectFillColor(element, ev.fillcolor, ev.fillopacity); 1152 for (i = 0; i < element.borders.length; i++) { 1153 this.setObjectStrokeColor(element.borders[i], element.borders[i].visProp.strokecolor, 1154 element.borders[i].visProp.strokeopacity); 1155 } 1156 } else { 1157 if (element.type === Const.OBJECT_TYPE_TEXT) { 1158 this.updateTextStyle(element, false); 1159 } else if (element.type === Const.OBJECT_TYPE_IMAGE) { 1160 this.updateImageStyle(element, false); 1161 } else { 1162 this.setObjectStrokeColor(element, ev.strokecolor, ev.strokeopacity); 1163 this.setObjectFillColor(element, ev.fillcolor, ev.fillopacity); 1164 } 1165 } 1166 this.setObjectStrokeWidth(element, ev.strokewidth); 1167 } 1168 1169 return this; 1170 }, 1171 1172 /* ************************** 1173 * renderer control 1174 * **************************/ 1175 1176 /** 1177 * Stop redraw. This method is called before every update, so a non-vector-graphics based renderer can use this 1178 * method to delete the contents of the drawing panel. This is an abstract method every descendant renderer 1179 * should implement, if appropriate. 1180 * @see JXG.AbstractRenderer#unsuspendRedraw 1181 */ 1182 suspendRedraw: function () { /* stub */ }, 1183 1184 /** 1185 * Restart redraw. This method is called after updating all the rendering node attributes. 1186 * @see JXG.AbstractRenderer#suspendRedraw 1187 */ 1188 unsuspendRedraw: function () { /* stub */ }, 1189 1190 /** 1191 * The tiny zoom bar shown on the bottom of a board (if showNavigation on board creation is true). 1192 * @param {JXG.Board} board Reference to a JSXGraph board. 1193 */ 1194 drawZoomBar: function (board) { 1195 var doc, 1196 node, 1197 cancelbubble = function (e) { 1198 if (!e) { 1199 e = window.event; 1200 } 1201 1202 if (e.stopPropagation) { 1203 // Non IE<=8 1204 e.stopPropagation(); 1205 } else { 1206 e.cancelBubble = true; 1207 } 1208 }, 1209 createButton = function (label, handler) { 1210 var button; 1211 1212 button = doc.createElement('span'); 1213 node.appendChild(button); 1214 button.appendChild(board.containerObj.ownerDocument.createTextNode(label)); 1215 Env.addEvent(button, 'mouseover', function () { 1216 this.style.backgroundColor = board.options.navbar.highlightFillColor; 1217 }, button); 1218 Env.addEvent(button, 'mouseover', function () { 1219 this.style.backgroundColor = board.options.navbar.highlightFillColor; 1220 }, button); 1221 Env.addEvent(button, 'mouseout', function () { 1222 this.style.backgroundColor = board.options.navbar.fillColor; 1223 }, button); 1224 1225 Env.addEvent(button, 'click', handler, board); 1226 // prevent the click from bubbling down to the board 1227 Env.addEvent(button, 'mouseup', cancelbubble, board); 1228 Env.addEvent(button, 'mousedown', cancelbubble, board); 1229 Env.addEvent(button, 'touchend', cancelbubble, board); 1230 Env.addEvent(button, 'touchstart', cancelbubble, board); 1231 }; 1232 1233 if (Env.isBrowser) { 1234 doc = board.containerObj.ownerDocument; 1235 node = doc.createElement('div'); 1236 1237 node.setAttribute('id', board.containerObj.id + '_navigationbar'); 1238 1239 node.style.color = board.options.navbar.strokeColor; 1240 node.style.backgroundColor = board.options.navbar.fillColor; 1241 node.style.padding = board.options.navbar.padding; 1242 node.style.position = board.options.navbar.position; 1243 node.style.fontSize = board.options.navbar.fontSize; 1244 node.style.cursor = board.options.navbar.cursor; 1245 node.style.zIndex = board.options.navbar.zIndex; 1246 board.containerObj.appendChild(node); 1247 node.style.right = board.options.navbar.right; 1248 node.style.bottom = board.options.navbar.bottom; 1249 1250 // For XHTML we need unicode instead of HTML entities 1251 1252 if (board.attr.showreload) { 1253 // full reload circle: \u27F2 1254 // the board.reload() method does not exist during the creation 1255 // of this button. That's why this anonymous function wrapper is required. 1256 createButton('\u00A0\u21BB\u00A0', function () { 1257 board.reload(); 1258 }); 1259 } 1260 1261 if (board.attr.showcleartraces) { 1262 // clear traces symbol (otimes): \u27F2 1263 createButton('\u00A0\u2297\u00A0', function () { 1264 board.clearTraces(); 1265 }); 1266 } 1267 1268 if (board.attr.shownavigation) { 1269 createButton('\u00A0\u2013\u00A0', board.zoomOut); 1270 createButton('\u00A0o\u00A0', board.zoom100); 1271 createButton('\u00A0+\u00A0', board.zoomIn); 1272 createButton('\u00A0\u2190\u00A0', board.clickLeftArrow); 1273 createButton('\u00A0\u2193\u00A0', board.clickUpArrow); 1274 createButton('\u00A0\u2191\u00A0', board.clickDownArrow); 1275 createButton('\u00A0\u2192\u00A0', board.clickRightArrow); 1276 } 1277 } 1278 }, 1279 1280 /** 1281 * Wrapper for getElementById for maybe other renderers which elements are not directly accessible by DOM 1282 * methods like document.getElementById(). 1283 * @param {String} id Unique identifier for element. 1284 * @returns {Object} Reference to a JavaScript object. In case of SVG/VMLRenderer it's a reference to a SVG/VML 1285 * node. 1286 */ 1287 getElementById: function (id) { 1288 return this.container.ownerDocument.getElementById(this.container.id + '_' + id); 1289 }, 1290 1291 /** 1292 * Remove an element and provide a function that inserts it into its original position. This method 1293 * is taken from this article {@link https://developers.google.com/speed/articles/javascript-dom}. 1294 * @author KeeKim Heng, Google Web Developer 1295 * @param {Element} element The element to be temporarily removed 1296 * @returns {Function} A function that inserts the element into its original position 1297 */ 1298 removeToInsertLater: function (element) { 1299 var parentNode = element.parentNode, 1300 nextSibling = element.nextSibling; 1301 1302 parentNode.removeChild(element); 1303 1304 return function () { 1305 if (nextSibling) { 1306 parentNode.insertBefore(element, nextSibling); 1307 } else { 1308 parentNode.appendChild(element); 1309 } 1310 }; 1311 }, 1312 1313 /** 1314 * Resizes the rendering element 1315 * @param {Number} w New width 1316 * @param {Number} h New height 1317 */ 1318 resize: function (w, h) { /* stub */}, 1319 1320 /** 1321 * Create crosshair elements (Fadenkreuz) for presentations. 1322 * @param {Number} n Number of crosshairs. 1323 */ 1324 createTouchpoints: function (n) {}, 1325 1326 /** 1327 * Show a specific crosshair. 1328 * @param {Number} i Number of the crosshair to show 1329 */ 1330 showTouchpoint: function (i) {}, 1331 1332 /** 1333 * Hide a specific crosshair. 1334 * @param {Number} i Number of the crosshair to show 1335 */ 1336 hideTouchpoint: function (i) {}, 1337 1338 /** 1339 * Move a specific crosshair. 1340 * @param {Number} i Number of the crosshair to show 1341 * @param {Array} pos New positon in screen coordinates 1342 */ 1343 updateTouchpoint: function (i, pos) {} 1344 }); 1345 1346 return JXG.AbstractRenderer; 1347 }); 1348