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