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