1 /*
  2     Copyright 2008-2022
  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, unparam: 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', 'math/statistics', 'options', 'utils/event', 'utils/color', 'utils/type'
 50 ], function (JXG, Const, Coords, Mat, Statistics, Options, 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#clearTrace
122          * @see JXG.GeometryElement#numTraces
123          * @type Object
124          */
125         this.traces = {};
126 
127         /**
128          * Counts the number of objects drawn as part of the trace of the element.
129          * @see JXG.GeometryElement#clearTrace
130          * @see JXG.GeometryElement#traces
131          * @type Number
132          */
133         this.numTraces = 0;
134 
135         /**
136          * Stores the  transformations which are applied during update in an array
137          * @type Array
138          * @see JXG.Transformation
139          */
140         this.transformations = [];
141 
142         /**
143          * @type JXG.GeometryElement
144          * @default null
145          * @private
146          */
147         this.baseElement = null;
148 
149         /**
150          * Elements depending on this element are stored here.
151          * @type Object
152          */
153         this.descendants = {};
154 
155         /**
156          * Elements on which this element depends on are stored here.
157          * @type Object
158          */
159         this.ancestors = {};
160 
161         /**
162          * Ids of elements on which this element depends directly are stored here.
163          * @type Object
164          */
165         this.parents = [];
166 
167         /**
168          * Stores variables for symbolic computations
169          * @type Object
170          */
171         this.symbolic = {};
172 
173         /**
174          * Stores the SVG (or VML) rendering node for the element. This enables low-level
175          * access to SVG nodes. The properties of such an SVG node can then be changed
176          * by calling setAttribute(). Note that there are a few elements which consist
177          * of more than one SVG nodes:
178          * <ul>
179          * <li> Elements with arrow tail or head: rendNodeTriangleStart, rendNodeTriangleEnd
180          * <li> SVG (or VML) texts: rendNodeText
181          * <li> Button: rendNodeForm, rendNodeButton, rendNodeTag
182          * <li> Checkbox: rendNodeForm, rendNodeCheckbox, rendNodeLabel, rendNodeTag
183          * <li> Input: rendNodeForm, rendNodeInput, rendNodeLabel, rendNodeTag
184          * </ul>
185          *
186          * Here is are two examples: The first example shows how to access the SVG node,
187          * the second example demonstrates how to change SVG attributes.
188          * @example
189          *     var p1 = board.create('point', [0, 0]);
190          *     console.log(p1.rendNode);
191          *     // returns the full SVG node details of the point p1, something like:
192          *     // <ellipse id='box_jxgBoard1P6' stroke='#ff0000' stroke-opacity='1' stroke-width='2px'
193          *     //   fill='#ff0000' fill-opacity='1' cx='250' cy='250' rx='4' ry='4'
194          *     //   style='position: absolute;'>
195          *     // </ellipse>
196          *
197          * @example
198          *     var s = board.create('segment', [p1, p2], {strokeWidth: 60});
199          *     s.rendNode.setAttribute('stroke-linecap', 'round');
200          *
201          * @type Object
202          */
203         this.rendNode = null;
204 
205         /**
206          * The string used with {@link JXG.Board#create}
207          * @type String
208          */
209         this.elType = '';
210 
211         /**
212          * The element is saved with an explicit entry in the file (<tt>true</tt>) or implicitly
213          * via a composition.
214          * @type Boolean
215          * @default true
216          */
217         this.dump = true;
218 
219         /**
220          * Subs contains the subelements, created during the create method.
221          * @type Object
222          */
223         this.subs = {};
224 
225         /**
226          * Inherits contains the subelements, which may have an attribute
227          * (in particular the attribute "visible") having value 'inherit'.
228          * @type Object
229          */
230         this.inherits = [];
231 
232         /**
233          * The position of this element inside the {@link JXG.Board#objectsList}.
234          * @type Number
235          * @default -1
236          * @private
237          */
238         this._pos = -1;
239 
240         /**
241          * [c, b0, b1, a, k, r, q0, q1]
242          *
243          * See
244          * A.E. Middleditch, T.W. Stacey, and S.B. Tor:
245          * "Intersection Algorithms for Lines and Circles",
246          * ACM Transactions on Graphics, Vol. 8, 1, 1989, pp 25-40.
247          *
248          * The meaning of the parameters is:
249          * Circle: points p=[p0, p1] on the circle fulfill
250          *  a<p, p> + <b, p> + c = 0
251          * For convenience we also store
252          *  r: radius
253          *  k: discriminant = sqrt(<b,b>-4ac)
254          *  q=[q0, q1] center
255          *
256          * Points have radius = 0.
257          * Lines have radius = infinity.
258          * b: normalized vector, representing the direction of the line.
259          *
260          * Should be put into Coords, when all elements possess Coords.
261          * @type Array
262          * @default [1, 0, 0, 0, 1, 1, 0, 0]
263          */
264         this.stdform = [1, 0, 0, 0, 1, 1, 0, 0];
265 
266         /**
267          * The methodMap determines which methods can be called from within JessieCode and under which name it
268          * can be used. The map is saved in an object, the name of a property is the name of the method used in JessieCode,
269          * the value of a property is the name of the method in JavaScript.
270          * @type Object
271          */
272         this.methodMap = {
273             setLabel: 'setLabel',
274             label: 'label',
275             setName: 'setName',
276             getName: 'getName',
277             addTransform: 'addTransform',
278             setProperty: 'setAttribute',
279             setAttribute: 'setAttribute',
280             addChild: 'addChild',
281             animate: 'animate',
282             on: 'on',
283             off: 'off',
284             trigger: 'trigger',
285             addTicks: 'addTicks',
286             removeTicks: 'removeTicks',
287             removeAllTicks: 'removeAllTicks'
288         };
289 
290         /**
291          * Quadratic form representation of circles (and conics)
292          * @type Array
293          * @default [[1,0,0],[0,1,0],[0,0,1]]
294          */
295         this.quadraticform = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
296 
297         /**
298          * An associative array containing all visual properties.
299          * @type Object
300          * @default empty object
301          */
302         this.visProp = {};
303 
304         /**
305          * An associative array containing visual properties which are calculated from
306          * the attribute values (i.e. visProp) and from other constraints.
307          * An example: if an intersection point does not have real coordinates,
308          * visPropCalc.visible is set to false.
309          * Additionally, the user can control visibility with the attribute "visible",
310          * even by supplying a functions as value.
311          *
312          * @type Object
313          * @default empty object
314          */
315         this.visPropCalc = {
316             visible: false
317         };
318 
319         EventEmitter.eventify(this);
320 
321         /**
322          * Is the mouse over this element?
323          * @type Boolean
324          * @default false
325          */
326         this.mouseover = false;
327 
328         /**
329          * Time stamp containing the last time this element has been dragged.
330          * @type Date
331          * @default creation time
332          */
333         this.lastDragTime = new Date();
334 
335         if (arguments.length > 0) {
336             /**
337              * Reference to the board associated with the element.
338              * @type JXG.Board
339              */
340             this.board = board;
341 
342             /**
343              * Type of the element.
344              * @constant
345              * @type Number
346              */
347             this.type = type;
348 
349             /**
350              * Original type of the element at construction time. Used for removing glider property.
351              * @constant
352              * @type Number
353              */
354             this._org_type = type;
355 
356             /**
357              * The element's class.
358              * @constant
359              * @type Number
360              */
361             this.elementClass = oclass || Const.OBJECT_CLASS_OTHER;
362 
363             /**
364              * Unique identifier for the element. Equivalent to id-attribute of renderer element.
365              * @type String
366              */
367             this.id = attributes.id;
368 
369             name = attributes.name;
370             /* If name is not set or null or even undefined, generate an unique name for this object */
371             if (!Type.exists(name)) {
372                 name = this.board.generateName(this);
373             }
374 
375             if (name !== '') {
376                 this.board.elementsByName[name] = this;
377             }
378 
379             /**
380              * Not necessarily unique name for the element.
381              * @type String
382              * @default Name generated by {@link JXG.Board#generateName}.
383              * @see JXG.Board#generateName
384              */
385             this.name = name;
386 
387             this.needsRegularUpdate = attributes.needsregularupdate;
388 
389             // create this.visPropOld and set default values
390             Type.clearVisPropOld(this);
391 
392             attr = this.resolveShortcuts(attributes);
393             for (key in attr) {
394                 if (attr.hasOwnProperty(key)) {
395                     this._set(key, attr[key]);
396                 }
397             }
398 
399             this.visProp.draft = attr.draft && attr.draft.draft;
400             //this.visProp.gradientangle = '270';
401             // this.visProp.gradientsecondopacity = Type.evaluate(this.visProp.fillopacity);
402             //this.visProp.gradientpositionx = 0.5;
403             //this.visProp.gradientpositiony = 0.5;
404         }
405     };
406 
407     JXG.extend(JXG.GeometryElement.prototype, /** @lends JXG.GeometryElement.prototype */ {
408         /**
409          * Add an element as a child to the current element. Can be used to model dependencies between geometry elements.
410          * @param {JXG.GeometryElement} obj The dependent object.
411          */
412         addChild: function (obj) {
413             var el, el2;
414 
415             this.childElements[obj.id] = obj;
416             this.addDescendants(obj);
417             obj.ancestors[this.id] = this;
418 
419             for (el in this.descendants) {
420                 if (this.descendants.hasOwnProperty(el)) {
421                     this.descendants[el].ancestors[this.id] = this;
422 
423                     for (el2 in this.ancestors) {
424                         if (this.ancestors.hasOwnProperty(el2)) {
425                             this.descendants[el].ancestors[this.ancestors[el2].id] = this.ancestors[el2];
426                         }
427                     }
428                 }
429             }
430 
431             for (el in this.ancestors) {
432                 if (this.ancestors.hasOwnProperty(el)) {
433                     for (el2 in this.descendants) {
434                         if (this.descendants.hasOwnProperty(el2)) {
435                             this.ancestors[el].descendants[this.descendants[el2].id] = this.descendants[el2];
436                         }
437                     }
438                 }
439             }
440             return this;
441         },
442 
443         /**
444          * Adds the given object to the descendants list of this object and all its child objects.
445          * @param {JXG.GeometryElement} obj The element that is to be added to the descendants list.
446          * @private
447          * @return
448          */
449         addDescendants: function (obj) {
450             var el;
451 
452             this.descendants[obj.id] = obj;
453             for (el in obj.childElements) {
454                 if (obj.childElements.hasOwnProperty(el)) {
455                     this.addDescendants(obj.childElements[el]);
456                 }
457             }
458             return this;
459         },
460 
461         /**
462          * Adds ids of elements to the array this.parents. This method needs to be called if some dependencies
463          * can not be detected automatically by JSXGraph. For example if a function graph is given by a function
464          * which referes to coordinates of a point, calling addParents() is necessary.
465          *
466          * @param {Array} parents Array of elements or ids of elements.
467          * Alternatively, one can give a list of objects as parameters.
468          * @returns {JXG.Object} reference to the object itself.
469          *
470          * @example
471          * // Movable function graph
472          * var A = board.create('point', [1, 0], {name:'A'}),
473          *     B = board.create('point', [3, 1], {name:'B'}),
474          *     f = board.create('functiongraph', function(x) {
475          *          var ax = A.X(),
476          *              ay = A.Y(),
477          *              bx = B.X(),
478          *              by = B.Y(),
479          *              a = (by - ay) / ( (bx - ax) * (bx - ax) );
480          *           return a * (x - ax) * (x - ax) + ay;
481          *      }, {fixed: false});
482          * f.addParents([A, B]);
483          * </pre><div class="jxgbox" id="JXG7c91d4d2-986c-4378-8135-24505027f251" style="width: 400px; height: 400px;"></div>
484          * <script type="text/javascript">
485          * (function() {
486          *   var board = JXG.JSXGraph.initBoard('JXG7c91d4d2-986c-4378-8135-24505027f251', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
487          *   var A = board.create('point', [1, 0], {name:'A'}),
488          *       B = board.create('point', [3, 1], {name:'B'}),
489          *       f = board.create('functiongraph', function(x) {
490          *            var ax = A.X(),
491          *                ay = A.Y(),
492          *                bx = B.X(),
493          *                by = B.Y(),
494          *                a = (by - ay) / ( (bx - ax) * (bx - ax) );
495          *             return a * (x - ax) * (x - ax) + ay;
496          *        }, {fixed: false});
497          *   f.addParents([A, B]);
498          * })();
499          * </script><pre>
500          *
501          **/
502         addParents: function (parents) {
503             var i, len, par;
504 
505             if (Type.isArray(parents)) {
506                 par = parents;
507             } else {
508                 par = arguments;
509             }
510 
511             len = par.length;
512             for (i = 0; i < len; ++i) {
513                 if (!Type.exists(par[i])) {
514                     continue;
515                 }
516                 if (Type.isId(this.board, par[i])) {
517                     this.parents.push(par[i]);
518                 } else if (Type.exists(par[i].id)) {
519                     this.parents.push(par[i].id);
520                 }
521             }
522             this.parents = Type.uniqueArray(this.parents);
523         },
524 
525         /**
526          * Sets ids of elements to the array this.parents.
527          * First, this.parents is cleared. See {@link JXG.GeometryElement#addParents}.
528          * @param {Array} parents Array of elements or ids of elements.
529          * Alternatively, one can give a list of objects as parameters.
530          * @returns {JXG.Object} reference to the object itself.
531          **/
532         setParents: function(parents) {
533             this.parents = [];
534             this.addParents(parents);
535         },
536 
537         /**
538          * Remove an element as a child from the current element.
539          * @param {JXG.GeometryElement} obj The dependent object.
540          */
541         removeChild: function (obj) {
542             //var el, el2;
543 
544             delete this.childElements[obj.id];
545             this.removeDescendants(obj);
546             delete obj.ancestors[this.id];
547 
548             /*
549              // I do not know if these addDescendants stuff has to be adapted to removeChild. A.W.
550             for (el in this.descendants) {
551                 if (this.descendants.hasOwnProperty(el)) {
552                     delete this.descendants[el].ancestors[this.id];
553 
554                     for (el2 in this.ancestors) {
555                         if (this.ancestors.hasOwnProperty(el2)) {
556                             this.descendants[el].ancestors[this.ancestors[el2].id] = this.ancestors[el2];
557                         }
558                     }
559                 }
560             }
561 
562             for (el in this.ancestors) {
563                 if (this.ancestors.hasOwnProperty(el)) {
564                     for (el2 in this.descendants) {
565                         if (this.descendants.hasOwnProperty(el2)) {
566                             this.ancestors[el].descendants[this.descendants[el2].id] = this.descendants[el2];
567                         }
568                     }
569                 }
570             }
571             */
572             return this;
573         },
574 
575         /**
576          * Removes the given object from the descendants list of this object and all its child objects.
577          * @param {JXG.GeometryElement} obj The element that is to be removed from the descendants list.
578          * @private
579          * @return
580          */
581         removeDescendants: function (obj) {
582             var el;
583 
584             delete this.descendants[obj.id];
585             for (el in obj.childElements) {
586                 if (obj.childElements.hasOwnProperty(el)) {
587                     this.removeDescendants(obj.childElements[el]);
588                 }
589             }
590             return this;
591         },
592 
593         /**
594          * Counts the direct children of an object without counting labels.
595          * @private
596          * @returns {number} Number of children
597          */
598         countChildren: function () {
599             var prop, d,
600                 s = 0;
601 
602             d = this.childElements;
603             for (prop in d) {
604                 if (d.hasOwnProperty(prop) && prop.indexOf('Label') < 0) {
605                     s++;
606                 }
607             }
608             return s;
609         },
610 
611         /**
612          * Returns the elements name. Used in JessieCode.
613          * @returns {String}
614          */
615         getName: function () {
616             return this.name;
617         },
618 
619         /**
620          * Add transformations to this element.
621          * @param {JXG.Transformation|Array} transform Either one {@link JXG.Transformation}
622          * or an array of {@link JXG.Transformation}s.
623          * @returns {JXG.GeometryElement} Reference to the element.
624          */
625         addTransform: function (transform) {
626             return this;
627         },
628 
629         /**
630          * Decides whether an element can be dragged. This is used in
631          * {@link JXG.GeometryElement#setPositionDirectly} methods
632          * where all parent elements are checked if they may be dragged, too.
633          * @private
634          * @returns {boolean}
635          */
636         draggable: function () {
637             return this.isDraggable && !Type.evaluate(this.visProp.fixed) &&
638                 /*!this.visProp.frozen &&*/ this.type !== Const.OBJECT_TYPE_GLIDER;
639         },
640 
641         /**
642          * Translates the object by <tt>(x, y)</tt>. In case the element is defined by points, the defining points are
643          * translated, e.g. a circle constructed by a center point and a point on the circle line.
644          * @param {Number} method The type of coordinates used here.
645          * Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}.
646          * @param {Array} coords array of translation vector.
647          * @returns {JXG.GeometryElement} Reference to the element object.
648          */
649         setPosition: function (method, coords) {
650             var parents = [],
651                 el, i, len, t;
652 
653             if (!Type.exists(this.parents)) {
654                 return this;
655             }
656 
657             len = this.parents.length;
658             for (i = 0; i < len; ++i) {
659                 el = this.board.select(this.parents[i]);
660                 if (Type.isPoint(el)) {
661                     if (!el.draggable()) {
662                         return this;
663                     }
664                     parents.push(el);
665                 }
666             }
667 
668             if (coords.length === 3) {
669                 coords = coords.slice(1);
670             }
671 
672             t = this.board.create('transform', coords, {type: 'translate'});
673 
674             // We distinguish two cases:
675             // 1) elements which depend on free elements, i.e. arcs and sectors
676             // 2) other elements
677             //
678             // In the first case we simply transform the parents elements
679             // In the second case we add a transform to the element.
680             //
681             len = parents.length;
682             if (len > 0) {
683                 t.applyOnce(parents);
684             } else {
685                 if (this.transformations.length > 0 &&
686                         this.transformations[this.transformations.length - 1].isNumericMatrix) {
687                     this.transformations[this.transformations.length - 1].melt(t);
688                 } else {
689                     this.addTransform(t);
690                 }
691             }
692 
693             /*
694              * If - against the default configuration - defining gliders are marked as
695              * draggable, then their position has to be updated now.
696              */
697             for (i = 0; i < len; ++i) {
698                 if (parents[i].type === Const.OBJECT_TYPE_GLIDER) {
699                     parents[i].updateGlider();
700                 }
701             }
702 
703             return this;
704         },
705 
706         /**
707          * Moves an element by the difference of two coordinates.
708          * @param {Number} method The type of coordinates used here.
709          * Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}.
710          * @param {Array} coords coordinates in screen/user units
711          * @param {Array} oldcoords previous coordinates in screen/user units
712          * @returns {JXG.GeometryElement} this element
713          */
714         setPositionDirectly: function (method, coords, oldcoords) {
715             var c = new Coords(method, coords, this.board, false),
716                 oldc = new Coords(method, oldcoords, this.board, false),
717                 dc = Statistics.subtract(c.usrCoords, oldc.usrCoords);
718 
719             this.setPosition(Const.COORDS_BY_USER, dc);
720 
721             return this;
722         },
723 
724         /**
725          * Array of strings containing the polynomials defining the element.
726          * Used for determining geometric loci the groebner way.
727          * @returns {Array} An array containing polynomials describing the locus of the current object.
728          * @public
729          */
730         generatePolynomial: function () {
731             return [];
732         },
733 
734         /**
735          * Animates properties for that object like stroke or fill color, opacity and maybe
736          * even more later.
737          * @param {Object} hash Object containing properties with target values for the animation.
738          * @param {number} time Number of milliseconds to complete the animation.
739          * @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>
740          * @returns {JXG.GeometryElement} A reference to the object
741          */
742         animate: function (hash, time, options) {
743             options = options || {};
744             var r, p, i,
745                 delay = this.board.attr.animationdelay,
746                 steps = Math.ceil(time / delay),
747                 self = this,
748 
749                 animateColor = function (startRGB, endRGB, property) {
750                     var hsv1, hsv2, sh, ss, sv;
751                     hsv1 = Color.rgb2hsv(startRGB);
752                     hsv2 = Color.rgb2hsv(endRGB);
753 
754                     sh = (hsv2[0] - hsv1[0]) / steps;
755                     ss = (hsv2[1] - hsv1[1]) / steps;
756                     sv = (hsv2[2] - hsv1[2]) / steps;
757                     self.animationData[property] = [];
758 
759                     for (i = 0; i < steps; i++) {
760                         self.animationData[property][steps - i - 1] = Color.hsv2rgb(hsv1[0] + (i + 1) * sh, hsv1[1] + (i + 1) * ss, hsv1[2] + (i + 1) * sv);
761                     }
762                 },
763 
764                 animateFloat = function (start, end, property, round) {
765                     var tmp, s;
766 
767                     start = parseFloat(start);
768                     end = parseFloat(end);
769 
770                     // we can't animate without having valid numbers.
771                     // And parseFloat returns NaN if the given string doesn't contain
772                     // a valid float number.
773                     if (isNaN(start) || isNaN(end)) {
774                         return;
775                     }
776 
777                     s = (end - start) / steps;
778                     self.animationData[property] = [];
779 
780                     for (i = 0; i < steps; i++) {
781                         tmp = start + (i + 1) * s;
782                         self.animationData[property][steps - i - 1] = round ? Math.floor(tmp) : tmp;
783                     }
784                 };
785 
786             this.animationData = {};
787 
788             for (r in hash) {
789                 if (hash.hasOwnProperty(r)) {
790                     p = r.toLowerCase();
791 
792                     switch (p) {
793                     case 'strokecolor':
794                     case 'fillcolor':
795                         animateColor(this.visProp[p], hash[r], p);
796                         break;
797                     case 'size':
798                         if (!Type.isPoint(this)) {
799                             break;
800                         }
801                         animateFloat(this.visProp[p], hash[r], p, true);
802                         break;
803                     case 'strokeopacity':
804                     case 'strokewidth':
805                     case 'fillopacity':
806                         animateFloat(this.visProp[p], hash[r], p, false);
807                         break;
808                     }
809                 }
810             }
811 
812             this.animationCallback = options.callback;
813             this.board.addAnimation(this);
814             return this;
815         },
816 
817         /**
818          * General update method. Should be overwritten by the element itself.
819          * Can be used sometimes to commit changes to the object.
820          * @return {JXG.GeometryElement} Reference to the element
821          */
822         update: function () {
823             if (Type.evaluate(this.visProp.trace)) {
824                 this.cloneToBackground();
825             }
826             return this;
827         },
828 
829         /**
830          * Provide updateRenderer method.
831          * @return {JXG.GeometryElement} Reference to the element
832          * @private
833          */
834         updateRenderer: function () {
835             return this;
836         },
837 
838         /**
839          * Run through the full update chain of an element.
840          * @param  {Boolean} visible Set visibility in case the elements attribute value is 'inherit'. null is allowed.
841          * @return {JXG.GeometryElement} Reference to the element
842          * @private
843          */
844         fullUpdate: function(visible) {
845             return this.prepareUpdate()
846                 .update()
847                 .updateVisibility(visible)
848                 .updateRenderer();
849         },
850 
851         /**
852          * Show the element or hide it. If hidden, it will still exist but not be
853          * visible on the board.
854          * @param  {Boolean} val true: show the element, false: hide the element
855          * @return {JXG.GeometryElement} Reference to the element
856          * @private
857          */
858         setDisplayRendNode: function(val) {
859             var i, len, s, len_s, obj;
860 
861             if (val === undefined) {
862                 val = this.visPropCalc.visible;
863             }
864 
865             if (val === this.visPropOld.visible) {
866                 return this;
867             }
868 
869             // Set display of the element itself
870             this.board.renderer.display(this, val);
871 
872             // Set the visibility of elements which inherit the attribute 'visible'
873             len = this.inherits.length;
874             for (s = 0; s < len; s++) {
875                 obj = this.inherits[s];
876                 if (Type.isArray(obj)) {
877                     len_s = obj.length;
878                     for (i = 0; i < len_s; i++) {
879                         if (Type.exists(obj[i]) && Type.exists(obj[i].rendNode) &&
880                             Type.evaluate(obj[i].visProp.visible) === 'inherit') {
881                             obj[i].setDisplayRendNode(val);
882                         }
883                     }
884                 } else {
885                     if (Type.exists(obj) && Type.exists(obj.rendNode) &&
886                         Type.evaluate(obj.visProp.visible) === 'inherit') {
887                             obj.setDisplayRendNode(val);
888                     }
889                 }
890             }
891 
892             // Set the visibility of the label if it inherits the attribute 'visible'
893             if (this.hasLabel && Type.exists(this.label) && Type.exists(this.label.rendNode)) {
894                 if (Type.evaluate(this.label.visProp.visible) === 'inherit') {
895                     this.label.setDisplayRendNode(val);
896                 }
897             }
898 
899             return this;
900         },
901 
902         /**
903          * Hide the element. It will still exist but not be visible on the board.
904          * Alias for "element.setAttribute({visible: false});"
905          * @return {JXG.GeometryElement} Reference to the element
906          */
907         hide: function () {
908             this.setAttribute({visible: false});
909             return this;
910         },
911 
912         /**
913          * Hide the element. It will still exist but not be visible on the board.
914          * Alias for {@link JXG.GeometryElement#hide}
915          * @returns {JXG.GeometryElement} Reference to the element
916          */
917         hideElement: function() {
918             this.hide();
919             return this;
920         },
921 
922         /**
923          * Make the element visible.
924          * Alias for "element.setAttribute({visible: true});"
925          * @return {JXG.GeometryElement} Reference to the element
926          */
927         show: function () {
928             this.setAttribute({visible: true});
929             return this;
930         },
931 
932         /**
933          * Make the element visible.
934          * Alias for {@link JXG.GeometryElement#show}
935          * @returns {JXG.GeometryElement} Reference to the element
936          */
937         showElement: function() {
938             this.show();
939             return this;
940         },
941 
942         /**
943          * Set the visibility of an element. The visibility is influenced by
944          * (listed in ascending priority):
945          * <ol>
946          * <li> The value of the element's attribute 'visible'
947          * <li> The visibility of a parent element. (Example: label)
948          * This overrules the value of the element's attribute value only if
949          * this attribute value of the element is 'inherit'.
950          * <li> being inside of the canvas
951          * </ol>
952          * <p>
953          * This method is called three times for most elements:
954          * <ol>
955          * <li> between {@link JXG.GeometryElement#update}
956          * and {@link JXG.GeometryElement#updateRenderer}. In case the value is 'inherit', nothing is done.
957          * <li> Recursively, called by itself for child elements. Here, 'inherit' is overruled by the parent's value.
958          * <li> In {@link JXG.GeometryElement#updateRenderer}, if the element is outside of the canvas.
959          * </ol>
960          *
961          * @param  {Boolean} parent_val Visibility of the parent element.
962          * @return {JXG.GeometryElement} Reference to the element.
963          * @private
964          */
965         updateVisibility: function(parent_val) {
966             var i, len, s, len_s, obj, val;
967 
968             if (this.needsUpdate) {
969                 // Handle the element
970                 if (parent_val !== undefined) {
971                     this.visPropCalc.visible = parent_val;
972                 } else {
973                     val = Type.evaluate(this.visProp.visible);
974 
975                     // infobox uses hiddenByParent
976                     if (Type.exists(this.hiddenByParent) && this.hiddenByParent) {
977                         val = false;
978                     }
979                     if (val !== 'inherit') {
980                         this.visPropCalc.visible = val;
981                     }
982                 }
983 
984                 // Handle elements which inherit the visibility
985                 len = this.inherits.length;
986                 for (s = 0; s < len; s++) {
987                     obj = this.inherits[s];
988                     if (Type.isArray(obj)) {
989                         len_s = obj.length;
990                         for (i = 0; i < len_s; i++) {
991                             if (Type.exists(obj[i]) /*&& Type.exists(obj[i].rendNode)*/ &&
992                                 Type.evaluate(obj[i].visProp.visible) === 'inherit') {
993                                 obj[i].prepareUpdate().updateVisibility(this.visPropCalc.visible);
994                             }
995                         }
996                     } else {
997                         if (Type.exists(obj) /*&& Type.exists(obj.rendNode)*/ &&
998                             Type.evaluate(obj.visProp.visible) === 'inherit') {
999                             obj.prepareUpdate().updateVisibility(this.visPropCalc.visible);
1000                         }
1001                     }
1002                 }
1003 
1004                 // Handle the label if it inherits the visibility
1005                 if (Type.exists(this.label) && Type.exists(this.label.visProp) &&
1006                     Type.evaluate(this.label.visProp.visible)) {
1007                     this.label.prepareUpdate().updateVisibility(this.visPropCalc.visible);
1008                 }
1009             }
1010             return this;
1011         },
1012 
1013         /**
1014          * Sets the value of property <tt>property</tt> to <tt>value</tt>.
1015          * @param {String} property The property's name.
1016          * @param value The new value
1017          * @private
1018          */
1019         _set: function (property, value) {
1020             var el;
1021 
1022             property = property.toLocaleLowerCase();
1023 
1024             // Search for entries in visProp with "color" as part of the property name
1025             // and containing a RGBA string
1026             if (this.visProp.hasOwnProperty(property) &&
1027                   property.indexOf('color') >= 0 &&
1028                   Type.isString(value) &&
1029                   value.length === 9 &&
1030                   value.charAt(0) === '#') {
1031 
1032                 value = Color.rgba2rgbo(value);
1033                 this.visProp[property] = value[0];
1034                 // Previously: *=. But then, we can only decrease opacity.
1035                 this.visProp[property.replace('color', 'opacity')] = value[1];
1036             } else {
1037                 if (value !== null &&Type.isObject(value) &&
1038                     !Type.exists(value.id) &&
1039                     !Type.exists(value.name)) {
1040                     // value is of type {prop: val, prop: val,...}
1041                     // Convert these attributes to lowercase, too
1042                     this.visProp[property] = {};
1043                     for (el in value) {
1044                         if (value.hasOwnProperty(el)) {
1045                             this.visProp[property][el.toLocaleLowerCase()] = value[el];
1046                         }
1047                     }
1048                 } else {
1049                     this.visProp[property] = value;
1050                 }
1051             }
1052         },
1053 
1054         /**
1055          * Resolves attribute shortcuts like <tt>color</tt> and expands them, e.g. <tt>strokeColor</tt> and <tt>fillColor</tt>.
1056          * Writes the expanded attributes back to the given <tt>attributes</tt>.
1057          * @param {Object} attributes object
1058          * @returns {Object} The given attributes object with shortcuts expanded.
1059          * @private
1060          */
1061         resolveShortcuts: function (attributes) {
1062             var key, i,
1063                 j,
1064                 subattr = ['traceattributes', 'traceAttributes'];
1065 
1066             for (key in Options.shortcuts) {
1067                 if (Options.shortcuts.hasOwnProperty(key)) {
1068 
1069                         if (Type.exists(attributes[key])) {
1070                             for (i = 0; i < Options.shortcuts[key].length; i++) {
1071                                 if (!Type.exists(attributes[Options.shortcuts[key][i]])) {
1072                                     attributes[Options.shortcuts[key][i]] = attributes[key];
1073                                 }
1074                             }
1075                         }
1076                         for (j = 0; j < subattr.length; j++) {
1077                             if (Type.isObject(attributes[subattr[j]])) {
1078                                     attributes[subattr[j]] = this.resolveShortcuts(attributes[subattr[j]]);
1079                                 }
1080                             }
1081                         }
1082             }
1083             return attributes;
1084         },
1085 
1086         /**
1087          * Sets a label and its text
1088          * If label doesn't exist, it creates one
1089          * @param {String} str
1090          */
1091         setLabel: function (str) {
1092             if (!this.hasLabel) {
1093                 this.setAttribute({'withlabel': true});
1094             }
1095             this.setLabelText(str);
1096         },
1097 
1098         /**
1099          * Updates the element's label text, strips all html.
1100          * @param {String} str
1101          */
1102         setLabelText: function (str) {
1103 
1104             if (Type.exists(this.label)) {
1105                 str = str.replace(/</g, '<').replace(/>/g, '>');
1106                 this.label.setText(str);
1107             }
1108 
1109             return this;
1110         },
1111 
1112         /**
1113          * Updates the element's label text and the element's attribute "name", strips all html.
1114          * @param {String} str
1115          */
1116         setName: function (str) {
1117             str = str.replace(/</g, '<').replace(/>/g, '>');
1118             if (this.elType !== 'slider') {
1119                 this.setLabelText(str);
1120             }
1121             this.setAttribute({name: str});
1122         },
1123 
1124         /**
1125          * Deprecated alias for {@link JXG.GeometryElement#setAttribute}.
1126          * @deprecated Use {@link JXG.GeometryElement#setAttribute}.
1127          */
1128         setProperty: function () {
1129             JXG.deprecated('setProperty()', 'setAttribute()');
1130             this.setAttribute.apply(this, arguments);
1131         },
1132 
1133         /**
1134          * Sets an arbitrary number of attributes. This method has one or more
1135          * parameters of the following types:
1136          * <ul>
1137          * <li> object: {key1:value1,key2:value2,...}
1138          * <li> string: "key1:value"
1139          * <li> array: [key, value]
1140          * </ul>
1141          * @param {Object} attributes An object with attributes.
1142          * @returns {JXG.GeometryElement} A reference to the element.
1143          *
1144          * @function
1145          * @example
1146          * // Set property directly on creation of an element using the attributes object parameter
1147          * var board = JXG.JSXGraph.initBoard('jxgbox', {boundingbox: [-1, 5, 5, 1]};
1148          * var p = board.create('point', [2, 2], {visible: false});
1149          *
1150          * // Now make this point visible and fixed:
1151          * p.setAttribute({
1152          *     fixed: true,
1153          *     visible: true
1154          * });
1155          */
1156         setAttribute: function (attributes) {
1157             var i, j, le, key, value, arg, opacity, pair, oldvalue,
1158                 properties = {};
1159 
1160             // Normalize the user input
1161             for (i = 0; i < arguments.length; i++) {
1162                 arg = arguments[i];
1163                 if (Type.isString(arg)) {
1164                     // pairRaw is string of the form 'key:value'
1165                     pair = arg.split(':');
1166                     properties[Type.trim(pair[0])] = Type.trim(pair[1]);
1167                 } else if (!Type.isArray(arg)) {
1168                     // pairRaw consists of objects of the form {key1:value1,key2:value2,...}
1169                     JXG.extend(properties, arg);
1170                 } else {
1171                     // pairRaw consists of array [key,value]
1172                     properties[arg[0]] = arg[1];
1173                 }
1174             }
1175 
1176             // Handle shortcuts
1177             properties = this.resolveShortcuts(properties);
1178 
1179             for (i in properties) {
1180                 if (properties.hasOwnProperty(i)) {
1181                     key = i.replace(/\s+/g, '').toLowerCase();
1182                     value = properties[i];
1183 
1184                     // This handles the subobjects, if the key:value pairs are contained in an object.
1185                     // Example:
1186                     // ticks.setAttribute({
1187                     //      strokeColor: 'blue',
1188                     //      label: {
1189                     //          visible: false
1190                     //      }
1191                     // })
1192                     // Now, only the supplied label attributes are overwritten.
1193                     // Otherwise, the value of label would be {visible:false} only.
1194                     if (Type.isObject(value) &&
1195                         Type.exists(this.visProp[key])) {
1196 
1197                         this.visProp[key] = Type.merge(this.visProp[key], value);
1198 
1199                         // First, handle the special case
1200                         // ticks.setAttribute({label: {anchorX: "right", ..., visible: true});
1201                         if (this.type === Const.OBJECT_TYPE_TICKS && Type.exists(this.labels)) {
1202                             le = this.labels.length;
1203                             for (j = 0; j < le; j++) {
1204                                 this.labels[j].setAttribute(value);
1205                             }
1206                         } else if (Type.exists(this[key])) {
1207                             if (Type.isArray(this[key])) {
1208                                 for (j = 0; j < this[key].length; j++) {
1209                                     this[key][j].setAttribute(value);
1210                                 }
1211                             } else {
1212                                 this[key].setAttribute(value);
1213                             }
1214                         }
1215                         continue;
1216                     }
1217 
1218                     oldvalue = this.visProp[key];
1219                     switch (key) {
1220                     case 'name':
1221                         oldvalue = this.name;
1222                         delete this.board.elementsByName[this.name];
1223                         this.name = value;
1224                         this.board.elementsByName[this.name] = this;
1225                         break;
1226                     case 'needsregularupdate':
1227                         this.needsRegularUpdate = !(value === 'false' || value === false);
1228                         this.board.renderer.setBuffering(this, this.needsRegularUpdate ? 'auto' : 'static');
1229                         break;
1230                     case 'labelcolor':
1231                         value = Color.rgba2rgbo(value);
1232                         opacity = value[1];
1233                         value = value[0];
1234                         if (opacity === 0) {
1235                             if (Type.exists(this.label) && this.hasLabel) {
1236                                 this.label.hideElement();
1237                             }
1238                         }
1239                         if (Type.exists(this.label) && this.hasLabel) {
1240                             this.label.visProp.strokecolor = value;
1241                             this.board.renderer.setObjectStrokeColor(this.label,
1242                                 value, opacity);
1243                         }
1244                         if (this.elementClass === Const.OBJECT_CLASS_TEXT) {
1245                             this.visProp.strokecolor = value;
1246                             this.visProp.strokeopacity = opacity;
1247                             this.board.renderer.setObjectStrokeColor(this,
1248                                 value, opacity);
1249                         }
1250                         break;
1251                     case 'infoboxtext':
1252                         if (Type.isString(value)) {
1253                             this.infoboxText = value;
1254                         } else {
1255                             this.infoboxText = false;
1256                         }
1257                         break;
1258                     case 'visible':
1259                         if (value === 'false') {
1260                             this.visProp.visible = false;
1261                         } else if (value === 'true') {
1262                             this.visProp.visible = true;
1263                         } else {
1264                             this.visProp.visible = value;
1265                         }
1266 
1267                         this.setDisplayRendNode(Type.evaluate(this.visProp.visible));
1268                         if (Type.evaluate(this.visProp.visible) && Type.exists(this.updateSize)) {
1269                             this.updateSize();
1270                         }
1271 
1272                         break;
1273                     case 'face':
1274                         if (Type.isPoint(this)) {
1275                             this.visProp.face = value;
1276                             this.board.renderer.changePointStyle(this);
1277                         }
1278                         break;
1279                     case 'trace':
1280                         if (value === 'false' || value === false) {
1281                             this.clearTrace();
1282                             this.visProp.trace = false;
1283                         } else if (value === 'pause') {
1284                                 this.visProp.trace = false;
1285                         } else {
1286                             this.visProp.trace = true;
1287                         }
1288                         break;
1289                     case 'gradient':
1290                         this.visProp.gradient = value;
1291                         this.board.renderer.setGradient(this);
1292                         break;
1293                     case 'gradientsecondcolor':
1294                         value = Color.rgba2rgbo(value);
1295                         this.visProp.gradientsecondcolor = value[0];
1296                         this.visProp.gradientsecondopacity = value[1];
1297                         this.board.renderer.updateGradient(this);
1298                         break;
1299                     case 'gradientsecondopacity':
1300                         this.visProp.gradientsecondopacity = value;
1301                         this.board.renderer.updateGradient(this);
1302                         break;
1303                     case 'withlabel':
1304                         this.visProp.withlabel = value;
1305                         if (!Type.evaluate(value)) {
1306                             if (this.label && this.hasLabel) {
1307                                 //this.label.hideElement();
1308                                 this.label.setAttribute({visible: false});
1309                             }
1310                         } else {
1311                             if (!this.label) {
1312                                 this.createLabel();
1313                             }
1314                             //this.label.showElement();
1315                             this.label.setAttribute({visible: 'inherit'});
1316                             //this.label.setDisplayRendNode(Type.evaluate(this.visProp.visible));
1317                         }
1318                         this.hasLabel = value;
1319                         break;
1320                     case 'radius':
1321                         if (this.type === Const.OBJECT_TYPE_ANGLE || this.type === Const.OBJECT_TYPE_SECTOR) {
1322                             this.setRadius(value);
1323                         }
1324                         break;
1325                     case 'rotate':
1326                         if ((this.elementClass === Const.OBJECT_CLASS_TEXT &&
1327                              Type.evaluate(this.visProp.display) === 'internal') ||
1328                             this.type === Const.OBJECT_TYPE_IMAGE) {
1329                             this.addRotation(value);
1330                         }
1331                         break;
1332                     case 'ticksdistance':
1333                         if (this.type === Const.OBJECT_TYPE_TICKS && Type.isNumber(value)) {
1334                             this.ticksFunction = this.makeTicksFunction(value);
1335                         }
1336                         break;
1337                     case 'generatelabelvalue':
1338                         if (this.type === Const.OBJECT_TYPE_TICKS && Type.isFunction(value)) {
1339                             this.generateLabelValue = value;
1340                         }
1341                         break;
1342                     case 'onpolygon':
1343                         if (this.type === Const.OBJECT_TYPE_GLIDER) {
1344                             this.onPolygon = !!value;
1345                         }
1346                         break;
1347                     case 'disabled':
1348                         // button, checkbox, input. Is not available on initial call.
1349                         if (Type.exists(this.rendNodeTag)) {
1350                             this.rendNodeTag.disabled = !!value;
1351                         }
1352                         break;
1353                     case 'checked':
1354                         // checkbox Is not available on initial call.
1355                         if (Type.exists(this.rendNodeTag)) {
1356                             this.rendNodeCheckbox.checked = !!value;
1357                         }
1358                         break;
1359                     case 'maxlength':
1360                         // input. Is not available on initial call.
1361                         if (Type.exists(this.rendNodeTag)) {
1362                             this.rendNodeTag.maxlength = !!value;
1363                         }
1364                         break;
1365                     case 'layer':
1366                         this.board.renderer.setLayer(this, Type.evaluate(value));
1367                         this._set(key, value);
1368                         break;
1369                     case 'tabindex':
1370                         if (Type.exists(this.rendNode)) {
1371                             this.rendNode.setAttribute('tabindex', value);
1372                             this._set(key, value);
1373                         }
1374                         break;
1375                     default:
1376                         if (Type.exists(this.visProp[key]) &&
1377                             (!JXG.Validator[key] ||
1378                                 (JXG.Validator[key] && JXG.Validator[key](value)) ||
1379                                 (JXG.Validator[key] && Type.isFunction(value) && JXG.Validator[key](value()))
1380                             )
1381                         ) {
1382                             value = (value.toLowerCase && value.toLowerCase() === 'false') ? false : value;
1383                             this._set(key, value);
1384                         }
1385                         break;
1386                     }
1387                     this.triggerEventHandlers(['attribute:' + key], [oldvalue, value, this]);
1388                 }
1389             }
1390 
1391             this.triggerEventHandlers(['attribute'], [properties, this]);
1392 
1393             if (!Type.evaluate(this.visProp.needsregularupdate)) {
1394                 this.board.fullUpdate();
1395             } else {
1396                 this.board.update(this);
1397             }
1398 
1399             return this;
1400         },
1401 
1402         /**
1403          * Deprecated alias for {@link JXG.GeometryElement#getAttribute}.
1404          * @deprecated Use {@link JXG.GeometryElement#getAttribute}.
1405          */
1406         getProperty: function () {
1407             JXG.deprecated('getProperty()', 'getAttribute()');
1408             this.getProperty.apply(this, arguments);
1409         },
1410 
1411         /**
1412          * Get the value of the property <tt>key</tt>.
1413          * @param {String} key The name of the property you are looking for
1414          * @returns The value of the property
1415          */
1416         getAttribute: function (key) {
1417             var result;
1418             key = key.toLowerCase();
1419 
1420             switch (key) {
1421             case 'needsregularupdate':
1422                 result = this.needsRegularUpdate;
1423                 break;
1424             case 'labelcolor':
1425                 result = this.label.visProp.strokecolor;
1426                 break;
1427             case 'infoboxtext':
1428                 result = this.infoboxText;
1429                 break;
1430             case 'withlabel':
1431                 result = this.hasLabel;
1432                 break;
1433             default:
1434                 result = this.visProp[key];
1435                 break;
1436             }
1437 
1438             return result;
1439         },
1440 
1441         /**
1442          * Set the dash style of an object. See {@link JXG.GeometryElement#dash}
1443          * for a list of available dash styles.
1444          * You should use {@link JXG.GeometryElement#setAttribute} instead of this method.
1445          *
1446          * @param {number} dash Indicates the new dash style
1447          * @private
1448          */
1449         setDash: function (dash) {
1450             this.setAttribute({dash: dash});
1451             return this;
1452         },
1453 
1454         /**
1455          * Notify all child elements for updates.
1456          * @private
1457          */
1458         prepareUpdate: function () {
1459             this.needsUpdate = true;
1460             return this;
1461         },
1462 
1463         /**
1464          * Removes the element from the construction.  This only removes the SVG or VML node of the element and its label (if available) from
1465          * the renderer, to remove the element completely you should use {@link JXG.Board#removeObject}.
1466          */
1467         remove: function () {
1468             this.board.renderer.remove(this.board.renderer.getElementById(this.id));
1469 
1470             if (this.hasLabel) {
1471                 this.board.renderer.remove(this.board.renderer.getElementById(this.label.id));
1472             }
1473             return this;
1474         },
1475 
1476         /**
1477          * Returns the coords object where a text that is bound to the element shall be drawn.
1478          * Differs in some cases from the values that getLabelAnchor returns.
1479          * @returns {JXG.Coords} JXG.Coords Place where the text shall be drawn.
1480          * @see JXG.GeometryElement#getLabelAnchor
1481          */
1482         getTextAnchor: function () {
1483             return new Coords(Const.COORDS_BY_USER, [0, 0], this.board);
1484         },
1485 
1486         /**
1487          * Returns the coords object where the label of the element shall be drawn.
1488          * Differs in some cases from the values that getTextAnchor returns.
1489          * @returns {JXG.Coords} JXG.Coords Place where the text shall be drawn.
1490          * @see JXG.GeometryElement#getTextAnchor
1491          */
1492         getLabelAnchor: function () {
1493             return new Coords(Const.COORDS_BY_USER, [0, 0], this.board);
1494         },
1495 
1496         /**
1497          * Determines whether the element has arrows at start or end of the arc.
1498          * If it is set to be a "typical" vector, ie lastArrow == true,
1499          * then the element.type is set to VECTOR.
1500          * @param {Boolean} firstArrow True if there is an arrow at the start of the arc, false otherwise.
1501          * @param {Boolean} lastArrow True if there is an arrow at the end of the arc, false otherwise.
1502          */
1503         setArrow: function (firstArrow, lastArrow) {
1504             this.visProp.firstarrow = firstArrow;
1505             this.visProp.lastarrow = lastArrow;
1506             if (lastArrow) {
1507                 this.type = Const.OBJECT_TYPE_VECTOR;
1508                 this.elType = 'arrow';
1509             }
1510 
1511             this.prepareUpdate().update().updateVisibility().updateRenderer();
1512             return this;
1513         },
1514 
1515         /**
1516          * Creates a gradient nodes in the renderer.
1517          * @see JXG.SVGRenderer#setGradient
1518          * @private
1519          */
1520         createGradient: function () {
1521             var ev_g = Type.evaluate(this.visProp.gradient);
1522             if (ev_g === 'linear' || ev_g === 'radial') {
1523                 this.board.renderer.setGradient(this);
1524             }
1525         },
1526 
1527          /**
1528          * Creates a label element for this geometry element.
1529          * @see #addLabelToElement
1530          */
1531         createLabel: function () {
1532             var attr,
1533                 that = this;
1534 
1535             // this is a dirty hack to resolve the text-dependency. If there is no text element available,
1536             // just don't create a label. This method is usually not called by a user, so we won't throw
1537             // an exception here and simply output a warning via JXG.debug.
1538             if (JXG.elements.text) {
1539                 attr =  Type.deepCopy(this.visProp.label, null);
1540                 attr.id = this.id + 'Label';
1541                 attr.isLabel = true;
1542                 attr.anchor = this;
1543                 attr.priv = this.visProp.priv;
1544 
1545                 if (this.visProp.withlabel) {
1546                     this.label = JXG.elements.text(this.board, [0, 0, function () {
1547                         if (Type.isFunction(that.name)) {
1548                             return that.name();
1549                         }
1550                         return that.name;
1551                     }], attr);
1552                     this.label.needsUpdate = true;
1553                     this.label.dump = false;
1554                     this.label.fullUpdate();
1555 
1556                     this.hasLabel = true;
1557                 }
1558             } else {
1559                 JXG.debug('JSXGraph: Can\'t create label: text element is not available. Make sure you include base/text');
1560             }
1561 
1562             return this;
1563         },
1564 
1565         /**
1566          * Highlights the element.
1567          * @param {Boolean} [force=false] Force the highlighting
1568          * @returns {JXG.Board}
1569          */
1570         highlight: function (force) {
1571             force = Type.def(force, false);
1572             // I know, we have the JXG.Board.highlightedObjects AND JXG.GeometryElement.highlighted and YES we need both.
1573             // Board.highlightedObjects is for the internal highlighting and GeometryElement.highlighted is for user highlighting
1574             // initiated by the user, e.g. through custom DOM events. We can't just pick one because this would break user
1575             // defined highlighting in many ways:
1576             //  * if overriding the highlight() methods the user had to handle the highlightedObjects stuff, otherwise he'd break
1577             //    everything (e.g. the pie chart example https://jsxgraph.org/wiki/index.php/Pie_chart (not exactly
1578             //    user defined but for this type of chart the highlight method was overridden and not adjusted to the changes in here)
1579             //    where it just kept highlighting until the radius of the pie was far beyond infinity...
1580             //  * user defined highlighting would get pointless, everytime the user highlights something using .highlight(), it would get
1581             //    dehighlighted immediately, because highlight puts the element into highlightedObjects and from there it gets dehighlighted
1582             //    through dehighlightAll.
1583 
1584             // highlight only if not highlighted
1585             if (Type.evaluate(this.visProp.highlight) && (!this.highlighted || force)) {
1586                 this.highlighted = true;
1587                 this.board.highlightedObjects[this.id] = this;
1588                 this.board.renderer.highlight(this);
1589             }
1590             return this;
1591         },
1592 
1593         /**
1594          * Uses the "normal" properties of the element.
1595          * @returns {JXG.Board}
1596          */
1597         noHighlight: function () {
1598             // see comment in JXG.GeometryElement.highlight()
1599 
1600             // dehighlight only if not highlighted
1601             if (this.highlighted) {
1602                 this.highlighted = false;
1603                 delete this.board.highlightedObjects[this.id];
1604                 this.board.renderer.noHighlight(this);
1605             }
1606             return this;
1607         },
1608 
1609         /**
1610          * Removes all objects generated by the trace function.
1611          */
1612         clearTrace: function () {
1613             var obj;
1614 
1615             for (obj in this.traces) {
1616                 if (this.traces.hasOwnProperty(obj)) {
1617                     this.board.renderer.remove(this.traces[obj]);
1618                 }
1619             }
1620 
1621             this.numTraces = 0;
1622             return this;
1623         },
1624 
1625         /**
1626          * Copy the element to background. This is used for tracing elements.
1627          * @returns {JXG.GeometryElement} A reference to the element
1628          */
1629         cloneToBackground: function () {
1630             return this;
1631         },
1632 
1633         /**
1634          * Dimensions of the smallest rectangle enclosing the element.
1635          * @returns {Array} The coordinates of the enclosing rectangle in a format
1636          * like the bounding box in {@link JXG.Board#setBoundingBox}.
1637          * 
1638          * @returns {Array} similar to {@link JXG.Board#setBoundingBox}.
1639          */
1640         bounds: function () {
1641             return [0, 0, 0, 0];
1642         },
1643 
1644         /**
1645          * Normalize the element's standard form.
1646          * @private
1647          */
1648         normalize: function () {
1649             this.stdform = Mat.normalize(this.stdform);
1650             return this;
1651         },
1652 
1653         /**
1654          * EXPERIMENTAL. Generate JSON object code of visProp and other properties.
1655          * @type String
1656          * @private
1657          * @ignore
1658          * @returns JSON string containing element's properties.
1659          */
1660         toJSON: function () {
1661             var vis, key,
1662                 json = ['{"name":', this.name];
1663 
1664             json.push(', ' + '"id":' + this.id);
1665 
1666             vis = [];
1667             for (key in this.visProp) {
1668                 if (this.visProp.hasOwnProperty(key)) {
1669                     if (Type.exists(this.visProp[key])) {
1670                         vis.push('"' + key + '":' + this.visProp[key]);
1671                     }
1672                 }
1673             }
1674             json.push(', "visProp":{' + vis.toString() + '}');
1675             json.push('}');
1676 
1677             return json.join('');
1678         },
1679 
1680         /**
1681          * Rotate texts or images by a given degree. Works only for texts where JXG.Text#display equal to "internal".
1682          * @param {number} angle The degree of the rotation (90 means vertical text).
1683          * @see JXG.GeometryElement#rotate
1684          */
1685         addRotation: function (angle) {
1686             var tOffInv, tOff, tS, tSInv, tRot,
1687                 that = this;
1688 
1689             if (((this.elementClass === Const.OBJECT_CLASS_TEXT &&
1690                     Type.evaluate(this.visProp.display) === 'internal') ||
1691                     this.type === Const.OBJECT_TYPE_IMAGE) && angle !== 0) {
1692 
1693                 tOffInv = this.board.create('transform', [
1694                     function () {
1695                         return -that.X();
1696                     }, function () {
1697                         return -that.Y();
1698                     }
1699                 ], {type: 'translate'});
1700 
1701                 tOff = this.board.create('transform', [
1702                     function () {
1703                         return that.X();
1704                     }, function () {
1705                         return that.Y();
1706                     }
1707                 ], {type: 'translate'});
1708 
1709                 tS = this.board.create('transform', [
1710                     function () {
1711                         return that.board.unitX / that.board.unitY;
1712                     }, function () {
1713                         return 1;
1714                     }
1715                 ], {type: 'scale'});
1716 
1717                 tSInv = this.board.create('transform', [
1718                     function () {
1719                         return that.board.unitY / that.board.unitX;
1720                     }, function () {
1721                         return 1;
1722                     }
1723                 ], {type: 'scale'});
1724 
1725                 tRot = this.board.create('transform', [
1726                         function() { return Type.evaluate(angle) * Math.PI / 180; }
1727                     ], {type: 'rotate'});
1728 
1729                 tOffInv.bindTo(this);
1730                 tS.bindTo(this);
1731                 tRot.bindTo(this);
1732                 tSInv.bindTo(this);
1733                 tOff.bindTo(this);
1734             }
1735 
1736             return this;
1737         },
1738 
1739         /**
1740          * Set the highlightStrokeColor of an element
1741          * @param {String} sColor String which determines the stroke color of an object when its highlighted.
1742          * @see JXG.GeometryElement#highlightStrokeColor
1743          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1744          */
1745         highlightStrokeColor: function (sColor) {
1746             JXG.deprecated('highlightStrokeColor()', 'setAttribute()');
1747             this.setAttribute({highlightStrokeColor: sColor});
1748             return this;
1749         },
1750 
1751         /**
1752          * Set the strokeColor of an element
1753          * @param {String} sColor String which determines the stroke color of an object.
1754          * @see JXG.GeometryElement#strokeColor
1755          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1756          */
1757         strokeColor: function (sColor) {
1758             JXG.deprecated('strokeColor()', 'setAttribute()');
1759             this.setAttribute({strokeColor: sColor});
1760             return this;
1761         },
1762 
1763         /**
1764          * Set the strokeWidth of an element
1765          * @param {Number} width Integer which determines the stroke width of an outline.
1766          * @see JXG.GeometryElement#strokeWidth
1767          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1768          */
1769         strokeWidth: function (width) {
1770             JXG.deprecated('strokeWidth()', 'setAttribute()');
1771             this.setAttribute({strokeWidth: width});
1772             return this;
1773         },
1774 
1775         /**
1776          * Set the fillColor of an element
1777          * @param {String} fColor String which determines the fill color of an object.
1778          * @see JXG.GeometryElement#fillColor
1779          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1780          */
1781         fillColor: function (fColor) {
1782             JXG.deprecated('fillColor()', 'setAttribute()');
1783             this.setAttribute({fillColor: fColor});
1784             return this;
1785         },
1786 
1787         /**
1788          * Set the highlightFillColor of an element
1789          * @param {String} fColor String which determines the fill color of an object when its highlighted.
1790          * @see JXG.GeometryElement#highlightFillColor
1791          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1792          */
1793         highlightFillColor: function (fColor) {
1794             JXG.deprecated('highlightFillColor()', 'setAttribute()');
1795             this.setAttribute({highlightFillColor: fColor});
1796             return this;
1797         },
1798 
1799         /**
1800          * Set the labelColor of an element
1801          * @param {String} lColor String which determines the text color of an object's label.
1802          * @see JXG.GeometryElement#labelColor
1803          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1804          */
1805         labelColor: function (lColor) {
1806             JXG.deprecated('labelColor()', 'setAttribute()');
1807             this.setAttribute({labelColor: lColor});
1808             return this;
1809         },
1810 
1811         /**
1812          * Set the dash type of an element
1813          * @param {Number} d Integer which determines the way of dashing an element's outline.
1814          * @see JXG.GeometryElement#dash
1815          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1816          */
1817         dash: function (d) {
1818             JXG.deprecated('dash()', 'setAttribute()');
1819             this.setAttribute({dash: d});
1820             return this;
1821         },
1822 
1823         /**
1824          * Set the visibility of an element
1825          * @param {Boolean} v Boolean which determines whether the element is drawn.
1826          * @see JXG.GeometryElement#visible
1827          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1828          */
1829         visible: function (v) {
1830             JXG.deprecated('visible()', 'setAttribute()');
1831             this.setAttribute({visible: v});
1832             return this;
1833         },
1834 
1835         /**
1836          * Set the shadow of an element
1837          * @param {Boolean} s Boolean which determines whether the element has a shadow or not.
1838          * @see JXG.GeometryElement#shadow
1839          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1840          */
1841         shadow: function (s) {
1842             JXG.deprecated('shadow()', 'setAttribute()');
1843             this.setAttribute({shadow: s});
1844             return this;
1845         },
1846 
1847         /**
1848          * The type of the element as used in {@link JXG.Board#create}.
1849          * @returns {String}
1850          */
1851         getType: function () {
1852             return this.elType;
1853         },
1854 
1855         /**
1856          * List of the element ids resp. values used as parents in {@link JXG.Board#create}.
1857          * @returns {Array}
1858          */
1859         getParents: function () {
1860             return Type.isArray(this.parents) ? this.parents : [];
1861         },
1862 
1863         /**
1864          * Snaps the element to the grid. Only works for points, lines and circles. Points will snap to the grid
1865          * as defined in their properties {@link JXG.Point#snapSizeX} and {@link JXG.Point#snapSizeY}. Lines and circles
1866          * will snap their parent points to the grid, if they have {@link JXG.Point#snapToGrid} set to true.
1867          * @returns {JXG.GeometryElement} Reference to the element.
1868          */
1869         snapToGrid: function () {
1870             return this;
1871         },
1872 
1873         /**
1874          * Snaps the element to points. Only works for points. Points will snap to the next point
1875          * as defined in their properties {@link JXG.Point#attractorDistance} and {@link JXG.Point#attractorUnit}.
1876          * Lines and circles
1877          * will snap their parent points to points.
1878          * @returns {JXG.GeometryElement} Reference to the element.
1879          */
1880         snapToPoints: function () {
1881             return this;
1882         },
1883 
1884         /**
1885          * Retrieve a copy of the current visProp.
1886          * @returns {Object}
1887          */
1888         getAttributes: function () {
1889             var attributes = Type.deepCopy(this.visProp),
1890                 /*
1891                 cleanThis = ['attractors', 'snatchdistance', 'traceattributes', 'frozen',
1892                     'shadow', 'gradientangle', 'gradientsecondopacity', 'gradientpositionx', 'gradientpositiony',
1893                     'needsregularupdate', 'zoom', 'layer', 'offset'],
1894                 */
1895                 cleanThis = [],
1896                 i, len = cleanThis.length;
1897 
1898             attributes.id = this.id;
1899             attributes.name = this.name;
1900 
1901             for (i = 0; i < len; i++) {
1902                 delete attributes[cleanThis[i]];
1903             }
1904 
1905             return attributes;
1906         },
1907 
1908         /**
1909          * Checks whether (x,y) is near the element.
1910          * @param {Number} x Coordinate in x direction, screen coordinates.
1911          * @param {Number} y Coordinate in y direction, screen coordinates.
1912          * @returns {Boolean} True if (x,y) is near the element, False otherwise.
1913          */
1914         hasPoint: function (x, y) {
1915             return false;
1916         },
1917 
1918         /**
1919          * Adds ticks to this line or curve. Ticks can be added to a curve or any kind of line: line, arrow, and axis.
1920          * @param {JXG.Ticks} ticks Reference to a ticks object which is describing the ticks (color, distance, how many, etc.).
1921          * @returns {String} Id of the ticks object.
1922          */
1923         addTicks: function (ticks) {
1924             if (ticks.id === '' || !Type.exists(ticks.id)) {
1925                 ticks.id = this.id + '_ticks_' + (this.ticks.length + 1);
1926             }
1927 
1928             this.board.renderer.drawTicks(ticks);
1929             this.ticks.push(ticks);
1930 
1931             return ticks.id;
1932         },
1933 
1934         /**
1935          * Removes all ticks from a line or curve.
1936          */
1937         removeAllTicks: function () {
1938             var t;
1939             if (Type.exists(this.ticks)) {
1940                 for (t = this.ticks.length - 1; t >= 0; t--) {
1941                     this.removeTicks(this.ticks[t]);
1942                 }
1943                 this.ticks = [];
1944                 this.board.update();
1945             }
1946         },
1947 
1948         /**
1949          * Removes ticks identified by parameter named tick from this line or curve.
1950          * @param {JXG.Ticks} tick Reference to tick object to remove.
1951          */
1952         removeTicks: function (tick) {
1953             var t, j;
1954 
1955             if (Type.exists(this.defaultTicks) && this.defaultTicks === tick) {
1956                 this.defaultTicks = null;
1957             }
1958 
1959             if (Type.exists(this.ticks)) {
1960                 for (t = this.ticks.length - 1; t >= 0; t--) {
1961                     if (this.ticks[t] === tick) {
1962                         this.board.removeObject(this.ticks[t]);
1963 
1964                         if (this.ticks[t].ticks) {
1965                             for (j = 0; j < this.ticks[t].ticks.length; j++) {
1966                                 if (Type.exists(this.ticks[t].labels[j])) {
1967                                     this.board.removeObject(this.ticks[t].labels[j]);
1968                                 }
1969                             }
1970                         }
1971 
1972                         delete this.ticks[t];
1973                         break;
1974                     }
1975                 }
1976             }
1977         },
1978 
1979         /**
1980          * Determine values of snapSizeX and snapSizeY. If the attributes
1981          * snapSizex and snapSizeY are greater than zero, these values are taken.
1982          * Otherwise, determine the distance between major ticks of the
1983          * default axes.
1984          * @returns {Array} containing the snap sizes for x and y direction.
1985          * @private
1986          */
1987         getSnapSizes: function() {
1988             var sX, sY, ticks;
1989 
1990             sX = Type.evaluate(this.visProp.snapsizex);
1991             sY = Type.evaluate(this.visProp.snapsizey);
1992 
1993             if (sX <= 0 && this.board.defaultAxes && this.board.defaultAxes.x.defaultTicks) {
1994                 ticks = this.board.defaultAxes.x.defaultTicks;
1995                 sX = ticks.ticksDelta * (Type.evaluate(ticks.visProp.minorticks) + 1);
1996             }
1997 
1998             if (sY <= 0 && this.board.defaultAxes && this.board.defaultAxes.y.defaultTicks) {
1999                 ticks = this.board.defaultAxes.y.defaultTicks;
2000                 sY = ticks.ticksDelta * (Type.evaluate(ticks.visProp.minorticks) + 1);
2001             }
2002 
2003             return [sX, sY];
2004         },
2005 
2006         /**
2007          * Move an element to its nearest grid point.
2008          * The function uses the coords object of the element as
2009          * its actual position. If there is no coords object or if the object is fixed, nothing is done.
2010          * @param {Boolean} force force snapping independent from what the snaptogrid attribute says
2011          * @param {Boolean} fromParent True if the drag comes from a child element. This is the case if a line
2012          *    through two points is dragged. In this case we do not try to force the points to stay inside of
2013          *    the visible board, but the distance between the two points stays constant.
2014          * @returns {JXG.GeometryElement} Reference to this element
2015          */
2016         handleSnapToGrid: function (force, fromParent) {
2017             var x, y, rx, ry, rcoords,
2018                 boardBB, res, sX, sY,
2019                 needsSnapToGrid = false,
2020                 attractToGrid = Type.evaluate(this.visProp.attracttogrid),
2021                 ev_au = Type.evaluate(this.visProp.attractorunit),
2022                 ev_ad = Type.evaluate(this.visProp.attractordistance);
2023 
2024             if (!Type.exists(this.coords) || Type.evaluate(this.visProp.fixed)) {
2025                 return this;
2026             }
2027 
2028             needsSnapToGrid = Type.evaluate(this.visProp.snaptogrid) || attractToGrid || force === true;
2029 
2030             if (needsSnapToGrid) {
2031                 x = this.coords.usrCoords[1];
2032                 y = this.coords.usrCoords[2];
2033                 res = this.getSnapSizes();
2034                 sX = res[0];
2035                 sY = res[1];
2036 
2037                 // If no valid snap sizes are available, don't change the coords.
2038                 if (sX > 0 && sY > 0) {
2039                     boardBB = this.board.getBoundingBox();
2040                     rx = Math.round(x / sX) * sX;
2041                     ry = Math.round(y / sY) * sY;
2042                     rcoords = new JXG.Coords(Const.COORDS_BY_USER, [rx, ry], this.board);
2043                     if (!attractToGrid ||
2044                         rcoords.distance(
2045                             ev_au === 'screen' ? Const.COORDS_BY_SCREEN : Const.COORDS_BY_USER, this.coords
2046                             ) < ev_ad) {
2047                         x = rx;
2048                         y = ry;
2049                         // Checking whether x and y are still within boundingBox.
2050                         // If not, adjust them to remain within the board.
2051                         // Otherwise a point may become invisible.
2052                         if (!fromParent) {
2053                             if (x < boardBB[0]) {
2054                                 x += sX;
2055                             } else if (x > boardBB[2]) {
2056                                 x -= sX;
2057                             }
2058 
2059                             if (y < boardBB[3]) {
2060                                 y += sY;
2061                             } else if (y > boardBB[1]) {
2062                                 y -= sY;
2063                             }
2064                         }
2065                         this.coords.setCoordinates(Const.COORDS_BY_USER, [x, y]);
2066                     }
2067                 }
2068             }
2069             return this;
2070         },
2071 
2072         getBoundingBox: function() {
2073             var i, le, v, x, y,
2074                 bb = [Infinity, Infinity, -Infinity, -Infinity];
2075 
2076             if (this.type === Const.OBJECT_TYPE_POLYGON) {
2077                 le = this.vertices.length - 1;
2078                 if (le <= 0) {
2079                     return bb;
2080                 }
2081                 for (i = 0; i < le; i++) {
2082                     v = this.vertices[i].X();
2083                     bb[0] = (v < bb[0]) ? v : bb[0];
2084                     bb[2] = (v > bb[2]) ? v : bb[2];
2085                     v = this.vertices[i].Y();
2086                     bb[1] = (v < bb[1]) ? v : bb[1];
2087                     bb[3] = (v > bb[3]) ? v : bb[3];
2088                 }
2089             } else if (this.elementClass === Const.OBJECT_CLASS_CIRCLE) {
2090                 x = this.center.X();
2091                 y = this.center.Y();
2092                 bb = [x - this.radius, y + this.radius, x + this.radius, y - this.radius];
2093             } else if (this.elementClass === Const.OBJECT_CLASS_CURVE) {
2094                 le = this.vertices.length;
2095                 if (le === 0) {
2096                     return bb;
2097                 }
2098                 for (i = 0; i < le; i++) {
2099                     v = this.points[i].coords.usrCoords[1];
2100                     bb[0] = (v < bb[0]) ? v : bb[0];
2101                     bb[2] = (v > bb[2]) ? v : bb[2];
2102                     v = this.points[i].coords.usrCoords[1];
2103                     bb[1] = (v < bb[1]) ? v : bb[1];
2104                     bb[3] = (v > bb[3]) ? v : bb[3];
2105                 }
2106             }
2107 
2108             return bb;
2109         },
2110 
2111         /**
2112          * Alias of {@link JXG.EventEmitter.on}.
2113          *
2114          * @name addEvent
2115          * @memberof JXG.GeometryElement
2116          * @function
2117          */
2118         addEvent: JXG.shortcut(JXG.GeometryElement.prototype, 'on'),
2119 
2120         /**
2121          * Alias of {@link JXG.EventEmitter.off}.
2122          *
2123          * @name removeEvent
2124          * @memberof JXG.GeometryElement
2125          * @function
2126          */
2127         removeEvent: JXG.shortcut(JXG.GeometryElement.prototype, 'off'),
2128 
2129         /* **************************
2130          *     EVENT DEFINITION
2131          * for documentation purposes
2132          * ************************** */
2133 
2134         //region Event handler documentation
2135         /**
2136          * @event
2137          * @description This event is fired whenever the user is hovering over an element.
2138          * @name JXG.GeometryElement#over
2139          * @param {Event} e The browser's event object.
2140          */
2141         __evt__over: function (e) { },
2142 
2143         /**
2144          * @event
2145          * @description This event is fired whenever the user puts the mouse over an element.
2146          * @name JXG.GeometryElement#mouseover
2147          * @param {Event} e The browser's event object.
2148          */
2149         __evt__mouseover: function (e) { },
2150 
2151         /**
2152          * @event
2153          * @description This event is fired whenever the user is leaving an element.
2154          * @name JXG.GeometryElement#out
2155          * @param {Event} e The browser's event object.
2156          */
2157         __evt__out: function (e) { },
2158 
2159         /**
2160          * @event
2161          * @description This event is fired whenever the user puts the mouse away from an element.
2162          * @name JXG.GeometryElement#mouseout
2163          * @param {Event} e The browser's event object.
2164          */
2165         __evt__mouseout: function (e) { },
2166 
2167         /**
2168          * @event
2169          * @description This event is fired whenever the user is moving over an element.
2170          * @name JXG.GeometryElement#move
2171          * @param {Event} e The browser's event object.
2172          */
2173         __evt__move: function (e) { },
2174 
2175         /**
2176          * @event
2177          * @description This event is fired whenever the user is moving the mouse over an element.
2178          * @name JXG.GeometryElement#mousemove
2179          * @param {Event} e The browser's event object.
2180          */
2181         __evt__mousemove: function (e) { },
2182 
2183         /**
2184          * @event
2185          * @description This event is fired whenever the user drags an element.
2186          * @name JXG.GeometryElement#drag
2187          * @param {Event} e The browser's event object.
2188          */
2189         __evt__drag: function (e) { },
2190 
2191         /**
2192          * @event
2193          * @description This event is fired whenever the user drags the element with a mouse.
2194          * @name JXG.GeometryElement#mousedrag
2195          * @param {Event} e The browser's event object.
2196          */
2197         __evt__mousedrag: function (e) { },
2198 
2199         /**
2200          * @event
2201          * @description This event is fired whenever the user drags the element with a pen.
2202          * @name JXG.GeometryElement#pendrag
2203          * @param {Event} e The browser's event object.
2204          */
2205         __evt__pendrag: function (e) { },
2206 
2207         /**
2208          * @event
2209          * @description This event is fired whenever the user drags the element on a touch device.
2210          * @name JXG.GeometryElement#touchdrag
2211          * @param {Event} e The browser's event object.
2212          */
2213         __evt__touchdrag: function (e) { },
2214 
2215         /**
2216          * @event
2217          * @description Whenever the user starts to touch or click an element.
2218          * @name JXG.GeometryElement#down
2219          * @param {Event} e The browser's event object.
2220          */
2221         __evt__down: function (e) { },
2222 
2223         /**
2224          * @event
2225          * @description Whenever the user starts to click an element.
2226          * @name JXG.GeometryElement#mousedown
2227          * @param {Event} e The browser's event object.
2228          */
2229         __evt__mousedown: function (e) { },
2230 
2231         /**
2232          * @event
2233          * @description Whenever the user taps an element with the pen.
2234          * @name JXG.GeometryElement#pendown
2235          * @param {Event} e The browser's event object.
2236          */
2237         __evt__pendown: function (e) { },
2238 
2239         /**
2240          * @event
2241          * @description Whenever the user starts to touch an element.
2242          * @name JXG.GeometryElement#touchdown
2243          * @param {Event} e The browser's event object.
2244          */
2245         __evt__touchdown: function (e) { },
2246 
2247         /**
2248          * @event
2249          * @description Whenever the user stops to touch or click an element.
2250          * @name JXG.GeometryElement#up
2251          * @param {Event} e The browser's event object.
2252          */
2253         __evt__up: function (e) { },
2254 
2255         /**
2256          * @event
2257          * @description Whenever the user releases the mousebutton over an element.
2258          * @name JXG.GeometryElement#mouseup
2259          * @param {Event} e The browser's event object.
2260          */
2261         __evt__mouseup: function (e) { },
2262 
2263         /**
2264          * @event
2265          * @description Whenever the user lifts the pen over an element.
2266          * @name JXG.GeometryElement#penup
2267          * @param {Event} e The browser's event object.
2268          */
2269         __evt__penup: function (e) { },
2270 
2271         /**
2272          * @event
2273          * @description Whenever the user stops touching an element.
2274          * @name JXG.GeometryElement#touchup
2275          * @param {Event} e The browser's event object.
2276          */
2277         __evt__touchup: function (e) {},
2278 
2279         /**
2280          * @event
2281          * @description Notify every time an attribute is changed.
2282          * @name JXG.GeometryElement#attribute
2283          * @param {Object} o A list of changed attributes and their new value.
2284          * @param {Object} el Reference to the element
2285          */
2286         __evt__attribute: function (o, el) {},
2287 
2288         /**
2289          * @event
2290          * @description This is a generic event handler. It exists for every possible attribute that can be set for
2291          * any element, e.g. if you want to be notified everytime an element's strokecolor is changed, is the event
2292          * <tt>attribute:strokecolor</tt>.
2293          * @name JXG.GeometryElement#attribute:key
2294          * @param val The old value.
2295          * @param nval The new value
2296          * @param {Object} el Reference to the element
2297          */
2298         __evt__attribute_: function (val, nval, el) {},
2299 
2300         /**
2301          * @ignore
2302          */
2303         __evt: function () {}
2304         //endregion
2305 
2306     });
2307 
2308     return JXG.GeometryElement;
2309 });
2310