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