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 (element.board.attr.keyboard.enabled && Type.exists(element.rendNode)) { 1588 val = Type.evaluate(element.visProp.tabindex); 1589 if (!element.visPropCalc.visible || Type.evaluate(element.visProp.fixed)) { 1590 val = null; 1591 } 1592 if (val !== element.visPropOld.tabindex) { 1593 element.rendNode.setAttribute('tabindex', val); 1594 element.visPropOld.tabindex = val; 1595 } 1596 } 1597 }, 1598 1599 /** 1600 * Shows or hides an element on the canvas; Only a stub, requires implementation in the derived renderer. 1601 * @param {JXG.GeometryElement} element Reference to the object that has to appear. 1602 * @param {Boolean} value true to show the element, false to hide the element. 1603 */ 1604 display: function (element, value) { 1605 if (element) { 1606 element.visPropOld.visible = value; 1607 } 1608 }, 1609 1610 /** 1611 * Shows a hidden element on the canvas; Only a stub, requires implementation in the derived renderer. 1612 * 1613 * Please use JXG.AbstractRenderer#display instead 1614 * @param {JXG.GeometryElement} element Reference to the object that has to appear. 1615 * @see JXG.AbstractRenderer#hide 1616 * @deprecated 1617 */ 1618 show: function (element) { /* stub */ }, 1619 1620 /** 1621 * Hides an element on the canvas; Only a stub, requires implementation in the derived renderer. 1622 * 1623 * Please use JXG.AbstractRenderer#display instead 1624 * @param {JXG.GeometryElement} element Reference to the geometry element that has to disappear. 1625 * @see JXG.AbstractRenderer#show 1626 * @deprecated 1627 */ 1628 hide: function (element) { /* stub */ }, 1629 1630 /** 1631 * Sets the buffering as recommended by SVGWG. Until now only Opera supports this and will be ignored by other 1632 * browsers. Although this feature is only supported by SVG we have this method in {@link JXG.AbstractRenderer} 1633 * because it is called from outside the renderer. 1634 * @param {Node} node The SVG DOM Node which buffering type to update. 1635 * @param {String} type Either 'auto', 'dynamic', or 'static'. For an explanation see 1636 * {@link http://www.w3.org/TR/SVGTiny12/painting.html#BufferedRenderingProperty}. 1637 */ 1638 setBuffering: function (node, type) { /* stub */ }, 1639 1640 /** 1641 * Sets an element's dash style. 1642 * @param {JXG.GeometryElement} element An JSXGraph element. 1643 */ 1644 setDashStyle: function (element) { /* stub */ }, 1645 1646 /** 1647 * Puts an object into draft mode, i.e. it's visual appearance will be changed. For GEONE<sub>x</sub>T backwards 1648 * compatibility. 1649 * @param {JXG.GeometryElement} el Reference of the object that is in draft mode. 1650 */ 1651 setDraft: function (el) { 1652 if (!Type.evaluate(el.visProp.draft)) { 1653 return; 1654 } 1655 var draftColor = el.board.options.elements.draft.color, 1656 draftOpacity = el.board.options.elements.draft.opacity; 1657 1658 this.setObjectTransition(el); 1659 if (el.type === Const.OBJECT_TYPE_POLYGON) { 1660 this.setObjectFillColor(el, draftColor, draftOpacity); 1661 } else { 1662 if (el.elementClass === Const.OBJECT_CLASS_POINT) { 1663 this.setObjectFillColor(el, draftColor, draftOpacity); 1664 } else { 1665 this.setObjectFillColor(el, 'none', 0); 1666 } 1667 this.setObjectStrokeColor(el, draftColor, draftOpacity); 1668 this.setObjectStrokeWidth(el, el.board.options.elements.draft.strokeWidth); 1669 } 1670 }, 1671 1672 /** 1673 * Puts an object from draft mode back into normal mode. 1674 * @param {JXG.GeometryElement} el Reference of the object that no longer is in draft mode. 1675 */ 1676 removeDraft: function (el) { 1677 this.setObjectTransition(el); 1678 if (el.type === Const.OBJECT_TYPE_POLYGON) { 1679 this.setObjectFillColor(el, 1680 el.visProp.fillcolor, 1681 el.visProp.fillopacity); 1682 } else { 1683 if (el.type === Const.OBJECT_CLASS_POINT) { 1684 this.setObjectFillColor(el, 1685 el.visProp.fillcolor, 1686 el.visProp.fillopacity); 1687 } 1688 this.setObjectStrokeColor(el, el.visProp.strokecolor, el.visProp.strokeopacity); 1689 this.setObjectStrokeWidth(el, el.visProp.strokewidth); 1690 } 1691 }, 1692 1693 /** 1694 * Sets up nodes for rendering a gradient fill. 1695 * @param element 1696 */ 1697 setGradient: function (element) { /* stub */ }, 1698 1699 /** 1700 * Updates the gradient fill. 1701 * @param {JXG.GeometryElement} element An JSXGraph element with an area that can be filled. 1702 */ 1703 updateGradient: function (element) { /* stub */ }, 1704 1705 /** 1706 * Sets the transition duration (in milliseconds) for fill color and stroke 1707 * color and opacity. 1708 * @param {JXG.GeometryElement} element Reference of the object that wants a 1709 * new transition duration. 1710 * @param {Number} duration (Optional) duration in milliseconds. If not given, 1711 * element.visProp.transitionDuration is taken. This is the default. 1712 */ 1713 setObjectTransition: function (element, duration) { /* stub */ }, 1714 1715 /** 1716 * Sets an objects fill color. 1717 * @param {JXG.GeometryElement} element Reference of the object that wants a new fill color. 1718 * @param {String} color Color in a HTML/CSS compatible format. If you don't want any fill color at all, choose 1719 * 'none'. 1720 * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1. 1721 */ 1722 setObjectFillColor: function (element, color, opacity) { /* stub */ }, 1723 1724 /** 1725 * Changes an objects stroke color to the given color. 1726 * @param {JXG.GeometryElement} element Reference of the {@link JXG.GeometryElement} that gets a new stroke 1727 * color. 1728 * @param {String} color Color value in a HTML compatible format, e.g. <strong>#00ff00</strong> or 1729 * <strong>green</strong> for green. 1730 * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1. 1731 */ 1732 setObjectStrokeColor: function (element, color, opacity) { /* stub */ }, 1733 1734 /** 1735 * Sets an element's stroke width. 1736 * @param {JXG.GeometryElement} element Reference to the geometry element. 1737 * @param {Number} width The new stroke width to be assigned to the element. 1738 */ 1739 setObjectStrokeWidth: function (element, width) { /* stub */ }, 1740 1741 /** 1742 * Sets the shadow properties to a geometry element. This method is only a stub, it is implemented in the actual 1743 * renderers. 1744 * @param {JXG.GeometryElement} element Reference to a geometry object, that should get a shadow 1745 */ 1746 setShadow: function (element) { /* stub */ }, 1747 1748 /** 1749 * Highlights an object, i.e. changes the current colors of the object to its highlighting colors 1750 * and highlighting stroke width. 1751 * @param {JXG.GeometryElement} el Reference of the object that will be highlighted. 1752 * @returns {JXG.AbstractRenderer} Reference to the renderer 1753 * @see JXG.AbstractRenderer#updateTextStyle 1754 */ 1755 highlight: function (el) { 1756 var i, ev = el.visProp, sw; 1757 1758 this.setObjectTransition(el); 1759 if (!ev.draft) { 1760 if (el.type === Const.OBJECT_TYPE_POLYGON) { 1761 this.setObjectFillColor(el, 1762 ev.highlightfillcolor, 1763 ev.highlightfillopacity); 1764 for (i = 0; i < el.borders.length; i++) { 1765 this.setObjectStrokeColor(el.borders[i], 1766 el.borders[i].visProp.highlightstrokecolor, 1767 el.borders[i].visProp.highlightstrokeopacity); 1768 } 1769 } else { 1770 if (el.elementClass === Const.OBJECT_CLASS_TEXT) { 1771 this.updateTextStyle(el, true); 1772 } else if (el.type === Const.OBJECT_TYPE_IMAGE) { 1773 this.updateImageStyle(el, true); 1774 this.setObjectFillColor(el, 1775 ev.highlightfillcolor, 1776 ev.highlightfillopacity); 1777 } else { 1778 this.setObjectStrokeColor(el, ev.highlightstrokecolor, ev.highlightstrokeopacity); 1779 this.setObjectFillColor(el, 1780 ev.highlightfillcolor, 1781 ev.highlightfillopacity); 1782 } 1783 } 1784 if (ev.highlightstrokewidth) { 1785 sw = Math.max(Type.evaluate(ev.highlightstrokewidth), Type.evaluate(ev.strokewidth)); 1786 this.setObjectStrokeWidth(el, sw); 1787 if (el.elementClass === Const.OBJECT_CLASS_LINE || el.elementClass === Const.OBJECT_CLASS_CURVE) { 1788 this.updatePathWithArrowHeads(el, true); 1789 } 1790 } 1791 } 1792 1793 return this; 1794 }, 1795 1796 /** 1797 * Uses the normal colors of an object, i.e. the opposite of {@link JXG.AbstractRenderer#highlight}. 1798 * @param {JXG.GeometryElement} el Reference of the object that will get its normal colors. 1799 * @returns {JXG.AbstractRenderer} Reference to the renderer 1800 * @see JXG.AbstractRenderer#updateTextStyle 1801 */ 1802 noHighlight: function (el) { 1803 var i, ev = el.visProp, sw; 1804 1805 this.setObjectTransition(el); 1806 if (!Type.evaluate(el.visProp.draft)) { 1807 if (el.type === Const.OBJECT_TYPE_POLYGON) { 1808 this.setObjectFillColor(el, 1809 ev.fillcolor, 1810 ev.fillopacity); 1811 for (i = 0; i < el.borders.length; i++) { 1812 this.setObjectStrokeColor(el.borders[i], 1813 el.borders[i].visProp.strokecolor, 1814 el.borders[i].visProp.strokeopacity); 1815 } 1816 } else { 1817 if (el.elementClass === Const.OBJECT_CLASS_TEXT) { 1818 this.updateTextStyle(el, false); 1819 } else if (el.type === Const.OBJECT_TYPE_IMAGE) { 1820 this.updateImageStyle(el, false); 1821 this.setObjectFillColor(el, 1822 ev.fillcolor, 1823 ev.fillopacity); 1824 } else { 1825 this.setObjectStrokeColor(el, 1826 ev.strokecolor, 1827 ev.strokeopacity); 1828 this.setObjectFillColor(el, 1829 ev.fillcolor, 1830 ev.fillopacity); 1831 } 1832 } 1833 1834 sw = Type.evaluate(ev.strokewidth); 1835 this.setObjectStrokeWidth(el, sw); 1836 if (el.elementClass === Const.OBJECT_CLASS_LINE || el.elementClass === Const.OBJECT_CLASS_CURVE) { 1837 this.updatePathWithArrowHeads(el, false); 1838 } 1839 1840 } 1841 1842 return this; 1843 }, 1844 1845 /* ************************** 1846 * renderer control 1847 * **************************/ 1848 1849 /** 1850 * Stop redraw. This method is called before every update, so a non-vector-graphics based renderer can use this 1851 * method to delete the contents of the drawing panel. This is an abstract method every descendant renderer 1852 * should implement, if appropriate. 1853 * @see JXG.AbstractRenderer#unsuspendRedraw 1854 */ 1855 suspendRedraw: function () { /* stub */ }, 1856 1857 /** 1858 * Restart redraw. This method is called after updating all the rendering node attributes. 1859 * @see JXG.AbstractRenderer#suspendRedraw 1860 */ 1861 unsuspendRedraw: function () { /* stub */ }, 1862 1863 /** 1864 * The tiny zoom bar shown on the bottom of a board (if showNavigation on board creation is true). 1865 * It is a div element and gets the CSS class "JXG_navigation" and the id {board id}_navigationbar. 1866 * 1867 * The buttons get the CSS class "JXG_navigation_button" and the id {board_id}_name where name is 1868 * one of [top, down, left, right, out, 100, in, fullscreen, screenshot, reload, cleartraces]. 1869 * 1870 * The symbols are hard-coded. 1871 * 1872 * @param {JXG.Board} board Reference to a JSXGraph board. 1873 * @param {Object} attr Attributes of the navigation bar 1874 * 1875 */ 1876 drawZoomBar: function (board, attr) { 1877 var doc, 1878 node, 1879 cancelbubble = function (e) { 1880 if (!e) { 1881 e = window.event; 1882 } 1883 1884 if (e.stopPropagation) { 1885 // Non IE<=8 1886 e.stopPropagation(); 1887 } else { 1888 e.cancelBubble = true; 1889 } 1890 }, 1891 createButton = function (label, handler, id) { 1892 var button; 1893 1894 id = id || ''; 1895 1896 button = doc.createElement('span'); 1897 button.innerHTML = label; // button.appendChild(doc.createTextNode(label)); 1898 1899 // Style settings are superseded by adding the CSS class below 1900 button.style.paddingLeft = '7px'; 1901 button.style.paddingRight = '7px'; 1902 1903 if (button.classList !== undefined) { // classList not available in IE 9 1904 button.classList.add('JXG_navigation_button'); 1905 } 1906 // button.setAttribute('tabindex', 0); 1907 1908 button.setAttribute('id', id); 1909 node.appendChild(button); 1910 1911 // Highlighting is now done with CSS 1912 // Env.addEvent(button, 'mouseover', function () { 1913 // this.style.backgroundColor = attr.highlightfillcolor; 1914 // }, button); 1915 // Env.addEvent(button, 'mouseover', function () { 1916 // this.style.backgroundColor = attr.highlightfillcolor; 1917 // }, button); 1918 // Env.addEvent(button, 'mouseout', function () { 1919 // this.style.backgroundColor = attr.fillcolor; 1920 // }, button); 1921 1922 Env.addEvent(button, 'click', function(e) { (Type.bind(handler, board))(); return false; }, board); 1923 // prevent the click from bubbling down to the board 1924 Env.addEvent(button, 'mouseup', cancelbubble, board); 1925 Env.addEvent(button, 'mousedown', cancelbubble, board); 1926 Env.addEvent(button, 'touchend', cancelbubble, board); 1927 Env.addEvent(button, 'touchstart', cancelbubble, board); 1928 }; 1929 1930 if (Env.isBrowser && this.type !== 'no') { 1931 doc = board.containerObj.ownerDocument; 1932 node = doc.createElement('div'); 1933 1934 node.setAttribute('id', board.container + '_navigationbar'); 1935 1936 // Style settings are superseded by adding the CSS class below 1937 node.style.color = attr.strokecolor; 1938 node.style.backgroundColor = attr.fillcolor; 1939 node.style.padding = attr.padding; 1940 node.style.position = attr.position; 1941 node.style.fontSize = attr.fontsize; 1942 node.style.cursor = attr.cursor; 1943 node.style.zIndex = attr.zindex; 1944 board.containerObj.appendChild(node); 1945 node.style.right = attr.right; 1946 node.style.bottom = attr.bottom; 1947 1948 if (node.classList !== undefined) { // classList not available in IE 9 1949 node.classList.add('JXG_navigation'); 1950 } 1951 // For XHTML we need unicode instead of HTML entities 1952 1953 if (board.attr.showfullscreen) { 1954 createButton(board.attr.fullscreen.symbol, function () { 1955 board.toFullscreen(board.attr.fullscreen.id); 1956 }, board.container + '_navigation_fullscreen'); 1957 } 1958 1959 if (board.attr.showscreenshot) { 1960 createButton(board.attr.screenshot.symbol, function () { 1961 window.setTimeout(function() { 1962 board.renderer.screenshot(board, '', false); 1963 }, 330); 1964 }, board.container + '_navigation_screenshot'); 1965 } 1966 1967 if (board.attr.showreload) { 1968 // full reload circle: \u27F2 1969 // the board.reload() method does not exist during the creation 1970 // of this button. That's why this anonymous function wrapper is required. 1971 createButton('\u21BB', function () { 1972 board.reload(); 1973 }, board.container + '_navigation_reload'); 1974 } 1975 1976 if (board.attr.showcleartraces) { 1977 // clear traces symbol (otimes): \u27F2 1978 createButton('\u2297', function () { 1979 board.clearTraces(); 1980 }, board.container + '_navigation_cleartraces'); 1981 } 1982 1983 if (board.attr.shownavigation) { 1984 if (board.attr.showzoom) { 1985 createButton('\u2013', board.zoomOut, board.container + '_navigation_out'); 1986 createButton('o', board.zoom100, board.container + '_navigation_100'); 1987 createButton('+', board.zoomIn, board.container + '_navigation_in'); 1988 } 1989 createButton('\u2190', board.clickLeftArrow, board.container + '_navigation_left'); 1990 createButton('\u2193', board.clickUpArrow, board.container + '_navigation_up'); 1991 createButton('\u2191', board.clickDownArrow, board.container + '_navigation_down'); 1992 createButton('\u2192', board.clickRightArrow, board.container + '_navigation_right'); 1993 } 1994 } 1995 }, 1996 1997 /** 1998 * Wrapper for getElementById for maybe other renderers which elements are not directly accessible by DOM 1999 * methods like document.getElementById(). 2000 * @param {String} id Unique identifier for element. 2001 * @returns {Object} Reference to a JavaScript object. In case of SVG/VMLRenderer it's a reference to a SVG/VML 2002 * node. 2003 */ 2004 getElementById: function (id) { 2005 if (Type.exists(this.container)) { 2006 return this.container.ownerDocument.getElementById(this.container.id + '_' + id); 2007 } 2008 return ''; 2009 }, 2010 2011 /** 2012 * Remove an element and provide a function that inserts it into its original position. This method 2014 * @author KeeKim Heng, Google Web Developer 2015 * @param {Element} el The element to be temporarily removed 2016 * @returns {Function} A function that inserts the element into its original position 2017 */ 2018 removeToInsertLater: function (el) { 2019 var parentNode = el.parentNode, 2020 nextSibling = el.nextSibling; 2021 2022 if (parentNode === null) { 2023 return; 2024 } 2025 parentNode.removeChild(el); 2026 2027 return function () { 2028 if (nextSibling) { 2029 parentNode.insertBefore(el, nextSibling); 2030 } else { 2031 parentNode.appendChild(el); 2032 } 2033 }; 2034 }, 2035 2036 /** 2037 * Resizes the rendering element 2038 * @param {Number} w New width 2039 * @param {Number} h New height 2040 */ 2041 resize: function (w, h) { /* stub */}, 2042 2043 /** 2044 * Create crosshair elements (Fadenkreuz) for presentations. 2045 * @param {Number} n Number of crosshairs. 2046 */ 2047 createTouchpoints: function (n) {}, 2048 2049 /** 2050 * Show a specific crosshair. 2051 * @param {Number} i Number of the crosshair to show 2052 */ 2053 showTouchpoint: function (i) {}, 2054 2055 /** 2056 * Hide a specific crosshair. 2057 * @param {Number} i Number of the crosshair to show 2058 */ 2059 hideTouchpoint: function (i) {}, 2060 2061 /** 2062 * Move a specific crosshair. 2063 * @param {Number} i Number of the crosshair to show 2064 * @param {Array} pos New positon in screen coordinates 2065 */ 2066 updateTouchpoint: function (i, pos) {}, 2067 2068 /** 2069 * Convert SVG construction to base64 encoded SVG data URL. 2070 * Only available on SVGRenderer. 2071 * 2072 * @see JXG.SVGRenderer#dumpToDataURI 2073 */ 2074 dumpToDataURI: function (_ignoreTexts) {}, 2075 2076 /** 2077 * Convert SVG construction to canvas. 2078 * Only available on SVGRenderer. 2079 * 2080 * @see JXG.SVGRenderer#dumpToCanvas 2081 */ 2082 dumpToCanvas: function (canvasId, w, h, _ignoreTexts) {}, 2083 2084 /** 2085 * Display SVG image in html img-tag which enables 2086 * easy download for the user. 2087 * 2088 * See JXG.SVGRenderer#screenshot 2089 */ 2090 screenshot: function (board) {}, 2091 2092 /** 2093 * Move element into new layer. This is trivial for canvas, but needs more effort in SVG. 2094 * Does not work dynamically, i.e. if level is a function. 2095 * 2096 * @param {JXG.GeometryElement} el Element which is put into different layer 2097 * @param {Number} value Layer number 2098 * @private 2099 */ 2100 setLayer: function(el, level) {} 2101 2102 }); 2103 2104 return JXG.AbstractRenderer; 2105 }); 2106