      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 = 3;
    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 screenCoords1 = new JXG.Coords(JXG.COORDS_BY_USER, element.point1.coords.usrCoords, element.board),
    318             screenCoords2 = new JXG.Coords(JXG.COORDS_BY_USER, element.point2.coords.usrCoords, element.board);
    319
    320         JXG.Math.Geometry.calcStraight(element, screenCoords1, screenCoords2);
    321         this.updateLinePrim(element.rendNode, screenCoords1.scrCoords[1], screenCoords1.scrCoords[2],
    322                                          screenCoords2.scrCoords[1], screenCoords2.scrCoords[2], element.board);
    323
    324         this.makeArrows(element);
    325         this._updateVisual(element, {fill: true});
    326     },
    327
    328     /**
    329      * Creates a rendering node for ticks added to a line.
    330      * @param {JXG.Line} element A arbitrary line.
    331      * @see Line
    332      * @see Ticks
    333      * @see JXG.Line
    334      * @see JXG.Ticks
    335      * @see JXG.AbstractRenderer#updateTicks
    336      */
    337     drawTicks: function (element) {
    338         var node = this.createPrim('path', element.id);
    339
    340         this.appendChildPrim(node, element.visProp.layer);
    341         this.appendNodesToElement(element, 'path');
    342     },
    343
    344     /**
    345      * Update {@link Ticks} on a {@link JXG.Line}. This method is only a stub and has to be implemented
    346      * in any descendant renderer class.
    347      * @param {JXG.Line} element Reference of an line object, thats ticks have to be updated.
    348      * @param {Number} dxMaj Number of pixels a major tick counts in x direction.
    349      * @param {Number} dyMaj Number of pixels a major tick counts in y direction.
    350      * @param {Number} dxMin Number of pixels a minor tick counts in x direction.
    351      * @param {Number} dyMin Number of pixels a minor tick counts in y direction.
    352      * @see Line
    353      * @see Ticks
    354      * @see JXG.Line
    355      * @see JXG.Ticks
    356      * @see JXG.AbstractRenderer#drawTicks
    357      */
    358     updateTicks: function (element, dxMaj, dyMaj, dxMin, dyMin) { /* stub */ },
    359
    360     /* **************************
    361      *    Curves
    362      * **************************/
    363
    364     /**
    365      * Draws a {@link JXG.Curve} on the {@link JXG.Board}.
    366      * @param {JXG.Curve} element Reference to a graph object, that has to be plotted.
    367      * @see Curve
    368      * @see JXG.Curve
    369      * @see JXG.AbstractRenderer#updateCurve
    370      */
    371     drawCurve: function (element) {
    372         this.appendChildPrim(this.createPrim('path', element.id), element.visProp.layer);
    373         this.appendNodesToElement(element, 'path');
    374         this._updateVisual(element, {shadow: true}, true);
    375         this.updateCurve(element);
    376     },
    377
    378     /**
    379      * Updates visual appearance of the renderer element assigned to the given {@link JXG.Curve}.
    380      * @param {JXG.Curve} element Reference to a {@link JXG.Curve} object, that has to be updated.
    381      * @see Curve
    382      * @see JXG.Curve
    383      * @see JXG.AbstractRenderer#drawCurve
    384      */
    385     updateCurve: function (element) {
    386         this._updateVisual(element);
    387         if (element.visProp.handdrawing) {
    388             this.updatePathPrim(element.rendNode, this.updatePathStringBezierPrim(element), element.board);
    389         } else {
    390             this.updatePathPrim(element.rendNode, this.updatePathStringPrim(element), element.board);
    391         }
    392         this.makeArrows(element);
    393     },
    394
    395     /* **************************
    396      *    Circle related stuff
    397      * **************************/
    398
    399     /**
    400      * Draws a {@link JXG.Circle}
    401      * @param {JXG.Circle} element Reference to a {@link JXG.Circle} object that has to be drawn.
    402      * @see Circle
    403      * @see JXG.Circle
    404      * @see JXG.AbstractRenderer#updateEllipse
    405      */
    406     drawEllipse: function (element) {
    407         this.appendChildPrim(this.createPrim('ellipse', element.id), element.visProp.layer);
    408         this.appendNodesToElement(element, 'ellipse');
    409         this.updateEllipse(element);
    410     },
    411
    412     /**
    413      * Updates visual appearance of a given {@link JXG.Circle} on the {@link JXG.Board}.
    414      * @param {JXG.Circle} element Reference to a {@link JXG.Circle} object, that has to be updated.
    415      * @see Circle
    416      * @see JXG.Circle
    417      * @see JXG.AbstractRenderer#drawEllipse
    418      */
    419     updateEllipse: function (element) {
    420         this._updateVisual(element);
    421
    422         // Radius umrechnen:
    423         var radius = element.Radius();
    424         if (radius > 0.0 && !isNaN(radius + element.center.coords.scrCoords[1] + element.center.coords.scrCoords[2]) && radius * element.board.unitX < 20000) {
    425             this.updateEllipsePrim(element.rendNode, element.center.coords.scrCoords[1], element.center.coords.scrCoords[2],
    426                     (radius * element.board.unitX), (radius * element.board.unitY));
    427         }
    428     },
    429
    430
    431     /* **************************
    432      *   Polygon related stuff
    433      * **************************/
    434
    435     /**
    436      * Draws a {@link JXG.Polygon} on the {@link JXG.Board}.
    437      * @param {JXG.Polygon} element Reference to a Polygon object, that is to be drawn.
    438      * @see Polygon
    439      * @see JXG.Polygon
    440      * @see JXG.AbstractRenderer#updatePolygon
    441      */
    442     drawPolygon: function (element) {
    443         this.appendChildPrim(this.createPrim('polygon', element.id), element.visProp.layer);
    444         this.appendNodesToElement(element, 'polygon');
    445         this.updatePolygon(element);
    446     },
    447
    448     /**
    449      * Updates properties of a {@link JXG.Polygon}'s rendering node.
    450      * @param {JXG.Polygon} element Reference to a {@link JXG.Polygon} object, that has to be updated.
    451      * @see Polygon
    452      * @see JXG.Polygon
    453      * @see JXG.AbstractRenderer#drawPolygon
    454      */
    455     updatePolygon: function (element) {
    456         // here originally strokecolor wasn't updated but strokewidth was
    457         // but if there's no strokecolor i don't see why we should update strokewidth.
    458         this._updateVisual(element, {stroke: true, dash: true});
    459         this.updatePolygonPrim(element.rendNode, element);
    460     },
    461
    462     /* **************************
    463      *    Text related stuff
    464      * **************************/
    465
    466     /**
    467      * Shows a small copyright notice in the top left corner of the board.
    468      * @param {String} str The copyright notice itself
    469      * @param {Number} fontsize Size of the font the copyright notice is written in
    470      */
    471     displayCopyright: function (str, fontsize) { /* stub */ },
    472
    473     /**
    474      * An internal text is a {@link JXG.Text} element which is drawn using only
    475      * the given renderer but no HTML. This method is only a stub, the drawing
    476      * is done in the special renderers.
    477      * @param {JXG.Text} element Reference to a {@link JXG.Text} object
    478      * @see Text
    479      * @see JXG.Text
    480      * @see JXG.AbstractRenderer#updateInternalText
    481      * @see JXG.AbstractRenderer#drawText
    482      * @see JXG.AbstractRenderer#updateText
    483      * @see JXG.AbstractRenderer#updateTextStyle
    484      */
    485     drawInternalText: function (element) { /* stub */ },
    486
    487     /**
    488      * Updates visual properties of an already existing {@link JXG.Text} element.
    489      * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be updated.
    490      * @see Text
    491      * @see JXG.Text
    492      * @see JXG.AbstractRenderer#drawInternalText
    493      * @see JXG.AbstractRenderer#drawText
    494      * @see JXG.AbstractRenderer#updateText
    495      * @see JXG.AbstractRenderer#updateTextStyle
    496      */
    497     updateInternalText: function (element) { /* stub */ },
    498
    499     /**
    500      * Displays a {@link JXG.Text} on the {@link JXG.Board} by putting a HTML div over it.
    501      * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be displayed
    502      * @see Text
    503      * @see JXG.Text
    504      * @see JXG.AbstractRenderer#drawInternalText
    505      * @see JXG.AbstractRenderer#updateText
    506      * @see JXG.AbstractRenderer#updateInternalText
    507      * @see JXG.AbstractRenderer#updateTextStyle
    508      */
    509     drawText: function (element) {
    510         var node, z;
    511
    512         if (element.visProp.display === 'html') {
    513             node = this.container.ownerDocument.createElement('div');
    514             node.style.position = 'absolute';
    515
    516             node.className = element.visProp.cssclass;
    517             if (this.container.style.zIndex=='') {
    518                 z = 0;
    519             } else {
    520                 z = parseInt(this.container.style.zIndex);
    521             }
    522
    523             node.style.zIndex = z+element.board.options.layer.text; //'10';
    524             this.container.appendChild(node);
    525             node.setAttribute('id', this.container.id + '_' + element.id);
    526         } else {
    527             node = this.drawInternalText(element);
    528         }
    529
    530         element.rendNode = node;
    531         element.htmlStr = '';
    532         this.updateText(element);
    533     },
    534
    535     /**
    536      * Updates visual properties of an already existing {@link JXG.Text} element.
    537      * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be updated.
    538      * @see Text
    539      * @see JXG.Text
    540      * @see JXG.AbstractRenderer#drawText
    541      * @see JXG.AbstractRenderer#drawInternalText
    542      * @see JXG.AbstractRenderer#updateInternalText
    543      * @see JXG.AbstractRenderer#updateTextStyle
    544      */
    545     updateText: function (element) {
    546         var content = element.plaintext;
    547
    548         if (element.visProp.visible) {
    549             this.updateTextStyle(element);
    550
    551             if (element.visProp.display === 'html') {
    552                 if (!isNaN(element.coords.scrCoords[1] + element.coords.scrCoords[2])) {
    553                     element.rendNode.style.left = parseInt(element.coords.scrCoords[1]) + 'px';
    554                     element.rendNode.style.top = parseInt(element.coords.scrCoords[2] - parseInt(element.visProp.fontsize) + this.vOffsetText) + 'px';
    555                 }
    556
    557                 if (element.htmlStr !== content) {
    558                     element.rendNode.innerHTML = content;
    559                     element.htmlStr = content;
    560
    561                     if (element.visProp.usemathjax) {
    562                         MathJax.Hub.Typeset(element.rendNode);
    563                     } else if (element.visProp.useasciimathml) {
    564                         AMprocessNode(element.rendNode, false);
    565                     }
    566                 }
    567                 this.transformImage(element, element.transformations);
    568             } else {
    569                 this.updateInternalText(element);
    570             }
    571         }
    572     },
    573
    574     /**
    575      * Updates CSS style properties of a {@link JXG.Text} node.
    576      * @param {JXG.Text} element Reference to the {@link JXG.Text} object, that has to be updated.
    577      * @see Text
    578      * @see JXG.Text
    579      * @see JXG.AbstractRenderer#drawText
    580      * @see JXG.AbstractRenderer#drawInternalText
    581      * @see JXG.AbstractRenderer#updateText
    582      * @see JXG.AbstractRenderer#updateInternalText
    583      */
    584     updateTextStyle: function (element) {
    585         var fs = JXG.evaluate(element.visProp.fontsize);
    586
    587         if (element.visProp.display === 'html' || this.type != 'canvas') {
    588         //if (element.visProp.display === 'html') {
    589             //element.rendNode.setAttribute("class", element.visProp.cssclass);
    590             element.rendNode.className = element.visProp.cssclass;
    591             try {
    592                 element.rendNode.style.fontSize = fs + 'px';
    593             } catch (e) {
    594                 // IE needs special treatment.
    595                 element.rendNode.style.fontSize = fs;
    596             }
    597         }
    598         this.setObjectStrokeColor(element, element.visProp.strokecolor, element.visProp.strokeopacity);
    599     },
    600
    601     /* **************************
    602      *    Image related stuff
    603      * **************************/
    604
    605     /**
    606      * Draws an {@link JXG.Image} on a board; This is just a template that has to be implemented by special renderers.
    607      * @param {JXG.Image} element Reference to the image object that is to be drawn
    608      * @see Image
    609      * @see JXG.Image
    610      * @see JXG.AbstractRenderer#updateImage
    611      */
    612     drawImage: function (element) { /* stub */ },
    613
    614     /**
    615      * Updates the properties of an {@link JXG.Image} element.
    616      * @param {JXG.Image} element Reference to an {@link JXG.Image} object, that has to be updated.
    617      * @see Image
    618      * @see JXG.Image
    619      * @see JXG.AbstractRenderer#drawImage
    620      */
    621     updateImage: function (element) {
    622         this.updateRectPrim(element.rendNode, element.coords.scrCoords[1], element.coords.scrCoords[2] - element.size[1],
    623                 element.size[0], element.size[1]);
    624
    625         this.updateImageURL(element);
    626         this.transformImage(element, element.transformations);
    627         this._updateVisual(element, {stroke: true, dash: true}, true);
    628     },
    629
    630     /**
    631      * Multiplication of transformations without updating. That means, at that point it is expected that the matrices
    632      * contain numbers only. First, the origin in user coords is translated to <tt>(0,0)</tt> in screen coords.
    633      * Then, the stretch factors are divided out. After the transformations in user coords, the  stretch factors
    634      * are multiplied in again, and the origin in user coords is translated back to its position.
    635      * This method does not have to be implemented in a new renderer.
    636      * @param {JXG.GeometryElement} element A JSXGraph element. We only need its board property.
    637      * @param {Array} transformations An array of JXG.Transformations.
    638      * @returns {Array} A matrix represented by a two dimensional array of numbers.
    639      * @see JXG.AbstractRenderer#transformImage
    640      */
    641     joinTransforms: function (element, transformations) {
    642         var m = [[1, 0, 0], [0, 1, 0], [0, 0, 1]],
    643             ox = element.board.origin.scrCoords[1],
    644             oy = element.board.origin.scrCoords[2],
    645             ux = element.board.unitX,
    646             uy = element.board.unitY,
    647             mpre1 =  [[1,   0, 0],      // Translate to 0,0 in screen coords
    648                       [-ox, 1, 0],
    649                       [-oy, 0, 1]],
    650             mpre2 =  [[1, 0,     0],    // Scale
    651                       [0, 1/ux,  0],
    652                       [0, 0, -1/uy]],
    653             mpost2 = [[1, 0,   0],      // Scale back
    654                       [0, ux,  0],
    655                       [0, 0, -uy]],
    656             mpost1 = [[1,  0, 0],       // Translate back
    657                       [ox, 1, 0],
    658                       [oy, 0, 1]],
    659             i, len = transformations.length;
    660
    661         for (i = 0; i < len; i++) {
    662             m = JXG.Math.matMatMult(mpre1, m);
    663             m = JXG.Math.matMatMult(mpre2, m);
    664             m = JXG.Math.matMatMult(transformations[i].matrix, m);
    665             m = JXG.Math.matMatMult(mpost2, m);
    666             m = JXG.Math.matMatMult(mpost1, m);
    667         }
    668         return m;
    669     },
    670
    671     /**
    672      * Applies transformations on images and text elements. This method is just a stub and has to be implemented in all
    673      * descendant classes where text and image transformations are to be supported.
    674      * @param {JXG.Image|JXG.Text} element A {@link JXG.Image} or {@link JXG.Text} object.
    675      * @param {Array} transformations An array of {@link JXG.Transformation} objects. This is usually the transformations property
    676      * of the given element <tt>el</tt>.
    677      */
    678     transformImage: function (element, transformations) { /* stub */ },
    679
    680     /**
    681      * If the URL of the image is provided by a function the URL has to be updated during updateImage()
    682      * @param {JXG.Image} element Reference to an image object.
    683      * @see JXG.AbstractRenderer#updateImage
    684      */
    685     updateImageURL: function (element) { /* stub */ },
    686
    687     /* **************************
    688      * Render primitive objects
    689      * **************************/
    690
    691     /**
    692      * Appends a node to a specific layer level. This is just an abstract method and has to be implemented
    693      * in all renderers that want to use the <tt>createPrim</tt> model to draw.
    694      * @param {Node} node A DOM tree node.
    695      * @param {Number} level The layer the node is attached to. This is the index of the layer in
    696      * {@link JXG.SVGRenderer#layer} or the <tt>z-index</tt> style property of the node in VMLRenderer.
    697      */
    698     appendChildPrim: function (node, level) { /* stub */ },
    699
    700     /**
    701      * Stores the rendering nodes. This is an abstract method which has to be implemented in all renderers that use
    702      * the <tt>createPrim</tt> method.
    703      * @param {JXG.GeometryElement} element A JSXGraph element.
    704      * @param {String} type The XML node name. Only used in VMLRenderer.
    705      */
    706     appendNodesToElement: function (element, type) { /* stub */ },
    707
    708     /**
    709      * Creates a node of a given type with a given id.
    710      * @param {String} type The type of the node to create.
    711      * @param {String} id Set the id attribute to this.
    712      * @returns {Node} Reference to the created node.
    713      */
    714     createPrim: function (type, id) {
    715         /* stub */
    716         return null;
    717     },
    718
    719     /**
    720      * Removes an element node. Just a stub.
    721      * @param {Node} node The node to remove.
    722      */
    723     remove: function (node) { /* stub */ },
    724
    725     /**
    726      * Can be used to create the nodes to display arrows. This is an abstract method which has to be implemented
    727      * in any descendant renderer.
    728      * @param {JXG.GeometryElement} element The element the arrows are to be attached to.
    729      */
    730     makeArrows: function(element) { /* stub */ },
    731
    732     /**
    733      * Updates an ellipse node primitive. This is an abstract method which has to be implemented in all renderers
    734      * that use the <tt>createPrim</tt> method.
    735      * @param {Node} node Reference to the node.
    736      * @param {Number} x Centre X coordinate
    737      * @param {Number} y Centre Y coordinate
    738      * @param {Number} rx The x-axis radius.
    739      * @param {Number} ry The y-axis radius.
    740      */
    741     updateEllipsePrim: function(node, x, y, rx, ry) { /* stub */ },
    742
    743     /**
    744      * Refreshes a line node. This is an abstract method which has to be implemented in all renderers that use
    745      * the <tt>createPrim</tt> method.
    746      * @param {Node} node The node to be refreshed.
    747      * @param {Number} p1x The first point's x coordinate.
    748      * @param {Number} p1y The first point's y coordinate.
    749      * @param {Number} p2x The second point's x coordinate.
    750      * @param {Number} p2y The second point's y coordinate.
    751      * @param {JXG.Board} board
    752      */
    753     updateLinePrim: function(node, p1x, p1y, p2x, p2y, board) { /* stub */ },
    754
    755     /**
    756      * Updates a path element. This is an abstract method which has to be implemented in all renderers that use
    757      * the <tt>createPrim</tt> method.
    758      * @param {Node} node The path node.
    759      * @param {String} pathString A string formatted like e.g. <em>'M 1,2 L 3,1 L5,5'</em>. The format of the string
    760      * depends on the rendering engine.
    761      * @param {JXG.Board} board Reference to the element's board.
    762      */
    763     updatePathPrim: function (node, pathString, board) { /* stub */ },
    764
    765     /**
    766      * Builds a path data string to draw a point with a face other than <em>rect</em> and <em>circle</em>. Since
    767      * the format of such a string usually depends on the renderer this method
    768      * is only an abstract method. Therefore, it has to be implemented in the descendant renderer itself unless
    769      * the renderer does not use the createPrim interface but the draw* interfaces to paint.
    770      * @param {JXG.Point} element The point element
    771      * @param {Number} size A positive number describing the size. Usually the half of the width and height of
    772      * the drawn point.
    773      * @param {String} type A string describing the point's face. This method only accepts the shortcut version of
    774      * each possible face: <tt>x, +, <>, ^, v, >, <
    775      */
    776     updatePathStringPoint: function (element, size, type) { /* stub */ },
    777
    778     /**
    779      * Builds a path data string from a {@link JXG.Curve} element. Since the path data strings heavily depend on the
    780      * underlying rendering technique this method is just a stub. Although such a path string is of no use for the
    781      * CanvasRenderer, this method is used there to draw a path directly.
    782      * @param element
    783      */
    784     updatePathStringPrim: function (element) { /* stub */ },
    785
    786     /**
    787      * Builds a path data string from a {@link JXG.Curve} element such that the curve looks like
    788      * hand drawn.
    789      * Since the path data strings heavily depend on the
    790      * underlying rendering technique this method is just a stub. Although such a path string is of no use for the
    791      * CanvasRenderer, this method is used there to draw a path directly.
    792      * @param element
    793      */
    794     updatePathStringBezierPrim: function (element) { /* stub */ },
    795
    796
    797     /**
    798      * Update a polygon primitive.
    799      * @param {Node} node
    800      * @param {JXG.Polygon} element A JSXGraph element of type {@link JXG.Polygon}
    801      */
    802     updatePolygonPrim: function (node, element) { /* stub */ },
    803
    804     /**
    805      * Update a rectangle primitive. This is used only for points with face of type 'rect'.
    806      * @param {Node} node The node yearning to be updated.
    807      * @param {Number} x x coordinate of the top left vertex.
    808      * @param {Number} y y coordinate of the top left vertex.
    809      * @param {Number} w Width of the rectangle.
    810      * @param {Number} h The rectangle's height.
    811      */
    812     updateRectPrim: function(node, x, y, w, h) { /* stub */ },
    813
    814     /* **************************
    815      *  Set Attributes
    816      * **************************/
    817
    818     /**
    819      * Sets a node's attribute.
    820      * @param {Node} node The node that is to be updated.
    821      * @param {String} key Name of the attribute.
    822      * @param {String} val New value for the attribute.
    823      */
    824     setPropertyPrim: function (node, key, val) { /* stub */ },
    825
    826     /**
    827      * Shows a hidden element on the canvas; Only a stub, requires implementation in the derived renderer.
    828      * @param {JXG.GeometryElement} element Reference to the object that has to appear.
    829      * @see JXG.AbstractRenderer#hide
    830      */
    831     show: function (element) { /* stub */ },
    832
    833     /**
    834      * Hides an element on the canvas; Only a stub, requires implementation in the derived renderer.
    835      * @param {JXG.GeometryElement} element Reference to the geometry element that has to disappear.
    836      * @see JXG.AbstractRenderer#show
    837      */
    838     hide: function (element) { /* stub */ },
    839
    840     /**
    841      * Sets the buffering as recommended by SVGWG. Until now only Opera supports this and will be ignored by
    842      * other browsers. Although this feature is only supported by SVG we have this method in {@link JXG.AbstractRenderer}
    843      * because it is called from outside the renderer.
    844      * @param {Node} node The SVG DOM Node which buffering type to update.
    845      * @param {String} type Either 'auto', 'dynamic', or 'static'. For an explanation see
    846      *   {@link http://www.w3.org/TR/SVGTiny12/painting.html#BufferedRenderingProperty}.
    847      */
    848     setBuffering: function (node, type) { /* stub */ },
    849
    850     /**
    851      * Sets an element's dash style.
    852      * @param {JXG.GeometryElement} element An JSXGraph element.
    853      */
    854     setDashStyle: function (element) { /* stub */ },
    855
    856     /**
    857      * Puts an object into draft mode, i.e. it's visual appearance will be changed. For GEONE<sub>x</sub>T backwards compatibility.
    858      * @param {JXG.GeometryElement} element Reference of the object that is in draft mode.
    859      */
    860     setDraft: function (element) {
    861         if (!element.visProp.draft) {
    862             return;
    863         }
    864         var draftColor = element.board.options.elements.draft.color,
    865             draftOpacity = element.board.options.elements.draft.opacity;
    866
    867         if (element.type === JXG.OBJECT_TYPE_POLYGON) {
    868             this.setObjectFillColor(element, draftColor, draftOpacity);
    869         }
    870         else {
    871             if (element.elementClass === JXG.OBJECT_CLASS_POINT) {
    872                 this.setObjectFillColor(element, draftColor, draftOpacity);
    873             }
    874             else {
    875                 this.setObjectFillColor(element, 'none', 0);
    876             }
    877             this.setObjectStrokeColor(element, draftColor, draftOpacity);
    878             this.setObjectStrokeWidth(element, element.board.options.elements.draft.strokeWidth);
    879         }
    880     },
    881
    882     /**
    883      * Puts an object from draft mode back into normal mode.
    884      * @param {JXG.GeometryElement} element Reference of the object that no longer is in draft mode.
    885      */
    886     removeDraft: function (element) {
    887         if (element.type === JXG.OBJECT_TYPE_POLYGON) {
    888             this.setObjectFillColor(element, element.visProp.fillcolor, element.visProp.fillopacity);
    889         }
    890         else {
    891             if (element.type === JXG.OBJECT_CLASS_POINT) {
    892                 this.setObjectFillColor(element, element.visProp.fillcolor, element.visProp.fillopacity);
    893             }
    894             this.setObjectStrokeColor(element, element.visProp.strokecolor, element.visProp.strokeopacity);
    895             this.setObjectStrokeWidth(element, element.visProp.strokewidth);
    896         }
    897     },
    898
    899     /**
    900      * Sets up nodes for rendering a gradient fill.
    901      * @param element
    902      */
    903     setGradient: function (element) { /* stub */ },
    904
    905     /**
    906      * Updates the gradient fill.
    907      * @param {JXG.GeometryElement} element An JSXGraph element with an area that can be filled.
    908      */
    909     updateGradient: function (element) { /* stub */ },
    910
    911     /**
    912      * Sets an objects fill color.
    913      * @param {JXG.GeometryElement} element Reference of the object that wants a new fill color.
    914      * @param {String} color Color in a HTML/CSS compatible format. If you don't want any fill color at all, choose 'none'.
    915      * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1.
    916      */
    917     setObjectFillColor: function (element, color, opacity) { /* stub */ },
    918
    919     /**
    920      * Changes an objects stroke color to the given color.
    921      * @param {JXG.GeometryElement} element Reference of the {@link JXG.GeometryElement} that gets a new stroke color.
    922      * @param {String} color Color value in a HTML compatible format, e.g. <strong>#00ff00</strong> or <strong>green</strong> for green.
    923      * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1.
    924      */
    925     setObjectStrokeColor: function (element, color, opacity) { /* stub */ },
    926
    927     /**
    928      * Sets an element's stroke width.
    929      * @param {JXG.GeometryElement} element Reference to the geometry element.
    930      * @param {Number} width The new stroke width to be assigned to the element.
    931      */
    932     setObjectStrokeWidth: function (element, width) { /* stub */ },
    933
    934     /**
    935      * Sets the shadow properties to a geometry element. This method is only a stub, it is implemented in the actual renderers.
    936      * @param {JXG.GeometryElement} element Reference to a geometry object, that should get a shadow
    937      */
    938     setShadow: function (element) { /* stub */ },
    939
    940     /**
    941      * Highlights an object, i.e. changes the current colors of the object to its highlighting colors
    942      * @param {JXG.GeometryElement} element Reference of the object that will be highlighted.
    943      * @returns {JXG.AbstractRenderer} Reference to the renderer
    944      */
    945     highlight: function (element) {
    946         var i, ev = element.visProp;
    947
    948         if (!ev.draft) {
    949             /*
    950                // Why did we have this? A.W.
    951             if (element.elementClass === JXG.OBJECT_CLASS_POINT) {
    952                 this.setObjectStrokeColor(element, ev.highlightstrokecolor, ev.highlightstrokeopacity);
    953                 this.setObjectFillColor(element, ev.highlightstrokecolor, ev.highlightstrokeopacity);
    954             } else
    955             */
    956             if (element.type === JXG.OBJECT_TYPE_POLYGON) {
    957                 this.setObjectFillColor(element, ev.highlightfillcolor, ev.highlightfillopacity);
    958                 for (i = 0; i < element.borders.length; i++) {
    959                     this.setObjectStrokeColor(element.borders[i], element.borders[i].visProp.highlightstrokecolor, element.borders[i].visProp.highlightstrokeopacity);
    960                 }
    961             } else {
    962                 this.setObjectStrokeColor(element, ev.highlightstrokecolor, ev.highlightstrokeopacity);
    963                 this.setObjectFillColor(element, ev.highlightfillcolor, ev.highlightfillopacity);
    964             }
    965             if (element.type === JXG.OBJECT_TYPE_TEXT) {
    966                 if (element.visProp.display === 'html') {
    967                     element.rendNode.className = element.visProp.highlightcssclass;
    968                 }
    969             }
    970             if (ev.highlightstrokewidth) {
    971                 this.setObjectStrokeWidth(element, Math.max(ev.highlightstrokewidth, ev.strokewidth));
    972             }
    973         }
    974
    975         return this;
    976     },
    977
    978     /**
    979      * Uses the normal colors of an object, i.e. the opposite of {@link JXG.AbstractRenderer#highlight}.
    980      * @param {JXG.GeometryElement} element Reference of the object that will get its normal colors.
    981      * @returns {JXG.AbstractRenderer} Reference to the renderer
    982      */
    983     noHighlight: function (element) {
    984         var i, ev = element.visProp;
    985
    986         if (!element.visProp.draft) {
    987             /*
    988                // Why did we have this? A.W.
    989               if (element.elementClass === JXG.OBJECT_CLASS_POINT) {
    990                 this.setObjectStrokeColor(element, ev.strokecolor, ev.strokeopacity);
    991                 this.setObjectFillColor(element, ev.strokecolor, ev.strokeopacity);
    992             } else
    993             */
    994             if (element.type === JXG.OBJECT_TYPE_POLYGON) {
    995                 this.setObjectFillColor(element, ev.fillcolor, ev.fillopacity);
    996                 for (i = 0; i < element.borders.length; i++) {
    997                     this.setObjectStrokeColor(element.borders[i], element.borders[i].visProp.strokecolor, element.borders[i].visProp.strokeopacity);
    998                 }
    999             } else {
    1000                 this.setObjectStrokeColor(element, ev.strokecolor, ev.strokeopacity);
    1001                 this.setObjectFillColor(element, ev.fillcolor, ev.fillopacity);
    1002             }
    1003             if (element.type === JXG.OBJECT_TYPE_TEXT) {
    1004                 if (element.visProp.display === 'html') {
    1005                     element.rendNode.className = element.visProp.cssclass;
    1006                 }
    1007             }
    1008             this.setObjectStrokeWidth(element, ev.strokewidth);
    1009         }
    1010
    1011         return this;
    1012     },
    1013
    1014
    1015     /* **************************
    1016      * renderer control
    1017      * **************************/
    1018
    1019     /**
    1020      * Stop redraw. This method is called before every update, so a non-vector-graphics based renderer
    1021      * can use this method to delete the contents of the drawing panel. This is an abstract method every
    1022      * descendant renderer should implement, if appropriate.
    1023      * @see JXG.AbstractRenderer#unsuspendRedraw
    1024      */
    1025     suspendRedraw: function () { /* stub */ },
    1026
    1027     /**
    1028      * Restart redraw. This method is called after updating all the rendering node attributes.
    1029      * @see JXG.AbstractRenderer#suspendRedraw
    1030      */
    1031     unsuspendRedraw: function () { /* stub */ },
    1032
    1033     /**
    1034      * The tiny zoom bar shown on the bottom of a board (if showNavigation on board creation is true).
    1035      * @param {JXG.Board} board Reference to a JSXGraph board.
    1036      */
    1037     drawZoomBar: function (board) {
    1038         var doc,
    1039             node,
    1040             createButton = function (label, handler) {
    1041                 var button;
    1042
    1043                 button = doc.createElement('span');
    1044                 node.appendChild(button);
    1045                 button.appendChild(document.createTextNode(label));
    1046                 /* button.innerHTML = label; */ // Does not work in XHTML
    1047                 JXG.addEvent(button, 'click', handler, board);
    1048             };
    1049
    1050         doc = board.containerObj.ownerDocument;
    1051         node = doc.createElement('div');
    1052
    1053         node.setAttribute('id', board.containerObj.id + '_navigationbar');
    1054
    1055         node.style.color = board.options.navbar.strokeColor;
    1056         node.style.backgroundColor = board.options.navbar.fillColor;
    1057         node.style.padding = board.options.navbar.padding;
    1058         node.style.position = board.options.navbar.position;
    1059         node.style.fontSize = board.options.navbar.fontSize;
    1060         node.style.cursor = board.options.navbar.cursor;
    1061         node.style.zIndex = board.options.navbar.zIndex;
    1062         board.containerObj.appendChild(node);
    1063         node.style.right = board.options.navbar.right;
    1064         node.style.bottom = board.options.navbar.bottom;
    1065
    1066         // For XHTML we need unicode instead of HTML entities
    1067         createButton('\u00A0\u2013\u00A0', board.zoomOut);
    1068         createButton('\u00A0o\u00A0', board.zoom100);
    1069         createButton('\u00A0+\u00A0', board.zoomIn);
    1070         createButton('\u00A0\u2190\u00A0', board.clickLeftArrow);
    1071         createButton('\u00A0\u2193\u00A0', board.clickUpArrow);
    1072         createButton('\u00A0\u2191\u00A0', board.clickDownArrow);
    1073         createButton('\u00A0\u2192\u00A0', board.clickRightArrow);
    1074     },
    1075
    1076     /**
    1077      * Wrapper for getElementById for maybe other renderers which elements are not directly accessible by DOM methods like document.getElementById().
    1078      * @param {String} id Unique identifier for element.
    1079      * @returns {Object} Reference to a JavaScript object. In case of SVG/VMLRenderer it's a reference to a SVG/VML node.
    1080      */
    1081     getElementById: function (id) {
    1082         return document.getElementById(this.container.id + '_' + id);
    1083     },
    1084
    1085     /**
    1086      * Resizes the rendering element
    1087      * @param {Number} w New width
    1088      * @param {Number} h New height
    1089      */
    1090     resize: function (w, h) { /* stub */}
    1091
    1092 });
    1093