1 /*
  2     Copyright 2008-2013
  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     JXG.extend(JXG.AbstractRenderer.prototype, /** @lends JXG.AbstractRenderer.prototype */ {
162 
163         /* ******************************** *
164          *    private methods               *
165          *    should not be called from     *
166          *    outside AbstractRenderer      *
167          * ******************************** */
168 
169         /**
170          * Update visual properties, but only if {@link JXG.AbstractRenderer#enhancedRendering} or <tt>enhanced</tt> is set to true.
171          * @param {JXG.GeometryElement} element The element to update
172          * @param {Object} [not={}] Select properties you don't want to be updated: <tt>{fill: true, dash: true}</tt> updates
173          * everything except for fill and dash. Possible values are <tt>stroke, fill, dash, shadow, gradient</tt>.
174          * @param {Boolean} [enhanced=false] If true, {@link JXG.AbstractRenderer#enhancedRendering} is assumed to be true.
175          * @private
176          */
177         _updateVisual: function (element, not, enhanced) {
178             var rgbo;
179 
180             if (enhanced || this.enhancedRendering) {
181                 not = not || {};
182 
183                 if (!element.visProp.draft) {
184                     if (!not.stroke) {
185                         this.setObjectStrokeColor(element, element.visProp.strokecolor, element.visProp.strokeopacity);
186                         this.setObjectStrokeWidth(element, element.visProp.strokewidth);
187                     }
188 
189                     if (!not.fill) {
190                         this.setObjectFillColor(element, element.visProp.fillcolor, element.visProp.fillopacity);
191                     }
192 
193                     if (!not.dash) {
194                         this.setDashStyle(element, element.visProp);
195                     }
196 
197                     if (!not.shadow) {
198                         this.setShadow(element);
199                     }
200 
201                     if (!not.gradient) {
202                         this.setShadow(element);
203                     }
204                 } else {
205                     this.setDraft(element);
206                 }
207             }
208         },
209 
210 
211         /* ******************************** *
212          *    Point drawing and updating    *
213          * ******************************** */
214 
215         /**
216          * Draws a point on the {@link JXG.Board}.
217          * @param {JXG.Point} element Reference to a {@link JXG.Point} object that has to be drawn.
218          * @see Point
219          * @see JXG.Point
220          * @see JXG.AbstractRenderer#updatePoint
221          * @see JXG.AbstractRenderer#changePointStyle
222          */
223         drawPoint: function (element) {
224             var prim,
225                 // sometimes element is not a real point and lacks the methods of a JXG.Point instance,
226                 // in these cases to not use element directly.
227                 face = Options.normalizePointFace(element.visProp.face);
228 
229             // determine how the point looks like
230             if (face === 'o') {
231                 prim = 'ellipse';
232             } else if (face === '[]') {
233                 prim = 'rect';
234             } else {
235                 // cross/x, diamond/<>, triangleup/a/^, triangledown/v, triangleleft/<,
236                 // triangleright/>, plus/+,
237                 prim = 'path';
238             }
239 
240             element.rendNode = this.appendChildPrim(this.createPrim(prim, element.id), element.visProp.layer);
241             this.appendNodesToElement(element, prim);
242 
243             // adjust visual propertys
244             this._updateVisual(element, {dash: true, shadow: true}, true);
245 
246 
247             // By now we only created the xml nodes and set some styles, in updatePoint
248             // the attributes are filled with data.
249             this.updatePoint(element);
250         },
251 
252         /**
253          * Updates visual appearance of the renderer element assigned to the given {@link JXG.Point}.
254          * @param {JXG.Point} element Reference to a {@link JXG.Point} object, that has to be updated.
255          * @see Point
256          * @see JXG.Point
257          * @see JXG.AbstractRenderer#drawPoint
258          * @see JXG.AbstractRenderer#changePointStyle
259          */
260         updatePoint: function (element) {
261             var size = element.visProp.size,
262                 // sometimes element is not a real point and lacks the methods of a JXG.Point instance,
263                 // in these cases to not use element directly.
264                 face = Options.normalizePointFace(element.visProp.face);
265 
266             if (!isNaN(element.coords.scrCoords[2] + element.coords.scrCoords[1])) {
267                 this._updateVisual(element, {dash: false, shadow: false});
268                 size *= ((!element.board || !element.board.options.point.zoom) ? 1.0 : Math.sqrt(element.board.zoomX * element.board.zoomY));
269 
270                 if (face === 'o') { // circle
271                     this.updateEllipsePrim(element.rendNode, element.coords.scrCoords[1], element.coords.scrCoords[2], size + 1, size + 1);
272                 } else if (face === '[]') { // rectangle
273                     this.updateRectPrim(element.rendNode, element.coords.scrCoords[1] - size, element.coords.scrCoords[2] - size, size * 2, size * 2);
274                 } else { // x, +, <>, ^, v, <, >
275                     this.updatePathPrim(element.rendNode, this.updatePathStringPoint(element, size, face), element.board);
276                 }
277                 this.setShadow(element);
278             }
279         },
280 
281         /**
282          * Changes the style of a {@link JXG.Point}. This is required because the point styles differ in what
283          * elements have to be drawn, e.g. if the point is marked by a "x" or a "+" two lines are drawn, if
284          * it's marked by spot a circle is drawn. This method removes the old renderer element(s) and creates
285          * the new one(s).
286          * @param {JXG.Point} element Reference to a {@link JXG.Point} object, that's style is changed.
287          * @see Point
288          * @see JXG.Point
289          * @see JXG.AbstractRenderer#updatePoint
290          * @see JXG.AbstractRenderer#drawPoint
291          */
292         changePointStyle: function (element) {
293             var node = this.getElementById(element.id);
294 
295             // remove the existing point rendering node
296             if (Type.exists(node)) {
297                 this.remove(node);
298             }
299 
300             // and make a new one
301             this.drawPoint(element);
302             Type.clearVisPropOld(element);
303 
304             if (!element.visProp.visible) {
305                 this.hide(element);
306             }
307 
308             if (element.visProp.draft) {
309                 this.setDraft(element);
310             }
311         },
312 
313         /* ******************************** *
314          *           Lines                  *
315          * ******************************** */
316 
317         /**
318          * Draws a line on the {@link JXG.Board}.
319          * @param {JXG.Line} element Reference to a line object, that has to be drawn.
320          * @see Line
321          * @see JXG.Line
322          * @see JXG.AbstractRenderer#updateLine
323          */
324         drawLine: function (element) {
325             element.rendNode = this.appendChildPrim(this.createPrim('line', element.id), element.visProp.layer);
326             this.appendNodesToElement(element, 'lines');
327             this.updateLine(element);
328         },
329 
330         /**
331          * Updates visual appearance of the renderer element assigned to the given {@link JXG.Line}.
332          * @param {JXG.Line} element Reference to the {@link JXG.Line} object that has to be updated.
333          * @see Line
334          * @see JXG.Line
335          * @see JXG.AbstractRenderer#drawLine
336          */
337         updateLine: function (element) {
338             var s, d, d1x, d1y, d2x, d2y,
339                 c1 = new Coords(Const.COORDS_BY_USER, element.point1.coords.usrCoords, element.board),
340                 c2 = new Coords(Const.COORDS_BY_USER, element.point2.coords.usrCoords, element.board),
341                 minlen = 10,
342                 margin = null;
343 
344             if (element.visProp.firstarrow || element.visProp.lastarrow) {
345                 margin = -4;
346             }
347             Geometry.calcStraight(element, c1, c2, margin);
348 
349             d1x = d1y = d2x = d2y = 0.0;
350             /*
351                Handle arrow heads.
352 
353                The arrow head is an equilateral triangle with base length 10 and height 10.
354                These 10 units are scaled to strokeWidth*3 pixels or minlen pixels.
355             */
356             s = Math.max(parseInt(element.visProp.strokewidth, 10) * 3, minlen);
357             d = c1.distance(Const.COORDS_BY_SCREEN, c2);
358             if (element.visProp.lastarrow && element.board.renderer.type !== 'vml' && d >= minlen/*Mat.eps*/) {
359                 d2x = (c2.scrCoords[1] - c1.scrCoords[1]) * s / d;
360                 d2y = (c2.scrCoords[2] - c1.scrCoords[2]) * s / d;
361             }
362             if (element.visProp.firstarrow && element.board.renderer.type !== 'vml' && d >= minlen /* Mat.eps*/) {
363                 d1x = (c2.scrCoords[1] - c1.scrCoords[1]) * s / d;
364                 d1y = (c2.scrCoords[2] - c1.scrCoords[2]) * s / d;
365             }
366 
367             this.updateLinePrim(element.rendNode,
368                 c1.scrCoords[1] + d1x, c1.scrCoords[2] + d1y,
369                 c2.scrCoords[1] - d2x, c2.scrCoords[2] - d2y, element.board);
370             
371             this.makeArrows(element);
372             this._updateVisual(element);
373         },
374 
375         /**
376          * Creates a rendering node for ticks added to a line.
377          * @param {JXG.Line} element A arbitrary line.
378          * @see Line
379          * @see Ticks
380          * @see JXG.Line
381          * @see JXG.Ticks
382          * @see JXG.AbstractRenderer#updateTicks
383          */
384         drawTicks: function (element) {
385             element.rendNode = this.appendChildPrim(this.createPrim('path', element.id), element.visProp.layer);
386             this.appendNodesToElement(element, 'path');
387         },
388 
389         /**
390          * Update {@link Ticks} on a {@link JXG.Line}. This method is only a stub and has to be implemented
391          * in any descendant renderer class.
392          * @param {JXG.Ticks} element Reference of a ticks object that has to be updated.
393          * @see Line
394          * @see Ticks
395          * @see JXG.Line
396          * @see JXG.Ticks
397          * @see JXG.AbstractRenderer#drawTicks
398          */
399         updateTicks: function (element) { /* stub */ },
400 
401         /* **************************
402          *    Curves
403          * **************************/
404 
405         /**
406          * Draws a {@link JXG.Curve} on the {@link JXG.Board}.
407          * @param {JXG.Curve} element Reference to a graph object, that has to be plotted.
408          * @see Curve
409          * @see JXG.Curve
410          * @see JXG.AbstractRenderer#updateCurve
411          */
412         drawCurve: function (element) {
413             element.rendNode = this.appendChildPrim(this.createPrim('path', element.id), element.visProp.layer);
414             this.appendNodesToElement(element, 'path');
415             this._updateVisual(element, {shadow: true}, true);
416             this.updateCurve(element);
417         },
418 
419         /**
420          * Updates visual appearance of the renderer element assigned to the given {@link JXG.Curve}.
421          * @param {JXG.Curve} element Reference to a {@link JXG.Curve} object, that has to be updated.
422          * @see Curve
423          * @see JXG.Curve
424          * @see JXG.AbstractRenderer#drawCurve
425          */
426         updateCurve: function (element) {
427             this._updateVisual(element);
428             if (element.visProp.handdrawing) {
429                 this.updatePathPrim(element.rendNode, this.updatePathStringBezierPrim(element), element.board);
430             } else {
431                 this.updatePathPrim(element.rendNode, this.updatePathStringPrim(element), element.board);
432             }
433             if (element.numberPoints > 1) {
434                 this.makeArrows(element);
435             }
436         },
437 
438         /* **************************
439          *    Circle related stuff
440          * **************************/
441 
442         /**
443          * Draws a {@link JXG.Circle}
444          * @param {JXG.Circle} element Reference to a {@link JXG.Circle} object that has to be drawn.
445          * @see Circle
446          * @see JXG.Circle
447          * @see JXG.AbstractRenderer#updateEllipse
448          */
449         drawEllipse: function (element) {
450             element.rendNode = this.appendChildPrim(this.createPrim('ellipse', element.id), element.visProp.layer);
451             this.appendNodesToElement(element, 'ellipse');
452             this.updateEllipse(element);
453         },
454 
455         /**
456          * Updates visual appearance of a given {@link JXG.Circle} on the {@link JXG.Board}.
457          * @param {JXG.Circle} element Reference to a {@link JXG.Circle} object, that has to be updated.
458          * @see Circle
459          * @see JXG.Circle
460          * @see JXG.AbstractRenderer#drawEllipse
461          */
462         updateEllipse: function (element) {
463             this._updateVisual(element);
464 
465             var radius = element.Radius();
466 
467             if (radius > 0.0 &&
468                     Math.abs(element.center.coords.usrCoords[0]) > Mat.eps &&
469                     !isNaN(radius + element.center.coords.scrCoords[1] + element.center.coords.scrCoords[2]) &&
470                     radius * element.board.unitX < 2000000) {
471                 this.updateEllipsePrim(element.rendNode, element.center.coords.scrCoords[1],
472                     element.center.coords.scrCoords[2], (radius * element.board.unitX), (radius * element.board.unitY));
473             }
474         },
475 
476 
477         /* **************************
478          *   Polygon related stuff
479          * **************************/
480 
481         /**
482          * Draws a {@link JXG.Polygon} on the {@link JXG.Board}.
483          * @param {JXG.Polygon} element Reference to a Polygon object, that is to be drawn.
484          * @see Polygon
485          * @see JXG.Polygon
486          * @see JXG.AbstractRenderer#updatePolygon
487          */
488         drawPolygon: function (element) {
489             element.rendNode = this.appendChildPrim(this.createPrim('polygon', element.id), element.visProp.layer);
490             this.appendNodesToElement(element, 'polygon');
491             this.updatePolygon(element);
492         },
493 
494         /**
495          * Updates properties of a {@link JXG.Polygon}'s rendering node.
496          * @param {JXG.Polygon} element Reference to a {@link JXG.Polygon} object, that has to be updated.
497          * @see Polygon
498          * @see JXG.Polygon
499          * @see JXG.AbstractRenderer#drawPolygon
500          */
501         updatePolygon: function (element) {
502             // here originally strokecolor wasn't updated but strokewidth was
503             // but if there's no strokecolor i don't see why we should update strokewidth.
504             this._updateVisual(element, {stroke: true, dash: true});
505             this.updatePolygonPrim(element.rendNode, element);
506         },
507 
508         /* **************************
509          *    Text related stuff
510          * **************************/
511 
512         /**
513          * Shows a small copyright notice in the top left corner of the board.
514          * @param {String} str The copyright notice itself
515          * @param {Number} fontsize Size of the font the copyright notice is written in
516          */
517         displayCopyright: function (str, fontsize) { /* stub */ },
518 
519         /**
520          * An internal text is a {@link JXG.Text} element which is drawn using only
521          * the given renderer but no HTML. This method is only a stub, the drawing
522          * is done in the special renderers.
523          * @param {JXG.Text} element Reference to a {@link JXG.Text} object
524          * @see Text
525          * @see JXG.Text
526          * @see JXG.AbstractRenderer#updateInternalText
527          * @see JXG.AbstractRenderer#drawText
528          * @see JXG.AbstractRenderer#updateText
529          * @see JXG.AbstractRenderer#updateTextStyle
530          */
531         drawInternalText: function (element) { /* stub */ },
532 
533         /**
534          * Updates visual properties of an already existing {@link JXG.Text} element.
535          * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be updated.
536          * @see Text
537          * @see JXG.Text
538          * @see JXG.AbstractRenderer#drawInternalText
539          * @see JXG.AbstractRenderer#drawText
540          * @see JXG.AbstractRenderer#updateText
541          * @see JXG.AbstractRenderer#updateTextStyle
542          */
543         updateInternalText: function (element) { /* stub */ },
544 
545         /**
546          * Displays a {@link JXG.Text} on the {@link JXG.Board} by putting a HTML div over it.
547          * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be displayed
548          * @see Text
549          * @see JXG.Text
550          * @see JXG.AbstractRenderer#drawInternalText
551          * @see JXG.AbstractRenderer#updateText
552          * @see JXG.AbstractRenderer#updateInternalText
553          * @see JXG.AbstractRenderer#updateTextStyle
554          */
555         drawText: function (element) {
556             var node, z;
557 
558             if (element.visProp.display === 'html' && Env.isBrowser) {
559                 node = this.container.ownerDocument.createElement('div');
560                 node.style.position = 'absolute';
561 
562                 node.className = element.visProp.cssclass;
563                 if (this.container.style.zIndex === '') {
564                     z = 0;
565                 } else {
566                     z = parseInt(this.container.style.zIndex, 10);
567                 }
568 
569                 node.style.zIndex = z + element.board.options.layer.text;
570                 this.container.appendChild(node);
571                 node.setAttribute('id', this.container.id + '_' + element.id);
572             } else {
573                 node = this.drawInternalText(element);
574             }
575 
576             element.rendNode = node;
577             element.htmlStr = '';
578             this.updateText(element);
579         },
580 
581         /**
582          * Updates visual properties of an already existing {@link JXG.Text} element.
583          * @param {JXG.Text} el Reference to an {@link JXG.Text} object, that has to be updated.
584          * @see Text
585          * @see JXG.Text
586          * @see JXG.AbstractRenderer#drawText
587          * @see JXG.AbstractRenderer#drawInternalText
588          * @see JXG.AbstractRenderer#updateInternalText
589          * @see JXG.AbstractRenderer#updateTextStyle
590          */
591         updateText: function (el) {
592             var content = el.plaintext, v, c;
593 
594             if (el.visProp.visible) {
595                 this.updateTextStyle(el, false);
596 
597                 if (el.visProp.display === 'html') {
598                     // Set the position
599                     if (!isNaN(el.coords.scrCoords[1] + el.coords.scrCoords[2])) {
600 
601                         // Horizontal
602                         c = el.coords.scrCoords[1];
603                         // webkit seems to fail for extremely large values for c.
604                         c = Math.abs(c) < 1000000 ? c : 1000000;
605 
606                         if (el.visProp.anchorx === 'right') {
607                             v = Math.floor(el.board.canvasWidth - c);
608                         } else if (el.visProp.anchorx === 'middle') {
609                             v = Math.floor(c - 0.5 * el.size[0]);
610                         } else { // 'left'
611                             v = Math.floor(c);
612                         }
613 
614                         if (el.visPropOld.left !== (el.visProp.anchorx + v)) {
615                             if (el.visProp.anchorx === 'right') {
616                                 el.rendNode.style.right = v + 'px';
617                                 el.rendNode.style.left = 'auto';
618                             } else {
619                                 el.rendNode.style.left = v + 'px';
620                                 el.rendNode.style.right = 'auto';
621                             }
622                             el.visPropOld.left = el.visProp.anchorx + v;
623                         }
624 
625                         // Vertical
626                         c = el.coords.scrCoords[2] + this.vOffsetText;
627                         c = Math.abs(c) < 1000000 ? c : 1000000;
628 
629                         if (el.visProp.anchory === 'bottom') {
630                             v = Math.floor(el.board.canvasHeight - c);
631                         } else if (el.visProp.anchory === 'middle') {
632                             v = Math.floor(c - 0.5 * el.size[1]);
633                         } else { // top
634                             v = Math.floor(c);
635                         }
636 
637                         if (el.visPropOld.top !== (el.visProp.anchory + v)) {
638                             if (el.visProp.anchory === 'bottom') {
639                                 el.rendNode.style.top = 'auto';
640                                 el.rendNode.style.bottom = v + 'px';
641                             } else {
642                                 el.rendNode.style.bottom = 'auto';
643                                 el.rendNode.style.top = v + 'px';
644                             }
645                             el.visPropOld.top = el.visProp.anchory + v;
646                         }
647                     }
648 
649                     // Set the content
650                     if (el.htmlStr !== content) {
651                         el.rendNode.innerHTML = content;
652                         el.htmlStr = content;
653 
654                         if (el.visProp.usemathjax) {
655                             // typesetting directly might not work because mathjax was not loaded completely
656                             // see http://www.mathjax.org/docs/1.1/typeset.html
657                             MathJax.Hub.Queue(['Typeset', MathJax.Hub, el.rendNode]);
658                         } else if (el.visProp.useasciimathml) {
659                             // This is not a constructor.
660                             // See http://www1.chapman.edu/~jipsen/mathml/asciimath.html for more information
661                             // about AsciiMathML and the project's source code.
662                             AMprocessNode(el.rendNode, false);
663                         }
664                     }
665                     this.transformImage(el, el.transformations);
666                 } else {
667                     this.updateInternalText(el);
668                 }
669             }
670         },
671 
672         /**
673          * Updates font-size, color and opacity propertiey and CSS style properties of a {@link JXG.Text} node.
674          * This function is also called by highlight() and nohighlight().
675          * @param {JXG.Text} element Reference to the {@link JXG.Text} object, that has to be updated.
676          * @param {Boolean} doHighlight
677          * @see Text
678          * @see JXG.Text
679          * @see JXG.AbstractRenderer#drawText
680          * @see JXG.AbstractRenderer#drawInternalText
681          * @see JXG.AbstractRenderer#updateText
682          * @see JXG.AbstractRenderer#updateInternalText
683          * @see JXG.AbstractRenderer#updateInternalTextStyle
684          */
685         updateTextStyle: function (element, doHighlight) {
686             var fs, so, sc, css,
687                 ev = element.visProp,
688                 display = Env.isBrowser ? ev.display : 'internal';
689 
690             if (doHighlight) {
691                 sc = ev.highlightstrokecolor;
692                 so = ev.highlightstrokeopacity;
693                 css = ev.highlightcssclass;
694             } else {
695                 sc = ev.strokecolor;
696                 so = ev.strokeopacity;
697                 css = ev.cssclass;
698             }
699 
700             // This part is executed for all text elements except internal texts in canvas.
701             if (display === 'html' || (this.type !== 'canvas' && this.type !== 'no')) {
702                 fs = Type.evaluate(element.visProp.fontsize);
703                 if (element.visPropOld.fontsize !== fs) {
704                     element.needsSizeUpdate = true;
705                     try {
706                         element.rendNode.style.fontSize = fs + 'px';
707                     } catch (e) {
708                         // IE needs special treatment.
709                         element.rendNode.style.fontSize = fs;
710                     }
711                     element.visPropOld.fontsize = fs;
712                 }
713 
714             }
715 
716             if (display === 'html') {
717                 if (element.visPropOld.cssclass !== css) {
718                     element.rendNode.className = css;
719                     element.visPropOld.cssclass = css;
720                     element.needsSizeUpdate = true;
721                 }
722                 this.setObjectStrokeColor(element, sc, so);
723             } else {
724                 this.updateInternalTextStyle(element, sc, so);
725             }
726             return this;
727         },
728 
729         /**
730          * Set color and opacity of internal texts.
731          * This method is used for Canvas and VML.
732          * SVG needs its own version.
733          * @private
734          * @see JXG.AbstractRenderer#updateTextStyle
735          * @see JXG.SVGRenderer#updateInternalTextStyle
736          */
737         updateInternalTextStyle: function (element, strokeColor, strokeOpacity) {
738             this.setObjectStrokeColor(element, strokeColor, strokeOpacity);
739         },
740 
741         /* **************************
742          *    Image related stuff
743          * **************************/
744 
745         /**
746          * Draws an {@link JXG.Image} on a board; This is just a template that has to be implemented by special
747          * renderers.
748          * @param {JXG.Image} element Reference to the image object that is to be drawn
749          * @see Image
750          * @see JXG.Image
751          * @see JXG.AbstractRenderer#updateImage
752          */
753         drawImage: function (element) { /* stub */ },
754 
755         /**
756          * Updates the properties of an {@link JXG.Image} element.
757          * @param {JXG.Image} element Reference to an {@link JXG.Image} object, that has to be updated.
758          * @see Image
759          * @see JXG.Image
760          * @see JXG.AbstractRenderer#drawImage
761          */
762         updateImage: function (element) {
763             this.updateRectPrim(element.rendNode, element.coords.scrCoords[1],
764                 element.coords.scrCoords[2] - element.size[1], element.size[0], element.size[1]);
765 
766             this.updateImageURL(element);
767             this.transformImage(element, element.transformations);
768             this._updateVisual(element, {stroke: true, dash: true}, true);
769         },
770 
771         /**
772          * Multiplication of transformations without updating. That means, at that point it is expected that the
773          * matrices contain numbers only. First, the origin in user coords is translated to <tt>(0,0)</tt> in screen
774          * coords. Then, the stretch factors are divided out. After the transformations in user coords, the  stretch
775          * factors are multiplied in again, and the origin in user coords is translated back to its position. This
776          * method does not have to be implemented in a new renderer.
777          * @param {JXG.GeometryElement} element A JSXGraph element. We only need its board property.
778          * @param {Array} transformations An array of JXG.Transformations.
779          * @returns {Array} A matrix represented by a two dimensional array of numbers.
780          * @see JXG.AbstractRenderer#transformImage
781          */
782         joinTransforms: function (element, transformations) {
783             var i,
784                 m = [[1, 0, 0], [0, 1, 0], [0, 0, 1]],
785                 ox = element.board.origin.scrCoords[1],
786                 oy = element.board.origin.scrCoords[2],
787                 ux = element.board.unitX,
788                 uy = element.board.unitY,
789                 // Translate to 0,0 in screen coords
790                 mpre1 =  [[1,   0, 0],
791                     [-ox, 1, 0],
792                     [-oy, 0, 1]],
793                 // Scale
794                 mpre2 =  [[1, 0,     0],
795                     [0, 1 / ux,  0],
796                     [0, 0, -1 / uy]],
797                 // Scale back
798                 mpost2 = [[1, 0,   0],
799                     [0, ux,  0],
800                     [0, 0, -uy]],
801                 // Translate back
802                 mpost1 = [[1,  0, 0],
803                     [ox, 1, 0],
804                     [oy, 0, 1]],
805                 len = transformations.length;
806 
807             for (i = 0; i < len; i++) {
808                 m = Mat.matMatMult(mpre1, m);
809                 m = Mat.matMatMult(mpre2, m);
810                 m = Mat.matMatMult(transformations[i].matrix, m);
811                 m = Mat.matMatMult(mpost2, m);
812                 m = Mat.matMatMult(mpost1, m);
813             }
814             return m;
815         },
816 
817         /**
818          * Applies transformations on images and text elements. This method is just a stub and has to be implemented in
819          * all descendant classes where text and image transformations are to be supported.
820          * @param {JXG.Image|JXG.Text} element A {@link JXG.Image} or {@link JXG.Text} object.
821          * @param {Array} transformations An array of {@link JXG.Transformation} objects. This is usually the
822          * transformations property of the given element <tt>el</tt>.
823          */
824         transformImage: function (element, transformations) { /* stub */ },
825 
826         /**
827          * If the URL of the image is provided by a function the URL has to be updated during updateImage()
828          * @param {JXG.Image} element Reference to an image object.
829          * @see JXG.AbstractRenderer#updateImage
830          */
831         updateImageURL: function (element) { /* stub */ },
832 
833         /**
834          * Updates CSS style properties of a {@link JXG.Image} node.
835          * In SVGRenderer opacity is the only available style element.
836          * This function is called by highlight() and nohighlight().
837          * This function works for VML.
838          * It does not work for Canvas.
839          * SVGRenderer overwrites this method.
840          * @param {JXG.Text} el Reference to the {@link JXG.Image} object, that has to be updated.
841          * @param {Boolean} doHighlight
842          * @see Image
843          * @see JXG.Image
844          * @see JXG.AbstractRenderer#highlight
845          * @see JXG.AbstractRenderer#noHighlight
846          */
847         updateImageStyle: function (el, doHighlight) {
848             el.rendNode.className = doHighlight ? el.visProp.highlightcssclass : el.visProp.cssclass;
849         },
850 
851 
852         /* **************************
853          * Render primitive objects
854          * **************************/
855 
856         /**
857          * Appends a node to a specific layer level. This is just an abstract method and has to be implemented
858          * in all renderers that want to use the <tt>createPrim</tt> model to draw.
859          * @param {Node} node A DOM tree node.
860          * @param {Number} level The layer the node is attached to. This is the index of the layer in
861          * {@link JXG.SVGRenderer#layer} or the <tt>z-index</tt> style property of the node in VMLRenderer.
862          */
863         appendChildPrim: function (node, level) { /* stub */ },
864 
865         /**
866          * Stores the rendering nodes. This is an abstract method which has to be implemented in all renderers that use
867          * the <tt>createPrim</tt> method.
868          * @param {JXG.GeometryElement} element A JSXGraph element.
869          * @param {String} type The XML node name. Only used in VMLRenderer.
870          */
871         appendNodesToElement: function (element, type) { /* stub */ },
872 
873         /**
874          * Creates a node of a given type with a given id.
875          * @param {String} type The type of the node to create.
876          * @param {String} id Set the id attribute to this.
877          * @returns {Node} Reference to the created node.
878          */
879         createPrim: function (type, id) {
880             /* stub */
881             return null;
882         },
883 
884         /**
885          * Removes an element node. Just a stub.
886          * @param {Node} node The node to remove.
887          */
888         remove: function (node) { /* stub */ },
889 
890         /**
891          * Can be used to create the nodes to display arrows. This is an abstract method which has to be implemented
892          * in any descendant renderer.
893          * @param {JXG.GeometryElement} element The element the arrows are to be attached to.
894          */
895         makeArrows: function (element) { /* stub */ },
896 
897         /**
898          * Updates an ellipse node primitive. This is an abstract method which has to be implemented in all renderers
899          * that use the <tt>createPrim</tt> method.
900          * @param {Node} node Reference to the node.
901          * @param {Number} x Centre X coordinate
902          * @param {Number} y Centre Y coordinate
903          * @param {Number} rx The x-axis radius.
904          * @param {Number} ry The y-axis radius.
905          */
906         updateEllipsePrim: function (node, x, y, rx, ry) { /* stub */ },
907 
908         /**
909          * Refreshes a line node. This is an abstract method which has to be implemented in all renderers that use
910          * the <tt>createPrim</tt> method.
911          * @param {Node} node The node to be refreshed.
912          * @param {Number} p1x The first point's x coordinate.
913          * @param {Number} p1y The first point's y coordinate.
914          * @param {Number} p2x The second point's x coordinate.
915          * @param {Number} p2y The second point's y coordinate.
916          * @param {JXG.Board} board
917          */
918         updateLinePrim: function (node, p1x, p1y, p2x, p2y, board) { /* stub */ },
919 
920         /**
921          * Updates a path element. This is an abstract method which has to be implemented in all renderers that use
922          * the <tt>createPrim</tt> method.
923          * @param {Node} node The path node.
924          * @param {String} pathString A string formatted like e.g. <em>'M 1,2 L 3,1 L5,5'</em>. The format of the string
925          * depends on the rendering engine.
926          * @param {JXG.Board} board Reference to the element's board.
927          */
928         updatePathPrim: function (node, pathString, board) { /* stub */ },
929 
930         /**
931          * Builds a path data string to draw a point with a face other than <em>rect</em> and <em>circle</em>. Since
932          * the format of such a string usually depends on the renderer this method
933          * is only an abstract method. Therefore, it has to be implemented in the descendant renderer itself unless
934          * the renderer does not use the createPrim interface but the draw* interfaces to paint.
935          * @param {JXG.Point} element The point element
936          * @param {Number} size A positive number describing the size. Usually the half of the width and height of
937          * the drawn point.
938          * @param {String} type A string describing the point's face. This method only accepts the shortcut version of
939          * each possible face: <tt>x, +, <>, ^, v, >, <
940          */
941         updatePathStringPoint: function (element, size, type) { /* stub */ },
942 
943         /**
944          * Builds a path data string from a {@link JXG.Curve} element. Since the path data strings heavily depend on the
945          * underlying rendering technique this method is just a stub. Although such a path string is of no use for the
946          * CanvasRenderer, this method is used there to draw a path directly.
947          * @param element
948          */
949         updatePathStringPrim: function (element) { /* stub */ },
950 
951         /**
952          * Builds a path data string from a {@link JXG.Curve} element such that the curve looks like hand drawn. Since
953          * the path data strings heavily depend on the underlying rendering technique this method is just a stub.
954          * Although such a path string is of no use for the CanvasRenderer, this method is used there to draw a path
955          * directly.
956          * @param element
957          */
958         updatePathStringBezierPrim: function (element) { /* stub */ },
959 
960 
961         /**
962          * Update a polygon primitive.
963          * @param {Node} node
964          * @param {JXG.Polygon} element A JSXGraph element of type {@link JXG.Polygon}
965          */
966         updatePolygonPrim: function (node, element) { /* stub */ },
967 
968         /**
969          * Update a rectangle primitive. This is used only for points with face of type 'rect'.
970          * @param {Node} node The node yearning to be updated.
971          * @param {Number} x x coordinate of the top left vertex.
972          * @param {Number} y y coordinate of the top left vertex.
973          * @param {Number} w Width of the rectangle.
974          * @param {Number} h The rectangle's height.
975          */
976         updateRectPrim: function (node, x, y, w, h) { /* stub */ },
977 
978         /* **************************
979          *  Set Attributes
980          * **************************/
981 
982         /**
983          * Sets a node's attribute.
984          * @param {Node} node The node that is to be updated.
985          * @param {String} key Name of the attribute.
986          * @param {String} val New value for the attribute.
987          */
988         setPropertyPrim: function (node, key, val) { /* stub */ },
989 
990         /**
991          * Shows a hidden element on the canvas; Only a stub, requires implementation in the derived renderer.
992          * @param {JXG.GeometryElement} element Reference to the object that has to appear.
993          * @see JXG.AbstractRenderer#hide
994          */
995         show: function (element) { /* stub */ },
996 
997         /**
998          * Hides an element on the canvas; Only a stub, requires implementation in the derived renderer.
999          * @param {JXG.GeometryElement} element Reference to the geometry element that has to disappear.
1000          * @see JXG.AbstractRenderer#show
1001          */
1002         hide: function (element) { /* stub */ },
1003 
1004         /**
1005          * Sets the buffering as recommended by SVGWG. Until now only Opera supports this and will be ignored by other
1006          * browsers. Although this feature is only supported by SVG we have this method in {@link JXG.AbstractRenderer}
1007          * because it is called from outside the renderer.
1008          * @param {Node} node The SVG DOM Node which buffering type to update.
1009          * @param {String} type Either 'auto', 'dynamic', or 'static'. For an explanation see
1010          *   {@link http://www.w3.org/TR/SVGTiny12/painting.html#BufferedRenderingProperty}.
1011          */
1012         setBuffering: function (node, type) { /* stub */ },
1013 
1014         /**
1015          * Sets an element's dash style.
1016          * @param {JXG.GeometryElement} element An JSXGraph element.
1017          */
1018         setDashStyle: function (element) { /* stub */ },
1019 
1020         /**
1021          * Puts an object into draft mode, i.e. it's visual appearance will be changed. For GEONE<sub>x</sub>T backwards
1022          * compatibility.
1023          * @param {JXG.GeometryElement} element Reference of the object that is in draft mode.
1024          */
1025         setDraft: function (element) {
1026             if (!element.visProp.draft) {
1027                 return;
1028             }
1029             var draftColor = element.board.options.elements.draft.color,
1030                 draftOpacity = element.board.options.elements.draft.opacity;
1031 
1032             if (element.type === Const.OBJECT_TYPE_POLYGON) {
1033                 this.setObjectFillColor(element, draftColor, draftOpacity);
1034             } else {
1035                 if (element.elementClass === Const.OBJECT_CLASS_POINT) {
1036                     this.setObjectFillColor(element, draftColor, draftOpacity);
1037                 } else {
1038                     this.setObjectFillColor(element, 'none', 0);
1039                 }
1040                 this.setObjectStrokeColor(element, draftColor, draftOpacity);
1041                 this.setObjectStrokeWidth(element, element.board.options.elements.draft.strokeWidth);
1042             }
1043         },
1044 
1045         /**
1046          * Puts an object from draft mode back into normal mode.
1047          * @param {JXG.GeometryElement} element Reference of the object that no longer is in draft mode.
1048          */
1049         removeDraft: function (element) {
1050             if (element.type === Const.OBJECT_TYPE_POLYGON) {
1051                 this.setObjectFillColor(element, element.visProp.fillcolor, element.visProp.fillopacity);
1052             } else {
1053                 if (element.type === Const.OBJECT_CLASS_POINT) {
1054                     this.setObjectFillColor(element, element.visProp.fillcolor, element.visProp.fillopacity);
1055                 }
1056                 this.setObjectStrokeColor(element, element.visProp.strokecolor, element.visProp.strokeopacity);
1057                 this.setObjectStrokeWidth(element, element.visProp.strokewidth);
1058             }
1059         },
1060 
1061         /**
1062          * Sets up nodes for rendering a gradient fill.
1063          * @param element
1064          */
1065         setGradient: function (element) { /* stub */ },
1066 
1067         /**
1068          * Updates the gradient fill.
1069          * @param {JXG.GeometryElement} element An JSXGraph element with an area that can be filled.
1070          */
1071         updateGradient: function (element) { /* stub */ },
1072 
1073         /**
1074          * Sets an objects fill color.
1075          * @param {JXG.GeometryElement} element Reference of the object that wants a new fill color.
1076          * @param {String} color Color in a HTML/CSS compatible format. If you don't want any fill color at all, choose
1077          * 'none'.
1078          * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1.
1079          */
1080         setObjectFillColor: function (element, color, opacity) { /* stub */ },
1081 
1082         /**
1083          * Changes an objects stroke color to the given color.
1084          * @param {JXG.GeometryElement} element Reference of the {@link JXG.GeometryElement} that gets a new stroke
1085          * color.
1086          * @param {String} color Color value in a HTML compatible format, e.g. <strong>#00ff00</strong> or
1087          * <strong>green</strong> for green.
1088          * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1.
1089          */
1090         setObjectStrokeColor: function (element, color, opacity) { /* stub */ },
1091 
1092         /**
1093          * Sets an element's stroke width.
1094          * @param {JXG.GeometryElement} element Reference to the geometry element.
1095          * @param {Number} width The new stroke width to be assigned to the element.
1096          */
1097         setObjectStrokeWidth: function (element, width) { /* stub */ },
1098 
1099         /**
1100          * Sets the shadow properties to a geometry element. This method is only a stub, it is implemented in the actual
1101          * renderers.
1102          * @param {JXG.GeometryElement} element Reference to a geometry object, that should get a shadow
1103          */
1104         setShadow: function (element) { /* stub */ },
1105 
1106         /**
1107          * Highlights an object, i.e. changes the current colors of the object to its highlighting colors
1108          * @param {JXG.GeometryElement} element Reference of the object that will be highlighted.
1109          * @returns {JXG.AbstractRenderer} Reference to the renderer
1110          * @see JXG.AbstractRenderer#updateTextStyle
1111          */
1112         highlight: function (element) {
1113             var i, ev = element.visProp;
1114 
1115             if (!ev.draft) {
1116                 if (element.type === Const.OBJECT_TYPE_POLYGON) {
1117                     this.setObjectFillColor(element, ev.highlightfillcolor, ev.highlightfillopacity);
1118                     for (i = 0; i < element.borders.length; i++) {
1119                         this.setObjectStrokeColor(element.borders[i], element.borders[i].visProp.highlightstrokecolor,
1120                             element.borders[i].visProp.highlightstrokeopacity);
1121                     }
1122                 } else {
1123                     if (element.type === Const.OBJECT_TYPE_TEXT) {
1124                         this.updateTextStyle(element, true);
1125                     } else if (element.type === Const.OBJECT_TYPE_IMAGE) {
1126                         this.updateImageStyle(element, true);
1127                     } else {
1128                         this.setObjectStrokeColor(element, ev.highlightstrokecolor, ev.highlightstrokeopacity);
1129                         this.setObjectFillColor(element, ev.highlightfillcolor, ev.highlightfillopacity);
1130                     }
1131                 }
1132                 if (ev.highlightstrokewidth) {
1133                     this.setObjectStrokeWidth(element, Math.max(ev.highlightstrokewidth, ev.strokewidth));
1134                 }
1135             }
1136 
1137             return this;
1138         },
1139 
1140         /**
1141          * Uses the normal colors of an object, i.e. the opposite of {@link JXG.AbstractRenderer#highlight}.
1142          * @param {JXG.GeometryElement} element Reference of the object that will get its normal colors.
1143          * @returns {JXG.AbstractRenderer} Reference to the renderer
1144          * @see JXG.AbstractRenderer#updateTextStyle
1145          */
1146         noHighlight: function (element) {
1147             var i, ev = element.visProp;
1148 
1149             if (!element.visProp.draft) {
1150                 if (element.type === Const.OBJECT_TYPE_POLYGON) {
1151                     this.setObjectFillColor(element, ev.fillcolor, ev.fillopacity);
1152                     for (i = 0; i < element.borders.length; i++) {
1153                         this.setObjectStrokeColor(element.borders[i], element.borders[i].visProp.strokecolor,
1154                             element.borders[i].visProp.strokeopacity);
1155                     }
1156                 } else {
1157                     if (element.type === Const.OBJECT_TYPE_TEXT) {
1158                         this.updateTextStyle(element, false);
1159                     } else if (element.type === Const.OBJECT_TYPE_IMAGE) {
1160                         this.updateImageStyle(element, false);
1161                     } else {
1162                         this.setObjectStrokeColor(element, ev.strokecolor, ev.strokeopacity);
1163                         this.setObjectFillColor(element, ev.fillcolor, ev.fillopacity);
1164                     }
1165                 }
1166                 this.setObjectStrokeWidth(element, ev.strokewidth);
1167             }
1168 
1169             return this;
1170         },
1171 
1172         /* **************************
1173          * renderer control
1174          * **************************/
1175 
1176         /**
1177          * Stop redraw. This method is called before every update, so a non-vector-graphics based renderer can use this
1178          * method to delete the contents of the drawing panel. This is an abstract method every descendant renderer
1179          * should implement, if appropriate.
1180          * @see JXG.AbstractRenderer#unsuspendRedraw
1181          */
1182         suspendRedraw: function () { /* stub */ },
1183 
1184         /**
1185          * Restart redraw. This method is called after updating all the rendering node attributes.
1186          * @see JXG.AbstractRenderer#suspendRedraw
1187          */
1188         unsuspendRedraw: function () { /* stub */ },
1189 
1190         /**
1191          * The tiny zoom bar shown on the bottom of a board (if showNavigation on board creation is true).
1192          * @param {JXG.Board} board Reference to a JSXGraph board.
1193          */
1194         drawZoomBar: function (board) {
1195             var doc,
1196                 node,
1197                 cancelbubble = function (e) {
1198                     if (!e) {
1199                         e = window.event;
1200                     }
1201 
1202                     if (e.stopPropagation) {
1203                         // Non IE<=8
1204                         e.stopPropagation();
1205                     } else {
1206                         e.cancelBubble = true;
1207                     }
1208                 },
1209                 createButton = function (label, handler) {
1210                     var button;
1211 
1212                     button = doc.createElement('span');
1213                     node.appendChild(button);
1214                     button.appendChild(board.containerObj.ownerDocument.createTextNode(label));
1215                     Env.addEvent(button, 'mouseover', function () {
1216                         this.style.backgroundColor = board.options.navbar.highlightFillColor;
1217                     }, button);
1218                     Env.addEvent(button, 'mouseover', function () {
1219                         this.style.backgroundColor = board.options.navbar.highlightFillColor;
1220                     }, button);
1221                     Env.addEvent(button, 'mouseout', function () {
1222                         this.style.backgroundColor = board.options.navbar.fillColor;
1223                     }, button);
1224 
1225                     Env.addEvent(button, 'click', handler, board);
1226                     // prevent the click from bubbling down to the board
1227                     Env.addEvent(button, 'mouseup', cancelbubble, board);
1228                     Env.addEvent(button, 'mousedown', cancelbubble, board);
1229                     Env.addEvent(button, 'touchend', cancelbubble, board);
1230                     Env.addEvent(button, 'touchstart', cancelbubble, board);
1231                 };
1232 
1233             if (Env.isBrowser) {
1234                 doc = board.containerObj.ownerDocument;
1235                 node = doc.createElement('div');
1236 
1237                 node.setAttribute('id', board.containerObj.id + '_navigationbar');
1238 
1239                 node.style.color = board.options.navbar.strokeColor;
1240                 node.style.backgroundColor = board.options.navbar.fillColor;
1241                 node.style.padding = board.options.navbar.padding;
1242                 node.style.position = board.options.navbar.position;
1243                 node.style.fontSize = board.options.navbar.fontSize;
1244                 node.style.cursor = board.options.navbar.cursor;
1245                 node.style.zIndex = board.options.navbar.zIndex;
1246                 board.containerObj.appendChild(node);
1247                 node.style.right = board.options.navbar.right;
1248                 node.style.bottom = board.options.navbar.bottom;
1249 
1250                 // For XHTML we need unicode instead of HTML entities
1251 
1252                 if (board.attr.showreload) {
1253                     // full reload circle: \u27F2
1254                     // the board.reload() method does not exist during the creation
1255                     // of this button. That's why this anonymous function wrapper is required.
1256                     createButton('\u00A0\u21BB\u00A0', function () {
1257                         board.reload();
1258                     });
1259                 }
1260                 
1261                 if (board.attr.showcleartraces) {
1262                     // clear traces symbol (otimes): \u27F2
1263                     createButton('\u00A0\u2297\u00A0', function () {
1264                         board.clearTraces();
1265                     });
1266                 }
1267 
1268                 if (board.attr.shownavigation) {
1269                     createButton('\u00A0\u2013\u00A0', board.zoomOut);
1270                     createButton('\u00A0o\u00A0', board.zoom100);
1271                     createButton('\u00A0+\u00A0', board.zoomIn);
1272                     createButton('\u00A0\u2190\u00A0', board.clickLeftArrow);
1273                     createButton('\u00A0\u2193\u00A0', board.clickUpArrow);
1274                     createButton('\u00A0\u2191\u00A0', board.clickDownArrow);
1275                     createButton('\u00A0\u2192\u00A0', board.clickRightArrow);
1276                 }
1277             }
1278         },
1279 
1280         /**
1281          * Wrapper for getElementById for maybe other renderers which elements are not directly accessible by DOM
1282          * methods like document.getElementById().
1283          * @param {String} id Unique identifier for element.
1284          * @returns {Object} Reference to a JavaScript object. In case of SVG/VMLRenderer it's a reference to a SVG/VML
1285          * node.
1286          */
1287         getElementById: function (id) {
1288             return this.container.ownerDocument.getElementById(this.container.id + '_' + id);
1289         },
1290 
1291         /**
1292          * Remove an element and provide a function that inserts it into its original position. This method
1293          * is taken from this article {@link https://developers.google.com/speed/articles/javascript-dom}.
1294          * @author KeeKim Heng, Google Web Developer
1295          * @param {Element} element The element to be temporarily removed
1296          * @returns {Function} A function that inserts the element into its original position
1297          */
1298         removeToInsertLater: function (element) {
1299             var parentNode = element.parentNode,
1300                 nextSibling = element.nextSibling;
1301 
1302             parentNode.removeChild(element);
1303 
1304             return function () {
1305                 if (nextSibling) {
1306                     parentNode.insertBefore(element, nextSibling);
1307                 } else {
1308                     parentNode.appendChild(element);
1309                 }
1310             };
1311         },
1312 
1313         /**
1314          * Resizes the rendering element
1315          * @param {Number} w New width
1316          * @param {Number} h New height
1317          */
1318         resize: function (w, h) { /* stub */},
1319 
1320         /**
1321          * Create crosshair elements (Fadenkreuz) for presentations.
1322          * @param {Number} n Number of crosshairs.
1323          */
1324         createTouchpoints: function (n) {},
1325 
1326         /**
1327          * Show a specific crosshair.
1328          * @param {Number} i Number of the crosshair to show
1329          */
1330         showTouchpoint: function (i) {},
1331 
1332         /**
1333          * Hide a specific crosshair.
1334          * @param {Number} i Number of the crosshair to show
1335          */
1336         hideTouchpoint: function (i) {},
1337 
1338         /**
1339          * Move a specific crosshair.
1340          * @param {Number} i Number of the crosshair to show
1341          * @param {Array} pos New positon in screen coordinates
1342          */
1343         updateTouchpoint: function (i, pos) {}
1344     });
1345 
1346     return JXG.AbstractRenderer;
1347 });
1348