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 JXG.OBJECT_TYPE_ARC  = 0x4F544143;                 // Hex fuer OTAC = Object Type ArC
 26 JXG.OBJECT_TYPE_ARROW  = 0x4F544157;                 // Hex fuer OTAW = Object Type ArroW
 27 JXG.OBJECT_TYPE_AXIS  = 0x4F544158;                 // Hex fuer OTAX = Object Type AXis
 28 JXG.OBJECT_TYPE_TICKS  = 0x4F545458;                 // Hex fuer OTTX = Object Type TiX
 29 JXG.OBJECT_TYPE_CIRCLE  = 0x4F54434C;                 // Hex fuer OTCC = Object Type CirCle
 30 JXG.OBJECT_TYPE_CONIC  = 0x4F54434F;                 // Hex fuer OTCC = Object Type COnic
 31 JXG.OBJECT_TYPE_CURVE  = 0x4F544750;                 // Hex fuer OTGP = Object Type GraphPlot
 32 JXG.OBJECT_TYPE_GLIDER  = 0x4F54474C;                 // Hex fuer OTGL = Object Type GLider
 33 JXG.OBJECT_TYPE_IMAGE  = 0x4F54524D;                 // Hex fuer OTIM = Object Type IMage
 34 JXG.OBJECT_TYPE_LINE  = 0x4F544C4E;                 // Hex fuer OTLN = Object Type LiNe
 35 JXG.OBJECT_TYPE_POINT  = 0x4F545054;                 // Hex fuer OTPT = Object Type PoinT
 36 JXG.OBJECT_TYPE_SLIDER = 0x4F545344;                 // Hex fuer OTSD = Object Type SliDer
 37 JXG.OBJECT_TYPE_CAS    = 0x4F544350;                 // Hex fuer OTCP = Object Type CasPoint
 38 JXG.OBJECT_TYPE_POLYGON  = 0x4F545059;                 // Hex fuer OTPY = Object Type PolYgon
 39 JXG.OBJECT_TYPE_SECTOR  = 0x4F545343;                 // Hex fuer OTSC = Object Type SeCtor
 40 JXG.OBJECT_TYPE_TEXT  = 0x4F545445;                 // Hex fuer OTTE = Object Type TextElement
 41 JXG.OBJECT_TYPE_ANGLE = 0x4F544147;                 // Hex fuer OTAG = Object Type AnGle
 42 JXG.OBJECT_TYPE_INTERSECTION = 0x4F54524E;          // Hex fuer OTIN = Object Type INtersection
 43 JXG.OBJECT_TYPE_TURTLE = 0x4F5455;                 // Hex fuer OTTU = Object Type TUrtle
 44 JXG.OBJECT_TYPE_VECTOR = 0x4F545654; 				//Hex fuer OTVT 0 Object Type VecTor
 45 
 46 JXG.OBJECT_CLASS_POINT = 1;
 47 JXG.OBJECT_CLASS_LINE = 2;
 48 JXG.OBJECT_CLASS_CIRCLE = 3;
 49 JXG.OBJECT_CLASS_CURVE = 4;
 50 JXG.OBJECT_CLASS_AREA = 5;
 51 JXG.OBJECT_CLASS_OTHER = 6;
 52 
 53 /**
 54  * Constructs a new GeometryElement object.
 55  * @class This is the basic class for geometry elements like points, circles and lines.
 56  * @constructor
 57  * of identical elements on the board. Is not yet implemented for all elements, only points, lines and circle can be traced.
 58  */
 59 JXG.GeometryElement = function() {
 60     /**
 61      * Reference to board where the element is drawn
 62      * @type JXG.Board
 63      * @default null
 64      * @see JXG.Board
 65      */
 66     this.board = null;
 67 
 68     /**
 69      * Unique identifier for the element. Equivalent to id-attribute of renderer element.
 70      * @type String
 71      * @default empty string
 72      */
 73     this.id = '';
 74 
 75     /**
 76      * Controls if updates are necessary
 77      * @type Boolean
 78      * @default true
 79      */
 80     this.needsUpdate = true;
 81 
 82     /**
 83      * Not necessarily unique name for the element.
 84      * @type String
 85      * @default Name generated by {@link JXG.Board#generateName}.
 86      * @see JXG.Board#generateName
 87      */
 88     this.name = '';
 89 
 90     /**
 91      * An associative array containing all visual properties.
 92      * @type Object
 93      * @default empty object
 94      */
 95     this.visProp = {};
 96 
 97     JXG.clearVisPropOld(this); // create this.visPropOld and set default values
 98 
 99     /**
100      * If element is in two dimensional real space this is true, else false.
101      * @type Boolean
102      * @default true
103      */
104     this.isReal = true;
105 
106     /**
107      * Determines the elements border-style.
108      * Possible values are:
109      * <ul><li>0 for a solid line</li>
110      * <li>1 for a dotted line</li>
111      * <li>2 for a line with small dashes</li>
112      * <li>3 for a line with medium dashes</li>
113      * <li>4 for a line with big dashes</li>
114      * <li>5 for a line with alternating medium and big dashes and large gaps</li>
115      * <li>6 for a line with alternating medium and big dashes and small gaps</li></ul>
116      * @type Number
117      * @name JXG.GeometryElement#dash
118      * @default 0
119      */
120     this.visProp['dash'] = 0;
121 
122     /**
123      * Stores all dependent objects to be updated when this point is moved.
124      * @type Object
125      */
126     this.childElements = {};
127 
128     /**
129      * If element has a label subelement then this property will be set to true.
130      * @type Boolean
131      * @default false
132      */
133     this.hasLabel = false;
134 
135     /**
136      * display layer which will conting the element.
137      * Controlled in JXG.Options.
138      */
139     this.layer = 9;
140 
141     /**
142      * Stores all Intersection Objects which in this moment are not real and
143      * so hide this element.
144      * @type Object
145      */
146     this.notExistingParents = {};
147 
148     /**
149      * If true the element will be traced, i.e. on every movement the element will be copied
150      * to the background. Use {@link JXG.GeometryElement#clearTrace} to delete the trace elements.
151      * @see JXG.GeometryElement#clearTrace
152      * @see JXG.GeometryElement#traces
153      * @see JXG.GeometryElement#numTraces
154      * @type Boolean
155      * @default false
156      * @name JXG.GeometryElement#trace
157      */
158     this.traced = false;
159 
160     /**
161      * If true the element is fixed and can not be dragged around. The element 
162      * will be repositioned on zoom and moveOrigin events.
163      * @type Boolean
164      * @default false
165      * @name JXG.GeometryElement#fixed
166      */
167     this.fixed = false;
168 
169     /**
170      * If true the element is fixed and can not be dragged around. The element 
171      * will even stay at its position on zoom and moveOrigin events.
172      * Only free elements like points, texts, curves can be frozen.
173      * @type Boolean
174      * @default false
175      * @name JXG.GeometryElement#fixed
176      */
177     this.frozen = false;
178 
179     /**
180      * Keeps track of all objects drawn as part of the trace of the element.
181      * @see JXG.GeometryElement#traced
182      * @see JXG.GeometryElement#clearTrace
183      * @see JXG.GeometryElement#numTraces
184      * @type Object
185      */
186     this.traces = {};
187 
188     /**
189      * Counts the number of objects drawn as part of the trace of the element.
190      * @see JXG.GeometryElement#traced
191      * @see JXG.GeometryElement#clearTrace
192      * @see JXG.GeometryElement#traces
193      * @type Number
194      */
195     this.numTraces = 0;
196 
197     /**
198      * Stores the  transformations which are applied during update in an array
199      * @type Array
200      * @see JXG.Transformation
201      */
202     this.transformations = [];
203 
204     /** TODO
205      * @type JXG.GeometryElement
206      * @default null
207      * @private
208      */
209     this.baseElement = null;
210 
211     /**
212      * Elements depending on this element are stored here.
213      * @type Object
214      */
215     this.descendants = {};
216 
217     /**
218      * Elements on which this elements depends on are stored here.
219      * @type Object
220      */
221     this.ancestors = {};
222 
223     /**
224      * Stores variables for symbolic computations
225      * @type Object
226      */
227     this.symbolic = {};
228 
229     /**
230      * [c,b0,b1,a,k,r,q0,q1]
231      *
232      * See
233      * A.E. Middleditch, T.W. Stacey, and S.B. Tor:
234      * "Intersection Algorithms for Lines and Circles",
235      * ACM Transactions on Graphics, Vol. 8, 1, 1989, pp 25-40.
236      *
237      * The meaning of the parameters is:
238      * Circle: points p=[p0,p1] on the circle fulfill
239      *  a<p,p> + <b,p> + c = 0
240      * For convenience we also store
241      *  r: radius
242      *  k: discriminant = sqrt(<b,b>-4ac)
243      *  q=[q0,q1] center
244      *
245      * Points have radius = 0.
246      * Lines have radius = infinity.
247      * b: normalized vector, representing the direction of the line.
248      *
249      * Should be put into Coords, when all elements possess Coords.
250      * @type Array
251      * @default [1, 0, 0, 0, 1, 1, 0, 0]
252      */
253     this.stdform = [1,0,0,0,1, 1,0,0];
254 
255     /**
256      * Quadratic form representation of circles (and conics)
257      * @type Array
258      * @default [[1,0,0],[0,1,0],[0,0,1]]
259      */
260     this.quadraticform = [[1,0,0],[0,1,0],[0,0,1]];
261 
262     /**
263      * If this is set to true, the element is updated in every update
264      * call of the board. If set to false, the element is updated only after
265      * zoom events or more generally, when the bounding box has been changed.
266      * Examples for the latter behaviour should be axes.
267      * @type Boolean
268      * @default true
269      */
270     this.needsRegularUpdate = true;
271 
272 };
273 
274 /**
275  * Initializes board, id and name which cannot be initialized properly in the constructor.
276  * @param {JXG.Board} board The board the new point is drawn on.
277  * @param {String} id Unique identifier for the point. If null or an empty string is given,
278  *  an unique id will be generated by Board
279  * @param {String} name Not necessarily unique name for the point. If null or an
280  *  empty string is given, an unique name will be generated
281  * @private
282  */
283 JXG.GeometryElement.prototype.init = function(board, id, name) {
284     this.board = board;
285     this.id = id;
286 
287     /* If name is not set or null or even undefined, generate an unique name for this object */
288     if (!JXG.exists(name)) {
289         name = this.board.generateName(this);
290     }
291     this.board.elementsByName[name] = this;
292 
293     this.name = name;
294 
295     /**
296      * The stroke color of the given geometry element.
297      * @type string
298      * @name JXG.GeometryElement#strokeColor
299      * @see #highlightStrokeColor
300      * @see #strokeWidth
301      * @see #strokeOpacity
302      * @see #highlightStrokeOpacity
303      * @default {@link JXG.Options.elements.color#strokeColor}
304      */
305     this.visProp.strokeColor = this.board.options.elements.strokeColor; //'#36393D';
306 
307     /**
308      * The stroke color of the given geometry element when the user moves the mouse over it.
309      * @type string
310      * @name JXG.GeometryElement#highlightStrokeColor
311      * @see #sstrokeColor
312      * @see #strokeWidth
313      * @see #strokeOpacity
314      * @see #highlightStrokeOpacity
315      * @default {@link JXG.Options.elements.color#highlightStrokeColor}
316      */
317     this.visProp.highlightStrokeColor = this.board.options.elements.highlightStrokeColor;
318 
319     /**
320      * The fill color of this geometry element.
321      * @type string
322      * @name JXG.GeometryElement#fillColor
323      * @see #highlightFillColor
324      * @see #fillOpacity
325      * @see #highlightFillOpacity
326      * @default {@link JXG.Options.elements.color#fillColor}
327      */
328     this.visProp.fillColor = this.board.options.elements.fillColor;
329 
330     /**
331      * The fill color of the given geometry element when the mouse is pointed over it.
332      * @type string
333      * @name JXG.GeometryElement#highlightFillColor
334      * @see #fillColor
335      * @see #fillOpacity
336      * @see #highlightFillOpacity
337      * @default {@link JXG.Options.elements.color#highlightFillColor}
338      */
339     this.visProp.highlightFillColor = this.board.options.elements.highlightFillColor;
340 
341     /**
342      * Width of the element's stroke.
343      * @type number
344      * @name JXG.GeometryElement#strokeWidth
345      * @see #strokeColor
346      * @see #highlightStrokeColor
347      * @see #strokeOpacity
348      * @see #highlightStrokeOpacity
349      * @default {@link JXG.Options.elements#strokeWidth}
350      */
351     this.visProp.strokeWidth = this.board.options.elements.strokeWidth;
352 
353     /**
354      * Width of the element's stroke when the mouse is pointed over it.
355      * @type number
356      * @name JXG.GeometryElement#highlightStrokeWidth
357      * @see #strokeColor
358      * @see #highlightStrokeColor
359      * @see #strokeOpacity
360      * @see #highlightStrokeOpacity
361      * @see #highlightFillColor
362      * @default {@#strokeWidth}
363      */
364     this.visProp.highlightStrokeWidth = this.visProp.strokeWidth;    
365     
366     /**
367      * Opacity for element's stroke color.
368      * @type number
369      * @name JXG.GeometryElement#strokeOpacity
370      * @see #strokeColor
371      * @see #highlightStrokeColor
372      * @see #strokeWidth
373      * @see #highlightStrokeOpacity
374      * @default {@link JXG.Options.elements#strokeOpacity}
375      */
376     this.visProp.strokeOpacity = this.board.options.elements.strokeOpacity;
377 
378     /**
379      * Opacity for stroke color when the object is highlighted.
380      * @type number
381      * @name JXG.GeometryElement#highlightStrokeOpacity
382      * @see #strokeColor
383      * @see #highlightStrokeColor
384      * @see #strokeWidth
385      * @see #strokeOpacity
386      * @default {@link JXG.Options.elements#highlightStrokeOpacity}
387      */
388     this.visProp.highlightStrokeOpacity = this.board.options.elements.highlightStrokeOpacity;
389 
390     /**
391      * Opacity for fill color.
392      * @type number
393      * @name JXG.GeometryElement#fillOpacity
394      * @see #fillColor
395      * @see #highlightFillColor
396      * @see #highlightFillOpacity
397      * @default {@link JXG.Options.elements.color#fillOpacity}
398      */
399     this.visProp.fillOpacity = this.board.options.elements.fillOpacity;
400 
401     /**
402      * Opacity for fill color when the object is highlighted.
403      * @type number
404      * @name JXG.GeometryElement#highlightFillOpacity
405      * @see #fillColor
406      * @see #highlightFillColor
407      * @see #fillOpacity
408      * @default {@link JXG.Options.elements.color#highlightFillOpacity}
409      */
410     this.visProp.highlightFillOpacity = this.board.options.elements.highlightFillOpacity;
411 
412     /**
413      * If true the element will be drawn in grey scale colors to visualize that it's only a draft.
414      * @type boolean
415      * @name JXG.GeometryElement#draft
416      * @default {@link JXG.Options.elements.draft#draft}
417      */
418     this.visProp.draft = this.board.options.elements.draft.draft;
419 
420     /**
421      * If false the element won't be visible on the board, otherwise it is shown.
422      * @type boolean
423      * @name JXG.GeometryElement#visible
424      * @see #hideElement
425      * @see #showElement
426      * @default true
427      */
428     this.visProp.visible = true;
429 
430     /**
431      * If true the element will get a shadow.
432      * @type boolean
433      * @name JXG.GeometryElement#shadow
434      * @default false
435      */
436     this.visProp['shadow'] = false;
437 
438     // TODO: withLabel
439 
440     // TODO: comment gradient possibilities
441     this.visProp['gradient'] = 'none';
442     this.visProp['gradientSecondColor'] = 'black';
443     this.visProp['gradientAngle'] = '270';
444     this.visProp['gradientSecondOpacity'] = this.visProp['fillOpacity'];
445     this.visProp['gradientPositionX'] = 0.5;
446     this.visProp['gradientPositionY'] = 0.5;
447 };
448 
449 /**
450  * Add an element as a child to the current element. Can be used to model dependencies between geometry elements.
451  * @param {JXG.GeometryElement} obj The dependent object.
452  */
453 JXG.GeometryElement.prototype.addChild = function (obj) {
454 	var el, el2;
455 
456     this.childElements[obj.id] = obj;
457 
458     this.addDescendants(obj);
459 
460     obj.ancestors[this.id] = this;
461     for(el in this.descendants) {
462         this.descendants[el].ancestors[this.id] = this;
463         for(el2 in this.ancestors) {
464             this.descendants[el].ancestors[this.ancestors[el2].id] = this.ancestors[el2];
465         }
466     }
467     for(el in this.ancestors) {
468         for(el2 in this.descendants) {
469             this.ancestors[el].descendants[this.descendants[el2].id] = this.descendants[el2];
470         }
471     }
472     return this;
473 };
474 
475 /**
476  * Adds the given object to the descendants list of this object and all its child objects.
477  * @param {JXG.GeometryElement} obj The element that is to be added to the descendants list.
478  * @private
479  * @return
480  */
481 JXG.GeometryElement.prototype.addDescendants = function (obj) {
482 	var el;
483 
484     this.descendants[obj.id] = obj;
485     for(el in obj.childElements) {
486         this.addDescendants(obj.childElements[el]);
487     }
488     return this;
489 };
490 
491 /**
492  * Array of strings containing the polynomials defining the element.
493  * Used for determining geometric loci the groebner way.
494  * @type Array
495  * @return An array containing polynomials describing the locus of the current object.
496  * @private
497  */
498 JXG.GeometryElement.prototype.generatePolynomial = function () {
499     return [];
500 };
501 
502 /**
503  * Animates properties for that object like stroke or fill color, opacity and maybe
504  * even more later.
505  * @param {Object} hash Object containing propiertes with target values for the animation.
506  * @param {number} time Number of milliseconds to complete the animation.
507  * @return A reference to the object
508  * @type JXG.GeometryElement
509  */
510 JXG.GeometryElement.prototype.animate = function(hash, time) {
511     var r, p,
512 	    delay = 35,
513 	    steps = Math.ceil(time/(delay * 1.0)),
514         i, self = this;
515 
516     this.animationData = {};
517 
518     var animateColor = function(startRGB, endRGB, property) {
519         var hsv1, hsv2, sh, ss, sv;
520         hsv1 = JXG.rgb2hsv(startRGB);
521         hsv2 = JXG.rgb2hsv(endRGB);
522 
523         sh = (hsv2[0]-hsv1[0])/(1.*steps);
524         ss = (hsv2[1]-hsv1[1])/(1.*steps);
525         sv = (hsv2[2]-hsv1[2])/(1.*steps);
526         self.animationData[property] = new Array(steps);
527         for(i=0; i<steps; i++) {
528             self.animationData[property][steps-i-1] = JXG.hsv2rgb(hsv1[0]+(i+1)*sh, hsv1[1]+(i+1)*ss, hsv1[2]+(i+1)*sv);
529         }
530     },
531 
532     animateFloat = function(start, end, property) {
533         start = parseFloat(start);
534         end = parseFloat(end);
535 
536         // we can't animate without having valid numbers.
537         // And parseFloat returns NaN if the given string doesn't contain
538         // a valid float number.
539         if(isNaN(start) || isNaN(end))
540             return;
541 
542         var s = (end - start)/(1.*steps);
543         self.animationData[property] = new Array(steps);
544         for(i=0; i<steps; i++) {
545             self.animationData[property][steps-i-1] = start + (i+1)*s;
546         }
547     };
548 
549     for(r in hash) {
550         p = r.toLowerCase();
551         switch(p) {
552             case 'strokecolor':
553                     animateColor(this.visProp['strokeColor'], hash[r], 'strokeColor');
554                 break;
555             case 'strokeopacity':
556                     animateFloat(this.visProp['strokeOpacity'], hash[r], 'strokeOpacity');
557                 break;
558             case 'strokewidth':
559                     animateFloat(this.visProp['strokeWidth'], hash[r], 'strokeWidth');
560                 break;
561             case 'fillcolor':
562                     animateColor(this.visProp['fillColor'], hash[r], 'fillColor');
563                 break;
564             case 'fillopacity':
565                     animateFloat(this.visProp['fillOpacity'], hash[r], 'fillOpacity');
566                 break;
567         }
568     }
569 
570     this.board.addAnimation(this);
571     return this;
572 };
573 
574 /**
575  * General update method. Should be overwritten by the element itself.
576  * Can be used sometimes to commit changes to the object.
577  */
578 JXG.GeometryElement.prototype.update = function() {
579     if(this.traced) {
580         this.cloneToBackground(true);
581     }
582     return this;
583 };
584 
585 /**
586  * Provide updateRenderer method.
587  * @private
588  */
589 JXG.GeometryElement.prototype.updateRenderer = function() {
590 };
591 
592 /**
593  * Hide the element. It will still exist but not visible on the board.
594  */
595 JXG.GeometryElement.prototype.hideElement = function() {
596     this.visProp['visible'] = false;
597     this.board.renderer.hide(this);
598     if (this.label!=null && this.hasLabel) {
599         this.label.hiddenByParent = true;
600         if(this.label.content.visProp['visible']) {
601             this.board.renderer.hide(this.label.content);
602         }
603     }
604     return this;
605 };
606 
607 /**
608  * Make the element visible.
609  */
610 JXG.GeometryElement.prototype.showElement = function() {
611     this.visProp['visible'] = true;
612     this.board.renderer.show(this);
613     if (this.label!=null && this.hasLabel && this.label.hiddenByParent) {
614         this.label.hiddenByParent = false;
615         if(this.label.content.visProp['visible']) {
616             this.board.renderer.show(this.label.content);
617         }
618     }
619     return this;
620 };
621 
622 
623 /* this list is left from the comment below. just to have a list of properties.
624 * <ul>Possible keys:</ul>
625 *<li>strokeWidth</li>
626 *<li>strokeColor</li>
627 *<li>fillColor</li>
628 *<li>highlightFillColor</li>
629 *<li>highlightStrokeColor</li>
630 *<li>strokeOpacity</li>
631 *<li>fillOpacity</li>
632 *<li>highlightFillOpacity</li>
633 *<li>highlightStrokeOpacity</li>
634 *<li>labelColor</li>
635 *<li>visible</li>
636 *<li>dash</li>
637 *<li>trace</li>
638 *<li>style <i>(Point)</i></li>
639 *<li>fixed</li>
640 *<li>frozen</li>
641 *<li>draft</li>
642 *<li>showInfobox</li>
643 *<li>straightFirst <i>(Line)</i></li>
644 *<li>straightLast <i>(Line)</i></li>
645 *<li>firstArrow <i>(Line,Arc)</li>
646 *<li>lastArrow <i>(Line,Arc)</li>
647 *<li>withTicks <i>(Line)</li>
648 *<li>fontSize</li>
649 *<li>color</li>
650 *<li>opacity</li>
651 * <li>needsRegularUpdate</li>
652 *</ul>*/
653 
654 /**
655  * Sets an arbitrary number of properties.
656  * @param % Arbitrary number of strings, containing "key:value" pairs.
657  * The possible key values are the element and class fields in this documentation.
658  * @example
659  * // Set property directly on creation of an element using the attributes object parameter
660  * var board = JXG.JSXGraph.initBoard('jxgbox', {boundingbox: [-1, 5, 5, 1]};
661  * var p = board.create('point', [2, 2], {visible: false});
662  *
663  * // Now make this point visible and fixed:
664  * p.setProperty('fixed:true', 'visible:true');
665  *
666  * // Alternatively you can use #hideElement resp. #showElement:
667  * p.hideElement();
668  */
669 JXG.GeometryElement.prototype.setProperty = function () {
670     var i, key, color, pairRaw,
671         opacity,
672         pair;
673 
674     for (i=0; i<arguments.length; i++) {
675         pairRaw = arguments[i];
676         if (typeof pairRaw == 'string') {    // pairRaw is string of the form 'key:value'
677             pair = pairRaw.split(':');
678             // trim pair[0] and pair[1]
679             pair[0] = pair[0].replace (/^\s+/, '').replace (/\s+$/, '');
680             pair[1] = pair[1].replace (/^\s+/, '').replace (/\s+$/, '');
681         } else if (!JXG.isArray(pairRaw)) {    // pairRaw consists of objects of the form {key1:value1,key2:value2,...}
682             /*
683             for (var i=0; i<Object.keys(pairRaw).length;i++) {  // Here, the prototype lib is used (Object.keys, Object.isArray)
684                 var key = Object.keys(pairRaw)[i];
685                 this.setProperty([key,pairRaw[key]]);
686             }
687             */
688             for (key in pairRaw) {
689                 this.setProperty([key,pairRaw[key]]);
690             }
691             return this;
692         } else {                             // pairRaw consists of array [key,value]
693             pair = pairRaw;
694         }
695         if (pair[1]==null) continue;
696         switch(pair[0].replace(/\s+/g, '').toLowerCase()) {   // Whitespace entfernt und in Kleinbuchstaben umgewandelt.
697             case 'needsregularupdate':
698                 this.needsRegularUpdate = !(pair[1] == 'false' || pair[1] == false);
699 
700                 this.board.renderer.setBuffering(this, this.needsRegularUpdate ? 'auto' : 'static');
701                 break;
702             case 'color':
703                 this.setProperty({strokeColor: pair[1], fillColor: pair[1]});
704                 break;
705             case 'opacity':
706                 this.setProperty({strokeOpacity: pair[1], fillOpacity: pair[1]});
707                 break;
708             case 'strokewidth':
709                 this.visProp['strokeWidth'] = pair[1];
710                 this.visProp['highlightStrokeWidth'] = pair[1];
711                 this.board.renderer.setObjectStrokeWidth(this, this.visProp['strokeWidth']);
712                 break;
713             case 'strokecolor':
714                 color = pair[1];
715                 if (color.length=='9' && color.substr(0,1)=='#') {
716                     opacity = color.substr(7,2);
717                     color = color.substr(0,7);
718                 }
719                 else {
720                     opacity = 'FF';
721                 }
722                 this.visProp['strokeColor'] = color;
723                 this.visProp['strokeOpacity'] = parseInt(opacity.toUpperCase(),16)/255;
724                 this.board.renderer.setObjectStrokeColor(this, this.visProp['strokeColor'], this.visProp['strokeOpacity']);
725                 break;
726             case 'fillcolor':
727                 color = pair[1];
728                 if (color.length=='9' && color.substr(0,1)=='#') {
729                     opacity = color.substr(7,2);
730                     color = color.substr(0,7);
731                 }
732                 else {
733                     opacity = 'FF';
734                 }
735                 this.visProp['fillColor'] = color;
736                 this.visProp['fillOpacity'] = parseInt(opacity.toUpperCase(),16)/255;
737                 this.board.renderer.setObjectFillColor(this, this.visProp['fillColor'], this.visProp['fillOpacity']);
738                 break;
739             case 'highlightstrokewidth':
740                 this.visProp['highlightStrokeWidth'] = pair[1];
741                 break;                
742             case 'highlightstrokecolor':
743                 color = pair[1];
744                 if (color.length=='9' && color.substr(0,1)=='#') {
745                     opacity = color.substr(7,2);
746                     color = color.substr(0,7);
747                 }
748                 else {
749                     opacity = 'FF';
750                 }
751                 this.visProp['highlightStrokeColor'] = color;
752                 this.visProp['highlightStrokeOpacity'] = parseInt(opacity.toUpperCase(),16)/255;
753                 break;
754             case 'highlightfillcolor':
755                 color = pair[1];
756                 if (color.length=='9' && color.substr(0,1)=='#') {
757                     opacity = color.substr(7,2);
758                     color = color.substr(0,7);
759                 }
760                 else {
761                     opacity = 'FF';
762                 }
763                 this.visProp['highlightFillColor'] = color;
764                 this.visProp['highlightFillOpacity'] = parseInt(opacity.toUpperCase(),16)/255;
765                 break;
766             case 'fillopacity':
767                 this.visProp['fillOpacity'] = pair[1];
768                 this.board.renderer.setObjectFillColor(this, this.visProp['fillColor'], this.visProp['fillOpacity']);
769                 break;
770             case 'strokeopacity':
771                 this.visProp['strokeOpacity'] = pair[1];
772                 this.board.renderer.setObjectStrokeColor(this, this.visProp['strokeColor'], this.visProp['strokeOpacity']);
773                 break;
774             case 'highlightfillopacity':
775                 this.visProp['highlightFillOpacity'] = pair[1];
776                 break;
777             case 'highlightstrokeopacity':
778                 this.visProp['highlightStrokeOpacity'] = pair[1];
779                 break;
780             case 'labelcolor':
781                 color = pair[1];
782                 if (color.length=='9' && color.substr(0,1)=='#') {
783                     opacity = color.substr(7,2);
784                     color = color.substr(0,7);
785                 }
786                 else {
787                     opacity = 'FF';
788                 }
789                 if(opacity == '00') {
790                     if (this.label!=null && this.hasLabel) {
791                         this.label.content.hideElement();
792                     }
793                 }
794                 if(this.label!=null && this.hasLabel) {
795                     this.label.color = color;
796                     this.board.renderer.setObjectStrokeColor(this.label.content, color, opacity);
797                 }
798                 if(this.type == JXG.OBJECT_TYPE_TEXT) {
799                     this.visProp['strokeColor'] = color;
800                     this.board.renderer.setObjectStrokeColor(this, this.visProp['strokeColor'], 1);
801                 }
802                 break;
803             case 'infoboxtext':
804                 if(typeof(pair[1]) == 'string') {
805                     this.infoboxText = pair[1];
806                 }
807                 else this.infoboxText = false;
808                 break;
809             case 'showinfobox':
810                 if(pair[1] == 'false' || pair[1] == false) {
811                     this.showInfobox = false;
812                 }
813                 else if(pair[1] == 'true' || pair[1] == true) {
814                     this.showInfobox = true;
815                 }
816                 break;
817             case 'visible':
818                 if(pair[1] == 'false' || pair[1] == false) {
819                     this.visProp['visible'] = false;
820                     this.hideElement();
821                 }
822                 else if(pair[1] == 'true' || pair[1] == true) {
823                     this.visProp['visible'] = true;
824                     this.showElement();
825                 }
826                 break;
827             case 'dash':
828                 this.setDash(pair[1]);
829                 break;
830             case 'trace':
831                 if(pair[1] == 'false' || pair[1] == false) {
832                     this.traced = false;
833                 }
834                 else if(pair[1] == 'true' || pair[1] == true) {
835                     this.traced = true;
836                 }
837                 break;
838             case 'style':
839                 this.setStyle(1*pair[1]);
840                 break;
841             case 'face':
842             	if(this.elementClass == JXG.OBJECT_CLASS_POINT)
843             		this.setFace(pair[1]);
844                 break;
845             case 'size':
846             	if(this.elementClass == JXG.OBJECT_CLASS_POINT) {
847             		this.visProp['size'] = 1*pair[1];
848                 	this.board.renderer.updatePoint(this);
849         		}
850                 break;
851             case 'fixed':
852                 this.fixed = ((pair[1]=='false') || (pair[1]==false)) ? false : true;
853                 break;
854             case 'frozen':
855                 this.frozen = ((pair[1]=='false') || (pair[1]==false)) ? false : true;
856                 break;
857             case 'shadow':
858                 if(pair[1] == 'false' || pair[1] == false) {
859                     this.visProp['shadow'] = false;
860                 }
861                 else if(pair[1] == 'true' || pair[1] == true) {
862                     this.visProp['shadow'] = true;
863                 }
864                 this.board.renderer.setShadow(this);
865                 break;
866             case 'gradient':
867                 this.visProp['gradient'] = pair[1];
868                 this.board.renderer.setGradient(this);
869                 break;
870             case 'gradientsecondcolor':
871                 color = pair[1];
872                 if (color.length=='9' && color.substr(0,1)=='#') {
873                     opacity = color.substr(7,2);
874                     color = color.substr(0,7);
875                 }
876                 else {
877                     opacity = 'FF';
878                 }
879                 this.visProp['gradientSecondColor'] = color;
880                 this.visProp['gradientSecondOpacity'] = parseInt(opacity.toUpperCase(),16)/255;
881                 this.board.renderer.updateGradient(this);
882                 break;
883             case 'gradientsecondopacity':
884                 this.visProp['gradientSecondOpacity'] = pair[1];
885                 this.board.renderer.updateGradient(this);
886                 break;
887             case 'draft':
888                 if(pair[1] == 'false' || pair[1] == false) {
889                     if(this.visProp['draft'] == true) {
890                         this.visProp['draft'] = false;
891                         this.board.renderer.removeDraft(this);
892                     }
893                 }
894                 else if(pair[1] == 'true' || pair[1] == true) {
895                     this.visProp['draft'] = true;
896                     this.board.renderer.setDraft(this);
897                 }
898                 break;
899             case 'straightfirst':
900                 if(pair[1] == 'false' || pair[1] == false) {
901                     this.visProp['straightFirst'] = false;
902                 }
903                 else if(pair[1] == 'true' || pair[1] == true) {
904                     this.visProp['straightFirst'] = true;
905                 }
906                 this.setStraight(this.visProp['straightFirst'], this.visProp['straightLast']);
907                 break;
908             case 'straightlast':
909                 if(pair[1] == 'false' || pair[1] == false) {
910                     this.visProp['straightLast'] = false;
911                 }
912                 else if(pair[1] == 'true' || pair[1] == true) {
913                     this.visProp['straightLast'] = true;
914                 }
915                 this.setStraight(this.visProp['straightFirst'], this.visProp['straightLast']);
916                 break;
917             case 'firstarrow':
918                 if(pair[1] == 'false' || pair[1] == false) {
919                     this.visProp['firstArrow'] = false;
920                 }
921                 else if(pair[1] == 'true' || pair[1] == true) {
922                     this.visProp['firstArrow'] = true;
923                 }
924                 this.setArrow(this.visProp['firstArrow'], this.visProp['lastArrow']);
925                 break;
926             case 'lastarrow':
927                 if(pair[1] == 'false' || pair[1] == false) {
928                     this.visProp['lastArrow'] = false;
929                 }
930                 else if(pair[1] == 'true' || pair[1] == true) {
931                     this.visProp['lastArrow'] = true;
932                 }
933                 this.setArrow(this.visProp['firstArrow'], this.visProp['lastArrow']);
934                 break;
935             case 'curvetype':
936                 this.curveType = pair[1];
937                 break;
938             case 'fontsize':
939                 this.visProp['fontSize'] = pair[1];
940                 break;
941             case 'insertticks':
942                 if(this.type == JXG.OBJECT_TYPE_TICKS) {
943                     var old = this.insertTicks;
944 
945                     this.insertTicks = !(pair[1] == 'false' || pair[1] == false);
946                     if(old != this.insertTicks) this.prepareUpdate().update().updateRenderer();
947                 }
948                 break;
949             case 'drawlabels':
950                 if(this.type == JXG.OBJECT_TYPE_TICKS) {
951                     var old = this.drawLabels;
952 
953                     this.drawLabels = !(pair[1] == 'false' || pair[1] == false);
954                     if(old != this.drawLabels) this.prepareUpdate().update().updateRenderer();
955                 }
956                 break;
957             case 'drawzero':
958                 if(this.type == JXG.OBJECT_TYPE_TICKS) {
959                     var old = this.drawZero;
960 
961                     this.drawZero = !(pair[1] == 'false' || pair[1] == false);
962                     if(old != this.drawZero) this.prepareUpdate().update().updateRenderer();
963                 }
964                 break;
965             case 'minorticks':
966                 if(this.type == JXG.OBJECT_TYPE_TICKS) {
967                     var old = this.minorTicks;
968                     if((pair[1] != null) && (pair[1] > 0))
969                         this.minorTicks = pair[1];
970                     if(old != this.minorTicks) this.prepareUpdate().update().updateRenderer();
971                 }
972                 break;
973             case 'majortickheight':
974                 if(this.type == JXG.OBJECT_TYPE_TICKS) {
975                     var old = this.majorHeight;
976                     if((pair[1] != null) && (pair[1] > 0))
977                         this.majorHeight = pair[1];
978                     if(old != this.majorHeight) this.prepareUpdate().update().updateRenderer();
979                 }
980                 break;
981             case 'minortickheight':
982                 if(this.type == JXG.OBJECT_TYPE_TICKS) {
983                     var old = this.minorHeight;
984                     if((pair[1] != null) && (pair[1] > 0))
985                         this.minorHeight = pair[1];
986                     if(old != this.minorHeight) this.prepareUpdate().update().updateRenderer();
987                 }
988                 break;
989             case 'snapwidth':
990                 if(this.type == JXG.OBJECT_TYPE_GLIDER) {
991                     this.snapWidth = pair[1];
992                 }
993                 break;
994             case 'withlabel':
995                 if(!pair[1]) {
996                     if (this.label!=null && this.hasLabel) {
997                         this.label.content.hideElement();
998                     }
999                 } 
1000                 else {
1001                     if (this.label!=null && this.hasLabel) {
1002                         if(this.visProp['visible']) {
1003                             this.label.content.showElement();
1004                         }
1005                         
1006                     }
1007                     else {
1008                         this.addLabelToElement();
1009                         if(!this.visProp['visible']) {
1010                             this.label.content.hideElement();
1011                         }                        
1012                     }
1013                 }
1014                 this.hasLabel = pair[1];
1015         }
1016     }
1017     this.board.update(this);
1018     return this;
1019 };
1020 
1021 /**
1022  * Set the dash style of an object. See {@link #dash} for a list of available dash styles.
1023  * You should use {@link #setProperty} instead of this method.
1024  * @param {number} dash Indicates the new dash style
1025  * @private
1026 */
1027 JXG.GeometryElement.prototype.setDash = function(dash) {
1028     this.visProp['dash'] = dash;
1029     this.board.renderer.setDashStyle(this,this.visProp);
1030     return this;
1031 };
1032 
1033 /**
1034  * Notify all child elements for updates.
1035  * @private
1036  */
1037 JXG.GeometryElement.prototype.prepareUpdate = function() {
1038     this.needsUpdate = true;
1039     return this; // Im Moment steigen wir nicht rekursiv hinab
1040     /* End of function  */
1041 
1042     /*
1043     var el;
1044     for(el in this.childElements) {
1045         // Wurde das Element vielleicht geloescht?
1046         if(JXG.exists(this.board.objects[el])) {
1047             // Nein, wurde es nicht, also updaten
1048             this.childElements[el].prepareUpdate();
1049         } else { //  es wurde geloescht, also aus dem Array entfernen
1050             delete(this.childElements[el]);
1051         }
1052     }
1053     */
1054 };
1055 
1056 /**
1057  * Removes the element from the construction.
1058  */
1059 JXG.GeometryElement.prototype.remove = function() {
1060     this.board.renderer.remove(this.board.renderer.getElementById(this.id));
1061     
1062     if (this.hasLabel) {
1063         this.board.renderer.remove(this.board.renderer.getElementById(this.label.content.id));
1064     }
1065     return this;
1066 };
1067 
1068 /**
1069  * Returns the coords object where a text that is bound to the element shall be drawn.
1070  * Differs in some cases from the values that getLabelAnchor returns.
1071  * @type JXG.Coords
1072  * @return JXG.Coords Place where the text shall be drawn.
1073  * @see #getLabelAnchor
1074  * @private
1075  */
1076 JXG.GeometryElement.prototype.getTextAnchor = function() {
1077     return new JXG.Coords(JXG.COORDS_BY_USER, [0,0], this.board);
1078 };
1079 
1080 /**
1081  * Returns the coords object where the label of the element shall be drawn.
1082   * Differs in some cases from the values that getTextAnchor returns.
1083  * @type JXG.Coords
1084  * @return JXG.Coords Place where the label of an element shall be drawn.
1085   * @see #getTextAnchor
1086  * @private
1087  */
1088 JXG.GeometryElement.prototype.getLabelAnchor = function() {
1089     return new JXG.Coords(JXG.COORDS_BY_USER, [0,0], this.board);
1090 };
1091 
1092 /**
1093  * TODO
1094  * Was hat das hier verloren? Styles gibts doch nur fuer Punkte oder?
1095  * Sollte das dann nicht nur in Point.js zu finden sein? --michael
1096  * @private
1097  */
1098 JXG.GeometryElement.prototype.setStyle = function(x) {
1099     return this;
1100 };
1101 
1102 /**
1103  * TODO
1104  * Was hat das hier verloren? "Straights" gibts doch nur fuer Lines oder?
1105  * Sollte das dann nicht nur in Line.js zu finden sein? --michael
1106  * @private
1107  */
1108 JXG.GeometryElement.prototype.setStraight = function(x,y) {
1109     return this;
1110 };
1111 
1112 /**
1113    * Determines whether the arc has arrows at start or end of the arc.
1114    * @param {bool} firstArrow True if there is an arrow at the start of the arc, false otherwise.
1115    * @param {bool} lastArrow True if there is an arrow at the end of the arc, false otherwise.
1116    * Is stored at visProp['firstArrow'] and visProp['lastArrow']
1117    */
1118 JXG.GeometryElement.prototype.setArrow = function (firstArrow, lastArrow) {
1119     this.visProp['firstArrow'] = firstArrow;
1120     this.visProp['lastArrow'] = lastArrow;
1121     this.prepareUpdate().update();
1122     return this;
1123 };
1124 
1125 /**
1126  * Creates a label element for this geometry element.
1127  * Doesn't add the label to the board, so it shouldn't be called itself. Use {@link #addLabelToElement} instead.
1128  * @param {boolean} withLabel true if a label shall be initialized, false otherwise.
1129  * @see #addLabelToElement
1130  * @private
1131  */
1132 JXG.GeometryElement.prototype.createLabel = function(withLabel,coords) {
1133     // WTF?!? Eine Methode namens createLabel, die optional kein label erstellt?
1134     // TODO - Spaeter genauer ansehen, grad keine Zeit
1135     var isTmpId = false;
1136     if (!JXG.exists(coords)) {
1137         coords = [10,10];
1138     }
1139     this.nameHTML = JXG.GeonextParser.replaceSup(JXG.GeonextParser.replaceSub(this.name));
1140     this.label = {};
1141     if (typeof withLabel=='undefined' || withLabel==true) {
1142         if (this.board.objects[this.id]==null) {
1143             this.board.objects[this.id] = this;
1144             isTmpId = true;
1145         }
1146         this.label.relativeCoords = coords;
1147         this.label.content = new JXG.Text(this.board, this.nameHTML, this.id,
1148             [this.label.relativeCoords[0],-this.label.relativeCoords[1]], this.id+"Label", "", null, true, this.board.options.text.defaultDisplay);
1149         if (isTmpId) delete(this.board.objects[this.id]);
1150         this.label.color = '#000000';
1151         if(!this.visProp['visible']) {
1152             this.label.hiddenByParent = true;
1153             this.label.content.visProp['visible'] = false;
1154         }
1155         this.hasLabel = true;
1156     }
1157     return this;
1158 };
1159 
1160 /**
1161  * Adds a label to the element.
1162  */
1163 JXG.GeometryElement.prototype.addLabelToElement = function() {
1164     this.createLabel(true);
1165     this.label.content.id = this.id+"Label";
1166     this.board.setId(this.label.content, 'T');
1167     this.board.renderer.drawText(this.label.content);
1168     if(!this.label.content.visProp['visible']) {
1169         this.board.renderer.hide(this.label.content);
1170     }
1171     return this;
1172 };
1173 
1174 /**
1175  * Highlights the element.
1176  */
1177 JXG.GeometryElement.prototype.highlight = function() {
1178     this.board.renderer.highlight(this);
1179     return this;
1180 };
1181 
1182 /**
1183  * Uses the "normal" properties of the element.
1184  */
1185 JXG.GeometryElement.prototype.noHighlight = function() {
1186     this.board.renderer.noHighlight(this);
1187     return this;
1188 };
1189 
1190 /**
1191  * Removes all objects generated by the trace function.
1192  */
1193 JXG.GeometryElement.prototype.clearTrace = function() {
1194     var obj;
1195     for(obj in this.traces) {
1196         this.board.renderer.remove(this.traces[obj]);
1197     }
1198     this.numTraces = 0;
1199     return this;
1200 };
1201 
1202 /**
1203  * Copy element to background. Has to be implemented in the element itself.
1204  * @private
1205  */
1206 JXG.GeometryElement.prototype.cloneToBackground = function(addToTrace) {
1207     return this;
1208 };
1209 
1210 // [c,b0,b1,a,k]
1211 /**
1212  * Normalize the element's standard form.
1213  * @private
1214  */
1215 JXG.GeometryElement.prototype.normalize = function() {
1216     this.stdform = JXG.Math.normalize(this.stdform);
1217     return this;
1218 };
1219 
1220 /**
1221  * EXPERIMENTAL. Generate JSON object code of visProp and other properties.
1222  * @type string
1223  * @private
1224  * @return JSON string containing element's properties.
1225  */
1226 JXG.GeometryElement.prototype.toJSON = function() {
1227     var json = '{"name":' + this.name;
1228     json += ', ' + '"id":' + this.id;
1229 
1230     var vis = [];
1231     for (var key in this.visProp) {
1232         if (this.visProp[key]!=null) {
1233             vis.push('"' + key + '":' + this.visProp[key]);
1234         }
1235     }
1236     json += ', "visProp":{'+vis.toString()+'}';
1237     json +='}';
1238 
1239     return json;
1240 };
1241 
1242 /**
1243  * Set the highlightStrokeColor of an element
1244  * @param {String} sColor String which determines the stroke color of an object when its highlighted.
1245  * @see JXG.GeometryElement#highlightStrokeColor
1246  */
1247 JXG.GeometryElement.prototype.highlightStrokeColor = function(sColor) {
1248     this.setProperty({highlightStrokeColor:sColor});
1249 };
1250 
1251 /**
1252  * Set the strokeColor of an element
1253  * @param {String} sColor String which determines the stroke color of an object.
1254  * @see JXG.GeometryElement#strokeColor
1255  */
1256 JXG.GeometryElement.prototype.strokeColor = function(sColor) {
1257     this.setProperty({strokeColor:sColor});
1258 };
1259 
1260 /**
1261  * Set the strokeWidth of an element
1262  * @param {Integer} width Integer which determines the stroke width of an outline.
1263  * @see JXG.GeometryElement#strokeWidth
1264  */
1265 JXG.GeometryElement.prototype.strokeWidth = function(width) {
1266     this.setProperty({strokeWidth:width});
1267 };
1268 
1269 
1270 /**
1271  * Set the fillColor of an element
1272  * @param {String} fColor String which determines the fill color of an object.
1273  * @see JXG.GeometryElement#fillColod
1274  */
1275 JXG.GeometryElement.prototype.fillColor = function(fColor) {
1276     this.setProperty({fillColor:fColor});
1277 };
1278 
1279 /**
1280  * Set the highlightFillColor of an element
1281  * @param {String} fColor String which determines the fill color of an object when its highlighted.
1282  * @see JXG.GeometryElement#highlightFillColor
1283  */
1284 JXG.GeometryElement.prototype.highlightFillColor = function(fColor) {
1285     this.setProperty({highlightFillColor:fColor});
1286 };
1287 
1288 /**
1289  * Set the labelColor of an element
1290  * @param {String} lColor String which determines the text color of an object's label.
1291  * @see JXG.GeometryElement#labelColor
1292  */
1293 JXG.GeometryElement.prototype.labelColor = function(lColor) {
1294     this.setProperty({labelColor:lColor});
1295 };
1296 
1297 /**
1298  * Set the dash type of an element
1299  * @param {Int} d Integer which determines the way of dashing an element's outline.
1300  * @see JXG.GeometryElement#dash
1301  */
1302 JXG.GeometryElement.prototype.dash = function(d) {
1303     this.setProperty({dash:d});
1304 };
1305 
1306 /**
1307  * Set the visibility of an element
1308  * @param {Boolean} v Boolean which determines whether the element is drawn.
1309  * @see JXG.GeometryElement#visible
1310  */
1311 JXG.GeometryElement.prototype.visible = function(v) {
1312     this.setProperty({visible:v});
1313 };
1314 
1315 /**
1316  * Set the shadow of an element
1317  * @param {Boolean} v Boolean which determines whether the element has a shadow.
1318  * @see JXG.GeometryElement#shadow
1319  */
1320 JXG.GeometryElement.prototype.shadow = function(s) {
1321     this.setProperty({shadow:s});
1322 };
1323 
1324 
1325 /**
1326   * Setting visPropOld is done in an none object oriented version
1327   * since otherwise there would be problems in cloneToBackground
1328   */
1329 JXG.clearVisPropOld = function(el) {
1330     el.visPropOld = {};
1331     el.visPropOld['strokeColor']= '';
1332     el.visPropOld['strokeOpacity']= '';
1333     el.visPropOld['strokeWidth']= '';
1334     el.visPropOld['fillColor']= '';
1335     el.visPropOld['fillOpacity']= '';
1336     el.visPropOld['shadow']= false;
1337     el.visPropOld['firstArrow'] = false;
1338     el.visPropOld['lastArrow'] = false;
1339 };
1340 // vim: et ts=4
1341