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