1 /*
  2     Copyright 2008-2011
  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: you can redistribute it and/or modify
 13     it under the terms of the GNU Lesser General Public License as published by
 14     the Free Software Foundation, either version 3 of the License, or
 15     (at your option) any later version.
 16 
 17     JSXGraph is distributed in the hope that it will be useful,
 18     but WITHOUT ANY WARRANTY; without even the implied warranty of
 19     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 20     GNU Lesser General Public License for more details.
 21 
 22     You should have received a copy of the GNU Lesser General Public License
 23     along with JSXGraph.  If not, see <http://www.gnu.org/licenses/>.
 24 
 25 */
 26 
 27 /*jshint bitwise: false, curly: true, debug: false, eqeqeq: true, devel: false, evil: false,
 28   forin: false, immed: true, laxbreak: false, newcap: false, noarg: true, nonew: true, onevar: true,
 29    undef: true, white: true, sub: false*/
 30 /*global JXG: true, AMprocessNode: true, MathJax: true, document: true */
 31 
 32 /**
 33  * @fileoverview JSXGraph can use various technologies to render the contents of a construction, e.g.
 34  * SVG, VML, and HTML5 Canvas. To accomplish this, The rendering and the logic and control mechanisms
 35  * are completely separated from each other. Every rendering technology has it's own class, called
 36  * Renderer, e.g. SVGRenderer for SVG, the same for VML and Canvas. The common base for all available
 37  * renderers is the class AbstractRenderer defined in this file.
 38  */
 39 
 40 /**
 41  * <p>This class defines the interface to the graphics part of JSXGraph. This class is an abstract class, it
 42  * actually does not render anything. This is up to the {@link JXG.SVGRenderer}, {@link JXG.VMLRenderer},
 43  * and {@link JXG.CanvasRenderer} classes. We strongly discourage you from using the methods in these classes
 44  * directly. Only the methods which are defined in this class and are not marked as private are guaranteed
 45  * to exist in any renderer instance you can access via {@link JXG.Board#renderer}. But not all methods may
 46  * work as expected.</p>
 47  * <p>The methods of this renderer can be divided into different categories:
 48  * <dl>
 49  *     <dt>Draw basic elements</dt>
 50  *     <dd>In this category we find methods to draw basic elements like {@link JXG.Point}, {@link JXG.Line},
 51  *     and {@link JXG.Curve} as well as assisting methods tightly bound to these basic painters. You do not
 52  *     need to implement these methods in a descendant renderer but instead implement the primitive drawing
 53  *     methods described below. This approach is encouraged when you're using a XML based rendering engine
 54  *     like VML and SVG. If you want to use a bitmap based rendering technique you are supposed to override
 55  *     these methods instead of the primitive drawing methods.</dd>
 56  *     <dt>Draw primitives</dt>
 57  *     <dd>This category summarizes methods to handle primitive nodes. As creation and management of these nodes
 58  *     is different among different the rendering techniques most of these methods are purely virtual and need
 59  *     proper implementation if you choose to not overwrite the basic element drawing methods.</dd>
 60  *     <dt>Attribute manipulation</dt>
 61  *     <dd>In XML based renders you have to manipulate XML nodes and their attributes to change the graphics.
 62  *     For that purpose attribute manipulation methods are defined to set the color, opacity, and other things.
 63  *     Please note that some of these methods are required in bitmap based renderers, too, because some elements
 64  *     like {@link JXG.Text} can be HTML nodes floating over the construction.</dd>
 65  *     <dt>Renderer control</dt>
 66  *     <dd>Methods to clear the drawing board or to stop and to resume the rendering engine.</dd>
 67  * </dl></p>
 68  * @class JXG.AbstractRenderer
 69  * @see JXG.SVGRenderer
 70  * @see JXG.VMLRenderer
 71  * @see JXG.CanvasRenderer
 72  */
 73 JXG.AbstractRenderer = function () {
 74 
 75     // WHY THIS IS A CLASS INSTEAD OF A SINGLETON OBJECT:
 76     //
 77     // The renderers need to keep track of some stuff which is not always the same on different boards,
 78     // like enhancedRendering, reference to the container object, and resolution in VML. Sure, those
 79     // things could be stored in board. But they are rendering related and JXG.Board is already very
 80     // very big.
 81     //
 82     // And we can't save the rendering related data in {SVG,VML,Canvas}Renderer and make only the
 83     // JXG.AbstractRenderer a singleton because of that:
 84     //
 85     // Given an object o with property a set to true
 86     //     var o = {a: true};
 87     // and a class c doing nothing
 88     //     c = function() {};
 89     // Set c's prototype to o
 90     //     c.prototype = o;
 91     // and create an instance of c we get i.a to be true
 92     //     i = new c();
 93     //     i.a;
 94     //     > true
 95     // But we can overwrite this property via
 96     //     c.prototype.a = false;
 97     //     i.a;
 98     //     > false
 99 
100     /**
101      * The vertical offset for {@link Text} elements. Every {@link Text} element will
102      * be placed this amount of pixels below the user given coordinates.
103      * @type number
104      * @default 8
105      */
106     this.vOffsetText = 0;
107 
108     /**
109      * If this property is set to <tt>true</tt> the visual properties of the elements are updated
110      * on every update. Visual properties means: All the stuff stored in the
111      * {@link JXG.GeometryElement#visProp} property won't be set if enhancedRendering is <tt>false</tt>
112      * @type Boolean
113      * @default true
114      */
115     this.enhancedRendering = true;
116 
117     /**
118      * The HTML element that stores the JSXGraph board in it.
119      * @type Node
120      */
121     this.container = null;
122 
123     /**
124      * This is used to easily determine which renderer we are using
125      * @example if (board.renderer.type === 'vml') {
126      *     // do something
127      * }
128      * @type String
129      */
130     this.type = '';
131 };
132 
133 JXG.extend(JXG.AbstractRenderer.prototype, /** @lends JXG.AbstractRenderer.prototype */ {
134 
135     /* ******************************** *
136      *    private methods               *
137      *    should not be called from     *
138      *    outside AbstractRenderer      *
139      * ******************************** */
140 
141     /**
142      * Update visual properties, but only if {@link JXG.AbstractRenderer#enhancedRendering} or <tt>enhanced</tt> is set to true.
143      * @param {JXG.GeometryElement} element The element to update
144      * @param {Object} [not={}] Select properties you don't want to be updated: <tt>{fill: true, dash: true}</tt> updates
145      * everything except for fill and dash. Possible values are <tt>stroke, fill, dash, shadow, gradient</tt>.
146      * @param {Boolean} [enhanced=false] If true, {@link JXG.AbstractRenderer#enhancedRendering} is assumed to be true.
147      * @private
148      */
149     _updateVisual: function (element, not, enhanced) {
150         var rgbo;
151         
152         if (enhanced || this.enhancedRendering) {
153             not = not || {};
154 
155             if (!element.visProp.draft) {
156                 /*
157                 if (JXG.isFunction(element.visProp.visible)) {
158                     if (element.visProp.visible()) {
159                         this.hide(element);
160                     } else {
161                         this.show(element);
162                     }
163                 } 
164                 */
165                         
166                 if (!not.stroke) {
167                     this.setObjectStrokeWidth(element, element.visProp.strokewidth);
168                     this.setObjectStrokeColor(element, element.visProp.strokecolor, element.visProp.strokeopacity);
169                 }
170 
171                 if (!not.fill) {
172                     this.setObjectFillColor(element, element.visProp.fillcolor, element.visProp.fillopacity);
173                 }
174 
175                 if (!not.dash) {
176                     this.setDashStyle(element, element.visProp);
177                 }
178 
179                 if (!not.shadow) {
180                     this.setShadow(element);
181                 }
182 
183                 if (!not.gradient) {
184                     this.setShadow(element);
185                 }
186             } else {
187                 this.setDraft(element);
188             }
189         }
190     },
191 
192 
193     /* ******************************** *
194      *    Point drawing and updating    *
195      * ******************************** */
196 
197     /**
198      * Draws a point on the {@link JXG.Board}.
199      * @param {JXG.Point} element Reference to a {@link JXG.Point} object that has to be drawn.
200      * @see Point
201      * @see JXG.Point
202      * @see JXG.AbstractRenderer#updatePoint
203      * @see JXG.AbstractRenderer#changePointStyle
204      */
205     drawPoint: function (element) {
206         var prim,
207             face = JXG.Point.prototype.normalizeFace.call(this, element.visProp.face);
208 
209         // determine how the point looks like
210         if (face === 'o') {
211             prim = 'ellipse';
212         } else if (face === '[]') {
213             prim = 'rect';
214         } else {
215             // cross/x, diamond/<>, triangleup/a/^, triangledown/v, triangleleft/<,
216             // triangleright/>, plus/+,
217             prim = 'path';
218         }
219 
220         this.appendChildPrim(this.createPrim(prim, element.id), element.visProp.layer);
221         this.appendNodesToElement(element, prim);
222 
223         // adjust visual propertys
224         this._updateVisual(element, {dash: true, shadow: true}, true);
225 
226         // By now we only created the xml nodes and set some styles, in updatePoint
227         // the attributes are filled with data.
228         this.updatePoint(element);
229     },
230 
231     /**
232      * Updates visual appearance of the renderer element assigned to the given {@link JXG.Point}.
233      * @param {JXG.Point} element Reference to a {@link JXG.Point} object, that has to be updated.
234      * @see Point
235      * @see JXG.Point
236      * @see JXG.AbstractRenderer#drawPoint
237      * @see JXG.AbstractRenderer#changePointStyle
238      */
239     updatePoint: function (element) {
240         var size = element.visProp.size,
241             face = JXG.Point.prototype.normalizeFace.call(this, element.visProp.face);
242 
243         if (!isNaN(element.coords.scrCoords[2] + element.coords.scrCoords[1])) {
244             this._updateVisual(element, {dash: false, shadow: false});
245 
246             // Zoom does not work for traces.
247             size *= ((!element.board || !element.board.options.point.zoom) ? 1.0 : Math.sqrt(element.board.zoomX * element.board.zoomY));
248 
249             if (face === 'o') { // circle
250                 this.updateEllipsePrim(element.rendNode, element.coords.scrCoords[1], element.coords.scrCoords[2], size + 1, size + 1);
251             } else if (face === '[]') { // rectangle
252                 this.updateRectPrim(element.rendNode, element.coords.scrCoords[1] - size, element.coords.scrCoords[2] - size, size * 2, size * 2);
253             } else { // x, +, <>, ^, v, <, >
254                 this.updatePathPrim(element.rendNode, this.updatePathStringPoint(element, size, face), element.board);
255             }
256             this.setShadow(element);
257         }
258     },
259 
260     /**
261      * Changes the style of a {@link JXG.Point}. This is required because the point styles differ in what
262      * elements have to be drawn, e.g. if the point is marked by a "x" or a "+" two lines are drawn, if
263      * it's marked by spot a circle is drawn. This method removes the old renderer element(s) and creates
264      * the new one(s).
265      * @param {JXG.Point} element Reference to a {@link JXG.Point} object, that's style is changed.
266      * @see Point
267      * @see JXG.Point
268      * @see JXG.AbstractRenderer#updatePoint
269      * @see JXG.AbstractRenderer#drawPoint
270      */
271     changePointStyle: function (element) {
272         var node = this.getElementById(element.id);
273 
274         // remove the existing point rendering node
275         if (JXG.exists(node)) {
276             this.remove(node);
277         }
278 
279         // and make a new one
280         this.drawPoint(element);
281         JXG.clearVisPropOld(element);
282 
283         if (!element.visProp.visible) {
284             this.hide(element);
285         }
286 
287         if (element.visProp.draft) {
288             this.setDraft(element);
289         }
290     },
291 
292     /* ******************************** *
293      *           Lines                  *
294      * ******************************** */
295 
296     /**
297      * Draws a line on the {@link JXG.Board}.
298      * @param {JXG.Line} element Reference to a line object, that has to be drawn.
299      * @see Line
300      * @see JXG.Line
301      * @see JXG.AbstractRenderer#updateLine
302      */
303     drawLine: function (element) {
304         this.appendChildPrim(this.createPrim('line', element.id), element.visProp.layer);
305         this.appendNodesToElement(element, 'lines');
306         this.updateLine(element);
307     },
308 
309     /**
310      * Updates visual appearance of the renderer element assigned to the given {@link JXG.Line}.
311      * @param {JXG.Line} element Reference to the {@link JXG.Line} object that has to be updated.
312      * @see Line
313      * @see JXG.Line
314      * @see JXG.AbstractRenderer#drawLine
315      */
316     updateLine: function (element) {
317         var c1 = new JXG.Coords(JXG.COORDS_BY_USER, element.point1.coords.usrCoords, element.board),
318             c2 = new JXG.Coords(JXG.COORDS_BY_USER, element.point2.coords.usrCoords, element.board),
319             margin = null;
320 
321         if (element.visProp.firstarrow || element.visProp.lastarrow) {
322             margin = 0;
323         }
324         JXG.Math.Geometry.calcStraight(element, c1, c2, margin);
325         this.updateLinePrim(element.rendNode, 
326                             c1.scrCoords[1], c1.scrCoords[2],
327                             c2.scrCoords[1], c2.scrCoords[2], element.board);
328 
329         this.makeArrows(element);
330         this._updateVisual(element, {fill: true});
331     },
332 
333     /**
334      * Creates a rendering node for ticks added to a line.
335      * @param {JXG.Line} element A arbitrary line.
336      * @see Line
337      * @see Ticks
338      * @see JXG.Line
339      * @see JXG.Ticks
340      * @see JXG.AbstractRenderer#updateTicks
341      */
342     drawTicks: function (element) {
343         var node = this.createPrim('path', element.id);
344 
345         this.appendChildPrim(node, element.visProp.layer);
346         this.appendNodesToElement(element, 'path');
347     },
348 
349     /**
350      * Update {@link Ticks} on a {@link JXG.Line}. This method is only a stub and has to be implemented
351      * in any descendant renderer class.
352      * @param {JXG.Line} element Reference of an line object, thats ticks have to be updated.
353      * @param {Number} dxMaj Number of pixels a major tick counts in x direction.
354      * @param {Number} dyMaj Number of pixels a major tick counts in y direction.
355      * @param {Number} dxMin Number of pixels a minor tick counts in x direction.
356      * @param {Number} dyMin Number of pixels a minor tick counts in y direction.
357      * @see Line
358      * @see Ticks
359      * @see JXG.Line
360      * @see JXG.Ticks
361      * @see JXG.AbstractRenderer#drawTicks
362      */
363     updateTicks: function (element, dxMaj, dyMaj, dxMin, dyMin) { /* stub */ },
364 
365     /* **************************
366      *    Curves
367      * **************************/
368 
369     /**
370      * Draws a {@link JXG.Curve} on the {@link JXG.Board}.
371      * @param {JXG.Curve} element Reference to a graph object, that has to be plotted.
372      * @see Curve
373      * @see JXG.Curve
374      * @see JXG.AbstractRenderer#updateCurve
375      */
376     drawCurve: function (element) {
377         this.appendChildPrim(this.createPrim('path', element.id), element.visProp.layer);
378         this.appendNodesToElement(element, 'path');
379         this._updateVisual(element, {shadow: true}, true);
380         this.updateCurve(element);
381     },
382 
383     /**
384      * Updates visual appearance of the renderer element assigned to the given {@link JXG.Curve}.
385      * @param {JXG.Curve} element Reference to a {@link JXG.Curve} object, that has to be updated.
386      * @see Curve
387      * @see JXG.Curve
388      * @see JXG.AbstractRenderer#drawCurve
389      */
390     updateCurve: function (element) {
391         this._updateVisual(element);
392         if (element.visProp.handdrawing) {
393             this.updatePathPrim(element.rendNode, this.updatePathStringBezierPrim(element), element.board);
394         } else {
395             this.updatePathPrim(element.rendNode, this.updatePathStringPrim(element), element.board);
396         }
397         if (element.numberPoints>1) {
398             this.makeArrows(element);
399         }
400     },
401 
402     /* **************************
403      *    Circle related stuff
404      * **************************/
405 
406     /**
407      * Draws a {@link JXG.Circle}
408      * @param {JXG.Circle} element Reference to a {@link JXG.Circle} object that has to be drawn.
409      * @see Circle
410      * @see JXG.Circle
411      * @see JXG.AbstractRenderer#updateEllipse
412      */
413     drawEllipse: function (element) {
414         this.appendChildPrim(this.createPrim('ellipse', element.id), element.visProp.layer);
415         this.appendNodesToElement(element, 'ellipse');
416         this.updateEllipse(element);
417     },
418 
419     /**
420      * Updates visual appearance of a given {@link JXG.Circle} on the {@link JXG.Board}.
421      * @param {JXG.Circle} element Reference to a {@link JXG.Circle} object, that has to be updated.
422      * @see Circle
423      * @see JXG.Circle
424      * @see JXG.AbstractRenderer#drawEllipse
425      */
426     updateEllipse: function (element) {
427         this._updateVisual(element);
428 
429         // Radius umrechnen:
430         var radius = element.Radius();
431         if (radius > 0.0 
432             && Math.abs(element.center.coords.usrCoords[0])>JXG.Math.eps
433             && !isNaN(radius + element.center.coords.scrCoords[1] + element.center.coords.scrCoords[2]) 
434             && radius * element.board.unitX < 2000000) {
435             this.updateEllipsePrim(element.rendNode, element.center.coords.scrCoords[1], element.center.coords.scrCoords[2],
436                     (radius * element.board.unitX), (radius * element.board.unitY));
437         } 
438     },
439 
440 
441     /* **************************
442      *   Polygon related stuff
443      * **************************/
444 
445     /**
446      * Draws a {@link JXG.Polygon} on the {@link JXG.Board}.
447      * @param {JXG.Polygon} element Reference to a Polygon object, that is to be drawn.
448      * @see Polygon
449      * @see JXG.Polygon
450      * @see JXG.AbstractRenderer#updatePolygon
451      */
452     drawPolygon: function (element) {
453         this.appendChildPrim(this.createPrim('polygon', element.id), element.visProp.layer);
454         this.appendNodesToElement(element, 'polygon');
455         this.updatePolygon(element);
456     },
457 
458     /**
459      * Updates properties of a {@link JXG.Polygon}'s rendering node.
460      * @param {JXG.Polygon} element Reference to a {@link JXG.Polygon} object, that has to be updated.
461      * @see Polygon
462      * @see JXG.Polygon
463      * @see JXG.AbstractRenderer#drawPolygon
464      */
465     updatePolygon: function (element) {
466         // here originally strokecolor wasn't updated but strokewidth was
467         // but if there's no strokecolor i don't see why we should update strokewidth.
468         this._updateVisual(element, {stroke: true, dash: true});
469         this.updatePolygonPrim(element.rendNode, element);
470     },
471 
472     /* **************************
473      *    Text related stuff
474      * **************************/
475 
476     /**
477      * Shows a small copyright notice in the top left corner of the board.
478      * @param {String} str The copyright notice itself
479      * @param {Number} fontsize Size of the font the copyright notice is written in
480      */
481     displayCopyright: function (str, fontsize) { /* stub */ },
482 
483     /**
484      * An internal text is a {@link JXG.Text} element which is drawn using only
485      * the given renderer but no HTML. This method is only a stub, the drawing
486      * is done in the special renderers.
487      * @param {JXG.Text} element Reference to a {@link JXG.Text} object
488      * @see Text
489      * @see JXG.Text
490      * @see JXG.AbstractRenderer#updateInternalText
491      * @see JXG.AbstractRenderer#drawText
492      * @see JXG.AbstractRenderer#updateText
493      * @see JXG.AbstractRenderer#updateTextStyle
494      */
495     drawInternalText: function (element) { /* stub */ },
496 
497     /**
498      * Updates visual properties of an already existing {@link JXG.Text} element.
499      * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be updated.
500      * @see Text
501      * @see JXG.Text
502      * @see JXG.AbstractRenderer#drawInternalText
503      * @see JXG.AbstractRenderer#drawText
504      * @see JXG.AbstractRenderer#updateText
505      * @see JXG.AbstractRenderer#updateTextStyle
506      */
507     updateInternalText: function (element) { /* stub */ },
508 
509     /**
510      * Displays a {@link JXG.Text} on the {@link JXG.Board} by putting a HTML div over it.
511      * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be displayed
512      * @see Text
513      * @see JXG.Text
514      * @see JXG.AbstractRenderer#drawInternalText
515      * @see JXG.AbstractRenderer#updateText
516      * @see JXG.AbstractRenderer#updateInternalText
517      * @see JXG.AbstractRenderer#updateTextStyle
518      */
519     drawText: function (element) {
520         var node, z;
521 
522         if (element.visProp.display === 'html') {
523             node = this.container.ownerDocument.createElement('div');
524             node.style.position = 'absolute';
525 
526             node.className = element.visProp.cssclass;
527             if (this.container.style.zIndex=='') {
528                 z = 0;
529             } else {
530                 z = parseInt(this.container.style.zIndex);
531             }
532 
533             node.style.zIndex = z+element.board.options.layer.text; //'10';
534             this.container.appendChild(node);
535             node.setAttribute('id', this.container.id + '_' + element.id);
536         } else {
537             node = this.drawInternalText(element);
538         }
539 
540         element.rendNode = node;
541         element.htmlStr = '';
542         this.updateText(element);
543     },
544 
545     /**
546      * Updates visual properties of an already existing {@link JXG.Text} element.
547      * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be updated.
548      * @see Text
549      * @see JXG.Text
550      * @see JXG.AbstractRenderer#drawText
551      * @see JXG.AbstractRenderer#drawInternalText
552      * @see JXG.AbstractRenderer#updateInternalText
553      * @see JXG.AbstractRenderer#updateTextStyle
554      */
555     updateText: function (el) {
556         var content = el.plaintext;
557 
558         if (el.visProp.visible) {
559             this.updateTextStyle(el, false);
560 
561             if (el.visProp.display === 'html') {
562                 // Set the position
563                 if (!isNaN(el.coords.scrCoords[1] + el.coords.scrCoords[2])) {
564                     if (el.visProp.anchorx === 'right') {
565                         el.rendNode.style.right = parseInt(el.board.canvasWidth - el.coords.scrCoords[1]) + 'px';
566                     } else if (el.visProp.anchorx === 'middle') {
567                         el.rendNode.style.left = parseInt(el.coords.scrCoords[1]-0.5*el.size[0]) + 'px';
568                     } else {   // 'left'
569                         el.rendNode.style.left = parseInt(el.coords.scrCoords[1]) + 'px';
570                     }
571                     
572                     if (el.visProp.anchory === 'top') {
573                         el.rendNode.style.top = parseInt(el.coords.scrCoords[2] + this.vOffsetText) + 'px';
574                     } else if (el.visProp.anchory === 'middle') {
575                         el.rendNode.style.top = parseInt(el.coords.scrCoords[2] - 0.5*el.size[1] + this.vOffsetText) + 'px';
576                     } else {
577                         el.rendNode.style.top = parseInt(el.coords.scrCoords[2] - el.size[1] + this.vOffsetText) + 'px';
578                     }
579                 }
580                 
581                 // Set the content
582                 if (el.htmlStr !== content) {
583                     el.rendNode.innerHTML = content;
584                     el.htmlStr = content;
585                     
586                     if (el.visProp.usemathjax) {
587                         // typesetting directly might not work because mathjax was not loaded completely
588                         // see http://www.mathjax.org/docs/1.1/typeset.html
589                         MathJax.Hub.Queue(['Typeset', MathJax.Hub, el.rendNode]);
590                         //MathJax.Hub.Typeset(el.rendNode);
591                     } else if (el.visProp.useasciimathml) {
592                         AMprocessNode(el.rendNode, false);
593                     }
594                 }
595                 this.transformImage(el, el.transformations);
596             } else {
597                 this.updateInternalText(el);
598             }
599         }
600     },
601 
602     /**
603      * Updates font-size, color and opacity propertiey and CSS style properties of a {@link JXG.Text} node.
604      * This function is also called by highlight() and nohighlight().
605      * @param {JXG.Text} element Reference to the {@link JXG.Text} object, that has to be updated.
606      * @see Text
607      * @see JXG.Text
608      * @see JXG.AbstractRenderer#drawText
609      * @see JXG.AbstractRenderer#drawInternalText
610      * @see JXG.AbstractRenderer#updateText
611      * @see JXG.AbstractRenderer#updateInternalText
612      * @see JXG.AbstractRenderer#updateInternalTextStyle
613      */
614     updateTextStyle: function (element, doHighlight) {
615         var fs, so, sc, css,
616             ev = element.visProp;
617 
618         if (doHighlight) {
619             sc = ev.highlightstrokecolor;
620             so = ev.highlightstrokeopacity;
621             css = ev.highlightcssclass;
622         } else {
623             sc = ev.strokecolor;
624             so = ev.strokeopacity;
625             css = ev.cssclass;
626         }
627        
628         /*
629          * This part is executed for all text elements except internal texts in canvas.
630          */
631         if (element.visProp.display === 'html' || this.type != 'canvas') {
632             fs = JXG.evaluate(element.visProp.fontsize);
633             if (element.visPropOld.fontsize != fs) {
634                 try {
635                     element.rendNode.style.fontSize = fs + 'px';
636                 } catch (e) {
637                     // IE needs special treatment.
638                     element.rendNode.style.fontSize = fs;
639                 }
640                 element.visPropOld.fontsize = fs;
641             }
642             
643             if (element.visPropOld.cssclass != css) {
644                 element.rendNode.className = css;
645                 element.visPropOld.cssclass = css;
646             }
647         }
648         if (element.visProp.display === 'html') {
649             this.setObjectStrokeColor(element, sc, so);        
650         } else {
651             this.updateInternalTextStyle(element, sc, so);
652         }
653         return this;
654     },
655     
656     /**
657      * Set color and opacity of internal texts. 
658      * This method is used for Canvas and VML.
659      * SVG needs its own version.
660      * @private
661      * @see JXG.AbstractRenderer#updateTextStyle
662      * @see JXG.SVGRenderer#updateInternalTextStyle
663      */
664     updateInternalTextStyle: function(element, strokeColor, strokeOpacity) {
665         this.setObjectStrokeColor(element, strokeColor, strokeOpacity);        
666     },
667 
668     /* **************************
669      *    Image related stuff
670      * **************************/
671 
672     /**
673      * Draws an {@link JXG.Image} on a board; This is just a template that has to be implemented by special renderers.
674      * @param {JXG.Image} element Reference to the image object that is to be drawn
675      * @see Image
676      * @see JXG.Image
677      * @see JXG.AbstractRenderer#updateImage
678      */
679     drawImage: function (element) { /* stub */ },
680 
681     /**
682      * Updates the properties of an {@link JXG.Image} element.
683      * @param {JXG.Image} element Reference to an {@link JXG.Image} object, that has to be updated.
684      * @see Image
685      * @see JXG.Image
686      * @see JXG.AbstractRenderer#drawImage
687      */
688     updateImage: function (element) {
689         this.updateRectPrim(element.rendNode, element.coords.scrCoords[1], element.coords.scrCoords[2] - element.size[1],
690                 element.size[0], element.size[1]);
691 
692         this.updateImageURL(element);
693         this.transformImage(element, element.transformations);
694         this._updateVisual(element, {stroke: true, dash: true}, true);
695     },
696 
697     /**
698      * Multiplication of transformations without updating. That means, at that point it is expected that the matrices
699      * contain numbers only. First, the origin in user coords is translated to <tt>(0,0)</tt> in screen coords.
700      * Then, the stretch factors are divided out. After the transformations in user coords, the  stretch factors
701      * are multiplied in again, and the origin in user coords is translated back to its position.
702      * This method does not have to be implemented in a new renderer.
703      * @param {JXG.GeometryElement} element A JSXGraph element. We only need its board property.
704      * @param {Array} transformations An array of JXG.Transformations.
705      * @returns {Array} A matrix represented by a two dimensional array of numbers.
706      * @see JXG.AbstractRenderer#transformImage
707      */
708     joinTransforms: function (element, transformations) {
709         var m = [[1, 0, 0], [0, 1, 0], [0, 0, 1]],
710             ox = element.board.origin.scrCoords[1],
711             oy = element.board.origin.scrCoords[2],
712             ux = element.board.unitX,
713             uy = element.board.unitY,
714             mpre1 =  [[1,   0, 0],      // Translate to 0,0 in screen coords
715                       [-ox, 1, 0], 
716                       [-oy, 0, 1]],  
717             mpre2 =  [[1, 0,     0],    // Scale
718                       [0, 1/ux,  0], 
719                       [0, 0, -1/uy]],
720             mpost2 = [[1, 0,   0],      // Scale back
721                       [0, ux,  0], 
722                       [0, 0, -uy]],
723             mpost1 = [[1,  0, 0],       // Translate back
724                       [ox, 1, 0], 
725                       [oy, 0, 1]],
726             i, len = transformations.length;
727 
728         for (i = 0; i < len; i++) {
729             m = JXG.Math.matMatMult(mpre1, m);
730             m = JXG.Math.matMatMult(mpre2, m);
731             m = JXG.Math.matMatMult(transformations[i].matrix, m);
732             m = JXG.Math.matMatMult(mpost2, m);
733             m = JXG.Math.matMatMult(mpost1, m);
734         }
735         return m;
736     },
737 
738     /**
739      * Applies transformations on images and text elements. This method is just a stub and has to be implemented in all
740      * descendant classes where text and image transformations are to be supported.
741      * @param {JXG.Image|JXG.Text} element A {@link JXG.Image} or {@link JXG.Text} object.
742      * @param {Array} transformations An array of {@link JXG.Transformation} objects. This is usually the transformations property
743      * of the given element <tt>el</tt>.
744      */
745     transformImage: function (element, transformations) { /* stub */ },
746 
747     /**
748      * If the URL of the image is provided by a function the URL has to be updated during updateImage()
749      * @param {JXG.Image} element Reference to an image object.
750      * @see JXG.AbstractRenderer#updateImage
751      */
752     updateImageURL: function (element) { /* stub */ },
753 
754     /**
755      * Updates CSS style properties of a {@link JXG.Image} node.
756      * In SVGRenderer opacity is the only available style element.
757      * This function is called by highlight() and nohighlight().
758      * This function works for VML. 
759      * It does not work for Canvas.
760      * SVGRenderer overwrites this method.
761      * @param {JXG.Text} element Reference to the {@link JXG.Image} object, that has to be updated.
762      * @see Image
763      * @see JXG.Image
764      * @see JXG.AbstractRenderer#highlight
765      * @see JXG.AbstractRenderer#nohighlight
766      */
767     updateImageStyle: function(element, doHighlight) { 
768         var css = (doHighlight) ? el.visProp.highlightcssclass : el.visProp.cssclass;
769         
770         element.rendNode.className = css;
771     },
772 
773 
774     /* **************************
775      * Render primitive objects
776      * **************************/
777 
778     /**
779      * Appends a node to a specific layer level. This is just an abstract method and has to be implemented
780      * in all renderers that want to use the <tt>createPrim</tt> model to draw.
781      * @param {Node} node A DOM tree node.
782      * @param {Number} level The layer the node is attached to. This is the index of the layer in
783      * {@link JXG.SVGRenderer#layer} or the <tt>z-index</tt> style property of the node in VMLRenderer.
784      */
785     appendChildPrim: function (node, level) { /* stub */ },
786 
787     /**
788      * Stores the rendering nodes. This is an abstract method which has to be implemented in all renderers that use
789      * the <tt>createPrim</tt> method.
790      * @param {JXG.GeometryElement} element A JSXGraph element.
791      * @param {String} type The XML node name. Only used in VMLRenderer.
792      */
793     appendNodesToElement: function (element, type) { /* stub */ },
794 
795     /**
796      * Creates a node of a given type with a given id.
797      * @param {String} type The type of the node to create.
798      * @param {String} id Set the id attribute to this.
799      * @returns {Node} Reference to the created node.
800      */
801     createPrim: function (type, id) {
802         /* stub */
803         return null;
804     },
805 
806     /**
807      * Removes an element node. Just a stub.
808      * @param {Node} node The node to remove.
809      */
810     remove: function (node) { /* stub */ },    
811 
812     /**
813      * Can be used to create the nodes to display arrows. This is an abstract method which has to be implemented
814      * in any descendant renderer.
815      * @param {JXG.GeometryElement} element The element the arrows are to be attached to.
816      */
817     makeArrows: function(element) { /* stub */ },
818 
819     /**
820      * Updates an ellipse node primitive. This is an abstract method which has to be implemented in all renderers
821      * that use the <tt>createPrim</tt> method.
822      * @param {Node} node Reference to the node.
823      * @param {Number} x Centre X coordinate
824      * @param {Number} y Centre Y coordinate
825      * @param {Number} rx The x-axis radius.
826      * @param {Number} ry The y-axis radius.
827      */
828     updateEllipsePrim: function(node, x, y, rx, ry) { /* stub */ },
829 
830     /**
831      * Refreshes a line node. This is an abstract method which has to be implemented in all renderers that use
832      * the <tt>createPrim</tt> method.
833      * @param {Node} node The node to be refreshed.
834      * @param {Number} p1x The first point's x coordinate.
835      * @param {Number} p1y The first point's y coordinate.
836      * @param {Number} p2x The second point's x coordinate.
837      * @param {Number} p2y The second point's y coordinate.
838      * @param {JXG.Board} board
839      */
840     updateLinePrim: function(node, p1x, p1y, p2x, p2y, board) { /* stub */ },
841 
842     /**
843      * Updates a path element. This is an abstract method which has to be implemented in all renderers that use
844      * the <tt>createPrim</tt> method.
845      * @param {Node} node The path node.
846      * @param {String} pathString A string formatted like e.g. <em>'M 1,2 L 3,1 L5,5'</em>. The format of the string
847      * depends on the rendering engine.
848      * @param {JXG.Board} board Reference to the element's board.
849      */
850     updatePathPrim: function (node, pathString, board) { /* stub */ },
851 
852     /**
853      * Builds a path data string to draw a point with a face other than <em>rect</em> and <em>circle</em>. Since
854      * the format of such a string usually depends on the renderer this method
855      * is only an abstract method. Therefore, it has to be implemented in the descendant renderer itself unless
856      * the renderer does not use the createPrim interface but the draw* interfaces to paint.
857      * @param {JXG.Point} element The point element
858      * @param {Number} size A positive number describing the size. Usually the half of the width and height of
859      * the drawn point.
860      * @param {String} type A string describing the point's face. This method only accepts the shortcut version of
861      * each possible face: <tt>x, +, <>, ^, v, >, <
862      */
863     updatePathStringPoint: function (element, size, type) { /* stub */ },
864 
865     /**
866      * Builds a path data string from a {@link JXG.Curve} element. Since the path data strings heavily depend on the
867      * underlying rendering technique this method is just a stub. Although such a path string is of no use for the
868      * CanvasRenderer, this method is used there to draw a path directly.
869      * @param element
870      */
871     updatePathStringPrim: function (element) { /* stub */ },
872 
873     /**
874      * Builds a path data string from a {@link JXG.Curve} element such that the curve looks like
875      * hand drawn.
876      * Since the path data strings heavily depend on the
877      * underlying rendering technique this method is just a stub. Although such a path string is of no use for the
878      * CanvasRenderer, this method is used there to draw a path directly.
879      * @param element
880      */
881     updatePathStringBezierPrim: function (element) { /* stub */ },
882 
883     
884     /**
885      * Update a polygon primitive.
886      * @param {Node} node
887      * @param {JXG.Polygon} element A JSXGraph element of type {@link JXG.Polygon}
888      */
889     updatePolygonPrim: function (node, element) { /* stub */ },
890 
891     /**
892      * Update a rectangle primitive. This is used only for points with face of type 'rect'.
893      * @param {Node} node The node yearning to be updated.
894      * @param {Number} x x coordinate of the top left vertex.
895      * @param {Number} y y coordinate of the top left vertex.
896      * @param {Number} w Width of the rectangle.
897      * @param {Number} h The rectangle's height.
898      */
899     updateRectPrim: function(node, x, y, w, h) { /* stub */ },
900 
901     /* **************************
902      *  Set Attributes
903      * **************************/
904 
905     /**
906      * Sets a node's attribute.
907      * @param {Node} node The node that is to be updated.
908      * @param {String} key Name of the attribute.
909      * @param {String} val New value for the attribute.
910      */
911     setPropertyPrim: function (node, key, val) { /* stub */ },
912 
913     /**
914      * Shows a hidden element on the canvas; Only a stub, requires implementation in the derived renderer.
915      * @param {JXG.GeometryElement} element Reference to the object that has to appear.
916      * @see JXG.AbstractRenderer#hide
917      */
918     show: function (element) { /* stub */ },
919 
920     /**
921      * Hides an element on the canvas; Only a stub, requires implementation in the derived renderer.
922      * @param {JXG.GeometryElement} element Reference to the geometry element that has to disappear.
923      * @see JXG.AbstractRenderer#show
924      */
925     hide: function (element) { /* stub */ },
926 
927     /**
928      * Sets the buffering as recommended by SVGWG. Until now only Opera supports this and will be ignored by
929      * other browsers. Although this feature is only supported by SVG we have this method in {@link JXG.AbstractRenderer}
930      * because it is called from outside the renderer.
931      * @param {Node} node The SVG DOM Node which buffering type to update.
932      * @param {String} type Either 'auto', 'dynamic', or 'static'. For an explanation see
933      *   {@link http://www.w3.org/TR/SVGTiny12/painting.html#BufferedRenderingProperty}.
934      */
935     setBuffering: function (node, type) { /* stub */ },
936 
937     /**
938      * Sets an element's dash style.
939      * @param {JXG.GeometryElement} element An JSXGraph element.
940      */
941     setDashStyle: function (element) { /* stub */ },
942 
943     /**
944      * Puts an object into draft mode, i.e. it's visual appearance will be changed. For GEONE<sub>x</sub>T backwards compatibility.
945      * @param {JXG.GeometryElement} element Reference of the object that is in draft mode.
946      */
947     setDraft: function (element) {
948         if (!element.visProp.draft) {
949             return;
950         }
951         var draftColor = element.board.options.elements.draft.color,
952             draftOpacity = element.board.options.elements.draft.opacity;
953 
954         if (element.type === JXG.OBJECT_TYPE_POLYGON) {
955             this.setObjectFillColor(element, draftColor, draftOpacity);
956         }
957         else {
958             if (element.elementClass === JXG.OBJECT_CLASS_POINT) {
959                 this.setObjectFillColor(element, draftColor, draftOpacity);
960             }
961             else {
962                 this.setObjectFillColor(element, 'none', 0);
963             }
964             this.setObjectStrokeColor(element, draftColor, draftOpacity);
965             this.setObjectStrokeWidth(element, element.board.options.elements.draft.strokeWidth);
966         }
967     },
968 
969     /**
970      * Puts an object from draft mode back into normal mode.
971      * @param {JXG.GeometryElement} element Reference of the object that no longer is in draft mode.
972      */
973     removeDraft: function (element) {
974         if (element.type === JXG.OBJECT_TYPE_POLYGON) {
975             this.setObjectFillColor(element, element.visProp.fillcolor, element.visProp.fillopacity);
976         }
977         else {
978             if (element.type === JXG.OBJECT_CLASS_POINT) {
979                 this.setObjectFillColor(element, element.visProp.fillcolor, element.visProp.fillopacity);
980             }
981             this.setObjectStrokeColor(element, element.visProp.strokecolor, element.visProp.strokeopacity);
982             this.setObjectStrokeWidth(element, element.visProp.strokewidth);
983         }
984     },
985 
986     /**
987      * Sets up nodes for rendering a gradient fill.
988      * @param element
989      */
990     setGradient: function (element) { /* stub */ },
991 
992     /**
993      * Updates the gradient fill.
994      * @param {JXG.GeometryElement} element An JSXGraph element with an area that can be filled.
995      */
996     updateGradient: function (element) { /* stub */ },
997 
998     /**
999      * Sets an objects fill color.
1000      * @param {JXG.GeometryElement} element Reference of the object that wants a new fill color.
1001      * @param {String} color Color in a HTML/CSS compatible format. If you don't want any fill color at all, choose 'none'.
1002      * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1.
1003      */
1004     setObjectFillColor: function (element, color, opacity) { /* stub */ },
1005 
1006     /**
1007      * Changes an objects stroke color to the given color.
1008      * @param {JXG.GeometryElement} element Reference of the {@link JXG.GeometryElement} that gets a new stroke color.
1009      * @param {String} color Color value in a HTML compatible format, e.g. <strong>#00ff00</strong> or <strong>green</strong> for green.
1010      * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1.
1011      */
1012     setObjectStrokeColor: function (element, color, opacity) { /* stub */ },
1013 
1014     /**
1015      * Sets an element's stroke width.
1016      * @param {JXG.GeometryElement} element Reference to the geometry element.
1017      * @param {Number} width The new stroke width to be assigned to the element.
1018      */
1019     setObjectStrokeWidth: function (element, width) { /* stub */ },
1020     
1021     /**
1022      * Sets the shadow properties to a geometry element. This method is only a stub, it is implemented in the actual renderers.
1023      * @param {JXG.GeometryElement} element Reference to a geometry object, that should get a shadow
1024      */
1025     setShadow: function (element) { /* stub */ },
1026 
1027     /**
1028      * Highlights an object, i.e. changes the current colors of the object to its highlighting colors
1029      * @param {JXG.GeometryElement} element Reference of the object that will be highlighted.
1030      * @returns {JXG.AbstractRenderer} Reference to the renderer
1031      * @see JXG.AbstractRenderer#updateTextStyle
1032      */
1033     highlight: function (element) {
1034         var i, ev = element.visProp;
1035 
1036         if (!ev.draft) {
1037             /*
1038                // Why did we have this? A.W.
1039             if (element.elementClass === JXG.OBJECT_CLASS_POINT) {
1040                 this.setObjectStrokeColor(element, ev.highlightstrokecolor, ev.highlightstrokeopacity);
1041                 this.setObjectFillColor(element, ev.highlightstrokecolor, ev.highlightstrokeopacity);
1042             } else 
1043             */
1044             if (element.type === JXG.OBJECT_TYPE_POLYGON) {
1045                 this.setObjectFillColor(element, ev.highlightfillcolor, ev.highlightfillopacity);
1046                 for (i = 0; i < element.borders.length; i++) {
1047                     this.setObjectStrokeColor(element.borders[i], element.borders[i].visProp.highlightstrokecolor, element.borders[i].visProp.highlightstrokeopacity);
1048                 }
1049             } else {
1050                 if (element.type === JXG.OBJECT_TYPE_TEXT) {
1051                     this.updateTextStyle(element, true);
1052                 } else if (element.type === JXG.OBJECT_TYPE_IMAGE) {
1053                     this.updateImageStyle(element, true);
1054                     //element.rendNode.className = 'JXGimageHighlight';
1055                     //element.rendNode.setAttributeNS(null, 'class', 'JXGimageHighlight');
1056                 } else {
1057                     this.setObjectStrokeColor(element, ev.highlightstrokecolor, ev.highlightstrokeopacity);
1058                     this.setObjectFillColor(element, ev.highlightfillcolor, ev.highlightfillopacity);
1059                 }
1060             }
1061             if (ev.highlightstrokewidth) {
1062                 this.setObjectStrokeWidth(element, Math.max(ev.highlightstrokewidth, ev.strokewidth));
1063             }
1064         }
1065 
1066         return this;
1067     },
1068 
1069     /**
1070      * Uses the normal colors of an object, i.e. the opposite of {@link JXG.AbstractRenderer#highlight}.
1071      * @param {JXG.GeometryElement} element Reference of the object that will get its normal colors.
1072      * @returns {JXG.AbstractRenderer} Reference to the renderer
1073      * @see JXG.AbstractRenderer#updateTextStyle
1074      */
1075     noHighlight: function (element) {
1076         var i, ev = element.visProp;
1077 
1078         if (!element.visProp.draft) {
1079             /* 
1080                // Why did we have this? A.W.
1081               if (element.elementClass === JXG.OBJECT_CLASS_POINT) {
1082                 this.setObjectStrokeColor(element, ev.strokecolor, ev.strokeopacity);
1083                 this.setObjectFillColor(element, ev.strokecolor, ev.strokeopacity);
1084             } else 
1085             */
1086             if (element.type === JXG.OBJECT_TYPE_POLYGON) {
1087                 this.setObjectFillColor(element, ev.fillcolor, ev.fillopacity);
1088                 for (i = 0; i < element.borders.length; i++) {
1089                     this.setObjectStrokeColor(element.borders[i], element.borders[i].visProp.strokecolor, element.borders[i].visProp.strokeopacity);
1090                 }
1091             } else {
1092                 if (element.type === JXG.OBJECT_TYPE_TEXT) {
1093                     this.updateTextStyle(element, false);
1094                 } else if (element.type === JXG.OBJECT_TYPE_IMAGE) {
1095                     this.updateImageStyle(element, false);
1096                     //element.rendNode.setAttributeNS(null, 'class', 'JXGimage');
1097                 } else {
1098                     this.setObjectStrokeColor(element, ev.strokecolor, ev.strokeopacity);
1099                     this.setObjectFillColor(element, ev.fillcolor, ev.fillopacity);
1100                 }
1101             }
1102             this.setObjectStrokeWidth(element, ev.strokewidth);
1103         }
1104 
1105         return this;
1106     },
1107 
1108     /* **************************
1109      * renderer control
1110      * **************************/
1111 
1112     /**
1113      * Stop redraw. This method is called before every update, so a non-vector-graphics based renderer
1114      * can use this method to delete the contents of the drawing panel. This is an abstract method every
1115      * descendant renderer should implement, if appropriate.
1116      * @see JXG.AbstractRenderer#unsuspendRedraw
1117      */
1118     suspendRedraw: function () { /* stub */ },
1119 
1120     /**
1121      * Restart redraw. This method is called after updating all the rendering node attributes.
1122      * @see JXG.AbstractRenderer#suspendRedraw
1123      */
1124     unsuspendRedraw: function () { /* stub */ },
1125 
1126     /**
1127      * The tiny zoom bar shown on the bottom of a board (if showNavigation on board creation is true).
1128      * @param {JXG.Board} board Reference to a JSXGraph board.
1129      */
1130     drawZoomBar: function (board) {
1131         var doc,
1132             node,
1133             createButton = function (label, handler) {
1134                 var button;
1135 
1136                 button = doc.createElement('span');
1137                 node.appendChild(button);
1138                 button.appendChild(document.createTextNode(label));
1139                 /* button.innerHTML = label; */ // Does not work in XHTML
1140                 JXG.addEvent(button, 'click', handler, board);
1141             };
1142 
1143         doc = board.containerObj.ownerDocument;
1144         node = doc.createElement('div');
1145 
1146         node.setAttribute('id', board.containerObj.id + '_navigationbar');
1147 
1148         node.style.color = board.options.navbar.strokeColor;
1149         node.style.backgroundColor = board.options.navbar.fillColor;
1150         node.style.padding = board.options.navbar.padding;
1151         node.style.position = board.options.navbar.position;
1152         node.style.fontSize = board.options.navbar.fontSize;
1153         node.style.cursor = board.options.navbar.cursor;
1154         node.style.zIndex = board.options.navbar.zIndex;
1155         board.containerObj.appendChild(node);
1156         node.style.right = board.options.navbar.right;
1157         node.style.bottom = board.options.navbar.bottom;
1158 
1159         // For XHTML we need unicode instead of HTML entities
1160         createButton('\u00A0\u2013\u00A0', board.zoomOut);
1161         createButton('\u00A0o\u00A0', board.zoom100);
1162         createButton('\u00A0+\u00A0', board.zoomIn);
1163         createButton('\u00A0\u2190\u00A0', board.clickLeftArrow);
1164         createButton('\u00A0\u2193\u00A0', board.clickUpArrow);
1165         createButton('\u00A0\u2191\u00A0', board.clickDownArrow);
1166         createButton('\u00A0\u2192\u00A0', board.clickRightArrow);
1167     },
1168 
1169     /**
1170      * Wrapper for getElementById for maybe other renderers which elements are not directly accessible by DOM methods like document.getElementById().
1171      * @param {String} id Unique identifier for element.
1172      * @returns {Object} Reference to a JavaScript object. In case of SVG/VMLRenderer it's a reference to a SVG/VML node.
1173      */
1174     getElementById: function (id) {
1175         return document.getElementById(this.container.id + '_' + id);
1176     },
1177 
1178     /**
1179      * Resizes the rendering element
1180      * @param {Number} w New width
1181      * @param {Number} h New height
1182      */
1183     resize: function (w, h) { /* stub */}
1184 
1185 });
1186