1 /* 2 Copyright 2008-2018 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 * True if the browsers' SVG engine supports foreignObject. 162 * Not supported browsers are IE 9 - 11. 163 * All other browsers return ture, since it is tested with 164 * document.implementation.hasFeature() which is deprecated. 165 * 166 * @type Boolean 167 * @private 168 */ 169 this.supportsForeignObject = false; 170 171 }; 172 173 JXG.extend(JXG.AbstractRenderer.prototype, /** @lends JXG.AbstractRenderer.prototype */ { 174 175 /* ******************************** * 176 * private methods * 177 * should not be called from * 178 * outside AbstractRenderer * 179 * ******************************** */ 180 181 /** 182 * Update visual properties, but only if {@link JXG.AbstractRenderer#enhancedRendering} or <tt>enhanced</tt> is set to true. 183 * @param {JXG.GeometryElement} element The element to update 184 * @param {Object} [not={}] Select properties you don't want to be updated: <tt>{fill: true, dash: true}</tt> updates 185 * everything except for fill and dash. Possible values are <tt>stroke, fill, dash, shadow, gradient</tt>. 186 * @param {Boolean} [enhanced=false] If true, {@link JXG.AbstractRenderer#enhancedRendering} is assumed to be true. 187 * @private 188 */ 189 _updateVisual: function (el, not, enhanced) { 190 if (enhanced || this.enhancedRendering) { 191 not = not || {}; 192 193 this.setObjectTransition(el); 194 if (!Type.evaluate(el.visProp.draft)) { 195 if (!not.stroke) { 196 if (el.highlighted) { 197 this.setObjectStrokeColor(el, 198 el.visProp.highlightstrokecolor, 199 el.visProp.highlightstrokeopacity); 200 this.setObjectStrokeWidth(el, el.visProp.highlightstrokewidth); 201 } else { 202 this.setObjectStrokeColor(el, 203 el.visProp.strokecolor, 204 el.visProp.strokeopacity); 205 this.setObjectStrokeWidth(el, el.visProp.strokewidth); 206 } 207 } 208 209 if (!not.fill) { 210 if (el.highlighted) { 211 this.setObjectFillColor(el, 212 el.visProp.highlightfillcolor, 213 el.visProp.highlightfillopacity); 214 } else { 215 this.setObjectFillColor(el, 216 el.visProp.fillcolor, 217 el.visProp.fillopacity); 218 } 219 } 220 221 if (!not.dash) { 222 this.setDashStyle(el, el.visProp); 223 } 224 225 if (!not.shadow) { 226 this.setShadow(el); 227 } 228 229 if (!not.gradient) { 230 this.setShadow(el); 231 } 232 } else { 233 this.setDraft(el); 234 } 235 } 236 }, 237 238 239 /* ******************************** * 240 * Point drawing and updating * 241 * ******************************** */ 242 243 /** 244 * Draws a point on the {@link JXG.Board}. 245 * @param {JXG.Point} el Reference to a {@link JXG.Point} object that has to be drawn. 246 * @see Point 247 * @see JXG.Point 248 * @see JXG.AbstractRenderer#updatePoint 249 * @see JXG.AbstractRenderer#changePointStyle 250 */ 251 drawPoint: function (el) { 252 var prim, 253 // sometimes el is not a real point and lacks the methods of a JXG.Point instance, 254 // in these cases to not use el directly. 255 face = Options.normalizePointFace(Type.evaluate(el.visProp.face)); 256 257 // determine how the point looks like 258 if (face === 'o') { 259 prim = 'ellipse'; 260 } else if (face === '[]') { 261 prim = 'rect'; 262 } else { 263 // cross/x, diamond/<>, triangleup/a/^, triangledown/v, triangleleft/<, 264 // triangleright/>, plus/+, 265 prim = 'path'; 266 } 267 268 el.rendNode = this.appendChildPrim(this.createPrim(prim, el.id), Type.evaluate(el.visProp.layer)); 269 this.appendNodesToElement(el, prim); 270 271 // adjust visual propertys 272 this._updateVisual(el, {dash: true, shadow: true}, true); 273 274 // By now we only created the xml nodes and set some styles, in updatePoint 275 // the attributes are filled with data. 276 this.updatePoint(el); 277 }, 278 279 /** 280 * Updates visual appearance of the renderer element assigned to the given {@link JXG.Point}. 281 * @param {JXG.Point} el Reference to a {@link JXG.Point} object, that has to be updated. 282 * @see Point 283 * @see JXG.Point 284 * @see JXG.AbstractRenderer#drawPoint 285 * @see JXG.AbstractRenderer#changePointStyle 286 */ 287 updatePoint: function (el) { 288 var size = Type.evaluate(el.visProp.size), 289 // sometimes el is not a real point and lacks the methods of a JXG.Point instance, 290 // in these cases to not use el directly. 291 face = Options.normalizePointFace(Type.evaluate(el.visProp.face)), 292 s1 = (size === 0) ? 0 : size + 1; 293 294 if (!isNaN(el.coords.scrCoords[2] + el.coords.scrCoords[1])) { 295 size *= ((!el.board || !el.board.options.point.zoom) ? 296 1.0 : Math.sqrt(el.board.zoomX * el.board.zoomY)); 297 298 if (face === 'o') { // circle 299 this.updateEllipsePrim(el.rendNode, el.coords.scrCoords[1], 300 el.coords.scrCoords[2], s1, s1); 301 } else if (face === '[]') { // rectangle 302 this.updateRectPrim(el.rendNode, el.coords.scrCoords[1] - size, 303 el.coords.scrCoords[2] - size, size * 2, size * 2); 304 } else { // x, +, <>, ^, v, <, > 305 this.updatePathPrim(el.rendNode, 306 this.updatePathStringPoint(el, size, face), el.board); 307 } 308 this._updateVisual(el, {dash: false, shadow: false}); 309 this.setShadow(el); 310 } 311 }, 312 313 /** 314 * Changes the style of a {@link JXG.Point}. This is required because the point styles differ in what 315 * elements have to be drawn, e.g. if the point is marked by a "x" or a "+" two lines are drawn, if 316 * it's marked by spot a circle is drawn. This method removes the old renderer element(s) and creates 317 * the new one(s). 318 * @param {JXG.Point} el Reference to a {@link JXG.Point} object, that's style is changed. 319 * @see Point 320 * @see JXG.Point 321 * @see JXG.AbstractRenderer#updatePoint 322 * @see JXG.AbstractRenderer#drawPoint 323 */ 324 changePointStyle: function (el) { 325 var node = this.getElementById(el.id); 326 327 // remove the existing point rendering node 328 if (Type.exists(node)) { 329 this.remove(node); 330 } 331 332 // and make a new one 333 this.drawPoint(el); 334 Type.clearVisPropOld(el); 335 336 if (!el.visPropCalc.visible) { 337 this.hide(el); 338 } 339 340 if (Type.evaluate(el.visProp.draft)) { 341 this.setDraft(el); 342 } 343 }, 344 345 /* ******************************** * 346 * Lines * 347 * ******************************** */ 348 349 /** 350 * Draws a line on the {@link JXG.Board}. 351 * @param {JXG.Line} el Reference to a line object, that has to be drawn. 352 * @see Line 353 * @see JXG.Line 354 * @see JXG.AbstractRenderer#updateLine 355 */ 356 drawLine: function (el) { 357 el.rendNode = this.appendChildPrim(this.createPrim('line', el.id), 358 Type.evaluate(el.visProp.layer)); 359 this.appendNodesToElement(el, 'lines'); 360 this.updateLine(el); 361 }, 362 363 /** 364 * Corrects the line length if there are arrow heads, such that 365 * the arrow ends exactly at the intended position. 366 * Calls the renderer method to draw the line. 367 * 368 * @param {JXG.Line} el Reference to a line object, that has to be drawn. 369 * @param {Number} strokeWidth Stroke width of the line. This determines the size of the 370 * arrow head. 371 * 372 * @returns {Object} Returns the object returned by 373 * {@link JXG.AbstractRenderer#getPositionArrowHead}. This contains the information in 374 * horizontal and vertical pixels how much 375 * the line has to be shortened on each end. 376 * 377 * @private 378 * @see Line 379 * @see JXG.Line 380 * @see JXG.AbstractRenderer#updateLine 381 * @see JXG.AbstractRenderer#getPositionArrowHead 382 * 383 */ 384 updateLineEndings: function(el, strokewidth) { 385 var c1 = new Coords(Const.COORDS_BY_USER, el.point1.coords.usrCoords, el.board), 386 c2 = new Coords(Const.COORDS_BY_USER, el.point2.coords.usrCoords, el.board), 387 obj, margin = null; 388 389 margin = Type.evaluate(el.visProp.margin); 390 Geometry.calcStraight(el, c1, c2, margin); 391 392 obj = this.getPositionArrowHead(el, c1, c2, strokewidth); 393 this.updateLinePrim(el.rendNode, 394 obj.c1.scrCoords[1] + obj.d1x, obj.c1.scrCoords[2] + obj.d1y, 395 obj.c2.scrCoords[1] - obj.d2x, obj.c2.scrCoords[2] - obj.d2y, el.board); 396 397 return obj; 398 }, 399 400 /** 401 * Read the attribute "size" of the arrow heads. Multiplied with the stroke width of the line 402 * this gives the absolute size of the arrow heads. Then the arrow heads are redrawn by the renderer. 403 * 404 * @param {JXG.Line} el Reference to a line object, that has to be drawn. 405 * @param {Object} obj Reference to a object returned by 406 * {@link JXG.AbstractRenderer#getPositionArrowHead} 407 * @returns {JXG.AbstractRenderer} Reference to the renderer 408 * 409 * @private 410 * @see Line 411 * @see JXG.Line 412 * @see JXG.AbstractRenderer#updateLine 413 * @see JXG.AbstractRenderer#getPositionArrowHead 414 */ 415 updateArrowSize: function(el, obj) { 416 var size, ev_fa, ev_la; 417 418 ev_fa = Type.evaluate(el.visProp.firstarrow); 419 if (ev_fa) { 420 if (Type.exists(ev_fa.size)) { 421 size = Type.evaluate(ev_fa.size); 422 } else { 423 size = 3; 424 } 425 426 this._setArrowWidth(el.rendNodeTriangleStart, obj.sFirst, el.rendNode, size); 427 } 428 ev_la = Type.evaluate(el.visProp.lastarrow); 429 if (ev_la) { 430 if (Type.exists(ev_la.size)) { 431 size = Type.evaluate(ev_la.size); 432 } else { 433 size = 3; 434 } 435 this._setArrowWidth(el.rendNodeTriangleEnd, obj.sLast, el.rendNode, size); 436 } 437 438 return this; 439 }, 440 441 /** 442 * Updates visual appearance of the renderer element assigned to the given {@link JXG.Line}. 443 * @param {JXG.Line} el Reference to the {@link JXG.Line} object that has to be updated. 444 * @see Line 445 * @see JXG.Line 446 * @see JXG.AbstractRenderer#drawLine 447 */ 448 updateLine: function (el) { 449 var obj; 450 451 obj = this.updateLineEndings(el, Type.evaluate(el.visProp.strokewidth)); 452 this.makeArrows(el); 453 this._updateVisual(el); 454 this.updateArrowSize(el, obj); 455 this.setLineCap(el); 456 }, 457 458 /** 459 * Shorten the line length such that the arrow head touches 460 * the start or end point and such that the arrow head ends exactly 461 * at the start / end position of the line. 462 * 463 * @param {JXG.Line} el Reference to the line object that gets arrow heads. 464 * @param {JXG.Coords} c1 Coords of the first point of the line (after {@link JXG.Geometry#calcStraight}). 465 * @param {JXG.Coords} c2 Coords of the second point of the line (after {@link JXG.Geometry#calcStraight}). 466 * @return {object} Object containing how much the line has to be shortened. 467 * Data structure: {d1x, d1y, d2x, d2y, sFirst, sLast}. sFirst and sLast is the length by which 468 * firstArrow and lastArrow have to shifted such that there is no gap between arrow head and line. 469 * Additionally, if one of these values is zero, the arrow is not displayed. This is the case, if the 470 * line length is very short. 471 */ 472 getPositionArrowHead: function(el, c1, c2, strokewidth) { 473 var s, s1, s2, d, d1x, d1y, d2x, d2y, 474 minlen = Mat.eps, 475 typeFirst, typeLast, 476 sFirst = 0, 477 sLast = 0, 478 ev_fa = Type.evaluate(el.visProp.firstarrow), 479 ev_la = Type.evaluate(el.visProp.lastarrow), 480 size; 481 482 d1x = d1y = d2x = d2y = 0.0; 483 /* 484 Handle arrow heads. 485 486 The arrow head is an isosceles triangle with base length 10 units and height 10 units. 487 These 10 units are scaled to strokeWidth * arrowSize pixels pixels. 488 */ 489 if (ev_fa || ev_la) { 490 s1 = Type.evaluate(el.point1.visProp.size) + Type.evaluate(el.point1.visProp.strokewidth); 491 s2 = Type.evaluate(el.point2.visProp.size) + Type.evaluate(el.point2.visProp.strokewidth); 492 s = s1 + s2; 493 494 // Handle touchlastpoint /touchfirstpoint 495 if (ev_la && Type.evaluate(el.visProp.touchlastpoint)) { 496 d = c1.distance(Const.COORDS_BY_SCREEN, c2); 497 if (d > s) { 498 d2x = (c2.scrCoords[1] - c1.scrCoords[1]) * s2 / d; 499 d2y = (c2.scrCoords[2] - c1.scrCoords[2]) * s2 / d; 500 c2 = new Coords(Const.COORDS_BY_SCREEN, [c2.scrCoords[1] - d2x, c2.scrCoords[2] - d2y], el.board); 501 } 502 } 503 if (ev_fa && Type.evaluate(el.visProp.touchfirstpoint)) { 504 d = c1.distance(Const.COORDS_BY_SCREEN, c2); 505 if (d > s) { 506 d1x = (c2.scrCoords[1] - c1.scrCoords[1]) * s1 / d; 507 d1y = (c2.scrCoords[2] - c1.scrCoords[2]) * s1 / d; 508 c1 = new Coords(Const.COORDS_BY_SCREEN, [c1.scrCoords[1] + d1x, c1.scrCoords[2] + d1y], el.board); 509 } 510 } 511 512 // Correct the position of the arrow heads 513 d1x = d1y = d2x = d2y = 0.0; 514 d = c1.distance(Const.COORDS_BY_SCREEN, c2); 515 516 if (Type.exists(ev_fa.type)) { 517 typeFirst = Type.evaluate(ev_fa.type); 518 } 519 if (Type.exists(ev_la.type)) { 520 typeLast = Type.evaluate(ev_la.type); 521 } 522 523 if (ev_fa) { 524 if (Type.exists(ev_fa.size)) { 525 size = Type.evaluate(ev_fa.size); 526 } else { 527 size = 3; 528 } 529 sFirst = strokewidth * size; 530 if (typeFirst === 2) { 531 sFirst *= 0.5; 532 minlen += strokewidth * size; 533 } else if (typeFirst === 3) { 534 sFirst = strokewidth; 535 minlen += strokewidth; 536 } else { 537 minlen += strokewidth * size; 538 } 539 } 540 if (ev_la) { 541 if (Type.exists(ev_la.size)) { 542 size = Type.evaluate(ev_la.size); 543 } else { 544 size = 3; 545 } 546 sLast = strokewidth * size; 547 if (typeLast === 2) { 548 sLast *= 0.5; 549 minlen += strokewidth * size; 550 } else if (typeLast === 3) { 551 sLast = strokewidth; 552 minlen += strokewidth; 553 } else { 554 minlen += strokewidth * size; 555 } 556 } 557 558 if (ev_fa && 559 el.board.renderer.type !== 'vml') { 560 if (d >= minlen) { 561 d1x = (c2.scrCoords[1] - c1.scrCoords[1]) * sFirst / d; 562 d1y = (c2.scrCoords[2] - c1.scrCoords[2]) * sFirst / d; 563 } else { 564 sFirst = 0; 565 } 566 } 567 568 if (ev_la && 569 el.board.renderer.type !== 'vml') { 570 571 if (d >= minlen) { 572 d2x = (c2.scrCoords[1] - c1.scrCoords[1]) * sLast / d; 573 d2y = (c2.scrCoords[2] - c1.scrCoords[2]) * sLast / d; 574 } else { 575 sLast = 0.0; 576 } 577 } 578 } 579 580 return { 581 c1: c1, 582 c2: c2, 583 d1x: d1x, 584 d1y: d1y, 585 d2x: d2x, 586 d2y: d2y, 587 sFirst: sFirst, 588 sLast: sLast 589 }; 590 }, 591 592 /** 593 * Update the line endings (linecap) of a straight line from its attribute 594 * 'linecap'. 595 * Possible values for the attribute 'linecap' are: 'butt', 'round', 'square'. 596 * The default value is 'butt'. Not available for VML renderer. 597 * 598 * @param {JXG.Line} element A arbitrary line. 599 * @see Line 600 * @see JXG.Line 601 * @see JXG.AbstractRenderer#updateLine 602 */ 603 setLineCap: function(el) { /* stub */ }, 604 605 /** 606 * Creates a rendering node for ticks added to a line. 607 * @param {JXG.Line} el A arbitrary line. 608 * @see Line 609 * @see Ticks 610 * @see JXG.Line 611 * @see JXG.Ticks 612 * @see JXG.AbstractRenderer#updateTicks 613 */ 614 drawTicks: function (el) { 615 el.rendNode = this.appendChildPrim(this.createPrim('path', el.id), Type.evaluate(el.visProp.layer)); 616 this.appendNodesToElement(el, 'path'); 617 }, 618 619 /** 620 * Update {@link Ticks} on a {@link JXG.Line}. This method is only a stub and has to be implemented 621 * in any descendant renderer class. 622 * @param {JXG.Ticks} element Reference of a ticks object that has to be updated. 623 * @see Line 624 * @see Ticks 625 * @see JXG.Line 626 * @see JXG.Ticks 627 * @see JXG.AbstractRenderer#drawTicks 628 */ 629 updateTicks: function (element) { /* stub */ }, 630 631 /* ************************** 632 * Curves 633 * **************************/ 634 635 /** 636 * Draws a {@link JXG.Curve} on the {@link JXG.Board}. 637 * @param {JXG.Curve} el Reference to a graph object, that has to be plotted. 638 * @see Curve 639 * @see JXG.Curve 640 * @see JXG.AbstractRenderer#updateCurve 641 */ 642 drawCurve: function (el) { 643 el.rendNode = this.appendChildPrim(this.createPrim('path', el.id), Type.evaluate(el.visProp.layer)); 644 this.appendNodesToElement(el, 'path'); 645 if (el.numberPoints > 1) { 646 this.makeArrows(el); 647 } 648 this._updateVisual(el, {shadow: true}, true); 649 this.updateCurve(el); 650 }, 651 652 /** 653 * Updates visual appearance of the renderer element assigned to the given {@link JXG.Curve}. 654 * @param {JXG.Curve} el Reference to a {@link JXG.Curve} object, that has to be updated. 655 * @see Curve 656 * @see JXG.Curve 657 * @see JXG.AbstractRenderer#drawCurve 658 */ 659 updateCurve: function (el) { 660 var w = Type.evaluate(el.visProp.strokewidth), 661 size, ev_fa, ev_la; 662 663 if (Type.evaluate(el.visProp.handdrawing)) { 664 this.updatePathPrim(el.rendNode, this.updatePathStringBezierPrim(el), el.board); 665 } else { 666 this.updatePathPrim(el.rendNode, this.updatePathStringPrim(el), el.board); 667 } 668 669 if (el.numberPoints > 1) { 670 this.makeArrows(el); 671 672 ev_fa = Type.evaluate(el.visProp.firstarrow); 673 if (ev_fa) { 674 if (Type.exists(ev_fa.size)) { 675 size = Type.evaluate(ev_fa.size); 676 } else { 677 size = 3; 678 } 679 680 this._setArrowWidth(el.rendNodeTriangleStart, w, el.rendNode, size); 681 } 682 ev_la = Type.evaluate(el.visProp.lastarrow); 683 if (ev_la) { 684 if (Type.exists(ev_la.size)) { 685 size = Type.evaluate(ev_la.size); 686 } else { 687 size = 3; 688 } 689 this._setArrowWidth(el.rendNodeTriangleEnd, w, el.rendNode, size); 690 } 691 } 692 this._updateVisual(el); 693 694 }, 695 696 /* ************************** 697 * Circle related stuff 698 * **************************/ 699 700 /** 701 * Draws a {@link JXG.Circle} 702 * @param {JXG.Circle} el Reference to a {@link JXG.Circle} object that has to be drawn. 703 * @see Circle 704 * @see JXG.Circle 705 * @see JXG.AbstractRenderer#updateEllipse 706 */ 707 drawEllipse: function (el) { 708 el.rendNode = this.appendChildPrim(this.createPrim('ellipse', el.id), 709 Type.evaluate(el.visProp.layer)); 710 this.appendNodesToElement(el, 'ellipse'); 711 this.updateEllipse(el); 712 }, 713 714 /** 715 * Updates visual appearance of a given {@link JXG.Circle} on the {@link JXG.Board}. 716 * @param {JXG.Circle} el Reference to a {@link JXG.Circle} object, that has to be updated. 717 * @see Circle 718 * @see JXG.Circle 719 * @see JXG.AbstractRenderer#drawEllipse 720 */ 721 updateEllipse: function (el) { 722 this._updateVisual(el); 723 724 var radius = el.Radius(); 725 726 if (radius > 0.0 && 727 Math.abs(el.center.coords.usrCoords[0]) > Mat.eps && 728 !isNaN(radius + el.center.coords.scrCoords[1] + el.center.coords.scrCoords[2]) && 729 radius * el.board.unitX < 2000000) { 730 this.updateEllipsePrim(el.rendNode, el.center.coords.scrCoords[1], 731 el.center.coords.scrCoords[2], 732 (radius * el.board.unitX), 733 (radius * el.board.unitY)); 734 } 735 }, 736 737 738 /* ************************** 739 * Polygon related stuff 740 * **************************/ 741 742 /** 743 * Draws a {@link JXG.Polygon} on the {@link JXG.Board}. 744 * @param {JXG.Polygon} el Reference to a Polygon object, that is to be drawn. 745 * @see Polygon 746 * @see JXG.Polygon 747 * @see JXG.AbstractRenderer#updatePolygon 748 */ 749 drawPolygon: function (el) { 750 el.rendNode = this.appendChildPrim(this.createPrim('polygon', el.id), 751 Type.evaluate(el.visProp.layer)); 752 this.appendNodesToElement(el, 'polygon'); 753 this.updatePolygon(el); 754 }, 755 756 /** 757 * Updates properties of a {@link JXG.Polygon}'s rendering node. 758 * @param {JXG.Polygon} el Reference to a {@link JXG.Polygon} object, that has to be updated. 759 * @see Polygon 760 * @see JXG.Polygon 761 * @see JXG.AbstractRenderer#drawPolygon 762 */ 763 updatePolygon: function (el) { 764 var i; 765 //, len, polIsReal; 766 767 // here originally strokecolor wasn't updated but strokewidth was 768 // but if there's no strokecolor i don't see why we should update strokewidth. 769 this._updateVisual(el, {stroke: true, dash: true}); 770 this.updatePolygonPrim(el.rendNode, el); 771 }, 772 773 /* ************************** 774 * Text related stuff 775 * **************************/ 776 777 /** 778 * Shows a small copyright notice in the top left corner of the board. 779 * @param {String} str The copyright notice itself 780 * @param {Number} fontsize Size of the font the copyright notice is written in 781 */ 782 displayCopyright: function (str, fontsize) { /* stub */ }, 783 784 /** 785 * An internal text is a {@link JXG.Text} element which is drawn using only 786 * the given renderer but no HTML. This method is only a stub, the drawing 787 * is done in the special renderers. 788 * @param {JXG.Text} element Reference to a {@link JXG.Text} object 789 * @see Text 790 * @see JXG.Text 791 * @see JXG.AbstractRenderer#updateInternalText 792 * @see JXG.AbstractRenderer#drawText 793 * @see JXG.AbstractRenderer#updateText 794 * @see JXG.AbstractRenderer#updateTextStyle 795 */ 796 drawInternalText: function (element) { /* stub */ }, 797 798 /** 799 * Updates visual properties of an already existing {@link JXG.Text} element. 800 * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be updated. 801 * @see Text 802 * @see JXG.Text 803 * @see JXG.AbstractRenderer#drawInternalText 804 * @see JXG.AbstractRenderer#drawText 805 * @see JXG.AbstractRenderer#updateText 806 * @see JXG.AbstractRenderer#updateTextStyle 807 */ 808 updateInternalText: function (element) { /* stub */ }, 809 810 /** 811 * Displays a {@link JXG.Text} on the {@link JXG.Board} by putting a HTML div over it. 812 * @param {JXG.Text} el Reference to an {@link JXG.Text} object, that has to be displayed 813 * @see Text 814 * @see JXG.Text 815 * @see JXG.AbstractRenderer#drawInternalText 816 * @see JXG.AbstractRenderer#updateText 817 * @see JXG.AbstractRenderer#updateInternalText 818 * @see JXG.AbstractRenderer#updateTextStyle 819 */ 820 drawText: function (el) { 821 var node, z, level; 822 823 if (Type.evaluate(el.visProp.display) === 'html' && Env.isBrowser && this.type !== 'no') { 824 node = this.container.ownerDocument.createElement('div'); 825 //node = this.container.ownerDocument.createElementNS('http://www.w3.org/1999/xhtml', 'div'); // 826 node.style.position = 'absolute'; 827 node.className = Type.evaluate(el.visProp.cssclass); 828 829 level = Type.evaluate(el.visProp.layer); 830 if (!Type.exists(level)) { // trace nodes have level not set 831 level = 0; 832 } 833 834 if (this.container.style.zIndex === '') { 835 z = 0; 836 } else { 837 z = parseInt(this.container.style.zIndex, 10); 838 } 839 840 node.style.zIndex = z + level; 841 this.container.appendChild(node); 842 843 node.setAttribute('id', this.container.id + '_' + el.id); 844 } else { 845 node = this.drawInternalText(el); 846 } 847 848 el.rendNode = node; 849 el.htmlStr = ''; 850 this.updateText(el); 851 }, 852 853 /** 854 * Updates visual properties of an already existing {@link JXG.Text} element. 855 * @param {JXG.Text} el Reference to an {@link JXG.Text} object, that has to be updated. 856 * @see Text 857 * @see JXG.Text 858 * @see JXG.AbstractRenderer#drawText 859 * @see JXG.AbstractRenderer#drawInternalText 860 * @see JXG.AbstractRenderer#updateInternalText 861 * @see JXG.AbstractRenderer#updateTextStyle 862 */ 863 updateText: function (el) { 864 var content = el.plaintext, v, c, 865 parentNode, 866 ax, ay; 867 868 if (el.visPropCalc.visible) { 869 this.updateTextStyle(el, false); 870 871 if (Type.evaluate(el.visProp.display) === 'html' && this.type !== 'no') { 872 // Set the position 873 if (!isNaN(el.coords.scrCoords[1] + el.coords.scrCoords[2])) { 874 875 // Horizontal 876 c = el.coords.scrCoords[1]; 877 // webkit seems to fail for extremely large values for c. 878 c = Math.abs(c) < 1000000 ? c : 1000000; 879 ax = el.getAnchorX(); 880 881 if (ax === 'right') { 882 v = Math.floor(el.board.canvasWidth - c); 883 } else if (ax === 'middle') { 884 v = Math.floor(c - 0.5 * el.size[0]); 885 } else { // 'left' 886 v = Math.floor(c); 887 } 888 889 // This may be useful for foreignObj. 890 //if (window.devicePixelRatio !== undefined) { 891 //v *= window.devicePixelRatio; 892 //} 893 894 if (el.visPropOld.left !== (ax + v)) { 895 if (ax === 'right') { 896 el.rendNode.style.right = v + 'px'; 897 el.rendNode.style.left = 'auto'; 898 } else { 899 el.rendNode.style.left = v + 'px'; 900 el.rendNode.style.right = 'auto'; 901 } 902 el.visPropOld.left = ax + v; 903 } 904 905 // Vertical 906 c = el.coords.scrCoords[2] + this.vOffsetText; 907 c = Math.abs(c) < 1000000 ? c : 1000000; 908 ay = el.getAnchorY(); 909 910 if (ay === 'bottom') { 911 v = Math.floor(el.board.canvasHeight - c); 912 } else if (ay === 'middle') { 913 v = Math.floor(c - 0.5 * el.size[1]); 914 } else { // top 915 v = Math.floor(c); 916 } 917 918 // This may be useful for foreignObj. 919 //if (window.devicePixelRatio !== undefined) { 920 //v *= window.devicePixelRatio; 921 //} 922 923 if (el.visPropOld.top !== (ay + v)) { 924 if (ay === 'bottom') { 925 el.rendNode.style.top = 'auto'; 926 el.rendNode.style.bottom = v + 'px'; 927 } else { 928 el.rendNode.style.bottom = 'auto'; 929 el.rendNode.style.top = v + 'px'; 930 } 931 el.visPropOld.top = ay + v; 932 } 933 } 934 935 // Set the content 936 if (el.htmlStr !== content) { 937 try { 938 el.rendNode.innerHTML = content; 939 } catch (e) { 940 // Setting innerHTML sometimes fails in IE8. A workaround is to 941 // take the node off the DOM, assign innerHTML, then append back. 942 // Works for text elements as they are absolutely positioned. 943 parentNode = el.rendNode.parentNode; 944 el.rendNode.parentNode.removeChild(el.rendNode); 945 el.rendNode.innerHTML = content; 946 parentNode.appendChild(el.rendNode); 947 } 948 el.htmlStr = content; 949 950 if (Type.evaluate(el.visProp.usemathjax)) { 951 // typesetting directly might not work because mathjax was not loaded completely 952 // see http://www.mathjax.org/docs/1.1/typeset.html 953 try { 954 MathJax.Hub.Queue(['Typeset', MathJax.Hub, el.rendNode]); 955 } catch (e) { 956 JXG.debug('MathJax (not yet) loaded'); 957 } 958 } else if (Type.evaluate(el.visProp.useasciimathml)) { 959 // This is not a constructor. 960 // See http://www1.chapman.edu/~jipsen/mathml/asciimath.html for more information 961 // about AsciiMathML and the project's source code. 962 try { 963 AMprocessNode(el.rendNode, false); 964 } catch (e) { 965 JXG.debug('AsciiMathML (not yet) loaded'); 966 } 967 } 968 } 969 this.transformImage(el, el.transformations); 970 } else { 971 this.updateInternalText(el); 972 } 973 } 974 }, 975 976 /** 977 * Converts string containing CSS properties into 978 * array with key-value pair objects. 979 * 980 * @example 981 * "color:blue; background-color:yellow" is converted to 982 * [{'color': 'blue'}, {'backgroundColor': 'yellow'}] 983 * 984 * @param {String} cssString String containing CSS properties 985 * @return {Array} Array of CSS key-value pairs 986 */ 987 _css2js: function(cssString) { 988 var pairs = [], 989 i, len, key, val, s, 990 list = Type.trim(cssString).replace(/;$/, '').split(";"); 991 992 len = list.length; 993 for (i = 0; i < len; ++i) { 994 if (Type.trim(list[i]) !== '') { 995 s = list[i].split(':'); 996 key = Type.trim(s[0].replace(/-([a-z])/gi, function(match, char) { return char.toUpperCase(); })); 997 val = Type.trim(s[1]); 998 pairs.push({'key': key, 'val': val}); 999 } 1000 } 1001 return pairs; 1002 1003 }, 1004 1005 /** 1006 * Updates font-size, color and opacity propertiey and CSS style properties of a {@link JXG.Text} node. 1007 * This function is also called by highlight() and nohighlight(). 1008 * @param {JXG.Text} el Reference to the {@link JXG.Text} object, that has to be updated. 1009 * @param {Boolean} doHighlight 1010 * @see Text 1011 * @see JXG.Text 1012 * @see JXG.AbstractRenderer#drawText 1013 * @see JXG.AbstractRenderer#drawInternalText 1014 * @see JXG.AbstractRenderer#updateText 1015 * @see JXG.AbstractRenderer#updateInternalText 1016 * @see JXG.AbstractRenderer#updateInternalTextStyle 1017 */ 1018 updateTextStyle: function (el, doHighlight) { 1019 var fs, so, sc, css, node, 1020 ev = el.visProp, 1021 display = Env.isBrowser ? ev.display : 'internal', 1022 nodeList = ['rendNode', 'rendNodeTag', 'rendNodeLabel'], 1023 lenN = nodeList.length, 1024 cssList, prop, style, cssString, 1025 styleList = ['cssdefaultstyle', 'cssstyle'], 1026 lenS = styleList.length; 1027 1028 if (doHighlight) { 1029 sc = ev.highlightstrokecolor; 1030 so = ev.highlightstrokeopacity; 1031 css = ev.highlightcssclass; 1032 } else { 1033 sc = ev.strokecolor; 1034 so = ev.strokeopacity; 1035 css = ev.cssclass; 1036 } 1037 1038 // This part is executed for all text elements except internal texts in canvas. 1039 // HTML-texts or internal texts in SVG or VML. 1040 // HTML internal 1041 // SVG + + 1042 // VML + + 1043 // canvas + - 1044 // no - - 1045 if ((this.type !== 'no') && 1046 (display === 'html' || this.type !== 'canvas') 1047 ) { 1048 for (style = 0; style < lenS; style++) { 1049 // First set cssString to 1050 // ev.cssdefaultstyle of ev.highlightcssdefaultstyle, 1051 // then to 1052 // ev.cssstyle of ev.highlightcssstyle 1053 cssString = Type.evaluate(ev[(doHighlight ? 'highlight' : '') + styleList[style]]); 1054 if (cssString !== '' && 1055 el.visPropOld[styleList[style]] !== cssString) { 1056 cssList = this._css2js(cssString); 1057 for (node = 0; node < lenN; node++) { 1058 if (Type.exists(el[nodeList[node]])) { 1059 for (prop in cssList) { 1060 if (cssList.hasOwnProperty(prop)) { 1061 el[nodeList[node]].style[cssList[prop].key] = cssList[prop].val; 1062 } 1063 } 1064 } 1065 } 1066 el.visPropOld[styleList[style]] = cssString; 1067 } 1068 } 1069 1070 fs = Type.evaluate(ev.fontsize); 1071 if (el.visPropOld.fontsize !== fs) { 1072 el.needsSizeUpdate = true; 1073 try { 1074 for (node = 0; node < lenN; node++) { 1075 if (Type.exists(el[nodeList[node]])) { 1076 el[nodeList[node]].style.fontSize = fs + 'px'; 1077 } 1078 } 1079 } catch (e) { 1080 // IE needs special treatment. 1081 for (node = 0; node < lenN; node++) { 1082 if (Type.exists(el[nodeList[node]])) { 1083 el[nodeList[node]].style.fontSize = fs; 1084 } 1085 } 1086 } 1087 el.visPropOld.fontsize = fs; 1088 } 1089 } 1090 1091 this.setObjectTransition(el); 1092 if (display === 'html' && this.type !== 'no') { 1093 // Set new CSS class 1094 if (el.visPropOld.cssclass !== css) { 1095 el.rendNode.className = css; 1096 el.visPropOld.cssclass = css; 1097 el.needsSizeUpdate = true; 1098 } 1099 this.setObjectStrokeColor(el, sc, so); 1100 } else { 1101 this.updateInternalTextStyle(el, sc, so); 1102 } 1103 1104 return this; 1105 }, 1106 1107 /** 1108 * Set color and opacity of internal texts. 1109 * This method is used for Canvas and VML. 1110 * SVG needs its own version. 1111 * @private 1112 * @see JXG.AbstractRenderer#updateTextStyle 1113 * @see JXG.SVGRenderer#updateInternalTextStyle 1114 */ 1115 updateInternalTextStyle: function (el, strokeColor, strokeOpacity) { 1116 this.setObjectStrokeColor(el, strokeColor, strokeOpacity); 1117 }, 1118 1119 /* ************************** 1120 * Image related stuff 1121 * **************************/ 1122 1123 /** 1124 * Draws an {@link JXG.Image} on a board; This is just a template that has to be implemented by special 1125 * renderers. 1126 * @param {JXG.Image} element Reference to the image object that is to be drawn 1127 * @see Image 1128 * @see JXG.Image 1129 * @see JXG.AbstractRenderer#updateImage 1130 */ 1131 drawImage: function (element) { /* stub */ }, 1132 1133 /** 1134 * Updates the properties of an {@link JXG.Image} element. 1135 * @param {JXG.Image} el Reference to an {@link JXG.Image} object, that has to be updated. 1136 * @see Image 1137 * @see JXG.Image 1138 * @see JXG.AbstractRenderer#drawImage 1139 */ 1140 updateImage: function (el) { 1141 this.updateRectPrim(el.rendNode, el.coords.scrCoords[1], 1142 el.coords.scrCoords[2] - el.size[1], el.size[0], el.size[1]); 1143 1144 this.updateImageURL(el); 1145 this.transformImage(el, el.transformations); 1146 this._updateVisual(el, {stroke: true, dash: true}, true); 1147 }, 1148 1149 /** 1150 * Multiplication of transformations without updating. That means, at that point it is expected that the 1151 * matrices contain numbers only. First, the origin in user coords is translated to <tt>(0,0)</tt> in screen 1152 * coords. Then, the stretch factors are divided out. After the transformations in user coords, the stretch 1153 * factors are multiplied in again, and the origin in user coords is translated back to its position. This 1154 * method does not have to be implemented in a new renderer. 1155 * @param {JXG.GeometryElement} el A JSXGraph element. We only need its board property. 1156 * @param {Array} transformations An array of JXG.Transformations. 1157 * @returns {Array} A matrix represented by a two dimensional array of numbers. 1158 * @see JXG.AbstractRenderer#transformImage 1159 */ 1160 joinTransforms: function (el, transformations) { 1161 var i, 1162 ox = el.board.origin.scrCoords[1], 1163 oy = el.board.origin.scrCoords[2], 1164 ux = el.board.unitX, 1165 uy = el.board.unitY, 1166 // Translate to 0,0 in screen coords 1167 /* 1168 m = [[1, 0, 0], [0, 1, 0], [0, 0, 1]], 1169 mpre1 = [[1, 0, 0], 1170 [-ox, 1, 0], 1171 [-oy, 0, 1]], 1172 // Scale 1173 mpre2 = [[1, 0, 0], 1174 [0, 1 / ux, 0], 1175 [0, 0, -1 / uy]], 1176 // Scale back 1177 mpost2 = [[1, 0, 0], 1178 [0, ux, 0], 1179 [0, 0, -uy]], 1180 // Translate back 1181 mpost1 = [[1, 0, 0], 1182 [ox, 1, 0], 1183 [oy, 0, 1]], 1184 */ 1185 len = transformations.length, 1186 // Translate to 0,0 in screen coords and then scale 1187 m = [[1, 0, 0], 1188 [-ox / ux, 1 / ux, 0], 1189 [ oy / uy, 0, -1 / uy]]; 1190 1191 for (i = 0; i < len; i++) { 1192 //m = Mat.matMatMult(mpre1, m); 1193 //m = Mat.matMatMult(mpre2, m); 1194 m = Mat.matMatMult(transformations[i].matrix, m); 1195 //m = Mat.matMatMult(mpost2, m); 1196 //m = Mat.matMatMult(mpost1, m); 1197 } 1198 // Scale back and then translate back 1199 m = Mat.matMatMult([[1, 0, 0], 1200 [ox, ux, 0], 1201 [oy, 0, -uy]], m); 1202 return m; 1203 }, 1204 1205 /** 1206 * Applies transformations on images and text elements. This method is just a stub and has to be implemented in 1207 * all descendant classes where text and image transformations are to be supported. 1208 * @param {JXG.Image|JXG.Text} element A {@link JXG.Image} or {@link JXG.Text} object. 1209 * @param {Array} transformations An array of {@link JXG.Transformation} objects. This is usually the 1210 * transformations property of the given element <tt>el</tt>. 1211 */ 1212 transformImage: function (element, transformations) { /* stub */ }, 1213 1214 /** 1215 * If the URL of the image is provided by a function the URL has to be updated during updateImage() 1216 * @param {JXG.Image} element Reference to an image object. 1217 * @see JXG.AbstractRenderer#updateImage 1218 */ 1219 updateImageURL: function (element) { /* stub */ }, 1220 1221 /** 1222 * Updates CSS style properties of a {@link JXG.Image} node. 1223 * In SVGRenderer opacity is the only available style element. 1224 * This function is called by highlight() and nohighlight(). 1225 * This function works for VML. 1226 * It does not work for Canvas. 1227 * SVGRenderer overwrites this method. 1228 * @param {JXG.Text} el Reference to the {@link JXG.Image} object, that has to be updated. 1229 * @param {Boolean} doHighlight 1230 * @see Image 1231 * @see JXG.Image 1232 * @see JXG.AbstractRenderer#highlight 1233 * @see JXG.AbstractRenderer#noHighlight 1234 */ 1235 updateImageStyle: function (el, doHighlight) { 1236 el.rendNode.className = Type.evaluate(doHighlight ? el.visProp.highlightcssclass : el.visProp.cssclass); 1237 }, 1238 1239 1240 /* ************************** 1241 * Render primitive objects 1242 * **************************/ 1243 1244 /** 1245 * Appends a node to a specific layer level. This is just an abstract method and has to be implemented 1246 * in all renderers that want to use the <tt>createPrim</tt> model to draw. 1247 * @param {Node} node A DOM tree node. 1248 * @param {Number} level The layer the node is attached to. This is the index of the layer in 1249 * {@link JXG.SVGRenderer#layer} or the <tt>z-index</tt> style property of the node in VMLRenderer. 1250 */ 1251 appendChildPrim: function (node, level) { /* stub */ }, 1252 1253 /** 1254 * Stores the rendering nodes. This is an abstract method which has to be implemented in all renderers that use 1255 * the <tt>createPrim</tt> method. 1256 * @param {JXG.GeometryElement} element A JSXGraph element. 1257 * @param {String} type The XML node name. Only used in VMLRenderer. 1258 */ 1259 appendNodesToElement: function (element, type) { /* stub */ }, 1260 1261 /** 1262 * Creates a node of a given type with a given id. 1263 * @param {String} type The type of the node to create. 1264 * @param {String} id Set the id attribute to this. 1265 * @returns {Node} Reference to the created node. 1266 */ 1267 createPrim: function (type, id) { 1268 /* stub */ 1269 return null; 1270 }, 1271 1272 /** 1273 * Removes an element node. Just a stub. 1274 * @param {Node} node The node to remove. 1275 */ 1276 remove: function (node) { /* stub */ }, 1277 1278 /** 1279 * Can be used to create the nodes to display arrows. This is an abstract method which has to be implemented 1280 * in any descendant renderer. 1281 * @param {JXG.GeometryElement} element The element the arrows are to be attached to. 1282 */ 1283 makeArrows: function (element) { /* stub */ }, 1284 1285 /** 1286 * Updates width of an arrow DOM node. Used in 1287 * @param {Node} node The arrow node. 1288 * @param {Number} width 1289 * @param {Node} parentNode Used in IE only 1290 */ 1291 _setArrowWidth: function(node, width, parentNode) { /* stub */}, 1292 1293 /** 1294 * Updates an ellipse node primitive. This is an abstract method which has to be implemented in all renderers 1295 * that use the <tt>createPrim</tt> method. 1296 * @param {Node} node Reference to the node. 1297 * @param {Number} x Centre X coordinate 1298 * @param {Number} y Centre Y coordinate 1299 * @param {Number} rx The x-axis radius. 1300 * @param {Number} ry The y-axis radius. 1301 */ 1302 updateEllipsePrim: function (node, x, y, rx, ry) { /* stub */ }, 1303 1304 /** 1305 * Refreshes a line node. This is an abstract method which has to be implemented in all renderers that use 1306 * the <tt>createPrim</tt> method. 1307 * @param {Node} node The node to be refreshed. 1308 * @param {Number} p1x The first point's x coordinate. 1309 * @param {Number} p1y The first point's y coordinate. 1310 * @param {Number} p2x The second point's x coordinate. 1311 * @param {Number} p2y The second point's y coordinate. 1312 * @param {JXG.Board} board 1313 */ 1314 updateLinePrim: function (node, p1x, p1y, p2x, p2y, board) { /* stub */ }, 1315 1316 /** 1317 * Updates a path element. This is an abstract method which has to be implemented in all renderers that use 1318 * the <tt>createPrim</tt> method. 1319 * @param {Node} node The path node. 1320 * @param {String} pathString A string formatted like e.g. <em>'M 1,2 L 3,1 L5,5'</em>. The format of the string 1321 * depends on the rendering engine. 1322 * @param {JXG.Board} board Reference to the element's board. 1323 */ 1324 updatePathPrim: function (node, pathString, board) { /* stub */ }, 1325 1326 /** 1327 * Builds a path data string to draw a point with a face other than <em>rect</em> and <em>circle</em>. Since 1328 * the format of such a string usually depends on the renderer this method 1329 * is only an abstract method. Therefore, it has to be implemented in the descendant renderer itself unless 1330 * the renderer does not use the createPrim interface but the draw* interfaces to paint. 1331 * @param {JXG.Point} element The point element 1332 * @param {Number} size A positive number describing the size. Usually the half of the width and height of 1333 * the drawn point. 1334 * @param {String} type A string describing the point's face. This method only accepts the shortcut version of 1335 * each possible face: <tt>x, +, <>, ^, v, >, < 1336 */ 1337 updatePathStringPoint: function (element, size, type) { /* stub */ }, 1338 1339 /** 1340 * Builds a path data string from a {@link JXG.Curve} element. Since the path data strings heavily depend on the 1341 * underlying rendering technique this method is just a stub. Although such a path string is of no use for the 1342 * CanvasRenderer, this method is used there to draw a path directly. 1343 * @param element 1344 */ 1345 updatePathStringPrim: function (element) { /* stub */ }, 1346 1347 /** 1348 * Builds a path data string from a {@link JXG.Curve} element such that the curve looks like hand drawn. Since 1349 * the path data strings heavily depend on the underlying rendering technique this method is just a stub. 1350 * Although such a path string is of no use for the CanvasRenderer, this method is used there to draw a path 1351 * directly. 1352 * @param element 1353 */ 1354 updatePathStringBezierPrim: function (element) { /* stub */ }, 1355 1356 1357 /** 1358 * Update a polygon primitive. 1359 * @param {Node} node 1360 * @param {JXG.Polygon} element A JSXGraph element of type {@link JXG.Polygon} 1361 */ 1362 updatePolygonPrim: function (node, element) { /* stub */ }, 1363 1364 /** 1365 * Update a rectangle primitive. This is used only for points with face of type 'rect'. 1366 * @param {Node} node The node yearning to be updated. 1367 * @param {Number} x x coordinate of the top left vertex. 1368 * @param {Number} y y coordinate of the top left vertex. 1369 * @param {Number} w Width of the rectangle. 1370 * @param {Number} h The rectangle's height. 1371 */ 1372 updateRectPrim: function (node, x, y, w, h) { /* stub */ }, 1373 1374 /* ************************** 1375 * Set Attributes 1376 * **************************/ 1377 1378 /** 1379 * Sets a node's attribute. 1380 * @param {Node} node The node that is to be updated. 1381 * @param {String} key Name of the attribute. 1382 * @param {String} val New value for the attribute. 1383 */ 1384 setPropertyPrim: function (node, key, val) { /* stub */ }, 1385 1386 /** 1387 * Shows or hides an element on the canvas; Only a stub, requires implementation in the derived renderer. 1388 * @param {JXG.GeometryElement} element Reference to the object that has to appear. 1389 * @param {Boolean} value true to show the element, false to hide the element. 1390 */ 1391 display: function (element, value) { 1392 if (element) { 1393 element.visPropOld.visible = value; 1394 } 1395 }, 1396 1397 /** 1398 * Shows a hidden element on the canvas; Only a stub, requires implementation in the derived renderer. 1399 * 1400 * Please use JXG.AbstractRenderer#display instead 1401 * @param {JXG.GeometryElement} element Reference to the object that has to appear. 1402 * @see JXG.AbstractRenderer#hide 1403 * @deprecated 1404 */ 1405 show: function (element) { /* stub */ }, 1406 1407 /** 1408 * Hides an element on the canvas; Only a stub, requires implementation in the derived renderer. 1409 * 1410 * Please use JXG.AbstractRenderer#display instead 1411 * @param {JXG.GeometryElement} element Reference to the geometry element that has to disappear. 1412 * @see JXG.AbstractRenderer#show 1413 * @deprecated 1414 */ 1415 hide: function (element) { /* stub */ }, 1416 1417 /** 1418 * Sets the buffering as recommended by SVGWG. Until now only Opera supports this and will be ignored by other 1419 * browsers. Although this feature is only supported by SVG we have this method in {@link JXG.AbstractRenderer} 1420 * because it is called from outside the renderer. 1421 * @param {Node} node The SVG DOM Node which buffering type to update. 1422 * @param {String} type Either 'auto', 'dynamic', or 'static'. For an explanation see 1423 * {@link http://www.w3.org/TR/SVGTiny12/painting.html#BufferedRenderingProperty}. 1424 */ 1425 setBuffering: function (node, type) { /* stub */ }, 1426 1427 /** 1428 * Sets an element's dash style. 1429 * @param {JXG.GeometryElement} element An JSXGraph element. 1430 */ 1431 setDashStyle: function (element) { /* stub */ }, 1432 1433 /** 1434 * Puts an object into draft mode, i.e. it's visual appearance will be changed. For GEONE<sub>x</sub>T backwards 1435 * compatibility. 1436 * @param {JXG.GeometryElement} el Reference of the object that is in draft mode. 1437 */ 1438 setDraft: function (el) { 1439 if (!Type.evaluate(el.visProp.draft)) { 1440 return; 1441 } 1442 var draftColor = el.board.options.elements.draft.color, 1443 draftOpacity = el.board.options.elements.draft.opacity; 1444 1445 this.setObjectTransition(el); 1446 if (el.type === Const.OBJECT_TYPE_POLYGON) { 1447 this.setObjectFillColor(el, draftColor, draftOpacity); 1448 } else { 1449 if (el.elementClass === Const.OBJECT_CLASS_POINT) { 1450 this.setObjectFillColor(el, draftColor, draftOpacity); 1451 } else { 1452 this.setObjectFillColor(el, 'none', 0); 1453 } 1454 this.setObjectStrokeColor(el, draftColor, draftOpacity); 1455 this.setObjectStrokeWidth(el, el.board.options.elements.draft.strokeWidth); 1456 } 1457 }, 1458 1459 /** 1460 * Puts an object from draft mode back into normal mode. 1461 * @param {JXG.GeometryElement} el Reference of the object that no longer is in draft mode. 1462 */ 1463 removeDraft: function (el) { 1464 this.setObjectTransition(el); 1465 if (el.type === Const.OBJECT_TYPE_POLYGON) { 1466 this.setObjectFillColor(el, 1467 el.visProp.fillcolor, 1468 el.visProp.fillopacity); 1469 } else { 1470 if (el.type === Const.OBJECT_CLASS_POINT) { 1471 this.setObjectFillColor(el, 1472 el.visProp.fillcolor, 1473 el.visProp.fillopacity); 1474 } 1475 this.setObjectStrokeColor(el, el.visProp.strokecolor, el.visProp.strokeopacity); 1476 this.setObjectStrokeWidth(el, el.visProp.strokewidth); 1477 } 1478 }, 1479 1480 /** 1481 * Sets up nodes for rendering a gradient fill. 1482 * @param element 1483 */ 1484 setGradient: function (element) { /* stub */ }, 1485 1486 /** 1487 * Updates the gradient fill. 1488 * @param {JXG.GeometryElement} element An JSXGraph element with an area that can be filled. 1489 */ 1490 updateGradient: function (element) { /* stub */ }, 1491 1492 /** 1493 * Sets the transition duration (in milliseconds) for fill color and stroke 1494 * color and opacity. 1495 * @param {JXG.GeometryElement} element Reference of the object that wants a 1496 * new transition duration. 1497 * @param {Number} duration (Optional) duration in milliseconds. If not given, 1498 * element.visProp.transitionDuration is taken. This is the default. 1499 */ 1500 setObjectTransition: function (element, duration) { /* stub */ }, 1501 1502 /** 1503 * Sets an objects fill color. 1504 * @param {JXG.GeometryElement} element Reference of the object that wants a new fill color. 1505 * @param {String} color Color in a HTML/CSS compatible format. If you don't want any fill color at all, choose 1506 * 'none'. 1507 * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1. 1508 */ 1509 setObjectFillColor: function (element, color, opacity) { /* stub */ }, 1510 1511 /** 1512 * Changes an objects stroke color to the given color. 1513 * @param {JXG.GeometryElement} element Reference of the {@link JXG.GeometryElement} that gets a new stroke 1514 * color. 1515 * @param {String} color Color value in a HTML compatible format, e.g. <strong>#00ff00</strong> or 1516 * <strong>green</strong> for green. 1517 * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1. 1518 */ 1519 setObjectStrokeColor: function (element, color, opacity) { /* stub */ }, 1520 1521 /** 1522 * Sets an element's stroke width. 1523 * @param {JXG.GeometryElement} element Reference to the geometry element. 1524 * @param {Number} width The new stroke width to be assigned to the element. 1525 */ 1526 setObjectStrokeWidth: function (element, width) { /* stub */ }, 1527 1528 /** 1529 * Sets the shadow properties to a geometry element. This method is only a stub, it is implemented in the actual 1530 * renderers. 1531 * @param {JXG.GeometryElement} element Reference to a geometry object, that should get a shadow 1532 */ 1533 setShadow: function (element) { /* stub */ }, 1534 1535 /** 1536 * Highlights an object, i.e. changes the current colors of the object to its highlighting colors 1537 * and highlighting stroke width. 1538 * @param {JXG.GeometryElement} el Reference of the object that will be highlighted. 1539 * @returns {JXG.AbstractRenderer} Reference to the renderer 1540 * @see JXG.AbstractRenderer#updateTextStyle 1541 */ 1542 highlight: function (el) { 1543 var i, ev = el.visProp, 1544 sw, obj; 1545 1546 this.setObjectTransition(el); 1547 if (!ev.draft) { 1548 if (el.type === Const.OBJECT_TYPE_POLYGON) { 1549 this.setObjectFillColor(el, 1550 ev.highlightfillcolor, 1551 ev.highlightfillopacity); 1552 for (i = 0; i < el.borders.length; i++) { 1553 this.setObjectStrokeColor(el.borders[i], 1554 el.borders[i].visProp.highlightstrokecolor, 1555 el.borders[i].visProp.highlightstrokeopacity); 1556 } 1557 } else { 1558 if (el.elementClass === Const.OBJECT_CLASS_TEXT) { 1559 this.updateTextStyle(el, true); 1560 } else if (el.type === Const.OBJECT_TYPE_IMAGE) { 1561 this.updateImageStyle(el, true); 1562 this.setObjectFillColor(el, 1563 ev.highlightfillcolor, 1564 ev.highlightfillopacity); 1565 } else { 1566 this.setObjectStrokeColor(el, ev.highlightstrokecolor, ev.highlightstrokeopacity); 1567 this.setObjectFillColor(el, 1568 ev.highlightfillcolor, 1569 ev.highlightfillopacity); 1570 } 1571 } 1572 if (ev.highlightstrokewidth) { 1573 sw = Math.max(Type.evaluate(ev.highlightstrokewidth), Type.evaluate(ev.strokewidth)); 1574 this.setObjectStrokeWidth(el, sw); 1575 if (el.elementClass === Const.OBJECT_CLASS_LINE) { 1576 obj = this.updateLineEndings(el, sw); 1577 this.makeArrows(el); 1578 this.updateArrowSize(el, obj); 1579 } 1580 } 1581 } 1582 1583 return this; 1584 }, 1585 1586 /** 1587 * Uses the normal colors of an object, i.e. the opposite of {@link JXG.AbstractRenderer#highlight}. 1588 * @param {JXG.GeometryElement} el Reference of the object that will get its normal colors. 1589 * @returns {JXG.AbstractRenderer} Reference to the renderer 1590 * @see JXG.AbstractRenderer#updateTextStyle 1591 */ 1592 noHighlight: function (el) { 1593 var i, ev = el.visProp, 1594 obj, sw; 1595 1596 this.setObjectTransition(el); 1597 if (!Type.evaluate(el.visProp.draft)) { 1598 if (el.type === Const.OBJECT_TYPE_POLYGON) { 1599 this.setObjectFillColor(el, 1600 ev.fillcolor, 1601 ev.fillopacity); 1602 for (i = 0; i < el.borders.length; i++) { 1603 this.setObjectStrokeColor(el.borders[i], 1604 el.borders[i].visProp.strokecolor, 1605 el.borders[i].visProp.strokeopacity); 1606 } 1607 } else { 1608 if (el.elementClass === Const.OBJECT_CLASS_TEXT) { 1609 this.updateTextStyle(el, false); 1610 } else if (el.type === Const.OBJECT_TYPE_IMAGE) { 1611 this.updateImageStyle(el, false); 1612 this.setObjectFillColor(el, 1613 ev.fillcolor, 1614 ev.fillopacity); 1615 } else { 1616 this.setObjectStrokeColor(el, 1617 ev.strokecolor, 1618 ev.strokeopacity); 1619 this.setObjectFillColor(el, 1620 ev.fillcolor, 1621 ev.fillopacity); 1622 } 1623 } 1624 1625 sw = Type.evaluate(ev.strokewidth); 1626 this.setObjectStrokeWidth(el, sw); 1627 if (el.elementClass === Const.OBJECT_CLASS_LINE) { 1628 obj = this.updateLineEndings(el, sw); 1629 this.makeArrows(el); 1630 this.updateArrowSize(el, obj); 1631 } 1632 1633 } 1634 1635 return this; 1636 }, 1637 1638 /* ************************** 1639 * renderer control 1640 * **************************/ 1641 1642 /** 1643 * Stop redraw. This method is called before every update, so a non-vector-graphics based renderer can use this 1644 * method to delete the contents of the drawing panel. This is an abstract method every descendant renderer 1645 * should implement, if appropriate. 1646 * @see JXG.AbstractRenderer#unsuspendRedraw 1647 */ 1648 suspendRedraw: function () { /* stub */ }, 1649 1650 /** 1651 * Restart redraw. This method is called after updating all the rendering node attributes. 1652 * @see JXG.AbstractRenderer#suspendRedraw 1653 */ 1654 unsuspendRedraw: function () { /* stub */ }, 1655 1656 /** 1657 * The tiny zoom bar shown on the bottom of a board (if showNavigation on board creation is true). 1658 * @param {JXG.Board} board Reference to a JSXGraph board. 1659 * @param {Object} attr Attributes of the navigation bar 1660 * 1661 */ 1662 drawZoomBar: function (board, attr) { 1663 var doc, 1664 node, 1665 cancelbubble = function (e) { 1666 if (!e) { 1667 e = window.event; 1668 } 1669 1670 if (e.stopPropagation) { 1671 // Non IE<=8 1672 e.stopPropagation(); 1673 } else { 1674 e.cancelBubble = true; 1675 } 1676 }, 1677 createButton = function (label, handler) { 1678 var button; 1679 1680 button = doc.createElement('span'); 1681 node.appendChild(button); 1682 button.appendChild(doc.createTextNode(label)); 1683 button.style.paddingLeft = '7px'; 1684 button.style.paddingRight = '7px'; 1685 1686 Env.addEvent(button, 'mouseover', function () { 1687 this.style.backgroundColor = attr.highlightfillcolor; 1688 }, button); 1689 Env.addEvent(button, 'mouseover', function () { 1690 this.style.backgroundColor = attr.highlightfillcolor; 1691 }, button); 1692 Env.addEvent(button, 'mouseout', function () { 1693 this.style.backgroundColor = attr.fillcolor; 1694 }, button); 1695 1696 Env.addEvent(button, 'click', function(e) { (Type.bind(handler, board))(); return false; }, board); 1697 // prevent the click from bubbling down to the board 1698 Env.addEvent(button, 'mouseup', cancelbubble, board); 1699 Env.addEvent(button, 'mousedown', cancelbubble, board); 1700 Env.addEvent(button, 'touchend', cancelbubble, board); 1701 Env.addEvent(button, 'touchstart', cancelbubble, board); 1702 }; 1703 1704 if (Env.isBrowser && this.type !== 'no') { 1705 doc = board.containerObj.ownerDocument; 1706 node = doc.createElement('div'); 1707 1708 node.setAttribute('id', board.containerObj.id + '_navigationbar'); 1709 1710 node.style.color = attr.strokecolor; 1711 node.style.backgroundColor = attr.fillcolor; 1712 node.style.padding = attr.padding; 1713 node.style.position = attr.position; 1714 node.style.fontSize = attr.fontsize; 1715 node.style.cursor = attr.cursor; 1716 node.style.zIndex = attr.zindex; 1717 board.containerObj.appendChild(node); 1718 node.style.right = attr.right; 1719 node.style.bottom = attr.bottom; 1720 1721 // For XHTML we need unicode instead of HTML entities 1722 1723 if (board.attr.showscreenshot) { 1724 createButton(board.attr.screenshot.symbol, function () { 1725 setTimeout(function() { 1726 board.renderer.screenshot(board, '', false); 1727 }, 330); 1728 }); 1729 } 1730 1731 if (board.attr.showreload) { 1732 // full reload circle: \u27F2 1733 // the board.reload() method does not exist during the creation 1734 // of this button. That's why this anonymous function wrapper is required. 1735 createButton('\u21BB', function () { 1736 board.reload(); 1737 }); 1738 } 1739 1740 if (board.attr.showcleartraces) { 1741 // clear traces symbol (otimes): \u27F2 1742 createButton('\u2297', function () { 1743 board.clearTraces(); 1744 }); 1745 } 1746 1747 if (board.attr.shownavigation) { 1748 if (board.attr.showzoom) { 1749 createButton('\u2013', board.zoomOut); 1750 createButton('o', board.zoom100); 1751 createButton('+', board.zoomIn); 1752 } 1753 createButton('\u2190', board.clickLeftArrow); 1754 createButton('\u2193', board.clickUpArrow); 1755 createButton('\u2191', board.clickDownArrow); 1756 createButton('\u2192', board.clickRightArrow); 1757 } 1758 } 1759 }, 1760 1761 /** 1762 * Wrapper for getElementById for maybe other renderers which elements are not directly accessible by DOM 1763 * methods like document.getElementById(). 1764 * @param {String} id Unique identifier for element. 1765 * @returns {Object} Reference to a JavaScript object. In case of SVG/VMLRenderer it's a reference to a SVG/VML 1766 * node. 1767 */ 1768 getElementById: function (id) { 1769 if (Type.exists(this.container)) { 1770 return this.container.ownerDocument.getElementById(this.container.id + '_' + id); 1771 } 1772 return ''; 1773 }, 1774 1775 /** 1776 * Remove an element and provide a function that inserts it into its original position. This method 1778 * @author KeeKim Heng, Google Web Developer 1779 * @param {Element} el The element to be temporarily removed 1780 * @returns {Function} A function that inserts the element into its original position 1781 */ 1782 removeToInsertLater: function (el) { 1783 var parentNode = el.parentNode, 1784 nextSibling = el.nextSibling; 1785 1786 parentNode.removeChild(el); 1787 1788 return function () { 1789 if (nextSibling) { 1790 parentNode.insertBefore(el, nextSibling); 1791 } else { 1792 parentNode.appendChild(el); 1793 } 1794 }; 1795 }, 1796 1797 /** 1798 * Resizes the rendering element 1799 * @param {Number} w New width 1800 * @param {Number} h New height 1801 */ 1802 resize: function (w, h) { /* stub */}, 1803 1804 /** 1805 * Create crosshair elements (Fadenkreuz) for presentations. 1806 * @param {Number} n Number of crosshairs. 1807 */ 1808 createTouchpoints: function (n) {}, 1809 1810 /** 1811 * Show a specific crosshair. 1812 * @param {Number} i Number of the crosshair to show 1813 */ 1814 showTouchpoint: function (i) {}, 1815 1816 /** 1817 * Hide a specific crosshair. 1818 * @param {Number} i Number of the crosshair to show 1819 */ 1820 hideTouchpoint: function (i) {}, 1821 1822 /** 1823 * Move a specific crosshair. 1824 * @param {Number} i Number of the crosshair to show 1825 * @param {Array} pos New positon in screen coordinates 1826 */ 1827 updateTouchpoint: function (i, pos) {}, 1828 1829 /** 1830 * Convert SVG construction to canvas. 1831 * Only available on SVGRenderer. 1832 * 1833 * @see JXG.SVGRenderer#dumpToCanvas 1834 */ 1835 dumpToCanvas: function(canvasId) {}, 1836 1837 /** 1838 * Display SVG image in html img-tag which enables 1839 * easy download for the user. 1840 * 1841 * See JXG.SVGRenderer#screenshot 1842 */ 1843 screenshot: function(board) {} 1844 1845 }); 1846 1847 return JXG.AbstractRenderer; 1848 }); 1849