1 /* 2 Copyright 2008-2026 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 <https://www.gnu.org/licenses/> 29 and <https://opensource.org/licenses/MIT/>. 30 */ 31 32 /*global JXG: true, define: true, AMprocessNode: true, MathJax: true, document: true, window: true */ 33 34 /* 35 nomen: Allow underscores to indicate private class members. Might be replaced by local variables. 36 plusplus: Only allowed in for-loops 37 newcap: AsciiMathMl exposes non-constructor functions beginning with upper case letters 38 */ 39 /*jslint nomen: true, plusplus: true, newcap: true, unparam: true*/ 40 41 /** 42 * @fileoverview JSXGraph can use various technologies to render the contents of a construction, e.g. 43 * SVG, VML, and HTML5 Canvas. To accomplish this, The rendering and the logic and control mechanisms 44 * are completely separated from each other. Every rendering technology has it's own class, called 45 * Renderer, e.g. SVGRenderer for SVG, the same for VML and Canvas. The common base for all available 46 * renderers is the class AbstractRenderer defined in this file. 47 */ 48 49 import JXG from "../jxg.js"; 50 import Options from "../options.js"; 51 import Coords from "../base/coords.js"; 52 import Const from "../base/constants.js"; 53 import Mat from "../math/math.js"; 54 import Geometry from "../math/geometry.js"; 55 import Type from "../utils/type.js"; 56 import Env from "../utils/env.js"; 57 58 /** 59 * <p>This class defines the interface to the graphics part of JSXGraph. This class is an abstract class, it 60 * actually does not render anything. This is up to the {@link JXG.SVGRenderer}, {@link JXG.VMLRenderer}, 61 * and {@link JXG.CanvasRenderer} classes. We strongly discourage you from using the methods in these classes 62 * directly. Only the methods which are defined in this class and are not marked as private are guaranteed 63 * to exist in any renderer instance you can access via {@link JXG.Board#renderer}. But not all methods may 64 * work as expected.</p> 65 * <p>The methods of this renderer can be divided into different categories: 66 * <dl> 67 * <dt>Draw basic elements</dt> 68 * <dd>In this category we find methods to draw basic elements like {@link JXG.Point}, {@link JXG.Line}, 69 * and {@link JXG.Curve} as well as assisting methods tightly bound to these basic painters. You do not 70 * need to implement these methods in a descendant renderer but instead implement the primitive drawing 71 * methods described below. This approach is encouraged when you're using a XML based rendering engine 72 * like VML and SVG. If you want to use a bitmap based rendering technique you are supposed to override 73 * these methods instead of the primitive drawing methods.</dd> 74 * <dt>Draw primitives</dt> 75 * <dd>This category summarizes methods to handle primitive nodes. As creation and management of these nodes 76 * is different among different the rendering techniques most of these methods are purely virtual and need 77 * proper implementation if you choose to not overwrite the basic element drawing methods.</dd> 78 * <dt>Attribute manipulation</dt> 79 * <dd>In XML based renders you have to manipulate XML nodes and their attributes to change the graphics. 80 * For that purpose attribute manipulation methods are defined to set the color, opacity, and other things. 81 * Please note that some of these methods are required in bitmap based renderers, too, because some elements 82 * like {@link JXG.Text} can be HTML nodes floating over the construction.</dd> 83 * <dt>Renderer control</dt> 84 * <dd>Methods to clear the drawing board or to stop and to resume the rendering engine.</dd> 85 * </dl></p> 86 * @class JXG.AbstractRenderer 87 * @constructor 88 * @see JXG.SVGRenderer 89 * @see JXG.VMLRenderer 90 * @see JXG.CanvasRenderer 91 */ 92 JXG.AbstractRenderer = function () { 93 // WHY THIS IS A CLASS INSTEAD OF A SINGLETON OBJECT: 94 // 95 // The renderers need to keep track of some stuff which is not always the same on different boards, 96 // like enhancedRendering, reference to the container object, and resolution in VML. Sure, those 97 // things could be stored in board. But they are rendering related and JXG.Board is already very 98 // very big. 99 // 100 // And we can't save the rendering related data in {SVG,VML,Canvas}Renderer and make only the 101 // JXG.AbstractRenderer a singleton because of that: 102 // 103 // Given an object o with property a set to true 104 // var o = {a: true}; 105 // and a class c doing nothing 106 // c = function() {}; 107 // Set c's prototype to o 108 // c.prototype = o; 109 // and create an instance of c we get i.a to be true 110 // i = new c(); 111 // i.a; 112 // > true 113 // But we can overwrite this property via 114 // c.prototype.a = false; 115 // i.a; 116 // > false 117 118 /** 119 * The vertical offset for {@link Text} elements. Every {@link Text} element will 120 * be placed this amount of pixels below the user given coordinates. 121 * @type Number 122 * @default 0 123 */ 124 this.vOffsetText = 0; 125 126 /** 127 * If this property is set to <tt>true</tt> the visual properties of the elements are updated 128 * on every update. Visual properties means: All the stuff stored in the 129 * {@link JXG.GeometryElement#visProp} property won't be set if enhancedRendering is <tt>false</tt> 130 * @type Boolean 131 * @default true 132 */ 133 this.enhancedRendering = true; 134 135 /** 136 * The HTML element that stores the JSXGraph board in it. 137 * @type Node 138 */ 139 this.container = null; 140 141 /** 142 * This is used to easily determine which renderer we are using 143 * @example if (board.renderer.type === 'vml') { 144 * // do something 145 * } 146 * @type String 147 */ 148 this.type = ""; 149 150 /** 151 * True if the browsers' SVG engine supports foreignObject. 152 * Not supported browsers are IE 9 - 11. 153 * It is tested in svg renderer. 154 * 155 * @type Boolean 156 * @private 157 */ 158 this.supportsForeignObject = false; 159 160 /** 161 * Defines dash patterns. Sizes are in pixel. 162 * Defined styles are: 163 * <ol> 164 * <li> 2 dash, 2 space</li> 165 * <li> 5 dash, 5 space</li> 166 * <li> 10 dash, 10 space</li> 167 * <li> 20 dash, 20 space</li> 168 * <li> 20 dash, 10 space, 10 dash, 10 space</li> 169 * <li> 20 dash, 5 space, 10 dash, 5 space</li> 170 * <li> 0 dash, 5 space (dotted line)</li> 171 * </ol> 172 * This means, the numbering is <b>1-based</b>. 173 * Solid lines are set with dash:0. 174 * If the object's attribute "dashScale:true" the dash pattern is multiplied by 175 * strokeWidth / 2. 176 * 177 * @type Array 178 * @default [[2, 2], [5, 5], [10, 10], [20, 20], [20, 10, 10, 10], [20, 5, 10, 5], [0, 5]] 179 * @see JXG.GeometryElement#dash 180 * @see JXG.GeometryElement#dashScale 181 */ 182 this.dashArray = [ 183 [2, 2], 184 [5, 5], 185 [10, 10], 186 [20, 20], 187 [20, 10, 10, 10], 188 [20, 5, 10, 5], 189 [0, 5] 190 ]; 191 }; 192 193 JXG.extend( 194 JXG.AbstractRenderer.prototype, 195 /** @lends JXG.AbstractRenderer.prototype */ { 196 197 /* ********* Private methods *********** */ 198 199 /** 200 * Update visual properties, but only if {@link JXG.AbstractRenderer#enhancedRendering} or <tt>enhanced</tt> is set to true. 201 * @param {JXG.GeometryElement} el The element to update 202 * @param {Object} [not={}] Select properties you don't want to be updated: <tt>{fill: true, dash: true}</tt> updates 203 * everything except for fill and dash. Possible values are <tt>stroke, fill, dash, shadow, gradient</tt>. 204 * @param {Boolean} [enhanced=false] If true, {@link JXG.AbstractRenderer#enhancedRendering} is assumed to be true. 205 * @private 206 */ 207 _updateVisual: function (el, not, enhanced) { 208 if (enhanced || this.enhancedRendering) { 209 not = not || {}; 210 211 this.setObjectTransition(el); 212 213 // Set clip-path 214 // The clip-path of the label is handled in updateText() 215 this.setClipPath(el, !!el.evalVisProp('clip')); 216 217 if (!el.evalVisProp('draft')) { 218 if (!not.stroke) { 219 if (el.highlighted) { 220 this.setObjectStrokeColor( 221 el, 222 el.evalVisProp('highlightstrokecolor'), 223 el.evalVisProp('highlightstrokeopacity') 224 ); 225 this.setObjectStrokeWidth(el, el.evalVisProp('highlightstrokewidth')); 226 } else { 227 this.setObjectStrokeColor( 228 el, 229 el.evalVisProp('strokecolor'), 230 el.evalVisProp('strokeopacity') 231 ); 232 this.setObjectStrokeWidth(el, el.evalVisProp('strokewidth')); 233 } 234 } 235 236 if (!not.fill) { 237 if (el.highlighted) { 238 this.setObjectFillColor( 239 el, 240 el.evalVisProp('highlightfillcolor'), 241 el.evalVisProp('highlightfillopacity') 242 ); 243 } else { 244 this.setObjectFillColor( 245 el, 246 el.evalVisProp('fillcolor'), 247 el.evalVisProp('fillopacity') 248 ); 249 } 250 } 251 252 if (!not.dash) { 253 this.setDashStyle(el, el.visProp); 254 } 255 256 if (!not.shadow) { 257 this.setShadow(el); 258 } 259 260 // if (!not.gradient) { 261 // // this.setGradient(el); 262 // this.setShadow(el); 263 // } 264 265 if (!not.tabindex) { 266 this.setTabindex(el); 267 } 268 } else { 269 this.setDraft(el); 270 } 271 272 if (el.highlighted) { 273 this.setCssClass(el, el.evalVisProp('highlightcssclass')); 274 } else { 275 this.setCssClass(el, el.evalVisProp('cssclass')); 276 } 277 278 if (el.evalVisProp('aria.enabled')) { 279 this.setARIA(el); 280 } 281 } 282 }, 283 284 /** 285 * Get information if element is highlighted. 286 * @param {JXG.GeometryElement} el The element which is tested for being highlighted. 287 * @returns {String} 'highlight' if highlighted, otherwise the ampty string '' is returned. 288 * @private 289 */ 290 _getHighlighted: function (el) { 291 var isTrace = false, 292 hl; 293 294 if (!Type.exists(el.board) || !Type.exists(el.board.highlightedObjects)) { 295 // This case handles trace elements. 296 // To make them work, we simply neglect highlighting. 297 isTrace = true; 298 } 299 300 if (!isTrace && Type.exists(el.board.highlightedObjects[el.id])) { 301 hl = 'highlight'; 302 } else { 303 hl = ""; 304 } 305 return hl; 306 }, 307 308 /* ********* Point related stuff *********** */ 309 310 /** 311 * Draws a point on the {@link JXG.Board}. 312 * @param {JXG.Point} el Reference to a {@link JXG.Point} object that has to be drawn. 313 * @see Point 314 * @see JXG.Point 315 * @see JXG.AbstractRenderer#updatePoint 316 * @see JXG.AbstractRenderer#changePointStyle 317 */ 318 drawPoint: function (el) { 319 var prim, 320 // Sometimes el is not a real point and lacks the methods of a JXG.Point instance, 321 // in these cases to not use el directly. 322 face = Options.normalizePointFace(el.evalVisProp('face')); 323 324 // Determine how the point looks like 325 if (face === 'o') { 326 prim = 'ellipse'; 327 } else if (face === "[]") { 328 prim = 'rect'; 329 } else { 330 // cross/x, diamond/<>, triangleup/A/^, triangledown/v, triangleleft/<, 331 // triangleright/>, plus/+, |, - 332 prim = 'path'; 333 } 334 335 el.rendNode = this.appendChildPrim( 336 this.createPrim(prim, el.id), 337 el.evalVisProp('layer') 338 ); 339 this.appendNodesToElement(el, prim); 340 341 // Adjust visual properties 342 this._updateVisual(el, { dash: true, shadow: true }, true); 343 344 // By now we only created the xml nodes and set some styles, in updatePoint 345 // the attributes are filled with data. 346 this.updatePoint(el); 347 }, 348 349 /** 350 * Updates visual appearance of the renderer element assigned to the given {@link JXG.Point}. 351 * @param {JXG.Point} el Reference to a {@link JXG.Point} object, that has to be updated. 352 * @see Point 353 * @see JXG.Point 354 * @see JXG.AbstractRenderer#drawPoint 355 * @see JXG.AbstractRenderer#changePointStyle 356 */ 357 updatePoint: function (el) { 358 var size = el.evalVisProp('size'), 359 // sometimes el is not a real point and lacks the methods of a JXG.Point instance, 360 // in these cases to not use el directly. 361 face = Options.normalizePointFace(el.evalVisProp('face')), 362 unit = el.evalVisProp('sizeunit'), 363 zoom = el.evalVisProp('zoom'), 364 s1; 365 366 if (!isNaN(el.coords.scrCoords[2] + el.coords.scrCoords[1])) { 367 if (unit === 'user') { 368 size *= Math.sqrt(Math.abs(el.board.unitX * el.board.unitY)); 369 } 370 size *= !el.board || !zoom ? 1.0 : Math.sqrt(el.board.zoomX * el.board.zoomY); 371 s1 = size === 0 ? 0 : size + 1; 372 373 if (face === 'o') { 374 // circle 375 this.updateEllipsePrim( 376 el.rendNode, 377 el.coords.scrCoords[1], 378 el.coords.scrCoords[2], 379 s1, 380 s1 381 ); 382 } else if (face === "[]") { 383 // rectangle 384 this.updateRectPrim( 385 el.rendNode, 386 el.coords.scrCoords[1] - size, 387 el.coords.scrCoords[2] - size, 388 size * 2, 389 size * 2 390 ); 391 } else { 392 // x, +, <>, <<>>, ^, v, <, > 393 this.updatePathPrim( 394 el.rendNode, 395 this.updatePathStringPoint(el, size, face), 396 el.board 397 ); 398 } 399 this._updateVisual(el, { dash: false, shadow: false }); 400 this.setShadow(el); 401 } 402 }, 403 404 /** 405 * Changes the style of a {@link JXG.Point}. This is required because the point styles differ in what 406 * elements have to be drawn, e.g. if the point is marked by a "x" or a "+" two lines are drawn, if 407 * it's marked by spot a circle is drawn. This method removes the old renderer element(s) and creates 408 * the new one(s). 409 * @param {JXG.Point} el Reference to a {@link JXG.Point} object, that's style is changed. 410 * @see Point 411 * @see JXG.Point 412 * @see JXG.AbstractRenderer#updatePoint 413 * @see JXG.AbstractRenderer#drawPoint 414 */ 415 changePointStyle: function (el) { 416 var node = this.getElementById(el.id); 417 418 // remove the existing point rendering node 419 if (Type.exists(node)) { 420 this.remove(node); 421 } 422 423 // and make a new one 424 this.drawPoint(el); 425 Type.clearVisPropOld(el); 426 427 if (!el.visPropCalc.visible) { 428 this.display(el, false); 429 } 430 431 if (el.evalVisProp('draft')) { 432 this.setDraft(el); 433 } 434 }, 435 436 /* ********* Line related stuff *********** */ 437 438 /** 439 * Draws a line on the {@link JXG.Board}. 440 * @param {JXG.Line} el Reference to a line object, that has to be drawn. 441 * @see Line 442 * @see JXG.Line 443 * @see JXG.AbstractRenderer#updateLine 444 */ 445 drawLine: function (el) { 446 el.rendNode = this.appendChildPrim( 447 this.createPrim("line", el.id), 448 el.evalVisProp('layer') 449 ); 450 this.appendNodesToElement(el, 'lines'); 451 this.updateLine(el); 452 }, 453 454 /** 455 * Updates visual appearance of the renderer element assigned to the given {@link JXG.Line}. 456 * @param {JXG.Line} el Reference to the {@link JXG.Line} object that has to be updated. 457 * @see Line 458 * @see JXG.Line 459 * @see JXG.AbstractRenderer#drawLine 460 */ 461 updateLine: function (el) { 462 this._updateVisual(el); 463 this.updatePathWithArrowHeads(el); // Calls the renderer primitive 464 this.setLineCap(el); 465 }, 466 467 /* ********* Curve related stuff *********** */ 468 469 /** 470 * Draws a {@link JXG.Curve} on the {@link JXG.Board}. 471 * @param {JXG.Curve} el Reference to a graph object, that has to be plotted. 472 * @see Curve 473 * @see JXG.Curve 474 * @see JXG.AbstractRenderer#updateCurve 475 */ 476 drawCurve: function (el) { 477 el.rendNode = this.appendChildPrim( 478 this.createPrim("path", el.id), 479 el.evalVisProp('layer') 480 ); 481 this.appendNodesToElement(el, 'path'); 482 this.updateCurve(el); 483 }, 484 485 /** 486 * Updates visual appearance of the renderer element assigned to the given {@link JXG.Curve}. 487 * @param {JXG.Curve} el Reference to a {@link JXG.Curve} object, that has to be updated. 488 * @see Curve 489 * @see JXG.Curve 490 * @see JXG.AbstractRenderer#drawCurve 491 */ 492 updateCurve: function (el) { 493 this._updateVisual(el); 494 this.updatePathWithArrowHeads(el); // Calls the renderer primitive 495 this.setLineCap(el); 496 }, 497 498 /* ********* Arrow heads and related stuff *********** */ 499 500 /** 501 * Handles arrow heads of a line or curve element and calls the renderer primitive. 502 * 503 * @param {JXG.GeometryElement} el Reference to a line or curve object that has to be drawn. 504 * @param {Boolean} doHighlight 505 * 506 * @private 507 * @see Line 508 * @see JXG.Line 509 * @see Curve 510 * @see JXG.Curve 511 * @see JXG.AbstractRenderer#updateLine 512 * @see JXG.AbstractRenderer#updateCurve 513 * @see JXG.AbstractRenderer#makeArrows 514 * @see JXG.AbstractRenderer#getArrowHeadData 515 */ 516 updatePathWithArrowHeads: function (el, doHighlight) { 517 var hl = doHighlight ? 'highlight' : '', 518 w, 519 arrowData; 520 521 if (doHighlight && el.evalVisProp('highlightstrokewidth')) { 522 w = Math.max( 523 el.evalVisProp('highlightstrokewidth'), 524 el.evalVisProp('strokewidth') 525 ); 526 } else { 527 w = el.evalVisProp('strokewidth'); 528 } 529 530 // Get information if there are arrow heads and how large they are. 531 arrowData = this.getArrowHeadData(el, w, hl); 532 533 // Create the SVG nodes if necessary 534 this.makeArrows(el, arrowData); 535 536 // Draw the paths with arrow heads 537 if (el.elementClass === Const.OBJECT_CLASS_LINE) { 538 this.updateLineWithEndings(el, arrowData); 539 } else if (el.elementClass === Const.OBJECT_CLASS_CURVE) { 540 this.updatePath(el); 541 } 542 543 this.setArrowSize(el, arrowData); 544 }, 545 546 /** 547 * This method determines some data about the line endings of this element. 548 * If there are arrow heads, the offset is determined so that no parts of the line stroke 549 * lap over the arrow head. 550 * <p> 551 * The returned object also contains the types of the arrow heads. 552 * 553 * @param {JXG.GeometryElement} el JSXGraph line or curve element 554 * @param {Number} strokewidth strokewidth of the element 555 * @param {String} hl Ither 'highlight' or empty string 556 * @returns {Object} object containing the data 557 * 558 * @private 559 */ 560 getArrowHeadData: function (el, strokewidth, hl) { 561 var minlen = Mat.eps, 562 typeFirst, 563 typeLast, 564 offFirst = 0, 565 offLast = 0, 566 sizeFirst = 0, 567 sizeLast = 0, 568 ev_fa = el.evalVisProp('firstarrow'), 569 ev_la = el.evalVisProp('lastarrow'), 570 off, 571 size; 572 573 /* 574 Handle arrow heads. 575 576 The default arrow head is an isosceles triangle with base length 10 units and height 10 units. 577 These 10 units are scaled to strokeWidth * arrowSize pixels. 578 */ 579 if (ev_fa || ev_la) { 580 if (Type.exists(ev_fa.type)) { 581 typeFirst = el.eval(ev_fa.type); 582 } else { 583 if (el.elementClass === Const.OBJECT_CLASS_LINE) { 584 typeFirst = 1; 585 } else { 586 typeFirst = 7; 587 } 588 } 589 if (Type.exists(ev_la.type)) { 590 typeLast = el.eval(ev_la.type); 591 } else { 592 if (el.elementClass === Const.OBJECT_CLASS_LINE) { 593 typeLast = 1; 594 } else { 595 typeLast = 7; 596 } 597 } 598 599 if (ev_fa) { 600 size = 6; 601 if (Type.exists(ev_fa.size)) { 602 size = el.eval(ev_fa.size); 603 } 604 if (hl !== "" && Type.exists(ev_fa[hl + "size"])) { 605 size = el.eval(ev_fa[hl + "size"]); 606 } 607 608 off = strokewidth * size; 609 if (typeFirst === 2) { 610 off *= 0.5; 611 minlen += strokewidth * size; 612 } else if (typeFirst === 3) { 613 off = (strokewidth * size) / 3; 614 minlen += strokewidth; 615 } else if (typeFirst === 4 || typeFirst === 5 || typeFirst === 6) { 616 off = (strokewidth * size) / 1.5; 617 minlen += strokewidth * size; 618 } else if (typeFirst === 7) { 619 off = 0; 620 size = 10; 621 minlen += strokewidth; 622 } else { 623 minlen += strokewidth * size; 624 } 625 offFirst += off; 626 sizeFirst = size; 627 } 628 629 if (ev_la) { 630 size = 6; 631 if (Type.exists(ev_la.size)) { 632 size = el.eval(ev_la.size); 633 } 634 if (hl !== "" && Type.exists(ev_la[hl + "size"])) { 635 size = el.eval(ev_la[hl + "size"]); 636 } 637 off = strokewidth * size; 638 if (typeLast === 2) { 639 off *= 0.5; 640 minlen += strokewidth * size; 641 } else if (typeLast === 3) { 642 off = (strokewidth * size) / 3; 643 minlen += strokewidth; 644 } else if (typeLast === 4 || typeLast === 5 || typeLast === 6) { 645 off = (strokewidth * size) / 1.5; 646 minlen += strokewidth * size; 647 } else if (typeLast === 7) { 648 off = 0; 649 size = 10; 650 minlen += strokewidth; 651 } else { 652 minlen += strokewidth * size; 653 } 654 offLast += off; 655 sizeLast = size; 656 } 657 } 658 el.visPropCalc.typeFirst = typeFirst; 659 el.visPropCalc.typeLast = typeLast; 660 661 return { 662 evFirst: ev_fa, 663 evLast: ev_la, 664 typeFirst: typeFirst, 665 typeLast: typeLast, 666 offFirst: offFirst, 667 offLast: offLast, 668 sizeFirst: sizeFirst, 669 sizeLast: sizeLast, 670 showFirst: 1, // Show arrow head. 0 if the distance is too small 671 showLast: 1, // Show arrow head. 0 if the distance is too small 672 minLen: minlen, 673 strokeWidth: strokewidth 674 }; 675 }, 676 677 /** 678 * Corrects the line length if there are arrow heads, such that 679 * the arrow ends exactly at the intended position. 680 * Calls the renderer method to draw the line. 681 * 682 * @param {JXG.Line} el Reference to a line object, that has to be drawn 683 * @param {Object} arrowData Data concerning possible arrow heads 684 * 685 * @returns {JXG.AbstractRenderer} Reference to the renderer 686 * 687 * @private 688 * @see Line 689 * @see JXG.Line 690 * @see JXG.AbstractRenderer#updateLine 691 * @see JXG.AbstractRenderer#getPositionArrowHead 692 * 693 */ 694 updateLineWithEndings: function (el, arrowData) { 695 var c1, 696 c2, 697 // useTotalLength = true, 698 margin = null; 699 700 c1 = new Coords(Const.COORDS_BY_USER, el.point1.coords.usrCoords, el.board); 701 c2 = new Coords(Const.COORDS_BY_USER, el.point2.coords.usrCoords, el.board); 702 margin = el.evalVisProp('margin'); 703 if (!el.evalVisProp('clip')) { 704 margin += 4096; 705 } 706 Geometry.calcStraight(el, c1, c2, margin); 707 708 this.handleTouchpoints(el, c1, c2, arrowData); 709 this.getPositionArrowHead(el, c1, c2, arrowData); 710 711 this.updateLinePrim( 712 el.rendNode, 713 c1.scrCoords[1], 714 c1.scrCoords[2], 715 c2.scrCoords[1], 716 c2.scrCoords[2], 717 el.board 718 ); 719 720 return this; 721 }, 722 723 /** 724 * 725 * Calls the renderer method to draw a curve. 726 * 727 * @param {JXG.GeometryElement} el Reference to a line object, that has to be drawn. 728 * @returns {JXG.AbstractRenderer} Reference to the renderer 729 * 730 * @private 731 * @see Curve 732 * @see JXG.Curve 733 * @see JXG.AbstractRenderer#updateCurve 734 * 735 */ 736 updatePath: function (el) { 737 if (el.evalVisProp('handdrawing')) { 738 this.updatePathPrim(el.rendNode, this.updatePathStringBezierPrim(el), el.board); 739 } else { 740 this.updatePathPrim(el.rendNode, this.updatePathStringPrim(el), el.board); 741 } 742 743 return this; 744 }, 745 746 /** 747 * Shorten the length of a line element such that the arrow head touches 748 * the start or end point and such that the arrow head ends exactly 749 * at the start / end position of the line. 750 * <p> 751 * The Coords objects c1 and c2 are changed in place. In object a, the Boolean properties 752 * 'showFirst' and 'showLast' are set. 753 * 754 * @param {JXG.Line} el Reference to the line object that gets arrow heads. 755 * @param {JXG.Coords} c1 Coords of the first point of the line (after {@link JXG.Math.Geometry#calcStraight}). 756 * @param {JXG.Coords} c2 Coords of the second point of the line (after {@link JXG.Math.Geometry#calcStraight}). 757 * @param {Object} a Object { evFirst: Boolean, evLast: Boolean} containing information about arrow heads. 758 * @see JXG.AbstractRenderer#getArrowHeadData 759 * 760 */ 761 getPositionArrowHead: function (el, c1, c2, a) { 762 var d, d1x, d1y, d2x, d2y; 763 764 // Handle arrow heads. 765 766 // The default arrow head (type==1) is an isosceles triangle with base length 10 units and height 10 units. 767 // These 10 units are scaled to strokeWidth * arrowSize pixels. 768 if (a.evFirst || a.evLast) { 769 // Correct the position of the arrow heads 770 d1x = d1y = d2x = d2y = 0.0; 771 d = c1.distance(Const.COORDS_BY_SCREEN, c2); 772 773 if (a.evFirst && el.board.renderer.type !== 'vml') { 774 if (d >= a.minLen) { 775 d1x = ((c2.scrCoords[1] - c1.scrCoords[1]) * a.offFirst) / d; 776 d1y = ((c2.scrCoords[2] - c1.scrCoords[2]) * a.offFirst) / d; 777 } else { 778 a.showFirst = 0; 779 } 780 } 781 782 if (a.evLast && el.board.renderer.type !== 'vml') { 783 if (d >= a.minLen) { 784 d2x = ((c2.scrCoords[1] - c1.scrCoords[1]) * a.offLast) / d; 785 d2y = ((c2.scrCoords[2] - c1.scrCoords[2]) * a.offLast) / d; 786 } else { 787 a.showLast = 0; 788 } 789 } 790 c1.setCoordinates( 791 Const.COORDS_BY_SCREEN, 792 [c1.scrCoords[1] + d1x, c1.scrCoords[2] + d1y], 793 false, 794 true 795 ); 796 c2.setCoordinates( 797 Const.COORDS_BY_SCREEN, 798 [c2.scrCoords[1] - d2x, c2.scrCoords[2] - d2y], 799 false, 800 true 801 ); 802 } 803 804 return this; 805 }, 806 807 /** 808 * Handle touchlastpoint / touchfirstpoint 809 * 810 * @param {JXG.GeometryElement} el 811 * @param {JXG.Coords} c1 Coordinates of the start of the line. The coordinates are changed in place. 812 * @param {JXG.Coords} c2 Coordinates of the end of the line. The coordinates are changed in place. 813 * @param {Object} a 814 * @see JXG.AbstractRenderer#getArrowHeadData 815 */ 816 handleTouchpoints: function (el, c1, c2, a) { 817 var s1, s2, d, d1x, d1y, d2x, d2y; 818 819 if (a.evFirst || a.evLast) { 820 d = d1x = d1y = d2x = d2y = 0.0; 821 822 s1 = el.point1.evalVisProp('size') + 823 el.point1.evalVisProp('strokewidth'); 824 825 s2 = el.point2.evalVisProp('size') + 826 el.point2.evalVisProp('strokewidth'); 827 828 // Handle touchlastpoint /touchfirstpoint 829 if (a.evFirst && el.evalVisProp('touchfirstpoint') && 830 el.point1.evalVisProp('visible')) { 831 d = c1.distance(Const.COORDS_BY_SCREEN, c2); 832 //if (d > s) { 833 d1x = ((c2.scrCoords[1] - c1.scrCoords[1]) * s1) / d; 834 d1y = ((c2.scrCoords[2] - c1.scrCoords[2]) * s1) / d; 835 //} 836 } 837 if (a.evLast && el.evalVisProp('touchlastpoint') && 838 el.point2.evalVisProp('visible')) { 839 d = c1.distance(Const.COORDS_BY_SCREEN, c2); 840 //if (d > s) { 841 d2x = ((c2.scrCoords[1] - c1.scrCoords[1]) * s2) / d; 842 d2y = ((c2.scrCoords[2] - c1.scrCoords[2]) * s2) / d; 843 //} 844 } 845 c1.setCoordinates( 846 Const.COORDS_BY_SCREEN, 847 [c1.scrCoords[1] + d1x, c1.scrCoords[2] + d1y], 848 false, 849 true 850 ); 851 c2.setCoordinates( 852 Const.COORDS_BY_SCREEN, 853 [c2.scrCoords[1] - d2x, c2.scrCoords[2] - d2y], 854 false, 855 true 856 ); 857 } 858 859 return this; 860 }, 861 862 /** 863 * Set the arrow head size. 864 * 865 * @param {JXG.GeometryElement} el Reference to a line or curve object that has to be drawn. 866 * @param {Object} arrowData Data concerning possible arrow heads 867 * @returns {JXG.AbstractRenderer} Reference to the renderer 868 * 869 * @private 870 * @see Line 871 * @see JXG.Line 872 * @see Curve 873 * @see JXG.Curve 874 * @see JXG.AbstractRenderer#updatePathWithArrowHeads 875 * @see JXG.AbstractRenderer#getArrowHeadData 876 */ 877 setArrowSize: function (el, a) { 878 if (a.evFirst) { 879 this._setArrowWidth( 880 el.rendNodeTriangleStart, 881 a.showFirst * a.strokeWidth, 882 el.rendNode, 883 a.sizeFirst 884 ); 885 } 886 if (a.evLast) { 887 this._setArrowWidth( 888 el.rendNodeTriangleEnd, 889 a.showLast * a.strokeWidth, 890 el.rendNode, 891 a.sizeLast 892 ); 893 } 894 return this; 895 }, 896 897 /** 898 * Update the line endings (linecap) of a straight line from its attribute 899 * 'linecap'. 900 * Possible values for the attribute 'linecap' are: 'butt', 'round', 'square'. 901 * The default value is 'butt'. Not available for VML renderer. 902 * 903 * @param {JXG.Line} element A arbitrary line. 904 * @see Line 905 * @see JXG.Line 906 * @see JXG.AbstractRenderer#updateLine 907 */ 908 setLineCap: function (el) { /* stub */ }, 909 910 /* ********* Ticks related stuff *********** */ 911 912 /** 913 * Creates a rendering node for ticks added to a line. 914 * @param {JXG.Line} el A arbitrary line. 915 * @see Line 916 * @see Ticks 917 * @see JXG.Line 918 * @see JXG.Ticks 919 * @see JXG.AbstractRenderer#updateTicks 920 */ 921 drawTicks: function (el) { 922 el.rendNode = this.appendChildPrim( 923 this.createPrim("path", el.id), 924 el.evalVisProp('layer') 925 ); 926 this.appendNodesToElement(el, 'path'); 927 }, 928 929 /** 930 * Update {@link Ticks} on a {@link JXG.Line}. This method is only a stub and has to be implemented 931 * in any descendant renderer class. 932 * @param {JXG.Ticks} el Reference of a ticks object that has to be updated. 933 * @see Line 934 * @see Ticks 935 * @see JXG.Line 936 * @see JXG.Ticks 937 * @see JXG.AbstractRenderer#drawTicks 938 */ 939 updateTicks: function (el) { /* stub */ }, 940 941 /* ********* Circle related stuff *********** */ 942 943 /** 944 * Draws a {@link JXG.Circle} 945 * @param {JXG.Circle} el Reference to a {@link JXG.Circle} object that has to be drawn. 946 * @see Circle 947 * @see JXG.Circle 948 * @see JXG.AbstractRenderer#updateEllipse 949 */ 950 drawEllipse: function (el) { 951 el.rendNode = this.appendChildPrim( 952 this.createPrim("ellipse", el.id), 953 el.evalVisProp('layer') 954 ); 955 this.appendNodesToElement(el, 'ellipse'); 956 this.updateEllipse(el); 957 }, 958 959 /** 960 * Updates visual appearance of a given {@link JXG.Circle} on the {@link JXG.Board}. 961 * @param {JXG.Circle} el Reference to a {@link JXG.Circle} object, that has to be updated. 962 * @see Circle 963 * @see JXG.Circle 964 * @see JXG.AbstractRenderer#drawEllipse 965 */ 966 updateEllipse: function (el) { 967 this._updateVisual(el); 968 969 var radius = el.Radius(); 970 971 if ( 972 /*radius > 0.0 &&*/ 973 Math.abs(el.center.coords.usrCoords[0]) > Mat.eps && 974 !isNaN(radius + el.center.coords.scrCoords[1] + el.center.coords.scrCoords[2]) && 975 radius * el.board.unitX < 2000000 976 ) { 977 this.updateEllipsePrim( 978 el.rendNode, 979 el.center.coords.scrCoords[1], 980 el.center.coords.scrCoords[2], 981 radius * el.board.unitX, 982 radius * el.board.unitY 983 ); 984 } 985 this.setLineCap(el); 986 }, 987 988 /* ********* Polygon related stuff *********** */ 989 990 /** 991 * Draws a {@link JXG.Polygon} on the {@link JXG.Board}. 992 * @param {JXG.Polygon} el Reference to a Polygon object, that is to be drawn. 993 * @see Polygon 994 * @see JXG.Polygon 995 * @see JXG.AbstractRenderer#updatePolygon 996 */ 997 drawPolygon: function (el) { 998 el.rendNode = this.appendChildPrim( 999 this.createPrim("polygon", el.id), 1000 el.evalVisProp('layer') 1001 ); 1002 this.appendNodesToElement(el, 'polygon'); 1003 this.updatePolygon(el); 1004 }, 1005 1006 /** 1007 * Updates properties of a {@link JXG.Polygon}'s rendering node. 1008 * @param {JXG.Polygon} el Reference to a {@link JXG.Polygon} object, that has to be updated. 1009 * @see Polygon 1010 * @see JXG.Polygon 1011 * @see JXG.AbstractRenderer#drawPolygon 1012 */ 1013 updatePolygon: function (el) { 1014 // Here originally strokecolor wasn't updated but strokewidth was. 1015 // But if there's no strokecolor i don't see why we should update strokewidth. 1016 this._updateVisual(el, { stroke: true, dash: true }); 1017 this.updatePolygonPrim(el.rendNode, el); 1018 }, 1019 1020 /* ********* Text related stuff *********** */ 1021 1022 /** 1023 * Shows a small copyright notice in the top left corner of the board. 1024 * @param {String} str The copyright notice itself 1025 * @param {Number} fontsize Size of the font the copyright notice is written in 1026 * @see JXG.AbstractRenderer#displayLogo 1027 * @see Text#fontSize 1028 */ 1029 displayCopyright: function (str, fontsize) { /* stub */ }, 1030 1031 /** 1032 * Shows a small JSXGraph logo in the top left corner of the board. 1033 * @param {String} str The data-URL of the logo 1034 * @param {Number} fontsize Size of the font the copyright notice is written in 1035 * @see JXG.AbstractRenderer#displayCopyright 1036 * @see Text#fontSize 1037 */ 1038 displayLogo: function (str, fontsize) { /* stub */ }, 1039 1040 /** 1041 * An internal text is a {@link JXG.Text} element which is drawn using only 1042 * the given renderer but no HTML. This method is only a stub, the drawing 1043 * is done in the special renderers. 1044 * @param {JXG.Text} el Reference to a {@link JXG.Text} object 1045 * @see Text 1046 * @see JXG.Text 1047 * @see JXG.AbstractRenderer#updateInternalText 1048 * @see JXG.AbstractRenderer#drawText 1049 * @see JXG.AbstractRenderer#updateText 1050 * @see JXG.AbstractRenderer#updateTextStyle 1051 */ 1052 drawInternalText: function (el) { /* stub */ }, 1053 1054 /** 1055 * Updates visual properties of an already existing {@link JXG.Text} element. 1056 * @param {JXG.Text} el Reference to an {@link JXG.Text} object, that has to be updated. 1057 * @see Text 1058 * @see JXG.Text 1059 * @see JXG.AbstractRenderer#drawInternalText 1060 * @see JXG.AbstractRenderer#drawText 1061 * @see JXG.AbstractRenderer#updateText 1062 * @see JXG.AbstractRenderer#updateTextStyle 1063 */ 1064 updateInternalText: function (el) { /* stub */ }, 1065 1066 /** 1067 * Displays a {@link JXG.Text} on the {@link JXG.Board} by putting a HTML div over it. 1068 * @param {JXG.Text} el Reference to an {@link JXG.Text} object, that has to be displayed 1069 * @see Text 1070 * @see JXG.Text 1071 * @see JXG.AbstractRenderer#drawInternalText 1072 * @see JXG.AbstractRenderer#updateText 1073 * @see JXG.AbstractRenderer#updateInternalText 1074 * @see JXG.AbstractRenderer#updateTextStyle 1075 */ 1076 drawText: function (el) { 1077 var node, z, level, ev_visible; 1078 1079 if ( 1080 el.evalVisProp('display') === "html" && 1081 Env.isBrowser && 1082 this.type !== "no" 1083 ) { 1084 node = this.container.ownerDocument.createElement('div'); 1085 //node = this.container.ownerDocument.createElementNS('http://www.w3.org/1999/xhtml', 'div'); // 1086 node.style.position = 'absolute'; 1087 node.className = el.evalVisProp('cssclass'); 1088 1089 level = el.evalVisProp('layer'); 1090 if (!Type.exists(level)) { 1091 // trace nodes have level not set 1092 level = 0; 1093 } 1094 1095 if (this.container.style.zIndex === "") { 1096 z = 0; 1097 } else { 1098 z = parseInt(this.container.style.zIndex, 10); 1099 } 1100 1101 node.style.zIndex = z + level; 1102 this.container.appendChild(node); 1103 1104 node.setAttribute("id", this.container.id + "_" + el.id); 1105 } else { 1106 node = this.drawInternalText(el); 1107 } 1108 1109 el.rendNode = node; 1110 el.htmlStr = ""; 1111 1112 // Set el.visPropCalc.visible 1113 if (el.visProp.islabel && Type.exists(el.visProp.anchor)) { 1114 ev_visible = el.visProp.anchor.evalVisProp('visible'); 1115 el.prepareUpdate().updateVisibility(ev_visible); 1116 } else { 1117 el.prepareUpdate().updateVisibility(); 1118 } 1119 this.updateText(el); 1120 }, 1121 1122 /** 1123 * Update CSS property clip-path to HTML texts if `overflow:hidden` 1124 * has to be avoided. 1125 * 1126 * TODO clipping for transformed texts 1127 * 1128 * @param {JXG.Text} el Reference to an {@link JXG.Text} object that has to be clipped. 1129 * @param {Boolean} [val=undefined] Set an explicit value, overwrites the element's attribute 'clip'. This is useful for handling the value 'inherit'. 1130 * @see Text 1131 * @see JXG.Text 1132 */ 1133 updateClipPath: function(el, val) { 1134 var x, y, x2, y2, 1135 w = el.rendNode.offsetWidth, 1136 h = el.rendNode.offsetHeight, 1137 is = el.rendNode.style.inset.split(' '), 1138 cw = el.board.canvasWidth, 1139 ch = el.board.canvasHeight; 1140 1141 if (val === undefined) { 1142 val = el.evalVisProp('clip'); 1143 } 1144 1145 if (!val) { 1146 el.rendNode.style.removeProperty('clip-path'); 1147 return; 1148 } 1149 if (is[3] !== 'auto') { 1150 x = parseFloat(is[3]); 1151 x2 = cw - x; 1152 } else { 1153 x2 = parseFloat(is[1]) + w; 1154 x = cw - x2; 1155 } 1156 1157 if (is[0] !== 'auto') { 1158 y = parseFloat(is[0]); 1159 y2 = ch - y; 1160 } else { 1161 y2 = parseFloat(is[2]) + h; 1162 y = ch - y2; 1163 } 1164 1165 el.rendNode.style.clipPath = 'rect(' + (-y) + 'px ' // top 1166 + (x2) + 'px ' // right 1167 + (y2) + 'px ' // bottom 1168 + (-x) + 'px)'; // left 1169 }, 1170 1171 /** 1172 * Updates visual properties of an already existing {@link JXG.Text} element. 1173 * @param {JXG.Text} el Reference to an {@link JXG.Text} object that has to be updated. 1174 * @see Text 1175 * @see JXG.Text 1176 * @see JXG.AbstractRenderer#drawText 1177 * @see JXG.AbstractRenderer#drawInternalText 1178 * @see JXG.AbstractRenderer#updateInternalText 1179 * @see JXG.AbstractRenderer#updateTextStyle 1180 */ 1181 updateText: function (el) { 1182 var content = el.plaintext, 1183 v, c, 1184 parentNode, node, 1185 // scale, vshift, 1186 // id, wrap_id, 1187 ax, ay, angle, co, si, 1188 to_h, to_v; 1189 1190 if (el.visPropCalc.visible) { 1191 this.updateTextStyle(el, false); 1192 1193 if (el.evalVisProp('display') === "html" && this.type !== 'no') { 1194 // Set the position 1195 if (!isNaN(el.coords.scrCoords[1] + el.coords.scrCoords[2])) { 1196 // Horizontal 1197 c = el.coords.scrCoords[1]; 1198 // webkit seems to fail for extremely large values for c. 1199 c = Math.abs(c) < 1000000 ? c : 1000000; 1200 ax = el.getAnchorX(); 1201 1202 if (ax === 'right') { 1203 // v = Math.floor(el.board.canvasWidth - c); 1204 v = el.board.canvasWidth - c; 1205 to_h = 'right'; 1206 } else if (ax === 'middle') { 1207 // v = Math.floor(c - 0.5 * el.size[0]); 1208 v = c - 0.5 * el.size[0]; 1209 to_h = 'center'; 1210 } else { 1211 // 'left' 1212 // v = Math.floor(c); 1213 v = c; 1214 to_h = 'left'; 1215 } 1216 1217 // This may be useful for foreignObj. 1218 //if (window.devicePixelRatio !== undefined) { 1219 //v *= window.devicePixelRatio; 1220 //} 1221 1222 if (el.visPropOld.left !== ax + v) { 1223 if (ax === 'right') { 1224 el.rendNode.style.right = v + 'px'; 1225 el.rendNode.style.left = 'auto'; 1226 } else { 1227 el.rendNode.style.left = v + 'px'; 1228 el.rendNode.style.right = 'auto'; 1229 } 1230 el.visPropOld.left = ax + v; 1231 } 1232 1233 // Vertical 1234 c = el.coords.scrCoords[2] + this.vOffsetText; 1235 c = Math.abs(c) < 1000000 ? c : 1000000; 1236 ay = el.getAnchorY(); 1237 1238 if (ay === 'bottom') { 1239 // v = Math.floor(el.board.canvasHeight - c); 1240 v = el.board.canvasHeight - c; 1241 to_v = 'bottom'; 1242 } else if (ay === 'middle') { 1243 // v = Math.floor(c - 0.5 * el.size[1]); 1244 v = c - 0.5 * el.size[1]; 1245 to_v = 'center'; 1246 } else { 1247 // top 1248 // v = Math.floor(c); 1249 v = c; 1250 to_v = 'top'; 1251 } 1252 1253 // This may be useful for foreignObj. 1254 //if (window.devicePixelRatio !== undefined) { 1255 //v *= window.devicePixelRatio; 1256 //} 1257 1258 if (el.visPropOld.top !== ay + v) { 1259 if (ay === 'bottom') { 1260 el.rendNode.style.top = 'auto'; 1261 el.rendNode.style.bottom = v + 'px'; 1262 } else { 1263 el.rendNode.style.bottom = 'auto'; 1264 el.rendNode.style.top = v + 'px'; 1265 } 1266 el.visPropOld.top = ay + v; 1267 } 1268 } 1269 1270 // Set the content 1271 if (el.htmlStr !== content) { 1272 try { 1273 if (el.type === Type.OBJECT_TYPE_BUTTON) { 1274 el.rendNodeButton.innerHTML = content; 1275 } else if ( 1276 el.type === Type.OBJECT_TYPE_CHECKBOX || 1277 el.type === Type.OBJECT_TYPE_INPUT 1278 ) { 1279 el.rendNodeLabel.innerHTML = content; 1280 } else { 1281 el.rendNode.innerHTML = content; 1282 } 1283 } catch (e) { 1284 // Setting innerHTML sometimes fails in IE8. 1285 // A workaround is to take the node off the DOM, assign innerHTML, 1286 // then append back. 1287 // Works for text elements as they are absolutely positioned. 1288 parentNode = el.rendNode.parentNode; 1289 el.rendNode.parentNode.removeChild(el.rendNode); 1290 el.rendNode.innerHTML = content; 1291 parentNode.appendChild(el.rendNode); 1292 } 1293 el.htmlStr = content; 1294 1295 if (el.evalVisProp('usemathjax')) { 1296 // Typesetting directly might not work because MathJax was not loaded completely 1297 try { 1298 if (MathJax.typeset) { 1299 // Version 3 1300 MathJax.typeset([el.rendNode]); 1301 } else { 1302 // Version 2 1303 MathJax.Hub.Queue(["Typeset", MathJax.Hub, el.rendNode]); 1304 } 1305 1306 // Obsolete: 1307 // // Restore the transformation necessary for fullscreen mode 1308 // // MathJax removes it when handling dynamic content 1309 // id = el.board.container; 1310 // wrap_id = "fullscreenwrap_" + id; 1311 // if (document.getElementById(wrap_id)) { 1312 // scale = el.board.containerObj._cssFullscreenStore.scale; 1313 // vshift = el.board.containerObj._cssFullscreenStore.vshift; 1314 // Env.scaleJSXGraphDiv( 1315 // "#" + wrap_id, 1316 // "#" + id, 1317 // scale, 1318 // vshift 1319 // ); 1320 // } 1321 } catch (e) { 1322 JXG.debug("MathJax (not yet) loaded"); 1323 } 1324 } else if (el.evalVisProp('usekatex')) { 1325 try { 1326 // Checkboxes et. al. do not possess rendNodeLabel during the first update. 1327 // In this case node will be undefined and not rendered by KaTeX. 1328 if (el.rendNode.innerHTML.indexOf('<span') === 0 && 1329 el.rendNode.innerHTML.indexOf('<label') > 0 && 1330 ( 1331 el.rendNode.innerHTML.indexOf('<checkbox') > 0 || 1332 el.rendNode.innerHTML.indexOf('<input') > 0 1333 ) 1334 ) { 1335 node = el.rendNodeLabel; 1336 } else if (el.rendNode.innerHTML.indexOf('<button') === 0) { 1337 node = el.rendNodeButton; 1338 } else { 1339 node = el.rendNode; 1340 } 1341 1342 if (node) { 1343 /* eslint-disable no-undef */ 1344 katex.render(content, node, { 1345 macros: el.evalVisProp('katexmacros'), 1346 throwOnError: false 1347 }); 1348 /* eslint-enable no-undef */ 1349 } 1350 } catch (e) { 1351 JXG.debug("KaTeX not loaded (yet)"); 1352 } 1353 } else if (el.evalVisProp('useasciimathml')) { 1354 // This is not a constructor. 1355 // See http://asciimath.org/ for more information 1356 // about AsciiMathML and the project's source code. 1357 try { 1358 AMprocessNode(el.rendNode, false); 1359 } catch (e) { 1360 JXG.debug("AsciiMathML not loaded (yet)"); 1361 } 1362 } 1363 } 1364 1365 angle = el.evalVisProp('rotate'); 1366 if (angle !== 0) { 1367 // Don't forget to convert to rad 1368 angle *= (Math.PI / 180); 1369 co = Math.cos(angle); 1370 si = Math.sin(angle); 1371 1372 el.rendNode.style['transform'] = 'matrix(' + 1373 [co, -1 * si, si, co, 0, 0].join(',') + 1374 ')'; 1375 el.rendNode.style['transform-origin'] = to_h + ' ' + to_v; 1376 } 1377 this.transformRect(el, el.transformations); 1378 1379 if (el.visProp.islabel && Type.exists(el.visProp.anchor) && 1380 el.evalVisProp('clip') === 'inherit') { 1381 this.updateClipPath(el, !!el.visProp.anchor.evalVisProp('clip')); 1382 } else { 1383 this.updateClipPath(el); 1384 } 1385 } else { 1386 this.updateInternalText(el); 1387 } 1388 } 1389 }, 1390 1391 /** 1392 * Updates font-size, color and opacity properties and CSS style properties of a {@link JXG.Text} node. 1393 * This function is also called by highlight() and nohighlight(). 1394 * @param {JXG.Text} el Reference to the {@link JXG.Text} object, that has to be updated. 1395 * @param {Boolean} doHighlight 1396 * @see Text 1397 * @see JXG.Text 1398 * @see JXG.AbstractRenderer#drawText 1399 * @see JXG.AbstractRenderer#drawInternalText 1400 * @see JXG.AbstractRenderer#updateText 1401 * @see JXG.AbstractRenderer#updateInternalText 1402 * @see JXG.AbstractRenderer#updateInternalTextStyle 1403 */ 1404 updateTextStyle: function (el, doHighlight) { 1405 var fs, 1406 so, sc, 1407 css, 1408 node, 1409 display = Env.isBrowser ? el.visProp.display : "internal", 1410 nodeList = ["rendNode", "rendNodeTag", "rendNodeLabel"], 1411 lenN = nodeList.length, 1412 fontUnit = el.evalVisProp('fontunit'), 1413 cssList, 1414 prop, pair, 1415 style, 1416 cssString, 1417 styleList = ["cssdefaultstyle", "cssstyle"], 1418 lenS = styleList.length; 1419 1420 if (doHighlight) { 1421 sc = el.evalVisProp('highlightstrokecolor'); 1422 so = el.evalVisProp('highlightstrokeopacity'); 1423 css = el.evalVisProp('highlightcssclass'); 1424 } else { 1425 sc = el.evalVisProp('strokecolor'); 1426 so = el.evalVisProp('strokeopacity'); 1427 css = el.evalVisProp('cssclass'); 1428 } 1429 1430 // This part is executed for all text elements except internal texts in canvas. 1431 // HTML-texts or internal texts in SVG or VML. 1432 // HTML internal 1433 // SVG + + 1434 // VML + + 1435 // canvas + - 1436 // no - - 1437 if (this.type !== "no" && (display === "html" || this.type !== 'canvas')) { 1438 for (style = 0; style < lenS; style++) { 1439 // First set cssString to 1440 // ev.cssdefaultstyle of ev.highlightcssdefaultstyle, 1441 // then to 1442 // ev.cssstyle of ev.highlightcssstyle 1443 cssString = el.evalVisProp( 1444 (doHighlight ? 'highlight' : '') + styleList[style] 1445 ); 1446 // Set the CSS style properties - without deleting other properties 1447 for (node = 0; node < lenN; node++) if (Type.exists(el[nodeList[node]])) { 1448 if (cssString !== "" && el.visPropOld[styleList[style] + '_' + node] !== cssString) { 1449 cssList = Type.css2js(cssString); 1450 for (prop in cssList) if (cssList.hasOwnProperty(prop)) { 1451 pair = cssList[prop]; 1452 el[nodeList[node]].style[pair.key] = pair.val; 1453 } 1454 el.visPropOld[styleList[style] + '_' + node] = cssString; 1455 } 1456 // el.visPropOld[styleList[style]] = cssString; 1457 } 1458 } 1459 1460 fs = el.evalVisProp('fontsize'); 1461 if (el.visPropOld.fontsize !== fs) { 1462 el.needsSizeUpdate = true; 1463 try { 1464 for (node = 0; node < lenN; node++) { 1465 if (Type.exists(el[nodeList[node]])) { 1466 el[nodeList[node]].style.fontSize = fs + fontUnit; 1467 } 1468 } 1469 } catch (e) { 1470 // IE needs special treatment. 1471 for (node = 0; node < lenN; node++) { 1472 if (Type.exists(el[nodeList[node]])) { 1473 el[nodeList[node]].style.fontSize = fs; 1474 } 1475 } 1476 } 1477 el.visPropOld.fontsize = fs; 1478 } 1479 } 1480 1481 this.setTabindex(el); 1482 1483 this.setObjectTransition(el); 1484 if (display === "html" && this.type !== 'no') { 1485 // Set new CSS class 1486 if (el.visPropOld.cssclass !== css) { 1487 el.rendNode.className = css; 1488 el.visPropOld.cssclass = css; 1489 el.needsSizeUpdate = true; 1490 } 1491 this.setObjectStrokeColor(el, sc, so); 1492 } else { 1493 this.updateInternalTextStyle(el, sc, so); 1494 } 1495 1496 if (el.evalVisProp('aria.enabled')) { 1497 this.setARIA(el); 1498 } 1499 1500 return this; 1501 }, 1502 1503 /** 1504 * Set color and opacity of internal texts. 1505 * This method is used for Canvas and VML. 1506 * SVG needs its own version. 1507 * @private 1508 * @see JXG.AbstractRenderer#updateTextStyle 1509 * @see JXG.SVGRenderer#updateInternalTextStyle 1510 */ 1511 updateInternalTextStyle: function (el, strokeColor, strokeOpacity) { 1512 this.setObjectStrokeColor(el, strokeColor, strokeOpacity); 1513 }, 1514 1515 /* ********* Image related stuff *********** */ 1516 1517 /** 1518 * Draws an {@link JXG.Image} on a board; This is just a template that has to be implemented by special 1519 * renderers. 1520 * @param {JXG.Image} el Reference to the image object that is to be drawn 1521 * @see Image 1522 * @see JXG.Image 1523 * @see JXG.AbstractRenderer#updateImage 1524 */ 1525 drawImage: function (el) { /* stub */ }, 1526 1527 /** 1528 * Updates the properties of an {@link JXG.Image} element. 1529 * @param {JXG.Image} el Reference to an {@link JXG.Image} object, that has to be updated. 1530 * @see Image 1531 * @see JXG.Image 1532 * @see JXG.AbstractRenderer#drawImage 1533 */ 1534 updateImage: function (el) { 1535 this.updateRectPrim( 1536 el.rendNode, 1537 el.coords.scrCoords[1], 1538 el.coords.scrCoords[2] - el.size[1], 1539 el.size[0], 1540 el.size[1] 1541 ); 1542 1543 this.updateImageURL(el); 1544 this.transformRect(el, el.transformations); 1545 this._updateVisual(el, { stroke: true, dash: true }, true); 1546 }, 1547 1548 /** 1549 * Multiplication of transformations without updating. That means, at that point it is expected that the 1550 * matrices contain numbers only. First, the origin in user coords is translated to <tt>(0,0)</tt> in screen 1551 * coords. Then, the stretch factors are divided out. After the transformations in user coords, the stretch 1552 * factors are multiplied in again, and the origin in user coords is translated back to its position. This 1553 * method does not have to be implemented in a new renderer. 1554 * @param {JXG.GeometryElement} el A JSXGraph element. We only need its board property. 1555 * @param {Array} transformations An array of JXG.Transformations. 1556 * @returns {Array} A matrix represented by a two dimensional array of numbers. 1557 * @see JXG.AbstractRenderer#transformRect 1558 */ 1559 joinTransforms: function (el, transformations) { 1560 var i, 1561 ox = el.board.origin.scrCoords[1], 1562 oy = el.board.origin.scrCoords[2], 1563 ux = el.board.unitX, 1564 uy = el.board.unitY, 1565 1566 len = transformations.length, 1567 // Translate to 0,0 in screen coords and then scale 1568 m = [ 1569 [1, 0, 0], 1570 [-ox / ux, 1 / ux, 0], 1571 [oy / uy, 0, -1 / uy] 1572 ]; 1573 1574 for (i = 0; i < len; i++) { 1575 m = Mat.matMatMult(transformations[i].matrix, m); 1576 } 1577 // Scale back and then translate back 1578 m = Mat.matMatMult( 1579 [ 1580 [1, 0, 0], 1581 [ox, ux, 0], 1582 [oy, 0, -uy] 1583 ], 1584 m 1585 ); 1586 return m; 1587 }, 1588 1589 /** 1590 * Applies transformations on images and text elements. This method has to implemented in 1591 * all descendant classes where text and image transformations are to be supported. 1592 * <p> 1593 * Only affine transformation are supported, no proper projective transformations. This means, the 1594 * respective entries of the transformation matrix are simply ignored. 1595 * 1596 * @param {JXG.Image|JXG.Text} el A {@link JXG.Image} or {@link JXG.Text} object. 1597 * @param {Array} transformations An array of {@link JXG.Transformation} objects. This is usually the 1598 * transformations property of the given element <tt>el</tt>. 1599 */ 1600 transformRect: function (el, transformations) { /* stub */ }, 1601 1602 /** 1603 * If the URL of the image is provided by a function the URL has to be updated during updateImage() 1604 * @param {JXG.Image} el Reference to an image object. 1605 * @see JXG.AbstractRenderer#updateImage 1606 */ 1607 updateImageURL: function (el) { /* stub */ }, 1608 1609 /** 1610 * Updates CSS style properties of a {@link JXG.Image} node. 1611 * In SVGRenderer opacity is the only available style element. 1612 * This function is called by highlight() and nohighlight(). 1613 * This function works for VML. 1614 * It does not work for Canvas. 1615 * SVGRenderer overwrites this method. 1616 * @param {JXG.Text} el Reference to the {@link JXG.Image} object, that has to be updated. 1617 * @param {Boolean} doHighlight 1618 * @see Image 1619 * @see JXG.Image 1620 * @see JXG.AbstractRenderer#highlight 1621 * @see JXG.AbstractRenderer#noHighlight 1622 */ 1623 updateImageStyle: function (el, doHighlight) { 1624 el.rendNode.className = el.evalVisProp( 1625 doHighlight ? 'highlightcssclass' : 'cssclass' 1626 ); 1627 }, 1628 1629 drawForeignObject: function (el) { /* stub */ }, 1630 1631 updateForeignObject: function (el) { 1632 /* stub */ 1633 }, 1634 1635 /* ********* Render primitive objects *********** */ 1636 1637 /** 1638 * Appends a node to a specific layer level. This is just an abstract method and has to be implemented 1639 * in all renderers that want to use the <tt>createPrim</tt> model to draw. 1640 * @param {Node} node A DOM tree node. 1641 * @param {Number} level The layer the node is attached to. This is the index of the layer in 1642 * {@link JXG.SVGRenderer#layer} or the <tt>z-index</tt> style property of the node in VMLRenderer. 1643 */ 1644 appendChildPrim: function (node, level) { /* stub */ }, 1645 1646 /** 1647 * Stores the rendering nodes. This is an abstract method which has to be implemented in all renderers that use 1648 * the <tt>createPrim</tt> method. 1649 * @param {JXG.GeometryElement} el A JSXGraph element. 1650 * @param {String} type The XML node name. Only used in VMLRenderer. 1651 */ 1652 appendNodesToElement: function (el, type) { /* stub */ }, 1653 1654 /** 1655 * Creates a node of a given type with a given id. 1656 * @param {String} type The type of the node to create. 1657 * @param {String} id Set the id attribute to this. 1658 * @returns {Node} Reference to the created node. 1659 */ 1660 createPrim: function (type, id) { /* stub */ return null; }, 1661 1662 /** 1663 * Removes an element node. Just a stub. 1664 * @param {Node} node The node to remove. 1665 */ 1666 remove: function (node) { /* stub */ }, 1667 1668 /** 1669 * Can be used to create the nodes to display arrows. This is an abstract method which has to be implemented 1670 * in any descendant renderer. 1671 * @param {JXG.GeometryElement} el The element the arrows are to be attached to. 1672 * @param {Object} arrowData Data concerning possible arrow heads 1673 * 1674 */ 1675 makeArrows: function (el, arrowData) { /* stub */ }, 1676 1677 /** 1678 * Updates width of an arrow DOM node. Used in 1679 * @param {Node} node The arrow node. 1680 * @param {Number} width 1681 * @param {Node} parentNode Used in IE only 1682 */ 1683 _setArrowWidth: function (node, width, parentNode) { /* stub */ }, 1684 1685 /** 1686 * Updates an ellipse node primitive. This is an abstract method which has to be implemented in all renderers 1687 * that use the <tt>createPrim</tt> method. 1688 * @param {Node} node Reference to the node. 1689 * @param {Number} x Centre X coordinate 1690 * @param {Number} y Centre Y coordinate 1691 * @param {Number} rx The x-axis radius. 1692 * @param {Number} ry The y-axis radius. 1693 */ 1694 updateEllipsePrim: function (node, x, y, rx, ry) { /* stub */ }, 1695 1696 /** 1697 * Refreshes a line node. This is an abstract method which has to be implemented in all renderers that use 1698 * the <tt>createPrim</tt> method. 1699 * @param {Node} node The node to be refreshed. 1700 * @param {Number} p1x The first point's x coordinate. 1701 * @param {Number} p1y The first point's y coordinate. 1702 * @param {Number} p2x The second point's x coordinate. 1703 * @param {Number} p2y The second point's y coordinate. 1704 * @param {JXG.Board} board 1705 */ 1706 updateLinePrim: function (node, p1x, p1y, p2x, p2y, board) { /* stub */ }, 1707 1708 /** 1709 * Updates a path element. This is an abstract method which has to be implemented in all renderers that use 1710 * the <tt>createPrim</tt> method. 1711 * @param {Node} node The path node. 1712 * @param {String} pathString A string formatted like e.g. <em>'M 1,2 L 3,1 L5,5'</em>. The format of the string 1713 * depends on the rendering engine. 1714 * @param {JXG.Board} board Reference to the element's board. 1715 */ 1716 updatePathPrim: function (node, pathString, board) { /* stub */ }, 1717 1718 /** 1719 * Builds a path data string to draw a point with a face other than <em>rect</em> and <em>circle</em>. Since 1720 * the format of such a string usually depends on the renderer this method 1721 * is only an abstract method. Therefore, it has to be implemented in the descendant renderer itself unless 1722 * the renderer does not use the createPrim interface but the draw* interfaces to paint. 1723 * @param {JXG.Point} el The point element 1724 * @param {Number} size A positive number describing the size. Usually the half of the width and height of 1725 * the drawn point. 1726 * @param {String} type A string describing the point's face. This method only accepts the shortcut version of 1727 * each possible face: <tt>x, +, |, -, [], <>, <<>>,^, v, >, < </tt> 1728 */ 1729 updatePathStringPoint: function (el, size, type) { /* stub */ }, 1730 1731 /** 1732 * Builds a path data string from a {@link JXG.Curve} element. Since the path data strings heavily depend on the 1733 * underlying rendering technique this method is just a stub. Although such a path string is of no use for the 1734 * CanvasRenderer, this method is used there to draw a path directly. 1735 * @param {JXG.GeometryElement} el 1736 */ 1737 updatePathStringPrim: function (el) { /* stub */ }, 1738 1739 /** 1740 * Builds a path data string from a {@link JXG.Curve} element such that the curve looks like hand drawn. Since 1741 * the path data strings heavily depend on the underlying rendering technique this method is just a stub. 1742 * Although such a path string is of no use for the CanvasRenderer, this method is used there to draw a path 1743 * directly. 1744 * @param {JXG.GeometryElement} el 1745 */ 1746 updatePathStringBezierPrim: function (el) { /* stub */ }, 1747 1748 /** 1749 * Update a polygon primitive. 1750 * @param {Node} node 1751 * @param {JXG.Polygon} el A JSXGraph element of type {@link JXG.Polygon} 1752 */ 1753 updatePolygonPrim: function (node, el) { /* stub */ }, 1754 1755 /** 1756 * Update a rectangle primitive. This is used only for points with face of type 'rect'. 1757 * @param {Node} node The node yearning to be updated. 1758 * @param {Number} x x coordinate of the top left vertex. 1759 * @param {Number} y y coordinate of the top left vertex. 1760 * @param {Number} w Width of the rectangle. 1761 * @param {Number} h The rectangle's height. 1762 */ 1763 updateRectPrim: function (node, x, y, w, h) { /* stub */ }, 1764 1765 /* ********* Set attributes *********** */ 1766 1767 /** 1768 * Shows or hides an element on the canvas; Only a stub, requires implementation in the derived renderer. 1769 * @param {JXG.GeometryElement} el Reference to the object that has to appear. 1770 * @param {Boolean} value true to show the element, false to hide the element. 1771 */ 1772 display: function (el, value) { 1773 if (el) { 1774 el.visPropOld.visible = value; 1775 } 1776 }, 1777 1778 /** 1779 * Hides an element on the canvas; Only a stub, requires implementation in the derived renderer. 1780 * 1781 * Please use JXG.AbstractRenderer#display instead 1782 * @param {JXG.GeometryElement} el Reference to the geometry element that has to disappear. 1783 * @see JXG.AbstractRenderer#show 1784 * @deprecated 1785 */ 1786 hide: function (el) { /* stub */ }, 1787 1788 /** 1789 * Highlights an object, i.e. changes the current colors of the object to its highlighting colors 1790 * and highlighting strokewidth. 1791 * @param {JXG.GeometryElement} el Reference of the object that will be highlighted. 1792 * @param {Boolean} [suppressHighlightStrokeWidth=undefined] If undefined or false, highlighting also changes strokeWidth. This might not be 1793 * the cases for polygon borders. Thus, if a polygon is highlighted, its polygon borders change strokeWidth only if the polygon attribute 1794 * highlightByStrokeWidth == true. 1795 * @returns {JXG.AbstractRenderer} Reference to the renderer 1796 * @see JXG.AbstractRenderer#updateTextStyle 1797 */ 1798 highlight: function (el, suppressHighlightStrokeWidth) { 1799 var i, do_hl, sw; 1800 1801 this.setObjectTransition(el); 1802 if (!el.visProp.draft) { 1803 if (el.type === Const.OBJECT_TYPE_POLYGON) { 1804 this.setObjectFillColor(el, el.evalVisProp('highlightfillcolor'), el.evalVisProp('highlightfillopacity')); 1805 do_hl = el.evalVisProp('highlightbystrokewidth'); 1806 for (i = 0; i < el.borders.length; i++) { 1807 this.highlight(el.borders[i], !do_hl); 1808 } 1809 } else { 1810 if (el.elementClass === Const.OBJECT_CLASS_TEXT) { 1811 this.updateTextStyle(el, true); 1812 } else if (el.type === Const.OBJECT_TYPE_IMAGE) { 1813 this.updateImageStyle(el, true); 1814 this.setObjectFillColor( 1815 el, 1816 el.evalVisProp('highlightfillcolor'), 1817 el.evalVisProp('highlightfillopacity') 1818 ); 1819 } else { 1820 this.setObjectStrokeColor( 1821 el, 1822 el.evalVisProp('highlightstrokecolor'), 1823 el.evalVisProp('highlightstrokeopacity') 1824 ); 1825 this.setObjectFillColor( 1826 el, 1827 el.evalVisProp('highlightfillcolor'), 1828 el.evalVisProp('highlightfillopacity') 1829 ); 1830 } 1831 } 1832 1833 // Highlight strokeWidth is suppressed if 1834 // parameter suppressHighlightStrokeWidth is false or undefined. 1835 // suppressHighlightStrokeWidth is false if polygon attribute 1836 // highlightbystrokewidth is true. 1837 if (!suppressHighlightStrokeWidth && el.evalVisProp('highlightstrokewidth')) { 1838 sw = Math.max( 1839 el.evalVisProp('highlightstrokewidth'), 1840 el.evalVisProp('strokewidth') 1841 ); 1842 this.setObjectStrokeWidth(el, sw); 1843 if ( 1844 el.elementClass === Const.OBJECT_CLASS_LINE || 1845 el.elementClass === Const.OBJECT_CLASS_CURVE 1846 ) { 1847 this.updatePathWithArrowHeads(el, true); 1848 } 1849 } 1850 } 1851 this.setCssClass(el, el.evalVisProp('highlightcssclass')); 1852 1853 return this; 1854 }, 1855 1856 /** 1857 * Uses the normal colors of an object, i.e. the opposite of {@link JXG.AbstractRenderer#highlight}. 1858 * @param {JXG.GeometryElement} el Reference of the object that will get its normal colors. 1859 * @returns {JXG.AbstractRenderer} Reference to the renderer 1860 * @see JXG.AbstractRenderer#updateTextStyle 1861 */ 1862 noHighlight: function (el) { 1863 var i, sw; 1864 1865 this.setObjectTransition(el); 1866 if (!el.evalVisProp('draft')) { 1867 if (el.type === Const.OBJECT_TYPE_POLYGON) { 1868 this.setObjectFillColor(el, el.evalVisProp('fillcolor'), el.evalVisProp('fillopacity')); 1869 for (i = 0; i < el.borders.length; i++) { 1870 this.noHighlight(el.borders[i]); 1871 } 1872 } else { 1873 if (el.elementClass === Const.OBJECT_CLASS_TEXT) { 1874 this.updateTextStyle(el, false); 1875 } else if (el.type === Const.OBJECT_TYPE_IMAGE) { 1876 this.updateImageStyle(el, false); 1877 this.setObjectFillColor(el, el.evalVisProp('fillcolor'), el.evalVisProp('fillopacity')); 1878 } else { 1879 this.setObjectStrokeColor(el, el.evalVisProp('strokecolor'), el.evalVisProp('strokeopacity')); 1880 this.setObjectFillColor(el, el.evalVisProp('fillcolor'), el.evalVisProp('fillopacity')); 1881 } 1882 } 1883 1884 sw = el.evalVisProp('strokewidth'); 1885 this.setObjectStrokeWidth(el, sw); 1886 if ( 1887 el.elementClass === Const.OBJECT_CLASS_LINE || 1888 el.elementClass === Const.OBJECT_CLASS_CURVE 1889 ) { 1890 this.updatePathWithArrowHeads(el, false); 1891 } 1892 } 1893 this.setCssClass(el, el.evalVisProp('cssclass')); 1894 1895 return this; 1896 }, 1897 1898 /** 1899 * Puts an object from draft mode back into normal mode. 1900 * @param {JXG.GeometryElement} el Reference of the object that no longer is in draft mode. 1901 */ 1902 removeDraft: function (el) { 1903 this.setObjectTransition(el); 1904 if (el.type === Const.OBJECT_TYPE_POLYGON) { 1905 this.setObjectFillColor(el, el.evalVisProp('fillcolor'), el.evalVisProp('fillopacity')); 1906 } else { 1907 if (el.type === Const.OBJECT_CLASS_POINT) { 1908 this.setObjectFillColor(el, el.evalVisProp('fillcolor'), el.evalVisProp('fillopacity')); 1909 } 1910 this.setObjectStrokeColor(el, el.evalVisProp('strokecolor'), el.evalVisProp('strokeopacity')); 1911 this.setObjectStrokeWidth(el, el.evalVisProp('strokewidth')); 1912 } 1913 }, 1914 1915 /** 1916 * Set ARIA related properties of an element. The attribute "aria" of an element contains at least the 1917 * properties "enabled", "label", and "live". Additionally, all available properties from 1918 * {@link https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA} may be set. 1919 * <p> 1920 * In JSXGraph, the available properties are used without the leading 'aria-'. 1921 * For example, the value of the JSXGraph attribute 'aria.label' will be set to the 1922 * HTML attribute 'aria-label'. 1923 * 1924 * @param {JXG.GeometryElement} el Reference of the object that wants new 1925 * ARIA attributes. 1926 */ 1927 setARIA: function(el) { /* stub */ }, 1928 1929 /** 1930 * Sets the buffering as recommended by SVGWG. Until now only Opera supports this and will be ignored by other 1931 * browsers. Although this feature is only supported by SVG we have this method in {@link JXG.AbstractRenderer} 1932 * because it is called from outside the renderer. 1933 * @param {Node} node The SVG DOM Node which buffering type to update. 1934 * @param {String} type Either 'auto', 'dynamic', or 'static'. For an explanation see 1935 * {@link https://www.w3.org/TR/SVGTiny12/painting.html#BufferedRenderingProperty}. 1936 */ 1937 setBuffering: function (node, type) { /* stub */ }, 1938 1939 /** 1940 * Clip element to the JSXGraph container element (div). To be precise: to the SVG node 1941 * in case of SVG rendering - the only renderer, where this is relevant. 1942 * 1943 * @param {JXG.GeometryElement} el Reference of the object 1944 * @param {Boolean} val true: clip to the JSXGraph div, false: do not clip 1945 * @see JXG.SVGRenderer#setClipPathRect 1946 */ 1947 setClipPath: function(el, val) { /* stub */ }, 1948 1949 /** 1950 * Sets CSS classes for elements (relevant for SVG only). 1951 * 1952 * @param {JXG.GeometryElement} el Reference of the object that wants a 1953 * new set of CSS classes. 1954 * @param {String} cssClass String containing a space separated list of CSS classes. 1955 */ 1956 setCssClass: function (el, cssClass) { /* stub */ }, 1957 1958 /** 1959 * Sets an element's dash style. 1960 * @param {JXG.GeometryElement} el An JSXGraph element. 1961 */ 1962 setDashStyle: function (el) { /* stub */ }, 1963 1964 /** 1965 * Puts an object into draft mode, i.e. it's visual appearance will be changed. For GEONE<sub>x</sub>T backwards 1966 * compatibility. 1967 * @param {JXG.GeometryElement} el Reference of the object that is in draft mode. 1968 */ 1969 setDraft: function (el) { 1970 if (!el.evalVisProp('draft')) { 1971 return; 1972 } 1973 var draftColor = el.board.options.elements.draft.color, 1974 draftOpacity = el.board.options.elements.draft.opacity; 1975 1976 this.setObjectTransition(el); 1977 if (el.type === Const.OBJECT_TYPE_POLYGON) { 1978 this.setObjectFillColor(el, draftColor, draftOpacity); 1979 } else { 1980 if (el.elementClass === Const.OBJECT_CLASS_POINT) { 1981 this.setObjectFillColor(el, draftColor, draftOpacity); 1982 } else { 1983 this.setObjectFillColor(el, "none", 0); 1984 } 1985 this.setObjectStrokeColor(el, draftColor, draftOpacity); 1986 this.setObjectStrokeWidth(el, el.board.options.elements.draft.strokeWidth); 1987 } 1988 }, 1989 1990 /** 1991 * Sets up nodes for rendering a gradient fill. 1992 * @param {JXG.GeometryElement} el Reference of the object which gets the gradient 1993 */ 1994 setGradient: function (el) { /* stub */ }, 1995 1996 /** 1997 * Move element into new layer. This is trivial for canvas, but needs more effort in SVG. 1998 * Does not work dynamically, i.e. if level is a function. 1999 * 2000 * @param {JXG.GeometryElement} el Element which is put into different layer 2001 * @param {Number} value Layer number 2002 * @private 2003 */ 2004 setLayer: function (el, level) { /* stub */ }, 2005 2006 /** 2007 * Sets an objects fill color. 2008 * @param {JXG.GeometryElement} el Reference of the object that wants a new fill color. 2009 * @param {String} color Color in a HTML/CSS compatible format. If you don't want any fill color at all, choose 2010 * 'none'. 2011 * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1. 2012 */ 2013 setObjectFillColor: function (el, color, opacity) { /* stub */ }, 2014 2015 /** 2016 * Changes an objects stroke color to the given color. 2017 * @param {JXG.GeometryElement} el Reference of the {@link JXG.GeometryElement} that gets a new stroke 2018 * color. 2019 * @param {String} color Color value in a HTML compatible format, e.g. <strong>#00ff00</strong> or 2020 * <strong>green</strong> for green. 2021 * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1. 2022 */ 2023 setObjectStrokeColor: function (el, color, opacity) { /* stub */ }, 2024 2025 /** 2026 * Sets an element's stroke width. 2027 * @param {JXG.GeometryElement} el Reference to the geometry element. 2028 * @param {Number} width The new stroke width to be assigned to the element. 2029 */ 2030 setObjectStrokeWidth: function (el, width) { /* stub */ }, 2031 2032 /** 2033 * Sets the transition duration (in milliseconds) for fill color and stroke 2034 * color and opacity. 2035 * @param {JXG.GeometryElement} el Reference of the object that wants a 2036 * new transition duration. 2037 * @param {Number} duration (Optional) duration in milliseconds. If not given, 2038 * element.visProp.transitionDuration is taken. This is the default. 2039 */ 2040 setObjectTransition: function (el, duration) { /* stub */ }, 2041 2042 /** 2043 * Sets a node's attribute. 2044 * @param {Node} node The node that is to be updated. 2045 * @param {String} key Name of the attribute. 2046 * @param {String} val New value for the attribute. 2047 */ 2048 setPropertyPrim: function (node, key, val) { /* stub */ }, 2049 2050 /** 2051 * Sets the shadow properties to a geometry element. This method is only a stub, it is implemented in the actual 2052 * renderers. 2053 * @param {JXG.GeometryElement} el Reference to a geometry object, that should get a shadow 2054 */ 2055 setShadow: function (el) { /* stub */ }, 2056 2057 /** 2058 * Set the attribute `tabindex` to the attribute `tabindex` of an element. 2059 * This is only relevant for the SVG renderer. 2060 * 2061 * @param {JXG.GeometryElement} el 2062 */ 2063 setTabindex: function (el) { /* stub */ }, 2064 2065 /** 2066 * Shows a hidden element on the canvas; Only a stub, requires implementation in the derived renderer. 2067 * 2068 * Please use JXG.AbstractRenderer#display instead 2069 * @param {JXG.GeometryElement} el Reference to the object that has to appear. 2070 * @see JXG.AbstractRenderer#hide 2071 * @deprecated 2072 */ 2073 show: function (el) { /* stub */ }, 2074 2075 /** 2076 * Updates the gradient fill. 2077 * @param {JXG.GeometryElement} el An JSXGraph element with an area that can be filled. 2078 */ 2079 updateGradient: function (el) { /* stub */ }, 2080 2081 /* ********* Renderer control *********** */ 2082 2083 /** 2084 * Stop redraw. This method is called before every update, so a non-vector-graphics based renderer can use this 2085 * method to delete the contents of the drawing panel. This is an abstract method every descendant renderer 2086 * should implement, if appropriate. 2087 * @see JXG.AbstractRenderer#unsuspendRedraw 2088 */ 2089 suspendRedraw: function () { /* stub */ }, 2090 2091 /** 2092 * Restart redraw. This method is called after updating all the rendering node attributes. 2093 * @see JXG.AbstractRenderer#suspendRedraw 2094 */ 2095 unsuspendRedraw: function () { /* stub */ }, 2096 2097 /** 2098 * The tiny zoom bar shown on the bottom of a board (if board attribute "showNavigation" is true). 2099 * It is a div element and gets the CSS class "JXG_navigation" and the id {board id}_navigationbar. 2100 * <p> 2101 * The buttons get the CSS class "JXG_navigation_button" and the id {board_id}_name where name is 2102 * one of [top, down, left, right, out, 100, in, fullscreen, screenshot, reload, cleartraces]. 2103 * <p> 2104 * The symbols for zoom, navigation and reload are hard-coded. 2105 * 2106 * @param {JXG.Board} board Reference to a JSXGraph board. 2107 * @param {Object} attr Attributes of the navigation bar 2108 * @private 2109 */ 2110 drawNavigationBar: function (board, attr) { 2111 var doc, 2112 node, 2113 cancelbubble = function (e) { 2114 if (!e) { 2115 e = window.event; 2116 } 2117 2118 if (e.stopPropagation) { 2119 // Non IE<=8 2120 e.stopPropagation(); 2121 } else { 2122 e.cancelBubble = true; 2123 } 2124 }, 2125 createButton = function (label, handler, board_id, type) { 2126 var button; 2127 2128 board_id = board_id || ""; 2129 2130 button = doc.createElement('span'); 2131 button.innerHTML = label; // button.appendChild(doc.createTextNode(label)); 2132 2133 // Style settings are superseded by adding the CSS class below 2134 button.style.paddingLeft = '7px'; 2135 button.style.paddingRight = '7px'; 2136 2137 if (button.classList !== undefined) { 2138 // classList not available in IE 9 2139 button.classList.add("JXG_navigation_button"); 2140 button.classList.add("JXG_navigation_button_" + type); 2141 } 2142 // button.setAttribute('tabindex', 0); 2143 2144 button.setAttribute("id", board_id + '_navigation_' + type); 2145 button.setAttribute("aria-hidden", 'true'); // navigation buttons should never appear in screen reader 2146 2147 node.appendChild(button); 2148 2149 Env.addEvent( 2150 button, 2151 "click", 2152 function (e) { 2153 Type.bind(handler, board)(); 2154 return false; 2155 }, 2156 board 2157 ); 2158 // prevent the click from bubbling down to the board 2159 Env.addEvent(button, "pointerup", cancelbubble, board); 2160 Env.addEvent(button, "pointerdown", cancelbubble, board); 2161 Env.addEvent(button, "pointerleave", cancelbubble, board); 2162 Env.addEvent(button, "mouseup", cancelbubble, board); 2163 Env.addEvent(button, "mousedown", cancelbubble, board); 2164 Env.addEvent(button, "touchend", cancelbubble, board); 2165 Env.addEvent(button, "touchstart", cancelbubble, board); 2166 }; 2167 2168 if (Env.isBrowser && this.type !== 'no') { 2169 doc = board.containerObj.ownerDocument; 2170 node = doc.createElement('div'); 2171 2172 node.setAttribute("id", board.container + "_navigationbar"); 2173 2174 // Style settings are superseded by adding the CSS class below 2175 node.style.color = attr.strokecolor; 2176 node.style.backgroundColor = attr.fillcolor; 2177 node.style.padding = attr.padding; 2178 node.style.position = attr.position; 2179 node.style.fontSize = attr.fontsize; 2180 node.style.cursor = attr.cursor; 2181 node.style.zIndex = attr.zindex; 2182 board.containerObj.appendChild(node); 2183 node.style.right = attr.right; 2184 node.style.bottom = attr.bottom; 2185 2186 if (node.classList !== undefined) { 2187 // classList not available in IE 9 2188 node.classList.add("JXG_navigation"); 2189 } 2190 // For XHTML we need unicode instead of HTML entities 2191 2192 if (board.attr.showfullscreen) { 2193 createButton( 2194 board.attr.fullscreen.symbol, 2195 function () { 2196 board.toFullscreen(board.attr.fullscreen.id); 2197 }, 2198 board.container, "fullscreen" 2199 ); 2200 } 2201 2202 if (board.attr.showscreenshot) { 2203 createButton( 2204 board.attr.screenshot.symbol, 2205 function () { 2206 window.setTimeout(function () { 2207 board.renderer.screenshot(board, "", false); 2208 }, 330); 2209 }, 2210 board.container, "screenshot" 2211 ); 2212 } 2213 2214 if (board.attr.showreload) { 2215 // full reload circle: \u27F2 2216 // the board.reload() method does not exist during the creation 2217 // of this button. That's why this anonymous function wrapper is required. 2218 createButton( 2219 "\u21BB", 2220 function () { 2221 board.reload(); 2222 }, 2223 board.container, "reload" 2224 ); 2225 } 2226 2227 if (board.attr.showcleartraces) { 2228 // clear traces symbol (otimes): \u27F2 2229 createButton("\u2297", 2230 function () { 2231 board.clearTraces(); 2232 }, 2233 board.container, "cleartraces" 2234 ); 2235 } 2236 2237 if (board.attr.shownavigation) { 2238 if (board.attr.showzoom) { 2239 createButton("\u2013", board.zoomOut, board.container, 'out'); 2240 createButton("o", board.zoom100, board.container, '100'); 2241 createButton("+", board.zoomIn, board.container, 'in'); 2242 } 2243 createButton("\u2190", board.clickLeftArrow, board.container, 'left'); 2244 createButton("\u2193", board.clickUpArrow, board.container, 'down'); // Down arrow 2245 createButton("\u2191", board.clickDownArrow, board.container, 'up'); // Up arrow 2246 createButton("\u2192", board.clickRightArrow, board.container, 'right'); 2247 } 2248 } 2249 }, 2250 2251 /** 2252 * Wrapper for getElementById for maybe other renderers which elements are not directly accessible by DOM 2253 * methods like document.getElementById(). 2254 * @param {String} id Unique identifier for element. 2255 * @returns {Object} Reference to a JavaScript object. In case of SVG/VMLRenderer it's a reference to a SVG/VML node. 2256 */ 2257 getElementById: function (id) { 2258 var str; 2259 if (Type.exists(this.container)) { 2260 // Use querySelector over getElementById for compatibility with both 'regular' document 2261 // and ShadowDOM fragments. 2262 str = this.container.id + '_' + id; 2263 // Mask special symbols like '/' and '\' in id 2264 if (Type.exists(CSS) && Type.exists(CSS.escape)) { 2265 str = CSS.escape(str); 2266 } 2267 return this.container.querySelector('#' + str); 2268 } 2269 return ""; 2270 }, 2271 2272 /** 2273 * Remove an element and provide a function that inserts it into its original position. This method 2274 * is taken from this article {@link https://developers.google.com/speed/articles/javascript-dom}. 2275 * @author KeeKim Heng, Google Web Developer 2276 * @param {Element} el The element to be temporarily removed 2277 * @returns {Function} A function that inserts the element into its original position 2278 */ 2279 removeToInsertLater: function (el) { 2280 var parentNode = el.parentNode, 2281 nextSibling = el.nextSibling; 2282 2283 if (parentNode === null) { 2284 return; 2285 } 2286 parentNode.removeChild(el); 2287 2288 return function () { 2289 if (nextSibling) { 2290 parentNode.insertBefore(el, nextSibling); 2291 } else { 2292 parentNode.appendChild(el); 2293 } 2294 }; 2295 }, 2296 2297 /** 2298 * Resizes the rendering element 2299 * @param {Number} w New width 2300 * @param {Number} h New height 2301 */ 2302 resize: function (w, h) { /* stub */ }, 2303 2304 /** 2305 * Create crosshair elements (Fadenkreuz) for presentations. 2306 * @param {Number} n Number of crosshairs. 2307 */ 2308 createTouchpoints: function (n) { /* stub */ }, 2309 2310 /** 2311 * Show a specific crosshair. 2312 * @param {Number} i Number of the crosshair to show 2313 */ 2314 showTouchpoint: function (i) { /* stub */ }, 2315 2316 /** 2317 * Hide a specific crosshair. 2318 * @param {Number} i Number of the crosshair to show 2319 */ 2320 hideTouchpoint: function (i) { /* stub */ }, 2321 2322 /** 2323 * Move a specific crosshair. 2324 * @param {Number} i Number of the crosshair to show 2325 * @param {Array} pos New positon in screen coordinates 2326 */ 2327 updateTouchpoint: function (i, pos) { /* stub */ }, 2328 2329 /* ********* Dump related stuff *********** */ 2330 2331 /** 2332 * Convert SVG construction to base64 encoded SVG data URL. 2333 * Only available on SVGRenderer. 2334 * 2335 * @see JXG.SVGRenderer#dumpToDataURI 2336 */ 2337 dumpToDataURI: function (_ignoreTexts) { /* stub */ }, 2338 2339 /** 2340 * Convert SVG construction to canvas. 2341 * Only available on SVGRenderer. 2342 * 2343 * @see JXG.SVGRenderer#dumpToCanvas 2344 */ 2345 dumpToCanvas: function (canvasId, w, h, _ignoreTexts) { /* stub */ }, 2346 2347 /** 2348 * Display SVG image in html img-tag which enables 2349 * easy download for the user. 2350 * 2351 * See JXG.SVGRenderer#screenshot 2352 */ 2353 screenshot: function (board) { /* stub */ } 2354 2355 } 2356 ); 2357 2358 export default JXG.AbstractRenderer; 2359