1 /* 2 Copyright 2008,2009 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 /** 28 * @fileoverview JSXGraph can use various technologies to render the contents of a construction, e.g. 29 * SVG, VML, and HTML5 Canvas. To accomplish this, The rendering and the logic and control mechanisms 30 * are completely separated from each other. Every rendering technology has it's own class, called 31 * Renderer, e.g. SVGRenderer for SVG, the same for VML and Canvas. The common base for all available 32 * renderers is the class AbstractRenderer defined in this file. 33 */ 34 35 /** 36 * This function returns the AbstractRenderer object which defines the interface between the renderer 37 * objects and the logical parts of JSXGraph. 38 * @returns {Object} An instance of the AbstractRenderer class. 39 * @see JXG.SVGRenderer 40 * @see JXG.VMLRenderer 41 * @see JXG.CanvasRenderer 42 */ 43 JXG.AbstractRenderer = function() { 44 45 return { 46 /** 47 * The vertical offset for {@link Text} elements. Every {@link Text} element will 48 * be placed this amount of pixels below the user given coordinates. 49 * @type number 50 * @default 8 51 */ 52 vOffsetText: 8, 53 54 /** 55 * If this property is set to <tt>true</tt> the visual properties of the elements are updated 56 * on every update. Visual properties means: All the stuff stored in the 57 * {@link JXG.GeometryElement#visProp} property won't be set if enhancedRendering is <tt>false</tt> 58 * @type Boolean 59 * @default true 60 */ 61 enhancedRendering: true, 62 63 /** 64 * Update visual properties, but only if JXG.AbstractRenderer#enhancedRendering is set to true. 65 * @param {JXG.GeometryElement} el The element to update 66 * @param {Object} not Select properties you don't want to be updated: {fill: true, dash: true} updates 67 * everything except for fill and dash. Possible values are stroke, fill, dash, shadow. 68 * @param {Boolean} enhanced If true JXG.AbstractRenderer#enhancedRendering is assumed to be true. 69 */ 70 updateVisual: function(el, not, enhanced) { 71 not = not || {}; 72 73 if (enhanced || this.enhancedRendering) { 74 if (!el.visProp['draft']) { 75 if(!not.stroke) { 76 this.setObjectStrokeWidth(el, el.visProp['strokeWidth']); 77 this.setObjectStrokeColor(el, el.visProp['strokeColor'], el.visProp['strokeOpacity']); 78 } 79 80 if(!not.fill) 81 this.setObjectFillColor(el, el.visProp['fillColor'], el.visProp['fillOpacity']); 82 83 if(!not.dash) 84 this.setDashStyle(el, el.visProp); 85 86 if(!not.shadow) 87 this.setShadow(el); 88 } else { 89 this.setDraft(el); 90 } 91 } 92 }, 93 94 95 /* ******************************** * 96 * Point drawing and updating * 97 * ******************************** */ 98 99 /** 100 * Draws a point on the {@link JXG.Board}. 101 * @param {JXG.Point} el Reference to a {@link JXG.Point} object that has to be drawn. 102 * @see JXG.Point 103 * @see #updatePoint 104 * @see #changePointStyle 105 */ 106 drawPoint: function(el) { 107 var prim, 108 face = JXG.Point.prototype.normalizeFace.call(this, el.visProp['face']);//el.normalizeFace(el.visProp['face']); 109 110 // determine how the point looks like 111 if (face === 'o') { 112 prim = 'circle'; 113 } else if (face === '[]') { 114 prim = 'rect'; 115 } else { 116 // cross/x, diamond/<>, triangleup/a/^, triangledown/v, triangleleft/<, 117 // triangleright/>, plus/+, 118 prim = 'path'; 119 } 120 121 this.appendChildPrim(this.createPrim(prim, el.id), el.layer); 122 this.appendNodesToElement(el, prim); 123 124 // adjust visual propertys 125 this.updateVisual(el, {dash: true, shadow: true}, true); 126 127 // By now we only created the xml nodes and set some styles, in updatePoint 128 // the attributes are filled with data. 129 this.updatePoint(el); 130 }, 131 132 // continue revision here 133 134 /** 135 * Updates visual appearance of the renderer element assigned to the given {@link JXG.Point}. 136 * @param {JXG.Point} el Reference to a {@link JXG.Point} object, that has to be updated. 137 * @see JXG.Point 138 * @see #drawPoint 139 * @see #changePointStyle 140 */ 141 updatePoint: function(el) { 142 var size = el.visProp['size'], 143 face = JXG.Point.prototype.normalizeFace.call(this, el.visProp['face']);//el.normalizeFace(el.visProp['face']); 144 145 if (isNaN(el.coords.scrCoords[2]) || isNaN(el.coords.scrCoords[1])) return; 146 147 this.updateVisual(el, {dash: false, shadow: false}); 148 149 // Zoom does not work for traces. 150 size *= ((!el.board || !el.board.options.point.zoom) ? 1.0 : Math.sqrt(el.board.zoomX * el.board.zoomY)); 151 152 if (face === 'o') { // circle 153 this.updateCirclePrim(el.rendNode, el.coords.scrCoords[1], el.coords.scrCoords[2], size + 1); 154 } else if (face === '[]') { // rectangle 155 this.updateRectPrim(el.rendNode, el.coords.scrCoords[1] - size, el.coords.scrCoords[2] - size, size * 2, size * 2); 156 } else { // x, +, <>, ^, v, <, > 157 this.updatePathPrim(el.rendNode, this.updatePathStringPoint(el, size, face), el.board); 158 } 159 this.setShadow(el); 160 }, 161 162 /** 163 * Changes the style of a {@link JXG.Point}. This is required because the point styles differ in what 164 * elements have to be drawn, e.g. if the point is marked by a "x" or a "+" two lines are drawn, if 165 * it's marked by spot a circle is drawn. This method removes the old renderer element(s) and creates 166 * the new one(s). 167 * @param {JXG.Point} el Reference to a {@link JXG.Point} object, that's style is changed. 168 * @see Point 169 * @see JXG.Point 170 * @see #updatePoint 171 * @see #drawPoint 172 */ 173 changePointStyle: function(el) { 174 var node = this.getElementById(el.id); 175 176 // remove the existing point rendering node 177 if (JXG.exists(node)) { 178 this.remove(node); 179 } 180 181 // and make a new one 182 this.drawPoint(el); 183 JXG.clearVisPropOld(el); 184 185 if (!el.visProp['visible']) { 186 this.hide(el); 187 } 188 189 if (el.visProp['draft']) { 190 this.setDraft(el); 191 } 192 }, 193 194 195 196 /* ******************************** * 197 * Lines * 198 * ******************************** */ 199 200 /** 201 * Draws a line on the {@link JXG.Board}. 202 * @param {JXG.Line} el Reference to a line object, that has to be drawn. 203 * @see Line 204 * @see JXG.Line 205 * @see #updateLine 206 * @see #calcStraight 207 */ 208 drawLine: function(el) { 209 this.appendChildPrim(this.createPrim('line', el.id), el.layer); 210 this.appendNodesToElement(el, 'lines'); 211 this.updateLine(el); 212 }, 213 214 /** 215 * Updates visual appearance of the renderer element assigned to the given {@link JXG.Line}. 216 * @param {JXG.Line} el Reference to the {@link JXG.Line} object that has to be updated. 217 * @see Line 218 * @see JXG.Line 219 * @see #drawLine 220 * @see #calcStraight 221 */ 222 updateLine: function(el) { 223 var screenCoords1 = new JXG.Coords(JXG.COORDS_BY_USER, el.point1.coords.usrCoords, el.board), 224 screenCoords2 = new JXG.Coords(JXG.COORDS_BY_USER, el.point2.coords.usrCoords, el.board), 225 ax, ay, bx, by, beta, x, y; 226 227 // a line can be a segment, straight, or ray. so it's not always delimited by point1 and point2 228 // calcstraight calculates the visual start point and end point of the line. 229 this.calcStraight(el, screenCoords1, screenCoords2); 230 this.updateLinePrim(el.rendNode, screenCoords1.scrCoords[1], screenCoords1.scrCoords[2], 231 screenCoords2.scrCoords[1], screenCoords2.scrCoords[2], el.board); 232 // if this line has arrows attached, update them, too. 233 this.makeArrows(el); 234 this.updateVisual(el, {fill: true}); 235 }, 236 237 /** 238 * Calculates drawing start and end point for a line. A segment is only drawn from start to end point, a straight line 239 * is drawn until it meets the boards boundaries. 240 * @param {JXG.Line} el Reference to a line object, that needs calculation of start and end point. 241 * @param {JXG.Coords} point1 Coordinates of the point where line drawing begins. This value is calculated and set by this method. 242 * @param {JXG.Coords} point2 Coordinates of the point where line drawing ends. This value is calculated and set by this method. 243 * @see Line 244 * @see JXG.Line 245 * @see #drawLine 246 * @see #updateLine 247 */ 248 calcStraight: function(el, point1, point2) { 249 var takePoint1, takePoint2, intersect1, intersect2, straightFirst, straightLast, 250 c, s, i, j, p1, p2; 251 252 straightFirst = el.visProp['straightFirst']; 253 straightLast = el.visProp['straightLast']; 254 255 // If one of the point is an ideal point in homogeneous coordinates 256 // drawing of line segments or rays are not possible. 257 if (Math.abs(point1.scrCoords[0]) < JXG.Math.eps) { 258 straightFirst = true; 259 } 260 if (Math.abs(point2.scrCoords[0]) < JXG.Math.eps) { 261 straightLast = true; 262 } 263 264 if (!straightFirst && !straightLast) { // Do nothing in case of line segments (inside or outside of the board) 265 return; 266 } 267 268 // Compute the stdform of the line in screen coordinates. 269 c = []; 270 c[0] = el.stdform[0] - 271 el.stdform[1] * el.board.origin.scrCoords[1] / el.board.stretchX + 272 el.stdform[2] * el.board.origin.scrCoords[2] / el.board.stretchY; 273 c[1] = el.stdform[1] / el.board.stretchX; 274 c[2] = el.stdform[2] / (-el.board.stretchY); 275 276 if (isNaN(c[0] + c[1] + c[2])) return; // p1=p2 277 278 // Intersect the line with the four borders of the board. 279 s = []; 280 s[0] = JXG.Math.crossProduct(c, [0,0,1]); // top 281 s[1] = JXG.Math.crossProduct(c, [0,1,0]); // left 282 s[2] = JXG.Math.crossProduct(c, [-el.board.canvasHeight,0,1]); // bottom 283 s[3] = JXG.Math.crossProduct(c, [-el.board.canvasWidth,1,0]); // right 284 285 // Normalize the intersections 286 for (i = 0; i < 4; i++) { 287 if (Math.abs(s[i][0]) > JXG.Math.eps) { 288 for (j = 2; j > 0; j--) { 289 s[i][j] /= s[i][0]; 290 } 291 s[i][0] = 1.0; 292 } 293 } 294 295 takePoint1 = false; 296 takePoint2 = false; 297 if (!straightFirst && // Line starts at point1 and point2 is inside the board 298 point1.scrCoords[1] >= 0.0 && point1.scrCoords[1] <= el.board.canvasWidth && 299 point1.scrCoords[2] >= 0.0 && point1.scrCoords[2] <= el.board.canvasHeight) { 300 takePoint1 = true; 301 } 302 if (!straightLast && // Line ends at point2 and point2 is inside the board 303 point2.scrCoords[1] >= 0.0 && point2.scrCoords[1] <= el.board.canvasWidth && 304 point2.scrCoords[2] >= 0.0 && point2.scrCoords[2] <= el.board.canvasHeight) { 305 takePoint2 = true; 306 } 307 308 if (Math.abs(s[1][0]) < JXG.Math.eps) { // line is parallel to "left", take "top" and "bottom" 309 intersect1 = s[0]; // top 310 intersect2 = s[2]; // bottom 311 } else if (Math.abs(s[0][0]) < JXG.Math.eps) { // line is parallel to "top", take "left" and "right" 312 intersect1 = s[1]; // left 313 intersect2 = s[3]; // right 314 } else if (s[1][2] < 0) { // left intersection out of board (above) 315 intersect1 = s[0]; // top 316 if (s[3][2] > el.board.canvasHeight) { // right intersection out of board (below) 317 intersect2 = s[2]; // bottom 318 } else { 319 intersect2 = s[3]; // right 320 } 321 } else if (s[1][2] > el.board.canvasHeight) { // left intersection out of board (below) 322 intersect1 = s[2]; // bottom 323 if (s[3][2] < 0) { // right intersection out of board (above) 324 intersect2 = s[0]; // top 325 } else { 326 intersect2 = s[3]; // right 327 } 328 } else { 329 intersect1 = s[1]; // left 330 if (s[3][2] < 0) { // right intersection out of board (above) 331 intersect2 = s[0]; // top 332 } else if (s[3][2] > el.board.canvasHeight) { // right intersection out of board (below) 333 intersect2 = s[2]; // bottom 334 } else { 335 intersect2 = s[3]; // right 336 } 337 } 338 339 intersect1 = new JXG.Coords(JXG.COORDS_BY_SCREEN, intersect1.slice(1), el.board); 340 intersect2 = new JXG.Coords(JXG.COORDS_BY_SCREEN, intersect2.slice(1), el.board); 341 342 if (!takePoint1 && !takePoint2) { // If both points are outside and the complete ray is outside we do nothing 343 if (!straightFirst && straightLast && // Ray starting at point 1 344 !this.isSameDirection(point1, point2, intersect1) && !this.isSameDirection(point1, point2, intersect2)) { 345 return; 346 } else if (straightFirst && !straightLast && // Ray starting at point 2 347 !this.isSameDirection(point2, point1, intersect1) && !this.isSameDirection(point2, point1, intersect2)) { 348 return; 349 } 350 } 351 352 if (!takePoint1) { 353 if (!takePoint2) { // Two border intersection points are used 354 if (this.isSameDirection(point1, point2, intersect1)) { 355 if (!this.isSameDirection(point1, point2, intersect2)) { 356 p2 = intersect1; 357 p1 = intersect2; 358 } else { 359 if (JXG.Math.Geometry.affineDistance(point2.usrCoords, intersect1.usrCoords) < JXG.Math.Geometry.affineDistance(point2.usrCoords, intersect2.usrCoords)) { 360 p1 = intersect1; 361 p2 = intersect2; 362 } else { 363 p2 = intersect1; 364 p1 = intersect2; 365 } 366 } 367 } else { 368 if (this.isSameDirection(point1, point2, intersect2)) { 369 p1 = intersect1; 370 p2 = intersect2; 371 } else { 372 if (JXG.Math.Geometry.affineDistance(point2.usrCoords, intersect1.usrCoords) < JXG.Math.Geometry.affineDistance(point2.usrCoords, intersect2.usrCoords)) { 373 p2 = intersect1; 374 p1 = intersect2; 375 } else { 376 p1 = intersect1; 377 p2 = intersect2; 378 } 379 } 380 } 381 } else { // Instead of point1 the border intersection is taken 382 if (this.isSameDirection(point2, point1, intersect1)) { 383 p1 = intersect1; 384 } else { 385 p1 = intersect2; 386 } 387 } 388 } else { 389 if (!takePoint2) { // Instead of point2 the border intersection is taken 390 if (this.isSameDirection(point1, point2, intersect1)) { 391 p2 = intersect1; 392 } else { 393 p2 = intersect2; 394 } 395 } 396 } 397 398 if (p1) point1.setCoordinates(JXG.COORDS_BY_USER, p1.usrCoords.slice(1)); 399 if (p2) point2.setCoordinates(JXG.COORDS_BY_USER, p2.usrCoords.slice(1)); 400 }, 401 402 /** 403 * If you're looking from point "start" towards point "s" and can see the point "p", true is returned. Otherwise false. 404 * @param {JXG.Coords} start The point you're standing on. 405 * @param {JXG.Coords} p The point in which direction you're looking. 406 * @param {JXG.Coords} s The point that should be visible. 407 * @returns {Boolean} True, if from start the point p is in the same direction as s is, that means s-start = k*(p-start) with k>=0. 408 */ 409 isSameDirection: function(start, p, s) { 410 var dx, dy, sx, sy, r = false; 411 412 dx = p.usrCoords[1] - start.usrCoords[1]; 413 dy = p.usrCoords[2] - start.usrCoords[2]; 414 415 sx = s.usrCoords[1] - start.usrCoords[1]; 416 sy = s.usrCoords[2] - start.usrCoords[2]; 417 418 if (Math.abs(dx) < JXG.Math.eps) dx = 0; 419 if (Math.abs(dy) < JXG.Math.eps) dy = 0; 420 if (Math.abs(sx) < JXG.Math.eps) sx = 0; 421 if (Math.abs(sy) < JXG.Math.eps) sy = 0; 422 423 if (dx >= 0 && sx >= 0) { 424 if ((dy >= 0 && sy >= 0) || (dy <= 0 && sy <= 0)) { 425 r = true; 426 } 427 } else if (dx <= 0 && sx <= 0) { 428 if ((dy >= 0 && sy >= 0) || (dy <= 0 && sy <= 0)) { 429 r = true; 430 } 431 } 432 433 return r; 434 }, 435 436 /** 437 * Update {@link Ticks} on a {@link JXG.Line}. This method is only a stub and is implemented only in the special renderers. 438 * @param {JXG.Line} axis Reference of an line object, thats ticks have to be updated. 439 * @param {Number} dxMaj Number of pixels a major tick counts in x direction. 440 * @param {Number} dyMaj Number of pixels a major tick counts in y direction. 441 * @param {Number} dxMin Number of pixels a minor tick counts in x direction. 442 * @param {Number} dyMin Number of pixels a minor tick counts in y direction. 443 * @see Line 444 * @see Ticks 445 * @see JXG.Line 446 * @see JXG.Ticks 447 * @see #removeTicks 448 */ 449 updateTicks: function(axis, dxMaj, dyMaj, dxMin, dyMin) { 450 }, 451 452 /** 453 * Removes all ticks from an {@link JXG.Line}. 454 * @param {JXG.Line} axis Reference of an {@link JXG.Line} object, that's ticks have to be removed. 455 * @deprecated 456 * @see Line 457 * @see Ticks 458 * @see Axis 459 * @see JXG.Line 460 * @see JXG.Ticks 461 */ 462 removeTicks: function(axis) { 463 this.remove(this.getElementById(axis.id + '_ticks')); 464 }, 465 466 /* ************************** 467 * Curves 468 * **************************/ 469 470 /** 471 * Draws a {@link JXG.Curve} on the {@link JXG.Board}. 472 * @param {JXG.Curve} el Reference to a graph object, that has to be plotted. 473 * @see Curve 474 * @see JXG.Curve 475 * @see #updateCurve 476 */ 477 drawCurve: function(el) { 478 this.appendChildPrim(this.createPrim('path', el.id), el.layer); 479 this.appendNodesToElement(el, 'path'); 480 this.updateVisual(el, {shadow: true}, true); 481 this.updateCurve(el); 482 }, 483 484 /** 485 * Updates visual appearance of the renderer element assigned to the given {@link JXG.Curve}. 486 * @param {JXG.Curve} el Reference to a {@link JXG.Curve} object, that has to be updated. 487 * @see Curve 488 * @see JXG.Curve 489 * @see #drawCurve 490 */ 491 updateCurve: function(el) { 492 this.updateVisual(el); 493 this.updatePathPrim(el.rendNode, this.updatePathStringPrim(el), el.board); 494 this.makeArrows(el); 495 }, 496 497 498 /* ************************** 499 * Circle related stuff 500 * **************************/ 501 502 /** 503 * Draws a {@link JXG.Circle} on the {@link JXG.Board}. 504 * @param {JXG.Circle} el Reference to a {@link JXG.Circle} object, that has to be drawn. 505 * @see Circle 506 * @see JXG.Circle 507 * @see #updateCircle 508 */ 509 drawCircle: function(el) { 510 this.appendChildPrim(this.createPrim('ellipse', el.id), el.layer); 511 this.appendNodesToElement(el, 'ellipse'); 512 this.updateCircle(el); 513 }, 514 515 /** 516 * Updates visual appearance of a given {@link JXG.Circle} on the {@link JXG.Board}. 517 * @param {JXG.Circle} el Reference to a {@link JXG.Circle} object, that has to be updated. 518 * @see Circle 519 * @see JXG.Circle 520 * @see #drawCircle 521 */ 522 updateCircle: function(el) { 523 this.updateVisual(el); 524 525 // Radius umrechnen: 526 var radius = el.Radius(); 527 if (radius > 0.0 && !isNaN(radius + el.midpoint.coords.scrCoords[1] + el.midpoint.coords.scrCoords[2]) && radius*el.board.stretchX<20000) { 528 this.updateEllipsePrim(el.rendNode, el.midpoint.coords.scrCoords[1], el.midpoint.coords.scrCoords[2], 529 (radius * el.board.stretchX), (radius * el.board.stretchY)); 530 } 531 }, 532 533 534 /* ************************** 535 * Polygon related stuff 536 * **************************/ 537 538 /** 539 * Draws a {@link JXG.Polygon} on the {@link JXG.Board}. 540 * @param {JXG.Polygon} el Reference to a Polygon object, that is to be drawn. 541 * @see Polygon 542 * @see JXG.Polygon 543 * @see #updatePolygon 544 */ 545 drawPolygon: function(el) { 546 this.appendChildPrim(this.createPrim('polygon', el.id), el.layer); 547 this.appendNodesToElement(el, 'polygon'); 548 this.updatePolygon(el); 549 }, 550 551 /** 552 * Updates properties of a {@link JXG.Polygon}'s rendering node. 553 * @param {JXG.Polygon} el Reference to a {@link JXG.Polygon} object, that has to be updated. 554 * @see Polygon 555 * @see JXG.Polygon 556 * @see #drawPolygon 557 */ 558 updatePolygon: function(el) { 559 // here originally strokecolor wasn't updated but strokewidth was 560 // but if there's no strokecolor i don't see why we should update strokewidth. 561 this.updateVisual(el, {stroke: true, dash: true}); 562 this.updatePolygonPrim(el.rendNode, el); 563 }, 564 565 /* ************************** 566 * Text related stuff 567 * **************************/ 568 569 /** 570 * Displays a {@link JXG.Text} on the {@link JXG.Board} by putting a HTML div over it. 571 * @param {JXG.Text} el Reference to an {@link JXG.Text} object, that has to be displayed 572 * @see Text 573 * @see JXG.Text 574 * @see #drawInternalText 575 * @see #updateText 576 * @see #updateInternalText 577 * @see #updateTextStyle 578 */ 579 drawText: function(el) { 580 var node; 581 582 if (el.display == 'html') { 583 node = this.container.ownerDocument.createElement('div'); 584 node.style.position = 'absolute'; 585 node.style.color = el.visProp['strokeColor']; 586 node.className = 'JXGtext'; 587 node.style.zIndex = '10'; 588 this.container.appendChild(node); 589 node.setAttribute('id', this.container.id + '_' + el.id); 590 } else { 591 node = this.drawInternalText(el); 592 } 593 node.style.fontSize = el.board.options.text.fontSize + 'px'; 594 el.rendNode = node; 595 el.htmlStr = ''; 596 this.updateText(el); 597 }, 598 599 /** 600 * An internal text is a {@link JXG.Text} element which is drawn using only 601 * the given renderer but no HTML. This method is only a stub, the drawing 602 * is done in the special renderers. 603 * @param {JXG.Text} el Reference to a {@link JXG.Text} object 604 * @see Text 605 * @see JXG.Text 606 * @see #updateInternalText 607 * @see #drawText 608 * @see #updateText 609 * @see #updateTextStyle 610 */ 611 drawInternalText: function(el) { 612 }, 613 614 615 /** 616 * Updates visual properties of an already existing {@link JXG.Text} element. 617 * @param {JXG.Text} el Reference to an {@link JXG.Text} object, that has to be updated. 618 * @see Text 619 * @see JXG.Text 620 * @see #drawText 621 * @see #drawInternalText 622 * @see #updateInternalText 623 * @see #updateTextStyle 624 */ 625 updateText: function(el) { 626 // Update only objects that are visible. 627 if (!el.visProp['visible']) return; 628 if (isNaN(el.coords.scrCoords[1] + el.coords.scrCoords[2])) return; 629 630 this.updateTextStyle(el); 631 if (el.display == 'html') { 632 el.rendNode.style.left = (el.coords.scrCoords[1]) + 'px'; 633 el.rendNode.style.top = (el.coords.scrCoords[2] - this.vOffsetText) + 'px'; 634 el.updateText(); 635 if (el.htmlStr != el.plaintextStr) { 636 el.rendNode.innerHTML = el.plaintextStr; 637 if (el.board.options.text.useASCIIMathML) { 638 AMprocessNode(el.rendNode, false); 639 } 640 el.htmlStr = el.plaintextStr; 641 if (el.board.options.text.useMathJax) { 642 MathJax.Hub.Typeset(el.rendNode); 643 } 644 } 645 this.transformImage(el, el.transformations); 646 } else { 647 this.updateInternalText(el); 648 } 649 }, 650 651 /** 652 * Updates visual properties of an already existing {@link JXG.Text} element. 653 * @param {JXG.Text} el Reference to an {@link JXG.Text} object, that has to be updated. 654 * @see Text 655 * @see JXG.Text 656 * @see #drawInternalText 657 * @see #drawText 658 * @see #updateText 659 * @see #updateTextStyle 660 */ 661 updateInternalText: function(el) { 662 }, 663 664 /** 665 * Updates CSS style properties of a {@link JXG.Text} node. 666 * @param {JXG.Text} el Reference to the {@link JXG.Text} object, that has to be updated. 667 * @see Text 668 * @see JXG.Text 669 * @see #drawText 670 * @see #drawInternalText 671 * @see #updateText 672 * @see #updateInternalText 673 */ 674 updateTextStyle: function(el) { 675 var fs; 676 677 if (el.visProp['fontSize']) { 678 if (typeof el.visProp['fontSize'] == 'function') { 679 fs = el.visProp['fontSize'](); 680 el.rendNode.style.fontSize = (fs > 0 ? fs : 0); 681 } else { 682 el.rendNode.style.fontSize = (el.visProp['fontSize']); 683 } 684 } 685 }, 686 687 /* ************************** 688 * Image related stuff 689 * **************************/ 690 691 /** 692 * Draws an {@link JXG.Image} on the {@link JXG.Board}; This is just a template, has to be implemented by special renderers. 693 * @param {JXG.Image} el Reference to an image object, that has to be drawn. 694 * @see Image 695 * @see JXG.Image 696 * @see #updateImage 697 */ 698 drawImage: function(el) { 699 }, 700 701 /** 702 * If the URL of the image is proveided by a function, i.e. dynamic URL 703 * the URL has to be updated during updateImage() 704 * @param {JXG.Image} el Reference to an image object. 705 * @see #updateImage 706 */ 707 updateImageURL: function(el) { 708 }, 709 710 /** 711 * Updates the properties of an {@link JXG.Image} element. 712 * @param {JXG.Image} el Reference to an {@link JXG.Image} object, that has to be updated. 713 * @see JXG.Image 714 * @see #drawImage 715 */ 716 updateImage: function(el) { 717 this.updateRectPrim(el.rendNode, el.coords.scrCoords[1], el.coords.scrCoords[2] - el.size[1], 718 el.size[0], el.size[1]); 719 720 this.updateImageURL(el); 721 this.transformImage(el, el.transformations); 722 this.updateVisual(el, {stroke: true, dash: true}, true); 723 }, 724 725 /** 726 * Multiplication of transformations without updating. 727 * That means, at that point it is expected that the matrices 728 * contain numbers only. 729 * First, the origin in user coords is translated to (0,0) in screen coords. 730 * Then, the stretch factors are divided out. 731 * After the transformations in user coords, the strech factors are multiplied in again, 732 * and the origin in user coords is translated back to its position. 733 * @see #transformImage 734 */ 735 joinTransforms: function(el,t) { 736 var m = [[1,0,0],[0,1,0],[0,0,1]], 737 mpre1 = [[1, 0, 0], [-el.board.origin.scrCoords[1], 1, 0], [-el.board.origin.scrCoords[2], 0, 1]], 738 mpre2 = [[1, 0, 0], [0, 1/el.board.stretchX, 0], [0, 0, -1/el.board.stretchY]], 739 mpost2 = [[1, 0, 0], [0, el.board.stretchX, 0], [0, 0, -el.board.stretchY]], 740 mpost1 = [[1, 0, 0], [el.board.origin.scrCoords[1], 1, 0], [el.board.origin.scrCoords[2], 0, 1]], 741 i, len = t.length; 742 743 for (i=0;i<len;i++) { 744 m = JXG.Math.matMatMult(mpre1,m); 745 m = JXG.Math.matMatMult(mpre2,m); 746 m = JXG.Math.matMatMult(t[i].matrix,m); 747 m = JXG.Math.matMatMult(mpost2,m); 748 m = JXG.Math.matMatMult(mpost1,m); 749 } 750 return m; 751 }, 752 753 754 /* ************************** 755 * Grid stuff 756 * **************************/ 757 758 /** 759 * Creates a grid on the board, i.e. light helper lines to support the user on creating and manipulating a construction. 760 * @param {JXG.Board} board Board on which the grid is drawn. 761 * @see #removeGrid 762 */ 763 drawGrid: function(board) { 764 var gridX = board.options.grid.gridX, 765 gridY = board.options.grid.gridY, 766 k = new JXG.Coords(JXG.COORDS_BY_SCREEN, [0,0], board), 767 k2 = new JXG.Coords(JXG.COORDS_BY_SCREEN, [board.canvasWidth, board.canvasHeight], board), 768 tmp = Math.ceil(k.usrCoords[1]), 769 j = 0, 770 i, j2, l, l2, 771 gx, gy, topLeft, bottomRight, node2, el; 772 773 board.options.grid.hasGrid = true; 774 775 for (i = 0; i <= gridX + 1; i++) { 776 if (tmp - i / gridX < k.usrCoords[1]) { 777 j = i - 1; 778 break; 779 } 780 } 781 782 tmp = Math.floor(k2.usrCoords[1]); 783 j2 = 0; 784 for (i = 0; i <= gridX + 1; i++) { 785 if (tmp + i / gridX > k2.usrCoords[1]) { 786 j2 = i - 1; 787 break; 788 } 789 } 790 791 tmp = Math.ceil(k2.usrCoords[2]); 792 l2 = 0; 793 for (i = 0; i <= gridY + 1; i++) { 794 if (tmp - i / gridY < k2.usrCoords[2]) { 795 l2 = i - 1; 796 break; 797 } 798 } 799 800 tmp = Math.floor(k.usrCoords[2]); 801 l = 0; 802 for (i = 0; i <= gridY + 1; i++) { 803 if (tmp + i / gridY > k.usrCoords[2]) { 804 l = i - 1; 805 break; 806 } 807 } 808 809 gx = Math.round((1.0 / gridX) * board.stretchX); 810 gy = Math.round((1.0 / gridY) * board.stretchY); 811 812 topLeft = new JXG.Coords(JXG.COORDS_BY_USER, 813 [Math.ceil(k.usrCoords[1]) - j / gridX, Math.floor(k.usrCoords[2]) + l / gridY], 814 board); 815 bottomRight = new JXG.Coords(JXG.COORDS_BY_USER, 816 [Math.floor(k2.usrCoords[1]) + j2 / gridX, Math.ceil(k2.usrCoords[2]) - l2 / gridY], 817 board); 818 819 node2 = this.drawVerticalGrid(topLeft, bottomRight, gx, board); 820 this.appendChildPrim(node2, board.options.layer['grid']); 821 if (!board.options.grid.snapToGrid) { 822 el = new Object(); 823 el.visProp = {}; 824 el.rendNode = node2; 825 el.elementClass = JXG.OBJECT_CLASS_LINE; 826 el.id = "gridx"; 827 JXG.clearVisPropOld(el); 828 this.setObjectStrokeColor(el, board.options.grid.gridColor, board.options.grid.gridOpacity); 829 } 830 else { 831 el = new Object(); 832 el.visProp = {}; 833 el.rendNode = node2; 834 el.elementClass = JXG.OBJECT_CLASS_LINE; 835 el.id = "gridx"; 836 JXG.clearVisPropOld(el); 837 this.setObjectStrokeColor(el, '#FF8080', 0.5); //board.gridOpacity); 838 } 839 this.setPropertyPrim(node2, 'stroke-width', '0.4px'); 840 if (board.options.grid.gridDash) { 841 this.setGridDash("gridx"); 842 } 843 844 node2 = this.drawHorizontalGrid(topLeft, bottomRight, gy, board); 845 this.appendChildPrim(node2, board.options.layer['grid']); // Attention layer=1 846 if (!board.options.grid.snapToGrid) { 847 el = new Object(); 848 el.visProp = {}; 849 el.rendNode = node2; 850 el.elementClass = JXG.OBJECT_CLASS_LINE; 851 el.id = "gridy"; 852 JXG.clearVisPropOld(el); 853 this.setObjectStrokeColor(el, board.options.grid.gridColor, board.options.grid.gridOpacity); 854 } 855 else { 856 el = new Object(); 857 el.visProp = {}; 858 el.rendNode = node2; 859 el.elementClass = JXG.OBJECT_CLASS_LINE; 860 el.id = "gridy"; 861 JXG.clearVisPropOld(el); 862 this.setObjectStrokeColor(el, '#FF8080', 0.5); //board.gridOpacity); 863 } 864 this.setPropertyPrim(node2, 'stroke-width', '0.4px'); 865 if (board.options.grid.gridDash) { 866 this.setGridDash("gridy"); 867 } 868 869 }, 870 871 /** 872 * Remove the grid from the given board. 873 * @param {JXG.Board} board Board from which the grid is removed. 874 * @see #drawGrid 875 */ 876 removeGrid: function(board) { 877 // var getElementById = this.getElementById; // Does not work, because 878 // this in getElementById would point to "window" 879 880 this.remove(this.getElementById('gridx')); 881 this.remove(this.getElementById('gridy')); 882 883 board.options.grid.hasGrid = false; 884 }, 885 886 887 /* ************************** 888 * general element helpers 889 * **************************/ 890 891 /** 892 * Hides an element on the canvas; Only a stub, requires implementation in the derived renderer. 893 * @param {JXG.GeometryElement} obj Reference to the geometry element that has to disappear. 894 * @see #show 895 */ 896 hide: function(obj) { 897 }, 898 899 /** 900 * Shows a hidden element on the canvas; Only a stub, requires implementation in the derived renderer. 901 * @param {JXG.GeometryElement} obj Reference to the object that has to appear. 902 * @see #hide 903 */ 904 show: function(obj) { 905 }, 906 907 /** 908 * Sets an element's stroke width. 909 * @param {JXG.GeometryElement} el Reference to the geometry element. 910 * @param {Number} width The new stroke width to be assigned to the element. 911 */ 912 setObjectStrokeWidth: function(el, width) { 913 }, 914 915 /** 916 * Changes an objects stroke color to the given color. 917 * @param {JXG.GeometryElement} obj Reference of the {@link JXG.GeometryElement} that gets a new stroke color. 918 * @param {String} color Color in a HTML/CSS compatible format, e.g. <strong>#00ff00</strong> or <strong>green</strong> for green. 919 * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1. 920 */ 921 setObjectStrokeColor: function(obj, color, opacity) { 922 }, 923 924 /** 925 * Sets an objects fill color. 926 * @param {JXG.GeometryElement} obj Reference of the object that wants a new fill color. 927 * @param {String} color Color in a HTML/CSS compatible format. If you don't want any fill color at all, choose 'none'. 928 * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1. 929 */ 930 setObjectFillColor: function(obj, color, opacity) { 931 }, 932 933 /** 934 * Puts an object into draft mode, i.e. it's visual appearance will be changed. For GEONE<sub>x</sub>T backwards compatibility. 935 * @param {JXG.GeometryElement} obj Reference of the object that is in draft mode. 936 */ 937 setDraft: function (obj) { 938 if (!obj.visProp['draft']) { 939 return; 940 } 941 var draftColor = obj.board.options.elements.draft.color, 942 draftOpacity = obj.board.options.elements.draft.opacity; 943 944 if (obj.type == JXG.OBJECT_TYPE_POLYGON) { 945 this.setObjectFillColor(obj, draftColor, draftOpacity); 946 } 947 else { 948 if (obj.elementClass == JXG.OBJECT_CLASS_POINT) { 949 this.setObjectFillColor(obj, draftColor, draftOpacity); 950 } 951 else { 952 this.setObjectFillColor(obj, 'none', 0); 953 } 954 this.setObjectStrokeColor(obj, draftColor, draftOpacity); 955 this.setObjectStrokeWidth(obj, obj.board.options.elements.draft.strokeWidth); 956 } 957 }, 958 959 /** 960 * Puts an object from draft mode back into normal mode. 961 * @param {JXG.GeometryElement} obj Reference of the object that no longer is in draft mode. 962 */ 963 removeDraft: function (obj) { 964 if (obj.type == JXG.OBJECT_TYPE_POLYGON) { 965 this.setObjectFillColor(obj, obj.visProp['fillColor'], obj.visProp['fillColorOpacity']); 966 } 967 else { 968 if (obj.type == JXG.OBJECT_CLASS_POINT) { 969 this.setObjectFillColor(obj, obj.visProp['fillColor'], obj.visProp['fillColorOpacity']); 970 } 971 this.setObjectStrokeColor(obj, obj.visProp['strokeColor'], obj.visProp['strokeColorOpacity']); 972 this.setObjectStrokeWidth(obj, obj.visProp['strokeWidth']); 973 } 974 }, 975 976 /** 977 * Highlights an object, i.e. changes the current colors of the object to its highlighting colors 978 * @param {JXG.GeometryElement} obj Reference of the object that will be highlighted. 979 */ 980 highlight: function(obj) { 981 var i; 982 983 if (!obj.visProp['draft']) { 984 if (obj.type == JXG.OBJECT_CLASS_POINT) { 985 this.setObjectStrokeColor(obj, obj.visProp['highlightStrokeColor'], obj.visProp['highlightStrokeOpacity']); 986 this.setObjectFillColor(obj, obj.visProp['highlightStrokeColor'], obj.visProp['highlightStrokeOpacity']); 987 } 988 else if (obj.type == JXG.OBJECT_TYPE_POLYGON) { 989 this.setObjectFillColor(obj, obj.visProp['highlightFillColor'], obj.visProp['highlightFillOpacity']); 990 for (i = 0; i < obj.borders.length; i++) { 991 this.setObjectStrokeColor(obj.borders[i], obj.borders[i].visProp['highlightStrokeColor'], obj.visProp['highlightStrokeOpacity']); 992 } 993 } 994 else { 995 this.setObjectStrokeColor(obj, obj.visProp['highlightStrokeColor'], obj.visProp['highlightStrokeOpacity']); 996 this.setObjectFillColor(obj, obj.visProp['highlightFillColor'], obj.visProp['highlightFillOpacity']); 997 } 998 if (obj.visProp['highlightStrokeWidth']) { 999 this.setObjectStrokeWidth(obj, obj.visProp['highlightStrokeWidth']); 1000 } 1001 } 1002 }, 1003 1004 /** 1005 * Uses the "normal" colors of an object, i.e. the opposite of {@link #highlight}. 1006 * @param {JXG.GeometryElement} obj Reference of the object that will get its normal colors. 1007 */ 1008 noHighlight: function(obj) { 1009 var i; 1010 1011 if (!obj.visProp['draft']) { 1012 if (obj.type == JXG.OBJECT_CLASS_POINT) { 1013 this.setObjectStrokeColor(obj, obj.visProp['strokeColor'], obj.visProp['strokeOpacity']); 1014 this.setObjectFillColor(obj, obj.visProp['strokeColor'], obj.visProp['strokeOpacity']); 1015 } 1016 else if (obj.type == JXG.OBJECT_TYPE_POLYGON) { 1017 this.setObjectFillColor(obj, obj.visProp['fillColor'], obj.visProp['fillOpacity']); 1018 for (i = 0; i < obj.borders.length; i++) { 1019 this.setObjectStrokeColor(obj.borders[i], obj.borders[i].visProp['strokeColor'], obj.visProp['strokeOpacity']); 1020 } 1021 } 1022 else { 1023 this.setObjectStrokeColor(obj, obj.visProp['strokeColor'], obj.visProp['strokeOpacity']); 1024 this.setObjectFillColor(obj, obj.visProp['fillColor'], obj.visProp['fillOpacity']); 1025 } 1026 this.setObjectStrokeWidth(obj, obj.visProp['strokeWidth']); 1027 } 1028 }, 1029 1030 /** 1031 * Removes an HTML-Element from Canvas. Just a stub. 1032 * @param {HTMLElement} node The HTMLElement to remove. 1033 */ 1034 remove: function(node) { 1035 }, 1036 1037 1038 /* ************************** 1039 * general renderer related methods 1040 * **************************/ 1041 1042 /** 1043 * Stop redraw. This method is called before every update, so a non-vector-graphics based renderer 1044 * can delete the contents of the drawing panel. 1045 * @see #unsuspendRedraw 1046 */ 1047 suspendRedraw: function() { 1048 }, 1049 1050 /** 1051 * Restart redraw. This method is called after updating all the rendering node attributes. 1052 * @see #suspendRedraw 1053 */ 1054 unsuspendRedraw: function() { 1055 }, 1056 1057 /** 1058 * The tiny zoom bar shown on the bottom of a board (if {@link JXG.Board#showNavigation} is true). 1059 * @param {JXG.Board} board Reference to a JSXGraph board. 1060 */ 1061 drawZoomBar: function(board) { 1062 var doc, 1063 node, 1064 createButton = function(label, handler) { 1065 var button; 1066 1067 button = doc.createElement('span'); 1068 node.appendChild(button); 1069 button.innerHTML = label; 1070 JXG.addEvent(button, 'click', handler, board); 1071 }; 1072 1073 doc = this.container.ownerDocument; 1074 node = doc.createElement('div'); 1075 1076 node.setAttribute('id', this.container.id + '_navigationbar'); 1077 node.className = 'JXGtext'; 1078 node.style.color = '#aaaaaa'; 1079 node.style.backgroundColor = '#f5f5f5'; 1080 node.style.padding = '2px'; 1081 node.style.position = 'absolute'; 1082 node.style.fontSize = '10px'; 1083 node.style.cursor = 'pointer'; 1084 node.style.zIndex = '100'; 1085 this.container.appendChild(node); 1086 node.style.right = '5px'; //(board.canvasWidth-100)+ 'px'; 1087 node.style.bottom = '5px'; 1088 1089 createButton(' – ', board.zoomOut); 1090 createButton(' o ', board.zoom100); 1091 createButton(' + ', board.zoomIn); 1092 createButton(' ← ', board.clickLeftArrow); 1093 createButton(' ↑ ', board.clickUpArrow); 1094 createButton(' ↓ ', board.clickDownArrow); 1095 createButton(' → ', board.clickRightArrow); 1096 }, 1097 1098 /** 1099 * Wrapper for getElementById for maybe other renderers which elements are not directly accessible by DOM methods like document.getElementById(). 1100 * @param {String} id Unique identifier for element. 1101 * @returns {Object} Reference to a JavaScript object. In case of SVG/VMLRenderer it's a reference to a SVG/VML node. 1102 */ 1103 getElementById: function(id) { 1104 return document.getElementById(this.container.id + '_' + id); 1105 }, 1106 1107 /** 1108 * findSplit() is a subroutine for {@link #RamenDouglasPeuker}. 1109 * It searches for the point between index i and j which 1110 * has the largest distance from the line between the points i and j. 1111 * @param {Array} pts Array of {@link JXG.Point} 1112 * @param {Number} i Index of a point in pts 1113 * @param {Number} j Index of a point in pts 1114 **/ 1115 findSplit: function(pts, i, j) { 1116 var dist = 0, 1117 f = i, 1118 d, k, ci, cj, ck, 1119 x0, y0, x1, y1, 1120 den, lbda; 1121 1122 if (j - i < 2) return [-1.0,0]; 1123 1124 ci = pts[i].scrCoords; 1125 cj = pts[j].scrCoords; 1126 if (isNaN(ci[1] + ci[2] + cj[1] + cj[2])) return [NaN,j]; 1127 1128 for (k = i + 1; k < j; k++) { 1129 ck = pts[k].scrCoords; 1130 x0 = ck[1] - ci[1]; 1131 y0 = ck[2] - ci[2]; 1132 x1 = cj[1] - ci[1]; 1133 y1 = cj[2] - ci[2]; 1134 den = x1 * x1 + y1 * y1; 1135 if (den >= JXG.Math.eps) { 1136 lbda = (x0 * x1 + y0 * y1) / den; 1137 d = x0 * x0 + y0 * y0 - lbda * (x0 * x1 + y0 * y1); 1138 } else { 1139 lbda = 0.0; 1140 d = x0 * x0 + y0 * y0; 1141 } 1142 if (lbda < 0.0) { 1143 d = x0 * x0 + y0 * y0; 1144 } else if (lbda > 1.0) { 1145 x0 = ck[1] - cj[1]; 1146 y0 = ck[2] - cj[2]; 1147 d = x0 * x0 + y0 * y0; 1148 } 1149 if (d > dist) { 1150 dist = d; 1151 f = k; 1152 } 1153 } 1154 return [Math.sqrt(dist),f]; 1155 }, 1156 1157 /** 1158 * RDB() is a subroutine for {@link #RamenDouglasPeuker}. 1159 * It runs recursively through the point set and searches the 1160 * point which has the largest distance from the line between the first point and 1161 * the last point. If the distance from the line is greater than eps, this point is 1162 * included in our new point set otherwise it is discarded. 1163 * If it is taken, we recursively apply the subroutine to the point set before 1164 * and after the chosen point. 1165 * @param {Array} pts Array of {@link JXG.Point}s 1166 * @param {Number} i Index of an element of pts 1167 * @param {Number} j Index of an element of pts 1168 * @param {Number} eps If the absolute value of a given number <tt>x</tt> is smaller than <tt>eps</tt> it is considered to be equal <tt>0</tt>. 1169 * @param {Array} newPts Array of {@link JXG.Point}s 1170 */ 1171 RDP: function(pts, i, j, eps, newPts) { 1172 var result = this.findSplit(pts, i, j); 1173 1174 if (result[0] > eps) { 1175 this.RDP(pts, i, result[1], eps, newPts); 1176 this.RDP(pts, result[1], j, eps, newPts); 1177 } else { 1178 newPts.push(pts[j]); 1179 } 1180 }, 1181 1182 /** 1183 * Ramen-Douglas-Peuker algorithm. 1184 * It discards points which are not necessary from the polygonal line defined by the point array 1185 * pts. The computation is done in screen coordinates. 1186 * Average runtime is O(nlog(n)), worst case runtime is O(n^2), where n is the number of points. 1187 * @param {Array} pts Array of {@link JXG.Point}s 1188 * @param {Number} eps If the absolute value of a given number <tt>x</tt> is smaller than <tt>eps</tt> it is considered to be equal <tt>0</tt>. 1189 * @returns {Array} An array containing points which represent an apparently identical curve as the points of pts do, but contains fewer points. 1190 */ 1191 RamenDouglasPeuker: function(pts, eps) { 1192 var newPts = [], i, k, len; 1193 1194 len = pts.length; 1195 1196 // Search for the left most point woithout NaN coordinates 1197 i = 0; 1198 while (i < len && isNaN(pts[i].scrCoords[1] + pts[i].scrCoords[2])) { 1199 i++; 1200 } 1201 // Search for the right most point woithout NaN coordinates 1202 k = len - 1; 1203 while (k > i && isNaN(pts[k].scrCoords[1] + pts[k].scrCoords[2])) { 1204 k--; 1205 } 1206 1207 // Only proceed if something is left 1208 if (!(i > k || i == len)) { 1209 newPts[0] = pts[i]; 1210 this.RDP(pts, i, k, eps, newPts); 1211 } 1212 1213 return newPts; 1214 }, 1215 1216 /** 1217 * Sets the shadow properties to a geometry element. This method is only a stub, it is implemented in the actual renderers. 1218 * @param {JXG.GeometryElement} element Reference to a geometry object, that should get a shadow 1219 */ 1220 setShadow: function(element) { 1221 }, 1222 1223 1224 /** 1225 * @TODO Description of parameters 1226 * Updates a path element. 1227 */ 1228 updatePathStringPoint: function(el, size, type) { 1229 }, 1230 1231 /** 1232 * If <tt>val</tt> is a function, it will be evaluated without giving any parameters, else the input value is just returned. 1233 * @param val Could be anything. 1234 */ 1235 evaluate: function(val) { 1236 if (JXG.isFunction(val)) { 1237 return val(); 1238 } else { 1239 return val; 1240 } 1241 }, 1242 1243 /** 1244 * This is just a stub. Usage and implementation may differ between the different renderers. 1245 */ 1246 setBuffering: function() { 1247 1248 } 1249 }; 1250 }; 1251