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

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