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