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