1 /* 2 Copyright 2008-2015 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, s1, s2, 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 if (element.visProp.lastarrow || element.visProp.firstarrow) { 357 358 s1 = element.point1.visProp.size; 359 s2 = element.point2.visProp.size; 360 s = s1 + s2; 361 if (element.visProp.lastarrow && element.visProp.touchlastpoint) { 362 d = c1.distance(Const.COORDS_BY_SCREEN, c2); 363 if (d > s) { 364 d2x = (c2.scrCoords[1] - c1.scrCoords[1]) * s2 / d; 365 d2y = (c2.scrCoords[2] - c1.scrCoords[2]) * s2 / d; 366 c2 = new Coords(Const.COORDS_BY_SCREEN, [c2.scrCoords[1] - d2x, c2.scrCoords[2] - d2y], element.board); 367 } 368 } 369 if (element.visProp.firstarrow && element.visProp.touchfirstpoint) { 370 d = c1.distance(Const.COORDS_BY_SCREEN, c2); 371 if (d > s) { 372 d1x = (c2.scrCoords[1] - c1.scrCoords[1]) * s1 / d; 373 d1y = (c2.scrCoords[2] - c1.scrCoords[2]) * s1 / d; 374 c1 = new Coords(Const.COORDS_BY_SCREEN, [c1.scrCoords[1] + d1x, c1.scrCoords[2] + d1y], element.board); 375 } 376 } 377 378 s = Math.max(parseInt(element.visProp.strokewidth, 10) * 3, minlen); 379 d = c1.distance(Const.COORDS_BY_SCREEN, c2); 380 if (element.visProp.lastarrow && element.board.renderer.type !== 'vml' && d >= minlen) { 381 d2x = (c2.scrCoords[1] - c1.scrCoords[1]) * s / d; 382 d2y = (c2.scrCoords[2] - c1.scrCoords[2]) * s / d; 383 } 384 if (element.visProp.firstarrow && element.board.renderer.type !== 'vml' && d >= minlen) { 385 d1x = (c2.scrCoords[1] - c1.scrCoords[1]) * s / d; 386 d1y = (c2.scrCoords[2] - c1.scrCoords[2]) * s / d; 387 } 388 } 389 390 this.updateLinePrim(element.rendNode, 391 c1.scrCoords[1] + d1x, c1.scrCoords[2] + d1y, 392 c2.scrCoords[1] - d2x, c2.scrCoords[2] - d2y, element.board); 393 394 this.makeArrows(element); 395 this._updateVisual(element); 396 }, 397 398 /** 399 * Creates a rendering node for ticks added to a line. 400 * @param {JXG.Line} element A arbitrary line. 401 * @see Line 402 * @see Ticks 403 * @see JXG.Line 404 * @see JXG.Ticks 405 * @see JXG.AbstractRenderer#updateTicks 406 */ 407 drawTicks: function (element) { 408 element.rendNode = this.appendChildPrim(this.createPrim('path', element.id), element.visProp.layer); 409 this.appendNodesToElement(element, 'path'); 410 }, 411 412 /** 413 * Update {@link Ticks} on a {@link JXG.Line}. This method is only a stub and has to be implemented 414 * in any descendant renderer class. 415 * @param {JXG.Ticks} element Reference of a ticks object that has to be updated. 416 * @see Line 417 * @see Ticks 418 * @see JXG.Line 419 * @see JXG.Ticks 420 * @see JXG.AbstractRenderer#drawTicks 421 */ 422 updateTicks: function (element) { /* stub */ }, 423 424 /* ************************** 425 * Curves 426 * **************************/ 427 428 /** 429 * Draws a {@link JXG.Curve} on the {@link JXG.Board}. 430 * @param {JXG.Curve} element Reference to a graph object, that has to be plotted. 431 * @see Curve 432 * @see JXG.Curve 433 * @see JXG.AbstractRenderer#updateCurve 434 */ 435 drawCurve: function (element) { 436 element.rendNode = this.appendChildPrim(this.createPrim('path', element.id), element.visProp.layer); 437 this.appendNodesToElement(element, 'path'); 438 this._updateVisual(element, {shadow: true}, true); 439 this.updateCurve(element); 440 }, 441 442 /** 443 * Updates visual appearance of the renderer element assigned to the given {@link JXG.Curve}. 444 * @param {JXG.Curve} element Reference to a {@link JXG.Curve} object, that has to be updated. 445 * @see Curve 446 * @see JXG.Curve 447 * @see JXG.AbstractRenderer#drawCurve 448 */ 449 updateCurve: function (element) { 450 this._updateVisual(element); 451 if (element.visProp.handdrawing) { 452 this.updatePathPrim(element.rendNode, this.updatePathStringBezierPrim(element), element.board); 453 } else { 454 this.updatePathPrim(element.rendNode, this.updatePathStringPrim(element), element.board); 455 } 456 if (element.numberPoints > 1) { 457 this.makeArrows(element); 458 } 459 }, 460 461 /* ************************** 462 * Circle related stuff 463 * **************************/ 464 465 /** 466 * Draws a {@link JXG.Circle} 467 * @param {JXG.Circle} element Reference to a {@link JXG.Circle} object that has to be drawn. 468 * @see Circle 469 * @see JXG.Circle 470 * @see JXG.AbstractRenderer#updateEllipse 471 */ 472 drawEllipse: function (element) { 473 element.rendNode = this.appendChildPrim(this.createPrim('ellipse', element.id), element.visProp.layer); 474 this.appendNodesToElement(element, 'ellipse'); 475 this.updateEllipse(element); 476 }, 477 478 /** 479 * Updates visual appearance of a given {@link JXG.Circle} on the {@link JXG.Board}. 480 * @param {JXG.Circle} element Reference to a {@link JXG.Circle} object, that has to be updated. 481 * @see Circle 482 * @see JXG.Circle 483 * @see JXG.AbstractRenderer#drawEllipse 484 */ 485 updateEllipse: function (element) { 486 this._updateVisual(element); 487 488 var radius = element.Radius(); 489 490 if (radius > 0.0 && 491 Math.abs(element.center.coords.usrCoords[0]) > Mat.eps && 492 !isNaN(radius + element.center.coords.scrCoords[1] + element.center.coords.scrCoords[2]) && 493 radius * element.board.unitX < 2000000) { 494 this.updateEllipsePrim(element.rendNode, element.center.coords.scrCoords[1], 495 element.center.coords.scrCoords[2], (radius * element.board.unitX), (radius * element.board.unitY)); 496 } 497 }, 498 499 500 /* ************************** 501 * Polygon related stuff 502 * **************************/ 503 504 /** 505 * Draws a {@link JXG.Polygon} on the {@link JXG.Board}. 506 * @param {JXG.Polygon} element Reference to a Polygon object, that is to be drawn. 507 * @see Polygon 508 * @see JXG.Polygon 509 * @see JXG.AbstractRenderer#updatePolygon 510 */ 511 drawPolygon: function (element) { 512 element.rendNode = this.appendChildPrim(this.createPrim('polygon', element.id), element.visProp.layer); 513 this.appendNodesToElement(element, 'polygon'); 514 this.updatePolygon(element); 515 }, 516 517 /** 518 * Updates properties of a {@link JXG.Polygon}'s rendering node. 519 * @param {JXG.Polygon} element Reference to a {@link JXG.Polygon} object, that has to be updated. 520 * @see Polygon 521 * @see JXG.Polygon 522 * @see JXG.AbstractRenderer#drawPolygon 523 */ 524 updatePolygon: function (element) { 525 var i, len, polIsReal; 526 527 // here originally strokecolor wasn't updated but strokewidth was 528 // but if there's no strokecolor i don't see why we should update strokewidth. 529 this._updateVisual(element, {stroke: true, dash: true}); 530 this.updatePolygonPrim(element.rendNode, element); 531 532 len = element.vertices.length; 533 polIsReal = true; 534 for (i = 0; i < len; ++i) { 535 if (!element.vertices[i].isReal) { 536 polIsReal = false; 537 break; 538 } 539 } 540 541 len = element.borders.length; 542 for (i = 0; i < len; ++i) { 543 if (polIsReal && element.borders[i].visProp.visible) { 544 this.show(element.borders[i]); 545 } else { 546 this.hide(element.borders[i]); 547 } 548 } 549 }, 550 551 /* ************************** 552 * Text related stuff 553 * **************************/ 554 555 /** 556 * Shows a small copyright notice in the top left corner of the board. 557 * @param {String} str The copyright notice itself 558 * @param {Number} fontsize Size of the font the copyright notice is written in 559 */ 560 displayCopyright: function (str, fontsize) { /* stub */ }, 561 562 /** 563 * An internal text is a {@link JXG.Text} element which is drawn using only 564 * the given renderer but no HTML. This method is only a stub, the drawing 565 * is done in the special renderers. 566 * @param {JXG.Text} element Reference to a {@link JXG.Text} object 567 * @see Text 568 * @see JXG.Text 569 * @see JXG.AbstractRenderer#updateInternalText 570 * @see JXG.AbstractRenderer#drawText 571 * @see JXG.AbstractRenderer#updateText 572 * @see JXG.AbstractRenderer#updateTextStyle 573 */ 574 drawInternalText: function (element) { /* stub */ }, 575 576 /** 577 * Updates visual properties of an already existing {@link JXG.Text} element. 578 * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be updated. 579 * @see Text 580 * @see JXG.Text 581 * @see JXG.AbstractRenderer#drawInternalText 582 * @see JXG.AbstractRenderer#drawText 583 * @see JXG.AbstractRenderer#updateText 584 * @see JXG.AbstractRenderer#updateTextStyle 585 */ 586 updateInternalText: function (element) { /* stub */ }, 587 588 /** 589 * Displays a {@link JXG.Text} on the {@link JXG.Board} by putting a HTML div over it. 590 * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be displayed 591 * @see Text 592 * @see JXG.Text 593 * @see JXG.AbstractRenderer#drawInternalText 594 * @see JXG.AbstractRenderer#updateText 595 * @see JXG.AbstractRenderer#updateInternalText 596 * @see JXG.AbstractRenderer#updateTextStyle 597 */ 598 drawText: function (element) { 599 var node, z; 600 601 if (element.visProp.display === 'html' && Env.isBrowser) { 602 node = this.container.ownerDocument.createElement('div'); 603 node.style.position = 'absolute'; 604 605 node.className = element.visProp.cssclass; 606 if (this.container.style.zIndex === '') { 607 z = 0; 608 } else { 609 z = parseInt(this.container.style.zIndex, 10); 610 } 611 612 node.style.zIndex = z + element.board.options.layer.text; 613 this.container.appendChild(node); 614 node.setAttribute('id', this.container.id + '_' + element.id); 615 } else { 616 node = this.drawInternalText(element); 617 } 618 619 element.rendNode = node; 620 element.htmlStr = ''; 621 this.updateText(element); 622 }, 623 624 /** 625 * Updates visual properties of an already existing {@link JXG.Text} element. 626 * @param {JXG.Text} el Reference to an {@link JXG.Text} object, that has to be updated. 627 * @see Text 628 * @see JXG.Text 629 * @see JXG.AbstractRenderer#drawText 630 * @see JXG.AbstractRenderer#drawInternalText 631 * @see JXG.AbstractRenderer#updateInternalText 632 * @see JXG.AbstractRenderer#updateTextStyle 633 */ 634 updateText: function (el) { 635 var content = el.plaintext, v, c; 636 637 if (el.visProp.visible) { 638 this.updateTextStyle(el, false); 639 640 if (el.visProp.display === 'html') { 641 // Set the position 642 if (!isNaN(el.coords.scrCoords[1] + el.coords.scrCoords[2])) { 643 644 // Horizontal 645 c = el.coords.scrCoords[1]; 646 // webkit seems to fail for extremely large values for c. 647 c = Math.abs(c) < 1000000 ? c : 1000000; 648 649 if (el.visProp.anchorx === 'right') { 650 v = Math.floor(el.board.canvasWidth - c); 651 } else if (el.visProp.anchorx === 'middle') { 652 v = Math.floor(c - 0.5 * el.size[0]); 653 } else { // 'left' 654 v = Math.floor(c); 655 } 656 657 if (el.visPropOld.left !== (el.visProp.anchorx + v)) { 658 if (el.visProp.anchorx === 'right') { 659 el.rendNode.style.right = v + 'px'; 660 el.rendNode.style.left = 'auto'; 661 } else { 662 el.rendNode.style.left = v + 'px'; 663 el.rendNode.style.right = 'auto'; 664 } 665 el.visPropOld.left = el.visProp.anchorx + v; 666 } 667 668 // Vertical 669 c = el.coords.scrCoords[2] + this.vOffsetText; 670 c = Math.abs(c) < 1000000 ? c : 1000000; 671 672 if (el.visProp.anchory === 'bottom') { 673 v = Math.floor(el.board.canvasHeight - c); 674 } else if (el.visProp.anchory === 'middle') { 675 v = Math.floor(c - 0.5 * el.size[1]); 676 } else { // top 677 v = Math.floor(c); 678 } 679 680 if (el.visPropOld.top !== (el.visProp.anchory + v)) { 681 if (el.visProp.anchory === 'bottom') { 682 el.rendNode.style.top = 'auto'; 683 el.rendNode.style.bottom = v + 'px'; 684 } else { 685 el.rendNode.style.bottom = 'auto'; 686 el.rendNode.style.top = v + 'px'; 687 } 688 el.visPropOld.top = el.visProp.anchory + v; 689 } 690 } 691 692 // Set the content 693 if (el.htmlStr !== content) { 694 el.rendNode.innerHTML = content; 695 el.htmlStr = content; 696 697 if (el.visProp.usemathjax) { 698 // typesetting directly might not work because mathjax was not loaded completely 699 // see http://www.mathjax.org/docs/1.1/typeset.html 700 MathJax.Hub.Queue(['Typeset', MathJax.Hub, el.rendNode]); 701 } else if (el.visProp.useasciimathml) { 702 // This is not a constructor. 703 // See http://www1.chapman.edu/~jipsen/mathml/asciimath.html for more information 704 // about AsciiMathML and the project's source code. 705 AMprocessNode(el.rendNode, false); 706 } 707 } 708 this.transformImage(el, el.transformations); 709 } else { 710 this.updateInternalText(el); 711 } 712 } 713 }, 714 715 /** 716 * Updates font-size, color and opacity propertiey and CSS style properties of a {@link JXG.Text} node. 717 * This function is also called by highlight() and nohighlight(). 718 * @param {JXG.Text} element Reference to the {@link JXG.Text} object, that has to be updated. 719 * @param {Boolean} doHighlight 720 * @see Text 721 * @see JXG.Text 722 * @see JXG.AbstractRenderer#drawText 723 * @see JXG.AbstractRenderer#drawInternalText 724 * @see JXG.AbstractRenderer#updateText 725 * @see JXG.AbstractRenderer#updateInternalText 726 * @see JXG.AbstractRenderer#updateInternalTextStyle 727 */ 728 updateTextStyle: function (element, doHighlight) { 729 var fs, so, sc, css, 730 ev = element.visProp, 731 display = Env.isBrowser ? ev.display : 'internal'; 732 733 if (doHighlight) { 734 sc = ev.highlightstrokecolor; 735 so = ev.highlightstrokeopacity; 736 css = ev.highlightcssclass; 737 } else { 738 sc = ev.strokecolor; 739 so = ev.strokeopacity; 740 css = ev.cssclass; 741 } 742 743 // This part is executed for all text elements except internal texts in canvas. 744 if (display === 'html' || (this.type !== 'canvas' && this.type !== 'no')) { 745 fs = Type.evaluate(element.visProp.fontsize); 746 if (element.visPropOld.fontsize !== fs) { 747 element.needsSizeUpdate = true; 748 try { 749 element.rendNode.style.fontSize = fs + 'px'; 750 } catch (e) { 751 // IE needs special treatment. 752 element.rendNode.style.fontSize = fs; 753 } 754 element.visPropOld.fontsize = fs; 755 } 756 757 } 758 759 if (display === 'html') { 760 if (element.visPropOld.cssclass !== css) { 761 element.rendNode.className = css; 762 element.visPropOld.cssclass = css; 763 element.needsSizeUpdate = true; 764 } 765 this.setObjectStrokeColor(element, sc, so); 766 } else { 767 this.updateInternalTextStyle(element, sc, so); 768 } 769 return this; 770 }, 771 772 /** 773 * Set color and opacity of internal texts. 774 * This method is used for Canvas and VML. 775 * SVG needs its own version. 776 * @private 777 * @see JXG.AbstractRenderer#updateTextStyle 778 * @see JXG.SVGRenderer#updateInternalTextStyle 779 */ 780 updateInternalTextStyle: function (element, strokeColor, strokeOpacity) { 781 this.setObjectStrokeColor(element, strokeColor, strokeOpacity); 782 }, 783 784 /* ************************** 785 * Image related stuff 786 * **************************/ 787 788 /** 789 * Draws an {@link JXG.Image} on a board; This is just a template that has to be implemented by special 790 * renderers. 791 * @param {JXG.Image} element Reference to the image object that is to be drawn 792 * @see Image 793 * @see JXG.Image 794 * @see JXG.AbstractRenderer#updateImage 795 */ 796 drawImage: function (element) { /* stub */ }, 797 798 /** 799 * Updates the properties of an {@link JXG.Image} element. 800 * @param {JXG.Image} element Reference to an {@link JXG.Image} object, that has to be updated. 801 * @see Image 802 * @see JXG.Image 803 * @see JXG.AbstractRenderer#drawImage 804 */ 805 updateImage: function (element) { 806 this.updateRectPrim(element.rendNode, element.coords.scrCoords[1], 807 element.coords.scrCoords[2] - element.size[1], element.size[0], element.size[1]); 808 809 this.updateImageURL(element); 810 this.transformImage(element, element.transformations); 811 this._updateVisual(element, {stroke: true, dash: true}, true); 812 }, 813 814 /** 815 * Multiplication of transformations without updating. That means, at that point it is expected that the 816 * matrices contain numbers only. First, the origin in user coords is translated to <tt>(0,0)</tt> in screen 817 * coords. Then, the stretch factors are divided out. After the transformations in user coords, the stretch 818 * factors are multiplied in again, and the origin in user coords is translated back to its position. This 819 * method does not have to be implemented in a new renderer. 820 * @param {JXG.GeometryElement} element A JSXGraph element. We only need its board property. 821 * @param {Array} transformations An array of JXG.Transformations. 822 * @returns {Array} A matrix represented by a two dimensional array of numbers. 823 * @see JXG.AbstractRenderer#transformImage 824 */ 825 joinTransforms: function (element, transformations) { 826 var i, 827 ox = element.board.origin.scrCoords[1], 828 oy = element.board.origin.scrCoords[2], 829 ux = element.board.unitX, 830 uy = element.board.unitY, 831 // Translate to 0,0 in screen coords 832 /* 833 m = [[1, 0, 0], [0, 1, 0], [0, 0, 1]], 834 mpre1 = [[1, 0, 0], 835 [-ox, 1, 0], 836 [-oy, 0, 1]], 837 // Scale 838 mpre2 = [[1, 0, 0], 839 [0, 1 / ux, 0], 840 [0, 0, -1 / uy]], 841 // Scale back 842 mpost2 = [[1, 0, 0], 843 [0, ux, 0], 844 [0, 0, -uy]], 845 // Translate back 846 mpost1 = [[1, 0, 0], 847 [ox, 1, 0], 848 [oy, 0, 1]], 849 */ 850 len = transformations.length, 851 // Translate to 0,0 in screen coords and then scale 852 m = [[1, 0, 0], 853 [-ox / ux, 1 / ux, 0], 854 [ oy / uy, 0, -1 / uy]]; 855 856 for (i = 0; i < len; i++) { 857 //m = Mat.matMatMult(mpre1, m); 858 //m = Mat.matMatMult(mpre2, m); 859 m = Mat.matMatMult(transformations[i].matrix, m); 860 //m = Mat.matMatMult(mpost2, m); 861 //m = Mat.matMatMult(mpost1, m); 862 } 863 // Scale back and then translate back 864 m = Mat.matMatMult([[1, 0, 0], 865 [ox, ux, 0], 866 [oy, 0, -uy]], m); 867 return m; 868 }, 869 870 /** 871 * Applies transformations on images and text elements. This method is just a stub and has to be implemented in 872 * all descendant classes where text and image transformations are to be supported. 873 * @param {JXG.Image|JXG.Text} element A {@link JXG.Image} or {@link JXG.Text} object. 874 * @param {Array} transformations An array of {@link JXG.Transformation} objects. This is usually the 875 * transformations property of the given element <tt>el</tt>. 876 */ 877 transformImage: function (element, transformations) { /* stub */ }, 878 879 /** 880 * If the URL of the image is provided by a function the URL has to be updated during updateImage() 881 * @param {JXG.Image} element Reference to an image object. 882 * @see JXG.AbstractRenderer#updateImage 883 */ 884 updateImageURL: function (element) { /* stub */ }, 885 886 /** 887 * Updates CSS style properties of a {@link JXG.Image} node. 888 * In SVGRenderer opacity is the only available style element. 889 * This function is called by highlight() and nohighlight(). 890 * This function works for VML. 891 * It does not work for Canvas. 892 * SVGRenderer overwrites this method. 893 * @param {JXG.Text} el Reference to the {@link JXG.Image} object, that has to be updated. 894 * @param {Boolean} doHighlight 895 * @see Image 896 * @see JXG.Image 897 * @see JXG.AbstractRenderer#highlight 898 * @see JXG.AbstractRenderer#noHighlight 899 */ 900 updateImageStyle: function (el, doHighlight) { 901 el.rendNode.className = doHighlight ? el.visProp.highlightcssclass : el.visProp.cssclass; 902 }, 903 904 905 /* ************************** 906 * Render primitive objects 907 * **************************/ 908 909 /** 910 * Appends a node to a specific layer level. This is just an abstract method and has to be implemented 911 * in all renderers that want to use the <tt>createPrim</tt> model to draw. 912 * @param {Node} node A DOM tree node. 913 * @param {Number} level The layer the node is attached to. This is the index of the layer in 914 * {@link JXG.SVGRenderer#layer} or the <tt>z-index</tt> style property of the node in VMLRenderer. 915 */ 916 appendChildPrim: function (node, level) { /* stub */ }, 917 918 /** 919 * Stores the rendering nodes. This is an abstract method which has to be implemented in all renderers that use 920 * the <tt>createPrim</tt> method. 921 * @param {JXG.GeometryElement} element A JSXGraph element. 922 * @param {String} type The XML node name. Only used in VMLRenderer. 923 */ 924 appendNodesToElement: function (element, type) { /* stub */ }, 925 926 /** 927 * Creates a node of a given type with a given id. 928 * @param {String} type The type of the node to create. 929 * @param {String} id Set the id attribute to this. 930 * @returns {Node} Reference to the created node. 931 */ 932 createPrim: function (type, id) { 933 /* stub */ 934 return null; 935 }, 936 937 /** 938 * Removes an element node. Just a stub. 939 * @param {Node} node The node to remove. 940 */ 941 remove: function (node) { /* stub */ }, 942 943 /** 944 * Can be used to create the nodes to display arrows. This is an abstract method which has to be implemented 945 * in any descendant renderer. 946 * @param {JXG.GeometryElement} element The element the arrows are to be attached to. 947 */ 948 makeArrows: function (element) { /* stub */ }, 949 950 /** 951 * Updates an ellipse node primitive. This is an abstract method which has to be implemented in all renderers 952 * that use the <tt>createPrim</tt> method. 953 * @param {Node} node Reference to the node. 954 * @param {Number} x Centre X coordinate 955 * @param {Number} y Centre Y coordinate 956 * @param {Number} rx The x-axis radius. 957 * @param {Number} ry The y-axis radius. 958 */ 959 updateEllipsePrim: function (node, x, y, rx, ry) { /* stub */ }, 960 961 /** 962 * Refreshes a line node. This is an abstract method which has to be implemented in all renderers that use 963 * the <tt>createPrim</tt> method. 964 * @param {Node} node The node to be refreshed. 965 * @param {Number} p1x The first point's x coordinate. 966 * @param {Number} p1y The first point's y coordinate. 967 * @param {Number} p2x The second point's x coordinate. 968 * @param {Number} p2y The second point's y coordinate. 969 * @param {JXG.Board} board 970 */ 971 updateLinePrim: function (node, p1x, p1y, p2x, p2y, board) { /* stub */ }, 972 973 /** 974 * Updates a path element. This is an abstract method which has to be implemented in all renderers that use 975 * the <tt>createPrim</tt> method. 976 * @param {Node} node The path node. 977 * @param {String} pathString A string formatted like e.g. <em>'M 1,2 L 3,1 L5,5'</em>. The format of the string 978 * depends on the rendering engine. 979 * @param {JXG.Board} board Reference to the element's board. 980 */ 981 updatePathPrim: function (node, pathString, board) { /* stub */ }, 982 983 /** 984 * Builds a path data string to draw a point with a face other than <em>rect</em> and <em>circle</em>. Since 985 * the format of such a string usually depends on the renderer this method 986 * is only an abstract method. Therefore, it has to be implemented in the descendant renderer itself unless 987 * the renderer does not use the createPrim interface but the draw* interfaces to paint. 988 * @param {JXG.Point} element The point element 989 * @param {Number} size A positive number describing the size. Usually the half of the width and height of 990 * the drawn point. 991 * @param {String} type A string describing the point's face. This method only accepts the shortcut version of 992 * each possible face: <tt>x, +, <>, ^, v, >, < 993 */ 994 updatePathStringPoint: function (element, size, type) { /* stub */ }, 995 996 /** 997 * Builds a path data string from a {@link JXG.Curve} element. Since the path data strings heavily depend on the 998 * underlying rendering technique this method is just a stub. Although such a path string is of no use for the 999 * CanvasRenderer, this method is used there to draw a path directly. 1000 * @param element 1001 */ 1002 updatePathStringPrim: function (element) { /* stub */ }, 1003 1004 /** 1005 * Builds a path data string from a {@link JXG.Curve} element such that the curve looks like hand drawn. Since 1006 * the path data strings heavily depend on the underlying rendering technique this method is just a stub. 1007 * Although such a path string is of no use for the CanvasRenderer, this method is used there to draw a path 1008 * directly. 1009 * @param element 1010 */ 1011 updatePathStringBezierPrim: function (element) { /* stub */ }, 1012 1013 1014 /** 1015 * Update a polygon primitive. 1016 * @param {Node} node 1017 * @param {JXG.Polygon} element A JSXGraph element of type {@link JXG.Polygon} 1018 */ 1019 updatePolygonPrim: function (node, element) { /* stub */ }, 1020 1021 /** 1022 * Update a rectangle primitive. This is used only for points with face of type 'rect'. 1023 * @param {Node} node The node yearning to be updated. 1024 * @param {Number} x x coordinate of the top left vertex. 1025 * @param {Number} y y coordinate of the top left vertex. 1026 * @param {Number} w Width of the rectangle. 1027 * @param {Number} h The rectangle's height. 1028 */ 1029 updateRectPrim: function (node, x, y, w, h) { /* stub */ }, 1030 1031 /* ************************** 1032 * Set Attributes 1033 * **************************/ 1034 1035 /** 1036 * Sets a node's attribute. 1037 * @param {Node} node The node that is to be updated. 1038 * @param {String} key Name of the attribute. 1039 * @param {String} val New value for the attribute. 1040 */ 1041 setPropertyPrim: function (node, key, val) { /* stub */ }, 1042 1043 /** 1044 * Shows a hidden element on the canvas; Only a stub, requires implementation in the derived renderer. 1045 * @param {JXG.GeometryElement} element Reference to the object that has to appear. 1046 * @see JXG.AbstractRenderer#hide 1047 */ 1048 show: function (element) { /* stub */ }, 1049 1050 /** 1051 * Hides an element on the canvas; Only a stub, requires implementation in the derived renderer. 1052 * @param {JXG.GeometryElement} element Reference to the geometry element that has to disappear. 1053 * @see JXG.AbstractRenderer#show 1054 */ 1055 hide: function (element) { /* stub */ }, 1056 1057 /** 1058 * Sets the buffering as recommended by SVGWG. Until now only Opera supports this and will be ignored by other 1059 * browsers. Although this feature is only supported by SVG we have this method in {@link JXG.AbstractRenderer} 1060 * because it is called from outside the renderer. 1061 * @param {Node} node The SVG DOM Node which buffering type to update. 1062 * @param {String} type Either 'auto', 'dynamic', or 'static'. For an explanation see 1063 * {@link http://www.w3.org/TR/SVGTiny12/painting.html#BufferedRenderingProperty}. 1064 */ 1065 setBuffering: function (node, type) { /* stub */ }, 1066 1067 /** 1068 * Sets an element's dash style. 1069 * @param {JXG.GeometryElement} element An JSXGraph element. 1070 */ 1071 setDashStyle: function (element) { /* stub */ }, 1072 1073 /** 1074 * Puts an object into draft mode, i.e. it's visual appearance will be changed. For GEONE<sub>x</sub>T backwards 1075 * compatibility. 1076 * @param {JXG.GeometryElement} element Reference of the object that is in draft mode. 1077 */ 1078 setDraft: function (element) { 1079 if (!element.visProp.draft) { 1080 return; 1081 } 1082 var draftColor = element.board.options.elements.draft.color, 1083 draftOpacity = element.board.options.elements.draft.opacity; 1084 1085 if (element.type === Const.OBJECT_TYPE_POLYGON) { 1086 this.setObjectFillColor(element, draftColor, draftOpacity); 1087 } else { 1088 if (element.elementClass === Const.OBJECT_CLASS_POINT) { 1089 this.setObjectFillColor(element, draftColor, draftOpacity); 1090 } else { 1091 this.setObjectFillColor(element, 'none', 0); 1092 } 1093 this.setObjectStrokeColor(element, draftColor, draftOpacity); 1094 this.setObjectStrokeWidth(element, element.board.options.elements.draft.strokeWidth); 1095 } 1096 }, 1097 1098 /** 1099 * Puts an object from draft mode back into normal mode. 1100 * @param {JXG.GeometryElement} element Reference of the object that no longer is in draft mode. 1101 */ 1102 removeDraft: function (element) { 1103 if (element.type === Const.OBJECT_TYPE_POLYGON) { 1104 this.setObjectFillColor(element, element.visProp.fillcolor, element.visProp.fillopacity); 1105 } else { 1106 if (element.type === Const.OBJECT_CLASS_POINT) { 1107 this.setObjectFillColor(element, element.visProp.fillcolor, element.visProp.fillopacity); 1108 } 1109 this.setObjectStrokeColor(element, element.visProp.strokecolor, element.visProp.strokeopacity); 1110 this.setObjectStrokeWidth(element, element.visProp.strokewidth); 1111 } 1112 }, 1113 1114 /** 1115 * Sets up nodes for rendering a gradient fill. 1116 * @param element 1117 */ 1118 setGradient: function (element) { /* stub */ }, 1119 1120 /** 1121 * Updates the gradient fill. 1122 * @param {JXG.GeometryElement} element An JSXGraph element with an area that can be filled. 1123 */ 1124 updateGradient: function (element) { /* stub */ }, 1125 1126 /** 1127 * Sets an objects fill color. 1128 * @param {JXG.GeometryElement} element Reference of the object that wants a new fill color. 1129 * @param {String} color Color in a HTML/CSS compatible format. If you don't want any fill color at all, choose 1130 * 'none'. 1131 * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1. 1132 */ 1133 setObjectFillColor: function (element, color, opacity) { /* stub */ }, 1134 1135 /** 1136 * Changes an objects stroke color to the given color. 1137 * @param {JXG.GeometryElement} element Reference of the {@link JXG.GeometryElement} that gets a new stroke 1138 * color. 1139 * @param {String} color Color value in a HTML compatible format, e.g. <strong>#00ff00</strong> or 1140 * <strong>green</strong> for green. 1141 * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1. 1142 */ 1143 setObjectStrokeColor: function (element, color, opacity) { /* stub */ }, 1144 1145 /** 1146 * Sets an element's stroke width. 1147 * @param {JXG.GeometryElement} element Reference to the geometry element. 1148 * @param {Number} width The new stroke width to be assigned to the element. 1149 */ 1150 setObjectStrokeWidth: function (element, width) { /* stub */ }, 1151 1152 /** 1153 * Sets the shadow properties to a geometry element. This method is only a stub, it is implemented in the actual 1154 * renderers. 1155 * @param {JXG.GeometryElement} element Reference to a geometry object, that should get a shadow 1156 */ 1157 setShadow: function (element) { /* stub */ }, 1158 1159 /** 1160 * Highlights an object, i.e. changes the current colors of the object to its highlighting colors 1161 * @param {JXG.GeometryElement} element Reference of the object that will be highlighted. 1162 * @returns {JXG.AbstractRenderer} Reference to the renderer 1163 * @see JXG.AbstractRenderer#updateTextStyle 1164 */ 1165 highlight: function (element) { 1166 var i, ev = element.visProp; 1167 1168 if (!ev.draft) { 1169 if (element.type === Const.OBJECT_TYPE_POLYGON) { 1170 this.setObjectFillColor(element, ev.highlightfillcolor, ev.highlightfillopacity); 1171 for (i = 0; i < element.borders.length; i++) { 1172 this.setObjectStrokeColor(element.borders[i], element.borders[i].visProp.highlightstrokecolor, 1173 element.borders[i].visProp.highlightstrokeopacity); 1174 } 1175 } else { 1176 if (element.elementClass === Const.OBJECT_CLASS_TEXT) { 1177 this.updateTextStyle(element, true); 1178 } else if (element.type === Const.OBJECT_TYPE_IMAGE) { 1179 this.updateImageStyle(element, true); 1180 } else { 1181 this.setObjectStrokeColor(element, ev.highlightstrokecolor, ev.highlightstrokeopacity); 1182 this.setObjectFillColor(element, ev.highlightfillcolor, ev.highlightfillopacity); 1183 } 1184 } 1185 if (ev.highlightstrokewidth) { 1186 this.setObjectStrokeWidth(element, Math.max(ev.highlightstrokewidth, ev.strokewidth)); 1187 } 1188 } 1189 1190 return this; 1191 }, 1192 1193 /** 1194 * Uses the normal colors of an object, i.e. the opposite of {@link JXG.AbstractRenderer#highlight}. 1195 * @param {JXG.GeometryElement} element Reference of the object that will get its normal colors. 1196 * @returns {JXG.AbstractRenderer} Reference to the renderer 1197 * @see JXG.AbstractRenderer#updateTextStyle 1198 */ 1199 noHighlight: function (element) { 1200 var i, ev = element.visProp; 1201 1202 if (!element.visProp.draft) { 1203 if (element.type === Const.OBJECT_TYPE_POLYGON) { 1204 this.setObjectFillColor(element, ev.fillcolor, ev.fillopacity); 1205 for (i = 0; i < element.borders.length; i++) { 1206 this.setObjectStrokeColor(element.borders[i], element.borders[i].visProp.strokecolor, 1207 element.borders[i].visProp.strokeopacity); 1208 } 1209 } else { 1210 if (element.elementClass === Const.OBJECT_CLASS_TEXT) { 1211 this.updateTextStyle(element, false); 1212 } else if (element.type === Const.OBJECT_TYPE_IMAGE) { 1213 this.updateImageStyle(element, false); 1214 } else { 1215 this.setObjectStrokeColor(element, ev.strokecolor, ev.strokeopacity); 1216 this.setObjectFillColor(element, ev.fillcolor, ev.fillopacity); 1217 } 1218 } 1219 this.setObjectStrokeWidth(element, ev.strokewidth); 1220 } 1221 1222 return this; 1223 }, 1224 1225 /* ************************** 1226 * renderer control 1227 * **************************/ 1228 1229 /** 1230 * Stop redraw. This method is called before every update, so a non-vector-graphics based renderer can use this 1231 * method to delete the contents of the drawing panel. This is an abstract method every descendant renderer 1232 * should implement, if appropriate. 1233 * @see JXG.AbstractRenderer#unsuspendRedraw 1234 */ 1235 suspendRedraw: function () { /* stub */ }, 1236 1237 /** 1238 * Restart redraw. This method is called after updating all the rendering node attributes. 1239 * @see JXG.AbstractRenderer#suspendRedraw 1240 */ 1241 unsuspendRedraw: function () { /* stub */ }, 1242 1243 /** 1244 * The tiny zoom bar shown on the bottom of a board (if showNavigation on board creation is true). 1245 * @param {JXG.Board} board Reference to a JSXGraph board. 1246 */ 1247 drawZoomBar: function (board) { 1248 var doc, 1249 node, 1250 cancelbubble = function (e) { 1251 if (!e) { 1252 e = window.event; 1253 } 1254 1255 if (e.stopPropagation) { 1256 // Non IE<=8 1257 e.stopPropagation(); 1258 } else { 1259 e.cancelBubble = true; 1260 } 1261 }, 1262 createButton = function (label, handler) { 1263 var button; 1264 1265 button = doc.createElement('span'); 1266 node.appendChild(button); 1267 button.appendChild(doc.createTextNode(label)); 1268 Env.addEvent(button, 'mouseover', function () { 1269 this.style.backgroundColor = board.options.navbar.highlightFillColor; 1270 }, button); 1271 Env.addEvent(button, 'mouseover', function () { 1272 this.style.backgroundColor = board.options.navbar.highlightFillColor; 1273 }, button); 1274 Env.addEvent(button, 'mouseout', function () { 1275 this.style.backgroundColor = board.options.navbar.fillColor; 1276 }, button); 1277 1278 Env.addEvent(button, 'click', handler, board); 1279 // prevent the click from bubbling down to the board 1280 Env.addEvent(button, 'mouseup', cancelbubble, board); 1281 Env.addEvent(button, 'mousedown', cancelbubble, board); 1282 Env.addEvent(button, 'touchend', cancelbubble, board); 1283 Env.addEvent(button, 'touchstart', cancelbubble, board); 1284 }; 1285 1286 if (Env.isBrowser) { 1287 doc = board.containerObj.ownerDocument; 1288 node = doc.createElement('div'); 1289 1290 node.setAttribute('id', board.containerObj.id + '_navigationbar'); 1291 1292 node.style.color = board.options.navbar.strokeColor; 1293 node.style.backgroundColor = board.options.navbar.fillColor; 1294 node.style.padding = board.options.navbar.padding; 1295 node.style.position = board.options.navbar.position; 1296 node.style.fontSize = board.options.navbar.fontSize; 1297 node.style.cursor = board.options.navbar.cursor; 1298 node.style.zIndex = board.options.navbar.zIndex; 1299 board.containerObj.appendChild(node); 1300 node.style.right = board.options.navbar.right; 1301 node.style.bottom = board.options.navbar.bottom; 1302 1303 // For XHTML we need unicode instead of HTML entities 1304 1305 if (board.attr.showreload) { 1306 // full reload circle: \u27F2 1307 // the board.reload() method does not exist during the creation 1308 // of this button. That's why this anonymous function wrapper is required. 1309 createButton('\u00A0\u21BB\u00A0', function () { 1310 board.reload(); 1311 }); 1312 } 1313 1314 if (board.attr.showcleartraces) { 1315 // clear traces symbol (otimes): \u27F2 1316 createButton('\u00A0\u2297\u00A0', function () { 1317 board.clearTraces(); 1318 }); 1319 } 1320 1321 if (board.attr.shownavigation) { 1322 createButton('\u00A0\u2013\u00A0', board.zoomOut); 1323 createButton('\u00A0o\u00A0', board.zoom100); 1324 createButton('\u00A0+\u00A0', board.zoomIn); 1325 createButton('\u00A0\u2190\u00A0', board.clickLeftArrow); 1326 createButton('\u00A0\u2193\u00A0', board.clickUpArrow); 1327 createButton('\u00A0\u2191\u00A0', board.clickDownArrow); 1328 createButton('\u00A0\u2192\u00A0', board.clickRightArrow); 1329 } 1330 } 1331 }, 1332 1333 /** 1334 * Wrapper for getElementById for maybe other renderers which elements are not directly accessible by DOM 1335 * methods like document.getElementById(). 1336 * @param {String} id Unique identifier for element. 1337 * @returns {Object} Reference to a JavaScript object. In case of SVG/VMLRenderer it's a reference to a SVG/VML 1338 * node. 1339 */ 1340 getElementById: function (id) { 1341 return this.container.ownerDocument.getElementById(this.container.id + '_' + id); 1342 }, 1343 1344 /** 1345 * Remove an element and provide a function that inserts it into its original position. This method 1346 * is taken from this article {@link https://developers.google.com/speed/articles/javascript-dom}. 1347 * @author KeeKim Heng, Google Web Developer 1348 * @param {Element} element The element to be temporarily removed 1349 * @returns {Function} A function that inserts the element into its original position 1350 */ 1351 removeToInsertLater: function (element) { 1352 var parentNode = element.parentNode, 1353 nextSibling = element.nextSibling; 1354 1355 parentNode.removeChild(element); 1356 1357 return function () { 1358 if (nextSibling) { 1359 parentNode.insertBefore(element, nextSibling); 1360 } else { 1361 parentNode.appendChild(element); 1362 } 1363 }; 1364 }, 1365 1366 /** 1367 * Resizes the rendering element 1368 * @param {Number} w New width 1369 * @param {Number} h New height 1370 */ 1371 resize: function (w, h) { /* stub */}, 1372 1373 /** 1374 * Create crosshair elements (Fadenkreuz) for presentations. 1375 * @param {Number} n Number of crosshairs. 1376 */ 1377 createTouchpoints: function (n) {}, 1378 1379 /** 1380 * Show a specific crosshair. 1381 * @param {Number} i Number of the crosshair to show 1382 */ 1383 showTouchpoint: function (i) {}, 1384 1385 /** 1386 * Hide a specific crosshair. 1387 * @param {Number} i Number of the crosshair to show 1388 */ 1389 hideTouchpoint: function (i) {}, 1390 1391 /** 1392 * Move a specific crosshair. 1393 * @param {Number} i Number of the crosshair to show 1394 * @param {Array} pos New positon in screen coordinates 1395 */ 1396 updateTouchpoint: function (i, pos) {} 1397 }); 1398 1399 return JXG.AbstractRenderer; 1400 }); 1401