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