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

    1778          * @author KeeKim Heng, Google Web Developer
    1779          * @param {Element} el The element to be temporarily removed
    1780          * @returns {Function} A function that inserts the element into its original position
    1781          */
    1782         removeToInsertLater: function (el) {
    1783             var parentNode = el.parentNode,
    1784                 nextSibling = el.nextSibling;
    1785
    1786             parentNode.removeChild(el);
    1787
    1788             return function () {
    1789                 if (nextSibling) {
    1790                     parentNode.insertBefore(el, nextSibling);
    1791                 } else {
    1792                     parentNode.appendChild(el);
    1793                 }
    1794             };
    1795         },
    1796
    1797         /**
    1798          * Resizes the rendering element
    1799          * @param {Number} w New width
    1800          * @param {Number} h New height
    1801          */
    1802         resize: function (w, h) { /* stub */},
    1803
    1804         /**
    1805          * Create crosshair elements (Fadenkreuz) for presentations.
    1806          * @param {Number} n Number of crosshairs.
    1807          */
    1808         createTouchpoints: function (n) {},
    1809
    1810         /**
    1811          * Show a specific crosshair.
    1812          * @param {Number} i Number of the crosshair to show
    1813          */
    1814         showTouchpoint: function (i) {},
    1815
    1816         /**
    1817          * Hide a specific crosshair.
    1818          * @param {Number} i Number of the crosshair to show
    1819          */
    1820         hideTouchpoint: function (i) {},
    1821
    1822         /**
    1823          * Move a specific crosshair.
    1824          * @param {Number} i Number of the crosshair to show
    1825          * @param {Array} pos New positon in screen coordinates
    1826          */
    1827         updateTouchpoint: function (i, pos) {},
    1828
    1829         /**
    1830          * Convert SVG construction to canvas.
    1831          * Only available on SVGRenderer.
    1832          *
    1833          * @see JXG.SVGRenderer#dumpToCanvas
    1834          */
    1835         dumpToCanvas: function(canvasId) {},
    1836
    1837         /**
    1838          * Display SVG image in html img-tag which enables
    1839          * easy download for the user.
    1840          *
    1841          * See JXG.SVGRenderer#screenshot
    1842          */
    1843         screenshot: function(board) {}
    1844
    1845     });
    1846
    1847     return JXG.AbstractRenderer;
    1848 });
    1849