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 JXG.OBJECT_TYPE_ARC = 1;
 26 JXG.OBJECT_TYPE_ARROW = 2;
 27 JXG.OBJECT_TYPE_AXIS = 3;
 28 JXG.OBJECT_TYPE_AXISPOINT = 4;
 29 JXG.OBJECT_TYPE_TICKS = 5;
 30 JXG.OBJECT_TYPE_CIRCLE = 6;
 31 JXG.OBJECT_TYPE_CONIC = 7;
 32 JXG.OBJECT_TYPE_CURVE = 8;
 33 JXG.OBJECT_TYPE_GLIDER = 9;
 34 JXG.OBJECT_TYPE_IMAGE = 10;
 35 JXG.OBJECT_TYPE_LINE = 11;
 36 JXG.OBJECT_TYPE_POINT = 12;
 37 JXG.OBJECT_TYPE_SLIDER = 13;
 38 JXG.OBJECT_TYPE_CAS = 14;
 39 JXG.OBJECT_TYPE_GXTCAS = 15;
 40 JXG.OBJECT_TYPE_POLYGON = 16;
 41 JXG.OBJECT_TYPE_SECTOR = 17;
 42 JXG.OBJECT_TYPE_TEXT = 18;
 43 JXG.OBJECT_TYPE_ANGLE = 19;
 44 JXG.OBJECT_TYPE_INTERSECTION = 20;
 45 JXG.OBJECT_TYPE_TURTLE = 21;
 46 JXG.OBJECT_TYPE_VECTOR = 22;
 47 JXG.OBJECT_TYPE_OPROJECT = 23;
 48 
 49 
 50 JXG.OBJECT_CLASS_POINT = 1;
 51 JXG.OBJECT_CLASS_LINE = 2;
 52 JXG.OBJECT_CLASS_CIRCLE = 3;
 53 JXG.OBJECT_CLASS_CURVE = 4;
 54 JXG.OBJECT_CLASS_AREA = 5;
 55 JXG.OBJECT_CLASS_OTHER = 6;
 56 
 57 /**
 58  * Constructs a new GeometryElement object.
 59  * @class This is the basic class for geometry elements like points, circles and lines.
 60  * @constructor
 61  * of identical elements on the board. Is not yet implemented for all elements, only points, lines and circle can be traced.
 62  */
 63 JXG.GeometryElement = function (board, attributes, type, oclass) {
 64     var name, key;
 65 
 66     /**
 67      * Controls if updates are necessary
 68      * @type Boolean
 69      * @default true
 70      */
 71     this.needsUpdate = true;
 72 
 73     /**
 74      * Controls if this element can be dragged. In GEONExT only
 75      * free points and gliders can be dragged.
 76      * @type Boolean
 77      * @default false
 78      */
 79     this.isDraggable = false;
 80     
 81     /**
 82      * If element is in two dimensional real space this is true, else false.
 83      * @type Boolean
 84      * @default true
 85      */
 86     this.isReal = true;
 87 
 88     /**
 89      * Stores all dependent objects to be updated when this point is moved.
 90      * @type Object
 91      */
 92     this.childElements = {};
 93 
 94     /**
 95      * If element has a label subelement then this property will be set to true.
 96      * @type Boolean
 97      * @default false
 98      */
 99     this.hasLabel = false;
100 
101     /**
102      * True, if the element is currently highlighted.
103      * @type Boolean
104      * @default false
105      */
106     this.highlighted = false;
107 
108     /**
109      * Stores all Intersection Objects which in this moment are not real and
110      * so hide this element.
111      * @type Object
112      */
113     this.notExistingParents = {};
114 
115     /**
116      * Keeps track of all objects drawn as part of the trace of the element.
117      * @see JXG.GeometryElement#traced
118      * @see JXG.GeometryElement#clearTrace
119      * @see JXG.GeometryElement#numTraces
120      * @type Object
121      */
122     this.traces = {};
123 
124     /**
125      * Counts the number of objects drawn as part of the trace of the element.
126      * @see JXG.GeometryElement#traced
127      * @see JXG.GeometryElement#clearTrace
128      * @see JXG.GeometryElement#traces
129      * @type Number
130      */
131     this.numTraces = 0;
132 
133     /**
134      * Stores the  transformations which are applied during update in an array
135      * @type Array
136      * @see JXG.Transformation
137      */
138     this.transformations = [];
139 
140     /** TODO
141      * @type JXG.GeometryElement
142      * @default null
143      * @private
144      */
145     this.baseElement = null;
146 
147     /**
148      * Elements depending on this element are stored here.
149      * @type Object
150      */
151     this.descendants = {};
152 
153     /**
154      * Elements on which this elements depends on are stored here.
155      * @type Object
156      */
157     this.ancestors = {};
158 
159     /**
160      * Stores variables for symbolic computations
161      * @type Object
162      */
163     this.symbolic = {};
164 
165     /**
166      * The string used with {@link JXG.Board#create}
167      * @type String
168      */
169     this.elType = '';
170 
171     /**
172      * The element is saved with an explicit entry in the file (<tt>true</tt>) or implicitly
173      * via a composition.
174      * @type Boolean
175      * @default true
176      */
177     this.dump = true;
178 
179     /**
180      * Subs contains the subelements, created during the create method.
181      * @type Object
182      */
183     this.subs = {};
184 
185     /**
186      * [c,b0,b1,a,k,r,q0,q1]
187      *
188      * See
189      * A.E. Middleditch, T.W. Stacey, and S.B. Tor:
190      * "Intersection Algorithms for Lines and Circles",
191      * ACM Transactions on Graphics, Vol. 8, 1, 1989, pp 25-40.
192      *
193      * The meaning of the parameters is:
194      * Circle: points p=[p0,p1] on the circle fulfill
195      *  a<p,p> + <b,p> + c = 0
196      * For convenience we also store
197      *  r: radius
198      *  k: discriminant = sqrt(<b,b>-4ac)
199      *  q=[q0,q1] center
200      *
201      * Points have radius = 0.
202      * Lines have radius = infinity.
203      * b: normalized vector, representing the direction of the line.
204      *
205      * Should be put into Coords, when all elements possess Coords.
206      * @type Array
207      * @default [1, 0, 0, 0, 1, 1, 0, 0]
208      */
209     this.stdform = [1,0,0,0,1, 1,0,0];
210 
211     /**
212      * The methodMap determines which methods can be called from within JessieCode and under which name it
213      * can be used. The map is saved in an object, the name of a property is the name of the method used in JessieCode,
214      * the value of a property is the name of the method in JavaScript.
215      * @type Object
216      */
217     this.methodMap = {
218         setLabel: 'setLabelText',
219         getName: 'getName'
220     };
221 
222     /**
223      * Quadratic form representation of circles (and conics)
224      * @type Array
225      * @default [[1,0,0],[0,1,0],[0,0,1]]
226      */
227     this.quadraticform = [[1,0,0],[0,1,0],[0,0,1]];
228 
229     /**
230      * An associative array containing all visual properties.
231      * @type Object
232      * @default empty object
233      */
234     this.visProp = {};
235 
236     /**
237      * Stores the eventhandlers attached to the element.
238      * @type Object
239      */
240     this.eventHandlers = {};
241 
242     /**
243      * Is the mouse over this element?
244      * @type Boolean
245      * @default false
246      */
247     this.mouseover = false;
248 
249     if (arguments.length > 0) {
250         /**
251          * Reference to the board associated with the element.
252          * @type JXG.Board
253          */
254         this.board = board;
255 
256         /**
257          * Type of the element.
258          * @constant
259          * @type number
260          */
261         this.type = type;
262 
263         /**
264          * The element's class.
265          * @constant
266          * @type number
267          */
268         this.elementClass = oclass || JXG.OBJECT_CLASS_OTHER;
269 
270         /**
271          * Unique identifier for the element. Equivalent to id-attribute of renderer element.
272          * @type String
273          */
274         this.id = attributes.id;
275 
276         name = attributes.name;
277         /* If name is not set or null or even undefined, generate an unique name for this object */
278         if (!JXG.exists(name)) {
279             name = this.board.generateName(this);
280         }
281         this.board.elementsByName[name] = this;
282 
283         /**
284          * Not necessarily unique name for the element.
285          * @type String
286          * @default Name generated by {@link JXG.Board#generateName}.
287          * @see JXG.Board#generateName
288          */
289         this.name = name;
290 
291         this.needsRegularUpdate = attributes.needsregularupdate;
292 
293         JXG.clearVisPropOld(this); // create this.visPropOld and set default values
294 
295         attributes = this.resolveShortcuts(attributes);
296         for (key in attributes) {
297             this._set(key, attributes[key]);
298         }
299 
300         // TODO: draft downwards compatibility.
301         this.visProp.draft = attributes.draft && attributes.draft.draft;
302 
303         this.visProp.gradientangle = '270';
304         this.visProp.gradientsecondopacity = this.visProp.fillopacity;
305         this.visProp.gradientpositionx = 0.5;
306         this.visProp.gradientpositiony = 0.5;
307     }
308 };
309 
310 JXG.extend(JXG.GeometryElement.prototype, /** @lends JXG.GeometryElement.prototype */ {
311     /**
312      * Add an element as a child to the current element. Can be used to model dependencies between geometry elements.
313      * @param {JXG.GeometryElement} obj The dependent object.
314      */
315     addChild: function (obj) {
316         var el, el2;
317 
318         this.childElements[obj.id] = obj;
319 
320         this.addDescendants(obj);
321 
322         obj.ancestors[this.id] = this;
323         for (el in this.descendants) {
324             this.descendants[el].ancestors[this.id] = this;
325             for (el2 in this.ancestors) {
326                 this.descendants[el].ancestors[this.ancestors[el2].id] = this.ancestors[el2];
327             }
328         }
329         for (el in this.ancestors) {
330             for (el2 in this.descendants) {
331                 this.ancestors[el].descendants[this.descendants[el2].id] = this.descendants[el2];
332             }
333         }
334         return this;
335     },
336 
337     /**
338      * Adds the given object to the descendants list of this object and all its child objects.
339      * @param {JXG.GeometryElement} obj The element that is to be added to the descendants list.
340      * @private
341      * @return
342      */
343     addDescendants: function (obj) {
344         var el;
345 
346         this.descendants[obj.id] = obj;
347         for (el in obj.childElements) {
348             this.addDescendants(obj.childElements[el]);
349         }
350         return this;
351     },
352 
353     /**
354      * Counts the direct children of an object without counting labels.
355      * @private
356      * @return {number} Number of children
357      */
358     countChildren: function () {
359         var prop, s=0, d;
360 
361         d = this.childElements;
362         for (prop in d) {
363             if (d.hasOwnProperty(prop) && prop.indexOf('Label')<0) {
364                 s++;
365             }
366         }
367         return s; 
368     },
369 
370     /**
371      * Returns the elements name, Used in JessieCode.
372      * @returns {String}
373      */
374     getName: function () {
375         return this.name;
376     },
377     
378     /**
379      * Decides whether an element can be dragged. This is used in setPositionDirectly methods
380      * where all parent elements are checked if they may be dragged, too.
381      * @private
382      * @return {boolean}
383      */
384     draggable: function() {
385         return this.isDraggable 
386                && !this.visProp.fixed 
387                && !this.visProp.frozen 
388                && this.type != JXG.OBJECT_TYPE_GLIDER;    // Experimentally turned off
389                // && this.countChildren() <= 1; // Experimentally turned off
390               
391     },
392     
393     /**
394      * Array of strings containing the polynomials defining the element.
395      * Used for determining geometric loci the groebner way.
396      * @type Array
397      * @return An array containing polynomials describing the locus of the current object.
398      * @private
399      */
400     generatePolynomial: function () {
401         return [];
402     },
403 
404     /**
405      * Animates properties for that object like stroke or fill color, opacity and maybe
406      * even more later.
407      * @param {Object} hash Object containing propiertes with target values for the animation.
408      * @param {number} time Number of milliseconds to complete the animation.
409      * @param {Object} [options] Optional settings for the animation:<ul><li>callback: A function that is called as soon as the animation is finished.</li></ul>
410      * @return A reference to the object
411      * @type JXG.GeometryElement
412      */
413     animate: function (hash, time, options) {
414         options = options || {};
415         var r, p,
416             delay = 35,
417             steps = Math.ceil(time/(delay * 1.0)),
418             i, self = this;
419 
420         this.animationData = {};
421 
422         var animateColor = function (startRGB, endRGB, property) {
423                 var hsv1, hsv2, sh, ss, sv;
424                 hsv1 = JXG.rgb2hsv(startRGB);
425                 hsv2 = JXG.rgb2hsv(endRGB);
426 
427                 sh = (hsv2[0]-hsv1[0])/(1.*steps);
428                 ss = (hsv2[1]-hsv1[1])/(1.*steps);
429                 sv = (hsv2[2]-hsv1[2])/(1.*steps);
430                 self.animationData[property] = new Array(steps);
431                 for (i=0; i<steps; i++) {
432                     self.animationData[property][steps-i-1] = JXG.hsv2rgb(hsv1[0]+(i+1)*sh, hsv1[1]+(i+1)*ss, hsv1[2]+(i+1)*sv);
433                 }
434             },
435             animateFloat = function (start, end, property) {
436                 start = parseFloat(start);
437                 end = parseFloat(end);
438 
439                 // we can't animate without having valid numbers.
440                 // And parseFloat returns NaN if the given string doesn't contain
441                 // a valid float number.
442                 if (isNaN(start) || isNaN(end))
443                     return;
444 
445                 var s = (end - start)/(1.*steps);
446                 self.animationData[property] = new Array(steps);
447                 for (i=0; i<steps; i++) {
448                     self.animationData[property][steps-i-1] = start + (i+1)*s;
449                 }
450             };
451 
452         for (r in hash) {
453             p = r.toLowerCase();
454             switch(p) {
455                 case 'strokecolor':
456                 case 'fillcolor':
457                     animateColor(this.visProp[p], hash[r], p);
458                     break;
459                 case 'strokeopacity':
460                 case 'strokewidth':
461                 case 'fillopacity':
462                     animateFloat(this.visProp[p], hash[r], p);
463                     break;
464             }
465         }
466 
467         this.animationCallback = options.callback;
468         this.board.addAnimation(this);
469         return this;
470     },
471 
472     /**
473      * General update method. Should be overwritten by the element itself.
474      * Can be used sometimes to commit changes to the object.
475      */
476     update: function () {
477         if (this.visProp.trace) {
478             this.cloneToBackground(true);
479         }
480         return this;
481     },
482 
483     /**
484      * Provide updateRenderer method.
485      * @private
486      */
487     updateRenderer: function () {
488         return this;
489     },
490 
491     /**
492      * Hide the element. It will still exist but not visible on the board.
493      */
494     hideElement: function () {
495         this.visProp.visible = false;
496         this.board.renderer.hide(this);
497         if (this.label!=null && this.hasLabel) {
498             this.label.hiddenByParent = true;
499             if (this.label.content.visProp.visible) {
500                 this.board.renderer.hide(this.label.content);
501             }
502         }
503         return this;
504     },
505 
506     /**
507      * Make the element visible.
508      */
509     showElement: function () {
510         this.visProp.visible = true;
511         this.board.renderer.show(this);
512         if (this.label!=null && this.hasLabel && this.label.hiddenByParent) {
513             this.label.hiddenByParent = false;
514             if (this.label.content.visProp.visible) {
515                 this.board.renderer.show(this.label.content);
516             }
517         }
518         return this;
519     },
520 
521     /**
522      * Sets the value of property <tt>property</tt> to <tt>value</tt>.
523      * @param {String} property The property's name.
524      * @param {%} value The new value
525      * @private
526      */
527     _set: function (property, value) {
528             property = property.toLocaleLowerCase();
529 
530             // Search for entries in visProp with "color" as part of the property name
531             // and containing a RGBA string
532             if (this.visProp.hasOwnProperty(property) && property.indexOf('color') >= 0 &&
533                 JXG.isString(value) && value.length == 9 && value.charAt(0) === '#') {
534 
535                 value = JXG.rgba2rgbo(value);
536                 this.visProp[property] = value[0];
537                 this.visProp[property.replace('color', 'opacity')] = value[1]; // Previously: *=. But then, we can only decrease opacity.
538             } else {
539                 this.visProp[property] = value;
540             }
541     },
542 
543     /**
544      * Resolves property shortcuts like <tt>color</tt> and expands them, e.g. <tt>strokeColor</tt> and <tt>fillColor</tt>.
545      * Writes the expanded properties back to the given <tt>properties</tt>.
546      * @param {Object} properties
547      * @returns {Object} The given parameter with shortcuts expanded.
548      */
549     resolveShortcuts: function(properties) {
550         var key, i;
551         
552         for (key in JXG.Options.shortcuts) {
553             if (JXG.exists(properties[key])) {
554                 for (i = 0; i < JXG.Options.shortcuts[key].length; i++) {
555                     if (!JXG.exists(properties[JXG.Options.shortcuts[key][i]])) {
556                         properties[JXG.Options.shortcuts[key][i]] = properties[key];
557                     }
558                 }
559             }
560         }
561         return properties;
562     },
563 
564     /**
565      * Updates the element's label text, strips all html.
566      * @param {String} str
567      */
568     setLabelText: function (str) {
569         str = str.replace(/</g, '<').replace(/>/g, '>');
570 
571         if (this.label !== null) {
572             this.label.content.setText(str);
573         }
574         
575         return this;
576     },
577 
578     /**
579      * Sets an arbitrary number of attributes.
580      * @param {Object} attributes An object with attributes.
581      * @function
582      * @example
583      * // Set property directly on creation of an element using the attributes object parameter
584      * var board = JXG.JSXGraph.initBoard('jxgbox', {boundingbox: [-1, 5, 5, 1]};
585      * var p = board.create('point', [2, 2], {visible: false});
586      *
587      * // Now make this point visible and fixed:
588      * p.setProperty({
589      *     fixed: true,
590      *     visible: true
591      * });
592      */
593     setAttribute: JXG.shortcut(JXG.GeometryElement.prototype, 'setProperty'),
594 
595     /**
596      * Deprecated alias for {@link JXG.GeometryElement#setAttribute}.
597      * @deprecated Use {@link JXG.GeometryElement#setAttribute}.
598      */
599     setProperty: function () {
600         var i, key, value, arg, opacity, pair, properties = {};
601 
602         // normalize the user input
603         for (i = 0; i < arguments.length; i++) {
604             arg = arguments[i];
605             if (JXG.isString(arg)) {
606                 // pairRaw is string of the form 'key:value'
607                 pair = arg.split(':');
608                 properties[JXG.trim(pair[0])] = JXG.trim(pair[1]);
609             } else if (!JXG.isArray(arg)) {
610                 // pairRaw consists of objects of the form {key1:value1,key2:value2,...}
611                 JXG.extend(properties, arg);
612             } else {
613                 // pairRaw consists of array [key,value]
614                 properties[arg[0]] = arg[1];
615             }
616         }
617 
618         // handle shortcuts
619         properties = this.resolveShortcuts(properties);
620         
621         for (i in properties) {
622             key = i.replace(/\s+/g, '').toLowerCase();
623             value = properties[i];
624 
625             switch(key) {
626                 case 'name':
627                     delete this.board.elementsByName[this.name];
628                     this.name = value;
629                     this.board.elementsByName[this.name] = this;
630                     break;
631                 case 'needsregularupdate':
632                     this.needsRegularUpdate = !(value == 'false' || value == false);
633                     this.board.renderer.setBuffering(this, this.needsRegularUpdate ? 'auto' : 'static');
634                     break;
635                 case 'labelcolor':
636                     value = JXG.rgba2rgbo(value); 
637                     opacity = value[1];
638                     value = value[0];
639                     if (opacity == 0) {
640                         if (this.label!=null && this.hasLabel) {
641                             this.label.content.hideElement();
642                         }
643                     }
644                     if (this.label!=null && this.hasLabel) {
645                         this.label.color = value;
646                         this.board.renderer.setObjectStrokeColor(this.label.content, value, opacity);
647                     }
648                     if (this.type == JXG.OBJECT_TYPE_TEXT) {
649                         this.visProp.strokecolor = value;
650                         this.visProp.strokeopacity = opacity;
651                         this.board.renderer.setObjectStrokeColor(this, this.visProp.strokecolor, this.visProp.strokeopacity);
652                     }
653                     break;
654                 case 'infoboxtext':
655                     // TODO: what about functions? numbers? maybe text elements?
656                     if (typeof(value) == 'string') {
657                         this.infoboxText = value;
658                     } else {
659                         this.infoboxText = false;
660                     }
661                     break;
662                 case 'visible':
663                     if (value == 'false' || value == false) {
664                         this.visProp.visible = false;
665                         this.hideElement();
666                     } else if (value == 'true' || value == true) {
667                         this.visProp.visible = true;
668                         this.showElement();
669                     }
670                     break;
671                 case 'face':
672                     if (this.elementClass == JXG.OBJECT_CLASS_POINT) {
673                         this.visProp.face = value;
674                         this.board.renderer.changePointStyle(this);
675                     }
676                     break;
677                 case 'trace':
678                     if (value == 'false' || value == false) {
679                         this.clearTrace();
680                         this.visProp.trace = false;
681                     } else {
682                         this.visProp.trace = true;
683                     }
684                     break;
685                 case 'gradient':
686                     this.visProp.gradient = value;
687                     this.board.renderer.setGradient(this);
688                     break;
689                 case 'gradientsecondcolor':
690                     value = JXG.rgba2rgbo(value);
691                     this.visProp.gradientsecondcolor = value[0];
692                     this.visProp.gradientsecondopacity = value[1];
693                     this.board.renderer.updateGradient(this);
694                     break;
695                 case 'gradientsecondopacity':
696                     this.visProp.gradientsecondopacity = value;
697                     this.board.renderer.updateGradient(this);
698                     break;
699                 case 'withlabel':
700                     this.visProp.withlabel = value;
701                     if (!value) {
702                         if (this.label && this.label.content && this.hasLabel) {
703                             this.label.content.hideElement();
704                         }
705                     } else {
706                         if (this.label && this.label.content) {
707                             if (this.visProp.visible) {
708                                 this.label.content.showElement();
709                             }
710                         } else {
711                             this.createLabel();
712                             if (!this.visProp.visible) {
713                                 this.label.content.hideElement();
714                             }
715                         }
716                     }
717                     this.hasLabel = value;
718                     break;
719                 default:
720                     if (JXG.exists(this.visProp[key]) && (!JXG.Validator[key] || (JXG.Validator[key] && JXG.Validator[key](value)) || (JXG.Validator[key] && JXG.isFunction(value) && JXG.Validator[key](value())))) {
721                         value = value.toLowerCase && value.toLowerCase() === 'false' ? false : value;
722                         this._set(key, value);
723                     }
724                     break;
725             }
726         }
727 
728         if (this.type == JXG.OBJECT_TYPE_AXIS)
729             this.board.fullUpdate();
730 
731         this.board.update(this);
732         return this;
733     },
734 
735     /**
736      * Get the value of the property <tt>key</tt>.
737      * @param {String} key The name of the property you are looking for
738      * @returns The value of the property
739      */
740     getAttribute: JXG.shortcut(JXG.GeometryElement.prototype, 'getProperty'),
741 
742     /**
743      * Deprecated alias for {@link JXG.GeometryElement#getAttribute}.
744      * @deprecated Use {@link JXG.GeometryElement#getAttribute}.
745      */
746     getProperty: function (key) {
747         var result;
748         key = key.toLowerCase();
749 
750         switch (key) {
751             case 'needsregularupdate':
752                 result = this.needsRegularUpdate;
753                 break;
754             case 'labelcolor':
755                 result = this.label.color;
756                 break;
757             case 'infoboxtext':
758                 result = this.infoboxText;
759                 break;
760             case 'withlabel':
761                 result = this.hasLabel;
762                 break;
763             default:
764                 result = this.visProp[key];
765                 break;
766         }
767 
768         return result;
769     },
770 
771     /**
772      * Set the dash style of an object. See {@link #dash} for a list of available dash styles.
773      * You should use {@link #setProperty} instead of this method.
774      * @param {number} dash Indicates the new dash style
775      * @private
776      */
777     setDash: function (dash) {
778         this.setProperty({dash: dash});
779         return this;
780     },
781 
782     /**
783      * Notify all child elements for updates.
784      * @private
785      */
786     prepareUpdate: function () {
787         this.needsUpdate = true;
788         return this;
789     },
790 
791     /**
792      * Removes the element from the construction.  This only removes the SVG or VML node of the element and its label (if available) from
793      * the renderer, to remove the element completely you should use {@link JXG.Board#removeObject}.
794      */
795     remove: function () {
796         this.board.renderer.remove(this.board.renderer.getElementById(this.id));
797 
798         if (this.hasLabel) {
799             this.board.renderer.remove(this.board.renderer.getElementById(this.label.content.id));
800         }
801         return this;
802     },
803 
804     /**
805      * Returns the coords object where a text that is bound to the element shall be drawn.
806      * Differs in some cases from the values that getLabelAnchor returns.
807      * @returns {JXG.Coords} JXG.Coords Place where the text shall be drawn.
808      * @see JXG.GeometryElement#getLabelAnchor
809      */
810     getTextAnchor: function () {
811         return new JXG.Coords(JXG.COORDS_BY_USER, [0,0], this.board);
812     },
813 
814     /**
815      * Returns the coords object where the label of the element shall be drawn.
816      * Differs in some cases from the values that getTextAnchor returns.
817      * @returns {JXG.Coords} JXG.Coords Place where the text shall be drawn.
818      * @see JXG.GeometryElement#getTextAnchor
819      */
820     getLabelAnchor: function () {
821         return new JXG.Coords(JXG.COORDS_BY_USER, [0,0], this.board);
822     },
823 
824     /**
825      * TODO
826      * Was hat das hier verloren? "Straights" gibts doch nur fuer Lines oder?
827      * Sollte das dann nicht nur in Line.js zu finden sein? --michael
828      * @private
829      */
830     setStraight: function (x,y) {
831         return this;
832     },
833 
834     /**
835      * Determines whether the arc has arrows at start or end of the arc.
836      * @param {bool} firstArrow True if there is an arrow at the start of the arc, false otherwise.
837      * @param {bool} lastArrow True if there is an arrow at the end of the arc, false otherwise.
838      * Is stored at visProp['firstarrow'] and visProp['lastarrow']
839      */
840     setArrow: function (firstArrow, lastArrow) {
841         this.visProp.firstarrow = firstArrow;
842         this.visProp.lastarrow = lastArrow;
843         this.prepareUpdate().update();
844         return this;
845     },
846 
847     /**
848      * Creates a gradient nodes in the renderer.
849      * @see JXG.SVGRenderer#setGradient
850      * @private
851      */
852     createGradient: function() {
853         if (this.visProp.gradient === 'linear' || this.visProp.gradient === 'radial' ) {
854             this.board.renderer.setGradient(this);
855         }
856     },
857            
858     /**
859      * Creates a label element for this geometry element.
860      * @see #addLabelToElement
861      */
862     createLabel: function () {
863         var attr = {};
864         
865         attr =  JXG.deepCopy(this.visProp.label, null);
866         attr.id = this.id + 'Label';
867         attr.isLabel = true;
868         attr.visible = this.visProp.visible;
869         attr.anchor = this;
870         
871         this.nameHTML = JXG.GeonextParser.replaceSup(JXG.GeonextParser.replaceSub(this.name));
872         this.label = {};
873 
874         if (this.visProp.withlabel) {
875             this.label.relativeCoords = [0, 0];
876 
877             this.label.content = JXG.createText(this.board, 
878                 [this.label.relativeCoords[0], -this.label.relativeCoords[1], this.nameHTML], 
879                 attr);
880 
881             this.label.content.dump = false;
882             this.label.color = this.label.content.visProp.strokecolor;
883 
884             if (!this.visProp.visible) {
885                 this.label.hiddenByParent = true;
886                 this.label.content.visProp.visible = false;
887             }
888             this.hasLabel = true;
889         }
890         return this;
891     },
892 
893     /**
894      * Highlights the element.
895      */
896     highlight: function () {
897         // I know, we have the JXG.Board.highlightedObjects AND JXG.GeometryElement.highlighted and YES we need both.
898         // Board.highlightedObjects is for the internal highlighting and GeometryElement.highlighted is for user highlighting
899         // initiated by the user, e.g. through custom DOM events. We can't just pick one because this would break user
900         // defined highlighting in many ways:
901         //  * if overriding the highlight() methods the user had to handle the highlightedObjects stuff, otherwise he'd break
902         //    everything (e.g. the pie chart example http://jsxgraph.uni-bayreuth.de/wiki/index.php/Pie_chart (not exactly
903         //    user defined but for this type of chart the highlight method was overridden and not adjusted to the changes in here)
904         //    where it just kept highlighting until the radius of the pie was far beyond infinity...
905         //  * user defined highlighting would get pointless, everytime the user highlights something using .highlight(), it would get
906         //    dehighlighted immediately, because highlight puts the element into highlightedObjects and from there it gets dehighlighted
907         //    through dehighlightAll.
908         if (!this.highlighted) { // highlight only if not highlighted
909             this.highlighted = true;
910             this.board.renderer.highlight(this);
911         }
912         return this;
913     },
914 
915     /**
916      * Uses the "normal" properties of the element.
917      */
918     noHighlight: function () {
919         // see comment in JXG.GeometryElement.highlight()
920         if (this.highlighted) { // highlight only if not highlighted
921             this.highlighted = false;
922             this.board.renderer.noHighlight(this);
923         }
924         return this;
925     },
926 
927     /**
928      * Removes all objects generated by the trace function.
929      */
930     clearTrace: function () {
931         var obj;
932         for (obj in this.traces) {
933             this.board.renderer.remove(this.traces[obj]);
934         }
935         this.numTraces = 0;
936         return this;
937     },
938 
939     /**
940      * Copy the element to background. This is used for tracing elements.
941      * @returns {JXG.GeometryElement} A reference to the element
942      */
943     cloneToBackground: function () {
944         return this;
945     },
946 
947     /**
948      * Dimensions of the smallest rectangle enclosing the element.
949      * @returns {Array} The coordinates of the enclosing rectangle in a format like the bounding box in {@link JXG.Board#setBoundingBox}.
950      */
951     bounds: function () { },
952 
953     /**
954      * Normalize the element's standard form.
955      * @private
956      */
957     normalize: function () {
958         this.stdform = JXG.Math.normalize(this.stdform);
959         return this;
960     },
961 
962     /**
963      * EXPERIMENTAL. Generate JSON object code of visProp and other properties.
964      * @type string
965      * @private
966      * @ignore
967      * @return JSON string containing element's properties.
968      */
969     toJSON: function () {
970         var json = '{"name":' + this.name;
971         json += ', ' + '"id":' + this.id;
972 
973         var vis = [];
974         for (var key in this.visProp) {
975             if (this.visProp[key]!=null) {
976                 vis.push('"' + key + '":' + this.visProp[key]);
977             }
978         }
979         json += ', "visProp":{'+vis.toString()+'}';
980         json +='}';
981 
982         return json;
983     },
984 
985     /**
986      * Set the highlightStrokeColor of an element
987      * @param {String} sColor String which determines the stroke color of an object when its highlighted.
988      * @see JXG.GeometryElement#highlightStrokeColor
989      */
990     highlightStrokeColor: function (sColor) {
991         this.setProperty({highlightStrokeColor:sColor});
992         return this;
993     },
994 
995     /**
996      * Set the strokeColor of an element
997      * @param {String} sColor String which determines the stroke color of an object.
998      * @see JXG.GeometryElement#strokeColor
999      */
1000     strokeColor: function (sColor) {
1001         this.setProperty({strokeColor:sColor});
1002         return this;
1003     },
1004 
1005     /**
1006      * Set the strokeWidth of an element
1007      * @param {Number} width Integer which determines the stroke width of an outline.
1008      * @see JXG.GeometryElement#strokeWidth
1009      */
1010     strokeWidth: function (width) {
1011         this.setProperty({strokeWidth:width});
1012         return this;
1013     },
1014 
1015 
1016     /**
1017      * Set the fillColor of an element
1018      * @param {String} fColor String which determines the fill color of an object.
1019      * @see JXG.GeometryElement#fillColor
1020      */
1021     fillColor: function (fColor) {
1022         this.setProperty({fillColor:fColor});
1023         return this;
1024     },
1025 
1026     /**
1027      * Set the highlightFillColor of an element
1028      * @param {String} fColor String which determines the fill color of an object when its highlighted.
1029      * @see JXG.GeometryElement#highlightFillColor
1030      */
1031     highlightFillColor: function (fColor) {
1032         this.setProperty({highlightFillColor:fColor});
1033         return this;
1034     },
1035 
1036     /**
1037      * Set the labelColor of an element
1038      * @param {String} lColor String which determines the text color of an object's label.
1039      * @see JXG.GeometryElement#labelColor
1040      */
1041     labelColor: function (lColor) {
1042         this.setProperty({labelColor:lColor});
1043         return this;
1044     },
1045 
1046     /**
1047      * Set the dash type of an element
1048      * @param {Number} d Integer which determines the way of dashing an element's outline.
1049      * @see JXG.GeometryElement#dash
1050      */
1051     dash: function (d) {
1052         this.setProperty({dash:d});
1053         return this;
1054     },
1055 
1056     /**
1057      * Set the visibility of an element
1058      * @param {Boolean} v Boolean which determines whether the element is drawn.
1059      * @see JXG.GeometryElement#visible
1060      */
1061     visible: function (v) {
1062         this.setProperty({visible:v});
1063         return this;
1064     },
1065 
1066     /**
1067      * Set the shadow of an element
1068      * @param {Boolean} s Boolean which determines whether the element has a shadow or not.
1069      * @see JXG.GeometryElement#shadow
1070      */
1071     shadow: function (s) {
1072         this.setProperty({shadow:s});
1073         return this;
1074     },
1075 
1076     /**
1077      * The type of the element as used in {@link JXG.Board#create}.
1078      * @returns {String}
1079      */
1080     getType: function () {
1081         return this.elType;
1082     },
1083 
1084     /**
1085      * List of the element ids resp. values used as parents in {@link JXG.Board#create}.
1086      * @returns {Array}
1087      */
1088     getParents: function () {
1089         return this.parents;
1090     },
1091 
1092     /**
1093      * Retrieve a copy of the current visProp.
1094      * @returns {Object}
1095      */
1096     getAttributes: function () {
1097         var attributes = JXG.deepCopy(this.visProp),
1098             cleanThis = ['attractors', 'attractordistance', 'snatchdistance', 'traceattributes', 'frozen',
1099                 'shadow', 'gradientangle', 'gradientsecondopacity', 'gradientpositionx', 'gradientpositiony',
1100                 'needsregularupdate', 'zoom', 'layer', 'labeloffsets'],
1101             i;
1102 
1103         attributes.id = this.id;
1104         attributes.name = this.name;
1105 
1106         for (i = 0; i < cleanThis.length; i++) {
1107             delete attributes[cleanThis[i]];
1108         }
1109 
1110         return attributes;
1111     },
1112 
1113     /**
1114      * Checks whether (x,y) is near the element.
1115      * @param {Number} x Coordinate in x direction, screen coordinates.
1116      * @param {Number} y Coordinate in y direction, screen coordinates.
1117      * @returns {Boolean} True if (x,y) is near the element, False otherwise.
1118      */
1119     hasPoint: function (x, y) {
1120         return false;
1121     },
1122 
1123     /**
1124      * Triggers all event handlers of this element for a given event.
1125      * @param {String} event
1126      */
1127     triggerEventHandlers: function (event) {
1128         var i;
1129 
1130         if (JXG.isArray(this.eventHandlers[event])) {
1131             for (i = 0; i < this.eventHandlers[event].length; i++) {
1132                 this.eventHandlers[event][i].call(this);
1133             }
1134         }
1135     },
1136 
1137     /**
1138      * Register a new event handler
1139      * @param {String} event
1140      * @param {Function} handler
1141      */
1142     on: function (event, handler) {
1143         if (!JXG.isArray(this.eventHandlers[event])) {
1144             this.eventHandlers[event] = [];
1145         }
1146 
1147         this.eventHandlers[event].push(handler);
1148     },
1149 
1150     /**
1151      * Alias of {@link JXG.GeometryElement#on}.
1152      */
1153     addEvent: JXG.shortcut(JXG.GeometryElement.prototype, 'on'),
1154 
1155     /**
1156      * Unregister an event handler.
1157      * @param {String} event
1158      * @param {Function} handler The same function used in {@link JXG.GeometryElement#on}.
1159      */
1160     off: function (event, handler) {
1161         var i;
1162 
1163         if (!event || !JXG.isArray(this.eventHandlers[event])) {
1164             return;
1165         }
1166 
1167         if (handler) {
1168             i = JXG.indexOf(this.eventHandlers[event], handler);
1169             if (i > -1) {
1170                 this.eventHandlers[event].splice(i, 1);
1171             }
1172         } else {
1173             this.eventHandlers[event].length = 0;
1174         }
1175     },
1176 
1177     /**
1178      * Alias of {@link JXG.GeometryElement#off}.
1179      */
1180     removeEvent: JXG.shortcut(JXG.GeometryElement.prototype, 'off')
1181 });
1182