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