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

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