1 /* 2 Copyright 2008-2011 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: you can redistribute it and/or modify 13 it under the terms of the GNU Lesser General Public License as published by 14 the Free Software Foundation, either version 3 of the License, or 15 (at your option) any later version. 16 17 JSXGraph is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 GNU Lesser General Public License for more details. 21 22 You should have received a copy of the GNU Lesser General Public License 23 along with JSXGraph. If not, see <http://www.gnu.org/licenses/>. 24 25 */ 26 27 /*jshint bitwise: false, curly: true, debug: false, eqeqeq: true, devel: false, evil: false, 28 forin: false, immed: true, laxbreak: false, newcap: false, noarg: true, nonew: true, onevar: true, 29 undef: true, white: true, sub: false*/ 30 /*global JXG: true, AMprocessNode: true, MathJax: true, document: true */ 31 32 /** 33 * @fileoverview JSXGraph can use various technologies to render the contents of a construction, e.g. 34 * SVG, VML, and HTML5 Canvas. To accomplish this, The rendering and the logic and control mechanisms 35 * are completely separated from each other. Every rendering technology has it's own class, called 36 * Renderer, e.g. SVGRenderer for SVG, the same for VML and Canvas. The common base for all available 37 * renderers is the class AbstractRenderer defined in this file. 38 */ 39 40 /** 41 * <p>This class defines the interface to the graphics part of JSXGraph. This class is an abstract class, it 42 * actually does not render anything. This is up to the {@link JXG.SVGRenderer}, {@link JXG.VMLRenderer}, 43 * and {@link JXG.CanvasRenderer} classes. We strongly discourage you from using the methods in these classes 44 * directly. Only the methods which are defined in this class and are not marked as private are guaranteed 45 * to exist in any renderer instance you can access via {@link JXG.Board#renderer}. But not all methods may 46 * work as expected.</p> 47 * <p>The methods of this renderer can be divided into different categories: 48 * <dl> 49 * <dt>Draw basic elements</dt> 50 * <dd>In this category we find methods to draw basic elements like {@link JXG.Point}, {@link JXG.Line}, 51 * and {@link JXG.Curve} as well as assisting methods tightly bound to these basic painters. You do not 52 * need to implement these methods in a descendant renderer but instead implement the primitive drawing 53 * methods described below. This approach is encouraged when you're using a XML based rendering engine 54 * like VML and SVG. If you want to use a bitmap based rendering technique you are supposed to override 55 * these methods instead of the primitive drawing methods.</dd> 56 * <dt>Draw primitives</dt> 57 * <dd>This category summarizes methods to handle primitive nodes. As creation and management of these nodes 58 * is different among different the rendering techniques most of these methods are purely virtual and need 59 * proper implementation if you choose to not overwrite the basic element drawing methods.</dd> 60 * <dt>Attribute manipulation</dt> 61 * <dd>In XML based renders you have to manipulate XML nodes and their attributes to change the graphics. 62 * For that purpose attribute manipulation methods are defined to set the color, opacity, and other things. 63 * Please note that some of these methods are required in bitmap based renderers, too, because some elements 64 * like {@link JXG.Text} can be HTML nodes floating over the construction.</dd> 65 * <dt>Renderer control</dt> 66 * <dd>Methods to clear the drawing board or to stop and to resume the rendering engine.</dd> 67 * </dl></p> 68 * @class JXG.AbstractRenderer 69 * @see JXG.SVGRenderer 70 * @see JXG.VMLRenderer 71 * @see JXG.CanvasRenderer 72 */ 73 JXG.AbstractRenderer = function () { 74 75 // WHY THIS IS A CLASS INSTEAD OF A SINGLETON OBJECT: 76 // 77 // The renderers need to keep track of some stuff which is not always the same on different boards, 78 // like enhancedRendering, reference to the container object, and resolution in VML. Sure, those 79 // things could be stored in board. But they are rendering related and JXG.Board is already very 80 // very big. 81 // 82 // And we can't save the rendering related data in {SVG,VML,Canvas}Renderer and make only the 83 // JXG.AbstractRenderer a singleton because of that: 84 // 85 // Given an object o with property a set to true 86 // var o = {a: true}; 87 // and a class c doing nothing 88 // c = function() {}; 89 // Set c's prototype to o 90 // c.prototype = o; 91 // and create an instance of c we get i.a to be true 92 // i = new c(); 93 // i.a; 94 // > true 95 // But we can overwrite this property via 96 // c.prototype.a = false; 97 // i.a; 98 // > false 99 100 /** 101 * The vertical offset for {@link Text} elements. Every {@link Text} element will 102 * be placed this amount of pixels below the user given coordinates. 103 * @type number 104 * @default 8 105 */ 106 this.vOffsetText = 0; 107 108 /** 109 * If this property is set to <tt>true</tt> the visual properties of the elements are updated 110 * on every update. Visual properties means: All the stuff stored in the 111 * {@link JXG.GeometryElement#visProp} property won't be set if enhancedRendering is <tt>false</tt> 112 * @type Boolean 113 * @default true 114 */ 115 this.enhancedRendering = true; 116 117 /** 118 * The HTML element that stores the JSXGraph board in it. 119 * @type Node 120 */ 121 this.container = null; 122 123 /** 124 * This is used to easily determine which renderer we are using 125 * @example if (board.renderer.type === 'vml') { 126 * // do something 127 * } 128 * @type String 129 */ 130 this.type = ''; 131 }; 132 133 JXG.extend(JXG.AbstractRenderer.prototype, /** @lends JXG.AbstractRenderer.prototype */ { 134 135 /* ******************************** * 136 * private methods * 137 * should not be called from * 138 * outside AbstractRenderer * 139 * ******************************** */ 140 141 /** 142 * Update visual properties, but only if {@link JXG.AbstractRenderer#enhancedRendering} or <tt>enhanced</tt> is set to true. 143 * @param {JXG.GeometryElement} element The element to update 144 * @param {Object} [not={}] Select properties you don't want to be updated: <tt>{fill: true, dash: true}</tt> updates 145 * everything except for fill and dash. Possible values are <tt>stroke, fill, dash, shadow, gradient</tt>. 146 * @param {Boolean} [enhanced=false] If true, {@link JXG.AbstractRenderer#enhancedRendering} is assumed to be true. 147 * @private 148 */ 149 _updateVisual: function (element, not, enhanced) { 150 var rgbo; 151 152 if (enhanced || this.enhancedRendering) { 153 not = not || {}; 154 155 if (!element.visProp.draft) { 156 /* 157 if (JXG.isFunction(element.visProp.visible)) { 158 if (element.visProp.visible()) { 159 this.hide(element); 160 } else { 161 this.show(element); 162 } 163 } 164 */ 165 166 if (!not.stroke) { 167 this.setObjectStrokeWidth(element, element.visProp.strokewidth); 168 this.setObjectStrokeColor(element, element.visProp.strokecolor, element.visProp.strokeopacity); 169 } 170 171 if (!not.fill) { 172 this.setObjectFillColor(element, element.visProp.fillcolor, element.visProp.fillopacity); 173 } 174 175 if (!not.dash) { 176 this.setDashStyle(element, element.visProp); 177 } 178 179 if (!not.shadow) { 180 this.setShadow(element); 181 } 182 183 if (!not.gradient) { 184 this.setShadow(element); 185 } 186 } else { 187 this.setDraft(element); 188 } 189 } 190 }, 191 192 193 /* ******************************** * 194 * Point drawing and updating * 195 * ******************************** */ 196 197 /** 198 * Draws a point on the {@link JXG.Board}. 199 * @param {JXG.Point} element Reference to a {@link JXG.Point} object that has to be drawn. 200 * @see Point 201 * @see JXG.Point 202 * @see JXG.AbstractRenderer#updatePoint 203 * @see JXG.AbstractRenderer#changePointStyle 204 */ 205 drawPoint: function (element) { 206 var prim, 207 face = JXG.Point.prototype.normalizeFace.call(this, element.visProp.face); 208 209 // determine how the point looks like 210 if (face === 'o') { 211 prim = 'ellipse'; 212 } else if (face === '[]') { 213 prim = 'rect'; 214 } else { 215 // cross/x, diamond/<>, triangleup/a/^, triangledown/v, triangleleft/<, 216 // triangleright/>, plus/+, 217 prim = 'path'; 218 } 219 220 this.appendChildPrim(this.createPrim(prim, element.id), element.visProp.layer); 221 this.appendNodesToElement(element, prim); 222 223 // adjust visual propertys 224 this._updateVisual(element, {dash: true, shadow: true}, true); 225 226 // By now we only created the xml nodes and set some styles, in updatePoint 227 // the attributes are filled with data. 228 this.updatePoint(element); 229 }, 230 231 /** 232 * Updates visual appearance of the renderer element assigned to the given {@link JXG.Point}. 233 * @param {JXG.Point} element Reference to a {@link JXG.Point} object, that has to be updated. 234 * @see Point 235 * @see JXG.Point 236 * @see JXG.AbstractRenderer#drawPoint 237 * @see JXG.AbstractRenderer#changePointStyle 238 */ 239 updatePoint: function (element) { 240 var size = element.visProp.size, 241 face = JXG.Point.prototype.normalizeFace.call(this, element.visProp.face); 242 243 if (!isNaN(element.coords.scrCoords[2] + element.coords.scrCoords[1])) { 244 this._updateVisual(element, {dash: false, shadow: false}); 245 246 // Zoom does not work for traces. 247 size *= ((!element.board || !element.board.options.point.zoom) ? 1.0 : Math.sqrt(element.board.zoomX * element.board.zoomY)); 248 249 if (face === 'o') { // circle 250 this.updateEllipsePrim(element.rendNode, element.coords.scrCoords[1], element.coords.scrCoords[2], size + 1, size + 1); 251 } else if (face === '[]') { // rectangle 252 this.updateRectPrim(element.rendNode, element.coords.scrCoords[1] - size, element.coords.scrCoords[2] - size, size * 2, size * 2); 253 } else { // x, +, <>, ^, v, <, > 254 this.updatePathPrim(element.rendNode, this.updatePathStringPoint(element, size, face), element.board); 255 } 256 this.setShadow(element); 257 } 258 }, 259 260 /** 261 * Changes the style of a {@link JXG.Point}. This is required because the point styles differ in what 262 * elements have to be drawn, e.g. if the point is marked by a "x" or a "+" two lines are drawn, if 263 * it's marked by spot a circle is drawn. This method removes the old renderer element(s) and creates 264 * the new one(s). 265 * @param {JXG.Point} element Reference to a {@link JXG.Point} object, that's style is changed. 266 * @see Point 267 * @see JXG.Point 268 * @see JXG.AbstractRenderer#updatePoint 269 * @see JXG.AbstractRenderer#drawPoint 270 */ 271 changePointStyle: function (element) { 272 var node = this.getElementById(element.id); 273 274 // remove the existing point rendering node 275 if (JXG.exists(node)) { 276 this.remove(node); 277 } 278 279 // and make a new one 280 this.drawPoint(element); 281 JXG.clearVisPropOld(element); 282 283 if (!element.visProp.visible) { 284 this.hide(element); 285 } 286 287 if (element.visProp.draft) { 288 this.setDraft(element); 289 } 290 }, 291 292 /* ******************************** * 293 * Lines * 294 * ******************************** */ 295 296 /** 297 * Draws a line on the {@link JXG.Board}. 298 * @param {JXG.Line} element Reference to a line object, that has to be drawn. 299 * @see Line 300 * @see JXG.Line 301 * @see JXG.AbstractRenderer#updateLine 302 */ 303 drawLine: function (element) { 304 this.appendChildPrim(this.createPrim('line', element.id), element.visProp.layer); 305 this.appendNodesToElement(element, 'lines'); 306 this.updateLine(element); 307 }, 308 309 /** 310 * Updates visual appearance of the renderer element assigned to the given {@link JXG.Line}. 311 * @param {JXG.Line} element Reference to the {@link JXG.Line} object that has to be updated. 312 * @see Line 313 * @see JXG.Line 314 * @see JXG.AbstractRenderer#drawLine 315 */ 316 updateLine: function (element) { 317 var c1 = new JXG.Coords(JXG.COORDS_BY_USER, element.point1.coords.usrCoords, element.board), 318 c2 = new JXG.Coords(JXG.COORDS_BY_USER, element.point2.coords.usrCoords, element.board), 319 margin = null; 320 321 if (element.visProp.firstarrow || element.visProp.lastarrow) { 322 margin = 0; 323 } 324 JXG.Math.Geometry.calcStraight(element, c1, c2, margin); 325 this.updateLinePrim(element.rendNode, 326 c1.scrCoords[1], c1.scrCoords[2], 327 c2.scrCoords[1], c2.scrCoords[2], element.board); 328 329 this.makeArrows(element); 330 this._updateVisual(element, {fill: true}); 331 }, 332 333 /** 334 * Creates a rendering node for ticks added to a line. 335 * @param {JXG.Line} element A arbitrary line. 336 * @see Line 337 * @see Ticks 338 * @see JXG.Line 339 * @see JXG.Ticks 340 * @see JXG.AbstractRenderer#updateTicks 341 */ 342 drawTicks: function (element) { 343 var node = this.createPrim('path', element.id); 344 345 this.appendChildPrim(node, element.visProp.layer); 346 this.appendNodesToElement(element, 'path'); 347 }, 348 349 /** 350 * Update {@link Ticks} on a {@link JXG.Line}. This method is only a stub and has to be implemented 351 * in any descendant renderer class. 352 * @param {JXG.Line} element Reference of an line object, thats ticks have to be updated. 353 * @param {Number} dxMaj Number of pixels a major tick counts in x direction. 354 * @param {Number} dyMaj Number of pixels a major tick counts in y direction. 355 * @param {Number} dxMin Number of pixels a minor tick counts in x direction. 356 * @param {Number} dyMin Number of pixels a minor tick counts in y direction. 357 * @see Line 358 * @see Ticks 359 * @see JXG.Line 360 * @see JXG.Ticks 361 * @see JXG.AbstractRenderer#drawTicks 362 */ 363 updateTicks: function (element, dxMaj, dyMaj, dxMin, dyMin) { /* stub */ }, 364 365 /* ************************** 366 * Curves 367 * **************************/ 368 369 /** 370 * Draws a {@link JXG.Curve} on the {@link JXG.Board}. 371 * @param {JXG.Curve} element Reference to a graph object, that has to be plotted. 372 * @see Curve 373 * @see JXG.Curve 374 * @see JXG.AbstractRenderer#updateCurve 375 */ 376 drawCurve: function (element) { 377 this.appendChildPrim(this.createPrim('path', element.id), element.visProp.layer); 378 this.appendNodesToElement(element, 'path'); 379 this._updateVisual(element, {shadow: true}, true); 380 this.updateCurve(element); 381 }, 382 383 /** 384 * Updates visual appearance of the renderer element assigned to the given {@link JXG.Curve}. 385 * @param {JXG.Curve} element Reference to a {@link JXG.Curve} object, that has to be updated. 386 * @see Curve 387 * @see JXG.Curve 388 * @see JXG.AbstractRenderer#drawCurve 389 */ 390 updateCurve: function (element) { 391 this._updateVisual(element); 392 if (element.visProp.handdrawing) { 393 this.updatePathPrim(element.rendNode, this.updatePathStringBezierPrim(element), element.board); 394 } else { 395 this.updatePathPrim(element.rendNode, this.updatePathStringPrim(element), element.board); 396 } 397 if (element.numberPoints>1) { 398 this.makeArrows(element); 399 } 400 }, 401 402 /* ************************** 403 * Circle related stuff 404 * **************************/ 405 406 /** 407 * Draws a {@link JXG.Circle} 408 * @param {JXG.Circle} element Reference to a {@link JXG.Circle} object that has to be drawn. 409 * @see Circle 410 * @see JXG.Circle 411 * @see JXG.AbstractRenderer#updateEllipse 412 */ 413 drawEllipse: function (element) { 414 this.appendChildPrim(this.createPrim('ellipse', element.id), element.visProp.layer); 415 this.appendNodesToElement(element, 'ellipse'); 416 this.updateEllipse(element); 417 }, 418 419 /** 420 * Updates visual appearance of a given {@link JXG.Circle} on the {@link JXG.Board}. 421 * @param {JXG.Circle} element Reference to a {@link JXG.Circle} object, that has to be updated. 422 * @see Circle 423 * @see JXG.Circle 424 * @see JXG.AbstractRenderer#drawEllipse 425 */ 426 updateEllipse: function (element) { 427 this._updateVisual(element); 428 429 // Radius umrechnen: 430 var radius = element.Radius(); 431 if (radius > 0.0 432 && Math.abs(element.center.coords.usrCoords[0])>JXG.Math.eps 433 && !isNaN(radius + element.center.coords.scrCoords[1] + element.center.coords.scrCoords[2]) 434 && radius * element.board.unitX < 2000000) { 435 this.updateEllipsePrim(element.rendNode, element.center.coords.scrCoords[1], element.center.coords.scrCoords[2], 436 (radius * element.board.unitX), (radius * element.board.unitY)); 437 } 438 }, 439 440 441 /* ************************** 442 * Polygon related stuff 443 * **************************/ 444 445 /** 446 * Draws a {@link JXG.Polygon} on the {@link JXG.Board}. 447 * @param {JXG.Polygon} element Reference to a Polygon object, that is to be drawn. 448 * @see Polygon 449 * @see JXG.Polygon 450 * @see JXG.AbstractRenderer#updatePolygon 451 */ 452 drawPolygon: function (element) { 453 this.appendChildPrim(this.createPrim('polygon', element.id), element.visProp.layer); 454 this.appendNodesToElement(element, 'polygon'); 455 this.updatePolygon(element); 456 }, 457 458 /** 459 * Updates properties of a {@link JXG.Polygon}'s rendering node. 460 * @param {JXG.Polygon} element Reference to a {@link JXG.Polygon} object, that has to be updated. 461 * @see Polygon 462 * @see JXG.Polygon 463 * @see JXG.AbstractRenderer#drawPolygon 464 */ 465 updatePolygon: function (element) { 466 // here originally strokecolor wasn't updated but strokewidth was 467 // but if there's no strokecolor i don't see why we should update strokewidth. 468 this._updateVisual(element, {stroke: true, dash: true}); 469 this.updatePolygonPrim(element.rendNode, element); 470 }, 471 472 /* ************************** 473 * Text related stuff 474 * **************************/ 475 476 /** 477 * Shows a small copyright notice in the top left corner of the board. 478 * @param {String} str The copyright notice itself 479 * @param {Number} fontsize Size of the font the copyright notice is written in 480 */ 481 displayCopyright: function (str, fontsize) { /* stub */ }, 482 483 /** 484 * An internal text is a {@link JXG.Text} element which is drawn using only 485 * the given renderer but no HTML. This method is only a stub, the drawing 486 * is done in the special renderers. 487 * @param {JXG.Text} element Reference to a {@link JXG.Text} object 488 * @see Text 489 * @see JXG.Text 490 * @see JXG.AbstractRenderer#updateInternalText 491 * @see JXG.AbstractRenderer#drawText 492 * @see JXG.AbstractRenderer#updateText 493 * @see JXG.AbstractRenderer#updateTextStyle 494 */ 495 drawInternalText: function (element) { /* stub */ }, 496 497 /** 498 * Updates visual properties of an already existing {@link JXG.Text} element. 499 * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be updated. 500 * @see Text 501 * @see JXG.Text 502 * @see JXG.AbstractRenderer#drawInternalText 503 * @see JXG.AbstractRenderer#drawText 504 * @see JXG.AbstractRenderer#updateText 505 * @see JXG.AbstractRenderer#updateTextStyle 506 */ 507 updateInternalText: function (element) { /* stub */ }, 508 509 /** 510 * Displays a {@link JXG.Text} on the {@link JXG.Board} by putting a HTML div over it. 511 * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be displayed 512 * @see Text 513 * @see JXG.Text 514 * @see JXG.AbstractRenderer#drawInternalText 515 * @see JXG.AbstractRenderer#updateText 516 * @see JXG.AbstractRenderer#updateInternalText 517 * @see JXG.AbstractRenderer#updateTextStyle 518 */ 519 drawText: function (element) { 520 var node, z; 521 522 if (element.visProp.display === 'html') { 523 node = this.container.ownerDocument.createElement('div'); 524 node.style.position = 'absolute'; 525 526 node.className = element.visProp.cssclass; 527 if (this.container.style.zIndex=='') { 528 z = 0; 529 } else { 530 z = parseInt(this.container.style.zIndex); 531 } 532 533 node.style.zIndex = z+element.board.options.layer.text; //'10'; 534 this.container.appendChild(node); 535 node.setAttribute('id', this.container.id + '_' + element.id); 536 } else { 537 node = this.drawInternalText(element); 538 } 539 540 element.rendNode = node; 541 element.htmlStr = ''; 542 this.updateText(element); 543 }, 544 545 /** 546 * Updates visual properties of an already existing {@link JXG.Text} element. 547 * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be updated. 548 * @see Text 549 * @see JXG.Text 550 * @see JXG.AbstractRenderer#drawText 551 * @see JXG.AbstractRenderer#drawInternalText 552 * @see JXG.AbstractRenderer#updateInternalText 553 * @see JXG.AbstractRenderer#updateTextStyle 554 */ 555 updateText: function (el) { 556 var content = el.plaintext; 557 558 if (el.visProp.visible) { 559 this.updateTextStyle(el, false); 560 561 if (el.visProp.display === 'html') { 562 // Set the position 563 if (!isNaN(el.coords.scrCoords[1] + el.coords.scrCoords[2])) { 564 if (el.visProp.anchorx === 'right') { 565 el.rendNode.style.right = parseInt(el.board.canvasWidth - el.coords.scrCoords[1]) + 'px'; 566 } else if (el.visProp.anchorx === 'middle') { 567 el.rendNode.style.left = parseInt(el.coords.scrCoords[1]-0.5*el.size[0]) + 'px'; 568 } else { // 'left' 569 el.rendNode.style.left = parseInt(el.coords.scrCoords[1]) + 'px'; 570 } 571 572 if (el.visProp.anchory === 'top') { 573 el.rendNode.style.top = parseInt(el.coords.scrCoords[2] + this.vOffsetText) + 'px'; 574 } else if (el.visProp.anchory === 'middle') { 575 el.rendNode.style.top = parseInt(el.coords.scrCoords[2] - 0.5*el.size[1] + this.vOffsetText) + 'px'; 576 } else { 577 el.rendNode.style.top = parseInt(el.coords.scrCoords[2] - el.size[1] + this.vOffsetText) + 'px'; 578 } 579 } 580 581 // Set the content 582 if (el.htmlStr !== content) { 583 el.rendNode.innerHTML = content; 584 el.htmlStr = content; 585 586 if (el.visProp.usemathjax) { 587 // typesetting directly might not work because mathjax was not loaded completely 588 // see http://www.mathjax.org/docs/1.1/typeset.html 589 MathJax.Hub.Queue(['Typeset', MathJax.Hub, el.rendNode]); 590 //MathJax.Hub.Typeset(el.rendNode); 591 } else if (el.visProp.useasciimathml) { 592 AMprocessNode(el.rendNode, false); 593 } 594 } 595 this.transformImage(el, el.transformations); 596 } else { 597 this.updateInternalText(el); 598 } 599 } 600 }, 601 602 /** 603 * Updates font-size, color and opacity propertiey and CSS style properties of a {@link JXG.Text} node. 604 * This function is also called by highlight() and nohighlight(). 605 * @param {JXG.Text} element Reference to the {@link JXG.Text} object, that has to be updated. 606 * @see Text 607 * @see JXG.Text 608 * @see JXG.AbstractRenderer#drawText 609 * @see JXG.AbstractRenderer#drawInternalText 610 * @see JXG.AbstractRenderer#updateText 611 * @see JXG.AbstractRenderer#updateInternalText 612 * @see JXG.AbstractRenderer#updateInternalTextStyle 613 */ 614 updateTextStyle: function (element, doHighlight) { 615 var fs, so, sc, css, 616 ev = element.visProp; 617 618 if (doHighlight) { 619 sc = ev.highlightstrokecolor; 620 so = ev.highlightstrokeopacity; 621 css = ev.highlightcssclass; 622 } else { 623 sc = ev.strokecolor; 624 so = ev.strokeopacity; 625 css = ev.cssclass; 626 } 627 628 /* 629 * This part is executed for all text elements except internal texts in canvas. 630 */ 631 if (element.visProp.display === 'html' || this.type != 'canvas') { 632 fs = JXG.evaluate(element.visProp.fontsize); 633 if (element.visPropOld.fontsize != fs) { 634 try { 635 element.rendNode.style.fontSize = fs + 'px'; 636 } catch (e) { 637 // IE needs special treatment. 638 element.rendNode.style.fontSize = fs; 639 } 640 element.visPropOld.fontsize = fs; 641 } 642 643 if (element.visPropOld.cssclass != css) { 644 element.rendNode.className = css; 645 element.visPropOld.cssclass = css; 646 } 647 } 648 if (element.visProp.display === 'html') { 649 this.setObjectStrokeColor(element, sc, so); 650 } else { 651 this.updateInternalTextStyle(element, sc, so); 652 } 653 return this; 654 }, 655 656 /** 657 * Set color and opacity of internal texts. 658 * This method is used for Canvas and VML. 659 * SVG needs its own version. 660 * @private 661 * @see JXG.AbstractRenderer#updateTextStyle 662 * @see JXG.SVGRenderer#updateInternalTextStyle 663 */ 664 updateInternalTextStyle: function(element, strokeColor, strokeOpacity) { 665 this.setObjectStrokeColor(element, strokeColor, strokeOpacity); 666 }, 667 668 /* ************************** 669 * Image related stuff 670 * **************************/ 671 672 /** 673 * Draws an {@link JXG.Image} on a board; This is just a template that has to be implemented by special renderers. 674 * @param {JXG.Image} element Reference to the image object that is to be drawn 675 * @see Image 676 * @see JXG.Image 677 * @see JXG.AbstractRenderer#updateImage 678 */ 679 drawImage: function (element) { /* stub */ }, 680 681 /** 682 * Updates the properties of an {@link JXG.Image} element. 683 * @param {JXG.Image} element Reference to an {@link JXG.Image} object, that has to be updated. 684 * @see Image 685 * @see JXG.Image 686 * @see JXG.AbstractRenderer#drawImage 687 */ 688 updateImage: function (element) { 689 this.updateRectPrim(element.rendNode, element.coords.scrCoords[1], element.coords.scrCoords[2] - element.size[1], 690 element.size[0], element.size[1]); 691 692 this.updateImageURL(element); 693 this.transformImage(element, element.transformations); 694 this._updateVisual(element, {stroke: true, dash: true}, true); 695 }, 696 697 /** 698 * Multiplication of transformations without updating. That means, at that point it is expected that the matrices 699 * contain numbers only. First, the origin in user coords is translated to <tt>(0,0)</tt> in screen coords. 700 * Then, the stretch factors are divided out. After the transformations in user coords, the stretch factors 701 * are multiplied in again, and the origin in user coords is translated back to its position. 702 * This method does not have to be implemented in a new renderer. 703 * @param {JXG.GeometryElement} element A JSXGraph element. We only need its board property. 704 * @param {Array} transformations An array of JXG.Transformations. 705 * @returns {Array} A matrix represented by a two dimensional array of numbers. 706 * @see JXG.AbstractRenderer#transformImage 707 */ 708 joinTransforms: function (element, transformations) { 709 var m = [[1, 0, 0], [0, 1, 0], [0, 0, 1]], 710 ox = element.board.origin.scrCoords[1], 711 oy = element.board.origin.scrCoords[2], 712 ux = element.board.unitX, 713 uy = element.board.unitY, 714 mpre1 = [[1, 0, 0], // Translate to 0,0 in screen coords 715 [-ox, 1, 0], 716 [-oy, 0, 1]], 717 mpre2 = [[1, 0, 0], // Scale 718 [0, 1/ux, 0], 719 [0, 0, -1/uy]], 720 mpost2 = [[1, 0, 0], // Scale back 721 [0, ux, 0], 722 [0, 0, -uy]], 723 mpost1 = [[1, 0, 0], // Translate back 724 [ox, 1, 0], 725 [oy, 0, 1]], 726 i, len = transformations.length; 727 728 for (i = 0; i < len; i++) { 729 m = JXG.Math.matMatMult(mpre1, m); 730 m = JXG.Math.matMatMult(mpre2, m); 731 m = JXG.Math.matMatMult(transformations[i].matrix, m); 732 m = JXG.Math.matMatMult(mpost2, m); 733 m = JXG.Math.matMatMult(mpost1, m); 734 } 735 return m; 736 }, 737 738 /** 739 * Applies transformations on images and text elements. This method is just a stub and has to be implemented in all 740 * descendant classes where text and image transformations are to be supported. 741 * @param {JXG.Image|JXG.Text} element A {@link JXG.Image} or {@link JXG.Text} object. 742 * @param {Array} transformations An array of {@link JXG.Transformation} objects. This is usually the transformations property 743 * of the given element <tt>el</tt>. 744 */ 745 transformImage: function (element, transformations) { /* stub */ }, 746 747 /** 748 * If the URL of the image is provided by a function the URL has to be updated during updateImage() 749 * @param {JXG.Image} element Reference to an image object. 750 * @see JXG.AbstractRenderer#updateImage 751 */ 752 updateImageURL: function (element) { /* stub */ }, 753 754 /** 755 * Updates CSS style properties of a {@link JXG.Image} node. 756 * In SVGRenderer opacity is the only available style element. 757 * This function is called by highlight() and nohighlight(). 758 * This function works for VML. 759 * It does not work for Canvas. 760 * SVGRenderer overwrites this method. 761 * @param {JXG.Text} element Reference to the {@link JXG.Image} object, that has to be updated. 762 * @see Image 763 * @see JXG.Image 764 * @see JXG.AbstractRenderer#highlight 765 * @see JXG.AbstractRenderer#nohighlight 766 */ 767 updateImageStyle: function(element, doHighlight) { 768 var css = (doHighlight) ? el.visProp.highlightcssclass : el.visProp.cssclass; 769 770 element.rendNode.className = css; 771 }, 772 773 774 /* ************************** 775 * Render primitive objects 776 * **************************/ 777 778 /** 779 * Appends a node to a specific layer level. This is just an abstract method and has to be implemented 780 * in all renderers that want to use the <tt>createPrim</tt> model to draw. 781 * @param {Node} node A DOM tree node. 782 * @param {Number} level The layer the node is attached to. This is the index of the layer in 783 * {@link JXG.SVGRenderer#layer} or the <tt>z-index</tt> style property of the node in VMLRenderer. 784 */ 785 appendChildPrim: function (node, level) { /* stub */ }, 786 787 /** 788 * Stores the rendering nodes. This is an abstract method which has to be implemented in all renderers that use 789 * the <tt>createPrim</tt> method. 790 * @param {JXG.GeometryElement} element A JSXGraph element. 791 * @param {String} type The XML node name. Only used in VMLRenderer. 792 */ 793 appendNodesToElement: function (element, type) { /* stub */ }, 794 795 /** 796 * Creates a node of a given type with a given id. 797 * @param {String} type The type of the node to create. 798 * @param {String} id Set the id attribute to this. 799 * @returns {Node} Reference to the created node. 800 */ 801 createPrim: function (type, id) { 802 /* stub */ 803 return null; 804 }, 805 806 /** 807 * Removes an element node. Just a stub. 808 * @param {Node} node The node to remove. 809 */ 810 remove: function (node) { /* stub */ }, 811 812 /** 813 * Can be used to create the nodes to display arrows. This is an abstract method which has to be implemented 814 * in any descendant renderer. 815 * @param {JXG.GeometryElement} element The element the arrows are to be attached to. 816 */ 817 makeArrows: function(element) { /* stub */ }, 818 819 /** 820 * Updates an ellipse node primitive. This is an abstract method which has to be implemented in all renderers 821 * that use the <tt>createPrim</tt> method. 822 * @param {Node} node Reference to the node. 823 * @param {Number} x Centre X coordinate 824 * @param {Number} y Centre Y coordinate 825 * @param {Number} rx The x-axis radius. 826 * @param {Number} ry The y-axis radius. 827 */ 828 updateEllipsePrim: function(node, x, y, rx, ry) { /* stub */ }, 829 830 /** 831 * Refreshes a line node. This is an abstract method which has to be implemented in all renderers that use 832 * the <tt>createPrim</tt> method. 833 * @param {Node} node The node to be refreshed. 834 * @param {Number} p1x The first point's x coordinate. 835 * @param {Number} p1y The first point's y coordinate. 836 * @param {Number} p2x The second point's x coordinate. 837 * @param {Number} p2y The second point's y coordinate. 838 * @param {JXG.Board} board 839 */ 840 updateLinePrim: function(node, p1x, p1y, p2x, p2y, board) { /* stub */ }, 841 842 /** 843 * Updates a path element. This is an abstract method which has to be implemented in all renderers that use 844 * the <tt>createPrim</tt> method. 845 * @param {Node} node The path node. 846 * @param {String} pathString A string formatted like e.g. <em>'M 1,2 L 3,1 L5,5'</em>. The format of the string 847 * depends on the rendering engine. 848 * @param {JXG.Board} board Reference to the element's board. 849 */ 850 updatePathPrim: function (node, pathString, board) { /* stub */ }, 851 852 /** 853 * Builds a path data string to draw a point with a face other than <em>rect</em> and <em>circle</em>. Since 854 * the format of such a string usually depends on the renderer this method 855 * is only an abstract method. Therefore, it has to be implemented in the descendant renderer itself unless 856 * the renderer does not use the createPrim interface but the draw* interfaces to paint. 857 * @param {JXG.Point} element The point element 858 * @param {Number} size A positive number describing the size. Usually the half of the width and height of 859 * the drawn point. 860 * @param {String} type A string describing the point's face. This method only accepts the shortcut version of 861 * each possible face: <tt>x, +, <>, ^, v, >, < 862 */ 863 updatePathStringPoint: function (element, size, type) { /* stub */ }, 864 865 /** 866 * Builds a path data string from a {@link JXG.Curve} element. Since the path data strings heavily depend on the 867 * underlying rendering technique this method is just a stub. Although such a path string is of no use for the 868 * CanvasRenderer, this method is used there to draw a path directly. 869 * @param element 870 */ 871 updatePathStringPrim: function (element) { /* stub */ }, 872 873 /** 874 * Builds a path data string from a {@link JXG.Curve} element such that the curve looks like 875 * hand drawn. 876 * Since the path data strings heavily depend on the 877 * underlying rendering technique this method is just a stub. Although such a path string is of no use for the 878 * CanvasRenderer, this method is used there to draw a path directly. 879 * @param element 880 */ 881 updatePathStringBezierPrim: function (element) { /* stub */ }, 882 883 884 /** 885 * Update a polygon primitive. 886 * @param {Node} node 887 * @param {JXG.Polygon} element A JSXGraph element of type {@link JXG.Polygon} 888 */ 889 updatePolygonPrim: function (node, element) { /* stub */ }, 890 891 /** 892 * Update a rectangle primitive. This is used only for points with face of type 'rect'. 893 * @param {Node} node The node yearning to be updated. 894 * @param {Number} x x coordinate of the top left vertex. 895 * @param {Number} y y coordinate of the top left vertex. 896 * @param {Number} w Width of the rectangle. 897 * @param {Number} h The rectangle's height. 898 */ 899 updateRectPrim: function(node, x, y, w, h) { /* stub */ }, 900 901 /* ************************** 902 * Set Attributes 903 * **************************/ 904 905 /** 906 * Sets a node's attribute. 907 * @param {Node} node The node that is to be updated. 908 * @param {String} key Name of the attribute. 909 * @param {String} val New value for the attribute. 910 */ 911 setPropertyPrim: function (node, key, val) { /* stub */ }, 912 913 /** 914 * Shows a hidden element on the canvas; Only a stub, requires implementation in the derived renderer. 915 * @param {JXG.GeometryElement} element Reference to the object that has to appear. 916 * @see JXG.AbstractRenderer#hide 917 */ 918 show: function (element) { /* stub */ }, 919 920 /** 921 * Hides an element on the canvas; Only a stub, requires implementation in the derived renderer. 922 * @param {JXG.GeometryElement} element Reference to the geometry element that has to disappear. 923 * @see JXG.AbstractRenderer#show 924 */ 925 hide: function (element) { /* stub */ }, 926 927 /** 928 * Sets the buffering as recommended by SVGWG. Until now only Opera supports this and will be ignored by 929 * other browsers. Although this feature is only supported by SVG we have this method in {@link JXG.AbstractRenderer} 930 * because it is called from outside the renderer. 931 * @param {Node} node The SVG DOM Node which buffering type to update. 932 * @param {String} type Either 'auto', 'dynamic', or 'static'. For an explanation see 933 * {@link http://www.w3.org/TR/SVGTiny12/painting.html#BufferedRenderingProperty}. 934 */ 935 setBuffering: function (node, type) { /* stub */ }, 936 937 /** 938 * Sets an element's dash style. 939 * @param {JXG.GeometryElement} element An JSXGraph element. 940 */ 941 setDashStyle: function (element) { /* stub */ }, 942 943 /** 944 * Puts an object into draft mode, i.e. it's visual appearance will be changed. For GEONE<sub>x</sub>T backwards compatibility. 945 * @param {JXG.GeometryElement} element Reference of the object that is in draft mode. 946 */ 947 setDraft: function (element) { 948 if (!element.visProp.draft) { 949 return; 950 } 951 var draftColor = element.board.options.elements.draft.color, 952 draftOpacity = element.board.options.elements.draft.opacity; 953 954 if (element.type === JXG.OBJECT_TYPE_POLYGON) { 955 this.setObjectFillColor(element, draftColor, draftOpacity); 956 } 957 else { 958 if (element.elementClass === JXG.OBJECT_CLASS_POINT) { 959 this.setObjectFillColor(element, draftColor, draftOpacity); 960 } 961 else { 962 this.setObjectFillColor(element, 'none', 0); 963 } 964 this.setObjectStrokeColor(element, draftColor, draftOpacity); 965 this.setObjectStrokeWidth(element, element.board.options.elements.draft.strokeWidth); 966 } 967 }, 968 969 /** 970 * Puts an object from draft mode back into normal mode. 971 * @param {JXG.GeometryElement} element Reference of the object that no longer is in draft mode. 972 */ 973 removeDraft: function (element) { 974 if (element.type === JXG.OBJECT_TYPE_POLYGON) { 975 this.setObjectFillColor(element, element.visProp.fillcolor, element.visProp.fillopacity); 976 } 977 else { 978 if (element.type === JXG.OBJECT_CLASS_POINT) { 979 this.setObjectFillColor(element, element.visProp.fillcolor, element.visProp.fillopacity); 980 } 981 this.setObjectStrokeColor(element, element.visProp.strokecolor, element.visProp.strokeopacity); 982 this.setObjectStrokeWidth(element, element.visProp.strokewidth); 983 } 984 }, 985 986 /** 987 * Sets up nodes for rendering a gradient fill. 988 * @param element 989 */ 990 setGradient: function (element) { /* stub */ }, 991 992 /** 993 * Updates the gradient fill. 994 * @param {JXG.GeometryElement} element An JSXGraph element with an area that can be filled. 995 */ 996 updateGradient: function (element) { /* stub */ }, 997 998 /** 999 * Sets an objects fill color. 1000 * @param {JXG.GeometryElement} element Reference of the object that wants a new fill color. 1001 * @param {String} color Color in a HTML/CSS compatible format. If you don't want any fill color at all, choose 'none'. 1002 * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1. 1003 */ 1004 setObjectFillColor: function (element, color, opacity) { /* stub */ }, 1005 1006 /** 1007 * Changes an objects stroke color to the given color. 1008 * @param {JXG.GeometryElement} element Reference of the {@link JXG.GeometryElement} that gets a new stroke color. 1009 * @param {String} color Color value in a HTML compatible format, e.g. <strong>#00ff00</strong> or <strong>green</strong> for green. 1010 * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1. 1011 */ 1012 setObjectStrokeColor: function (element, color, opacity) { /* stub */ }, 1013 1014 /** 1015 * Sets an element's stroke width. 1016 * @param {JXG.GeometryElement} element Reference to the geometry element. 1017 * @param {Number} width The new stroke width to be assigned to the element. 1018 */ 1019 setObjectStrokeWidth: function (element, width) { /* stub */ }, 1020 1021 /** 1022 * Sets the shadow properties to a geometry element. This method is only a stub, it is implemented in the actual renderers. 1023 * @param {JXG.GeometryElement} element Reference to a geometry object, that should get a shadow 1024 */ 1025 setShadow: function (element) { /* stub */ }, 1026 1027 /** 1028 * Highlights an object, i.e. changes the current colors of the object to its highlighting colors 1029 * @param {JXG.GeometryElement} element Reference of the object that will be highlighted. 1030 * @returns {JXG.AbstractRenderer} Reference to the renderer 1031 * @see JXG.AbstractRenderer#updateTextStyle 1032 */ 1033 highlight: function (element) { 1034 var i, ev = element.visProp; 1035 1036 if (!ev.draft) { 1037 /* 1038 // Why did we have this? A.W. 1039 if (element.elementClass === JXG.OBJECT_CLASS_POINT) { 1040 this.setObjectStrokeColor(element, ev.highlightstrokecolor, ev.highlightstrokeopacity); 1041 this.setObjectFillColor(element, ev.highlightstrokecolor, ev.highlightstrokeopacity); 1042 } else 1043 */ 1044 if (element.type === JXG.OBJECT_TYPE_POLYGON) { 1045 this.setObjectFillColor(element, ev.highlightfillcolor, ev.highlightfillopacity); 1046 for (i = 0; i < element.borders.length; i++) { 1047 this.setObjectStrokeColor(element.borders[i], element.borders[i].visProp.highlightstrokecolor, element.borders[i].visProp.highlightstrokeopacity); 1048 } 1049 } else { 1050 if (element.type === JXG.OBJECT_TYPE_TEXT) { 1051 this.updateTextStyle(element, true); 1052 } else if (element.type === JXG.OBJECT_TYPE_IMAGE) { 1053 this.updateImageStyle(element, true); 1054 //element.rendNode.className = 'JXGimageHighlight'; 1055 //element.rendNode.setAttributeNS(null, 'class', 'JXGimageHighlight'); 1056 } else { 1057 this.setObjectStrokeColor(element, ev.highlightstrokecolor, ev.highlightstrokeopacity); 1058 this.setObjectFillColor(element, ev.highlightfillcolor, ev.highlightfillopacity); 1059 } 1060 } 1061 if (ev.highlightstrokewidth) { 1062 this.setObjectStrokeWidth(element, Math.max(ev.highlightstrokewidth, ev.strokewidth)); 1063 } 1064 } 1065 1066 return this; 1067 }, 1068 1069 /** 1070 * Uses the normal colors of an object, i.e. the opposite of {@link JXG.AbstractRenderer#highlight}. 1071 * @param {JXG.GeometryElement} element Reference of the object that will get its normal colors. 1072 * @returns {JXG.AbstractRenderer} Reference to the renderer 1073 * @see JXG.AbstractRenderer#updateTextStyle 1074 */ 1075 noHighlight: function (element) { 1076 var i, ev = element.visProp; 1077 1078 if (!element.visProp.draft) { 1079 /* 1080 // Why did we have this? A.W. 1081 if (element.elementClass === JXG.OBJECT_CLASS_POINT) { 1082 this.setObjectStrokeColor(element, ev.strokecolor, ev.strokeopacity); 1083 this.setObjectFillColor(element, ev.strokecolor, ev.strokeopacity); 1084 } else 1085 */ 1086 if (element.type === JXG.OBJECT_TYPE_POLYGON) { 1087 this.setObjectFillColor(element, ev.fillcolor, ev.fillopacity); 1088 for (i = 0; i < element.borders.length; i++) { 1089 this.setObjectStrokeColor(element.borders[i], element.borders[i].visProp.strokecolor, element.borders[i].visProp.strokeopacity); 1090 } 1091 } else { 1092 if (element.type === JXG.OBJECT_TYPE_TEXT) { 1093 this.updateTextStyle(element, false); 1094 } else if (element.type === JXG.OBJECT_TYPE_IMAGE) { 1095 this.updateImageStyle(element, false); 1096 //element.rendNode.setAttributeNS(null, 'class', 'JXGimage'); 1097 } else { 1098 this.setObjectStrokeColor(element, ev.strokecolor, ev.strokeopacity); 1099 this.setObjectFillColor(element, ev.fillcolor, ev.fillopacity); 1100 } 1101 } 1102 this.setObjectStrokeWidth(element, ev.strokewidth); 1103 } 1104 1105 return this; 1106 }, 1107 1108 /* ************************** 1109 * renderer control 1110 * **************************/ 1111 1112 /** 1113 * Stop redraw. This method is called before every update, so a non-vector-graphics based renderer 1114 * can use this method to delete the contents of the drawing panel. This is an abstract method every 1115 * descendant renderer should implement, if appropriate. 1116 * @see JXG.AbstractRenderer#unsuspendRedraw 1117 */ 1118 suspendRedraw: function () { /* stub */ }, 1119 1120 /** 1121 * Restart redraw. This method is called after updating all the rendering node attributes. 1122 * @see JXG.AbstractRenderer#suspendRedraw 1123 */ 1124 unsuspendRedraw: function () { /* stub */ }, 1125 1126 /** 1127 * The tiny zoom bar shown on the bottom of a board (if showNavigation on board creation is true). 1128 * @param {JXG.Board} board Reference to a JSXGraph board. 1129 */ 1130 drawZoomBar: function (board) { 1131 var doc, 1132 node, 1133 createButton = function (label, handler) { 1134 var button; 1135 1136 button = doc.createElement('span'); 1137 node.appendChild(button); 1138 button.appendChild(document.createTextNode(label)); 1139 /* button.innerHTML = label; */ // Does not work in XHTML 1140 JXG.addEvent(button, 'click', handler, board); 1141 }; 1142 1143 doc = board.containerObj.ownerDocument; 1144 node = doc.createElement('div'); 1145 1146 node.setAttribute('id', board.containerObj.id + '_navigationbar'); 1147 1148 node.style.color = board.options.navbar.strokeColor; 1149 node.style.backgroundColor = board.options.navbar.fillColor; 1150 node.style.padding = board.options.navbar.padding; 1151 node.style.position = board.options.navbar.position; 1152 node.style.fontSize = board.options.navbar.fontSize; 1153 node.style.cursor = board.options.navbar.cursor; 1154 node.style.zIndex = board.options.navbar.zIndex; 1155 board.containerObj.appendChild(node); 1156 node.style.right = board.options.navbar.right; 1157 node.style.bottom = board.options.navbar.bottom; 1158 1159 // For XHTML we need unicode instead of HTML entities 1160 createButton('\u00A0\u2013\u00A0', board.zoomOut); 1161 createButton('\u00A0o\u00A0', board.zoom100); 1162 createButton('\u00A0+\u00A0', board.zoomIn); 1163 createButton('\u00A0\u2190\u00A0', board.clickLeftArrow); 1164 createButton('\u00A0\u2193\u00A0', board.clickUpArrow); 1165 createButton('\u00A0\u2191\u00A0', board.clickDownArrow); 1166 createButton('\u00A0\u2192\u00A0', board.clickRightArrow); 1167 }, 1168 1169 /** 1170 * Wrapper for getElementById for maybe other renderers which elements are not directly accessible by DOM methods like document.getElementById(). 1171 * @param {String} id Unique identifier for element. 1172 * @returns {Object} Reference to a JavaScript object. In case of SVG/VMLRenderer it's a reference to a SVG/VML node. 1173 */ 1174 getElementById: function (id) { 1175 return document.getElementById(this.container.id + '_' + id); 1176 }, 1177 1178 /** 1179 * Resizes the rendering element 1180 * @param {Number} w New width 1181 * @param {Number} h New height 1182 */ 1183 resize: function (w, h) { /* stub */} 1184 1185 }); 1186