1 /*
  2     Copyright 2008-2018
  3         Matthias Ehmann,
  4         Michael Gerhaeuser,
  5         Carsten Miller,
  6         Bianca Valentin,
  7         Alfred Wassermann,
  8         Peter Wilfahrt
  9 
 10     This file is part of JSXGraph.
 11 
 12     JSXGraph is free software dual licensed under the GNU LGPL or MIT License.
 13 
 14     You can redistribute it and/or modify it under the terms of the
 15 
 16       * GNU Lesser General Public License as published by
 17         the Free Software Foundation, either version 3 of the License, or
 18         (at your option) any later version
 19       OR
 20       * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT
 21 
 22     JSXGraph is distributed in the hope that it will be useful,
 23     but WITHOUT ANY WARRANTY; without even the implied warranty of
 24     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 25     GNU Lesser General Public License for more details.
 26 
 27     You should have received a copy of the GNU Lesser General Public License and
 28     the MIT License along with JSXGraph. If not, see <http://www.gnu.org/licenses/>
 29     and <http://opensource.org/licenses/MIT/>.
 30  */
 31 
 32 
 33 /*global JXG: true, define: true*/
 34 /*jslint nomen: true, plusplus: true*/
 35 
 36 /* depends:
 37  jxg
 38  base/constants
 39  base/coords
 40  math/math
 41  options
 42  parser/geonext
 43  utils/event
 44  utils/color
 45  utils/type
 46  */
 47 
 48 define([
 49     'jxg', 'base/constants', 'base/coords', 'math/math', 'math/statistics', 'options', 'parser/geonext', 'utils/event', 'utils/color', 'utils/type'
 50 ], function (JXG, Const, Coords, Mat, Statistics, Options, GeonextParser, EventEmitter, Color, Type) {
 51 
 52     "use strict";
 53 
 54     /**
 55      * Constructs a new GeometryElement object.
 56      * @class This is the basic class for geometry elements like points, circles and lines.
 57      * @constructor
 58      * @param {JXG.Board} board Reference to the board the element is constructed on.
 59      * @param {Object} attributes Hash of attributes and their values.
 60      * @param {Number} type Element type (a <tt>JXG.OBJECT_TYPE_</tt> value).
 61      * @param {Number} oclass The element's class (a <tt>JXG.OBJECT_CLASS_</tt> value).
 62      * @borrows JXG.EventEmitter#on as this.on
 63      * @borrows JXG.EventEmitter#off as this.off
 64      * @borrows JXG.EventEmitter#triggerEventHandlers as this.triggerEventHandlers
 65      * @borrows JXG.EventEmitter#eventHandlers as this.eventHandlers
 66      */
 67     JXG.GeometryElement = function (board, attributes, type, oclass) {
 68         var name, key, attr;
 69 
 70         /**
 71          * Controls if updates are necessary
 72          * @type Boolean
 73          * @default true
 74          */
 75         this.needsUpdate = true;
 76 
 77         /**
 78          * Controls if this element can be dragged. In GEONExT only
 79          * free points and gliders can be dragged.
 80          * @type Boolean
 81          * @default false
 82          */
 83         this.isDraggable = false;
 84 
 85         /**
 86          * If element is in two dimensional real space this is true, else false.
 87          * @type Boolean
 88          * @default true
 89          */
 90         this.isReal = true;
 91 
 92         /**
 93          * Stores all dependent objects to be updated when this point is moved.
 94          * @type Object
 95          */
 96         this.childElements = {};
 97 
 98         /**
 99          * If element has a label subelement then this property will be set to true.
100          * @type Boolean
101          * @default false
102          */
103         this.hasLabel = false;
104 
105         /**
106          * True, if the element is currently highlighted.
107          * @type Boolean
108          * @default false
109          */
110         this.highlighted = false;
111 
112         /**
113          * Stores all Intersection Objects which in this moment are not real and
114          * so hide this element.
115          * @type Object
116          */
117         this.notExistingParents = {};
118 
119         /**
120          * Keeps track of all objects drawn as part of the trace of the element.
121          * @see JXG.GeometryElement#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 partuclar 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         };
286 
287         /**
288          * Quadratic form representation of circles (and conics)
289          * @type Array
290          * @default [[1,0,0],[0,1,0],[0,0,1]]
291          */
292         this.quadraticform = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
293 
294         /**
295          * An associative array containing all visual properties.
296          * @type Object
297          * @default empty object
298          */
299         this.visProp = {};
300 
301         /**
302          * An associative array containing visual properties which are calculated from
303          * the attribute values (i.e. visProp) and from other constraints.
304          * An example: if an intersection point does not have real coordinates,
305          * visPropCalc.visible is set to false.
306          * Additionally, the user can control visibility with the attribute "visible",
307          * even by supplying a functions as value.
308          *
309          * @type Object
310          * @default empty object
311          */
312         this.visPropCalc = {
313             visible: false
314         };
315 
316         EventEmitter.eventify(this);
317 
318         /**
319          * Is the mouse over this element?
320          * @type Boolean
321          * @default false
322          */
323         this.mouseover = false;
324 
325         /**
326          * Time stamp containing the last time this element has been dragged.
327          * @type Date
328          * @default creation time
329          */
330         this.lastDragTime = new Date();
331 
332         if (arguments.length > 0) {
333             /**
334              * Reference to the board associated with the element.
335              * @type JXG.Board
336              */
337             this.board = board;
338 
339             /**
340              * Type of the element.
341              * @constant
342              * @type number
343              */
344             this.type = type;
345 
346             /**
347              * Original type of the element at construction time. Used for removing glider property.
348              * @constant
349              * @type number
350              */
351             this._org_type = type;
352 
353             /**
354              * The element's class.
355              * @constant
356              * @type number
357              */
358             this.elementClass = oclass || Const.OBJECT_CLASS_OTHER;
359 
360             /**
361              * Unique identifier for the element. Equivalent to id-attribute of renderer element.
362              * @type String
363              */
364             this.id = attributes.id;
365 
366             name = attributes.name;
367             /* If name is not set or null or even undefined, generate an unique name for this object */
368             if (!Type.exists(name)) {
369                 name = this.board.generateName(this);
370             }
371 
372             if (name !== '') {
373                 this.board.elementsByName[name] = this;
374             }
375 
376             /**
377              * Not necessarily unique name for the element.
378              * @type String
379              * @default Name generated by {@link JXG.Board#generateName}.
380              * @see JXG.Board#generateName
381              */
382             this.name = name;
383 
384             this.needsRegularUpdate = attributes.needsregularupdate;
385 
386             // create this.visPropOld and set default values
387             Type.clearVisPropOld(this);
388 
389             attr = this.resolveShortcuts(attributes);
390             for (key in attr) {
391                 if (attr.hasOwnProperty(key)) {
392                     this._set(key, attr[key]);
393                 }
394             }
395 
396             this.visProp.draft = attr.draft && attr.draft.draft;
397             this.visProp.gradientangle = '270';
398             this.visProp.gradientsecondopacity = Type.evaluate(this.visProp.fillopacity);
399             this.visProp.gradientpositionx = 0.5;
400             this.visProp.gradientpositiony = 0.5;
401         }
402     };
403 
404     JXG.extend(JXG.GeometryElement.prototype, /** @lends JXG.GeometryElement.prototype */ {
405         /**
406          * Add an element as a child to the current element. Can be used to model dependencies between geometry elements.
407          * @param {JXG.GeometryElement} obj The dependent object.
408          */
409         addChild: function (obj) {
410             var el, el2;
411 
412             this.childElements[obj.id] = obj;
413             this.addDescendants(obj);
414             obj.ancestors[this.id] = this;
415 
416             for (el in this.descendants) {
417                 if (this.descendants.hasOwnProperty(el)) {
418                     this.descendants[el].ancestors[this.id] = this;
419 
420                     for (el2 in this.ancestors) {
421                         if (this.ancestors.hasOwnProperty(el2)) {
422                             this.descendants[el].ancestors[this.ancestors[el2].id] = this.ancestors[el2];
423                         }
424                     }
425                 }
426             }
427 
428             for (el in this.ancestors) {
429                 if (this.ancestors.hasOwnProperty(el)) {
430                     for (el2 in this.descendants) {
431                         if (this.descendants.hasOwnProperty(el2)) {
432                             this.ancestors[el].descendants[this.descendants[el2].id] = this.descendants[el2];
433                         }
434                     }
435                 }
436             }
437             return this;
438         },
439 
440         /**
441          * Adds the given object to the descendants list of this object and all its child objects.
442          * @param {JXG.GeometryElement} obj The element that is to be added to the descendants list.
443          * @private
444          * @return
445          */
446         addDescendants: function (obj) {
447             var el;
448 
449             this.descendants[obj.id] = obj;
450             for (el in obj.childElements) {
451                 if (obj.childElements.hasOwnProperty(el)) {
452                     this.addDescendants(obj.childElements[el]);
453                 }
454             }
455             return this;
456         },
457 
458         /**
459          * Adds ids of elements to the array this.parents. This method needs to be called if some dependencies
460          * can not be detected automatically by JSXGraph. For example if a function graph is given by a function
461          * which referes to coordinates of a point, calling addParents() is necessary.
462          *
463          * @param {Array} parents Array of elements or ids of elements.
464          * Alternatively, one can give a list of objects as parameters.
465          * @returns {JXG.Object} reference to the object itself.
466          *
467          * @example
468          * // Movable function graph
469          * var A = board.create('point', [1, 0], {name:'A'}),
470          *     B = board.create('point', [3, 1], {name:'B'}),
471          *     f = board.create('functiongraph', function(x) {
472          *          var ax = A.X(),
473          *              ay = A.Y(),
474          *              bx = B.X(),
475          *              by = B.Y(),
476          *              a = (by - ay) / ( (bx - ax) * (bx - ax) );
477          *           return a * (x - ax) * (x - ax) + ay;
478          *      }, {fixed: false});
479          * f.addParents([A, B]);
480          * </pre><div class="jxgbox" id="7c91d4d2-986c-4378-8135-24505027f251" style="width: 400px; height: 400px;"></div>
481          * <script type="text/javascript">
482          * (function() {
483          *   var board = JXG.JSXGraph.initBoard('7c91d4d2-986c-4378-8135-24505027f251', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
484          *   var A = board.create('point', [1, 0], {name:'A'}),
485          *       B = board.create('point', [3, 1], {name:'B'}),
486          *       f = board.create('functiongraph', function(x) {
487          *            var ax = A.X(),
488          *                ay = A.Y(),
489          *                bx = B.X(),
490          *                by = B.Y(),
491          *                a = (by - ay) / ( (bx - ax) * (bx - ax) );
492          *             return a * (x - ax) * (x - ax) + ay;
493          *        }, {fixed: false});
494          *   f.addParents([A, B]);
495          * })();
496          * </script><pre>
497          *
498          **/
499         addParents: function (parents) {
500             var i, len, par;
501 
502             if (Type.isArray(parents)) {
503                 par = parents;
504             } else {
505                 par = arguments;
506             }
507 
508             len = par.length;
509             for (i = 0; i < len; ++i) {
510                 if (!Type.exists(par[i])) {
511                     continue;
512                 }
513                 if (Type.isId(this.board, par[i])) {
514                     this.parents.push(par[i]);
515                 } else if (Type.exists(par[i].id)) {
516                     this.parents.push(par[i].id);
517                 }
518             }
519             this.parents = Type.uniqueArray(this.parents);
520         },
521 
522         /**
523          * Sets ids of elements to the array this.parents.
524          * First, this.parents is cleared. See {@link JXG.GeometryElement#addParents}.
525          * @param {Array} parents Array of elements or ids of elements.
526          * Alternatively, one can give a list of objects as parameters.
527          * @returns {JXG.Object} reference to the object itself.
528          **/
529         setParents: function(parents) {
530             this.parents = [];
531             this.addParents(parents);
532         },
533 
534         /**
535          * Remove an element as a child from the current element.
536          * @param {JXG.GeometryElement} obj The dependent object.
537          */
538         removeChild: function (obj) {
539             //var el, el2;
540 
541             delete this.childElements[obj.id];
542             this.removeDescendants(obj);
543             delete obj.ancestors[this.id];
544 
545             /*
546              // I do not know if these addDescendants stuff has to be adapted to removeChild. A.W.
547             for (el in this.descendants) {
548                 if (this.descendants.hasOwnProperty(el)) {
549                     delete this.descendants[el].ancestors[this.id];
550 
551                     for (el2 in this.ancestors) {
552                         if (this.ancestors.hasOwnProperty(el2)) {
553                             this.descendants[el].ancestors[this.ancestors[el2].id] = this.ancestors[el2];
554                         }
555                     }
556                 }
557             }
558 
559             for (el in this.ancestors) {
560                 if (this.ancestors.hasOwnProperty(el)) {
561                     for (el2 in this.descendants) {
562                         if (this.descendants.hasOwnProperty(el2)) {
563                             this.ancestors[el].descendants[this.descendants[el2].id] = this.descendants[el2];
564                         }
565                     }
566                 }
567             }
568             */
569             return this;
570         },
571 
572         /**
573          * Removes the given object from the descendants list of this object and all its child objects.
574          * @param {JXG.GeometryElement} obj The element that is to be removed from the descendants list.
575          * @private
576          * @return
577          */
578         removeDescendants: function (obj) {
579             var el;
580 
581             delete this.descendants[obj.id];
582             for (el in obj.childElements) {
583                 if (obj.childElements.hasOwnProperty(el)) {
584                     this.removeDescendants(obj.childElements[el]);
585                 }
586             }
587             return this;
588         },
589 
590         /**
591          * Counts the direct children of an object without counting labels.
592          * @private
593          * @returns {number} Number of children
594          */
595         countChildren: function () {
596             var prop, d,
597                 s = 0;
598 
599             d = this.childElements;
600             for (prop in d) {
601                 if (d.hasOwnProperty(prop) && prop.indexOf('Label') < 0) {
602                     s++;
603                 }
604             }
605             return s;
606         },
607 
608         /**
609          * Returns the elements name, Used in JessieCode.
610          * @returns {String}
611          */
612         getName: function () {
613             return this.name;
614         },
615 
616         /**
617          * Add transformations to this element.
618          * @param {JXG.Transformation|Array} transform Either one {@link JXG.Transformation}
619          * or an array of {@link JXG.Transformation}s.
620          * @returns {JXG.GeometryElement} Reference to the element.
621          */
622         addTransform: function (transform) {
623             return this;
624         },
625 
626         /**
627          * Decides whether an element can be dragged. This is used in
628          * {@link JXG.GeometryElement#setPositionDirectly} methods
629          * where all parent elements are checked if they may be dragged, too.
630          * @private
631          * @returns {boolean}
632          */
633         draggable: function () {
634             return this.isDraggable && !Type.evaluate(this.visProp.fixed) &&
635                 /*!this.visProp.frozen &&*/ this.type !== Const.OBJECT_TYPE_GLIDER;
636         },
637 
638         /**
639          * Translates the object by <tt>(x, y)</tt>. In case the element is defined by points, the defining points are
640          * translated, e.g. a circle constructed by a center point and a point on the circle line.
641          * @param {Number} method The type of coordinates used here.
642          * Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}.
643          * @param {Array} coords array of translation vector.
644          * @returns {JXG.GeometryElement} Reference to the element object.
645          */
646         setPosition: function (method, coords) {
647             var parents = [],
648                 el, i, len, t;
649 
650             if (!Type.exists(this.parents)) {
651                 return this;
652             }
653 
654             len = this.parents.length;
655             for (i = 0; i < len; ++i) {
656                 el = this.board.select(this.parents[i]);
657                 if (Type.isPoint(el)) {
658                     if (!el.draggable()) {
659                         return this;
660                     } else {
661                         parents.push(el);
662                     }
663                 }
664             }
665 
666             if (coords.length === 3) {
667                 coords = coords.slice(1);
668             }
669 
670             t = this.board.create('transform', coords, {type: 'translate'});
671 
672             // We distinguish two cases:
673             // 1) elements which depend on free elements, i.e. arcs and sectors
674             // 2) other elements
675             //
676             // In the first case we simply transform the parents elements
677             // In the second case we add a transform to the element.
678             //
679             len = parents.length;
680             if (len > 0) {
681                 t.applyOnce(parents);
682             } else {
683                 if (this.transformations.length > 0 &&
684                         this.transformations[this.transformations.length - 1].isNumericMatrix) {
685                     this.transformations[this.transformations.length - 1].melt(t);
686                 } else {
687                     this.addTransform(t);
688                 }
689             }
690 
691             /*
692              * If - against the default configuration - defining gliders are marked as
693              * draggable, then their position has to be updated now.
694              */
695             for (i = 0; i < len; ++i) {
696                 if (parents[i].type === Const.OBJECT_TYPE_GLIDER) {
697                     parents[i].updateGlider();
698                 }
699             }
700 
701             return this;
702         },
703 
704         /**
705          * Moves an element by the difference of two coordinates.
706          * @param {Number} method The type of coordinates used here.
707          * Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}.
708          * @param {Array} coords coordinates in screen/user units
709          * @param {Array} oldcoords previous coordinates in screen/user units
710          * @returns {JXG.GeometryElement} this element
711          */
712         setPositionDirectly: function (method, coords, oldcoords) {
713             var c = new Coords(method, coords, this.board, false),
714                 oldc = new Coords(method, oldcoords, this.board, false),
715                 dc = Statistics.subtract(c.usrCoords, oldc.usrCoords);
716 
717             this.setPosition(Const.COORDS_BY_USER, dc);
718 
719             return this;
720         },
721 
722         /**
723          * Array of strings containing the polynomials defining the element.
724          * Used for determining geometric loci the groebner way.
725          * @returns {Array} An array containing polynomials describing the locus of the current object.
726          * @public
727          */
728         generatePolynomial: function () {
729             return [];
730         },
731 
732         /**
733          * Animates properties for that object like stroke or fill color, opacity and maybe
734          * even more later.
735          * @param {Object} hash Object containing properties with target values for the animation.
736          * @param {number} time Number of milliseconds to complete the animation.
737          * @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>
738          * @returns {JXG.GeometryElement} A reference to the object
739          */
740         animate: function (hash, time, options) {
741             options = options || {};
742             var r, p, i,
743                 delay = this.board.attr.animationdelay,
744                 steps = Math.ceil(time / delay),
745                 self = this,
746 
747                 animateColor = function (startRGB, endRGB, property) {
748                     var hsv1, hsv2, sh, ss, sv;
749                     hsv1 = Color.rgb2hsv(startRGB);
750                     hsv2 = Color.rgb2hsv(endRGB);
751 
752                     sh = (hsv2[0] - hsv1[0]) / steps;
753                     ss = (hsv2[1] - hsv1[1]) / steps;
754                     sv = (hsv2[2] - hsv1[2]) / steps;
755                     self.animationData[property] = [];
756 
757                     for (i = 0; i < steps; i++) {
758                         self.animationData[property][steps - i - 1] = Color.hsv2rgb(hsv1[0] + (i + 1) * sh, hsv1[1] + (i + 1) * ss, hsv1[2] + (i + 1) * sv);
759                     }
760                 },
761 
762                 animateFloat = function (start, end, property, round) {
763                     var tmp, s;
764 
765                     start = parseFloat(start);
766                     end = parseFloat(end);
767 
768                     // we can't animate without having valid numbers.
769                     // And parseFloat returns NaN if the given string doesn't contain
770                     // a valid float number.
771                     if (isNaN(start) || isNaN(end)) {
772                         return;
773                     }
774 
775                     s = (end - start) / steps;
776                     self.animationData[property] = [];
777 
778                     for (i = 0; i < steps; i++) {
779                         tmp = start + (i + 1) * s;
780                         self.animationData[property][steps - i - 1] = round ? Math.floor(tmp) : tmp;
781                     }
782                 };
783 
784             this.animationData = {};
785 
786             for (r in hash) {
787                 if (hash.hasOwnProperty(r)) {
788                     p = r.toLowerCase();
789 
790                     switch (p) {
791                     case 'strokecolor':
792                     case 'fillcolor':
793                         animateColor(this.visProp[p], hash[r], p);
794                         break;
795                     case 'size':
796                         if (!Type.isPoint(this)) {
797                             break;
798                         }
799                         animateFloat(this.visProp[p], hash[r], p, true);
800                         break;
801                     case 'strokeopacity':
802                     case 'strokewidth':
803                     case 'fillopacity':
804                         animateFloat(this.visProp[p], hash[r], p, false);
805                         break;
806                     }
807                 }
808             }
809 
810             this.animationCallback = options.callback;
811             this.board.addAnimation(this);
812             return this;
813         },
814 
815         /**
816          * General update method. Should be overwritten by the element itself.
817          * Can be used sometimes to commit changes to the object.
818          * @return {JXG.GeometryElement} Reference to the element
819          */
820         update: function () {
821             if (Type.evaluate(this.visProp.trace)) {
822                 this.cloneToBackground();
823             }
824             return this;
825         },
826 
827         /**
828          * Provide updateRenderer method.
829          * @return {JXG.GeometryElement} Reference to the element
830          * @private
831          */
832         updateRenderer: function () {
833             return this;
834         },
835 
836         /**
837          * Run through the full update chain of an element.
838          * @param  {Boolean} visible Set visibility in case the elements attribute value is 'inherit'. null is allowed.
839          * @return {JXG.GeometryElement} Reference to the element
840          * @private
841          */
842         fullUpdate: function(visible) {
843             return this.prepareUpdate()
844                 .update()
845                 .updateVisibility(visible)
846                 .updateRenderer();
847         },
848 
849         /**
850          * Show the element or hide it. If hidden, it will still exist but not be
851          * visible on the board.
852          * @param  {Boolean} val true: show the element, false: hide the element
853          * @return {JXG.GeometryElement} Reference to the element
854          * @private
855          */
856         setDisplayRendNode: function(val) {
857             var i, len, s, len_s, obj;
858 
859             if (val === undefined) {
860                 val = this.visPropCalc.visible;
861             }
862 
863             if (val === this.visPropOld.visible) {
864                 return this;
865             }
866 
867             // Set display of the element itself
868             this.board.renderer.display(this, val);
869 
870             // Set the visibility of elements which inherit the attribute 'visible'
871             len = this.inherits.length;
872             for (s = 0; s < len; s++) {
873                 obj = this.inherits[s];
874                 if (Type.isArray(obj)) {
875                     len_s = obj.length;
876                     for (i = 0; i < len_s; i++) {
877                         if (Type.exists(obj[i]) && Type.exists(obj[i].rendNode) &&
878                             Type.evaluate(obj[i].visProp.visible) === 'inherit') {
879                             obj[i].setDisplayRendNode(val);
880                         }
881                     }
882                 } else {
883                     if (Type.exists(obj) && Type.exists(obj.rendNode) &&
884                         Type.evaluate(obj.visProp.visible) === 'inherit') {
885                             obj.setDisplayRendNode(val);
886                     }
887                 }
888             }
889 
890             // Set the visibility of the label if it inherits the attribute 'visible'
891             if (this.hasLabel && Type.exists(this.label) && Type.exists(this.label.rendNode)) {
892                 if (Type.evaluate(this.label.visProp.visible) === 'inherit') {
893                     this.label.setDisplayRendNode(val);
894                 }
895             }
896 
897             return this;
898         },
899 
900         /**
901          * Hide the element. It will still exist but not visible on the board.
902          * @return {JXG.GeometryElement} Reference to the element
903          * @deprecated
904          * @private
905          */
906         hideElement: function () {
907             JXG.deprecated('Element.hideElement()', 'Element.setDisplayRendNode()');
908 
909             // TODO: Does override value of  this.visProp.visible
910             this.visPropCalc.visible = this.visProp.visible = false;
911             this.board.renderer.display(this, false);
912 
913             if (Type.exists(this.label) && this.hasLabel) {
914                 this.label.hiddenByParent = true;
915                 if (this.label.visPropCalc.visible) {
916                     this.label.hideElement();
917                 }
918             }
919             return this;
920         },
921 
922         /**
923          * Make the element visible.
924          * @return {JXG.GeometryElement} Reference to the element
925          * @deprecated
926          * @private
927          */
928         showElement: function () {
929             JXG.deprecated('Element.showElement()', 'Element.setDisplayRendNode()');
930 
931             this.visPropCalc.visible = this.visProp.visible = true;
932             this.board.renderer.display(this, true);
933 
934             if (Type.exists(this.label) && this.hasLabel && this.label.hiddenByParent) {
935                 this.label.hiddenByParent = false;
936                 if (!this.label.visPropCalc.visible) {
937                     this.label.showElement().updateRenderer();
938                 }
939             }
940             return this;
941         },
942 
943         /**
944          * Set the visibility of an element. The visibility is influenced by
945          * (listed in ascending priority):
946          * <ol>
947          * <li> The value of the element's attribute 'visible'
948          * <li> The visibility of a parent element. (Example: label)
949          * This overrules the value of the element's attribute value only if
950          * this attribute value of the element is 'inherit'.
951          * <li> being inside of the canvas
952          * </ol>
953          * <p>
954          * This method is called three times for most elements:
955          * <ol>
956          * <li> between {@link JXG.GeometryElement#update}
957          * and {@link JXG.GeometryElement#updateRenderer}. In case the value is 'inherit', nothing is done.
958          * <li> Recursively, called by itself for child elements. Here, 'inherit' is overruled by the parent's value.
959          * <li> In {@link JXG.GeometryElement#updateRenderer}, if the element is outside of the canvas.
960          * </ol>
961          *
962          * @param  {Boolean} parent_val Visibility of the parent element.
963          * @return {JXG.GeometryElement} Reference to the element.
964          * @private
965          */
966         updateVisibility: function(parent_val) {
967             var i, len, s, len_s, obj, val;
968 
969             if (this.needsUpdate) {
970                 // Handle the element
971                 if (parent_val !== undefined) {
972                     this.visPropCalc.visible = parent_val;
973                 } else {
974                     val = Type.evaluate(this.visProp.visible);
975 
976                     // infobox uses hiddenByParent
977                     if (Type.exists(this.hiddenByParent) && this.hiddenByParent) {
978                         val = false;
979                     }
980                     if (val !== 'inherit') {
981                         this.visPropCalc.visible = val;
982                     }
983                 }
984 
985                 // Handle elements which inherit the visibility
986                 len = this.inherits.length;
987                 for (s = 0; s < len; s++) {
988                     obj = this.inherits[s];
989                     if (Type.isArray(obj)) {
990                         len_s = obj.length;
991                         for (i = 0; i < len_s; i++) {
992                             if (Type.exists(obj[i]) /*&& Type.exists(obj[i].rendNode)*/ &&
993                                 Type.evaluate(obj[i].visProp.visible) === 'inherit') {
994                                 obj[i].prepareUpdate().updateVisibility(this.visPropCalc.visible);
995                             }
996                         }
997                     } else {
998                         if (Type.exists(obj) /*&& Type.exists(obj.rendNode)*/ &&
999                             Type.evaluate(obj.visProp.visible) === 'inherit') {
1000                             obj.prepareUpdate().updateVisibility(this.visPropCalc.visible);
1001                         }
1002                     }
1003                 }
1004 
1005                 // Handle the label if it inherits the visibility
1006                 if (Type.exists(this.label)  && Type.exists(this.label.visProp) &&
1007                     Type.evaluate(this.label.visProp.visible)) {
1008                     this.label.prepareUpdate().updateVisibility(this.visPropCalc.visible);
1009                 }
1010             }
1011             return this;
1012         },
1013 
1014         /**
1015          * Sets the value of property <tt>property</tt> to <tt>value</tt>.
1016          * @param {String} property The property's name.
1017          * @param value The new value
1018          * @private
1019          */
1020         _set: function (property, value) {
1021             property = property.toLocaleLowerCase();
1022 
1023             // Search for entries in visProp with "color" as part of the property name
1024             // and containing a RGBA string
1025             if (this.visProp.hasOwnProperty(property) &&
1026                   property.indexOf('color') >= 0 &&
1027                   Type.isString(value) &&
1028                   value.length === 9 &&
1029                   value.charAt(0) === '#') {
1030 
1031                 value = Color.rgba2rgbo(value);
1032                 this.visProp[property] = value[0];
1033                 // Previously: *=. But then, we can only decrease opacity.
1034                 this.visProp[property.replace('color', 'opacity')] = value[1];
1035             } else {
1036                 this.visProp[property] = value;
1037             }
1038         },
1039 
1040         /**
1041          * Resolves property shortcuts like <tt>color</tt> and expands them, e.g. <tt>strokeColor</tt> and <tt>fillColor</tt>.
1042          * Writes the expanded properties back to the given <tt>properties</tt>.
1043          * @param {Object} properties
1044          * @returns {Object} The given parameter with shortcuts expanded.
1045          */
1046         resolveShortcuts: function (properties) {
1047             var key, i;
1048 
1049             for (key in Options.shortcuts) {
1050                 if (Options.shortcuts.hasOwnProperty(key)) {
1051                     if (Type.exists(properties[key])) {
1052                         for (i = 0; i < Options.shortcuts[key].length; i++) {
1053                             if (!Type.exists(properties[Options.shortcuts[key][i]])) {
1054                                 properties[Options.shortcuts[key][i]] = properties[key];
1055                             }
1056                         }
1057                     }
1058                 }
1059             }
1060             return properties;
1061         },
1062 
1063         /**
1064          * Sets a label and its text
1065          * If label doesn't exist, it creates one
1066          * @param {String} str
1067          */
1068         setLabel: function (str) {
1069             if (!this.hasLabel) {
1070                 this.setAttribute({'withlabel': true});
1071             }
1072             this.setLabelText(str);
1073         },
1074 
1075         /**
1076          * Updates the element's label text, strips all html.
1077          * @param {String} str
1078          */
1079         setLabelText: function (str) {
1080 
1081             if (Type.exists(this.label)) {
1082                 str = str.replace(/</g, '<').replace(/>/g, '>');
1083                 this.label.setText(str);
1084             }
1085 
1086             return this;
1087         },
1088 
1089         /**
1090          * Updates the element's label text and the element's attribute "name", strips all html.
1091          * @param {String} str
1092          */
1093         setName: function (str) {
1094             str = str.replace(/</g, '<').replace(/>/g, '>');
1095             if (this.elType !== 'slider') {
1096                 this.setLabelText(str);
1097             }
1098             this.setAttribute({name: str});
1099         },
1100 
1101         /**
1102          * Deprecated alias for {@link JXG.GeometryElement#setAttribute}.
1103          * @deprecated Use {@link JXG.GeometryElement#setAttribute}.
1104          */
1105         setProperty: function () {
1106             JXG.deprecated('setProperty()', 'setAttribute()');
1107             this.setAttribute.apply(this, arguments);
1108         },
1109 
1110         /**
1111          * Sets an arbitrary number of attributes.
1112          * @param {Object} attributes An object with attributes.
1113          * @function
1114          * @example
1115          * // Set property directly on creation of an element using the attributes object parameter
1116          * var board = JXG.JSXGraph.initBoard('jxgbox', {boundingbox: [-1, 5, 5, 1]};
1117          * var p = board.create('point', [2, 2], {visible: false});
1118          *
1119          * // Now make this point visible and fixed:
1120          * p.setAttribute({
1121          *     fixed: true,
1122          *     visible: true
1123          * });
1124          */
1125         setAttribute: function (attributes) {
1126             var i, j, le, key, value, arg, opacity, pair, oldvalue,
1127                 properties = {};
1128 
1129             // normalize the user input
1130             for (i = 0; i < arguments.length; i++) {
1131                 arg = arguments[i];
1132                 if (Type.isString(arg)) {
1133                     // pairRaw is string of the form 'key:value'
1134                     pair = arg.split(':');
1135                     properties[Type.trim(pair[0])] = Type.trim(pair[1]);
1136                 } else if (!Type.isArray(arg)) {
1137                     // pairRaw consists of objects of the form {key1:value1,key2:value2,...}
1138                     JXG.extend(properties, arg);
1139                 } else {
1140                     // pairRaw consists of array [key,value]
1141                     properties[arg[0]] = arg[1];
1142                 }
1143             }
1144 
1145             // handle shortcuts
1146             properties = this.resolveShortcuts(properties);
1147 
1148             for (i in properties) {
1149                 if (properties.hasOwnProperty(i)) {
1150                     key = i.replace(/\s+/g, '').toLowerCase();
1151                     value = properties[i];
1152                     oldvalue = this.visProp[key];
1153 
1154                     // This handles the subobjects, if the key:value pairs are contained in an object.
1155                     // Example
1156                     // ticks.setAttribute({
1157                     //      strokeColor: 'blue',
1158                     //      label: {
1159                     //          visible: false
1160                     //      }
1161                     // })
1162                     // Now, only the supplied label attributes are overwritten.
1163                     // Otherwise, the the value of label would be {visible:false} only.
1164                     if (Type.isObject(value) && Type.exists(this.visProp[key])) {
1165                         this.visProp[key] = Type.merge(this.visProp[key], value);
1166 
1167                         if (this.type === Const.OBJECT_TYPE_TICKS && Type.exists(this.labels)) {
1168                             le = this.labels.length;
1169                             for (j = 0; j < le; j++) {
1170                                 this.labels[j].setAttribute(value);
1171                             }
1172                         }
1173                         continue;
1174                     }
1175 
1176                     switch (key) {
1177                     case 'name':
1178                         oldvalue = this.name;
1179                         delete this.board.elementsByName[this.name];
1180                         this.name = value;
1181                         this.board.elementsByName[this.name] = this;
1182                         break;
1183                     case 'needsregularupdate':
1184                         this.needsRegularUpdate = !(value === 'false' || value === false);
1185                         this.board.renderer.setBuffering(this, this.needsRegularUpdate ? 'auto' : 'static');
1186                         break;
1187                     case 'labelcolor':
1188                         value = Color.rgba2rgbo(value);
1189                         opacity = value[1];
1190                         value = value[0];
1191                         if (opacity === 0) {
1192                             if (Type.exists(this.label) && this.hasLabel) {
1193                                 this.label.hideElement();
1194                             }
1195                         }
1196                         if (Type.exists(this.label) && this.hasLabel) {
1197                             this.label.visProp.strokecolor = value;
1198                             this.board.renderer.setObjectStrokeColor(this.label,
1199                                 value, opacity);
1200                         }
1201                         if (this.elementClass === Const.OBJECT_CLASS_TEXT) {
1202                             this.visProp.strokecolor = value;
1203                             this.visProp.strokeopacity = opacity;
1204                             this.board.renderer.setObjectStrokeColor(this,
1205                                 value, opacity);
1206                         }
1207                         break;
1208                     case 'infoboxtext':
1209                         if (Type.isString(value)) {
1210                             this.infoboxText = value;
1211                         } else {
1212                             this.infoboxText = false;
1213                         }
1214                         break;
1215                     case 'visible':
1216                         if (value === 'false') {
1217                             this.visProp.visible = false;
1218                         } else if (value === 'true') {
1219                             this.visProp.visible = true;
1220                         } else {
1221                             this.visProp.visible = value;
1222                         }
1223 
1224                         this.setDisplayRendNode(Type.evaluate(this.visProp.visible));
1225                         break;
1226                     case 'face':
1227                         if (Type.isPoint(this)) {
1228                             this.visProp.face = value;
1229                             this.board.renderer.changePointStyle(this);
1230                         }
1231                         break;
1232                     case 'trace':
1233                         if (value === 'false' || value === false) {
1234                             this.clearTrace();
1235                             this.visProp.trace = false;
1236                         } else {
1237                             this.visProp.trace = true;
1238                         }
1239                         break;
1240                     case 'gradient':
1241                         this.visProp.gradient = value;
1242                         this.board.renderer.setGradient(this);
1243                         break;
1244                     case 'gradientsecondcolor':
1245                         value = Color.rgba2rgbo(value);
1246                         this.visProp.gradientsecondcolor = value[0];
1247                         this.visProp.gradientsecondopacity = value[1];
1248                         this.board.renderer.updateGradient(this);
1249                         break;
1250                     case 'gradientsecondopacity':
1251                         this.visProp.gradientsecondopacity = value;
1252                         this.board.renderer.updateGradient(this);
1253                         break;
1254                     case 'withlabel':
1255                         this.visProp.withlabel = value;
1256                         if (!Type.evaluate(value)) {
1257                             if (this.label && this.hasLabel) {
1258                                 this.label.hideElement();
1259                             }
1260                         } else {
1261                             if (!this.label) {
1262                                 this.createLabel();
1263                             }
1264                             this.label.showElement();
1265                             //this.label.setDisplayRendNode(Type.evaluate(this.visProp.visible));
1266                         }
1267                         this.hasLabel = value;
1268                         break;
1269                     case 'radius':
1270                         if (this.type === Const.OBJECT_TYPE_ANGLE || this.type === Const.OBJECT_TYPE_SECTOR) {
1271                             this.setRadius(value);
1272                         }
1273                         break;
1274                     case 'rotate':
1275                         if ((this.elementClass === Const.OBJECT_CLASS_TEXT &&
1276                              Type.evaluate(this.visProp.display) === 'internal') ||
1277                             this.type === Const.OBJECT_TYPE_IMAGE) {
1278                             this.addRotation(value);
1279                         }
1280                         break;
1281                     case 'ticksdistance':
1282                         if (this.type === Const.OBJECT_TYPE_TICKS && Type.isNumber(value)) {
1283                             this.ticksFunction = this.makeTicksFunction(value);
1284                         }
1285                         break;
1286                     case 'generatelabelvalue':
1287                         if (this.type === Const.OBJECT_TYPE_TICKS && Type.isFunction(value)) {
1288                             this.generateLabelValue = value;
1289                         }
1290                         break;
1291                     case 'onpolygon':
1292                         if (this.type === Const.OBJECT_TYPE_GLIDER) {
1293                             this.onPolygon = !!value;
1294                         }
1295                         break;
1296                     case 'disabled':
1297                         // button, checkbox, input. Is not available on initial call.
1298                         if (Type.exists(this.rendNodeTag)) {
1299                             this.rendNodeTag.disabled = !!value;
1300                         }
1301                         break;
1302                     case 'checked':
1303                         // checkbox Is not available on initial call.
1304                         if (Type.exists(this.rendNodeTag)) {
1305                             this.rendNodeCheckbox.checked = !!value;
1306                         }
1307                             break;
1308                     case 'maxlength':
1309                         // input. Is not available on initial call.
1310                         if (Type.exists(this.rendNodeTag)) {
1311                             this.rendNodeTag.maxlength = !!value;
1312                         }
1313                         break;
1314                     default:
1315                         if (Type.exists(this.visProp[key]) &&
1316                             (!JXG.Validator[key] ||
1317                                 (JXG.Validator[key] && JXG.Validator[key](value)) ||
1318                                 (JXG.Validator[key] && Type.isFunction(value) && JXG.Validator[key](value()))
1319                             )
1320                         ) {
1321                             value = value.toLowerCase && value.toLowerCase() === 'false' ? false : value;
1322                             this._set(key, value);
1323                         }
1324                         break;
1325                     }
1326                     this.triggerEventHandlers(['attribute:' + key], [oldvalue, value, this]);
1327                 }
1328             }
1329 
1330             this.triggerEventHandlers(['attribute'], [properties, this]);
1331 
1332             if (!Type.evaluate(this.visProp.needsregularupdate)) {
1333                 this.board.fullUpdate();
1334             } else {
1335                 this.board.update(this);
1336             }
1337 
1338             return this;
1339         },
1340 
1341         /**
1342          * Deprecated alias for {@link JXG.GeometryElement#getAttribute}.
1343          * @deprecated Use {@link JXG.GeometryElement#getAttribute}.
1344          */
1345         getProperty: function () {
1346             JXG.deprecated('getProperty()', 'getAttribute()');
1347             this.getProperty.apply(this, arguments);
1348         },
1349 
1350         /**
1351          * Get the value of the property <tt>key</tt>.
1352          * @param {String} key The name of the property you are looking for
1353          * @returns The value of the property
1354          */
1355         getAttribute: function (key) {
1356             var result;
1357             key = key.toLowerCase();
1358 
1359             switch (key) {
1360             case 'needsregularupdate':
1361                 result = this.needsRegularUpdate;
1362                 break;
1363             case 'labelcolor':
1364                 result = this.label.visProp.strokecolor;
1365                 break;
1366             case 'infoboxtext':
1367                 result = this.infoboxText;
1368                 break;
1369             case 'withlabel':
1370                 result = this.hasLabel;
1371                 break;
1372             default:
1373                 result = this.visProp[key];
1374                 break;
1375             }
1376 
1377             return result;
1378         },
1379 
1380         /**
1381          * Set the dash style of an object. See {@link JXG.GeometryElement#dash}
1382          * for a list of available dash styles.
1383          * You should use {@link JXG.GeometryElement#setAttribute} instead of this method.
1384          *
1385          * @param {number} dash Indicates the new dash style
1386          * @private
1387          */
1388         setDash: function (dash) {
1389             this.setAttribute({dash: dash});
1390             return this;
1391         },
1392 
1393         /**
1394          * Notify all child elements for updates.
1395          * @private
1396          */
1397         prepareUpdate: function () {
1398             this.needsUpdate = true;
1399             return this;
1400         },
1401 
1402         /**
1403          * Removes the element from the construction.  This only removes the SVG or VML node of the element and its label (if available) from
1404          * the renderer, to remove the element completely you should use {@link JXG.Board#removeObject}.
1405          */
1406         remove: function () {
1407             this.board.renderer.remove(this.board.renderer.getElementById(this.id));
1408 
1409             if (this.hasLabel) {
1410                 this.board.renderer.remove(this.board.renderer.getElementById(this.label.id));
1411             }
1412             return this;
1413         },
1414 
1415         /**
1416          * Returns the coords object where a text that is bound to the element shall be drawn.
1417          * Differs in some cases from the values that getLabelAnchor returns.
1418          * @returns {JXG.Coords} JXG.Coords Place where the text shall be drawn.
1419          * @see JXG.GeometryElement#getLabelAnchor
1420          */
1421         getTextAnchor: function () {
1422             return new Coords(Const.COORDS_BY_USER, [0, 0], this.board);
1423         },
1424 
1425         /**
1426          * Returns the coords object where the label of the element shall be drawn.
1427          * Differs in some cases from the values that getTextAnchor returns.
1428          * @returns {JXG.Coords} JXG.Coords Place where the text shall be drawn.
1429          * @see JXG.GeometryElement#getTextAnchor
1430          */
1431         getLabelAnchor: function () {
1432             return new Coords(Const.COORDS_BY_USER, [0, 0], this.board);
1433         },
1434 
1435         /**
1436          * Determines whether the element has arrows at start or end of the arc.
1437          * If it is set to be a "typical" vector, ie lastArrow == true,
1438          * then the element.type is set to VECTOR.
1439          * @param {Boolean} firstArrow True if there is an arrow at the start of the arc, false otherwise.
1440          * @param {Boolean} lastArrow True if there is an arrow at the end of the arc, false otherwise.
1441          */
1442         setArrow: function (firstArrow, lastArrow) {
1443             this.visProp.firstarrow = firstArrow;
1444             this.visProp.lastarrow = lastArrow;
1445             if (lastArrow) {
1446                 this.type = Const.OBJECT_TYPE_VECTOR;
1447                 this.elType = 'arrow';
1448             }
1449 
1450             this.prepareUpdate().update().updateVisibility().updateRenderer();
1451             return this;
1452         },
1453 
1454         /**
1455          * Creates a gradient nodes in the renderer.
1456          * @see JXG.SVGRenderer#setGradient
1457          * @private
1458          */
1459         createGradient: function () {
1460             var ev_g = Type.evaluate(this.visProp.gradient);
1461             if (ev_g === 'linear' || ev_g === 'radial') {
1462                 this.board.renderer.setGradient(this);
1463             }
1464         },
1465 
1466          /**
1467          * Creates a label element for this geometry element.
1468          * @see #addLabelToElement
1469          */
1470         createLabel: function () {
1471             var attr,
1472                 that = this;
1473 
1474             // this is a dirty hack to resolve the text-dependency. If there is no text element available,
1475             // just don't create a label. This method is usually not called by a user, so we won't throw
1476             // an exception here and simply output a warning via JXG.debug.
1477             if (JXG.elements.text) {
1478                 attr =  Type.deepCopy(this.visProp.label, null);
1479                 attr.id = this.id + 'Label';
1480                 attr.isLabel = true;
1481                 attr.anchor = this;
1482                 attr.priv = this.visProp.priv;
1483 
1484                 if (this.visProp.withlabel) {
1485                     this.label = JXG.elements.text(this.board, [0, 0, function () {
1486                         if (Type.isFunction(that.name)) {
1487                             return that.name();
1488                         }
1489                         return that.name;
1490                     }], attr);
1491                     this.label.needsUpdate = true;
1492                     this.label.dump = false;
1493                     this.label.update();
1494 
1495                     // if (!Type.evaluate(this.visProp.visible)) {
1496                     //     this.label.hiddenByParent = true;
1497                     //     this.label.visPropCalc.visible = false;
1498                     // }
1499                     // this.label.fullUpdate();
1500                     this.hasLabel = true;
1501                 }
1502             } else {
1503                 JXG.debug('JSXGraph: Can\'t create label: text element is not available. Make sure you include base/text');
1504             }
1505 
1506             return this;
1507         },
1508 
1509         /**
1510          * Highlights the element.
1511          * @param {Boolean} [force=false] Force the highlighting
1512          * @returns {JXG.Board}
1513          */
1514         highlight: function (force) {
1515             force = Type.def(force, false);
1516             // I know, we have the JXG.Board.highlightedObjects AND JXG.GeometryElement.highlighted and YES we need both.
1517             // Board.highlightedObjects is for the internal highlighting and GeometryElement.highlighted is for user highlighting
1518             // initiated by the user, e.g. through custom DOM events. We can't just pick one because this would break user
1519             // defined highlighting in many ways:
1520             //  * if overriding the highlight() methods the user had to handle the highlightedObjects stuff, otherwise he'd break
1521             //    everything (e.g. the pie chart example http://jsxgraph.uni-bayreuth.de/wiki/index.php/Pie_chart (not exactly
1522             //    user defined but for this type of chart the highlight method was overridden and not adjusted to the changes in here)
1523             //    where it just kept highlighting until the radius of the pie was far beyond infinity...
1524             //  * user defined highlighting would get pointless, everytime the user highlights something using .highlight(), it would get
1525             //    dehighlighted immediately, because highlight puts the element into highlightedObjects and from there it gets dehighlighted
1526             //    through dehighlightAll.
1527 
1528             // highlight only if not highlighted
1529             if (Type.evaluate(this.visProp.highlight) && (!this.highlighted || force)) {
1530                 this.highlighted = true;
1531                 this.board.highlightedObjects[this.id] = this;
1532                 this.board.renderer.highlight(this);
1533             }
1534             return this;
1535         },
1536 
1537         /**
1538          * Uses the "normal" properties of the element.
1539          * @returns {JXG.Board}
1540          */
1541         noHighlight: function () {
1542             // see comment in JXG.GeometryElement.highlight()
1543 
1544             // dehighlight only if not highlighted
1545             if (this.highlighted) {
1546                 this.highlighted = false;
1547                 delete this.board.highlightedObjects[this.id];
1548                 this.board.renderer.noHighlight(this);
1549             }
1550             return this;
1551         },
1552 
1553         /**
1554          * Removes all objects generated by the trace function.
1555          */
1556         clearTrace: function () {
1557             var obj;
1558 
1559             for (obj in this.traces) {
1560                 if (this.traces.hasOwnProperty(obj)) {
1561                     this.board.renderer.remove(this.traces[obj]);
1562                 }
1563             }
1564 
1565             this.numTraces = 0;
1566             return this;
1567         },
1568 
1569         /**
1570          * Copy the element to background. This is used for tracing elements.
1571          * @returns {JXG.GeometryElement} A reference to the element
1572          */
1573         cloneToBackground: function () {
1574             return this;
1575         },
1576 
1577         /**
1578          * Dimensions of the smallest rectangle enclosing the element.
1579          * @returns {Array} The coordinates of the enclosing rectangle in a format
1580          * like the bounding box in {@link JXG.Board#setBoundingBox}.
1581          */
1582         bounds: function () {
1583             return [0, 0, 0, 0];
1584         },
1585 
1586         /**
1587          * Normalize the element's standard form.
1588          * @private
1589          */
1590         normalize: function () {
1591             this.stdform = Mat.normalize(this.stdform);
1592             return this;
1593         },
1594 
1595         /**
1596          * EXPERIMENTAL. Generate JSON object code of visProp and other properties.
1597          * @type string
1598          * @private
1599          * @ignore
1600          * @returns JSON string containing element's properties.
1601          */
1602         toJSON: function () {
1603             var vis, key,
1604                 json = ['{"name":', this.name];
1605 
1606             json.push(', ' + '"id":' + this.id);
1607 
1608             vis = [];
1609             for (key in this.visProp) {
1610                 if (this.visProp.hasOwnProperty(key)) {
1611                     if (Type.exists(this.visProp[key])) {
1612                         vis.push('"' + key + '":' + this.visProp[key]);
1613                     }
1614                 }
1615             }
1616             json.push(', "visProp":{' + vis.toString() + '}');
1617             json.push('}');
1618 
1619             return json.join('');
1620         },
1621 
1622 
1623         /**
1624          * Rotate texts or images by a given degree. Works only for texts where JXG.Text#display equal to "internal".
1625          * @param {number} angle The degree of the rotation (90 means vertical text).
1626          * @see JXG.GeometryElement#rotate
1627          */
1628         addRotation: function (angle) {
1629             var tOffInv, tOff, tS, tSInv, tRot,
1630                 that = this;
1631 
1632             if (((this.elementClass === Const.OBJECT_CLASS_TEXT &&
1633                     Type.evaluate(this.visProp.display) === 'internal') ||
1634                     this.type === Const.OBJECT_TYPE_IMAGE) && angle !== 0) {
1635 
1636                 tOffInv = this.board.create('transform', [
1637                     function () {
1638                         return -that.X();
1639                     }, function () {
1640                         return -that.Y();
1641                     }
1642                 ], {type: 'translate'});
1643 
1644                 tOff = this.board.create('transform', [
1645                     function () {
1646                         return that.X();
1647                     }, function () {
1648                         return that.Y();
1649                     }
1650                 ], {type: 'translate'});
1651 
1652                 tS = this.board.create('transform', [
1653                     function () {
1654                         return that.board.unitX / that.board.unitY;
1655                     }, function () {
1656                         return 1;
1657                     }
1658                 ], {type: 'scale'});
1659 
1660                 tSInv = this.board.create('transform', [
1661                     function () {
1662                         return that.board.unitY / that.board.unitX;
1663                     }, function () {
1664                         return 1;
1665                     }
1666                 ], {type: 'scale'});
1667 
1668                 tRot = this.board.create('transform', [angle * Math.PI / 180], {type: 'rotate'});
1669 
1670                 tOffInv.bindTo(this);
1671                 tS.bindTo(this);
1672                 tRot.bindTo(this);
1673                 tSInv.bindTo(this);
1674                 tOff.bindTo(this);
1675             }
1676 
1677             return this;
1678         },
1679 
1680         /**
1681          * Set the highlightStrokeColor of an element
1682          * @param {String} sColor String which determines the stroke color of an object when its highlighted.
1683          * @see JXG.GeometryElement#highlightStrokeColor
1684          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1685          */
1686         highlightStrokeColor: function (sColor) {
1687             JXG.deprecated('highlightStrokeColor()', 'setAttribute()');
1688             this.setAttribute({highlightStrokeColor: sColor});
1689             return this;
1690         },
1691 
1692         /**
1693          * Set the strokeColor of an element
1694          * @param {String} sColor String which determines the stroke color of an object.
1695          * @see JXG.GeometryElement#strokeColor
1696          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1697          */
1698         strokeColor: function (sColor) {
1699             JXG.deprecated('strokeColor()', 'setAttribute()');
1700             this.setAttribute({strokeColor: sColor});
1701             return this;
1702         },
1703 
1704         /**
1705          * Set the strokeWidth of an element
1706          * @param {Number} width Integer which determines the stroke width of an outline.
1707          * @see JXG.GeometryElement#strokeWidth
1708          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1709          */
1710         strokeWidth: function (width) {
1711             JXG.deprecated('strokeWidth()', 'setAttribute()');
1712             this.setAttribute({strokeWidth: width});
1713             return this;
1714         },
1715 
1716 
1717         /**
1718          * Set the fillColor of an element
1719          * @param {String} fColor String which determines the fill color of an object.
1720          * @see JXG.GeometryElement#fillColor
1721          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1722          */
1723         fillColor: function (fColor) {
1724             JXG.deprecated('fillColor()', 'setAttribute()');
1725             this.setAttribute({fillColor: fColor});
1726             return this;
1727         },
1728 
1729         /**
1730          * Set the highlightFillColor of an element
1731          * @param {String} fColor String which determines the fill color of an object when its highlighted.
1732          * @see JXG.GeometryElement#highlightFillColor
1733          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1734          */
1735         highlightFillColor: function (fColor) {
1736             JXG.deprecated('highlightFillColor()', 'setAttribute()');
1737             this.setAttribute({highlightFillColor: fColor});
1738             return this;
1739         },
1740 
1741         /**
1742          * Set the labelColor of an element
1743          * @param {String} lColor String which determines the text color of an object's label.
1744          * @see JXG.GeometryElement#labelColor
1745          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1746          */
1747         labelColor: function (lColor) {
1748             JXG.deprecated('labelColor()', 'setAttribute()');
1749             this.setAttribute({labelColor: lColor});
1750             return this;
1751         },
1752 
1753         /**
1754          * Set the dash type of an element
1755          * @param {Number} d Integer which determines the way of dashing an element's outline.
1756          * @see JXG.GeometryElement#dash
1757          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1758          */
1759         dash: function (d) {
1760             JXG.deprecated('dash()', 'setAttribute()');
1761             this.setAttribute({dash: d});
1762             return this;
1763         },
1764 
1765         /**
1766          * Set the visibility of an element
1767          * @param {Boolean} v Boolean which determines whether the element is drawn.
1768          * @see JXG.GeometryElement#visible
1769          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1770          */
1771         visible: function (v) {
1772             JXG.deprecated('visible()', 'setAttribute()');
1773             this.setAttribute({visible: v});
1774             return this;
1775         },
1776 
1777         /**
1778          * Set the shadow of an element
1779          * @param {Boolean} s Boolean which determines whether the element has a shadow or not.
1780          * @see JXG.GeometryElement#shadow
1781          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1782          */
1783         shadow: function (s) {
1784             JXG.deprecated('shadow()', 'setAttribute()');
1785             this.setAttribute({shadow: s});
1786             return this;
1787         },
1788 
1789         /**
1790          * The type of the element as used in {@link JXG.Board#create}.
1791          * @returns {String}
1792          */
1793         getType: function () {
1794             return this.elType;
1795         },
1796 
1797         /**
1798          * List of the element ids resp. values used as parents in {@link JXG.Board#create}.
1799          * @returns {Array}
1800          */
1801         getParents: function () {
1802             return Type.isArray(this.parents) ? this.parents : [];
1803         },
1804 
1805         /**
1806          * Snaps the element to the grid. Only works for points, lines and circles. Points will snap to the grid
1807          * as defined in their properties {@link JXG.Point#snapSizeX} and {@link JXG.Point#snapSizeY}. Lines and circles
1808          * will snap their parent points to the grid, if they have {@link JXG.Point#snapToGrid} set to true.
1809          * @returns {JXG.GeometryElement} Reference to the element.
1810          */
1811         snapToGrid: function () {
1812             return this;
1813         },
1814 
1815         /**
1816          * Snaps the element to points. Only works for points. Points will snap to the next point
1817          * as defined in their properties {@link JXG.Point#attractorDistance} and {@link JXG.Point#attractorUnit}.
1818          * Lines and circles
1819          * will snap their parent points to points.
1820          * @returns {JXG.GeometryElement} Reference to the element.
1821          */
1822         snapToPoints: function () {
1823             return this;
1824         },
1825 
1826         /**
1827          * Retrieve a copy of the current visProp.
1828          * @returns {Object}
1829          */
1830         getAttributes: function () {
1831             var attributes = Type.deepCopy(this.visProp),
1832                 /*
1833                 cleanThis = ['attractors', 'snatchdistance', 'traceattributes', 'frozen',
1834                     'shadow', 'gradientangle', 'gradientsecondopacity', 'gradientpositionx', 'gradientpositiony',
1835                     'needsregularupdate', 'zoom', 'layer', 'offset'],
1836                 */
1837                 cleanThis = [],
1838                 i, len = cleanThis.length;
1839 
1840             attributes.id = this.id;
1841             attributes.name = this.name;
1842 
1843             for (i = 0; i < len; i++) {
1844                 delete attributes[cleanThis[i]];
1845             }
1846 
1847             return attributes;
1848         },
1849 
1850         /**
1851          * Checks whether (x,y) is near the element.
1852          * @param {Number} x Coordinate in x direction, screen coordinates.
1853          * @param {Number} y Coordinate in y direction, screen coordinates.
1854          * @returns {Boolean} True if (x,y) is near the element, False otherwise.
1855          */
1856         hasPoint: function (x, y) {
1857             return false;
1858         },
1859 
1860         /**
1861          * Move an element to its nearest grid point.
1862          * The function uses the coords object of the element as
1863          * its actual position. If there is no coords object, nothing is done.
1864          * @param {Boolean} force force snapping independent from what the snaptogrid attribute says
1865          * @param {Boolean} fromParent True if the drag comes from a child element. This is the case if a line
1866          *    through two points is dragged. In this case we do not try to force the points to stay inside of
1867          *    the visible board, but the distance between the two points stays constant.
1868          * @returns {JXG.GeometryElement} Reference to this element
1869          */
1870         handleSnapToGrid: function (force, fromParent) {
1871             var x, y, ticks,
1872                 //i, len, g, el, p,
1873                 boardBB,
1874                 needsSnapToGrid = false,
1875                 sX = Type.evaluate(this.visProp.snapsizex),
1876                 sY = Type.evaluate(this.visProp.snapsizey);
1877 
1878             if (!Type.exists(this.coords)) {
1879                 return this;
1880             }
1881 
1882             needsSnapToGrid = Type.evaluate(this.visProp.snaptogrid) || force === true;
1883 
1884             if (needsSnapToGrid) {
1885                 x = this.coords.usrCoords[1];
1886                 y = this.coords.usrCoords[2];
1887 
1888                 if (sX <= 0 && this.board.defaultAxes && this.board.defaultAxes.x.defaultTicks) {
1889                     ticks = this.board.defaultAxes.x.defaultTicks;
1890                     sX = ticks.ticksDelta * (Type.evaluate(ticks.visProp.minorticks) + 1);
1891                 }
1892 
1893                 if (sY <= 0 && this.board.defaultAxes && this.board.defaultAxes.y.defaultTicks) {
1894                     ticks = this.board.defaultAxes.y.defaultTicks;
1895                     sY = ticks.ticksDelta * (Type.evaluate(ticks.visProp.minorticks) + 1);
1896                 }
1897 
1898                 // if no valid snap sizes are available, don't change the coords.
1899                 if (sX > 0 && sY > 0) {
1900                     boardBB = this.board.getBoundingBox();
1901                     x = Math.round(x / sX) * sX;
1902                     y = Math.round(y / sY) * sY;
1903 
1904                     // checking whether x and y are still within boundingBox,
1905                     // if not, adjust them to remain within the board
1906                     if (!fromParent) {
1907                         if (x < boardBB[0]) {
1908                             x += sX;
1909                         } else if (x > boardBB[2]) {
1910                             x -= sX;
1911                         }
1912 
1913                         if (y < boardBB[3]) {
1914                             y += sY;
1915                         } else if (y > boardBB[1]) {
1916                             y -= sY;
1917                         }
1918                     }
1919                     this.coords.setCoordinates(Const.COORDS_BY_USER, [x, y]);
1920                 }
1921             }
1922             return this;
1923         },
1924 
1925         /**
1926          * Alias of {@link JXG.EventEmitter.on}.
1927          *
1928          * @name addEvent
1929          * @memberof JXG.GeometryElement
1930          * @function
1931          */
1932         addEvent: JXG.shortcut(JXG.GeometryElement.prototype, 'on'),
1933 
1934         /**
1935          * Alias of {@link JXG.EventEmitter.off}.
1936          *
1937          * @name removeEvent
1938          * @memberof JXG.GeometryElement
1939          * @function
1940          */
1941         removeEvent: JXG.shortcut(JXG.GeometryElement.prototype, 'off'),
1942 
1943         /* **************************
1944          *     EVENT DEFINITION
1945          * for documentation purposes
1946          * ************************** */
1947 
1948         //region Event handler documentation
1949         /**
1950          * @event
1951          * @description This event is fired whenever the user is hovering over an element.
1952          * @name JXG.GeometryElement#over
1953          * @param {Event} e The browser's event object.
1954          */
1955         __evt__over: function (e) { },
1956 
1957         /**
1958          * @event
1959          * @description This event is fired whenever the user puts the mouse over an element.
1960          * @name JXG.GeometryElement#mouseover
1961          * @param {Event} e The browser's event object.
1962          */
1963         __evt__mouseover: function (e) { },
1964 
1965         /**
1966          * @event
1967          * @description This event is fired whenever the user is leaving an element.
1968          * @name JXG.GeometryElement#out
1969          * @param {Event} e The browser's event object.
1970          */
1971         __evt__out: function (e) { },
1972 
1973         /**
1974          * @event
1975          * @description This event is fired whenever the user puts the mouse away from an element.
1976          * @name JXG.GeometryElement#mouseout
1977          * @param {Event} e The browser's event object.
1978          */
1979         __evt__mouseout: function (e) { },
1980 
1981         /**
1982          * @event
1983          * @description This event is fired whenever the user is moving over an element.
1984          * @name JXG.GeometryElement#move
1985          * @param {Event} e The browser's event object.
1986          */
1987         __evt__move: function (e) { },
1988 
1989         /**
1990          * @event
1991          * @description This event is fired whenever the user is moving the mouse over an element.
1992          * @name JXG.GeometryElement#mousemove
1993          * @param {Event} e The browser's event object.
1994          */
1995         __evt__mousemove: function (e) { },
1996 
1997         /**
1998          * @event
1999          * @description This event is fired whenever the user drags an element.
2000          * @name JXG.GeometryElement#drag
2001          * @param {Event} e The browser's event object.
2002          */
2003         __evt__drag: function (e) { },
2004 
2005         /**
2006          * @event
2007          * @description This event is fired whenever the user drags the element with a mouse.
2008          * @name JXG.GeometryElement#mousedrag
2009          * @param {Event} e The browser's event object.
2010          */
2011         __evt__mousedrag: function (e) { },
2012 
2013         /**
2014          * @event
2015          * @description This event is fired whenever the user drags the element on a touch device.
2016          * @name JXG.GeometryElement#touchdrag
2017          * @param {Event} e The browser's event object.
2018          */
2019         __evt__touchdrag: function (e) { },
2020 
2021         /**
2022          * @event
2023          * @description Whenever the user starts to touch or click an element.
2024          * @name JXG.GeometryElement#down
2025          * @param {Event} e The browser's event object.
2026          */
2027         __evt__down: function (e) { },
2028 
2029         /**
2030          * @event
2031          * @description Whenever the user starts to click an element.
2032          * @name JXG.GeometryElement#mousedown
2033          * @param {Event} e The browser's event object.
2034          */
2035         __evt__mousedown: function (e) { },
2036 
2037         /**
2038          * @event
2039          * @description Whenever the user starts to touch an element.
2040          * @name JXG.GeometryElement#touchdown
2041          * @param {Event} e The browser's event object.
2042          */
2043         __evt__touchdown: function (e) { },
2044 
2045         /**
2046          * @event
2047          * @description Whenever the user stops to touch or click an element.
2048          * @name JXG.GeometryElement#up
2049          * @param {Event} e The browser's event object.
2050          */
2051         __evt__up: function (e) { },
2052 
2053         /**
2054          * @event
2055          * @description Whenever the user releases the mousebutton over an element.
2056          * @name JXG.GeometryElement#mouseup
2057          * @param {Event} e The browser's event object.
2058          */
2059         __evt__mouseup: function (e) { },
2060 
2061         /**
2062          * @event
2063          * @description Whenever the user stops touching an element.
2064          * @name JXG.GeometryElement#touchup
2065          * @param {Event} e The browser's event object.
2066          */
2067         __evt__touchup: function (e) {},
2068 
2069         /**
2070          * @event
2071          * @description Notify every time an attribute is changed.
2072          * @name JXG.GeometryElement#attribute
2073          * @param {Object} o A list of changed attributes and their new value.
2074          * @param {Object} el Reference to the element
2075          */
2076         __evt__attribute: function (o, el) {},
2077 
2078         /**
2079          * @event
2080          * @description This is a generic event handler. It exists for every possible attribute that can be set for
2081          * any element, e.g. if you want to be notified everytime an element's strokecolor is changed, is the event
2082          * <tt>attribute:strokecolor</tt>.
2083          * @name JXG.GeometryElement#attribute:<attribute>
2084          * @param val The old value.
2085          * @param nval The new value
2086          * @param {Object} el Reference to the element
2087          */
2088         __evt__attribute_: function (val, nval, el) {},
2089 
2090         /**
2091          * @ignore
2092          */
2093         __evt: function () {}
2094         //endregion
2095 
2096     });
2097 
2098     return JXG.GeometryElement;
2099 });
2100