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