1 /* 2 Copyright 2008-2016 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*/ 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 8 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 supporting browsers are IE 9 - 11. 163 * @type Boolean 164 * @private 165 */ 166 this.supportsForeignObject = false; 167 }; 168 169 JXG.extend(JXG.AbstractRenderer.prototype, /** @lends JXG.AbstractRenderer.prototype */ { 170 171 /* ******************************** * 172 * private methods * 173 * should not be called from * 174 * outside AbstractRenderer * 175 * ******************************** */ 176 177 /** 178 * Update visual properties, but only if {@link JXG.AbstractRenderer#enhancedRendering} or <tt>enhanced</tt> is set to true. 179 * @param {JXG.GeometryElement} element The element to update 180 * @param {Object} [not={}] Select properties you don't want to be updated: <tt>{fill: true, dash: true}</tt> updates 181 * everything except for fill and dash. Possible values are <tt>stroke, fill, dash, shadow, gradient</tt>. 182 * @param {Boolean} [enhanced=false] If true, {@link JXG.AbstractRenderer#enhancedRendering} is assumed to be true. 183 * @private 184 */ 185 _updateVisual: function (element, not, enhanced) { 186 if (enhanced || this.enhancedRendering) { 187 not = not || {}; 188 189 if (!element.visProp.draft) { 190 if (!not.stroke) { 191 this.setObjectStrokeColor(element, element.visProp.strokecolor, element.visProp.strokeopacity); 192 this.setObjectStrokeWidth(element, element.visProp.strokewidth); 193 } 194 195 if (!not.fill) { 196 this.setObjectFillColor(element, element.visProp.fillcolor, element.visProp.fillopacity); 197 } 198 199 if (!not.dash) { 200 this.setDashStyle(element, element.visProp); 201 } 202 203 if (!not.shadow) { 204 this.setShadow(element); 205 } 206 207 if (!not.gradient) { 208 this.setShadow(element); 209 } 210 } else { 211 this.setDraft(element); 212 } 213 } 214 }, 215 216 217 /* ******************************** * 218 * Point drawing and updating * 219 * ******************************** */ 220 221 /** 222 * Draws a point on the {@link JXG.Board}. 223 * @param {JXG.Point} element Reference to a {@link JXG.Point} object that has to be drawn. 224 * @see Point 225 * @see JXG.Point 226 * @see JXG.AbstractRenderer#updatePoint 227 * @see JXG.AbstractRenderer#changePointStyle 228 */ 229 drawPoint: function (element) { 230 var prim, 231 // sometimes element is not a real point and lacks the methods of a JXG.Point instance, 232 // in these cases to not use element directly. 233 face = Options.normalizePointFace(element.visProp.face); 234 235 // determine how the point looks like 236 if (face === 'o') { 237 prim = 'ellipse'; 238 } else if (face === '[]') { 239 prim = 'rect'; 240 } else { 241 // cross/x, diamond/<>, triangleup/a/^, triangledown/v, triangleleft/<, 242 // triangleright/>, plus/+, 243 prim = 'path'; 244 } 245 246 element.rendNode = this.appendChildPrim(this.createPrim(prim, element.id), element.visProp.layer); 247 this.appendNodesToElement(element, prim); 248 249 // adjust visual propertys 250 this._updateVisual(element, {dash: true, shadow: true}, true); 251 252 253 // By now we only created the xml nodes and set some styles, in updatePoint 254 // the attributes are filled with data. 255 this.updatePoint(element); 256 }, 257 258 /** 259 * Updates visual appearance of the renderer element assigned to the given {@link JXG.Point}. 260 * @param {JXG.Point} element Reference to a {@link JXG.Point} object, that has to be updated. 261 * @see Point 262 * @see JXG.Point 263 * @see JXG.AbstractRenderer#drawPoint 264 * @see JXG.AbstractRenderer#changePointStyle 265 */ 266 updatePoint: function (element) { 267 var size = element.visProp.size, 268 // sometimes element is not a real point and lacks the methods of a JXG.Point instance, 269 // in these cases to not use element directly. 270 face = Options.normalizePointFace(element.visProp.face); 271 272 if (!isNaN(element.coords.scrCoords[2] + element.coords.scrCoords[1])) { 273 this._updateVisual(element, {dash: false, shadow: false}); 274 size *= ((!element.board || !element.board.options.point.zoom) ? 1.0 : Math.sqrt(element.board.zoomX * element.board.zoomY)); 275 276 if (face === 'o') { // circle 277 this.updateEllipsePrim(element.rendNode, element.coords.scrCoords[1], element.coords.scrCoords[2], size + 1, size + 1); 278 } else if (face === '[]') { // rectangle 279 this.updateRectPrim(element.rendNode, element.coords.scrCoords[1] - size, element.coords.scrCoords[2] - size, size * 2, size * 2); 280 } else { // x, +, <>, ^, v, <, > 281 this.updatePathPrim(element.rendNode, this.updatePathStringPoint(element, size, face), element.board); 282 } 283 this.setShadow(element); 284 } 285 }, 286 287 /** 288 * Changes the style of a {@link JXG.Point}. This is required because the point styles differ in what 289 * elements have to be drawn, e.g. if the point is marked by a "x" or a "+" two lines are drawn, if 290 * it's marked by spot a circle is drawn. This method removes the old renderer element(s) and creates 291 * the new one(s). 292 * @param {JXG.Point} element Reference to a {@link JXG.Point} object, that's style is changed. 293 * @see Point 294 * @see JXG.Point 295 * @see JXG.AbstractRenderer#updatePoint 296 * @see JXG.AbstractRenderer#drawPoint 297 */ 298 changePointStyle: function (element) { 299 var node = this.getElementById(element.id); 300 301 // remove the existing point rendering node 302 if (Type.exists(node)) { 303 this.remove(node); 304 } 305 306 // and make a new one 307 this.drawPoint(element); 308 Type.clearVisPropOld(element); 309 310 if (!element.visProp.visible) { 311 this.hide(element); 312 } 313 314 if (element.visProp.draft) { 315 this.setDraft(element); 316 } 317 }, 318 319 /* ******************************** * 320 * Lines * 321 * ******************************** */ 322 323 /** 324 * Draws a line on the {@link JXG.Board}. 325 * @param {JXG.Line} element Reference to a line object, that has to be drawn. 326 * @see Line 327 * @see JXG.Line 328 * @see JXG.AbstractRenderer#updateLine 329 */ 330 drawLine: function (element) { 331 element.rendNode = this.appendChildPrim(this.createPrim('line', element.id), element.visProp.layer); 332 this.appendNodesToElement(element, 'lines'); 333 this.updateLine(element); 334 }, 335 336 /** 337 * Updates visual appearance of the renderer element assigned to the given {@link JXG.Line}. 338 * @param {JXG.Line} element Reference to the {@link JXG.Line} object that has to be updated. 339 * @see Line 340 * @see JXG.Line 341 * @see JXG.AbstractRenderer#drawLine 342 */ 343 updateLine: function (element) { 344 var s, s1, s2, d, d1x, d1y, d2x, d2y, 345 c1 = new Coords(Const.COORDS_BY_USER, element.point1.coords.usrCoords, element.board), 346 c2 = new Coords(Const.COORDS_BY_USER, element.point2.coords.usrCoords, element.board), 347 minlen = 10, 348 margin = null; 349 350 margin = element.visProp.margin; 351 Geometry.calcStraight(element, c1, c2, margin); 352 353 d1x = d1y = d2x = d2y = 0.0; 354 /* 355 Handle arrow heads. 356 357 The arrow head is an equilateral triangle with base length 10 and height 10. 358 These 10 units are scaled to strokeWidth*3 pixels or minlen pixels. 359 */ 360 if (element.visProp.lastarrow || element.visProp.firstarrow) { 361 362 s1 = element.point1.visProp.size; 363 s2 = element.point2.visProp.size; 364 s = s1 + s2; 365 if (element.visProp.lastarrow && element.visProp.touchlastpoint) { 366 d = c1.distance(Const.COORDS_BY_SCREEN, c2); 367 if (d > s) { 368 d2x = (c2.scrCoords[1] - c1.scrCoords[1]) * s2 / d; 369 d2y = (c2.scrCoords[2] - c1.scrCoords[2]) * s2 / d; 370 c2 = new Coords(Const.COORDS_BY_SCREEN, [c2.scrCoords[1] - d2x, c2.scrCoords[2] - d2y], element.board); 371 } 372 } 373 if (element.visProp.firstarrow && element.visProp.touchfirstpoint) { 374 d = c1.distance(Const.COORDS_BY_SCREEN, c2); 375 if (d > s) { 376 d1x = (c2.scrCoords[1] - c1.scrCoords[1]) * s1 / d; 377 d1y = (c2.scrCoords[2] - c1.scrCoords[2]) * s1 / d; 378 c1 = new Coords(Const.COORDS_BY_SCREEN, [c1.scrCoords[1] + d1x, c1.scrCoords[2] + d1y], element.board); 379 } 380 } 381 382 s = Math.max(parseInt(element.visProp.strokewidth, 10) * 3, minlen); 383 d = c1.distance(Const.COORDS_BY_SCREEN, c2); 384 if (element.visProp.lastarrow && element.board.renderer.type !== 'vml' && d >= minlen) { 385 d2x = (c2.scrCoords[1] - c1.scrCoords[1]) * s / d; 386 d2y = (c2.scrCoords[2] - c1.scrCoords[2]) * s / d; 387 } 388 if (element.visProp.firstarrow && element.board.renderer.type !== 'vml' && d >= minlen) { 389 d1x = (c2.scrCoords[1] - c1.scrCoords[1]) * s / d; 390 d1y = (c2.scrCoords[2] - c1.scrCoords[2]) * s / d; 391 } 392 } 393 394 this.updateLinePrim(element.rendNode, 395 c1.scrCoords[1] + d1x, c1.scrCoords[2] + d1y, 396 c2.scrCoords[1] - d2x, c2.scrCoords[2] - d2y, element.board); 397 398 this.makeArrows(element); 399 this._updateVisual(element); 400 }, 401 402 /** 403 * Creates a rendering node for ticks added to a line. 404 * @param {JXG.Line} element A arbitrary line. 405 * @see Line 406 * @see Ticks 407 * @see JXG.Line 408 * @see JXG.Ticks 409 * @see JXG.AbstractRenderer#updateTicks 410 */ 411 drawTicks: function (element) { 412 element.rendNode = this.appendChildPrim(this.createPrim('path', element.id), element.visProp.layer); 413 this.appendNodesToElement(element, 'path'); 414 }, 415 416 /** 417 * Update {@link Ticks} on a {@link JXG.Line}. This method is only a stub and has to be implemented 418 * in any descendant renderer class. 419 * @param {JXG.Ticks} element Reference of a ticks object that has to be updated. 420 * @see Line 421 * @see Ticks 422 * @see JXG.Line 423 * @see JXG.Ticks 424 * @see JXG.AbstractRenderer#drawTicks 425 */ 426 updateTicks: function (element) { /* stub */ }, 427 428 /* ************************** 429 * Curves 430 * **************************/ 431 432 /** 433 * Draws a {@link JXG.Curve} on the {@link JXG.Board}. 434 * @param {JXG.Curve} element Reference to a graph object, that has to be plotted. 435 * @see Curve 436 * @see JXG.Curve 437 * @see JXG.AbstractRenderer#updateCurve 438 */ 439 drawCurve: function (element) { 440 element.rendNode = this.appendChildPrim(this.createPrim('path', element.id), element.visProp.layer); 441 this.appendNodesToElement(element, 'path'); 442 this._updateVisual(element, {shadow: true}, true); 443 this.updateCurve(element); 444 }, 445 446 /** 447 * Updates visual appearance of the renderer element assigned to the given {@link JXG.Curve}. 448 * @param {JXG.Curve} element Reference to a {@link JXG.Curve} object, that has to be updated. 449 * @see Curve 450 * @see JXG.Curve 451 * @see JXG.AbstractRenderer#drawCurve 452 */ 453 updateCurve: function (element) { 454 this._updateVisual(element); 455 if (element.visProp.handdrawing) { 456 this.updatePathPrim(element.rendNode, this.updatePathStringBezierPrim(element), element.board); 457 } else { 458 this.updatePathPrim(element.rendNode, this.updatePathStringPrim(element), element.board); 459 } 460 if (element.numberPoints > 1) { 461 this.makeArrows(element); 462 } 463 }, 464 465 /* ************************** 466 * Circle related stuff 467 * **************************/ 468 469 /** 470 * Draws a {@link JXG.Circle} 471 * @param {JXG.Circle} element Reference to a {@link JXG.Circle} object that has to be drawn. 472 * @see Circle 473 * @see JXG.Circle 474 * @see JXG.AbstractRenderer#updateEllipse 475 */ 476 drawEllipse: function (element) { 477 element.rendNode = this.appendChildPrim(this.createPrim('ellipse', element.id), element.visProp.layer); 478 this.appendNodesToElement(element, 'ellipse'); 479 this.updateEllipse(element); 480 }, 481 482 /** 483 * Updates visual appearance of a given {@link JXG.Circle} on the {@link JXG.Board}. 484 * @param {JXG.Circle} element Reference to a {@link JXG.Circle} object, that has to be updated. 485 * @see Circle 486 * @see JXG.Circle 487 * @see JXG.AbstractRenderer#drawEllipse 488 */ 489 updateEllipse: function (element) { 490 this._updateVisual(element); 491 492 var radius = element.Radius(); 493 494 if (radius > 0.0 && 495 Math.abs(element.center.coords.usrCoords[0]) > Mat.eps && 496 !isNaN(radius + element.center.coords.scrCoords[1] + element.center.coords.scrCoords[2]) && 497 radius * element.board.unitX < 2000000) { 498 this.updateEllipsePrim(element.rendNode, element.center.coords.scrCoords[1], 499 element.center.coords.scrCoords[2], (radius * element.board.unitX), (radius * element.board.unitY)); 500 } 501 }, 502 503 504 /* ************************** 505 * Polygon related stuff 506 * **************************/ 507 508 /** 509 * Draws a {@link JXG.Polygon} on the {@link JXG.Board}. 510 * @param {JXG.Polygon} element Reference to a Polygon object, that is to be drawn. 511 * @see Polygon 512 * @see JXG.Polygon 513 * @see JXG.AbstractRenderer#updatePolygon 514 */ 515 drawPolygon: function (element) { 516 element.rendNode = this.appendChildPrim(this.createPrim('polygon', element.id), element.visProp.layer); 517 this.appendNodesToElement(element, 'polygon'); 518 this.updatePolygon(element); 519 }, 520 521 /** 522 * Updates properties of a {@link JXG.Polygon}'s rendering node. 523 * @param {JXG.Polygon} element Reference to a {@link JXG.Polygon} object, that has to be updated. 524 * @see Polygon 525 * @see JXG.Polygon 526 * @see JXG.AbstractRenderer#drawPolygon 527 */ 528 updatePolygon: function (element) { 529 var i, len, polIsReal; 530 531 // here originally strokecolor wasn't updated but strokewidth was 532 // but if there's no strokecolor i don't see why we should update strokewidth. 533 this._updateVisual(element, {stroke: true, dash: true}); 534 this.updatePolygonPrim(element.rendNode, element); 535 536 len = element.vertices.length; 537 polIsReal = true; 538 for (i = 0; i < len; ++i) { 539 if (!element.vertices[i].isReal) { 540 polIsReal = false; 541 break; 542 } 543 } 544 545 len = element.borders.length; 546 for (i = 0; i < len; ++i) { 547 if (polIsReal && element.borders[i].visProp.visible) { 548 this.show(element.borders[i]); 549 } else { 550 this.hide(element.borders[i]); 551 } 552 } 553 }, 554 555 /* ************************** 556 * Text related stuff 557 * **************************/ 558 559 /** 560 * Shows a small copyright notice in the top left corner of the board. 561 * @param {String} str The copyright notice itself 562 * @param {Number} fontsize Size of the font the copyright notice is written in 563 */ 564 displayCopyright: function (str, fontsize) { /* stub */ }, 565 566 /** 567 * An internal text is a {@link JXG.Text} element which is drawn using only 568 * the given renderer but no HTML. This method is only a stub, the drawing 569 * is done in the special renderers. 570 * @param {JXG.Text} element Reference to a {@link JXG.Text} object 571 * @see Text 572 * @see JXG.Text 573 * @see JXG.AbstractRenderer#updateInternalText 574 * @see JXG.AbstractRenderer#drawText 575 * @see JXG.AbstractRenderer#updateText 576 * @see JXG.AbstractRenderer#updateTextStyle 577 */ 578 drawInternalText: function (element) { /* stub */ }, 579 580 /** 581 * Updates visual properties of an already existing {@link JXG.Text} element. 582 * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be updated. 583 * @see Text 584 * @see JXG.Text 585 * @see JXG.AbstractRenderer#drawInternalText 586 * @see JXG.AbstractRenderer#drawText 587 * @see JXG.AbstractRenderer#updateText 588 * @see JXG.AbstractRenderer#updateTextStyle 589 */ 590 updateInternalText: function (element) { /* stub */ }, 591 592 /** 593 * Displays a {@link JXG.Text} on the {@link JXG.Board} by putting a HTML div over it. 594 * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be displayed 595 * @see Text 596 * @see JXG.Text 597 * @see JXG.AbstractRenderer#drawInternalText 598 * @see JXG.AbstractRenderer#updateText 599 * @see JXG.AbstractRenderer#updateInternalText 600 * @see JXG.AbstractRenderer#updateTextStyle 601 */ 602 drawText: function (element) { 603 var node, z, level; 604 605 if (element.visProp.display === 'html' && Env.isBrowser && this.type !== 'no') { 606 node = this.container.ownerDocument.createElement('div'); 607 //node = this.container.ownerDocument.createElementNS('http://www.w3.org/1999/xhtml', 'div'); // 608 node.style.position = 'absolute'; 609 610 node.className = element.visProp.cssclass; 611 612 /* SVG renderer - beside IE 9-11 - support foreignObject. This 613 is used to host the HTML. Then, conversion to canvas works also 614 for HTML text. 615 616 But to allow interaction with the HTML elements (e.g. buttons) all such texts 617 have to be in a single foreign object. The reason is our foreign objects have to cover the 618 whole board and would block event bubbling. 619 */ 620 level = element.visProp.layer; 621 if (!Type.exists(level)) { // trace nodes have level not set 622 level = 0; 623 } 624 if (!element.visProp.externalhtml && this.supportsForeignObject && 625 Type.exists(this.foreignObjLayer[level])) { 626 this.foreignObjLayer[level].appendChild(node); 627 } else { 628 if (this.container.style.zIndex === '') { 629 z = 0; 630 } else { 631 z = parseInt(this.container.style.zIndex, 10); 632 } 633 634 node.style.zIndex = z + level; 635 this.container.appendChild(node); 636 } 637 638 node.setAttribute('id', this.container.id + '_' + element.id); 639 } else { 640 node = this.drawInternalText(element); 641 } 642 643 element.rendNode = node; 644 element.htmlStr = ''; 645 this.updateText(element); 646 }, 647 648 /** 649 * Updates visual properties of an already existing {@link JXG.Text} element. 650 * @param {JXG.Text} el Reference to an {@link JXG.Text} object, that has to be updated. 651 * @see Text 652 * @see JXG.Text 653 * @see JXG.AbstractRenderer#drawText 654 * @see JXG.AbstractRenderer#drawInternalText 655 * @see JXG.AbstractRenderer#updateInternalText 656 * @see JXG.AbstractRenderer#updateTextStyle 657 */ 658 updateText: function (el) { 659 var content = el.plaintext, v, c, parentNode; 660 661 if (el.visProp.visible) { 662 this.updateTextStyle(el, false); 663 664 if (el.visProp.display === 'html' && this.type !== 'no') { 665 // Set the position 666 if (!isNaN(el.coords.scrCoords[1] + el.coords.scrCoords[2])) { 667 668 // Horizontal 669 c = el.coords.scrCoords[1]; 670 // webkit seems to fail for extremely large values for c. 671 c = Math.abs(c) < 1000000 ? c : 1000000; 672 673 if (el.visProp.anchorx === 'right') { 674 v = Math.floor(el.board.canvasWidth - c); 675 } else if (el.visProp.anchorx === 'middle') { 676 v = Math.floor(c - 0.5 * el.size[0]); 677 } else { // 'left' 678 v = Math.floor(c); 679 } 680 681 // This may be useful for foreignObj. 682 //if (window.devicePixelRatio !== undefined) { 683 //v *= window.devicePixelRatio; 684 //} 685 686 if (el.visPropOld.left !== (el.visProp.anchorx + v)) { 687 if (el.visProp.anchorx === 'right') { 688 el.rendNode.style.right = v + 'px'; 689 el.rendNode.style.left = 'auto'; 690 } else { 691 el.rendNode.style.left = v + 'px'; 692 el.rendNode.style.right = 'auto'; 693 } 694 el.visPropOld.left = el.visProp.anchorx + v; 695 } 696 697 // Vertical 698 c = el.coords.scrCoords[2] + this.vOffsetText; 699 c = Math.abs(c) < 1000000 ? c : 1000000; 700 701 if (el.visProp.anchory === 'bottom') { 702 v = Math.floor(el.board.canvasHeight - c); 703 } else if (el.visProp.anchory === 'middle') { 704 v = Math.floor(c - 0.5 * el.size[1]); 705 } else { // top 706 v = Math.floor(c); 707 } 708 709 // This may be useful for foreignObj. 710 //if (window.devicePixelRatio !== undefined) { 711 //v *= window.devicePixelRatio; 712 //} 713 714 if (el.visPropOld.top !== (el.visProp.anchory + v)) { 715 if (el.visProp.anchory === 'bottom') { 716 el.rendNode.style.top = 'auto'; 717 el.rendNode.style.bottom = v + 'px'; 718 } else { 719 el.rendNode.style.bottom = 'auto'; 720 el.rendNode.style.top = v + 'px'; 721 } 722 el.visPropOld.top = el.visProp.anchory + v; 723 } 724 } 725 726 // Set the content 727 if (el.htmlStr !== content) { 728 try { 729 el.rendNode.innerHTML = content; 730 } catch (e) { 731 // Setting innerHTML sometimes fails in IE8. A workaround is to 732 // take the node off the DOM, assign innerHTML, then append back. 733 // Works for text elements as they are absolutely positioned. 734 parentNode = el.rendNode.parentNode; 735 el.rendNode.parentNode.removeChild(el.rendNode); 736 el.rendNode.innerHTML = content; 737 parentNode.appendChild(el.rendNode); 738 } 739 el.htmlStr = content; 740 741 if (el.visProp.usemathjax) { 742 // typesetting directly might not work because mathjax was not loaded completely 743 // see http://www.mathjax.org/docs/1.1/typeset.html 744 MathJax.Hub.Queue(['Typeset', MathJax.Hub, el.rendNode]); 745 } else if (el.visProp.useasciimathml) { 746 // This is not a constructor. 747 // See http://www1.chapman.edu/~jipsen/mathml/asciimath.html for more information 748 // about AsciiMathML and the project's source code. 749 AMprocessNode(el.rendNode, false); 750 } 751 } 752 this.transformImage(el, el.transformations); 753 } else { 754 this.updateInternalText(el); 755 } 756 } 757 }, 758 759 /** 760 * Updates font-size, color and opacity propertiey and CSS style properties of a {@link JXG.Text} node. 761 * This function is also called by highlight() and nohighlight(). 762 * @param {JXG.Text} element Reference to the {@link JXG.Text} object, that has to be updated. 763 * @param {Boolean} doHighlight 764 * @see Text 765 * @see JXG.Text 766 * @see JXG.AbstractRenderer#drawText 767 * @see JXG.AbstractRenderer#drawInternalText 768 * @see JXG.AbstractRenderer#updateText 769 * @see JXG.AbstractRenderer#updateInternalText 770 * @see JXG.AbstractRenderer#updateInternalTextStyle 771 */ 772 updateTextStyle: function (element, doHighlight) { 773 var fs, so, sc, css, node, list, 774 ev = element.visProp, 775 display = Env.isBrowser ? ev.display : 'internal'; 776 777 if (doHighlight) { 778 sc = ev.highlightstrokecolor; 779 so = ev.highlightstrokeopacity; 780 css = ev.highlightcssclass; 781 } else { 782 sc = ev.strokecolor; 783 so = ev.strokeopacity; 784 css = ev.cssclass; 785 } 786 787 // This part is executed for all text elements except internal texts in canvas. 788 if (display === 'html' || (this.type !== 'canvas' && this.type !== 'no')) { 789 fs = Type.evaluate(element.visProp.fontsize); 790 if (element.visPropOld.fontsize !== fs) { 791 element.needsSizeUpdate = true; 792 list = ['rendNode', 'rendNodeTag', 'rendNodeLabel']; 793 try { 794 for (node in list) { 795 if (JXG.exists(element[list[node]])) { 796 element[list[node]].style.fontSize = fs + 'px'; 797 } 798 } 799 } catch (e) { 800 // IE needs special treatment. 801 for (node in list) { 802 if (JXG.exists(element[list[node]])) { 803 element[list[node]].style.fontSize = fs; 804 } 805 } 806 } 807 element.visPropOld.fontsize = fs; 808 } 809 810 } 811 812 if (display === 'html' && this.type !== 'no') { 813 if (element.visPropOld.cssclass !== css) { 814 element.rendNode.className = css; 815 element.visPropOld.cssclass = css; 816 element.needsSizeUpdate = true; 817 } 818 this.setObjectStrokeColor(element, sc, so); 819 } else { 820 this.updateInternalTextStyle(element, sc, so); 821 } 822 return this; 823 }, 824 825 /** 826 * Set color and opacity of internal texts. 827 * This method is used for Canvas and VML. 828 * SVG needs its own version. 829 * @private 830 * @see JXG.AbstractRenderer#updateTextStyle 831 * @see JXG.SVGRenderer#updateInternalTextStyle 832 */ 833 updateInternalTextStyle: function (element, strokeColor, strokeOpacity) { 834 this.setObjectStrokeColor(element, strokeColor, strokeOpacity); 835 }, 836 837 /* ************************** 838 * Image related stuff 839 * **************************/ 840 841 /** 842 * Draws an {@link JXG.Image} on a board; This is just a template that has to be implemented by special 843 * renderers. 844 * @param {JXG.Image} element Reference to the image object that is to be drawn 845 * @see Image 846 * @see JXG.Image 847 * @see JXG.AbstractRenderer#updateImage 848 */ 849 drawImage: function (element) { /* stub */ }, 850 851 /** 852 * Updates the properties of an {@link JXG.Image} element. 853 * @param {JXG.Image} element Reference to an {@link JXG.Image} object, that has to be updated. 854 * @see Image 855 * @see JXG.Image 856 * @see JXG.AbstractRenderer#drawImage 857 */ 858 updateImage: function (element) { 859 this.updateRectPrim(element.rendNode, element.coords.scrCoords[1], 860 element.coords.scrCoords[2] - element.size[1], element.size[0], element.size[1]); 861 862 this.updateImageURL(element); 863 this.transformImage(element, element.transformations); 864 this._updateVisual(element, {stroke: true, dash: true}, true); 865 }, 866 867 /** 868 * Multiplication of transformations without updating. That means, at that point it is expected that the 869 * matrices contain numbers only. First, the origin in user coords is translated to <tt>(0,0)</tt> in screen 870 * coords. Then, the stretch factors are divided out. After the transformations in user coords, the stretch 871 * factors are multiplied in again, and the origin in user coords is translated back to its position. This 872 * method does not have to be implemented in a new renderer. 873 * @param {JXG.GeometryElement} element A JSXGraph element. We only need its board property. 874 * @param {Array} transformations An array of JXG.Transformations. 875 * @returns {Array} A matrix represented by a two dimensional array of numbers. 876 * @see JXG.AbstractRenderer#transformImage 877 */ 878 joinTransforms: function (element, transformations) { 879 var i, 880 ox = element.board.origin.scrCoords[1], 881 oy = element.board.origin.scrCoords[2], 882 ux = element.board.unitX, 883 uy = element.board.unitY, 884 // Translate to 0,0 in screen coords 885 /* 886 m = [[1, 0, 0], [0, 1, 0], [0, 0, 1]], 887 mpre1 = [[1, 0, 0], 888 [-ox, 1, 0], 889 [-oy, 0, 1]], 890 // Scale 891 mpre2 = [[1, 0, 0], 892 [0, 1 / ux, 0], 893 [0, 0, -1 / uy]], 894 // Scale back 895 mpost2 = [[1, 0, 0], 896 [0, ux, 0], 897 [0, 0, -uy]], 898 // Translate back 899 mpost1 = [[1, 0, 0], 900 [ox, 1, 0], 901 [oy, 0, 1]], 902 */ 903 len = transformations.length, 904 // Translate to 0,0 in screen coords and then scale 905 m = [[1, 0, 0], 906 [-ox / ux, 1 / ux, 0], 907 [ oy / uy, 0, -1 / uy]]; 908 909 for (i = 0; i < len; i++) { 910 //m = Mat.matMatMult(mpre1, m); 911 //m = Mat.matMatMult(mpre2, m); 912 m = Mat.matMatMult(transformations[i].matrix, m); 913 //m = Mat.matMatMult(mpost2, m); 914 //m = Mat.matMatMult(mpost1, m); 915 } 916 // Scale back and then translate back 917 m = Mat.matMatMult([[1, 0, 0], 918 [ox, ux, 0], 919 [oy, 0, -uy]], m); 920 return m; 921 }, 922 923 /** 924 * Applies transformations on images and text elements. This method is just a stub and has to be implemented in 925 * all descendant classes where text and image transformations are to be supported. 926 * @param {JXG.Image|JXG.Text} element A {@link JXG.Image} or {@link JXG.Text} object. 927 * @param {Array} transformations An array of {@link JXG.Transformation} objects. This is usually the 928 * transformations property of the given element <tt>el</tt>. 929 */ 930 transformImage: function (element, transformations) { /* stub */ }, 931 932 /** 933 * If the URL of the image is provided by a function the URL has to be updated during updateImage() 934 * @param {JXG.Image} element Reference to an image object. 935 * @see JXG.AbstractRenderer#updateImage 936 */ 937 updateImageURL: function (element) { /* stub */ }, 938 939 /** 940 * Updates CSS style properties of a {@link JXG.Image} node. 941 * In SVGRenderer opacity is the only available style element. 942 * This function is called by highlight() and nohighlight(). 943 * This function works for VML. 944 * It does not work for Canvas. 945 * SVGRenderer overwrites this method. 946 * @param {JXG.Text} el Reference to the {@link JXG.Image} object, that has to be updated. 947 * @param {Boolean} doHighlight 948 * @see Image 949 * @see JXG.Image 950 * @see JXG.AbstractRenderer#highlight 951 * @see JXG.AbstractRenderer#noHighlight 952 */ 953 updateImageStyle: function (el, doHighlight) { 954 el.rendNode.className = doHighlight ? el.visProp.highlightcssclass : el.visProp.cssclass; 955 }, 956 957 958 /* ************************** 959 * Render primitive objects 960 * **************************/ 961 962 /** 963 * Appends a node to a specific layer level. This is just an abstract method and has to be implemented 964 * in all renderers that want to use the <tt>createPrim</tt> model to draw. 965 * @param {Node} node A DOM tree node. 966 * @param {Number} level The layer the node is attached to. This is the index of the layer in 967 * {@link JXG.SVGRenderer#layer} or the <tt>z-index</tt> style property of the node in VMLRenderer. 968 */ 969 appendChildPrim: function (node, level) { /* stub */ }, 970 971 /** 972 * Stores the rendering nodes. This is an abstract method which has to be implemented in all renderers that use 973 * the <tt>createPrim</tt> method. 974 * @param {JXG.GeometryElement} element A JSXGraph element. 975 * @param {String} type The XML node name. Only used in VMLRenderer. 976 */ 977 appendNodesToElement: function (element, type) { /* stub */ }, 978 979 /** 980 * Creates a node of a given type with a given id. 981 * @param {String} type The type of the node to create. 982 * @param {String} id Set the id attribute to this. 983 * @returns {Node} Reference to the created node. 984 */ 985 createPrim: function (type, id) { 986 /* stub */ 987 return null; 988 }, 989 990 /** 991 * Removes an element node. Just a stub. 992 * @param {Node} node The node to remove. 993 */ 994 remove: function (node) { /* stub */ }, 995 996 /** 997 * Can be used to create the nodes to display arrows. This is an abstract method which has to be implemented 998 * in any descendant renderer. 999 * @param {JXG.GeometryElement} element The element the arrows are to be attached to. 1000 */ 1001 makeArrows: function (element) { /* stub */ }, 1002 1003 /** 1004 * Updates an ellipse node primitive. This is an abstract method which has to be implemented in all renderers 1005 * that use the <tt>createPrim</tt> method. 1006 * @param {Node} node Reference to the node. 1007 * @param {Number} x Centre X coordinate 1008 * @param {Number} y Centre Y coordinate 1009 * @param {Number} rx The x-axis radius. 1010 * @param {Number} ry The y-axis radius. 1011 */ 1012 updateEllipsePrim: function (node, x, y, rx, ry) { /* stub */ }, 1013 1014 /** 1015 * Refreshes a line node. This is an abstract method which has to be implemented in all renderers that use 1016 * the <tt>createPrim</tt> method. 1017 * @param {Node} node The node to be refreshed. 1018 * @param {Number} p1x The first point's x coordinate. 1019 * @param {Number} p1y The first point's y coordinate. 1020 * @param {Number} p2x The second point's x coordinate. 1021 * @param {Number} p2y The second point's y coordinate. 1022 * @param {JXG.Board} board 1023 */ 1024 updateLinePrim: function (node, p1x, p1y, p2x, p2y, board) { /* stub */ }, 1025 1026 /** 1027 * Updates a path element. This is an abstract method which has to be implemented in all renderers that use 1028 * the <tt>createPrim</tt> method. 1029 * @param {Node} node The path node. 1030 * @param {String} pathString A string formatted like e.g. <em>'M 1,2 L 3,1 L5,5'</em>. The format of the string 1031 * depends on the rendering engine. 1032 * @param {JXG.Board} board Reference to the element's board. 1033 */ 1034 updatePathPrim: function (node, pathString, board) { /* stub */ }, 1035 1036 /** 1037 * Builds a path data string to draw a point with a face other than <em>rect</em> and <em>circle</em>. Since 1038 * the format of such a string usually depends on the renderer this method 1039 * is only an abstract method. Therefore, it has to be implemented in the descendant renderer itself unless 1040 * the renderer does not use the createPrim interface but the draw* interfaces to paint. 1041 * @param {JXG.Point} element The point element 1042 * @param {Number} size A positive number describing the size. Usually the half of the width and height of 1043 * the drawn point. 1044 * @param {String} type A string describing the point's face. This method only accepts the shortcut version of 1045 * each possible face: <tt>x, +, <>, ^, v, >, < 1046 */ 1047 updatePathStringPoint: function (element, size, type) { /* stub */ }, 1048 1049 /** 1050 * Builds a path data string from a {@link JXG.Curve} element. Since the path data strings heavily depend on the 1051 * underlying rendering technique this method is just a stub. Although such a path string is of no use for the 1052 * CanvasRenderer, this method is used there to draw a path directly. 1053 * @param element 1054 */ 1055 updatePathStringPrim: function (element) { /* stub */ }, 1056 1057 /** 1058 * Builds a path data string from a {@link JXG.Curve} element such that the curve looks like hand drawn. Since 1059 * the path data strings heavily depend on the underlying rendering technique this method is just a stub. 1060 * Although such a path string is of no use for the CanvasRenderer, this method is used there to draw a path 1061 * directly. 1062 * @param element 1063 */ 1064 updatePathStringBezierPrim: function (element) { /* stub */ }, 1065 1066 1067 /** 1068 * Update a polygon primitive. 1069 * @param {Node} node 1070 * @param {JXG.Polygon} element A JSXGraph element of type {@link JXG.Polygon} 1071 */ 1072 updatePolygonPrim: function (node, element) { /* stub */ }, 1073 1074 /** 1075 * Update a rectangle primitive. This is used only for points with face of type 'rect'. 1076 * @param {Node} node The node yearning to be updated. 1077 * @param {Number} x x coordinate of the top left vertex. 1078 * @param {Number} y y coordinate of the top left vertex. 1079 * @param {Number} w Width of the rectangle. 1080 * @param {Number} h The rectangle's height. 1081 */ 1082 updateRectPrim: function (node, x, y, w, h) { /* stub */ }, 1083 1084 /* ************************** 1085 * Set Attributes 1086 * **************************/ 1087 1088 /** 1089 * Sets a node's attribute. 1090 * @param {Node} node The node that is to be updated. 1091 * @param {String} key Name of the attribute. 1092 * @param {String} val New value for the attribute. 1093 */ 1094 setPropertyPrim: function (node, key, val) { /* stub */ }, 1095 1096 /** 1097 * Shows a hidden element on the canvas; Only a stub, requires implementation in the derived renderer. 1098 * @param {JXG.GeometryElement} element Reference to the object that has to appear. 1099 * @see JXG.AbstractRenderer#hide 1100 */ 1101 show: function (element) { /* stub */ }, 1102 1103 /** 1104 * Hides an element on the canvas; Only a stub, requires implementation in the derived renderer. 1105 * @param {JXG.GeometryElement} element Reference to the geometry element that has to disappear. 1106 * @see JXG.AbstractRenderer#show 1107 */ 1108 hide: function (element) { /* stub */ }, 1109 1110 /** 1111 * Sets the buffering as recommended by SVGWG. Until now only Opera supports this and will be ignored by other 1112 * browsers. Although this feature is only supported by SVG we have this method in {@link JXG.AbstractRenderer} 1113 * because it is called from outside the renderer. 1114 * @param {Node} node The SVG DOM Node which buffering type to update. 1115 * @param {String} type Either 'auto', 'dynamic', or 'static'. For an explanation see 1116 * {@link http://www.w3.org/TR/SVGTiny12/painting.html#BufferedRenderingProperty}. 1117 */ 1118 setBuffering: function (node, type) { /* stub */ }, 1119 1120 /** 1121 * Sets an element's dash style. 1122 * @param {JXG.GeometryElement} element An JSXGraph element. 1123 */ 1124 setDashStyle: function (element) { /* stub */ }, 1125 1126 /** 1127 * Puts an object into draft mode, i.e. it's visual appearance will be changed. For GEONE<sub>x</sub>T backwards 1128 * compatibility. 1129 * @param {JXG.GeometryElement} element Reference of the object that is in draft mode. 1130 */ 1131 setDraft: function (element) { 1132 if (!element.visProp.draft) { 1133 return; 1134 } 1135 var draftColor = element.board.options.elements.draft.color, 1136 draftOpacity = element.board.options.elements.draft.opacity; 1137 1138 if (element.type === Const.OBJECT_TYPE_POLYGON) { 1139 this.setObjectFillColor(element, draftColor, draftOpacity); 1140 } else { 1141 if (element.elementClass === Const.OBJECT_CLASS_POINT) { 1142 this.setObjectFillColor(element, draftColor, draftOpacity); 1143 } else { 1144 this.setObjectFillColor(element, 'none', 0); 1145 } 1146 this.setObjectStrokeColor(element, draftColor, draftOpacity); 1147 this.setObjectStrokeWidth(element, element.board.options.elements.draft.strokeWidth); 1148 } 1149 }, 1150 1151 /** 1152 * Puts an object from draft mode back into normal mode. 1153 * @param {JXG.GeometryElement} element Reference of the object that no longer is in draft mode. 1154 */ 1155 removeDraft: function (element) { 1156 if (element.type === Const.OBJECT_TYPE_POLYGON) { 1157 this.setObjectFillColor(element, element.visProp.fillcolor, element.visProp.fillopacity); 1158 } else { 1159 if (element.type === Const.OBJECT_CLASS_POINT) { 1160 this.setObjectFillColor(element, element.visProp.fillcolor, element.visProp.fillopacity); 1161 } 1162 this.setObjectStrokeColor(element, element.visProp.strokecolor, element.visProp.strokeopacity); 1163 this.setObjectStrokeWidth(element, element.visProp.strokewidth); 1164 } 1165 }, 1166 1167 /** 1168 * Sets up nodes for rendering a gradient fill. 1169 * @param element 1170 */ 1171 setGradient: function (element) { /* stub */ }, 1172 1173 /** 1174 * Updates the gradient fill. 1175 * @param {JXG.GeometryElement} element An JSXGraph element with an area that can be filled. 1176 */ 1177 updateGradient: function (element) { /* stub */ }, 1178 1179 /** 1180 * Sets an objects fill color. 1181 * @param {JXG.GeometryElement} element Reference of the object that wants a new fill color. 1182 * @param {String} color Color in a HTML/CSS compatible format. If you don't want any fill color at all, choose 1183 * 'none'. 1184 * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1. 1185 */ 1186 setObjectFillColor: function (element, color, opacity) { /* stub */ }, 1187 1188 /** 1189 * Changes an objects stroke color to the given color. 1190 * @param {JXG.GeometryElement} element Reference of the {@link JXG.GeometryElement} that gets a new stroke 1191 * color. 1192 * @param {String} color Color value in a HTML compatible format, e.g. <strong>#00ff00</strong> or 1193 * <strong>green</strong> for green. 1194 * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1. 1195 */ 1196 setObjectStrokeColor: function (element, color, opacity) { /* stub */ }, 1197 1198 /** 1199 * Sets an element's stroke width. 1200 * @param {JXG.GeometryElement} element Reference to the geometry element. 1201 * @param {Number} width The new stroke width to be assigned to the element. 1202 */ 1203 setObjectStrokeWidth: function (element, width) { /* stub */ }, 1204 1205 /** 1206 * Sets the shadow properties to a geometry element. This method is only a stub, it is implemented in the actual 1207 * renderers. 1208 * @param {JXG.GeometryElement} element Reference to a geometry object, that should get a shadow 1209 */ 1210 setShadow: function (element) { /* stub */ }, 1211 1212 /** 1213 * Highlights an object, i.e. changes the current colors of the object to its highlighting colors 1214 * @param {JXG.GeometryElement} element Reference of the object that will be highlighted. 1215 * @returns {JXG.AbstractRenderer} Reference to the renderer 1216 * @see JXG.AbstractRenderer#updateTextStyle 1217 */ 1218 highlight: function (element) { 1219 var i, ev = element.visProp; 1220 1221 if (!ev.draft) { 1222 if (element.type === Const.OBJECT_TYPE_POLYGON) { 1223 this.setObjectFillColor(element, ev.highlightfillcolor, ev.highlightfillopacity); 1224 for (i = 0; i < element.borders.length; i++) { 1225 this.setObjectStrokeColor(element.borders[i], 1226 element.borders[i].visProp.highlightstrokecolor, 1227 element.borders[i].visProp.highlightstrokeopacity); 1228 } 1229 } else { 1230 if (element.elementClass === Const.OBJECT_CLASS_TEXT) { 1231 this.updateTextStyle(element, true); 1232 } else if (element.type === Const.OBJECT_TYPE_IMAGE) { 1233 this.updateImageStyle(element, true); 1234 this.setObjectFillColor(element, ev.highlightfillcolor, ev.highlightfillopacity); 1235 } else { 1236 this.setObjectStrokeColor(element, ev.highlightstrokecolor, ev.highlightstrokeopacity); 1237 this.setObjectFillColor(element, ev.highlightfillcolor, ev.highlightfillopacity); 1238 } 1239 } 1240 if (ev.highlightstrokewidth) { 1241 this.setObjectStrokeWidth(element, Math.max(ev.highlightstrokewidth, ev.strokewidth)); 1242 } 1243 } 1244 1245 return this; 1246 }, 1247 1248 /** 1249 * Uses the normal colors of an object, i.e. the opposite of {@link JXG.AbstractRenderer#highlight}. 1250 * @param {JXG.GeometryElement} element Reference of the object that will get its normal colors. 1251 * @returns {JXG.AbstractRenderer} Reference to the renderer 1252 * @see JXG.AbstractRenderer#updateTextStyle 1253 */ 1254 noHighlight: function (element) { 1255 var i, ev = element.visProp; 1256 1257 if (!element.visProp.draft) { 1258 if (element.type === Const.OBJECT_TYPE_POLYGON) { 1259 this.setObjectFillColor(element, ev.fillcolor, ev.fillopacity); 1260 for (i = 0; i < element.borders.length; i++) { 1261 this.setObjectStrokeColor(element.borders[i], element.borders[i].visProp.strokecolor, 1262 element.borders[i].visProp.strokeopacity); 1263 } 1264 } else { 1265 if (element.elementClass === Const.OBJECT_CLASS_TEXT) { 1266 this.updateTextStyle(element, false); 1267 } else if (element.type === Const.OBJECT_TYPE_IMAGE) { 1268 this.updateImageStyle(element, false); 1269 this.setObjectFillColor(element, ev.fillcolor, ev.fillopacity); 1270 } else { 1271 this.setObjectStrokeColor(element, ev.strokecolor, ev.strokeopacity); 1272 this.setObjectFillColor(element, ev.fillcolor, ev.fillopacity); 1273 } 1274 } 1275 this.setObjectStrokeWidth(element, ev.strokewidth); 1276 } 1277 1278 return this; 1279 }, 1280 1281 /* ************************** 1282 * renderer control 1283 * **************************/ 1284 1285 /** 1286 * Stop redraw. This method is called before every update, so a non-vector-graphics based renderer can use this 1287 * method to delete the contents of the drawing panel. This is an abstract method every descendant renderer 1288 * should implement, if appropriate. 1289 * @see JXG.AbstractRenderer#unsuspendRedraw 1290 */ 1291 suspendRedraw: function () { /* stub */ }, 1292 1293 /** 1294 * Restart redraw. This method is called after updating all the rendering node attributes. 1295 * @see JXG.AbstractRenderer#suspendRedraw 1296 */ 1297 unsuspendRedraw: function () { /* stub */ }, 1298 1299 /** 1300 * The tiny zoom bar shown on the bottom of a board (if showNavigation on board creation is true). 1301 * @param {JXG.Board} board Reference to a JSXGraph board. 1302 */ 1303 drawZoomBar: function (board) { 1304 var doc, 1305 node, 1306 cancelbubble = function (e) { 1307 if (!e) { 1308 e = window.event; 1309 } 1310 1311 if (e.stopPropagation) { 1312 // Non IE<=8 1313 e.stopPropagation(); 1314 } else { 1315 e.cancelBubble = true; 1316 } 1317 }, 1318 createButton = function (label, handler) { 1319 var button; 1320 1321 button = doc.createElement('span'); 1322 node.appendChild(button); 1323 button.appendChild(doc.createTextNode(label)); 1324 Env.addEvent(button, 'mouseover', function () { 1325 this.style.backgroundColor = board.options.navbar.highlightFillColor; 1326 }, button); 1327 Env.addEvent(button, 'mouseover', function () { 1328 this.style.backgroundColor = board.options.navbar.highlightFillColor; 1329 }, button); 1330 Env.addEvent(button, 'mouseout', function () { 1331 this.style.backgroundColor = board.options.navbar.fillColor; 1332 }, button); 1333 1334 Env.addEvent(button, 'click', function(e) { (Type.bind(handler, board))(); return false; }, board); 1335 // prevent the click from bubbling down to the board 1336 Env.addEvent(button, 'mouseup', cancelbubble, board); 1337 Env.addEvent(button, 'mousedown', cancelbubble, board); 1338 Env.addEvent(button, 'touchend', cancelbubble, board); 1339 Env.addEvent(button, 'touchstart', cancelbubble, board); 1340 }; 1341 1342 if (Env.isBrowser && this.type !== 'no') { 1343 doc = board.containerObj.ownerDocument; 1344 node = doc.createElement('div'); 1345 1346 node.setAttribute('id', board.containerObj.id + '_navigationbar'); 1347 1348 node.style.color = board.options.navbar.strokeColor; 1349 node.style.backgroundColor = board.options.navbar.fillColor; 1350 node.style.padding = board.options.navbar.padding; 1351 node.style.position = board.options.navbar.position; 1352 node.style.fontSize = board.options.navbar.fontSize; 1353 node.style.cursor = board.options.navbar.cursor; 1354 node.style.zIndex = board.options.navbar.zIndex; 1355 board.containerObj.appendChild(node); 1356 node.style.right = board.options.navbar.right; 1357 node.style.bottom = board.options.navbar.bottom; 1358 1359 // For XHTML we need unicode instead of HTML entities 1360 1361 if (board.attr.showreload) { 1362 // full reload circle: \u27F2 1363 // the board.reload() method does not exist during the creation 1364 // of this button. That's why this anonymous function wrapper is required. 1365 createButton('\u00A0\u21BB\u00A0', function () { 1366 board.reload(); 1367 }); 1368 } 1369 1370 if (board.attr.showcleartraces) { 1371 // clear traces symbol (otimes): \u27F2 1372 createButton('\u00A0\u2297\u00A0', function () { 1373 board.clearTraces(); 1374 }); 1375 } 1376 1377 if (board.attr.shownavigation) { 1378 createButton('\u00A0\u2013\u00A0', board.zoomOut); 1379 createButton('\u00A0o\u00A0', board.zoom100); 1380 createButton('\u00A0+\u00A0', board.zoomIn); 1381 createButton('\u00A0\u2190\u00A0', board.clickLeftArrow); 1382 createButton('\u00A0\u2193\u00A0', board.clickUpArrow); 1383 createButton('\u00A0\u2191\u00A0', board.clickDownArrow); 1384 createButton('\u00A0\u2192\u00A0', board.clickRightArrow); 1385 } 1386 } 1387 }, 1388 1389 /** 1390 * Wrapper for getElementById for maybe other renderers which elements are not directly accessible by DOM 1391 * methods like document.getElementById(). 1392 * @param {String} id Unique identifier for element. 1393 * @returns {Object} Reference to a JavaScript object. In case of SVG/VMLRenderer it's a reference to a SVG/VML 1394 * node. 1395 */ 1396 getElementById: function (id) { 1397 return this.container.ownerDocument.getElementById(this.container.id + '_' + id); 1398 }, 1399 1400 /** 1401 * Remove an element and provide a function that inserts it into its original position. This method 1402 * is taken from this article {@link https://developers.google.com/speed/articles/javascript-dom}. 1403 * @author KeeKim Heng, Google Web Developer 1404 * @param {Element} element The element to be temporarily removed 1405 * @returns {Function} A function that inserts the element into its original position 1406 */ 1407 removeToInsertLater: function (element) { 1408 var parentNode = element.parentNode, 1409 nextSibling = element.nextSibling; 1410 1411 parentNode.removeChild(element); 1412 1413 return function () { 1414 if (nextSibling) { 1415 parentNode.insertBefore(element, nextSibling); 1416 } else { 1417 parentNode.appendChild(element); 1418 } 1419 }; 1420 }, 1421 1422 /** 1423 * Resizes the rendering element 1424 * @param {Number} w New width 1425 * @param {Number} h New height 1426 */ 1427 resize: function (w, h) { /* stub */}, 1428 1429 /** 1430 * Create crosshair elements (Fadenkreuz) for presentations. 1431 * @param {Number} n Number of crosshairs. 1432 */ 1433 createTouchpoints: function (n) {}, 1434 1435 /** 1436 * Show a specific crosshair. 1437 * @param {Number} i Number of the crosshair to show 1438 */ 1439 showTouchpoint: function (i) {}, 1440 1441 /** 1442 * Hide a specific crosshair. 1443 * @param {Number} i Number of the crosshair to show 1444 */ 1445 hideTouchpoint: function (i) {}, 1446 1447 /** 1448 * Move a specific crosshair. 1449 * @param {Number} i Number of the crosshair to show 1450 * @param {Array} pos New positon in screen coordinates 1451 */ 1452 updateTouchpoint: function (i, pos) {}, 1453 1454 /** 1455 * Convert SVG construction to canvas. 1456 * Only available on SVGRenderer. 1457 * 1458 * @see JXG.SVGRenderer#dumpToCanvas 1459 */ 1460 dumpToCanvas: function(canvasId) {} 1461 }); 1462 1463 return JXG.AbstractRenderer; 1464 }); 1465