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 * Converts string containing CSS properties into 1393 * array with key-value pair objects. 1394 * 1395 * @example 1396 * "color:blue; background-color:yellow" is converted to 1397 * [{'color': 'blue'}, {'backgroundColor': 'yellow'}] 1398 * 1399 * @param {String} cssString String containing CSS properties 1400 * @return {Array} Array of CSS key-value pairs 1401 */ 1402 _css2js: function (cssString) { 1403 var pairs = [], 1404 i, 1405 len, 1406 key, 1407 val, 1408 s, 1409 list = Type.trim(cssString).replace(/;$/, "").split(";"); 1410 1411 len = list.length; 1412 for (i = 0; i < len; ++i) { 1413 if (Type.trim(list[i]) !== "") { 1414 s = list[i].split(":"); 1415 key = Type.trim( 1416 s[0].replace(/-([a-z])/gi, function (match, char) { 1417 return char.toUpperCase(); 1418 }) 1419 ); 1420 val = Type.trim(s[1]); 1421 pairs.push({ key: key, val: val }); 1422 } 1423 } 1424 return pairs; 1425 }, 1426 1427 /** 1428 * Updates font-size, color and opacity properties and CSS style properties of a {@link JXG.Text} node. 1429 * This function is also called by highlight() and nohighlight(). 1430 * @param {JXG.Text} el Reference to the {@link JXG.Text} object, that has to be updated. 1431 * @param {Boolean} doHighlight 1432 * @see Text 1433 * @see JXG.Text 1434 * @see JXG.AbstractRenderer#drawText 1435 * @see JXG.AbstractRenderer#drawInternalText 1436 * @see JXG.AbstractRenderer#updateText 1437 * @see JXG.AbstractRenderer#updateInternalText 1438 * @see JXG.AbstractRenderer#updateInternalTextStyle 1439 */ 1440 updateTextStyle: function (el, doHighlight) { 1441 var fs, 1442 so, sc, 1443 css, 1444 node, 1445 display = Env.isBrowser ? el.visProp.display : "internal", 1446 nodeList = ["rendNode", "rendNodeTag", "rendNodeLabel"], 1447 lenN = nodeList.length, 1448 fontUnit = el.evalVisProp('fontunit'), 1449 cssList, 1450 prop, 1451 style, 1452 cssString, 1453 styleList = ["cssdefaultstyle", "cssstyle"], 1454 lenS = styleList.length; 1455 1456 if (doHighlight) { 1457 sc = el.evalVisProp('highlightstrokecolor'); 1458 so = el.evalVisProp('highlightstrokeopacity'); 1459 css = el.evalVisProp('highlightcssclass'); 1460 } else { 1461 sc = el.evalVisProp('strokecolor'); 1462 so = el.evalVisProp('strokeopacity'); 1463 css = el.evalVisProp('cssclass'); 1464 } 1465 1466 // This part is executed for all text elements except internal texts in canvas. 1467 // HTML-texts or internal texts in SVG or VML. 1468 // HTML internal 1469 // SVG + + 1470 // VML + + 1471 // canvas + - 1472 // no - - 1473 if (this.type !== "no" && (display === "html" || this.type !== 'canvas')) { 1474 for (style = 0; style < lenS; style++) { 1475 // First set cssString to 1476 // ev.cssdefaultstyle of ev.highlightcssdefaultstyle, 1477 // then to 1478 // ev.cssstyle of ev.highlightcssstyle 1479 cssString = el.evalVisProp( 1480 (doHighlight ? 'highlight' : '') + styleList[style] 1481 ); 1482 // Set the CSS style properties - without deleting other properties 1483 for (node = 0; node < lenN; node++) { 1484 if (Type.exists(el[nodeList[node]])) { 1485 if (cssString !== "" && el.visPropOld[styleList[style] + '_' + node] !== cssString) { 1486 cssList = this._css2js(cssString); 1487 for (prop in cssList) { 1488 if (cssList.hasOwnProperty(prop)) { 1489 el[nodeList[node]].style[cssList[prop].key] = cssList[prop].val; 1490 } 1491 } 1492 el.visPropOld[styleList[style] + '_' + node] = cssString; 1493 } 1494 } 1495 // el.visPropOld[styleList[style]] = cssString; 1496 } 1497 } 1498 1499 fs = el.evalVisProp('fontsize'); 1500 if (el.visPropOld.fontsize !== fs) { 1501 el.needsSizeUpdate = true; 1502 try { 1503 for (node = 0; node < lenN; node++) { 1504 if (Type.exists(el[nodeList[node]])) { 1505 el[nodeList[node]].style.fontSize = fs + fontUnit; 1506 } 1507 } 1508 } catch (e) { 1509 // IE needs special treatment. 1510 for (node = 0; node < lenN; node++) { 1511 if (Type.exists(el[nodeList[node]])) { 1512 el[nodeList[node]].style.fontSize = fs; 1513 } 1514 } 1515 } 1516 el.visPropOld.fontsize = fs; 1517 } 1518 } 1519 1520 this.setTabindex(el); 1521 1522 this.setObjectTransition(el); 1523 if (display === "html" && this.type !== 'no') { 1524 // Set new CSS class 1525 if (el.visPropOld.cssclass !== css) { 1526 el.rendNode.className = css; 1527 el.visPropOld.cssclass = css; 1528 el.needsSizeUpdate = true; 1529 } 1530 this.setObjectStrokeColor(el, sc, so); 1531 } else { 1532 this.updateInternalTextStyle(el, sc, so); 1533 } 1534 1535 if (el.evalVisProp('aria.enabled')) { 1536 this.setARIA(el); 1537 } 1538 1539 return this; 1540 }, 1541 1542 /** 1543 * Set color and opacity of internal texts. 1544 * This method is used for Canvas and VML. 1545 * SVG needs its own version. 1546 * @private 1547 * @see JXG.AbstractRenderer#updateTextStyle 1548 * @see JXG.SVGRenderer#updateInternalTextStyle 1549 */ 1550 updateInternalTextStyle: function (el, strokeColor, strokeOpacity) { 1551 this.setObjectStrokeColor(el, strokeColor, strokeOpacity); 1552 }, 1553 1554 /* ********* Image related stuff *********** */ 1555 1556 /** 1557 * Draws an {@link JXG.Image} on a board; This is just a template that has to be implemented by special 1558 * renderers. 1559 * @param {JXG.Image} el Reference to the image object that is to be drawn 1560 * @see Image 1561 * @see JXG.Image 1562 * @see JXG.AbstractRenderer#updateImage 1563 */ 1564 drawImage: function (el) { /* stub */ }, 1565 1566 /** 1567 * Updates the properties of an {@link JXG.Image} element. 1568 * @param {JXG.Image} el Reference to an {@link JXG.Image} object, that has to be updated. 1569 * @see Image 1570 * @see JXG.Image 1571 * @see JXG.AbstractRenderer#drawImage 1572 */ 1573 updateImage: function (el) { 1574 this.updateRectPrim( 1575 el.rendNode, 1576 el.coords.scrCoords[1], 1577 el.coords.scrCoords[2] - el.size[1], 1578 el.size[0], 1579 el.size[1] 1580 ); 1581 1582 this.updateImageURL(el); 1583 this.transformRect(el, el.transformations); 1584 this._updateVisual(el, { stroke: true, dash: true }, true); 1585 }, 1586 1587 /** 1588 * Multiplication of transformations without updating. That means, at that point it is expected that the 1589 * matrices contain numbers only. First, the origin in user coords is translated to <tt>(0,0)</tt> in screen 1590 * coords. Then, the stretch factors are divided out. After the transformations in user coords, the stretch 1591 * factors are multiplied in again, and the origin in user coords is translated back to its position. This 1592 * method does not have to be implemented in a new renderer. 1593 * @param {JXG.GeometryElement} el A JSXGraph element. We only need its board property. 1594 * @param {Array} transformations An array of JXG.Transformations. 1595 * @returns {Array} A matrix represented by a two dimensional array of numbers. 1596 * @see JXG.AbstractRenderer#transformRect 1597 */ 1598 joinTransforms: function (el, transformations) { 1599 var i, 1600 ox = el.board.origin.scrCoords[1], 1601 oy = el.board.origin.scrCoords[2], 1602 ux = el.board.unitX, 1603 uy = el.board.unitY, 1604 1605 len = transformations.length, 1606 // Translate to 0,0 in screen coords and then scale 1607 m = [ 1608 [1, 0, 0], 1609 [-ox / ux, 1 / ux, 0], 1610 [oy / uy, 0, -1 / uy] 1611 ]; 1612 1613 for (i = 0; i < len; i++) { 1614 m = Mat.matMatMult(transformations[i].matrix, m); 1615 } 1616 // Scale back and then translate back 1617 m = Mat.matMatMult( 1618 [ 1619 [1, 0, 0], 1620 [ox, ux, 0], 1621 [oy, 0, -uy] 1622 ], 1623 m 1624 ); 1625 return m; 1626 }, 1627 1628 /** 1629 * Applies transformations on images and text elements. This method has to implemented in 1630 * all descendant classes where text and image transformations are to be supported. 1631 * <p> 1632 * Only affine transformation are supported, no proper projective transformations. This means, the 1633 * respective entries of the transformation matrix are simply ignored. 1634 * 1635 * @param {JXG.Image|JXG.Text} el A {@link JXG.Image} or {@link JXG.Text} object. 1636 * @param {Array} transformations An array of {@link JXG.Transformation} objects. This is usually the 1637 * transformations property of the given element <tt>el</tt>. 1638 */ 1639 transformRect: function (el, transformations) { /* stub */ }, 1640 1641 /** 1642 * If the URL of the image is provided by a function the URL has to be updated during updateImage() 1643 * @param {JXG.Image} el Reference to an image object. 1644 * @see JXG.AbstractRenderer#updateImage 1645 */ 1646 updateImageURL: function (el) { /* stub */ }, 1647 1648 /** 1649 * Updates CSS style properties of a {@link JXG.Image} node. 1650 * In SVGRenderer opacity is the only available style element. 1651 * This function is called by highlight() and nohighlight(). 1652 * This function works for VML. 1653 * It does not work for Canvas. 1654 * SVGRenderer overwrites this method. 1655 * @param {JXG.Text} el Reference to the {@link JXG.Image} object, that has to be updated. 1656 * @param {Boolean} doHighlight 1657 * @see Image 1658 * @see JXG.Image 1659 * @see JXG.AbstractRenderer#highlight 1660 * @see JXG.AbstractRenderer#noHighlight 1661 */ 1662 updateImageStyle: function (el, doHighlight) { 1663 el.rendNode.className = el.evalVisProp( 1664 doHighlight ? 'highlightcssclass' : 'cssclass' 1665 ); 1666 }, 1667 1668 drawForeignObject: function (el) { /* stub */ }, 1669 1670 updateForeignObject: function (el) { 1671 /* stub */ 1672 }, 1673 1674 /* ********* Render primitive objects *********** */ 1675 1676 /** 1677 * Appends a node to a specific layer level. This is just an abstract method and has to be implemented 1678 * in all renderers that want to use the <tt>createPrim</tt> model to draw. 1679 * @param {Node} node A DOM tree node. 1680 * @param {Number} level The layer the node is attached to. This is the index of the layer in 1681 * {@link JXG.SVGRenderer#layer} or the <tt>z-index</tt> style property of the node in VMLRenderer. 1682 */ 1683 appendChildPrim: function (node, level) { /* stub */ }, 1684 1685 /** 1686 * Stores the rendering nodes. This is an abstract method which has to be implemented in all renderers that use 1687 * the <tt>createPrim</tt> method. 1688 * @param {JXG.GeometryElement} el A JSXGraph element. 1689 * @param {String} type The XML node name. Only used in VMLRenderer. 1690 */ 1691 appendNodesToElement: function (el, type) { /* stub */ }, 1692 1693 /** 1694 * Creates a node of a given type with a given id. 1695 * @param {String} type The type of the node to create. 1696 * @param {String} id Set the id attribute to this. 1697 * @returns {Node} Reference to the created node. 1698 */ 1699 createPrim: function (type, id) { /* stub */ return null; }, 1700 1701 /** 1702 * Removes an element node. Just a stub. 1703 * @param {Node} node The node to remove. 1704 */ 1705 remove: function (node) { /* stub */ }, 1706 1707 /** 1708 * Can be used to create the nodes to display arrows. This is an abstract method which has to be implemented 1709 * in any descendant renderer. 1710 * @param {JXG.GeometryElement} el The element the arrows are to be attached to. 1711 * @param {Object} arrowData Data concerning possible arrow heads 1712 * 1713 */ 1714 makeArrows: function (el, arrowData) { /* stub */ }, 1715 1716 /** 1717 * Updates width of an arrow DOM node. Used in 1718 * @param {Node} node The arrow node. 1719 * @param {Number} width 1720 * @param {Node} parentNode Used in IE only 1721 */ 1722 _setArrowWidth: function (node, width, parentNode) { /* stub */ }, 1723 1724 /** 1725 * Updates an ellipse node primitive. This is an abstract method which has to be implemented in all renderers 1726 * that use the <tt>createPrim</tt> method. 1727 * @param {Node} node Reference to the node. 1728 * @param {Number} x Centre X coordinate 1729 * @param {Number} y Centre Y coordinate 1730 * @param {Number} rx The x-axis radius. 1731 * @param {Number} ry The y-axis radius. 1732 */ 1733 updateEllipsePrim: function (node, x, y, rx, ry) { /* stub */ }, 1734 1735 /** 1736 * Refreshes a line node. This is an abstract method which has to be implemented in all renderers that use 1737 * the <tt>createPrim</tt> method. 1738 * @param {Node} node The node to be refreshed. 1739 * @param {Number} p1x The first point's x coordinate. 1740 * @param {Number} p1y The first point's y coordinate. 1741 * @param {Number} p2x The second point's x coordinate. 1742 * @param {Number} p2y The second point's y coordinate. 1743 * @param {JXG.Board} board 1744 */ 1745 updateLinePrim: function (node, p1x, p1y, p2x, p2y, board) { /* stub */ }, 1746 1747 /** 1748 * Updates a path element. This is an abstract method which has to be implemented in all renderers that use 1749 * the <tt>createPrim</tt> method. 1750 * @param {Node} node The path node. 1751 * @param {String} pathString A string formatted like e.g. <em>'M 1,2 L 3,1 L5,5'</em>. The format of the string 1752 * depends on the rendering engine. 1753 * @param {JXG.Board} board Reference to the element's board. 1754 */ 1755 updatePathPrim: function (node, pathString, board) { /* stub */ }, 1756 1757 /** 1758 * Builds a path data string to draw a point with a face other than <em>rect</em> and <em>circle</em>. Since 1759 * the format of such a string usually depends on the renderer this method 1760 * is only an abstract method. Therefore, it has to be implemented in the descendant renderer itself unless 1761 * the renderer does not use the createPrim interface but the draw* interfaces to paint. 1762 * @param {JXG.Point} el The point element 1763 * @param {Number} size A positive number describing the size. Usually the half of the width and height of 1764 * the drawn point. 1765 * @param {String} type A string describing the point's face. This method only accepts the shortcut version of 1766 * each possible face: <tt>x, +, |, -, [], <>, <<>>,^, v, >, < </tt> 1767 */ 1768 updatePathStringPoint: function (el, size, type) { /* stub */ }, 1769 1770 /** 1771 * Builds a path data string from a {@link JXG.Curve} element. Since the path data strings heavily depend on the 1772 * underlying rendering technique this method is just a stub. Although such a path string is of no use for the 1773 * CanvasRenderer, this method is used there to draw a path directly. 1774 * @param {JXG.GeometryElement} el 1775 */ 1776 updatePathStringPrim: function (el) { /* stub */ }, 1777 1778 /** 1779 * Builds a path data string from a {@link JXG.Curve} element such that the curve looks like hand drawn. Since 1780 * the path data strings heavily depend on the underlying rendering technique this method is just a stub. 1781 * Although such a path string is of no use for the CanvasRenderer, this method is used there to draw a path 1782 * directly. 1783 * @param {JXG.GeometryElement} el 1784 */ 1785 updatePathStringBezierPrim: function (el) { /* stub */ }, 1786 1787 /** 1788 * Update a polygon primitive. 1789 * @param {Node} node 1790 * @param {JXG.Polygon} el A JSXGraph element of type {@link JXG.Polygon} 1791 */ 1792 updatePolygonPrim: function (node, el) { /* stub */ }, 1793 1794 /** 1795 * Update a rectangle primitive. This is used only for points with face of type 'rect'. 1796 * @param {Node} node The node yearning to be updated. 1797 * @param {Number} x x coordinate of the top left vertex. 1798 * @param {Number} y y coordinate of the top left vertex. 1799 * @param {Number} w Width of the rectangle. 1800 * @param {Number} h The rectangle's height. 1801 */ 1802 updateRectPrim: function (node, x, y, w, h) { /* stub */ }, 1803 1804 /* ********* Set attributes *********** */ 1805 1806 /** 1807 * Shows or hides an element on the canvas; Only a stub, requires implementation in the derived renderer. 1808 * @param {JXG.GeometryElement} el Reference to the object that has to appear. 1809 * @param {Boolean} value true to show the element, false to hide the element. 1810 */ 1811 display: function (el, value) { 1812 if (el) { 1813 el.visPropOld.visible = value; 1814 } 1815 }, 1816 1817 /** 1818 * Hides an element on the canvas; Only a stub, requires implementation in the derived renderer. 1819 * 1820 * Please use JXG.AbstractRenderer#display instead 1821 * @param {JXG.GeometryElement} el Reference to the geometry element that has to disappear. 1822 * @see JXG.AbstractRenderer#show 1823 * @deprecated 1824 */ 1825 hide: function (el) { /* stub */ }, 1826 1827 /** 1828 * Highlights an object, i.e. changes the current colors of the object to its highlighting colors 1829 * and highlighting strokewidth. 1830 * @param {JXG.GeometryElement} el Reference of the object that will be highlighted. 1831 * @param {Boolean} [suppressHighlightStrokeWidth=undefined] If undefined or false, highlighting also changes strokeWidth. This might not be 1832 * the cases for polygon borders. Thus, if a polygon is highlighted, its polygon borders change strokeWidth only if the polygon attribute 1833 * highlightByStrokeWidth == true. 1834 * @returns {JXG.AbstractRenderer} Reference to the renderer 1835 * @see JXG.AbstractRenderer#updateTextStyle 1836 */ 1837 highlight: function (el, suppressHighlightStrokeWidth) { 1838 var i, do_hl, sw; 1839 1840 this.setObjectTransition(el); 1841 if (!el.visProp.draft) { 1842 if (el.type === Const.OBJECT_TYPE_POLYGON) { 1843 this.setObjectFillColor(el, el.evalVisProp('highlightfillcolor'), el.evalVisProp('highlightfillopacity')); 1844 do_hl = el.evalVisProp('highlightbystrokewidth'); 1845 for (i = 0; i < el.borders.length; i++) { 1846 this.highlight(el.borders[i], !do_hl); 1847 } 1848 } else { 1849 if (el.elementClass === Const.OBJECT_CLASS_TEXT) { 1850 this.updateTextStyle(el, true); 1851 } else if (el.type === Const.OBJECT_TYPE_IMAGE) { 1852 this.updateImageStyle(el, true); 1853 this.setObjectFillColor( 1854 el, 1855 el.evalVisProp('highlightfillcolor'), 1856 el.evalVisProp('highlightfillopacity') 1857 ); 1858 } else { 1859 this.setObjectStrokeColor( 1860 el, 1861 el.evalVisProp('highlightstrokecolor'), 1862 el.evalVisProp('highlightstrokeopacity') 1863 ); 1864 this.setObjectFillColor( 1865 el, 1866 el.evalVisProp('highlightfillcolor'), 1867 el.evalVisProp('highlightfillopacity') 1868 ); 1869 } 1870 } 1871 1872 // Highlight strokeWidth is suppressed if 1873 // parameter suppressHighlightStrokeWidth is false or undefined. 1874 // suppressHighlightStrokeWidth is false if polygon attribute 1875 // highlightbystrokewidth is true. 1876 if (!suppressHighlightStrokeWidth && el.evalVisProp('highlightstrokewidth')) { 1877 sw = Math.max( 1878 el.evalVisProp('highlightstrokewidth'), 1879 el.evalVisProp('strokewidth') 1880 ); 1881 this.setObjectStrokeWidth(el, sw); 1882 if ( 1883 el.elementClass === Const.OBJECT_CLASS_LINE || 1884 el.elementClass === Const.OBJECT_CLASS_CURVE 1885 ) { 1886 this.updatePathWithArrowHeads(el, true); 1887 } 1888 } 1889 } 1890 this.setCssClass(el, el.evalVisProp('highlightcssclass')); 1891 1892 return this; 1893 }, 1894 1895 /** 1896 * Uses the normal colors of an object, i.e. the opposite of {@link JXG.AbstractRenderer#highlight}. 1897 * @param {JXG.GeometryElement} el Reference of the object that will get its normal colors. 1898 * @returns {JXG.AbstractRenderer} Reference to the renderer 1899 * @see JXG.AbstractRenderer#updateTextStyle 1900 */ 1901 noHighlight: function (el) { 1902 var i, sw; 1903 1904 this.setObjectTransition(el); 1905 if (!el.evalVisProp('draft')) { 1906 if (el.type === Const.OBJECT_TYPE_POLYGON) { 1907 this.setObjectFillColor(el, el.evalVisProp('fillcolor'), el.evalVisProp('fillopacity')); 1908 for (i = 0; i < el.borders.length; i++) { 1909 this.noHighlight(el.borders[i]); 1910 } 1911 } else { 1912 if (el.elementClass === Const.OBJECT_CLASS_TEXT) { 1913 this.updateTextStyle(el, false); 1914 } else if (el.type === Const.OBJECT_TYPE_IMAGE) { 1915 this.updateImageStyle(el, false); 1916 this.setObjectFillColor(el, el.evalVisProp('fillcolor'), el.evalVisProp('fillopacity')); 1917 } else { 1918 this.setObjectStrokeColor(el, el.evalVisProp('strokecolor'), el.evalVisProp('strokeopacity')); 1919 this.setObjectFillColor(el, el.evalVisProp('fillcolor'), el.evalVisProp('fillopacity')); 1920 } 1921 } 1922 1923 sw = el.evalVisProp('strokewidth'); 1924 this.setObjectStrokeWidth(el, sw); 1925 if ( 1926 el.elementClass === Const.OBJECT_CLASS_LINE || 1927 el.elementClass === Const.OBJECT_CLASS_CURVE 1928 ) { 1929 this.updatePathWithArrowHeads(el, false); 1930 } 1931 } 1932 this.setCssClass(el, el.evalVisProp('cssclass')); 1933 1934 return this; 1935 }, 1936 1937 /** 1938 * Puts an object from draft mode back into normal mode. 1939 * @param {JXG.GeometryElement} el Reference of the object that no longer is in draft mode. 1940 */ 1941 removeDraft: function (el) { 1942 this.setObjectTransition(el); 1943 if (el.type === Const.OBJECT_TYPE_POLYGON) { 1944 this.setObjectFillColor(el, el.evalVisProp('fillcolor'), el.evalVisProp('fillopacity')); 1945 } else { 1946 if (el.type === Const.OBJECT_CLASS_POINT) { 1947 this.setObjectFillColor(el, el.evalVisProp('fillcolor'), el.evalVisProp('fillopacity')); 1948 } 1949 this.setObjectStrokeColor(el, el.evalVisProp('strokecolor'), el.evalVisProp('strokeopacity')); 1950 this.setObjectStrokeWidth(el, el.evalVisProp('strokewidth')); 1951 } 1952 }, 1953 1954 /** 1955 * Set ARIA related properties of an element. The attribute "aria" of an element contains at least the 1956 * properties "enabled", "label", and "live". Additionally, all available properties from 1957 * {@link https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA} may be set. 1958 * <p> 1959 * In JSXGraph, the available properties are used without the leading 'aria-'. 1960 * For example, the value of the JSXGraph attribute 'aria.label' will be set to the 1961 * HTML attribute 'aria-label'. 1962 * 1963 * @param {JXG.GeometryElement} el Reference of the object that wants new 1964 * ARIA attributes. 1965 */ 1966 setARIA: function(el) { /* stub */ }, 1967 1968 /** 1969 * Sets the buffering as recommended by SVGWG. Until now only Opera supports this and will be ignored by other 1970 * browsers. Although this feature is only supported by SVG we have this method in {@link JXG.AbstractRenderer} 1971 * because it is called from outside the renderer. 1972 * @param {Node} node The SVG DOM Node which buffering type to update. 1973 * @param {String} type Either 'auto', 'dynamic', or 'static'. For an explanation see 1974 * {@link https://www.w3.org/TR/SVGTiny12/painting.html#BufferedRenderingProperty}. 1975 */ 1976 setBuffering: function (node, type) { /* stub */ }, 1977 1978 /** 1979 * Clip element to the JSXGraph container element (div). To be precise: to the SVG node. 1980 * 1981 * @param {JXG.GeometryElement} el Reference of the object 1982 * @param {Boolean} val true: clip to the JSXGraph div, false: do not clip 1983 */ 1984 setClipPath: function(el, val) { /* stub */ }, 1985 1986 /** 1987 * Sets CSS classes for elements (relevant for SVG only). 1988 * 1989 * @param {JXG.GeometryElement} el Reference of the object that wants a 1990 * new set of CSS classes. 1991 * @param {String} cssClass String containing a space separated list of CSS classes. 1992 */ 1993 setCssClass: function (el, cssClass) { /* stub */ }, 1994 1995 /** 1996 * Sets an element's dash style. 1997 * @param {JXG.GeometryElement} el An JSXGraph element. 1998 */ 1999 setDashStyle: function (el) { /* stub */ }, 2000 2001 /** 2002 * Puts an object into draft mode, i.e. it's visual appearance will be changed. For GEONE<sub>x</sub>T backwards 2003 * compatibility. 2004 * @param {JXG.GeometryElement} el Reference of the object that is in draft mode. 2005 */ 2006 setDraft: function (el) { 2007 if (!el.evalVisProp('draft')) { 2008 return; 2009 } 2010 var draftColor = el.board.options.elements.draft.color, 2011 draftOpacity = el.board.options.elements.draft.opacity; 2012 2013 this.setObjectTransition(el); 2014 if (el.type === Const.OBJECT_TYPE_POLYGON) { 2015 this.setObjectFillColor(el, draftColor, draftOpacity); 2016 } else { 2017 if (el.elementClass === Const.OBJECT_CLASS_POINT) { 2018 this.setObjectFillColor(el, draftColor, draftOpacity); 2019 } else { 2020 this.setObjectFillColor(el, "none", 0); 2021 } 2022 this.setObjectStrokeColor(el, draftColor, draftOpacity); 2023 this.setObjectStrokeWidth(el, el.board.options.elements.draft.strokeWidth); 2024 } 2025 }, 2026 2027 /** 2028 * Sets up nodes for rendering a gradient fill. 2029 * @param {JXG.GeometryElement} el Reference of the object which gets the gradient 2030 */ 2031 setGradient: function (el) { /* stub */ }, 2032 2033 /** 2034 * Move element into new layer. This is trivial for canvas, but needs more effort in SVG. 2035 * Does not work dynamically, i.e. if level is a function. 2036 * 2037 * @param {JXG.GeometryElement} el Element which is put into different layer 2038 * @param {Number} value Layer number 2039 * @private 2040 */ 2041 setLayer: function (el, level) { /* stub */ }, 2042 2043 /** 2044 * Sets an objects fill color. 2045 * @param {JXG.GeometryElement} el Reference of the object that wants a new fill color. 2046 * @param {String} color Color in a HTML/CSS compatible format. If you don't want any fill color at all, choose 2047 * 'none'. 2048 * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1. 2049 */ 2050 setObjectFillColor: function (el, color, opacity) { /* stub */ }, 2051 2052 /** 2053 * Changes an objects stroke color to the given color. 2054 * @param {JXG.GeometryElement} el Reference of the {@link JXG.GeometryElement} that gets a new stroke 2055 * color. 2056 * @param {String} color Color value in a HTML compatible format, e.g. <strong>#00ff00</strong> or 2057 * <strong>green</strong> for green. 2058 * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1. 2059 */ 2060 setObjectStrokeColor: function (el, color, opacity) { /* stub */ }, 2061 2062 /** 2063 * Sets an element's stroke width. 2064 * @param {JXG.GeometryElement} el Reference to the geometry element. 2065 * @param {Number} width The new stroke width to be assigned to the element. 2066 */ 2067 setObjectStrokeWidth: function (el, width) { /* stub */ }, 2068 2069 /** 2070 * Sets the transition duration (in milliseconds) for fill color and stroke 2071 * color and opacity. 2072 * @param {JXG.GeometryElement} el Reference of the object that wants a 2073 * new transition duration. 2074 * @param {Number} duration (Optional) duration in milliseconds. If not given, 2075 * element.visProp.transitionDuration is taken. This is the default. 2076 */ 2077 setObjectTransition: function (el, duration) { /* stub */ }, 2078 2079 /** 2080 * Sets a node's attribute. 2081 * @param {Node} node The node that is to be updated. 2082 * @param {String} key Name of the attribute. 2083 * @param {String} val New value for the attribute. 2084 */ 2085 setPropertyPrim: function (node, key, val) { /* stub */ }, 2086 2087 /** 2088 * Sets the shadow properties to a geometry element. This method is only a stub, it is implemented in the actual 2089 * renderers. 2090 * @param {JXG.GeometryElement} el Reference to a geometry object, that should get a shadow 2091 */ 2092 setShadow: function (el) { /* stub */ }, 2093 2094 /** 2095 * Set the attribute `tabindex` to the attribute `tabindex` of an element. 2096 * This is only relevant for the SVG renderer. 2097 * 2098 * @param {JXG.GeometryElement} el 2099 */ 2100 setTabindex: function (el) { /* stub */ }, 2101 2102 /** 2103 * Shows a hidden element on the canvas; Only a stub, requires implementation in the derived renderer. 2104 * 2105 * Please use JXG.AbstractRenderer#display instead 2106 * @param {JXG.GeometryElement} el Reference to the object that has to appear. 2107 * @see JXG.AbstractRenderer#hide 2108 * @deprecated 2109 */ 2110 show: function (el) { /* stub */ }, 2111 2112 /** 2113 * Updates the gradient fill. 2114 * @param {JXG.GeometryElement} el An JSXGraph element with an area that can be filled. 2115 */ 2116 updateGradient: function (el) { /* stub */ }, 2117 2118 /* ********* Renderer control *********** */ 2119 2120 /** 2121 * Stop redraw. This method is called before every update, so a non-vector-graphics based renderer can use this 2122 * method to delete the contents of the drawing panel. This is an abstract method every descendant renderer 2123 * should implement, if appropriate. 2124 * @see JXG.AbstractRenderer#unsuspendRedraw 2125 */ 2126 suspendRedraw: function () { /* stub */ }, 2127 2128 /** 2129 * Restart redraw. This method is called after updating all the rendering node attributes. 2130 * @see JXG.AbstractRenderer#suspendRedraw 2131 */ 2132 unsuspendRedraw: function () { /* stub */ }, 2133 2134 /** 2135 * The tiny zoom bar shown on the bottom of a board (if board attribute "showNavigation" is true). 2136 * It is a div element and gets the CSS class "JXG_navigation" and the id {board id}_navigationbar. 2137 * <p> 2138 * The buttons get the CSS class "JXG_navigation_button" and the id {board_id}_name where name is 2139 * one of [top, down, left, right, out, 100, in, fullscreen, screenshot, reload, cleartraces]. 2140 * <p> 2141 * The symbols for zoom, navigation and reload are hard-coded. 2142 * 2143 * @param {JXG.Board} board Reference to a JSXGraph board. 2144 * @param {Object} attr Attributes of the navigation bar 2145 * @private 2146 */ 2147 drawNavigationBar: function (board, attr) { 2148 var doc, 2149 node, 2150 cancelbubble = function (e) { 2151 if (!e) { 2152 e = window.event; 2153 } 2154 2155 if (e.stopPropagation) { 2156 // Non IE<=8 2157 e.stopPropagation(); 2158 } else { 2159 e.cancelBubble = true; 2160 } 2161 }, 2162 createButton = function (label, handler, board_id, type) { 2163 var button; 2164 2165 board_id = board_id || ""; 2166 2167 button = doc.createElement('span'); 2168 button.innerHTML = label; // button.appendChild(doc.createTextNode(label)); 2169 2170 // Style settings are superseded by adding the CSS class below 2171 button.style.paddingLeft = '7px'; 2172 button.style.paddingRight = '7px'; 2173 2174 if (button.classList !== undefined) { 2175 // classList not available in IE 9 2176 button.classList.add("JXG_navigation_button"); 2177 button.classList.add("JXG_navigation_button_" + type); 2178 } 2179 // button.setAttribute('tabindex', 0); 2180 2181 button.setAttribute("id", board_id + '_navigation_' + type); 2182 button.setAttribute("aria-hidden", 'true'); // navigation buttons should never appear in screen reader 2183 2184 node.appendChild(button); 2185 2186 Env.addEvent( 2187 button, 2188 "click", 2189 function (e) { 2190 Type.bind(handler, board)(); 2191 return false; 2192 }, 2193 board 2194 ); 2195 // prevent the click from bubbling down to the board 2196 Env.addEvent(button, "pointerup", cancelbubble, board); 2197 Env.addEvent(button, "pointerdown", cancelbubble, board); 2198 Env.addEvent(button, "pointerleave", cancelbubble, board); 2199 Env.addEvent(button, "mouseup", cancelbubble, board); 2200 Env.addEvent(button, "mousedown", cancelbubble, board); 2201 Env.addEvent(button, "touchend", cancelbubble, board); 2202 Env.addEvent(button, "touchstart", cancelbubble, board); 2203 }; 2204 2205 if (Env.isBrowser && this.type !== 'no') { 2206 doc = board.containerObj.ownerDocument; 2207 node = doc.createElement('div'); 2208 2209 node.setAttribute("id", board.container + "_navigationbar"); 2210 2211 // Style settings are superseded by adding the CSS class below 2212 node.style.color = attr.strokecolor; 2213 node.style.backgroundColor = attr.fillcolor; 2214 node.style.padding = attr.padding; 2215 node.style.position = attr.position; 2216 node.style.fontSize = attr.fontsize; 2217 node.style.cursor = attr.cursor; 2218 node.style.zIndex = attr.zindex; 2219 board.containerObj.appendChild(node); 2220 node.style.right = attr.right; 2221 node.style.bottom = attr.bottom; 2222 2223 if (node.classList !== undefined) { 2224 // classList not available in IE 9 2225 node.classList.add("JXG_navigation"); 2226 } 2227 // For XHTML we need unicode instead of HTML entities 2228 2229 if (board.attr.showfullscreen) { 2230 createButton( 2231 board.attr.fullscreen.symbol, 2232 function () { 2233 board.toFullscreen(board.attr.fullscreen.id); 2234 }, 2235 board.container, "fullscreen" 2236 ); 2237 } 2238 2239 if (board.attr.showscreenshot) { 2240 createButton( 2241 board.attr.screenshot.symbol, 2242 function () { 2243 window.setTimeout(function () { 2244 board.renderer.screenshot(board, "", false); 2245 }, 330); 2246 }, 2247 board.container, "screenshot" 2248 ); 2249 } 2250 2251 if (board.attr.showreload) { 2252 // full reload circle: \u27F2 2253 // the board.reload() method does not exist during the creation 2254 // of this button. That's why this anonymous function wrapper is required. 2255 createButton( 2256 "\u21BB", 2257 function () { 2258 board.reload(); 2259 }, 2260 board.container, "reload" 2261 ); 2262 } 2263 2264 if (board.attr.showcleartraces) { 2265 // clear traces symbol (otimes): \u27F2 2266 createButton("\u2297", 2267 function () { 2268 board.clearTraces(); 2269 }, 2270 board.container, "cleartraces" 2271 ); 2272 } 2273 2274 if (board.attr.shownavigation) { 2275 if (board.attr.showzoom) { 2276 createButton("\u2013", board.zoomOut, board.container, 'out'); 2277 createButton("o", board.zoom100, board.container, '100'); 2278 createButton("+", board.zoomIn, board.container, 'in'); 2279 } 2280 createButton("\u2190", board.clickLeftArrow, board.container, 'left'); 2281 createButton("\u2193", board.clickUpArrow, board.container, 'down'); // Down arrow 2282 createButton("\u2191", board.clickDownArrow, board.container, 'up'); // Up arrow 2283 createButton("\u2192", board.clickRightArrow, board.container, 'right'); 2284 } 2285 } 2286 }, 2287 2288 /** 2289 * Wrapper for getElementById for maybe other renderers which elements are not directly accessible by DOM 2290 * methods like document.getElementById(). 2291 * @param {String} id Unique identifier for element. 2292 * @returns {Object} Reference to a JavaScript object. In case of SVG/VMLRenderer it's a reference to a SVG/VML node. 2293 */ 2294 getElementById: function (id) { 2295 var str; 2296 if (Type.exists(this.container)) { 2297 // Use querySelector over getElementById for compatibility with both 'regular' document 2298 // and ShadowDOM fragments. 2299 str = this.container.id + '_' + id; 2300 // Mask special symbols like '/' and '\' in id 2301 if (Type.exists(CSS) && Type.exists(CSS.escape)) { 2302 str = CSS.escape(str); 2303 } 2304 return this.container.querySelector('#' + str); 2305 } 2306 return ""; 2307 }, 2308 2309 /** 2310 * Remove an element and provide a function that inserts it into its original position. This method 2311 * is taken from this article {@link https://developers.google.com/speed/articles/javascript-dom}. 2312 * @author KeeKim Heng, Google Web Developer 2313 * @param {Element} el The element to be temporarily removed 2314 * @returns {Function} A function that inserts the element into its original position 2315 */ 2316 removeToInsertLater: function (el) { 2317 var parentNode = el.parentNode, 2318 nextSibling = el.nextSibling; 2319 2320 if (parentNode === null) { 2321 return; 2322 } 2323 parentNode.removeChild(el); 2324 2325 return function () { 2326 if (nextSibling) { 2327 parentNode.insertBefore(el, nextSibling); 2328 } else { 2329 parentNode.appendChild(el); 2330 } 2331 }; 2332 }, 2333 2334 /** 2335 * Resizes the rendering element 2336 * @param {Number} w New width 2337 * @param {Number} h New height 2338 */ 2339 resize: function (w, h) { /* stub */ }, 2340 2341 /** 2342 * Create crosshair elements (Fadenkreuz) for presentations. 2343 * @param {Number} n Number of crosshairs. 2344 */ 2345 createTouchpoints: function (n) { /* stub */ }, 2346 2347 /** 2348 * Show a specific crosshair. 2349 * @param {Number} i Number of the crosshair to show 2350 */ 2351 showTouchpoint: function (i) { /* stub */ }, 2352 2353 /** 2354 * Hide a specific crosshair. 2355 * @param {Number} i Number of the crosshair to show 2356 */ 2357 hideTouchpoint: function (i) { /* stub */ }, 2358 2359 /** 2360 * Move a specific crosshair. 2361 * @param {Number} i Number of the crosshair to show 2362 * @param {Array} pos New positon in screen coordinates 2363 */ 2364 updateTouchpoint: function (i, pos) { /* stub */ }, 2365 2366 /* ********* Dump related stuff *********** */ 2367 2368 /** 2369 * Convert SVG construction to base64 encoded SVG data URL. 2370 * Only available on SVGRenderer. 2371 * 2372 * @see JXG.SVGRenderer#dumpToDataURI 2373 */ 2374 dumpToDataURI: function (_ignoreTexts) { /* stub */ }, 2375 2376 /** 2377 * Convert SVG construction to canvas. 2378 * Only available on SVGRenderer. 2379 * 2380 * @see JXG.SVGRenderer#dumpToCanvas 2381 */ 2382 dumpToCanvas: function (canvasId, w, h, _ignoreTexts) { /* stub */ }, 2383 2384 /** 2385 * Display SVG image in html img-tag which enables 2386 * easy download for the user. 2387 * 2388 * See JXG.SVGRenderer#screenshot 2389 */ 2390 screenshot: function (board) { /* stub */ } 2391 2392 } 2393 ); 2394 2395 export default JXG.AbstractRenderer; 2396