      1 /*
      2     Copyright 2008-2022
      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, unparam: true*/
     41 /*eslint no-unused-vars: "off"*/
     42
     43 /* depends:
     44  jxg
     45  options
     46  base/coords
     47  base/constants
     48  math/math
     49  math/geometry
     50  utils/type
     51  utils/env
     52 */
     53
     54 /**
     55  * @fileoverview JSXGraph can use various technologies to render the contents of a construction, e.g.
     56  * SVG, VML, and HTML5 Canvas. To accomplish this, The rendering and the logic and control mechanisms
     57  * are completely separated from each other. Every rendering technology has it's own class, called
     58  * Renderer, e.g. SVGRenderer for SVG, the same for VML and Canvas. The common base for all available
     59  * renderers is the class AbstractRenderer defined in this file.
     60  */
     61
     62 define([
     63     'jxg', 'options', 'base/coords', 'base/constants', 'math/math', 'math/geometry', 'utils/type', 'utils/env'
     64 ], function (JXG, Options, Coords, Const, Mat, Geometry, Type, Env) {
     65
     66     "use strict";
     67
     68     /**
     69      * <p>This class defines the interface to the graphics part of JSXGraph. This class is an abstract class, it
     70      * actually does not render anything. This is up to the {@link JXG.SVGRenderer}, {@link JXG.VMLRenderer},
     71      * and {@link JXG.CanvasRenderer} classes. We strongly discourage you from using the methods in these classes
     72      * directly. Only the methods which are defined in this class and are not marked as private are guaranteed
     73      * to exist in any renderer instance you can access via {@link JXG.Board#renderer}. But not all methods may
     74      * work as expected.</p>
     75      * <p>The methods of this renderer can be divided into different categories:
     76      * <dl>
     77      *     <dt>Draw basic elements</dt>
     78      *     <dd>In this category we find methods to draw basic elements like {@link JXG.Point}, {@link JXG.Line},
     79      *     and {@link JXG.Curve} as well as assisting methods tightly bound to these basic painters. You do not
     80      *     need to implement these methods in a descendant renderer but instead implement the primitive drawing
     81      *     methods described below. This approach is encouraged when you're using a XML based rendering engine
     82      *     like VML and SVG. If you want to use a bitmap based rendering technique you are supposed to override
     83      *     these methods instead of the primitive drawing methods.</dd>
     84      *     <dt>Draw primitives</dt>
     85      *     <dd>This category summarizes methods to handle primitive nodes. As creation and management of these nodes
     86      *     is different among different the rendering techniques most of these methods are purely virtual and need
     87      *     proper implementation if you choose to not overwrite the basic element drawing methods.</dd>
     88      *     <dt>Attribute manipulation</dt>
     89      *     <dd>In XML based renders you have to manipulate XML nodes and their attributes to change the graphics.
     90      *     For that purpose attribute manipulation methods are defined to set the color, opacity, and other things.
     91      *     Please note that some of these methods are required in bitmap based renderers, too, because some elements
     92      *     like {@link JXG.Text} can be HTML nodes floating over the construction.</dd>
     93      *     <dt>Renderer control</dt>
     94      *     <dd>Methods to clear the drawing board or to stop and to resume the rendering engine.</dd>
     95      * </dl></p>
     96      * @class JXG.AbstractRenderer
     97      * @constructor
     98      * @see JXG.SVGRenderer
     99      * @see JXG.VMLRenderer
    100      * @see JXG.CanvasRenderer
    101      */
    102     JXG.AbstractRenderer = function () {
    103
    104         // WHY THIS IS A CLASS INSTEAD OF A SINGLETON OBJECT:
    105         //
    106         // The renderers need to keep track of some stuff which is not always the same on different boards,
    107         // like enhancedRendering, reference to the container object, and resolution in VML. Sure, those
    108         // things could be stored in board. But they are rendering related and JXG.Board is already very
    109         // very big.
    110         //
    111         // And we can't save the rendering related data in {SVG,VML,Canvas}Renderer and make only the
    112         // JXG.AbstractRenderer a singleton because of that:
    113         //
    114         // Given an object o with property a set to true
    115         //     var o = {a: true};
    116         // and a class c doing nothing
    117         //     c = function() {};
    118         // Set c's prototype to o
    119         //     c.prototype = o;
    120         // and create an instance of c we get i.a to be true
    121         //     i = new c();
    122         //     i.a;
    123         //     > true
    124         // But we can overwrite this property via
    125         //     c.prototype.a = false;
    126         //     i.a;
    127         //     > false
    128
    129         /**
    130          * The vertical offset for {@link Text} elements. Every {@link Text} element will
    131          * be placed this amount of pixels below the user given coordinates.
    132          * @type Number
    133          * @default 0
    134          */
    135         this.vOffsetText = 0;
    136
    137         /**
    138          * If this property is set to <tt>true</tt> the visual properties of the elements are updated
    139          * on every update. Visual properties means: All the stuff stored in the
    140          * {@link JXG.GeometryElement#visProp} property won't be set if enhancedRendering is <tt>false</tt>
    141          * @type Boolean
    142          * @default true
    143          */
    144         this.enhancedRendering = true;
    145
    146         /**
    147          * The HTML element that stores the JSXGraph board in it.
    148          * @type Node
    149          */
    150         this.container = null;
    151
    152         /**
    153          * This is used to easily determine which renderer we are using
    154          * @example if (board.renderer.type === 'vml') {
    155           *     // do something
    156          * }
    157          * @type String
    158          */
    159         this.type = '';
    160
    161         /**
    162          * True if the browsers' SVG engine supports foreignObject.
    163          * Not supported browsers are IE 9 - 11.
    164          * All other browsers return ture, since it is tested with
    165          * document.implementation.hasFeature() which is deprecated.
    166          *
    167          * @type Boolean
    168          * @private
    169          */
    170         this.supportsForeignObject = false;
    171
    172     };
    173
    174     JXG.extend(JXG.AbstractRenderer.prototype, /** @lends JXG.AbstractRenderer.prototype */ {
    175
    176         /* ******************************** *
    177          *    private methods               *
    178          *    should not be called from     *
    179          *    outside AbstractRenderer      *
    180          * ******************************** */
    181
    182         /**
    183          * Update visual properties, but only if {@link JXG.AbstractRenderer#enhancedRendering} or <tt>enhanced</tt> is set to true.
    184          * @param {JXG.GeometryElement} el The element to update
    185          * @param {Object} [not={}] Select properties you don't want to be updated: <tt>{fill: true, dash: true}</tt> updates
    186          * everything except for fill and dash. Possible values are <tt>stroke, fill, dash, shadow, gradient</tt>.
    187          * @param {Boolean} [enhanced=false] If true, {@link JXG.AbstractRenderer#enhancedRendering} is assumed to be true.
    188          * @private
    189          */
    190         _updateVisual: function (el, not, enhanced) {
    191             if (enhanced || this.enhancedRendering) {
    192                 not = not || {};
    193
    194                 this.setObjectTransition(el);
    195                 if (!Type.evaluate(el.visProp.draft)) {
    196                     if (!not.stroke) {
    197                         if (el.highlighted) {
    198                             this.setObjectStrokeColor(el,
    199                                 el.visProp.highlightstrokecolor,
    200                                 el.visProp.highlightstrokeopacity);
    201                             this.setObjectStrokeWidth(el, el.visProp.highlightstrokewidth);
    202                         } else {
    203                             this.setObjectStrokeColor(el,
    204                                 el.visProp.strokecolor,
    205                                 el.visProp.strokeopacity);
    206                             this.setObjectStrokeWidth(el, el.visProp.strokewidth);
    207                         }
    208                     }
    209
    210                     if (!not.fill) {
    211                         if (el.highlighted) {
    212                             this.setObjectFillColor(el,
    213                                 el.visProp.highlightfillcolor,
    214                                 el.visProp.highlightfillopacity);
    215                         } else {
    216                             this.setObjectFillColor(el,
    217                                 el.visProp.fillcolor,
    218                                 el.visProp.fillopacity);
    219                         }
    220                     }
    221
    222                     if (!not.dash) {
    223                         this.setDashStyle(el, el.visProp);
    224                     }
    225
    226                     if (!not.shadow) {
    227                         this.setShadow(el);
    228                     }
    229
    230                     if (!not.gradient) {
    231                         this.setShadow(el);
    232                     }
    233
    234                     if (!not.tabindex) {
    235                         this.setTabindex(el);
    236                     }
    237                 } else {
    238                     this.setDraft(el);
    239                 }
    240             }
    241         },
    242
    243         /**
    244          * Get information if element is highlighted.
    245          * @param {JXG.GeometryElement} el The element which is tested for being highlighted.
    246          * @returns {String} 'highlight' if highlighted, otherwise the ampty string '' is returned.
    247          * @private
    248          */
    249         _getHighlighted: function(el) {
    250             var isTrace = false,
    251                 hl;
    252
    253             if (!Type.exists(el.board) || !Type.exists(el.board.highlightedObjects)) {
    254                 // This case handles trace elements.
    255                 // To make them work, we simply neglect highlighting.
    256                 isTrace = true;
    257             }
    258
    259             if (!isTrace && Type.exists(el.board.highlightedObjects[el.id])) {
    260                 hl = 'highlight';
    261             } else {
    262                 hl = '';
    263             }
    264             return hl;
    265         },
    266
    267         /* ******************************** *
    268          *    Point drawing and updating    *
    269          * ******************************** */
    270
    271         /**
    272          * Draws a point on the {@link JXG.Board}.
    273          * @param {JXG.Point} el Reference to a {@link JXG.Point} object that has to be drawn.
    274          * @see Point
    275          * @see JXG.Point
    276          * @see JXG.AbstractRenderer#updatePoint
    277          * @see JXG.AbstractRenderer#changePointStyle
    278          */
    279         drawPoint: function (el) {
    280             var prim,
    281                 // sometimes el is not a real point and lacks the methods of a JXG.Point instance,
    282                 // in these cases to not use el directly.
    283                 face = Options.normalizePointFace(Type.evaluate(el.visProp.face));
    284
    285             // determine how the point looks like
    286             if (face === 'o') {
    287                 prim = 'ellipse';
    288             } else if (face === '[]') {
    289                 prim = 'rect';
    290             } else {
    291                 // cross/x, diamond/<>, triangleup/a/^, triangledown/v, triangleleft/<,
    292                 // triangleright/>, plus/+,
    293                 prim = 'path';
    294             }
    295
    296             el.rendNode = this.appendChildPrim(this.createPrim(prim, el.id), Type.evaluate(el.visProp.layer));
    297             this.appendNodesToElement(el, prim);
    298
    299             // adjust visual propertys
    300             this._updateVisual(el, {dash: true, shadow: true}, true);
    301
    302             // By now we only created the xml nodes and set some styles, in updatePoint
    303             // the attributes are filled with data.
    304             this.updatePoint(el);
    305         },
    306
    307         /**
    308          * Updates visual appearance of the renderer element assigned to the given {@link JXG.Point}.
    309          * @param {JXG.Point} el Reference to a {@link JXG.Point} object, that has to be updated.
    310          * @see Point
    311          * @see JXG.Point
    312          * @see JXG.AbstractRenderer#drawPoint
    313          * @see JXG.AbstractRenderer#changePointStyle
    314          */
    315         updatePoint: function (el) {
    316             var size = Type.evaluate(el.visProp.size),
    317                 // sometimes el is not a real point and lacks the methods of a JXG.Point instance,
    318                 // in these cases to not use el directly.
    319                 face = Options.normalizePointFace(Type.evaluate(el.visProp.face)),
    320                 unit = Type.evaluate(el.visProp.sizeunit),
    321                 zoom = Type.evaluate(el.visProp.zoom),
    322                 s1;
    323
    324             if (!isNaN(el.coords.scrCoords[2] + el.coords.scrCoords[1])) {
    325                 if (unit === 'user') {
    326                     size *= Math.sqrt(el.board.unitX * el.board.unitY);
    327                 }
    328                 size *= ((!el.board || !zoom) ?
    329                     1.0 : Math.sqrt(el.board.zoomX * el.board.zoomY));
    330                 s1 = (size === 0) ? 0 : size + 1;
    331
    332                 if (face === 'o') { // circle
    333                     this.updateEllipsePrim(el.rendNode, el.coords.scrCoords[1],
    334                          el.coords.scrCoords[2], s1, s1);
    335                 } else if (face === '[]') { // rectangle
    336                     this.updateRectPrim(el.rendNode, el.coords.scrCoords[1] - size,
    337                          el.coords.scrCoords[2] - size, size * 2, size * 2);
    338                 } else { // x, +, <>, ^, v, <, >
    339                     this.updatePathPrim(el.rendNode,
    340                         this.updatePathStringPoint(el, size, face), el.board);
    341                 }
    342                 this._updateVisual(el, {dash: false, shadow: false});
    343                 this.setShadow(el);
    344             }
    345         },
    346
    347         /**
    348          * Changes the style of a {@link JXG.Point}. This is required because the point styles differ in what
    349          * elements have to be drawn, e.g. if the point is marked by a "x" or a "+" two lines are drawn, if
    350          * it's marked by spot a circle is drawn. This method removes the old renderer element(s) and creates
    351          * the new one(s).
    352          * @param {JXG.Point} el Reference to a {@link JXG.Point} object, that's style is changed.
    353          * @see Point
    354          * @see JXG.Point
    355          * @see JXG.AbstractRenderer#updatePoint
    356          * @see JXG.AbstractRenderer#drawPoint
    357          */
    358         changePointStyle: function (el) {
    359             var node = this.getElementById(el.id);
    360
    361             // remove the existing point rendering node
    362             if (Type.exists(node)) {
    363                 this.remove(node);
    364             }
    365
    366             // and make a new one
    367             this.drawPoint(el);
    368             Type.clearVisPropOld(el);
    369
    370             if (!el.visPropCalc.visible) {
    371                 this.display(el, false);
    372             }
    373
    374             if (Type.evaluate(el.visProp.draft)) {
    375                 this.setDraft(el);
    376             }
    377         },
    378
    379         /* ******************************** *
    380          *           Lines                  *
    381          * ******************************** */
    382
    383         /**
    384          * Draws a line on the {@link JXG.Board}.
    385          * @param {JXG.Line} el Reference to a line object, that has to be drawn.
    386          * @see Line
    387          * @see JXG.Line
    388          * @see JXG.AbstractRenderer#updateLine
    389          */
    390         drawLine: function (el) {
    391             el.rendNode = this.appendChildPrim(this.createPrim('line', el.id),
    392                                     Type.evaluate(el.visProp.layer));
    393             this.appendNodesToElement(el, 'lines');
    394             this.updateLine(el);
    395         },
    396
    397         /**
    398          * Updates visual appearance of the renderer element assigned to the given {@link JXG.Line}.
    399          * @param {JXG.Line} el Reference to the {@link JXG.Line} object that has to be updated.
    400          * @see Line
    401          * @see JXG.Line
    402          * @see JXG.AbstractRenderer#drawLine
    403          */
    404         updateLine: function (el) {
    405             this._updateVisual(el);
    406             this.updatePathWithArrowHeads(el);  // Calls the renderer primitive
    407             this.setLineCap(el);
    408         },
    409
    410         /* **************************
    411          *    Curves
    412          * **************************/
    413
    414         /**
    415          * Draws a {@link JXG.Curve} on the {@link JXG.Board}.
    416          * @param {JXG.Curve} el Reference to a graph object, that has to be plotted.
    417          * @see Curve
    418          * @see JXG.Curve
    419          * @see JXG.AbstractRenderer#updateCurve
    420          */
    421         drawCurve: function (el) {
    422             el.rendNode = this.appendChildPrim(this.createPrim('path', el.id), Type.evaluate(el.visProp.layer));
    423             this.appendNodesToElement(el, 'path');
    424             this.updateCurve(el);
    425         },
    426
    427         /**
    428          * Updates visual appearance of the renderer element assigned to the given {@link JXG.Curve}.
    429          * @param {JXG.Curve} el Reference to a {@link JXG.Curve} object, that has to be updated.
    430          * @see Curve
    431          * @see JXG.Curve
    432          * @see JXG.AbstractRenderer#drawCurve
    433          */
    434         updateCurve: function (el) {
    435             this._updateVisual(el);
    436             this.updatePathWithArrowHeads(el); // Calls the renderer primitive
    437             this.setLineCap(el);
    438         },
    439
    440         /* **************************
    441          *    Arrow heads and related stuff
    442          * **************************/
    443
    444         /**
    445          * Handles arrow heads of a line or curve element and calls the renderer primitive.
    446          *
    447          * @param {JXG.GeometryElement} el Reference to a line or curve object that has to be drawn.
    448          * @param {Boolean} doHighlight
    449          *
    450          * @private
    451          * @see Line
    452          * @see JXG.Line
    453          * @see Curve
    454          * @see JXG.Curve
    455          * @see JXG.AbstractRenderer#updateLine
    456          * @see JXG.AbstractRenderer#updateCurve
    457          * @see JXG.AbstractRenderer#makeArrows
    458          * @see JXG.AbstractRenderer#getArrowHeadData
    459          */
    460         updatePathWithArrowHeads: function(el, doHighlight) {
    461             var ev = el.visProp,
    462                 hl = doHighlight ? 'highlight' : '',
    463                 w,
    464                 arrowData;
    465
    466             if (doHighlight && ev.highlightstrokewidth) {
    467                 w = Math.max(Type.evaluate(ev.highlightstrokewidth), Type.evaluate(ev.strokewidth));
    468             } else {
    469                 w = Type.evaluate(ev.strokewidth);
    470             }
    471
    472             // Get information if there are arrow heads and how large they are.
    473             arrowData = this.getArrowHeadData(el, w, hl);
    474
    475             // Create the SVG nodes if neccessary
    476             this.makeArrows(el, arrowData);
    477
    478             // Draw the paths with arrow heads
    479             if (el.elementClass === Const.OBJECT_CLASS_LINE) {
    480                 this.updateLineWithEndings(el, arrowData);
    481             } else if (el.elementClass === Const.OBJECT_CLASS_CURVE) {
    482                 this.updatePath(el);
    483             }
    484
    485             this.setArrowSize(el, arrowData);
    486         },
    487
    488         /**
    489          * This method determines some data about the line endings of this element.
    490          * If there are arrow heads, the offset is determined so that no parts of the line stroke
    491          * lap over the arrow head.
    492          * <p>
    493          * The returned object also contains the types of the arrow heads.
    494          *
    495          * @param {JXG.GeometryElement} el JSXGraph line or curve element
    496          * @param {Number} strokewidth strokewidth of the element
    497          * @param {String} hl Ither 'highlight' or empty string
    498          * @returns {Object} object containing the data
    499          *
    500          * @private
    501          */
    502         getArrowHeadData: function(el, strokewidth, hl) {
    503             var minlen = Mat.eps,
    504                 typeFirst, typeLast,
    505                 offFirst = 0,
    506                 offLast = 0,
    507                 sizeFirst = 0,
    508                 sizeLast = 0,
    509                 ev_fa = Type.evaluate(el.visProp.firstarrow),
    510                 ev_la = Type.evaluate(el.visProp.lastarrow),
    511                 off, size;
    512
    513             /*
    514                Handle arrow heads.
    515
    516                The default arrow head is an isosceles triangle with base length 10 units and height 10 units.
    517                These 10 units are scaled to strokeWidth * arrowSize pixels pixels.
    518             */
    519             if (ev_fa || ev_la) {
    520
    521                 if (Type.exists(ev_fa.type)) {
    522                     typeFirst = Type.evaluate(ev_fa.type);
    523                 } else {
    524                     if (el.elementClass === Const.OBJECT_CLASS_LINE) {
    525                         typeFirst = 1;
    526                     } else {
    527                         typeFirst = 7;
    528                     }
    529                 }
    530                 if (Type.exists(ev_la.type)) {
    531                     typeLast = Type.evaluate(ev_la.type);
    532                 } else {
    533                     if (el.elementClass === Const.OBJECT_CLASS_LINE) {
    534                         typeLast = 1;
    535                     } else {
    536                         typeLast = 7;
    537                     }
    538                 }
    539
    540                 if (ev_fa) {
    541                     size = 6;
    542                     if (Type.exists(ev_fa.size)) {
    543                         size = Type.evaluate(ev_fa.size);
    544                     }
    545                     if (hl !== '' && Type.exists(ev_fa[hl + 'size'])) {
    546                         size = Type.evaluate(ev_fa[hl + 'size']);
    547                     }
    548
    549                     off = strokewidth * size;
    550                     if (typeFirst === 2) {
    551                         off *= 0.5;
    552                         minlen += strokewidth * size;
    553                     } else if (typeFirst === 3) {
    554                         off = strokewidth * size / 3;
    555                         minlen += strokewidth;
    556                     } else if (typeFirst === 4 || typeFirst === 5 || typeFirst === 6) {
    557                         off = strokewidth * size / 1.5;
    558                         minlen += strokewidth * size;
    559                     } else if (typeFirst === 7) {
    560                         off = 0;
    561                         size = 10;
    562                         minlen += strokewidth;
    563                     } else {
    564                         minlen += strokewidth * size;
    565                     }
    566                     offFirst += off;
    567                     sizeFirst = size;
    568                 }
    569
    570                 if (ev_la) {
    571                     size = 6;
    572                     if (Type.exists(ev_la.size)) {
    573                         size = Type.evaluate(ev_la.size);
    574                     }
    575                     if (hl !== '' && Type.exists(ev_la[hl + 'size'])) {
    576                         size = Type.evaluate(ev_la[hl + 'size']);
    577                     }
    578                     off = strokewidth * size;
    579                     if (typeLast === 2) {
    580                         off *= 0.5;
    581                         minlen += strokewidth * size;
    582                     } else if (typeLast === 3) {
    583                         off = strokewidth * size / 3;
    584                         minlen += strokewidth;
    585                     } else if (typeLast === 4 || typeLast === 5 || typeLast === 6) {
    586                         off = strokewidth * size / 1.5;
    587                         minlen += strokewidth * size;
    588                     } else if (typeLast === 7) {
    589                         off = 0;
    590                         size = 10;
    591                         minlen += strokewidth;
    592                     } else {
    593                         minlen += strokewidth * size;
    594                     }
    595                     offLast += off;
    596                     sizeLast = size;
    597                 }
    598             }
    599             el.visPropCalc.typeFirst = typeFirst;
    600             el.visPropCalc.typeLast = typeLast;
    601
    602             return {
    603                 evFirst: ev_fa,
    604                 evLast: ev_la,
    605                 typeFirst: typeFirst,
    606                 typeLast: typeLast,
    607                 offFirst: offFirst,
    608                 offLast: offLast,
    609                 sizeFirst: sizeFirst,
    610                 sizeLast: sizeLast,
    611                 showFirst: 1, // Show arrow head. 0 if the distance is too small
    612                 showLast: 1,  // Show arrow head. 0 if the distance is too small
    613                 minLen: minlen,
    614                 strokeWidth: strokewidth
    615             };
    616         },
    617
    618         /**
    619          * Corrects the line length if there are arrow heads, such that
    620          * the arrow ends exactly at the intended position.
    621          * Calls the renderer method to draw the line.
    622          *
    623          * @param {JXG.Line} el Reference to a line object, that has to be drawn
    624          * @param {Object} arrowData Data concerning possible arrow heads
    625          *
    626          * @returns {JXG.AbstractRenderer} Reference to the renderer
    627          *
    628          * @private
    629          * @see Line
    630          * @see JXG.Line
    631          * @see JXG.AbstractRenderer#updateLine
    632          * @see JXG.AbstractRenderer#getPositionArrowHead
    633          *
    634          */
    635         updateLineWithEndings: function(el, arrowData) {
    636             var c1, c2,
    637                 // useTotalLength = true,
    638                 margin = null;
    639
    640             c1 = new Coords(Const.COORDS_BY_USER, el.point1.coords.usrCoords, el.board);
    641             c2 = new Coords(Const.COORDS_BY_USER, el.point2.coords.usrCoords, el.board);
    642             margin = Type.evaluate(el.visProp.margin);
    643             Geometry.calcStraight(el, c1, c2, margin);
    644
    645             this.handleTouchpoints(el, c1, c2, arrowData);
    646             this.getPositionArrowHead(el, c1, c2, arrowData);
    647
    648             this.updateLinePrim(el.rendNode,
    649                 c1.scrCoords[1], c1.scrCoords[2],
    650                 c2.scrCoords[1], c2.scrCoords[2], el.board);
    651
    652             return this;
    653         },
    654
    655         /**
    656          *
    657          * Calls the renderer method to draw a curve.
    658          *
    659          * @param {JXG.GeometryElement} el Reference to a line object, that has to be drawn.
    660          * @returns {JXG.AbstractRenderer} Reference to the renderer
    661          *
    662          * @private
    663          * @see Curve
    664          * @see JXG.Curve
    665          * @see JXG.AbstractRenderer#updateCurve
    666          *
    667          */
    668         updatePath: function(el) {
    669             if (Type.evaluate(el.visProp.handdrawing)) {
    670                 this.updatePathPrim(el.rendNode, this.updatePathStringBezierPrim(el), el.board);
    671             } else {
    672                 this.updatePathPrim(el.rendNode, this.updatePathStringPrim(el), el.board);
    673             }
    674
    675             return this;
    676         },
    677
    678         /**
    679          * Shorten the length of a line element such that the arrow head touches
    680          * the start or end point and such that the arrow head ends exactly
    681          * at the start / end position of the line.
    682          *
    683          * @param  {JXG.Line} el Reference to the line object that gets arrow heads.
    684          * @param  {JXG.Coords} c1  Coords of the first point of the line (after {@link JXG.Math.Geometry#calcStraight}).
    685          * @param  {JXG.Coords} c2  Coords of the second point of the line (after {@link JXG.Math.Geometry#calcStraight}).
    686          * @param  {Object}  a
    687          * @return {object} Object containing how much the line has to be shortened.
    688          * Data structure: {c1, c2, d1x, d1y, d2x, d2y, sFirst, sLast}. sFirst and sLast is the length by which
    689          * firstArrow and lastArrow have to shifted such that there is no gap between arrow head and line.
    690          * Additionally, if one of these values is zero, the arrow is not displayed. This is the case, if the
    691          * line length is very short.
    692          */
    693          getPositionArrowHead: function(el, c1, c2, a) {
    694             var d, d1x, d1y, d2x, d2y;
    695
    696             /*
    697                Handle arrow heads.
    698
    699                The default arrow head (type==1) is an isosceles triangle with base length 10 units and height 10 units.
    700                These 10 units are scaled to strokeWidth * arrowSize pixels pixels.
    701             */
    702             if (a.evFirst || a.evLast) {
    703                 // Correct the position of the arrow heads
    704                 d1x = d1y = d2x = d2y = 0.0;
    705                 d = c1.distance(Const.COORDS_BY_SCREEN, c2);
    706
    707                 if (a.evFirst &&
    708                     el.board.renderer.type !== 'vml') {
    709                     if (d >= a.minLen) {
    710                         d1x = (c2.scrCoords[1] - c1.scrCoords[1]) * a.offFirst / d;
    711                         d1y = (c2.scrCoords[2] - c1.scrCoords[2]) * a.offFirst / d;
    712                     } else {
    713                         a.showFirst = 0;
    714                     }
    715                 }
    716
    717                 if (a.evLast &&
    718                     el.board.renderer.type !== 'vml') {
    719                     if (d >= a.minLen) {
    720                         d2x = (c2.scrCoords[1] - c1.scrCoords[1]) * a.offLast / d;
    721                         d2y = (c2.scrCoords[2] - c1.scrCoords[2]) * a.offLast / d;
    722                     } else {
    723                         a.showLast = 0;
    724                     }
    725                 }
    726                 c1.setCoordinates(Const.COORDS_BY_SCREEN, [c1.scrCoords[1] + d1x, c1.scrCoords[2] + d1y], false, true);
    727                 c2.setCoordinates(Const.COORDS_BY_SCREEN, [c2.scrCoords[1] - d2x, c2.scrCoords[2] - d2y], false, true);
    728             }
    729
    730             return this;
    731         },
    732
    733         /**
    734          * Handle touchlastpoint / touchfirstpoint
    735          *
    736          * @param {JXG.GeometryElement} el
    737          * @param {JXG.Coords} c1 Coordinates of the start of the line. The coordinates are changed in place.
    738          * @param {JXG.Coords} c2 Coordinates of the end of the line. The coordinates are changed in place.
    739          * @param {Object} a
    740          */
    741         handleTouchpoints: function(el, c1, c2, a) {
    742             var s1, s2, d,
    743                 d1x, d1y, d2x, d2y;
    744
    745             if (a.evFirst || a.evLast) {
    746                 d = d1x = d1y = d2x = d2y = 0.0;
    747
    748                 s1 = Type.evaluate(el.point1.visProp.size) + Type.evaluate(el.point1.visProp.strokewidth);
    749                 s2 = Type.evaluate(el.point2.visProp.size) + Type.evaluate(el.point2.visProp.strokewidth);
    750
    751                 // Handle touchlastpoint /touchfirstpoint
    752                 if (a.evFirst && Type.evaluate(el.visProp.touchfirstpoint)) {
    753                     d = c1.distance(Const.COORDS_BY_SCREEN, c2);
    754                     //if (d > s) {
    755                         d1x = (c2.scrCoords[1] - c1.scrCoords[1]) * s1 / d;
    756                         d1y = (c2.scrCoords[2] - c1.scrCoords[2]) * s1 / d;
    757                     //}
    758                 }
    759                 if (a.evLast && Type.evaluate(el.visProp.touchlastpoint)) {
    760                     d = c1.distance(Const.COORDS_BY_SCREEN, c2);
    761                     //if (d > s) {
    762                         d2x = (c2.scrCoords[1] - c1.scrCoords[1]) * s2 / d;
    763                         d2y = (c2.scrCoords[2] - c1.scrCoords[2]) * s2 / d;
    764                     //}
    765                 }
    766                 c1.setCoordinates(Const.COORDS_BY_SCREEN, [c1.scrCoords[1] + d1x, c1.scrCoords[2] + d1y], false, true);
    767                 c2.setCoordinates(Const.COORDS_BY_SCREEN, [c2.scrCoords[1] - d2x, c2.scrCoords[2] - d2y], false, true);
    768             }
    769
    770             return this;
    771         },
    772
    773         /**
    774          * Set the arrow head size.
    775          *
    776          * @param {JXG.GeometryElement} el Reference to a line or curve object that has to be drawn.
    777          * @param {Object} arrowData Data concerning possible arrow heads
    778          * @returns {JXG.AbstractRenderer} Reference to the renderer
    779          *
    780          * @private
    781          * @see Line
    782          * @see JXG.Line
    783          * @see Curve
    784          * @see JXG.Curve
    785          * @see JXG.AbstractRenderer#updatePathWithArrowHeads
    786          * @see JXG.AbstractRenderer#getArrowHeadData
    787          */
    788         setArrowSize: function(el, a) {
    789             if (a.evFirst) {
    790                 this._setArrowWidth(el.rendNodeTriangleStart, a.showFirst * a.strokeWidth, el.rendNode, a.sizeFirst);
    791             }
    792             if (a.evLast) {
    793                 this._setArrowWidth(el.rendNodeTriangleEnd, a.showLast * a.strokeWidth, el.rendNode, a.sizeLast);
    794             }
    795             return this;
    796         },
    797
    798         /**
    799          * Update the line endings (linecap) of a straight line from its attribute
    800          * 'linecap'.
    801          * Possible values for the attribute 'linecap' are: 'butt', 'round', 'square'.
    802          * The default value is 'butt'. Not available for VML renderer.
    803          *
    804          * @param {JXG.Line} element A arbitrary line.
    805          * @see Line
    806          * @see JXG.Line
    807          * @see JXG.AbstractRenderer#updateLine
    808          */
    809         setLineCap: function(el) { /* stub */ },
    810
    811         /* **************************
    812          *    Ticks related stuff
    813          * **************************/
    814
    815         /**
    816          * Creates a rendering node for ticks added to a line.
    817          * @param {JXG.Line} el A arbitrary line.
    818          * @see Line
    819          * @see Ticks
    820          * @see JXG.Line
    821          * @see JXG.Ticks
    822          * @see JXG.AbstractRenderer#updateTicks
    823          */
    824         drawTicks: function (el) {
    825             el.rendNode = this.appendChildPrim(this.createPrim('path', el.id), Type.evaluate(el.visProp.layer));
    826             this.appendNodesToElement(el, 'path');
    827         },
    828
    829         /**
    830          * Update {@link Ticks} on a {@link JXG.Line}. This method is only a stub and has to be implemented
    831          * in any descendant renderer class.
    832          * @param {JXG.Ticks} element Reference of a ticks object that has to be updated.
    833          * @see Line
    834          * @see Ticks
    835          * @see JXG.Line
    836          * @see JXG.Ticks
    837          * @see JXG.AbstractRenderer#drawTicks
    838          */
    839         updateTicks: function (element) { /* stub */ },
    840
    841         /* **************************
    842          *    Circle related stuff
    843          * **************************/
    844
    845         /**
    846          * Draws a {@link JXG.Circle}
    847          * @param {JXG.Circle} el Reference to a {@link JXG.Circle} object that has to be drawn.
    848          * @see Circle
    849          * @see JXG.Circle
    850          * @see JXG.AbstractRenderer#updateEllipse
    851          */
    852         drawEllipse: function (el) {
    853             el.rendNode = this.appendChildPrim(this.createPrim('ellipse', el.id),
    854                                     Type.evaluate(el.visProp.layer));
    855             this.appendNodesToElement(el, 'ellipse');
    856             this.updateEllipse(el);
    857         },
    858
    859         /**
    860          * Updates visual appearance of a given {@link JXG.Circle} on the {@link JXG.Board}.
    861          * @param {JXG.Circle} el Reference to a {@link JXG.Circle} object, that has to be updated.
    862          * @see Circle
    863          * @see JXG.Circle
    864          * @see JXG.AbstractRenderer#drawEllipse
    865          */
    866         updateEllipse: function (el) {
    867             this._updateVisual(el);
    868
    869             var radius = el.Radius();
    870
    871             if (radius > 0.0 &&
    872                     Math.abs(el.center.coords.usrCoords[0]) > Mat.eps &&
    873                     !isNaN(radius + el.center.coords.scrCoords[1] + el.center.coords.scrCoords[2]) &&
    874                     radius * el.board.unitX < 2000000) {
    875                 this.updateEllipsePrim(el.rendNode, el.center.coords.scrCoords[1],
    876                     el.center.coords.scrCoords[2],
    877                     (radius * el.board.unitX),
    878                     (radius * el.board.unitY));
    879             }
    880         },
    881
    882         /* **************************
    883          *   Polygon related stuff
    884          * **************************/
    885
    886         /**
    887          * Draws a {@link JXG.Polygon} on the {@link JXG.Board}.
    888          * @param {JXG.Polygon} el Reference to a Polygon object, that is to be drawn.
    889          * @see Polygon
    890          * @see JXG.Polygon
    891          * @see JXG.AbstractRenderer#updatePolygon
    892          */
    893         drawPolygon: function (el) {
    894             el.rendNode = this.appendChildPrim(this.createPrim('polygon', el.id),
    895                                         Type.evaluate(el.visProp.layer));
    896             this.appendNodesToElement(el, 'polygon');
    897             this.updatePolygon(el);
    898         },
    899
    900         /**
    901          * Updates properties of a {@link JXG.Polygon}'s rendering node.
    902          * @param {JXG.Polygon} el Reference to a {@link JXG.Polygon} object, that has to be updated.
    903          * @see Polygon
    904          * @see JXG.Polygon
    905          * @see JXG.AbstractRenderer#drawPolygon
    906          */
    907         updatePolygon: function (el) {
    908             // Here originally strokecolor wasn't updated but strokewidth was.
    909             // But if there's no strokecolor i don't see why we should update strokewidth.
    910             this._updateVisual(el, {stroke: true, dash: true});
    911             this.updatePolygonPrim(el.rendNode, el);
    912         },
    913
    914         /* **************************
    915          *    Text related stuff
    916          * **************************/
    917
    918         /**
    919          * Shows a small copyright notice in the top left corner of the board.
    920          * @param {String} str The copyright notice itself
    921          * @param {Number} fontsize Size of the font the copyright notice is written in
    922          */
    923         displayCopyright: function (str, fontsize) { /* stub */ },
    924
    925         /**
    926          * An internal text is a {@link JXG.Text} element which is drawn using only
    927          * the given renderer but no HTML. This method is only a stub, the drawing
    928          * is done in the special renderers.
    929          * @param {JXG.Text} element Reference to a {@link JXG.Text} object
    930          * @see Text
    931          * @see JXG.Text
    932          * @see JXG.AbstractRenderer#updateInternalText
    933          * @see JXG.AbstractRenderer#drawText
    934          * @see JXG.AbstractRenderer#updateText
    935          * @see JXG.AbstractRenderer#updateTextStyle
    936          */
    937         drawInternalText: function (element) { /* stub */ },
    938
    939         /**
    940          * Updates visual properties of an already existing {@link JXG.Text} element.
    941          * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be updated.
    942          * @see Text
    943          * @see JXG.Text
    944          * @see JXG.AbstractRenderer#drawInternalText
    945          * @see JXG.AbstractRenderer#drawText
    946          * @see JXG.AbstractRenderer#updateText
    947          * @see JXG.AbstractRenderer#updateTextStyle
    948          */
    949         updateInternalText: function (element) { /* stub */ },
    950
    951         /**
    952          * Displays a {@link JXG.Text} on the {@link JXG.Board} by putting a HTML div over it.
    953          * @param {JXG.Text} el Reference to an {@link JXG.Text} object, that has to be displayed
    954          * @see Text
    955          * @see JXG.Text
    956          * @see JXG.AbstractRenderer#drawInternalText
    957          * @see JXG.AbstractRenderer#updateText
    958          * @see JXG.AbstractRenderer#updateInternalText
    959          * @see JXG.AbstractRenderer#updateTextStyle
    960          */
    961         drawText: function (el) {
    962             var node, z, level,
    963                 ev_visible;
    964
    965             if (Type.evaluate(el.visProp.display) === 'html' && Env.isBrowser && this.type !== 'no') {
    966                 node = this.container.ownerDocument.createElement('div');
    967                 //node = this.container.ownerDocument.createElementNS('http://www.w3.org/1999/xhtml', 'div'); //
    968                 node.style.position = 'absolute';
    969                 node.className = Type.evaluate(el.visProp.cssclass);
    970
    971                 level = Type.evaluate(el.visProp.layer);
    972                 if (!Type.exists(level)) { // trace nodes have level not set
    973                     level = 0;
    974                 }
    975
    976                 if (this.container.style.zIndex === '') {
    977                     z = 0;
    978                 } else {
    979                     z = parseInt(this.container.style.zIndex, 10);
    980                 }
    981
    982                 node.style.zIndex = z + level;
    983                 this.container.appendChild(node);
    984
    985                 node.setAttribute('id', this.container.id + '_' + el.id);
    986             } else {
    987                 node = this.drawInternalText(el);
    988             }
    989
    990             el.rendNode = node;
    991             el.htmlStr = '';
    992
    993             // Set el.visPropCalc.visible
    994             if (el.visProp.islabel && Type.exists(el.visProp.anchor)) {
    995                 ev_visible = Type.evaluate(el.visProp.anchor.visProp.visible);
    996                 el.prepareUpdate().updateVisibility(ev_visible);
    997             } else {
    998                 el.prepareUpdate().updateVisibility();
    999             }
    1000             this.updateText(el);
    1001         },
    1002
    1003         /**
    1004          * Updates visual properties of an already existing {@link JXG.Text} element.
    1005          * @param {JXG.Text} el Reference to an {@link JXG.Text} object, that has to be updated.
    1006          * @see Text
    1007          * @see JXG.Text
    1008          * @see JXG.AbstractRenderer#drawText
    1009          * @see JXG.AbstractRenderer#drawInternalText
    1010          * @see JXG.AbstractRenderer#updateInternalText
    1011          * @see JXG.AbstractRenderer#updateTextStyle
    1012          */
    1013         updateText: function (el) {
    1014             var content = el.plaintext,
    1015                 v, c,
    1016                 parentNode,
    1017                 scale, vshift, id, wrap_id,
    1018                 ax, ay;
    1019
    1020             if (el.visPropCalc.visible) {
    1021                 this.updateTextStyle(el, false);
    1022
    1023                 if (Type.evaluate(el.visProp.display) === 'html' && this.type !== 'no') {
    1024                     // Set the position
    1025                     if (!isNaN(el.coords.scrCoords[1] + el.coords.scrCoords[2])) {
    1026
    1027                         // Horizontal
    1028                         c = el.coords.scrCoords[1];
    1029                         // webkit seems to fail for extremely large values for c.
    1030                         c = Math.abs(c) < 1000000 ? c : 1000000;
    1031                         ax = el.getAnchorX();
    1032
    1033                         if (ax === 'right') {
    1034                             // v = Math.floor(el.board.canvasWidth - c);
    1035                             v = el.board.canvasWidth - c;
    1036                         } else if (ax === 'middle') {
    1037                             // v = Math.floor(c - 0.5 * el.size[0]);
    1038                             v = c - 0.5 * el.size[0];
    1039                         } else { // 'left'
    1040                             // v = Math.floor(c);
    1041                             v = c;
    1042                         }
    1043
    1044                         // This may be useful for foreignObj.
    1045                         //if (window.devicePixelRatio !== undefined) {
    1046                         //v *= window.devicePixelRatio;
    1047                         //}
    1048
    1049                         if (el.visPropOld.left !== (ax + v)) {
    1050                             if (ax === 'right') {
    1051                                 el.rendNode.style.right = v + 'px';
    1052                                 el.rendNode.style.left = 'auto';
    1053                             } else {
    1054                                 el.rendNode.style.left = v + 'px';
    1055                                 el.rendNode.style.right = 'auto';
    1056                             }
    1057                             el.visPropOld.left = ax + v;
    1058                         }
    1059
    1060                         // Vertical
    1061                         c = el.coords.scrCoords[2] + this.vOffsetText;
    1062                         c = Math.abs(c) < 1000000 ? c : 1000000;
    1063                         ay = el.getAnchorY();
    1064
    1065                         if (ay === 'bottom') {
    1066                             // v = Math.floor(el.board.canvasHeight - c);
    1067                             v = el.board.canvasHeight - c;
    1068                         } else if (ay === 'middle') {
    1069                             // v = Math.floor(c - 0.5 * el.size[1]);
    1070                             v = c - 0.5 * el.size[1];
    1071                         } else { // top
    1072                             // v = Math.floor(c);
    1073                             v = c;
    1074                         }
    1075
    1076                         // This may be useful for foreignObj.
    1077                         //if (window.devicePixelRatio !== undefined) {
    1078                         //v *= window.devicePixelRatio;
    1079                         //}
    1080
    1081                         if (el.visPropOld.top !== (ay + v)) {
    1082                             if (ay === 'bottom') {
    1083                                 el.rendNode.style.top = 'auto';
    1084                                 el.rendNode.style.bottom = v + 'px';
    1085                             } else {
    1086                                 el.rendNode.style.bottom = 'auto';
    1087                                 el.rendNode.style.top = v + 'px';
    1088                             }
    1089                             el.visPropOld.top = ay + v;
    1090                         }
    1091                     }
    1092
    1093                     // Set the content
    1094                     if (el.htmlStr !== content) {
    1095                         try {
    1096                             if (el.type === Type.OBJECT_TYPE_BUTTON) {
    1097                                 el.rendNodeButton.innerHTML = content;
    1098                             } else if (el.type === Type.OBJECT_TYPE_CHECKBOX ||
    1099                                 el.type === Type.OBJECT_TYPE_INPUT) {
    1100                                 el.rendNodeLabel.innerHTML = content;
    1101                             } else {
    1102                                 el.rendNode.innerHTML = content;
    1103                             }
    1104                         } catch (e) {
    1105                             // Setting innerHTML sometimes fails in IE8.
    1106                             // A workaround is to take the node off the DOM, assign innerHTML,
    1107                             // then append back.
    1108                             // Works for text elements as they are absolutely positioned.
    1109                             parentNode = el.rendNode.parentNode;
    1110                             el.rendNode.parentNode.removeChild(el.rendNode);
    1111                             el.rendNode.innerHTML = content;
    1112                             parentNode.appendChild(el.rendNode);
    1113                         }
    1114                         el.htmlStr = content;
    1115
    1116                         if (Type.evaluate(el.visProp.usemathjax)) {
    1117                             // Typesetting directly might not work because mathjax was not loaded completely
    1118                             // see http://www.mathjax.org/docs/1.1/typeset.html
    1119                             try {
    1120                                 if (MathJax.typeset) {
    1121                                     // Version 3
    1122                                     MathJax.typeset([el.rendNode]);
    1123                                 } else {
    1124                                     // Version 2
    1125                                     MathJax.Hub.Queue(['Typeset', MathJax.Hub, el.rendNode]);
    1126                                 }
    1127
    1128                                 // Restore the transformation necessary for fullscreen mode
    1129                                 // MathJax removes it when handling dynamic content
    1130                                 id = el.board.container;
    1131                                 wrap_id = 'fullscreenwrap_' + id;
    1132                                 if (document.getElementById(wrap_id)) {
    1133                                     scale = el.board.containerObj._cssFullscreenStore.scale;
    1134                                     vshift = el.board.containerObj._cssFullscreenStore.vshift;
    1135                                     Env.scaleJSXGraphDiv('#' + wrap_id, '#' + id, scale, vshift);
    1136                                 }
    1137
    1138                             } catch (e) {
    1139                                 JXG.debug('MathJax (not yet) loaded');
    1140                             }
    1141                         } else if (Type.evaluate(el.visProp.usekatex)) {
    1142                             try {
    1143                                 /* eslint-disable no-undef */
    1144                                 katex.render(content, el.rendNode, {
    1145                                     throwOnError: false
    1146                                 });
    1147                                 /* eslint-enable no-undef */
    1148                             } catch (e) {
    1149                                 JXG.debug('KaTeX (not yet) loaded');
    1150                             }
    1151                     } else if (Type.evaluate(el.visProp.useasciimathml)) {
    1152                             // This is not a constructor.
    1153                             // See http://www1.chapman.edu/~jipsen/mathml/asciimath.html for more information
    1154                             // about AsciiMathML and the project's source code.
    1155                             try {
    1156                                 AMprocessNode(el.rendNode, false);
    1157                             } catch (e) {
    1158                                 JXG.debug('AsciiMathML (not yet) loaded');
    1159                             }
    1160                         }
    1161                     }
    1162                     this.transformImage(el, el.transformations);
    1163                 } else {
    1164                     this.updateInternalText(el);
    1165                 }
    1166             }
    1167         },
    1168
    1169         /**
    1170          * Converts string containing CSS properties into
    1171          * array with key-value pair objects.
    1172          *
    1173          * @example
    1174          * "color:blue; background-color:yellow" is converted to
    1175          * [{'color': 'blue'}, {'backgroundColor': 'yellow'}]
    1176          *
    1177          * @param  {String} cssString String containing CSS properties
    1178          * @return {Array}           Array of CSS key-value pairs
    1179          */
    1180         _css2js: function(cssString) {
    1181             var pairs = [],
    1182                 i, len, key, val, s,
    1183                 list = Type.trim(cssString).replace(/;$/, '').split(";");
    1184
    1185             len = list.length;
    1186             for (i = 0; i < len; ++i) {
    1187                 if (Type.trim(list[i]) !== '') {
    1188                     s = list[i].split(':');
    1189                     key = Type.trim(s[0].replace(/-([a-z])/gi, function(match, char) { return char.toUpperCase(); }));
    1190                     val = Type.trim(s[1]);
    1191                     pairs.push({'key': key, 'val': val});
    1192                 }
    1193             }
    1194             return pairs;
    1195
    1196         },
    1197
    1198         /**
    1199          * Updates font-size, color and opacity propertiey and CSS style properties of a {@link JXG.Text} node.
    1200          * This function is also called by highlight() and nohighlight().
    1201          * @param {JXG.Text} el Reference to the {@link JXG.Text} object, that has to be updated.
    1202          * @param {Boolean} doHighlight
    1203          * @see Text
    1204          * @see JXG.Text
    1205          * @see JXG.AbstractRenderer#drawText
    1206          * @see JXG.AbstractRenderer#drawInternalText
    1207          * @see JXG.AbstractRenderer#updateText
    1208          * @see JXG.AbstractRenderer#updateInternalText
    1209          * @see JXG.AbstractRenderer#updateInternalTextStyle
    1210          */
    1211         updateTextStyle: function (el, doHighlight) {
    1212             var fs, so, sc, css, node,
    1213                 ev = el.visProp,
    1214                 display = Env.isBrowser ? ev.display : 'internal',
    1215                 nodeList = ['rendNode', 'rendNodeTag', 'rendNodeLabel'],
    1216                 lenN = nodeList.length,
    1217                 fontUnit = Type.evaluate(ev.fontunit),
    1218                 cssList, prop, style, cssString,
    1219                 styleList = ['cssdefaultstyle', 'cssstyle'],
    1220                 lenS = styleList.length;
    1221
    1222             if (doHighlight) {
    1223                 sc = ev.highlightstrokecolor;
    1224                 so = ev.highlightstrokeopacity;
    1225                 css = ev.highlightcssclass;
    1226             } else {
    1227                 sc = ev.strokecolor;
    1228                 so = ev.strokeopacity;
    1229                 css = ev.cssclass;
    1230             }
    1231
    1232             // This part is executed for all text elements except internal texts in canvas.
    1233             // HTML-texts or internal texts in SVG or VML.
    1234             //            HTML    internal
    1235             //  SVG        +         +
    1236             //  VML        +         +
    1237             //  canvas     +         -
    1238             //  no         -         -
    1239             if ((this.type !== 'no') &&
    1240                 (display === 'html' || this.type !== 'canvas')
    1241                ) {
    1242                 for (style = 0; style < lenS; style++) {
    1243                     // First set cssString to
    1244                     // ev.cssdefaultstyle of ev.highlightcssdefaultstyle,
    1245                     // then to
    1246                     // ev.cssstyle of ev.highlightcssstyle
    1247                     cssString = Type.evaluate(ev[(doHighlight ? 'highlight' : '') + styleList[style]]);
    1248                     if (cssString !== '' &&
    1249                         el.visPropOld[styleList[style]] !== cssString) {
    1250                         cssList = this._css2js(cssString);
    1251                         for (node = 0; node < lenN; node++) {
    1252                             if (Type.exists(el[nodeList[node]])) {
    1253                                 for (prop in cssList) {
    1254                                     if (cssList.hasOwnProperty(prop)) {
    1255                                         el[nodeList[node]].style[cssList[prop].key] = cssList[prop].val;
    1256                                     }
    1257                                 }
    1258                             }
    1259                         }
    1260                         el.visPropOld[styleList[style]] = cssString;
    1261                     }
    1262                 }
    1263
    1264                 fs = Type.evaluate(ev.fontsize);
    1265                 if (el.visPropOld.fontsize !== fs) {
    1266                     el.needsSizeUpdate = true;
    1267                     try {
    1268                         for (node = 0; node < lenN; node++) {
    1269                             if (Type.exists(el[nodeList[node]])) {
    1270                                 el[nodeList[node]].style.fontSize = fs + fontUnit;
    1271                             }
    1272                         }
    1273                     } catch (e) {
    1274                         // IE needs special treatment.
    1275                         for (node = 0; node < lenN; node++) {
    1276                             if (Type.exists(el[nodeList[node]])) {
    1277                                 el[nodeList[node]].style.fontSize = fs;
    1278                             }
    1279                         }
    1280                     }
    1281                     el.visPropOld.fontsize = fs;
    1282                 }
    1283             }
    1284
    1285             this.setObjectTransition(el);
    1286             if (display === 'html' && this.type !== 'no') {
    1287                 // Set new CSS class
    1288                 if (el.visPropOld.cssclass !== css) {
    1289                     el.rendNode.className = css;
    1290                     el.visPropOld.cssclass = css;
    1291                     el.needsSizeUpdate = true;
    1292                 }
    1293                 this.setObjectStrokeColor(el, sc, so);
    1294             } else {
    1295                 this.updateInternalTextStyle(el, sc, so);
    1296             }
    1297
    1298             return this;
    1299         },
    1300
    1301         /**
    1302          * Set color and opacity of internal texts.
    1303          * This method is used for Canvas and VML.
    1304          * SVG needs its own version.
    1305          * @private
    1306          * @see JXG.AbstractRenderer#updateTextStyle
    1307          * @see JXG.SVGRenderer#updateInternalTextStyle
    1308          */
    1309         updateInternalTextStyle: function (el, strokeColor, strokeOpacity) {
    1310             this.setObjectStrokeColor(el, strokeColor, strokeOpacity);
    1311         },
    1312
    1313         /* **************************
    1314          *    Image related stuff
    1315          * **************************/
    1316
    1317         /**
    1318          * Draws an {@link JXG.Image} on a board; This is just a template that has to be implemented by special
    1319          * renderers.
    1320          * @param {JXG.Image} element Reference to the image object that is to be drawn
    1321          * @see Image
    1322          * @see JXG.Image
    1323          * @see JXG.AbstractRenderer#updateImage
    1324          */
    1325         drawImage: function (element) { /* stub */ },
    1326
    1327         /**
    1328          * Updates the properties of an {@link JXG.Image} element.
    1329          * @param {JXG.Image} el Reference to an {@link JXG.Image} object, that has to be updated.
    1330          * @see Image
    1331          * @see JXG.Image
    1332          * @see JXG.AbstractRenderer#drawImage
    1333          */
    1334         updateImage: function (el) {
    1335             this.updateRectPrim(el.rendNode, el.coords.scrCoords[1],
    1336                 el.coords.scrCoords[2] - el.size[1], el.size[0], el.size[1]);
    1337
    1338             this.updateImageURL(el);
    1339             this.transformImage(el, el.transformations);
    1340             this._updateVisual(el, {stroke: true, dash: true}, true);
    1341         },
    1342
    1343         /**
    1344          * Multiplication of transformations without updating. That means, at that point it is expected that the
    1345          * matrices contain numbers only. First, the origin in user coords is translated to <tt>(0,0)</tt> in screen
    1346          * coords. Then, the stretch factors are divided out. After the transformations in user coords, the stretch
    1347          * factors are multiplied in again, and the origin in user coords is translated back to its position. This
    1348          * method does not have to be implemented in a new renderer.
    1349          * @param {JXG.GeometryElement} el A JSXGraph element. We only need its board property.
    1350          * @param {Array} transformations An array of JXG.Transformations.
    1351          * @returns {Array} A matrix represented by a two dimensional array of numbers.
    1352          * @see JXG.AbstractRenderer#transformImage
    1353          */
    1354         joinTransforms: function (el, transformations) {
    1355             var i,
    1356                 ox = el.board.origin.scrCoords[1],
    1357                 oy = el.board.origin.scrCoords[2],
    1358                 ux = el.board.unitX,
    1359                 uy = el.board.unitY,
    1360                 // Translate to 0,0 in screen coords
    1361                 /*
    1362                 m = [[1, 0, 0], [0, 1, 0], [0, 0, 1]],
    1363                 mpre1 =  [[1,   0, 0],
    1364                     [-ox, 1, 0],
    1365                     [-oy, 0, 1]],
    1366                 // Scale
    1367                 mpre2 =  [[1, 0,     0],
    1368                     [0, 1 / ux,  0],
    1369                     [0, 0, -1 / uy]],
    1370                 // Scale back
    1371                 mpost2 = [[1, 0,   0],
    1372                     [0, ux,  0],
    1373                     [0, 0, -uy]],
    1374                 // Translate back
    1375                 mpost1 = [[1,  0, 0],
    1376                     [ox, 1, 0],
    1377                     [oy, 0, 1]],
    1378                 */
    1379                 len = transformations.length,
    1380                 // Translate to 0,0 in screen coords and then scale
    1381                 m = [[1,        0,       0],
    1382                      [-ox / ux, 1 / ux,  0],
    1383                      [ oy / uy, 0, -1 / uy]];
    1384
    1385             for (i = 0; i < len; i++) {
    1386                 //m = Mat.matMatMult(mpre1, m);
    1387                 //m = Mat.matMatMult(mpre2, m);
    1388                 m = Mat.matMatMult(transformations[i].matrix, m);
    1389                 //m = Mat.matMatMult(mpost2, m);
    1390                 //m = Mat.matMatMult(mpost1, m);
    1391             }
    1392             // Scale back and then translate back
    1393             m = Mat.matMatMult([[1,   0, 0],
    1394                                 [ox, ux, 0],
    1395                                 [oy,  0, -uy]], m);
    1396             return m;
    1397         },
    1398
    1399         /**
    1400          * Applies transformations on images and text elements. This method is just a stub and has to be implemented in
    1401          * all descendant classes where text and image transformations are to be supported.
    1402          * @param {JXG.Image|JXG.Text} element A {@link JXG.Image} or {@link JXG.Text} object.
    1403          * @param {Array} transformations An array of {@link JXG.Transformation} objects. This is usually the
    1404          * transformations property of the given element <tt>el</tt>.
    1405          */
    1406         transformImage: function (element, transformations) { /* stub */ },
    1407
    1408         /**
    1409          * If the URL of the image is provided by a function the URL has to be updated during updateImage()
    1410          * @param {JXG.Image} element Reference to an image object.
    1411          * @see JXG.AbstractRenderer#updateImage
    1412          */
    1413         updateImageURL: function (element) { /* stub */ },
    1414
    1415         /**
    1416          * Updates CSS style properties of a {@link JXG.Image} node.
    1417          * In SVGRenderer opacity is the only available style element.
    1418          * This function is called by highlight() and nohighlight().
    1419          * This function works for VML.
    1420          * It does not work for Canvas.
    1421          * SVGRenderer overwrites this method.
    1422          * @param {JXG.Text} el Reference to the {@link JXG.Image} object, that has to be updated.
    1423          * @param {Boolean} doHighlight
    1424          * @see Image
    1425          * @see JXG.Image
    1426          * @see JXG.AbstractRenderer#highlight
    1427          * @see JXG.AbstractRenderer#noHighlight
    1428          */
    1429         updateImageStyle: function (el, doHighlight) {
    1430             el.rendNode.className = Type.evaluate(doHighlight ? el.visProp.highlightcssclass : el.visProp.cssclass);
    1431         },
    1432
    1433         drawForeignObject: function (el) { /* stub */ },
    1434
    1435         updateForeignObject: function(el) { /* stub */ },
    1436
    1437         /* **************************
    1438          * Render primitive objects
    1439          * **************************/
    1440
    1441         /**
    1442          * Appends a node to a specific layer level. This is just an abstract method and has to be implemented
    1443          * in all renderers that want to use the <tt>createPrim</tt> model to draw.
    1444          * @param {Node} node A DOM tree node.
    1445          * @param {Number} level The layer the node is attached to. This is the index of the layer in
    1446          * {@link JXG.SVGRenderer#layer} or the <tt>z-index</tt> style property of the node in VMLRenderer.
    1447          */
    1448         appendChildPrim: function (node, level) { /* stub */ },
    1449
    1450         /**
    1451          * Stores the rendering nodes. This is an abstract method which has to be implemented in all renderers that use
    1452          * the <tt>createPrim</tt> method.
    1453          * @param {JXG.GeometryElement} element A JSXGraph element.
    1454          * @param {String} type The XML node name. Only used in VMLRenderer.
    1455          */
    1456         appendNodesToElement: function (element, type) { /* stub */ },
    1457
    1458         /**
    1459          * Creates a node of a given type with a given id.
    1460          * @param {String} type The type of the node to create.
    1461          * @param {String} id Set the id attribute to this.
    1462          * @returns {Node} Reference to the created node.
    1463          */
    1464         createPrim: function (type, id) {
    1465             /* stub */
    1466             return null;
    1467         },
    1468
    1469         /**
    1470          * Removes an element node. Just a stub.
    1471          * @param {Node} node The node to remove.
    1472          */
    1473         remove: function (node) { /* stub */ },
    1474
    1475         /**
    1476          * Can be used to create the nodes to display arrows. This is an abstract method which has to be implemented
    1477          * in any descendant renderer.
    1478          * @param {JXG.GeometryElement} element The element the arrows are to be attached to.
    1479          * @param {Object} arrowData Data concerning possible arrow heads
    1480          *
    1481          */
    1482         makeArrows: function (element, arrowData) { /* stub */ },
    1483
    1484         /**
    1485          * Updates width of an arrow DOM node. Used in
    1486          * @param {Node} node The arrow node.
    1487          * @param {Number} width
    1488          * @param {Node} parentNode Used in IE only
    1489          */
    1490         _setArrowWidth: function(node, width, parentNode) { /* stub */},
    1491
    1492         /**
    1493          * Updates an ellipse node primitive. This is an abstract method which has to be implemented in all renderers
    1494          * that use the <tt>createPrim</tt> method.
    1495          * @param {Node} node Reference to the node.
    1496          * @param {Number} x Centre X coordinate
    1497          * @param {Number} y Centre Y coordinate
    1498          * @param {Number} rx The x-axis radius.
    1499          * @param {Number} ry The y-axis radius.
    1500          */
    1501         updateEllipsePrim: function (node, x, y, rx, ry) { /* stub */ },
    1502
    1503         /**
    1504          * Refreshes a line node. This is an abstract method which has to be implemented in all renderers that use
    1505          * the <tt>createPrim</tt> method.
    1506          * @param {Node} node The node to be refreshed.
    1507          * @param {Number} p1x The first point's x coordinate.
    1508          * @param {Number} p1y The first point's y coordinate.
    1509          * @param {Number} p2x The second point's x coordinate.
    1510          * @param {Number} p2y The second point's y coordinate.
    1511          * @param {JXG.Board} board
    1512          */
    1513         updateLinePrim: function (node, p1x, p1y, p2x, p2y, board) { /* stub */ },
    1514
    1515         /**
    1516          * Updates a path element. This is an abstract method which has to be implemented in all renderers that use
    1517          * the <tt>createPrim</tt> method.
    1518          * @param {Node} node The path node.
    1519          * @param {String} pathString A string formatted like e.g. <em>'M 1,2 L 3,1 L5,5'</em>. The format of the string
    1520          * depends on the rendering engine.
    1521          * @param {JXG.Board} board Reference to the element's board.
    1522          */
    1523         updatePathPrim: function (node, pathString, board) { /* stub */ },
    1524
    1525         /**
    1526          * Builds a path data string to draw a point with a face other than <em>rect</em> and <em>circle</em>. Since
    1527          * the format of such a string usually depends on the renderer this method
    1528          * is only an abstract method. Therefore, it has to be implemented in the descendant renderer itself unless
    1529          * the renderer does not use the createPrim interface but the draw* interfaces to paint.
    1530          * @param {JXG.Point} element The point element
    1531          * @param {Number} size A positive number describing the size. Usually the half of the width and height of
    1532          * the drawn point.
    1533          * @param {String} type A string describing the point's face. This method only accepts the shortcut version of
    1534          * each possible face: <tt>x, +, <>, ^, v, >, < </tt>
    1535          */
    1536         updatePathStringPoint: function (element, size, type) { /* stub */ },
    1537
    1538         /**
    1539          * Builds a path data string from a {@link JXG.Curve} element. Since the path data strings heavily depend on the
    1540          * underlying rendering technique this method is just a stub. Although such a path string is of no use for the
    1541          * CanvasRenderer, this method is used there to draw a path directly.
    1542          * @param element
    1543          */
    1544         updatePathStringPrim: function (element) { /* stub */ },
    1545
    1546         /**
    1547          * Builds a path data string from a {@link JXG.Curve} element such that the curve looks like hand drawn. Since
    1548          * the path data strings heavily depend on the underlying rendering technique this method is just a stub.
    1549          * Although such a path string is of no use for the CanvasRenderer, this method is used there to draw a path
    1550          * directly.
    1551          * @param element
    1552          */
    1553         updatePathStringBezierPrim: function (element) { /* stub */ },
    1554
    1555
    1556         /**
    1557          * Update a polygon primitive.
    1558          * @param {Node} node
    1559          * @param {JXG.Polygon} element A JSXGraph element of type {@link JXG.Polygon}
    1560          */
    1561         updatePolygonPrim: function (node, element) { /* stub */ },
    1562
    1563         /**
    1564          * Update a rectangle primitive. This is used only for points with face of type 'rect'.
    1565          * @param {Node} node The node yearning to be updated.
    1566          * @param {Number} x x coordinate of the top left vertex.
    1567          * @param {Number} y y coordinate of the top left vertex.
    1568          * @param {Number} w Width of the rectangle.
    1569          * @param {Number} h The rectangle's height.
    1570          */
    1571         updateRectPrim: function (node, x, y, w, h) { /* stub */ },
    1572
    1573         /* **************************
    1574          *  Set Attributes
    1575          * **************************/
    1576
    1577         /**
    1578          * Sets a node's attribute.
    1579          * @param {Node} node The node that is to be updated.
    1580          * @param {String} key Name of the attribute.
    1581          * @param {String} val New value for the attribute.
    1582          */
    1583         setPropertyPrim: function (node, key, val) { /* stub */ },
    1584
    1585         setTabindex: function(element) {
    1586             var val;
    1587             if (element.board.attr.keyboard.enabled && Type.exists(element.rendNode)) {
    1588                 val = Type.evaluate(element.visProp.tabindex);
    1589                 if (!element.visPropCalc.visible || Type.evaluate(element.visProp.fixed)) {
    1590                     val = null;
    1591                 }
    1592                 if (val !== element.visPropOld.tabindex) {
    1593                     element.rendNode.setAttribute('tabindex', val);
    1594                     element.visPropOld.tabindex = val;
    1595                 }
    1596             }
    1597         },
    1598
    1599         /**
    1600          * Shows or hides an element on the canvas; Only a stub, requires implementation in the derived renderer.
    1601          * @param {JXG.GeometryElement} element Reference to the object that has to appear.
    1602          * @param {Boolean} value true to show the element, false to hide the element.
    1603          */
    1604         display: function (element, value) {
    1605             if (element) {
    1606                 element.visPropOld.visible = value;
    1607             }
    1608         },
    1609
    1610         /**
    1611          * Shows a hidden element on the canvas; Only a stub, requires implementation in the derived renderer.
    1612          *
    1613          * Please use JXG.AbstractRenderer#display instead
    1614          * @param {JXG.GeometryElement} element Reference to the object that has to appear.
    1615          * @see JXG.AbstractRenderer#hide
    1616          * @deprecated
    1617          */
    1618         show: function (element) { /* stub */ },
    1619
    1620         /**
    1621          * Hides an element on the canvas; Only a stub, requires implementation in the derived renderer.
    1622          *
    1623          * Please use JXG.AbstractRenderer#display instead
    1624          * @param {JXG.GeometryElement} element Reference to the geometry element that has to disappear.
    1625          * @see JXG.AbstractRenderer#show
    1626          * @deprecated
    1627          */
    1628         hide: function (element) { /* stub */ },
    1629
    1630         /**
    1631          * Sets the buffering as recommended by SVGWG. Until now only Opera supports this and will be ignored by other
    1632          * browsers. Although this feature is only supported by SVG we have this method in {@link JXG.AbstractRenderer}
    1633          * because it is called from outside the renderer.
    1634          * @param {Node} node The SVG DOM Node which buffering type to update.
    1635          * @param {String} type Either 'auto', 'dynamic', or 'static'. For an explanation see
    1636          *   {@link http://www.w3.org/TR/SVGTiny12/painting.html#BufferedRenderingProperty}.
    1637          */
    1638         setBuffering: function (node, type) { /* stub */ },
    1639
    1640         /**
    1641          * Sets an element's dash style.
    1642          * @param {JXG.GeometryElement} element An JSXGraph element.
    1643          */
    1644         setDashStyle: function (element) { /* stub */ },
    1645
    1646         /**
    1647          * Puts an object into draft mode, i.e. it's visual appearance will be changed. For GEONE<sub>x</sub>T backwards
    1648          * compatibility.
    1649          * @param {JXG.GeometryElement} el Reference of the object that is in draft mode.
    1650          */
    1651         setDraft: function (el) {
    1652             if (!Type.evaluate(el.visProp.draft)) {
    1653                 return;
    1654             }
    1655             var draftColor = el.board.options.elements.draft.color,
    1656                 draftOpacity = el.board.options.elements.draft.opacity;
    1657
    1658             this.setObjectTransition(el);
    1659             if (el.type === Const.OBJECT_TYPE_POLYGON) {
    1660                 this.setObjectFillColor(el, draftColor, draftOpacity);
    1661             } else {
    1662                 if (el.elementClass === Const.OBJECT_CLASS_POINT) {
    1663                     this.setObjectFillColor(el, draftColor, draftOpacity);
    1664                 } else {
    1665                     this.setObjectFillColor(el, 'none', 0);
    1666                 }
    1667                 this.setObjectStrokeColor(el, draftColor, draftOpacity);
    1668                 this.setObjectStrokeWidth(el, el.board.options.elements.draft.strokeWidth);
    1669             }
    1670         },
    1671
    1672         /**
    1673          * Puts an object from draft mode back into normal mode.
    1674          * @param {JXG.GeometryElement} el Reference of the object that no longer is in draft mode.
    1675          */
    1676         removeDraft: function (el) {
    1677             this.setObjectTransition(el);
    1678             if (el.type === Const.OBJECT_TYPE_POLYGON) {
    1679                 this.setObjectFillColor(el,
    1680                     el.visProp.fillcolor,
    1681                     el.visProp.fillopacity);
    1682             } else {
    1683                 if (el.type === Const.OBJECT_CLASS_POINT) {
    1684                     this.setObjectFillColor(el,
    1685                         el.visProp.fillcolor,
    1686                         el.visProp.fillopacity);
    1687                 }
    1688                 this.setObjectStrokeColor(el, el.visProp.strokecolor, el.visProp.strokeopacity);
    1689                 this.setObjectStrokeWidth(el, el.visProp.strokewidth);
    1690             }
    1691         },
    1692
    1693         /**
    1694          * Sets up nodes for rendering a gradient fill.
    1695          * @param element
    1696          */
    1697         setGradient: function (element) { /* stub */ },
    1698
    1699         /**
    1700          * Updates the gradient fill.
    1701          * @param {JXG.GeometryElement} element An JSXGraph element with an area that can be filled.
    1702          */
    1703         updateGradient: function (element) { /* stub */ },
    1704
    1705         /**
    1706          * Sets the transition duration (in milliseconds) for fill color and stroke
    1707          * color and opacity.
    1708          * @param {JXG.GeometryElement} element Reference of the object that wants a
    1709          *         new transition duration.
    1710          * @param {Number} duration (Optional) duration in milliseconds. If not given,
    1711          *        element.visProp.transitionDuration is taken. This is the default.
    1712          */
    1713         setObjectTransition: function (element, duration) { /* stub */ },
    1714
    1715         /**
    1716          * Sets an objects fill color.
    1717          * @param {JXG.GeometryElement} element Reference of the object that wants a new fill color.
    1718          * @param {String} color Color in a HTML/CSS compatible format. If you don't want any fill color at all, choose
    1719          * 'none'.
    1720          * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1.
    1721          */
    1722         setObjectFillColor: function (element, color, opacity) { /* stub */ },
    1723
    1724         /**
    1725          * Changes an objects stroke color to the given color.
    1726          * @param {JXG.GeometryElement} element Reference of the {@link JXG.GeometryElement} that gets a new stroke
    1727          * color.
    1728          * @param {String} color Color value in a HTML compatible format, e.g. <strong>#00ff00</strong> or
    1729          * <strong>green</strong> for green.
    1730          * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1.
    1731          */
    1732         setObjectStrokeColor: function (element, color, opacity) { /* stub */ },
    1733
    1734         /**
    1735          * Sets an element's stroke width.
    1736          * @param {JXG.GeometryElement} element Reference to the geometry element.
    1737          * @param {Number} width The new stroke width to be assigned to the element.
    1738          */
    1739         setObjectStrokeWidth: function (element, width) { /* stub */ },
    1740
    1741         /**
    1742          * Sets the shadow properties to a geometry element. This method is only a stub, it is implemented in the actual
    1743          * renderers.
    1744          * @param {JXG.GeometryElement} element Reference to a geometry object, that should get a shadow
    1745          */
    1746         setShadow: function (element) { /* stub */ },
    1747
    1748         /**
    1749          * Highlights an object, i.e. changes the current colors of the object to its highlighting colors
    1750          * and highlighting stroke width.
    1751          * @param {JXG.GeometryElement} el Reference of the object that will be highlighted.
    1752          * @returns {JXG.AbstractRenderer} Reference to the renderer
    1753          * @see JXG.AbstractRenderer#updateTextStyle
    1754          */
    1755         highlight: function (el) {
    1756             var i, ev = el.visProp, sw;
    1757
    1758             this.setObjectTransition(el);
    1759             if (!ev.draft) {
    1760                 if (el.type === Const.OBJECT_TYPE_POLYGON) {
    1761                     this.setObjectFillColor(el,
    1762                         ev.highlightfillcolor,
    1763                         ev.highlightfillopacity);
    1764                     for (i = 0; i < el.borders.length; i++) {
    1765                         this.setObjectStrokeColor(el.borders[i],
    1766                             el.borders[i].visProp.highlightstrokecolor,
    1767                             el.borders[i].visProp.highlightstrokeopacity);
    1768                     }
    1769                 } else {
    1770                     if (el.elementClass === Const.OBJECT_CLASS_TEXT) {
    1771                         this.updateTextStyle(el, true);
    1772                     } else if (el.type === Const.OBJECT_TYPE_IMAGE) {
    1773                         this.updateImageStyle(el, true);
    1774                         this.setObjectFillColor(el,
    1775                             ev.highlightfillcolor,
    1776                             ev.highlightfillopacity);
    1777                     } else {
    1778                         this.setObjectStrokeColor(el, ev.highlightstrokecolor, ev.highlightstrokeopacity);
    1779                         this.setObjectFillColor(el,
    1780                             ev.highlightfillcolor,
    1781                             ev.highlightfillopacity);
    1782                     }
    1783                 }
    1784                 if (ev.highlightstrokewidth) {
    1785                     sw = Math.max(Type.evaluate(ev.highlightstrokewidth), Type.evaluate(ev.strokewidth));
    1786                     this.setObjectStrokeWidth(el, sw);
    1787                     if (el.elementClass === Const.OBJECT_CLASS_LINE || el.elementClass === Const.OBJECT_CLASS_CURVE) {
    1788                         this.updatePathWithArrowHeads(el, true);
    1789                     }
    1790                 }
    1791             }
    1792
    1793             return this;
    1794         },
    1795
    1796         /**
    1797          * Uses the normal colors of an object, i.e. the opposite of {@link JXG.AbstractRenderer#highlight}.
    1798          * @param {JXG.GeometryElement} el Reference of the object that will get its normal colors.
    1799          * @returns {JXG.AbstractRenderer} Reference to the renderer
    1800          * @see JXG.AbstractRenderer#updateTextStyle
    1801          */
    1802         noHighlight: function (el) {
    1803             var i, ev = el.visProp, sw;
    1804
    1805             this.setObjectTransition(el);
    1806             if (!Type.evaluate(el.visProp.draft)) {
    1807                 if (el.type === Const.OBJECT_TYPE_POLYGON) {
    1808                     this.setObjectFillColor(el,
    1809                         ev.fillcolor,
    1810                         ev.fillopacity);
    1811                     for (i = 0; i < el.borders.length; i++) {
    1812                         this.setObjectStrokeColor(el.borders[i],
    1813                             el.borders[i].visProp.strokecolor,
    1814                             el.borders[i].visProp.strokeopacity);
    1815                     }
    1816                 } else {
    1817                     if (el.elementClass === Const.OBJECT_CLASS_TEXT) {
    1818                         this.updateTextStyle(el, false);
    1819                     } else if (el.type === Const.OBJECT_TYPE_IMAGE) {
    1820                         this.updateImageStyle(el, false);
    1821                         this.setObjectFillColor(el,
    1822                             ev.fillcolor,
    1823                             ev.fillopacity);
    1824                     } else {
    1825                         this.setObjectStrokeColor(el,
    1826                             ev.strokecolor,
    1827                             ev.strokeopacity);
    1828                         this.setObjectFillColor(el,
    1829                             ev.fillcolor,
    1830                             ev.fillopacity);
    1831                     }
    1832                 }
    1833
    1834                 sw = Type.evaluate(ev.strokewidth);
    1835                 this.setObjectStrokeWidth(el, sw);
    1836                 if (el.elementClass === Const.OBJECT_CLASS_LINE || el.elementClass === Const.OBJECT_CLASS_CURVE) {
    1837                     this.updatePathWithArrowHeads(el, false);
    1838                 }
    1839
    1840             }
    1841
    1842             return this;
    1843         },
    1844
    1845         /* **************************
    1846          * renderer control
    1847          * **************************/
    1848
    1849         /**
    1850          * Stop redraw. This method is called before every update, so a non-vector-graphics based renderer can use this
    1851          * method to delete the contents of the drawing panel. This is an abstract method every descendant renderer
    1852          * should implement, if appropriate.
    1853          * @see JXG.AbstractRenderer#unsuspendRedraw
    1854          */
    1855         suspendRedraw: function () { /* stub */ },
    1856
    1857         /**
    1858          * Restart redraw. This method is called after updating all the rendering node attributes.
    1859          * @see JXG.AbstractRenderer#suspendRedraw
    1860          */
    1861         unsuspendRedraw: function () { /* stub */ },
    1862
    1863         /**
    1864          * The tiny zoom bar shown on the bottom of a board (if showNavigation on board creation is true).
    1865          * @param {JXG.Board} board Reference to a JSXGraph board.
    1866          * @param {Object} attr Attributes of the navigation bar
    1867          *
    1868          */
    1869         drawZoomBar: function (board, attr) {
    1870             var doc,
    1871                 node,
    1872                 cancelbubble = function (e) {
    1873                     if (!e) {
    1874                         e = window.event;
    1875                     }
    1876
    1877                     if (e.stopPropagation) {
    1878                         // Non IE<=8
    1879                         e.stopPropagation();
    1880                     } else {
    1881                         e.cancelBubble = true;
    1882                     }
    1883                 },
    1884                 createButton = function (label, handler) {
    1885                     var button;
    1886
    1887                     button = doc.createElement('span');
    1888                     node.appendChild(button);
    1889                     button.appendChild(doc.createTextNode(label));
    1890
    1891                     // Style settings are superseded by adding the CSS class below
    1892                     button.style.paddingLeft = '7px';
    1893                     button.style.paddingRight = '7px';
    1894
    1895                     if (button.classList !== undefined) { // classList not available in IE 9
    1896                         button.classList.add('JXG_navigation_button');
    1897                     }
    1898                     // button.setAttribute('tabindex', 0);
    1899
    1900                     // Highlighting is now done with CSS
    1901                     // Env.addEvent(button, 'mouseover', function () {
    1902                     //     this.style.backgroundColor = attr.highlightfillcolor;
    1903                     // }, button);
    1904                     // Env.addEvent(button, 'mouseover', function () {
    1905                     //     this.style.backgroundColor = attr.highlightfillcolor;
    1906                     // }, button);
    1907                     // Env.addEvent(button, 'mouseout', function () {
    1908                     //     this.style.backgroundColor = attr.fillcolor;
    1909                     // }, button);
    1910
    1911                     Env.addEvent(button, 'click', function(e) { (Type.bind(handler, board))(); return false; }, board);
    1912                     // prevent the click from bubbling down to the board
    1913                     Env.addEvent(button, 'mouseup', cancelbubble, board);
    1914                     Env.addEvent(button, 'mousedown', cancelbubble, board);
    1915                     Env.addEvent(button, 'touchend', cancelbubble, board);
    1916                     Env.addEvent(button, 'touchstart', cancelbubble, board);
    1917                 };
    1918
    1919             if (Env.isBrowser && this.type !== 'no') {
    1920                 doc = board.containerObj.ownerDocument;
    1921                 node = doc.createElement('div');
    1922
    1923                 node.setAttribute('id', board.containerObj.id + '_navigationbar');
    1924
    1925                 // Style settings are superseded by adding the CSS class below
    1926                 node.style.color = attr.strokecolor;
    1927                 node.style.backgroundColor = attr.fillcolor;
    1928                 node.style.padding = attr.padding;
    1929                 node.style.position = attr.position;
    1930                 node.style.fontSize = attr.fontsize;
    1931                 node.style.cursor = attr.cursor;
    1932                 node.style.zIndex = attr.zindex;
    1933                 board.containerObj.appendChild(node);
    1934                 node.style.right = attr.right;
    1935                 node.style.bottom = attr.bottom;
    1936
    1937                 if (node.classList !== undefined) { // classList not available in IE 9
    1938                     node.classList.add('JXG_navigation');
    1939                 }
    1940                 // For XHTML we need unicode instead of HTML entities
    1941
    1942                 if (board.attr.showfullscreen) {
    1943                     createButton(board.attr.fullscreen.symbol, function () {
    1944                         board.toFullscreen(board.attr.fullscreen.id);
    1945                     });
    1946                 }
    1947
    1948                 if (board.attr.showscreenshot) {
    1949                     createButton(board.attr.screenshot.symbol, function () {
    1950                         window.setTimeout(function() {
    1951                             board.renderer.screenshot(board, '', false);
    1952                         }, 330);
    1953                     });
    1954                 }
    1955
    1956                 if (board.attr.showreload) {
    1957                     // full reload circle: \u27F2
    1958                     // the board.reload() method does not exist during the creation
    1959                     // of this button. That's why this anonymous function wrapper is required.
    1960                     createButton('\u21BB', function () {
    1961                         board.reload();
    1962                     });
    1963                 }
    1964
    1965                 if (board.attr.showcleartraces) {
    1966                     // clear traces symbol (otimes): \u27F2
    1967                     createButton('\u2297', function () {
    1968                         board.clearTraces();
    1969                     });
    1970                 }
    1971
    1972                 if (board.attr.shownavigation) {
    1973                     if (board.attr.showzoom) {
    1974                         createButton('\u2013', board.zoomOut);
    1975                         createButton('o', board.zoom100);
    1976                         createButton('+', board.zoomIn);
    1977                     }
    1978                     createButton('\u2190', board.clickLeftArrow);
    1979                     createButton('\u2193', board.clickUpArrow);
    1980                     createButton('\u2191', board.clickDownArrow);
    1981                     createButton('\u2192', board.clickRightArrow);
    1982                 }
    1983             }
    1984         },
    1985
    1986         /**
    1987          * Wrapper for getElementById for maybe other renderers which elements are not directly accessible by DOM
    1988          * methods like document.getElementById().
    1989          * @param {String} id Unique identifier for element.
    1990          * @returns {Object} Reference to a JavaScript object. In case of SVG/VMLRenderer it's a reference to a SVG/VML
    1991          * node.
    1992          */
    1993         getElementById: function (id) {
    1994             if (Type.exists(this.container)) {
    1995                 return this.container.ownerDocument.getElementById(this.container.id + '_' + id);
    1996             }
    1997             return '';
    1998         },
    1999
    2000         /**
    2001          * Remove an element and provide a function that inserts it into its original position. This method

    2003          * @author KeeKim Heng, Google Web Developer
    2004          * @param {Element} el The element to be temporarily removed
    2005          * @returns {Function} A function that inserts the element into its original position
    2006          */
    2007         removeToInsertLater: function (el) {
    2008             var parentNode = el.parentNode,
    2009                 nextSibling = el.nextSibling;
    2010
    2011             if (parentNode === null) {
    2012                 return;
    2013             }
    2014             parentNode.removeChild(el);
    2015
    2016             return function () {
    2017                 if (nextSibling) {
    2018                     parentNode.insertBefore(el, nextSibling);
    2019                 } else {
    2020                     parentNode.appendChild(el);
    2021                 }
    2022             };
    2023         },
    2024
    2025         /**
    2026          * Resizes the rendering element
    2027          * @param {Number} w New width
    2028          * @param {Number} h New height
    2029          */
    2030         resize: function (w, h) { /* stub */},
    2031
    2032         /**
    2033          * Create crosshair elements (Fadenkreuz) for presentations.
    2034          * @param {Number} n Number of crosshairs.
    2035          */
    2036         createTouchpoints: function (n) {},
    2037
    2038         /**
    2039          * Show a specific crosshair.
    2040          * @param {Number} i Number of the crosshair to show
    2041          */
    2042         showTouchpoint: function (i) {},
    2043
    2044         /**
    2045          * Hide a specific crosshair.
    2046          * @param {Number} i Number of the crosshair to show
    2047          */
    2048         hideTouchpoint: function (i) {},
    2049
    2050         /**
    2051          * Move a specific crosshair.
    2052          * @param {Number} i Number of the crosshair to show
    2053          * @param {Array} pos New positon in screen coordinates
    2054          */
    2055         updateTouchpoint: function (i, pos) {},
    2056
    2057         /**
    2058          * Convert SVG construction to base64 encoded SVG data URL.
    2059          * Only available on SVGRenderer.
    2060          *
    2061          * @see JXG.SVGRenderer#dumpToDataURI
    2062          */
    2063         dumpToDataURI: function (_ignoreTexts) {},
    2064
    2065         /**
    2066          * Convert SVG construction to canvas.
    2067          * Only available on SVGRenderer.
    2068          *
    2069          * @see JXG.SVGRenderer#dumpToCanvas
    2070          */
    2071         dumpToCanvas: function (canvasId, w, h, _ignoreTexts) {},
    2072
    2073         /**
    2074          * Display SVG image in html img-tag which enables
    2075          * easy download for the user.
    2076          *
    2077          * See JXG.SVGRenderer#screenshot
    2078          */
    2079         screenshot: function (board) {},
    2080
    2081         /**
    2082          * Move element into new layer. This is trivial for canvas, but needs more effort in SVG.
    2083          * Does not work dynamically, i.e. if level is a function.
    2084          *
    2085          * @param {JXG.GeometryElement} el Element which is put into different layer
    2086          * @param {Number} value Layer number
    2087          * @private
    2088          */
    2089         setLayer: function(el, level) {}
    2090
    2091     });
    2092
    2093     return JXG.AbstractRenderer;
    2094 });
    2095