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 = 3;
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 screenCoords1 = new JXG.Coords(JXG.COORDS_BY_USER, element.point1.coords.usrCoords, element.board),
318             screenCoords2 = new JXG.Coords(JXG.COORDS_BY_USER, element.point2.coords.usrCoords, element.board);
319 
320         JXG.Math.Geometry.calcStraight(element, screenCoords1, screenCoords2);
321         this.updateLinePrim(element.rendNode, screenCoords1.scrCoords[1], screenCoords1.scrCoords[2],
322                                          screenCoords2.scrCoords[1], screenCoords2.scrCoords[2], element.board);
323 
324         this.makeArrows(element);
325         this._updateVisual(element, {fill: true});
326     },
327 
328     /**
329      * Creates a rendering node for ticks added to a line.
330      * @param {JXG.Line} element A arbitrary line.
331      * @see Line
332      * @see Ticks
333      * @see JXG.Line
334      * @see JXG.Ticks
335      * @see JXG.AbstractRenderer#updateTicks
336      */
337     drawTicks: function (element) {
338         var node = this.createPrim('path', element.id);
339 
340         this.appendChildPrim(node, element.visProp.layer);
341         this.appendNodesToElement(element, 'path');
342     },
343 
344     /**
345      * Update {@link Ticks} on a {@link JXG.Line}. This method is only a stub and has to be implemented
346      * in any descendant renderer class.
347      * @param {JXG.Line} element Reference of an line object, thats ticks have to be updated.
348      * @param {Number} dxMaj Number of pixels a major tick counts in x direction.
349      * @param {Number} dyMaj Number of pixels a major tick counts in y direction.
350      * @param {Number} dxMin Number of pixels a minor tick counts in x direction.
351      * @param {Number} dyMin Number of pixels a minor tick counts in y direction.
352      * @see Line
353      * @see Ticks
354      * @see JXG.Line
355      * @see JXG.Ticks
356      * @see JXG.AbstractRenderer#drawTicks
357      */
358     updateTicks: function (element, dxMaj, dyMaj, dxMin, dyMin) { /* stub */ },
359 
360     /* **************************
361      *    Curves
362      * **************************/
363 
364     /**
365      * Draws a {@link JXG.Curve} on the {@link JXG.Board}.
366      * @param {JXG.Curve} element Reference to a graph object, that has to be plotted.
367      * @see Curve
368      * @see JXG.Curve
369      * @see JXG.AbstractRenderer#updateCurve
370      */
371     drawCurve: function (element) {
372         this.appendChildPrim(this.createPrim('path', element.id), element.visProp.layer);
373         this.appendNodesToElement(element, 'path');
374         this._updateVisual(element, {shadow: true}, true);
375         this.updateCurve(element);
376     },
377 
378     /**
379      * Updates visual appearance of the renderer element assigned to the given {@link JXG.Curve}.
380      * @param {JXG.Curve} element Reference to a {@link JXG.Curve} object, that has to be updated.
381      * @see Curve
382      * @see JXG.Curve
383      * @see JXG.AbstractRenderer#drawCurve
384      */
385     updateCurve: function (element) {
386         this._updateVisual(element);
387         if (element.visProp.handdrawing) {
388             this.updatePathPrim(element.rendNode, this.updatePathStringBezierPrim(element), element.board);
389         } else {
390             this.updatePathPrim(element.rendNode, this.updatePathStringPrim(element), element.board);
391         }
392         this.makeArrows(element);
393     },
394 
395     /* **************************
396      *    Circle related stuff
397      * **************************/
398 
399     /**
400      * Draws a {@link JXG.Circle}
401      * @param {JXG.Circle} element Reference to a {@link JXG.Circle} object that has to be drawn.
402      * @see Circle
403      * @see JXG.Circle
404      * @see JXG.AbstractRenderer#updateEllipse
405      */
406     drawEllipse: function (element) {
407         this.appendChildPrim(this.createPrim('ellipse', element.id), element.visProp.layer);
408         this.appendNodesToElement(element, 'ellipse');
409         this.updateEllipse(element);
410     },
411 
412     /**
413      * Updates visual appearance of a given {@link JXG.Circle} on the {@link JXG.Board}.
414      * @param {JXG.Circle} element Reference to a {@link JXG.Circle} object, that has to be updated.
415      * @see Circle
416      * @see JXG.Circle
417      * @see JXG.AbstractRenderer#drawEllipse
418      */
419     updateEllipse: function (element) {
420         this._updateVisual(element);
421 
422         // Radius umrechnen:
423         var radius = element.Radius();
424         if (radius > 0.0 && !isNaN(radius + element.center.coords.scrCoords[1] + element.center.coords.scrCoords[2]) && radius * element.board.unitX < 20000) {
425             this.updateEllipsePrim(element.rendNode, element.center.coords.scrCoords[1], element.center.coords.scrCoords[2],
426                     (radius * element.board.unitX), (radius * element.board.unitY));
427         }
428     },
429 
430 
431     /* **************************
432      *   Polygon related stuff
433      * **************************/
434 
435     /**
436      * Draws a {@link JXG.Polygon} on the {@link JXG.Board}.
437      * @param {JXG.Polygon} element Reference to a Polygon object, that is to be drawn.
438      * @see Polygon
439      * @see JXG.Polygon
440      * @see JXG.AbstractRenderer#updatePolygon
441      */
442     drawPolygon: function (element) {
443         this.appendChildPrim(this.createPrim('polygon', element.id), element.visProp.layer);
444         this.appendNodesToElement(element, 'polygon');
445         this.updatePolygon(element);
446     },
447 
448     /**
449      * Updates properties of a {@link JXG.Polygon}'s rendering node.
450      * @param {JXG.Polygon} element Reference to a {@link JXG.Polygon} object, that has to be updated.
451      * @see Polygon
452      * @see JXG.Polygon
453      * @see JXG.AbstractRenderer#drawPolygon
454      */
455     updatePolygon: function (element) {
456         // here originally strokecolor wasn't updated but strokewidth was
457         // but if there's no strokecolor i don't see why we should update strokewidth.
458         this._updateVisual(element, {stroke: true, dash: true});
459         this.updatePolygonPrim(element.rendNode, element);
460     },
461 
462     /* **************************
463      *    Text related stuff
464      * **************************/
465 
466     /**
467      * Shows a small copyright notice in the top left corner of the board.
468      * @param {String} str The copyright notice itself
469      * @param {Number} fontsize Size of the font the copyright notice is written in
470      */
471     displayCopyright: function (str, fontsize) { /* stub */ },
472 
473     /**
474      * An internal text is a {@link JXG.Text} element which is drawn using only
475      * the given renderer but no HTML. This method is only a stub, the drawing
476      * is done in the special renderers.
477      * @param {JXG.Text} element Reference to a {@link JXG.Text} object
478      * @see Text
479      * @see JXG.Text
480      * @see JXG.AbstractRenderer#updateInternalText
481      * @see JXG.AbstractRenderer#drawText
482      * @see JXG.AbstractRenderer#updateText
483      * @see JXG.AbstractRenderer#updateTextStyle
484      */
485     drawInternalText: function (element) { /* stub */ },
486 
487     /**
488      * Updates visual properties of an already existing {@link JXG.Text} element.
489      * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be updated.
490      * @see Text
491      * @see JXG.Text
492      * @see JXG.AbstractRenderer#drawInternalText
493      * @see JXG.AbstractRenderer#drawText
494      * @see JXG.AbstractRenderer#updateText
495      * @see JXG.AbstractRenderer#updateTextStyle
496      */
497     updateInternalText: function (element) { /* stub */ },
498 
499     /**
500      * Displays a {@link JXG.Text} on the {@link JXG.Board} by putting a HTML div over it.
501      * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be displayed
502      * @see Text
503      * @see JXG.Text
504      * @see JXG.AbstractRenderer#drawInternalText
505      * @see JXG.AbstractRenderer#updateText
506      * @see JXG.AbstractRenderer#updateInternalText
507      * @see JXG.AbstractRenderer#updateTextStyle
508      */
509     drawText: function (element) {
510         var node, z;
511 
512         if (element.visProp.display === 'html') {
513             node = this.container.ownerDocument.createElement('div');
514             node.style.position = 'absolute';
515 
516             node.className = element.visProp.cssclass;
517             if (this.container.style.zIndex=='') {
518                 z = 0;
519             } else {
520                 z = parseInt(this.container.style.zIndex);
521             }
522 
523             node.style.zIndex = z+element.board.options.layer.text; //'10';
524             this.container.appendChild(node);
525             node.setAttribute('id', this.container.id + '_' + element.id);
526         } else {
527             node = this.drawInternalText(element);
528         }
529 
530         element.rendNode = node;
531         element.htmlStr = '';
532         this.updateText(element);
533     },
534 
535     /**
536      * Updates visual properties of an already existing {@link JXG.Text} element.
537      * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be updated.
538      * @see Text
539      * @see JXG.Text
540      * @see JXG.AbstractRenderer#drawText
541      * @see JXG.AbstractRenderer#drawInternalText
542      * @see JXG.AbstractRenderer#updateInternalText
543      * @see JXG.AbstractRenderer#updateTextStyle
544      */
545     updateText: function (element) {
546         var content = element.plaintext;
547 
548         if (element.visProp.visible) {
549             this.updateTextStyle(element);
550 
551             if (element.visProp.display === 'html') {
552                 if (!isNaN(element.coords.scrCoords[1] + element.coords.scrCoords[2])) {
553                     element.rendNode.style.left = parseInt(element.coords.scrCoords[1]) + 'px';
554                     element.rendNode.style.top = parseInt(element.coords.scrCoords[2] - parseInt(element.visProp.fontsize) + this.vOffsetText) + 'px';
555                 }
556 
557                 if (element.htmlStr !== content) {
558                     element.rendNode.innerHTML = content;
559                     element.htmlStr = content;
560                     
561                     if (element.visProp.usemathjax) {
562                         MathJax.Hub.Typeset(element.rendNode);
563                     } else if (element.visProp.useasciimathml) {
564                         AMprocessNode(element.rendNode, false);
565                     }
566                 }
567                 this.transformImage(element, element.transformations);
568             } else {
569                 this.updateInternalText(element);
570             }
571         }
572     },
573 
574     /**
575      * Updates CSS style properties of a {@link JXG.Text} node.
576      * @param {JXG.Text} element Reference to the {@link JXG.Text} object, that has to be updated.
577      * @see Text
578      * @see JXG.Text
579      * @see JXG.AbstractRenderer#drawText
580      * @see JXG.AbstractRenderer#drawInternalText
581      * @see JXG.AbstractRenderer#updateText
582      * @see JXG.AbstractRenderer#updateInternalText
583      */
584     updateTextStyle: function (element) {
585         var fs = JXG.evaluate(element.visProp.fontsize);
586 
587         if (element.visProp.display === 'html' || this.type != 'canvas') {
588         //if (element.visProp.display === 'html') {
589             //element.rendNode.setAttribute("class", element.visProp.cssclass);
590             element.rendNode.className = element.visProp.cssclass;
591             try {
592                 element.rendNode.style.fontSize = fs + 'px';
593             } catch (e) {
594                 // IE needs special treatment.
595                 element.rendNode.style.fontSize = fs;
596             }
597         }
598         this.setObjectStrokeColor(element, element.visProp.strokecolor, element.visProp.strokeopacity);        
599     },
600 
601     /* **************************
602      *    Image related stuff
603      * **************************/
604 
605     /**
606      * Draws an {@link JXG.Image} on a board; This is just a template that has to be implemented by special renderers.
607      * @param {JXG.Image} element Reference to the image object that is to be drawn
608      * @see Image
609      * @see JXG.Image
610      * @see JXG.AbstractRenderer#updateImage
611      */
612     drawImage: function (element) { /* stub */ },
613 
614     /**
615      * Updates the properties of an {@link JXG.Image} element.
616      * @param {JXG.Image} element Reference to an {@link JXG.Image} object, that has to be updated.
617      * @see Image
618      * @see JXG.Image
619      * @see JXG.AbstractRenderer#drawImage
620      */
621     updateImage: function (element) {
622         this.updateRectPrim(element.rendNode, element.coords.scrCoords[1], element.coords.scrCoords[2] - element.size[1],
623                 element.size[0], element.size[1]);
624 
625         this.updateImageURL(element);
626         this.transformImage(element, element.transformations);
627         this._updateVisual(element, {stroke: true, dash: true}, true);
628     },
629 
630     /**
631      * Multiplication of transformations without updating. That means, at that point it is expected that the matrices
632      * contain numbers only. First, the origin in user coords is translated to <tt>(0,0)</tt> in screen coords.
633      * Then, the stretch factors are divided out. After the transformations in user coords, the  stretch factors
634      * are multiplied in again, and the origin in user coords is translated back to its position.
635      * This method does not have to be implemented in a new renderer.
636      * @param {JXG.GeometryElement} element A JSXGraph element. We only need its board property.
637      * @param {Array} transformations An array of JXG.Transformations.
638      * @returns {Array} A matrix represented by a two dimensional array of numbers.
639      * @see JXG.AbstractRenderer#transformImage
640      */
641     joinTransforms: function (element, transformations) {
642         var m = [[1, 0, 0], [0, 1, 0], [0, 0, 1]],
643             ox = element.board.origin.scrCoords[1],
644             oy = element.board.origin.scrCoords[2],
645             ux = element.board.unitX,
646             uy = element.board.unitY,
647             mpre1 =  [[1,   0, 0],      // Translate to 0,0 in screen coords
648                       [-ox, 1, 0], 
649                       [-oy, 0, 1]],  
650             mpre2 =  [[1, 0,     0],    // Scale
651                       [0, 1/ux,  0], 
652                       [0, 0, -1/uy]],
653             mpost2 = [[1, 0,   0],      // Scale back
654                       [0, ux,  0], 
655                       [0, 0, -uy]],
656             mpost1 = [[1,  0, 0],       // Translate back
657                       [ox, 1, 0], 
658                       [oy, 0, 1]],
659             i, len = transformations.length;
660 
661         for (i = 0; i < len; i++) {
662             m = JXG.Math.matMatMult(mpre1, m);
663             m = JXG.Math.matMatMult(mpre2, m);
664             m = JXG.Math.matMatMult(transformations[i].matrix, m);
665             m = JXG.Math.matMatMult(mpost2, m);
666             m = JXG.Math.matMatMult(mpost1, m);
667         }
668         return m;
669     },
670 
671     /**
672      * Applies transformations on images and text elements. This method is just a stub and has to be implemented in all
673      * descendant classes where text and image transformations are to be supported.
674      * @param {JXG.Image|JXG.Text} element A {@link JXG.Image} or {@link JXG.Text} object.
675      * @param {Array} transformations An array of {@link JXG.Transformation} objects. This is usually the transformations property
676      * of the given element <tt>el</tt>.
677      */
678     transformImage: function (element, transformations) { /* stub */ },
679 
680     /**
681      * If the URL of the image is provided by a function the URL has to be updated during updateImage()
682      * @param {JXG.Image} element Reference to an image object.
683      * @see JXG.AbstractRenderer#updateImage
684      */
685     updateImageURL: function (element) { /* stub */ },
686 
687     /* **************************
688      * Render primitive objects
689      * **************************/
690 
691     /**
692      * Appends a node to a specific layer level. This is just an abstract method and has to be implemented
693      * in all renderers that want to use the <tt>createPrim</tt> model to draw.
694      * @param {Node} node A DOM tree node.
695      * @param {Number} level The layer the node is attached to. This is the index of the layer in
696      * {@link JXG.SVGRenderer#layer} or the <tt>z-index</tt> style property of the node in VMLRenderer.
697      */
698     appendChildPrim: function (node, level) { /* stub */ },
699 
700     /**
701      * Stores the rendering nodes. This is an abstract method which has to be implemented in all renderers that use
702      * the <tt>createPrim</tt> method.
703      * @param {JXG.GeometryElement} element A JSXGraph element.
704      * @param {String} type The XML node name. Only used in VMLRenderer.
705      */
706     appendNodesToElement: function (element, type) { /* stub */ },
707 
708     /**
709      * Creates a node of a given type with a given id.
710      * @param {String} type The type of the node to create.
711      * @param {String} id Set the id attribute to this.
712      * @returns {Node} Reference to the created node.
713      */
714     createPrim: function (type, id) {
715         /* stub */
716         return null;
717     },
718 
719     /**
720      * Removes an element node. Just a stub.
721      * @param {Node} node The node to remove.
722      */
723     remove: function (node) { /* stub */ },    
724 
725     /**
726      * Can be used to create the nodes to display arrows. This is an abstract method which has to be implemented
727      * in any descendant renderer.
728      * @param {JXG.GeometryElement} element The element the arrows are to be attached to.
729      */
730     makeArrows: function(element) { /* stub */ },
731 
732     /**
733      * Updates an ellipse node primitive. This is an abstract method which has to be implemented in all renderers
734      * that use the <tt>createPrim</tt> method.
735      * @param {Node} node Reference to the node.
736      * @param {Number} x Centre X coordinate
737      * @param {Number} y Centre Y coordinate
738      * @param {Number} rx The x-axis radius.
739      * @param {Number} ry The y-axis radius.
740      */
741     updateEllipsePrim: function(node, x, y, rx, ry) { /* stub */ },
742 
743     /**
744      * Refreshes a line node. This is an abstract method which has to be implemented in all renderers that use
745      * the <tt>createPrim</tt> method.
746      * @param {Node} node The node to be refreshed.
747      * @param {Number} p1x The first point's x coordinate.
748      * @param {Number} p1y The first point's y coordinate.
749      * @param {Number} p2x The second point's x coordinate.
750      * @param {Number} p2y The second point's y coordinate.
751      * @param {JXG.Board} board
752      */
753     updateLinePrim: function(node, p1x, p1y, p2x, p2y, board) { /* stub */ },
754 
755     /**
756      * Updates a path element. This is an abstract method which has to be implemented in all renderers that use
757      * the <tt>createPrim</tt> method.
758      * @param {Node} node The path node.
759      * @param {String} pathString A string formatted like e.g. <em>'M 1,2 L 3,1 L5,5'</em>. The format of the string
760      * depends on the rendering engine.
761      * @param {JXG.Board} board Reference to the element's board.
762      */
763     updatePathPrim: function (node, pathString, board) { /* stub */ },
764 
765     /**
766      * Builds a path data string to draw a point with a face other than <em>rect</em> and <em>circle</em>. Since
767      * the format of such a string usually depends on the renderer this method
768      * is only an abstract method. Therefore, it has to be implemented in the descendant renderer itself unless
769      * the renderer does not use the createPrim interface but the draw* interfaces to paint.
770      * @param {JXG.Point} element The point element
771      * @param {Number} size A positive number describing the size. Usually the half of the width and height of
772      * the drawn point.
773      * @param {String} type A string describing the point's face. This method only accepts the shortcut version of
774      * each possible face: <tt>x, +, <>, ^, v, >, <
775      */
776     updatePathStringPoint: function (element, size, type) { /* stub */ },
777 
778     /**
779      * Builds a path data string from a {@link JXG.Curve} element. Since the path data strings heavily depend on the
780      * underlying rendering technique this method is just a stub. Although such a path string is of no use for the
781      * CanvasRenderer, this method is used there to draw a path directly.
782      * @param element
783      */
784     updatePathStringPrim: function (element) { /* stub */ },
785 
786     /**
787      * Builds a path data string from a {@link JXG.Curve} element such that the curve looks like
788      * hand drawn.
789      * Since the path data strings heavily depend on the
790      * underlying rendering technique this method is just a stub. Although such a path string is of no use for the
791      * CanvasRenderer, this method is used there to draw a path directly.
792      * @param element
793      */
794     updatePathStringBezierPrim: function (element) { /* stub */ },
795 
796     
797     /**
798      * Update a polygon primitive.
799      * @param {Node} node
800      * @param {JXG.Polygon} element A JSXGraph element of type {@link JXG.Polygon}
801      */
802     updatePolygonPrim: function (node, element) { /* stub */ },
803 
804     /**
805      * Update a rectangle primitive. This is used only for points with face of type 'rect'.
806      * @param {Node} node The node yearning to be updated.
807      * @param {Number} x x coordinate of the top left vertex.
808      * @param {Number} y y coordinate of the top left vertex.
809      * @param {Number} w Width of the rectangle.
810      * @param {Number} h The rectangle's height.
811      */
812     updateRectPrim: function(node, x, y, w, h) { /* stub */ },
813 
814     /* **************************
815      *  Set Attributes
816      * **************************/
817 
818     /**
819      * Sets a node's attribute.
820      * @param {Node} node The node that is to be updated.
821      * @param {String} key Name of the attribute.
822      * @param {String} val New value for the attribute.
823      */
824     setPropertyPrim: function (node, key, val) { /* stub */ },
825 
826     /**
827      * Shows a hidden element on the canvas; Only a stub, requires implementation in the derived renderer.
828      * @param {JXG.GeometryElement} element Reference to the object that has to appear.
829      * @see JXG.AbstractRenderer#hide
830      */
831     show: function (element) { /* stub */ },
832 
833     /**
834      * Hides an element on the canvas; Only a stub, requires implementation in the derived renderer.
835      * @param {JXG.GeometryElement} element Reference to the geometry element that has to disappear.
836      * @see JXG.AbstractRenderer#show
837      */
838     hide: function (element) { /* stub */ },
839 
840     /**
841      * Sets the buffering as recommended by SVGWG. Until now only Opera supports this and will be ignored by
842      * other browsers. Although this feature is only supported by SVG we have this method in {@link JXG.AbstractRenderer}
843      * because it is called from outside the renderer.
844      * @param {Node} node The SVG DOM Node which buffering type to update.
845      * @param {String} type Either 'auto', 'dynamic', or 'static'. For an explanation see
846      *   {@link http://www.w3.org/TR/SVGTiny12/painting.html#BufferedRenderingProperty}.
847      */
848     setBuffering: function (node, type) { /* stub */ },
849 
850     /**
851      * Sets an element's dash style.
852      * @param {JXG.GeometryElement} element An JSXGraph element.
853      */
854     setDashStyle: function (element) { /* stub */ },
855 
856     /**
857      * Puts an object into draft mode, i.e. it's visual appearance will be changed. For GEONE<sub>x</sub>T backwards compatibility.
858      * @param {JXG.GeometryElement} element Reference of the object that is in draft mode.
859      */
860     setDraft: function (element) {
861         if (!element.visProp.draft) {
862             return;
863         }
864         var draftColor = element.board.options.elements.draft.color,
865             draftOpacity = element.board.options.elements.draft.opacity;
866 
867         if (element.type === JXG.OBJECT_TYPE_POLYGON) {
868             this.setObjectFillColor(element, draftColor, draftOpacity);
869         }
870         else {
871             if (element.elementClass === JXG.OBJECT_CLASS_POINT) {
872                 this.setObjectFillColor(element, draftColor, draftOpacity);
873             }
874             else {
875                 this.setObjectFillColor(element, 'none', 0);
876             }
877             this.setObjectStrokeColor(element, draftColor, draftOpacity);
878             this.setObjectStrokeWidth(element, element.board.options.elements.draft.strokeWidth);
879         }
880     },
881 
882     /**
883      * Puts an object from draft mode back into normal mode.
884      * @param {JXG.GeometryElement} element Reference of the object that no longer is in draft mode.
885      */
886     removeDraft: function (element) {
887         if (element.type === JXG.OBJECT_TYPE_POLYGON) {
888             this.setObjectFillColor(element, element.visProp.fillcolor, element.visProp.fillopacity);
889         }
890         else {
891             if (element.type === JXG.OBJECT_CLASS_POINT) {
892                 this.setObjectFillColor(element, element.visProp.fillcolor, element.visProp.fillopacity);
893             }
894             this.setObjectStrokeColor(element, element.visProp.strokecolor, element.visProp.strokeopacity);
895             this.setObjectStrokeWidth(element, element.visProp.strokewidth);
896         }
897     },
898 
899     /**
900      * Sets up nodes for rendering a gradient fill.
901      * @param element
902      */
903     setGradient: function (element) { /* stub */ },
904 
905     /**
906      * Updates the gradient fill.
907      * @param {JXG.GeometryElement} element An JSXGraph element with an area that can be filled.
908      */
909     updateGradient: function (element) { /* stub */ },
910 
911     /**
912      * Sets an objects fill color.
913      * @param {JXG.GeometryElement} element Reference of the object that wants a new fill color.
914      * @param {String} color Color in a HTML/CSS compatible format. If you don't want any fill color at all, choose 'none'.
915      * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1.
916      */
917     setObjectFillColor: function (element, color, opacity) { /* stub */ },
918 
919     /**
920      * Changes an objects stroke color to the given color.
921      * @param {JXG.GeometryElement} element Reference of the {@link JXG.GeometryElement} that gets a new stroke color.
922      * @param {String} color Color value in a HTML compatible format, e.g. <strong>#00ff00</strong> or <strong>green</strong> for green.
923      * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1.
924      */
925     setObjectStrokeColor: function (element, color, opacity) { /* stub */ },
926 
927     /**
928      * Sets an element's stroke width.
929      * @param {JXG.GeometryElement} element Reference to the geometry element.
930      * @param {Number} width The new stroke width to be assigned to the element.
931      */
932     setObjectStrokeWidth: function (element, width) { /* stub */ },
933     
934     /**
935      * Sets the shadow properties to a geometry element. This method is only a stub, it is implemented in the actual renderers.
936      * @param {JXG.GeometryElement} element Reference to a geometry object, that should get a shadow
937      */
938     setShadow: function (element) { /* stub */ },
939 
940     /**
941      * Highlights an object, i.e. changes the current colors of the object to its highlighting colors
942      * @param {JXG.GeometryElement} element Reference of the object that will be highlighted.
943      * @returns {JXG.AbstractRenderer} Reference to the renderer
944      */
945     highlight: function (element) {
946         var i, ev = element.visProp;
947 
948         if (!ev.draft) {
949             /*
950                // Why did we have this? A.W.
951             if (element.elementClass === JXG.OBJECT_CLASS_POINT) {
952                 this.setObjectStrokeColor(element, ev.highlightstrokecolor, ev.highlightstrokeopacity);
953                 this.setObjectFillColor(element, ev.highlightstrokecolor, ev.highlightstrokeopacity);
954             } else 
955             */
956             if (element.type === JXG.OBJECT_TYPE_POLYGON) {
957                 this.setObjectFillColor(element, ev.highlightfillcolor, ev.highlightfillopacity);
958                 for (i = 0; i < element.borders.length; i++) {
959                     this.setObjectStrokeColor(element.borders[i], element.borders[i].visProp.highlightstrokecolor, element.borders[i].visProp.highlightstrokeopacity);
960                 }
961             } else {
962                 this.setObjectStrokeColor(element, ev.highlightstrokecolor, ev.highlightstrokeopacity);
963                 this.setObjectFillColor(element, ev.highlightfillcolor, ev.highlightfillopacity);
964             }
965             if (element.type === JXG.OBJECT_TYPE_TEXT) {
966                 if (element.visProp.display === 'html') {    
967                     element.rendNode.className = element.visProp.highlightcssclass;
968                 } 
969             }
970             if (ev.highlightstrokewidth) {
971                 this.setObjectStrokeWidth(element, Math.max(ev.highlightstrokewidth, ev.strokewidth));
972             }
973         }
974 
975         return this;
976     },
977 
978     /**
979      * Uses the normal colors of an object, i.e. the opposite of {@link JXG.AbstractRenderer#highlight}.
980      * @param {JXG.GeometryElement} element Reference of the object that will get its normal colors.
981      * @returns {JXG.AbstractRenderer} Reference to the renderer
982      */
983     noHighlight: function (element) {
984         var i, ev = element.visProp;
985 
986         if (!element.visProp.draft) {
987             /* 
988                // Why did we have this? A.W.
989               if (element.elementClass === JXG.OBJECT_CLASS_POINT) {
990                 this.setObjectStrokeColor(element, ev.strokecolor, ev.strokeopacity);
991                 this.setObjectFillColor(element, ev.strokecolor, ev.strokeopacity);
992             } else 
993             */
994             if (element.type === JXG.OBJECT_TYPE_POLYGON) {
995                 this.setObjectFillColor(element, ev.fillcolor, ev.fillopacity);
996                 for (i = 0; i < element.borders.length; i++) {
997                     this.setObjectStrokeColor(element.borders[i], element.borders[i].visProp.strokecolor, element.borders[i].visProp.strokeopacity);
998                 }
999             } else {
1000                 this.setObjectStrokeColor(element, ev.strokecolor, ev.strokeopacity);
1001                 this.setObjectFillColor(element, ev.fillcolor, ev.fillopacity);
1002             }
1003             if (element.type === JXG.OBJECT_TYPE_TEXT) {
1004                 if (element.visProp.display === 'html') {    
1005                     element.rendNode.className = element.visProp.cssclass;
1006                 }
1007             }
1008             this.setObjectStrokeWidth(element, ev.strokewidth);
1009         }
1010 
1011         return this;
1012     },
1013 
1014 
1015     /* **************************
1016      * renderer control
1017      * **************************/
1018 
1019     /**
1020      * Stop redraw. This method is called before every update, so a non-vector-graphics based renderer
1021      * can use this method to delete the contents of the drawing panel. This is an abstract method every
1022      * descendant renderer should implement, if appropriate.
1023      * @see JXG.AbstractRenderer#unsuspendRedraw
1024      */
1025     suspendRedraw: function () { /* stub */ },
1026 
1027     /**
1028      * Restart redraw. This method is called after updating all the rendering node attributes.
1029      * @see JXG.AbstractRenderer#suspendRedraw
1030      */
1031     unsuspendRedraw: function () { /* stub */ },
1032 
1033     /**
1034      * The tiny zoom bar shown on the bottom of a board (if showNavigation on board creation is true).
1035      * @param {JXG.Board} board Reference to a JSXGraph board.
1036      */
1037     drawZoomBar: function (board) {
1038         var doc,
1039             node,
1040             createButton = function (label, handler) {
1041                 var button;
1042 
1043                 button = doc.createElement('span');
1044                 node.appendChild(button);
1045                 button.appendChild(document.createTextNode(label));
1046                 /* button.innerHTML = label; */ // Does not work in XHTML
1047                 JXG.addEvent(button, 'click', handler, board);
1048             };
1049 
1050         doc = board.containerObj.ownerDocument;
1051         node = doc.createElement('div');
1052 
1053         node.setAttribute('id', board.containerObj.id + '_navigationbar');
1054 
1055         node.style.color = board.options.navbar.strokeColor;
1056         node.style.backgroundColor = board.options.navbar.fillColor;
1057         node.style.padding = board.options.navbar.padding;
1058         node.style.position = board.options.navbar.position;
1059         node.style.fontSize = board.options.navbar.fontSize;
1060         node.style.cursor = board.options.navbar.cursor;
1061         node.style.zIndex = board.options.navbar.zIndex;
1062         board.containerObj.appendChild(node);
1063         node.style.right = board.options.navbar.right;
1064         node.style.bottom = board.options.navbar.bottom;
1065 
1066         // For XHTML we need unicode instead of HTML entities
1067         createButton('\u00A0\u2013\u00A0', board.zoomOut);
1068         createButton('\u00A0o\u00A0', board.zoom100);
1069         createButton('\u00A0+\u00A0', board.zoomIn);
1070         createButton('\u00A0\u2190\u00A0', board.clickLeftArrow);
1071         createButton('\u00A0\u2193\u00A0', board.clickUpArrow);
1072         createButton('\u00A0\u2191\u00A0', board.clickDownArrow);
1073         createButton('\u00A0\u2192\u00A0', board.clickRightArrow);
1074     },
1075 
1076     /**
1077      * Wrapper for getElementById for maybe other renderers which elements are not directly accessible by DOM methods like document.getElementById().
1078      * @param {String} id Unique identifier for element.
1079      * @returns {Object} Reference to a JavaScript object. In case of SVG/VMLRenderer it's a reference to a SVG/VML node.
1080      */
1081     getElementById: function (id) {
1082         return document.getElementById(this.container.id + '_' + id);
1083     },
1084 
1085     /**
1086      * Resizes the rendering element
1087      * @param {Number} w New width
1088      * @param {Number} h New height
1089      */
1090     resize: function (w, h) { /* stub */}
1091 
1092 });
1093