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