1 /*
  2     Copyright 2008,2009
  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 von AbstractRenderer abgeleitete Zeichenklasse
 28 fuer Browser mit VML-Elementen (Internet Explorer)
 29 --------------------------------------------------------------------
 30 */
 31 
 32 JXG.VMLRenderer = function(container) {
 33     this.constructor();
 34     
 35     /* 
 36         Enable easy test which renderer is used.
 37     */
 38     this.type = 'vml';
 39     
 40     this.container = container;
 41     this.container.style.overflow = 'hidden';
 42     this.container.onselectstart = function () { return false; };
 43     
 44     this.resolution = 10; // Paths are drawn with a a resolution of this.resolution/pixel.
 45   
 46     // Add VML includes and namespace
 47     // Original: IE <=7
 48     //container.ownerDocument.createStyleSheet().addRule("v\\:*", "behavior: url(#default#VML);");
 49     if (JXG.vmlStylesheet==null) {
 50         container.ownerDocument.namespaces.add("jxgvml", "urn:schemas-microsoft-com:vml");
 51         JXG.vmlStylesheet = this.container.ownerDocument.createStyleSheet();
 52         JXG.vmlStylesheet.addRule(".jxgvml", "behavior:url(#default#VML)");
 53     }
 54     try {
 55         !container.ownerDocument.namespaces.jxgvml && container.ownerDocument.namespaces.add("jxgvml", "urn:schemas-microsoft-com:vml");
 56         this.createNode = function (tagName) {
 57             return container.ownerDocument.createElement('<jxgvml:' + tagName + ' class="jxgvml">');
 58         };
 59     } catch (e) {
 60         this.createNode = function (tagName) {
 61             return container.ownerDocument.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="jxgvml">');
 62         };
 63     }
 64     // um Dashes zu realisieren
 65     this.dashArray = ['Solid', '1 1', 'ShortDash', 'Dash', 'LongDash', 'ShortDashDot', 'LongDashDot'];    
 66 };
 67 
 68 JXG.VMLRenderer.prototype = JXG.AbstractRenderer();
 69 
 70 JXG.VMLRenderer.prototype.setAttr = function(node, key, val, val2) {
 71     try {
 72         if (document.documentMode==8) {
 73             node[key] = val;
 74         } else {
 75             node.setAttribute(key,val,val2);
 76         }
 77     } catch (e) {
 78         //document.getElementById('debug').innerHTML += node.id+' '+key+' '+val+'<br>\n';
 79     }
 80 };
 81 
 82 JXG.VMLRenderer.prototype.setShadow = function(el) {
 83     var nodeShadow = el.rendNodeShadow;
 84     
 85     if (!nodeShadow) return;                          // Added 29.9.09. A.W.
 86     if (el.visPropOld['shadow']==el.visProp['shadow']) {
 87         return;
 88     }
 89     if(el.visProp['shadow']) {
 90         this.setAttr(nodeShadow, 'On', 'True');
 91         this.setAttr(nodeShadow, 'Offset', '3pt,3pt');
 92         this.setAttr(nodeShadow, 'Opacity', '60%');
 93         this.setAttr(nodeShadow, 'Color', '#aaaaaa');
 94     }
 95     else {
 96         this.setAttr(nodeShadow, 'On', 'False');
 97     }
 98     el.visPropOld['shadow']=el.visProp['shadow'];
 99 };
