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