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