100 
101 JXG.VMLRenderer.prototype.setGradient = function(el) {
102     var nodeFill = el.rendNodeFill;
103     if(el.visProp['gradient'] == 'linear') {
104         this.setAttr(nodeFill, 'type', 'gradient');
105         this.setAttr(nodeFill, 'color2', el.visProp['gradientSecondColor']);
106         this.setAttr(nodeFill, 'opacity2', el.visProp['gradientSecondOpacity']);
107         this.setAttr(nodeFill, 'angle', el.visProp['gradientAngle']);
108     }
109     else if (el.visProp['gradient'] == 'radial') {
110         this.setAttr(nodeFill, 'type','gradientradial');
111         this.setAttr(nodeFill, 'color2',el.visProp['gradientSecondColor']);
112         this.setAttr(nodeFill, 'opacity2',el.visProp['gradientSecondOpacity']);
113         this.setAttr(nodeFill, 'focusposition', el.visProp['gradientPositionX']*100+'%,'+el.visProp['gradientPositionY']*100+'%');
114         this.setAttr(nodeFill, 'focussize', '0,0');
115     }
116     else {
117         this.setAttr(nodeFill, 'type','solid');
118     }
119 };
120 
121 JXG.VMLRenderer.prototype.updateGradient = function(el) {}; // Not needed in VML;
122 
123 JXG.VMLRenderer.prototype.addShadowToGroup = function(groupname, board) {
124     var el, pEl;
125     if(groupname == "lines") {
126         for(el in board.objects) {
127             pEl = board.objects[el];
128             if(pEl.elementClass == JXG.OBJECT_CLASS_LINE) {
129                 this.addShadowToElement(pEl);
130             }
131         }
132     }
133     else if(groupname == "points") {
134         for(el in board.objects) {
135             pEl = board.objects[el];
136             if(pEl.elementClass == JXG.OBJECT_CLASS_POINT) {
137                 this.addShadowToElement(pEl);
138             }
139         }
140     }
141     else if(groupname == "circles") {
142         for(el in board.objects) {
143             pEl = board.objects[el];
144             if(pEl.elementClass == JXG.OBJECT_CLASS_CIRCLE) {
145                 this.addShadowToElement(pEl);
146             }
147         }
148     }    
149     board.fullUpdate();
150 };
151 
152 JXG.VMLRenderer.prototype.displayCopyright = function(str,fontsize) {
153     var node, t;
154     
155     //node = this.container.ownerDocument.createElement('v:textbox');
156     node = this.createNode('textbox');
157     node.style.position = 'absolute';
158     this.setAttr(node,'id', this.container.id+'_'+'licenseText');
159     
160     node.style.left = 20;
161     node.style.top = (2);
162     node.style.fontSize = (fontsize);
163     node.style.color = '#356AA0';
164     node.style.fontFamily = 'Arial,Helvetica,sans-serif';
165     this.setAttr(node,'opacity','30%');
166     node.style.filter = 'alpha(opacity = 30)';
167     
168     t = document.createTextNode(str);
169     node.appendChild(t);
170     this.appendChildPrim(node,0);
171 };
172 
173 JXG.VMLRenderer.prototype.drawInternalText = function(el) {
174     var node;
175     node = this.createNode('textbox');
176     node.style.position = 'absolute';
177     if (document.documentMode==8) {    
178         node.setAttribute('class', 'JXGtext');
179     } else {
180         node.setAttribute('className', 9);
181     }
182     el.rendNodeText = document.createTextNode('');
183     node.appendChild(el.rendNodeText);
184     this.appendChildPrim(node,9);
185     return node;
186 };
187 
188 JXG.VMLRenderer.prototype.updateInternalText = function(/** JXG.Text */ el) { 
189     el.rendNode.style.left = (el.coords.scrCoords[1])+'px'; 
190     el.rendNode.style.top = (el.coords.scrCoords[2] - this.vOffsetText)+'px'; 
191     el.updateText();
192     if (el.htmlStr!= el.plaintextStr) {
193         el.rendNodeText.data = el.plaintextStr;
194         el.htmlStr = el.plaintextStr;
195     }
196     this.transformImage(el, el.transformations);
197     
198 };
199 
200 JXG.VMLRenderer.prototype.drawTicks = function(ticks) {
201     var ticksNode = this.createPrim('path', ticks.id);
202     this.appendChildPrim(ticksNode,ticks.layer);
203     //ticks.rendNode = ticksNode;
204     this.appendNodesToElement(ticks, 'path');
205 };
206 
207 JXG.VMLRenderer.prototype.updateTicks = function(axis,dxMaj,dyMaj,dxMin,dyMin) {
208     var tickArr = [], i, len, c, ticks, r = this.resolution;
209     
210     len = axis.ticks.length;
211     for (i=0; i<len; i++) {
212         c = axis.ticks[i].scrCoords;
213         if(axis.ticks[i].major) {
214             if ((axis.board.needsFullUpdate||axis.needsRegularUpdate) && axis.labels[i].visProp['visible']) {
215                 this.drawText(axis.labels[i]);        
216             }
217             tickArr.push(' m ' + Math.round(r*(c[1]+dxMaj)) + 
218                          ', ' + Math.round(r*(c[2]-dyMaj)) + 
219                          ' l ' + Math.round(r*(c[1]-dxMaj)) + 
220                          ', ' + Math.round(r*(c[2]+dyMaj))+' ');
221         }
222         else
223             tickArr.push(' m ' + Math.round(r*(c[1]+dxMin)) + 
224                          ', ' + Math.round(r*(c[2]-dyMin)) + 
225                          ' l ' + Math.round(r*(c[1]-dxMin)) + 
226                          ', ' + Math.round(r*(c[2]+dyMin))+' ');
227     }
228 
229     ticks = this.getElementById(axis.id);
230     if(ticks == null) {
231         ticks = this.createPrim('path', axis.id);
232         this.appendChildPrim(ticks,axis.layer);
233         this.appendNodesToElement(axis,'path');
234     } 
235     this.setAttr(ticks,'stroked', 'true');
236     this.setAttr(ticks,'strokecolor', axis.visProp['strokeColor'], 1);
237     this.setAttr(ticks,'strokeweight', axis.visProp['strokeWidth']);   
238     //ticks.setAttributeNS(null, 'stroke-opacity', axis.visProp['strokeOpacity']);
239     this.updatePathPrim(ticks, tickArr, axis.board);
240 };
241 
242 JXG.VMLRenderer.prototype.drawImage = function(el) {
243     // IE 8: Bilder ueber data URIs werden bis 32kB unterstuetzt.
244     var node; // url = el.url(); //'data:image/png;base64,' + el.imageBase64String;    
245     
246     node = this.container.ownerDocument.createElement('img');
247     node.style.position = 'absolute';
248     this.setAttr(node,'id', this.container.id+'_'+el.id);
249 
250     //this.setAttr(node,'src',url);
251     this.container.appendChild(node);
252     this.appendChildPrim(node,el.layer);
253     // Adding the rotation filter. This is always filter item 0:
254     // node.filters.item(0), see transformImage
255     node.style.filter = node.style['-ms-filter'] = 
256         "progid:DXImageTransform.Microsoft.Matrix(M11='1.0', sizingMethod='auto expand')";
257     el.rendNode = node;
258     this.updateImage(el);
259 };
260 
261 JXG.VMLRenderer.prototype.updateImageURL = function(el) {
262     var url;
263     if (JXG.isFunction(el.url)) {
264         url = el.url();
265     } else {
266         url = el.url;
267     }
268     this.setAttr(el.rendNode,'src',url);
269 };
270 
271 JXG.VMLRenderer.prototype.transformImage = function(el,t) {
272     var node = el.rendNode, 
273         m, p = [], s, len = t.length,
274         maxX, maxY, minX, minY, i, h, w,
275         nt;
276 
277     if (el.type==JXG.OBJECT_TYPE_TEXT) {
278         el.updateSize();
279     }
280     if (len>0) {
281         nt = el.rendNode.style.filter.toString();
282         if (!nt.match(/DXImageTransform/)) {
283             node.style.filter = node.style['-ms-filter'] = 
284                 "progid:DXImageTransform.Microsoft.Matrix(M11='1.0', sizingMethod='auto expand') " + nt;
285         }
286 
287         m = this.joinTransforms(el,t);
288         p[0] = JXG.Math.matVecMult(m, el.coords.scrCoords);
289         p[0][1] /= p[0][0];
290         p[0][2] /= p[0][0];
291         p[1] = JXG.Math.matVecMult(m, [1, el.coords.scrCoords[1]+el.size[0], el.coords.scrCoords[2]]);
292         p[1][1] /= p[1][0];
293         p[1][2] /= p[1][0];
294         p[2] = JXG.Math.matVecMult(m, [1, el.coords.scrCoords[1]+el.size[0], el.coords.scrCoords[2]-el.size[1]]);
295         p[2][1] /= p[2][0];
296         p[2][2] /= p[2][0];
297         p[3] = JXG.Math.matVecMult(m, [1, el.coords.scrCoords[1], el.coords.scrCoords[2]-el.size[1]]);
298         p[3][1] /= p[3][0];
299         p[3][2] /= p[3][0];
300         maxX = p[0][1];
301         minX = p[0][1];
302         maxY = p[0][2];
303         minY = p[0][2];
304         for (i=1; i<4; i++) {
305             maxX = Math.max(maxX, p[i][1]);
306             minX = Math.min(minX, p[i][1]);
307             maxY = Math.max(maxY, p[i][2]);
308             minY = Math.min(minY, p[i][2]);
309         }
310         node.style.left = minX + 'px'; 
311         node.style.top = minY + 'px';    
312         
313         node.filters.item(0).M11 = m[1][1];
314         node.filters.item(0).M12 = m[1][2];
315         node.filters.item(0).M21 = m[2][1];
316         node.filters.item(0).M22 = m[2][2];
317     }
318 };
319 
320 /*
321 JXG.VMLRenderer.prototype.removeGrid = function(board) { 
322     var c = this.getElementById('gridx');
323     this.remove(c);
324 
325     c = this.getElementById('gridy');
326     this.remove(c);
327 
328     board.hasGrid = false;
329 };
330 */
331 
332 JXG.VMLRenderer.prototype.hide = function(el) {
333     var node;
334 
335     if (!JXG.exists(el))
336         return;
337     node = el.rendNode;
338     if(JXG.exists(node)) {
339         node.style.visibility = "hidden";
340     }
341 };
342 
343 JXG.VMLRenderer.prototype.show = function(el) {
344     var node;
345 
346     if (!JXG.exists(el))
347         return;
348     node = el.rendNode;
349     if(JXG.exists(node)) {
350         node.style.visibility = "inherit";
351     }
352 };
353 
354 JXG.VMLRenderer.prototype.setDashStyle = function(el,visProp) {
355     var node;
356     if(visProp['dash'] >= 0) {
357         node = el.rendNodeStroke;
358         this.setAttr(node,'dashstyle', this.dashArray[visProp['dash']]);
359     }
360 };
361  
362 JXG.VMLRenderer.prototype.setObjectStrokeColor = function(el, color, opacity) {
363     var c = this.evaluate(color),
364         o = this.evaluate(opacity), 
365         node, nodeStroke;
366 
367     o = (o>0)?o:0;
368 
369     if (el.visPropOld['strokeColor']==c && el.visPropOld['strokeOpacity']==o) {
370         return;
371     }
372     if(el.type == JXG.OBJECT_TYPE_TEXT) {
373         el.rendNode.style.color = c;
374     }        
375     else {       
376         node = el.rendNode;
377         this.setAttr(node,'stroked', 'true');
378         this.setAttr(node,'strokecolor', c);
379         
380         if(el.id == 'gridx') {
381             nodeStroke = this.getElementById('gridx_stroke');
382         }
383         else if(el.id == 'gridy') {
384             nodeStroke = this.getElementById('gridy_stroke');
385         }
386         else {
387             nodeStroke = el.rendNodeStroke;
388         }
389         if (JXG.exists(o)) {
390             this.setAttr(nodeStroke,'opacity', (o*100)+'%');  
391             
392         }
393     }
394     el.visPropOld['strokeColor'] = c;
395     el.visPropOld['strokeOpacity'] = o;
396 };
397 
398 JXG.VMLRenderer.prototype.setObjectFillColor = function(el, color, opacity) {
399     var c = this.evaluate(color),
400         o = this.evaluate(opacity), t;
401 
402     o = (o>0)?o:0;
403 
404     if (el.visPropOld['fillColor']==c && el.visPropOld['fillOpacity']==o) {
405         return;
406     }
407     if(c == 'none') {
408         this.setAttr(el.rendNode,'filled', 'false');
409     }
410     else {
411         this.setAttr(el.rendNode,'filled', 'true');
412         this.setAttr(el.rendNode,'fillcolor', c); 
413         if (JXG.exists(o) && el.rendNodeFill) {  // Added el.rendNodeFill 29.9.09  A.W.
414             this.setAttr(el.rendNodeFill,'opacity', (o*100)+'%');
415         }
416     }
417     if (el.type==JXG.OBJECT_TYPE_IMAGE) {
418         t = el.rendNode.style.filter.toString();
419         if (t.match(/alpha/)) {
420             el.rendNode.style.filter = t.replace(/alpha\(opacity *= *[0-9\.]+\)/,
421                 'alpha(opacity = '+(o*100)+')');
422         } else {
423             el.rendNode.style.filter += ' alpha(opacity = ' + (o*100) +')';
424         }
425     }
426     el.visPropOld['fillColor'] = c;
427     el.visPropOld['fillOpacity'] = o;
428 };
429 
430 JXG.VMLRenderer.prototype.remove = function(node) {
431   if (node!=null) node.removeNode(true);
432 };
433 
434 JXG.VMLRenderer.prototype.suspendRedraw = function() {
435     this.container.style.display='none';
436 };
437 
438 JXG.VMLRenderer.prototype.unsuspendRedraw = function() {
439     this.container.style.display='';
440 };
441 
442 JXG.VMLRenderer.prototype.setAttributes = function(node,props,vmlprops,visProp) {
443     var val, i, p,
444         len = props.length;
445 
446     for (i=0;i<len;i++) {
447         p = props[i];
448         if (visProp[p]!=null) {
449             val = this.evaluate(visProp[p]);
450             val = (val>0)?val:0;
451             this.setAttr(node,vmlprops[i], val);
452         }
453     }
454 };
455 
456 JXG.VMLRenderer.prototype.setGridDash = function(id, node) {
457     var node = this.getElementById(id+'_stroke');
458     this.setAttr(node,'dashstyle', 'Dash');
459 };
460 
461 /**
462  * Sets an elements stroke width.
463  * @param {Object} el Reference to the geometry element.
464  * @param {int} width The new stroke width to be assigned to the element.
465  */
466 JXG.VMLRenderer.prototype.setObjectStrokeWidth = function(el, width) {
467     var w = this.evaluate(width),
468         node;
469     //w = (w>0)?w:0;
470     
471     if (el.visPropOld['strokeWidth']==w) {
472         return;
473     }
474     
475     node = el.rendNode;
476     this.setPropertyPrim(node,'stroked', 'true');
477     if (w!=null) { 
478         this.setPropertyPrim(node,'stroke-width',w); 
479     }
480     el.visPropOld['strokeWidth'] = w;
481 };
482 
483 JXG.VMLRenderer.prototype.createPrim = function(type, id) {
484     var node, 
485         fillNode = this.createNode('fill'), 
486         strokeNode = this.createNode('stroke'), 
487         shadowNode = this.createNode('shadow'), 
488         pathNode;
489     
490     this.setAttr(fillNode, 'id', this.container.id+'_'+id+'_fill');
491     this.setAttr(strokeNode, 'id', this.container.id+'_'+id+'_stroke');
492     this.setAttr(shadowNode, 'id', this.container.id+'_'+id+'_shadow');
493     
494     if (type=='circle' || type=='ellipse' ) {
495         node = this.createNode('oval');
496         node.appendChild(fillNode);
497         node.appendChild(strokeNode);
498         node.appendChild(shadowNode);
499     } else if (type == 'polygon' || type == 'path' || type == 'shape' || type == 'line') {    
500         node = this.createNode('shape');
501         node.appendChild(fillNode);
502         node.appendChild(strokeNode);
503         node.appendChild(shadowNode);   
504         pathNode = this.createNode('path');
505         this.setAttr(pathNode, 'id', this.container.id+'_'+id+'_path');        
506         node.appendChild(pathNode);
507     } else {
508         node = this.createNode(type);
509         node.appendChild(fillNode);
510         node.appendChild(strokeNode);
511         node.appendChild(shadowNode);
512     }
513     node.style.position = 'absolute';
514     this.setAttr(node, 'id', this.container.id+'_'+id);
515     
516     return node;
517 };
518 
519 JXG.VMLRenderer.prototype.appendNodesToElement = function(element, type) {
520     if(type == 'shape' || type == 'path' || type == 'polygon') {
521         element.rendNodePath = this.getElementById(element.id+'_path');
522     }
523     element.rendNodeFill = this.getElementById(element.id+'_fill');
524     element.rendNodeStroke = this.getElementById(element.id+'_stroke');
525     element.rendNodeShadow = this.getElementById(element.id+'_shadow');
526     element.rendNode = this.getElementById(element.id);
527 };
528 
529 /*
530 // seems to be unused
531 JXG.VMLRenderer.prototype.makeArrow = function(node,el,idAppendix) {
532     var nodeStroke = el.rendNodeStroke;
533     this.setAttr(nodeStroke, 'endarrow', 'block');
534     this.setAttr(nodeStroke, 'endarrowlength', 'long');
535 };
536 */
537 
538 JXG.VMLRenderer.prototype.makeArrows = function(el) {
539     var nodeStroke;
540     
541     if (el.visPropOld['firstArrow']==el.visProp['firstArrow'] && el.visPropOld['lastArrow']==el.visProp['lastArrow']) {
542         return;
543     }
544 
545     if(el.visProp['firstArrow']) {
546         nodeStroke = el.rendNodeStroke;
547         this.setAttr(nodeStroke, 'startarrow', 'block');
548         this.setAttr(nodeStroke, 'startarrowlength', 'long');                 
549     }
550     else {
551         nodeStroke = el.rendNodeStroke;
552         if(nodeStroke != null) {
553             this.setAttr(nodeStroke, 'startarrow', 'none');
554         }            
555     }
556     if(el.visProp['lastArrow']) {
557         nodeStroke = el.rendNodeStroke;
558         this.setAttr(nodeStroke, 'id', this.container.id+'_'+el.id+"stroke");
559         this.setAttr(nodeStroke, 'endarrow', 'block');
560         this.setAttr(nodeStroke, 'endarrowlength', 'long');            
561     }
562     else {
563         nodeStroke = el.rendNodeStroke;
564         if(nodeStroke != null) {
565             this.setAttr(nodeStroke, 'endarrow', 'none');
566         }        
567     }    
568     el.visPropOld['firstArrow'] = el.visProp['firstArrow'];
569     el.visPropOld['lastArrow'] = el.visProp['lastArrow'];
570 };
571 
572 JXG.VMLRenderer.prototype.updateLinePrim = function(node,p1x,p1y,p2x,p2y,board) {
573     /* 
574     this.setAttr(node, 'from', [p1x,p1y].join(',')); 
575     this.setAttr(node, 'to', [p2x,p2y].join(','));      
576     */
577     var s, r = this.resolution;
578     s = ['m ',r*p1x,', ',r*p1y,' l ',r*p2x,', ',r*p2y];
579     this.updatePathPrim(node,s,board);
580 };
581 
582 JXG.VMLRenderer.prototype.updateCirclePrim = function(node,x,y,r) {
583     //node.setAttribute('style','left:'+(x-r)+'px; top:'+(y-r)+'px; width:'+(r*2)+'px; height:'+ (r*2)+'px'); 
584     node.style.left = (x-r)+'px';
585     node.style.top = (y-r)+'px';    
586     node.style.width = (r*2)+'px'; 
587     node.style.height = (r*2)+'px';   
588 };
589 
590 JXG.VMLRenderer.prototype.updateRectPrim = function(node,x,y,w,h) {
591     node.style.left = (x)+'px';
592     node.style.top = (y)+'px';    
593     if (w>=0) node.style.width = (w)+'px'; 
594     if (h>=0) node.style.height = (h)+'px';   
595 };
596 
597 JXG.VMLRenderer.prototype.updateEllipsePrim = function(node,x,y,rx,ry) {
598     node.style.left = (x-rx)+'px';
599     node.style.top =  (y-ry)+'px'; 
600     node.style.width = (rx*2)+'px'; 
601     node.style.height = (ry*2)+'px';
602 };
603 
604 JXG.VMLRenderer.prototype.updatePathPrim = function(node,pointString,board) {
605     var x = board.canvasWidth, 
606         y = board.canvasHeight;
607     node.style.width = x;
608     node.style.height = y;
609     this.setAttr(node, 'coordsize', [(this.resolution*x),(this.resolution*y)].join(','));
610     this.setAttr(node, 'path',pointString.join(""));
611 };
612 
613 JXG.VMLRenderer.prototype.updatePathStringPrim = function(el) {
614     var pStr = [], 
615         //h = 3*el.board.canvasHeight, 
616         //w = 100*el.board.canvasWidth, 
617         i, scr,
618         r = this.resolution,
619         mround = Math.round,
620         symbm = ' m ', 
621         symbl = ' l ',
622         nextSymb = symbm, 
623         isNoPlot = (el.curveType!='plot'),
624         //isFunctionGraph = (el.curveType=='functiongraph'),
625         len = Math.min(el.numberPoints,8192); // otherwise IE 7 crashes in hilbert.html
626     
627     if (el.numberPoints<=0) { return ''; }
628     if (isNoPlot && el.board.options.curve.RDPsmoothing) {
629         el.points = this.RamenDouglasPeuker(el.points,1.0);
630     }
631     len = Math.min(len,el.points.length);
632 
633     for (i=0; i<len; i++) {
634         scr = el.points[i].scrCoords;
635         if (isNaN(scr[1]) || isNaN(scr[2]) /* || Math.abs(scr[1])>w || (isFunctionGraph && (scr[2]>h || scr[2]<-0.5*h))*/ ) {  // PenUp
636             nextSymb = symbm;
637         } else {
638             // IE has problems with values  being too far away.
639             if (scr[1]>20000.0) { scr[1] = 20000.0; }
640             else if (scr[1]<-20000.0) { scr[1] = -20000.0; }
641             if (scr[2]>20000.0) { scr[2] = 20000.0; }
642             else if (scr[2]<-20000.0) { scr[2] = -20000.0; }
643 
644             pStr.push([nextSymb,mround(r*scr[1]),', ',mround(r*scr[2])].join(''));
645             nextSymb = symbl;
646         }
647     }
648     pStr.push(' e');
649     return pStr;
650 };
651 
652 JXG.VMLRenderer.prototype.updatePathStringPoint = function(el, size, type) {
653     var s = [],
654         scr = el.coords.scrCoords,
655         sqrt32 = size*Math.sqrt(3)*0.5,
656         s05 = size*0.5,
657         r = this.resolution;
658 
659     if(type == 'x') {
660         s.push(['m ',(r*(scr[1]-size)),', ',(r*(scr[2]-size)),' l ',
661         (r*(scr[1]+size)),', ',(r*(scr[2]+size)),' m ',
662         (r*(scr[1]+size)),', ',(r*(scr[2]-size)),' l ',
663         (r*(scr[1]-size)),', ',(r*(scr[2]+size))].join(''));
664     }
665     else if(type == '+') {
666         s.push(['m ',(r*(scr[1]-size)),', ',(r*(scr[2])),' l ',
667         (r*(scr[1]+size)),', ',(r*(scr[2])),' m ',
668         (r*(scr[1])),', ',(r*(scr[2]-size)),' l ',
669         (r*(scr[1])),', ',(r*(scr[2]+size))].join(''));    
670     }
671     else if(type == '<>') {
672         s.push(['m ',(r*(scr[1]-size)),', ',(r*(scr[2])),' l ',
673         (r*(scr[1])),', ',(r*(scr[2]+size)),' l ',
674         (r*(scr[1]+size)),', ',(r*(scr[2])),' l ',
675         (r*(scr[1])),', ',(r*(scr[2]-size)),' x e '
676         ].join(''));   
677     }
678     else if(type == '^') {
679         s.push(['m ',(r*(scr[1])),', ',(r*(scr[2]-size)),' l ',
680         Math.round(r*(scr[1]-sqrt32)),', ',(r*(scr[2]+s05)),' l ',
681         Math.round(r*(scr[1]+sqrt32)),', ',(r*(scr[2]+s05)),' x e '
682         ].join(''));           
683     } 
684     else if(type == 'v') {
685         s.push(['m ',(r*(scr[1])),', ',(r*(scr[2]+size)),' l ',
686         Math.round(r*(scr[1]-sqrt32)),', ',(r*(scr[2]-s05)),' l ',
687         Math.round(r*(scr[1]+sqrt32)),', ',(r*(scr[2]-s05)),' x e '
688         ].join(''));       
689     }   
690     else if(type == '>') {
691         s.push(['m ',(r*(scr[1]+size)),', ',(r*(scr[2])),' l ',
692         (r*(scr[1]-s05)),', ',Math.round(r*(scr[2]-sqrt32)),' l ',
693         (r*(scr[1]-s05)),', ',Math.round(r*(scr[2]+sqrt32)),
694         //' x e '
695         ' l ',(r*(scr[1]+size)),', ',(r*(scr[2])) 
696         ].join(''));        
697     }
698     else if(type == '<') {
699         s.push(['m ',(r*(scr[1]-size)),', ',(r*(scr[2])),' l ',
700         (r*(scr[1]+s05)),', ',Math.round(r*(scr[2]-sqrt32)),' l ',
701         (r*(scr[1]+s05)),', ',Math.round(r*(scr[2]+sqrt32)),' x e '
702         ].join(''));    
703     }    
704     return s;
705 }
706 
707 JXG.VMLRenderer.prototype.updatePolygonPrim = function(node,el) {
708     var minX = el.vertices[0].coords.scrCoords[1],
709         maxX = el.vertices[0].coords.scrCoords[1],
710         minY = el.vertices[0].coords.scrCoords[2],
711         maxY = el.vertices[0].coords.scrCoords[2],
712         i, 
713         len = el.vertices.length,
714         scr, x, y, 
715         pStr = [];
716         
717     this.setAttr(node, 'stroked', 'false');
718     for(i=1; i<len-1; i++) {
719         scr = el.vertices[i].coords.scrCoords;
720         if(scr[1] < minX) {
721             minX = scr[1];
722         }
723         else if(scr[1] > maxX) {
724             maxX = scr[1];
725         }
726         if(scr[2] < minY) {
727             minY = scr[2];
728         }
729         else if(scr[2] > maxY) {
730             maxY = scr[2];
731         }
732     }
733 
734     x = Math.round(maxX-minX); // Breite des umgebenden Rechtecks?
735     y = Math.round(maxY-minY); // Hoehe des umgebenden Rechtecks?
736 
737     if (!isNaN(x) && !isNaN(y)) {
738         node.style.width = x;
739         node.style.height = y;
740         this.setAttr(node, 'coordsize', x+','+y);
741     }
742      
743     scr = el.vertices[0].coords.scrCoords;
744     pStr.push(["m ",scr[1],",",scr[2]," l "].join(''));
745     
746     for(i=1; i<len-1; i++) {
747         scr = el.vertices[i].coords.scrCoords;
748         pStr.push(scr[1] + "," + scr[2]);
749         if(i<len-2) {
750             pStr.push(", ");
751         }
752     }
753     pStr.push(" x e");
754 
755     this.setAttr(node, 'path',pStr.join(""));
756 };
757 
758 JXG.VMLRenderer.prototype.appendChildPrim = function(node,level) {
759     if (!JXG.exists(level)) level = 0;   // For trace nodes    
760     node.style.zIndex = level;
761     this.container.appendChild(node);
762 };
763 
764 JXG.VMLRenderer.prototype.setPropertyPrim = function(node,key,val) {
765     var keyVml = '', 
766         node2, v;
767         
768     switch (key) {
769         case 'stroke': keyVml = 'strokecolor'; break;
770         case 'stroke-width': keyVml = 'strokeweight'; break;
771         case 'stroke-dasharray': keyVml = 'dashstyle'; break;
772     }
773     if (keyVml!='') {
774         v = this.evaluate(val);
775         this.setAttr(node, keyVml, v);
776     }
777 };
778 
779 JXG.VMLRenderer.prototype.drawVerticalGrid = function(topLeft, bottomRight, gx, board) {
780     var node = this.createPrim('path', 'gridx'),
781         gridArr = [];
782         
783     while(topLeft.scrCoords[1] < bottomRight.scrCoords[1] + gx - 1) { 
784         gridArr.push(' m ' + (this.resolution*topLeft.scrCoords[1]) + 
785                      ', ' + 0 + 
786                      ' l ' + (this.resolution*topLeft.scrCoords[1]) + 
787                      ', ' + (this.resolution*board.canvasHeight)+' ');
788         topLeft.setCoordinates(JXG.COORDS_BY_SCREEN, [topLeft.scrCoords[1] + gx, topLeft.scrCoords[2]]);   
789     }
790     this.updatePathPrim(node, gridArr, board);
791     return node;
792 };
793 
794 JXG.VMLRenderer.prototype.drawHorizontalGrid = function(topLeft, bottomRight, gy, board) {
795     var node = this.createPrim('path', 'gridy'),
796         gridArr = [];
797     while(topLeft.scrCoords[2] <= bottomRight.scrCoords[2] + gy - 1) {
798         gridArr.push(' m ' + 0 + 
799                      ', ' + (this.resolution*topLeft.scrCoords[2]) + 
800                      ' l ' + (this.resolution*board.canvasWidth) + 
801                      ', ' + (this.resolution*topLeft.scrCoords[2])+' ');
802         topLeft.setCoordinates(JXG.COORDS_BY_SCREEN, [topLeft.scrCoords[1], topLeft.scrCoords[2] + gy]);
803     }
804     this.updatePathPrim(node, gridArr, board);
805     return node;
806 };
807