1 /*
  2     Copyright 2008-2017
  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 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             this.visPropCalc.visible = false;
910             this.board.renderer.display(this, false);
911 
912             if (Type.exists(this.label) && this.hasLabel) {
913                 this.label.hiddenByParent = true;
914                 if (this.label.visPropCalc.visible) {
915                     this.label.hideElement();
916                 }
917             }
918             return this;
919         },
920 
921         /**
922          * Make the element visible.
923          * @return {JXG.GeometryElement} Reference to the element
924          * @deprecated
925          * @private
926          */
927         showElement: function () {
928             JXG.deprecated('Element.showElement()', 'Element.setDisplayRendNode()');
929 
930             this.visPropCalc.visible = true;
931             this.board.renderer.display(this, true);
932 
933             if (Type.exists(this.label) && this.hasLabel && this.label.hiddenByParent) {
934                 this.label.hiddenByParent = false;
935                 if (!this.label.visPropCalc.visible) {
936                     this.label.showElement().updateRenderer();
937                 }
938             }
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             property = property.toLocaleLowerCase();
1021 
1022             // Search for entries in visProp with "color" as part of the property name
1023             // and containing a RGBA string
1024             if (this.visProp.hasOwnProperty(property) &&
1025                   property.indexOf('color') >= 0 &&
1026                   Type.isString(value) &&
1027                   value.length === 9 &&
1028                   value.charAt(0) === '#') {
1029 
1030                 value = Color.rgba2rgbo(value);
1031                 this.visProp[property] = value[0];
1032                 // Previously: *=. But then, we can only decrease opacity.
1033                 this.visProp[property.replace('color', 'opacity')] = value[1];
1034             } else {
1035                 this.visProp[property] = value;
1036             }
1037         },
1038 
1039         /**
1040          * Resolves property shortcuts like <tt>color</tt> and expands them, e.g. <tt>strokeColor</tt> and <tt>fillColor</tt>.
1041          * Writes the expanded properties back to the given <tt>properties</tt>.
1042          * @param {Object} properties
1043          * @returns {Object} The given parameter with shortcuts expanded.
1044          */
1045         resolveShortcuts: function (properties) {
1046             var key, i;
1047 
1048             for (key in Options.shortcuts) {
1049                 if (Options.shortcuts.hasOwnProperty(key)) {
1050                     if (Type.exists(properties[key])) {
1051                         for (i = 0; i < Options.shortcuts[key].length; i++) {
1052                             if (!Type.exists(properties[Options.shortcuts[key][i]])) {
1053                                 properties[Options.shortcuts[key][i]] = properties[key];
1054                             }
1055                         }
1056                     }
1057                 }
1058             }
1059             return properties;
1060         },
1061 
1062         /**
1063          * Sets a label and it's text
1064          * If label doesn't exist, it creates one
1065          * @param {String} str
1066          */
1067         setLabel: function (str) {
1068             if (!this.hasLabel) {
1069                 this.setAttribute({'withlabel': true});
1070             }
1071             this.setLabelText(str);
1072         },
1073 
1074         /**
1075          * Updates the element's label text, strips all html.
1076          * @param {String} str
1077          */
1078         setLabelText: function (str) {
1079 
1080             if (Type.exists(this.label)) {
1081                 str = str.replace(/</g, '<').replace(/>/g, '>');
1082                 this.label.setText(str);
1083             }
1084 
1085             return this;
1086         },
1087 
1088         /**
1089          * Updates the element's label text and the element's attribute "name", strips all html.
1090          * @param {String} str
1091          */
1092         setName: function (str) {
1093             str = str.replace(/</g, '<').replace(/>/g, '>');
1094             if (this.elType !== 'slider') {
1095                 this.setLabelText(str);
1096             }
1097             this.setAttribute({name: str});
1098         },
1099 
1100         /**
1101          * Deprecated alias for {@link JXG.GeometryElement#setAttribute}.
1102          * @deprecated Use {@link JXG.GeometryElement#setAttribute}.
1103          */
1104         setProperty: function () {
1105             JXG.deprecated('setProperty()', 'setAttribute()');
1106             this.setAttribute.apply(this, arguments);
1107         },
1108 
1109         /**
1110          * Sets an arbitrary number of attributes.
1111          * @param {Object} attributes An object with attributes.
1112          * @function
1113          * @example
1114          * // Set property directly on creation of an element using the attributes object parameter
1115          * var board = JXG.JSXGraph.initBoard('jxgbox', {boundingbox: [-1, 5, 5, 1]};
1116          * var p = board.create('point', [2, 2], {visible: false});
1117          *
1118          * // Now make this point visible and fixed:
1119          * p.setAttribute({
1120          *     fixed: true,
1121          *     visible: true
1122          * });
1123          */
1124         setAttribute: function (attributes) {
1125             var i, j, le, key, value, arg, opacity, pair, oldvalue,
1126                 properties = {};
1127 
1128             // normalize the user input
1129             for (i = 0; i < arguments.length; i++) {
1130                 arg = arguments[i];
1131                 if (Type.isString(arg)) {
1132                     // pairRaw is string of the form 'key:value'
1133                     pair = arg.split(':');
1134                     properties[Type.trim(pair[0])] = Type.trim(pair[1]);
1135                 } else if (!Type.isArray(arg)) {
1136                     // pairRaw consists of objects of the form {key1:value1,key2:value2,...}
1137                     JXG.extend(properties, arg);
1138                 } else {
1139                     // pairRaw consists of array [key,value]
1140                     properties[arg[0]] = arg[1];
1141                 }
1142             }
1143 
1144             // handle shortcuts
1145             properties = this.resolveShortcuts(properties);
1146 
1147             for (i in properties) {
1148                 if (properties.hasOwnProperty(i)) {
1149                     key = i.replace(/\s+/g, '').toLowerCase();
1150                     value = properties[i];
1151                     oldvalue = this.visProp[key];
1152 
1153                     // This handles the subobjects, if the key:value pairs are contained in an object.
1154                     // Example
1155                     // ticks.setAttribute({
1156                     //      strokeColor: 'blue',
1157                     //      label: {
1158                     //          visible: false
1159                     //      }
1160                     // })
1161                     // Now, only the supplied label attributes are overwritten.
1162                     // Otherwise, the the value of label would be {visible:false} only.
1163                     if (Type.isObject(value) && Type.exists(this.visProp[key])) {
1164                         this.visProp[key] = Type.merge(this.visProp[key], value);
1165 
1166                         if (this.type === Const.OBJECT_TYPE_TICKS && Type.exists(this.labels)) {
1167                             le = this.labels.length;
1168                             for (j = 0; j < le; j++) {
1169                                 this.labels[j].setAttribute(value);
1170                             }
1171                         }
1172                         continue;
1173                     }
1174 
1175                     switch (key) {
1176                     case 'name':
1177                         oldvalue = this.name;
1178                         delete this.board.elementsByName[this.name];
1179                         this.name = value;
1180                         this.board.elementsByName[this.name] = this;
1181                         break;
1182                     case 'needsregularupdate':
1183                         this.needsRegularUpdate = !(value === 'false' || value === false);
1184                         this.board.renderer.setBuffering(this, this.needsRegularUpdate ? 'auto' : 'static');
1185                         break;
1186                     case 'labelcolor':
1187                         value = Color.rgba2rgbo(value);
1188                         opacity = value[1];
1189                         value = value[0];
1190                         if (opacity === 0) {
1191                             if (Type.exists(this.label) && this.hasLabel) {
1192                                 this.label.hideElement();
1193                             }
1194                         }
1195                         if (Type.exists(this.label) && this.hasLabel) {
1196                             this.label.visProp.strokecolor = value;
1197                             this.board.renderer.setObjectStrokeColor(this.label,
1198                                 value, opacity);
1199                         }
1200                         if (this.elementClass === Const.OBJECT_CLASS_TEXT) {
1201                             this.visProp.strokecolor = value;
1202                             this.visProp.strokeopacity = opacity;
1203                             this.board.renderer.setObjectStrokeColor(this,
1204                                 value, opacity);
1205                         }
1206                         break;
1207                     case 'infoboxtext':
1208                         if (Type.isString(value)) {
1209                             this.infoboxText = value;
1210                         } else {
1211                             this.infoboxText = false;
1212                         }
1213                         break;
1214                     case 'visible':
1215                         if (value === 'false') {
1216                             this.visProp.visible = false;
1217                         } else if (value === 'true') {
1218                             this.visProp.visible = true;
1219                         } else {
1220                             this.visProp.visible = value;
1221                         }
1222 
1223                         this.setDisplayRendNode(Type.evaluate(this.visProp.visible));
1224                         break;
1225                     case 'face':
1226                         if (Type.isPoint(this)) {
1227                             this.visProp.face = value;
1228                             this.board.renderer.changePointStyle(this);
1229                         }
1230                         break;
1231                     case 'trace':
1232                         if (value === 'false' || value === false) {
1233                             this.clearTrace();
1234                             this.visProp.trace = false;
1235                         } else {
1236                             this.visProp.trace = true;
1237                         }
1238                         break;
1239                     case 'gradient':
1240                         this.visProp.gradient = value;
1241                         this.board.renderer.setGradient(this);
1242                         break;
1243                     case 'gradientsecondcolor':
1244                         value = Color.rgba2rgbo(value);
1245                         this.visProp.gradientsecondcolor = value[0];
1246                         this.visProp.gradientsecondopacity = value[1];
1247                         this.board.renderer.updateGradient(this);
1248                         break;
1249                     case 'gradientsecondopacity':
1250                         this.visProp.gradientsecondopacity = value;
1251                         this.board.renderer.updateGradient(this);
1252                         break;
1253                     case 'withlabel':
1254                         this.visProp.withlabel = value;
1255                         if (!Type.evaluate(value)) {
1256                             if (this.label && this.hasLabel) {
1257                                 this.label.hideElement();
1258                             }
1259                         } else {
1260                             if (!this.label) {
1261                                 this.createLabel();
1262                             }
1263                             this.label.setDisplayRendNode(Type.evaluate(this.visProp.visible));
1264                         }
1265                         this.hasLabel = value;
1266                         break;
1267                     case 'radius':
1268                         if (this.type === Const.OBJECT_TYPE_ANGLE || this.type === Const.OBJECT_TYPE_SECTOR) {
1269                             this.setRadius(value);
1270                         }
1271                         break;
1272                     case 'rotate':
1273                         if ((this.elementClass === Const.OBJECT_CLASS_TEXT &&
1274                              Type.evaluate(this.visProp.display) === 'internal') ||
1275                             this.type === Const.OBJECT_TYPE_IMAGE) {
1276                             this.addRotation(value);
1277                         }
1278                         break;
1279                     case 'ticksdistance':
1280                         if (this.type === Const.OBJECT_TYPE_TICKS && Type.isNumber(value)) {
1281                             this.ticksFunction = this.makeTicksFunction(value);
1282                         }
1283                         break;
1284                     case 'generatelabelvalue':
1285                         if (this.type === Const.OBJECT_TYPE_TICKS && Type.isFunction(value)) {
1286                             this.generateLabelValue = value;
1287                         }
1288                         break;
1289                     case 'onpolygon':
1290                         if (this.type === Const.OBJECT_TYPE_GLIDER) {
1291                             this.onPolygon = !!value;
1292                         }
1293                         break;
1294                     case 'disabled':
1295                         // button, checkbox, input. Is not available on initial call.
1296                         if (Type.exists(this.rendNodeTag)) {
1297                             this.rendNodeTag.disabled = !!value;
1298                         }
1299                         break;
1300                     case 'maxlength':
1301                         // input. Is not available on initial call.
1302                         if (Type.exists(this.rendNodeTag)) {
1303                             this.rendNodeTag.maxlength = !!value;
1304                         }
1305                         break;
1306                     default:
1307                         if (Type.exists(this.visProp[key]) &&
1308                             (!JXG.Validator[key] ||
1309                                 (JXG.Validator[key] && JXG.Validator[key](value)) ||
1310                                 (JXG.Validator[key] && Type.isFunction(value) && JXG.Validator[key](value()))
1311                             )
1312                         ) {
1313                             value = value.toLowerCase && value.toLowerCase() === 'false' ? false : value;
1314                             this._set(key, value);
1315                         }
1316                         break;
1317                     }
1318                     this.triggerEventHandlers(['attribute:' + key], [oldvalue, value, this]);
1319                 }
1320             }
1321 
1322             this.triggerEventHandlers(['attribute'], [properties, this]);
1323 
1324             if (!Type.evaluate(this.visProp.needsregularupdate)) {
1325                 this.board.fullUpdate();
1326             } else {
1327                 this.board.update(this);
1328             }
1329 
1330             return this;
1331         },
1332 
1333         /**
1334          * Deprecated alias for {@link JXG.GeometryElement#getAttribute}.
1335          * @deprecated Use {@link JXG.GeometryElement#getAttribute}.
1336          */
1337         getProperty: function () {
1338             JXG.deprecated('getProperty()', 'getAttribute()');
1339             this.getProperty.apply(this, arguments);
1340         },
1341 
1342         /**
1343          * Get the value of the property <tt>key</tt>.
1344          * @param {String} key The name of the property you are looking for
1345          * @returns The value of the property
1346          */
1347         getAttribute: function (key) {
1348             var result;
1349             key = key.toLowerCase();
1350 
1351             switch (key) {
1352             case 'needsregularupdate':
1353                 result = this.needsRegularUpdate;
1354                 break;
1355             case 'labelcolor':
1356                 result = this.label.visProp.strokecolor;
1357                 break;
1358             case 'infoboxtext':
1359                 result = this.infoboxText;
1360                 break;
1361             case 'withlabel':
1362                 result = this.hasLabel;
1363                 break;
1364             default:
1365                 result = this.visProp[key];
1366                 break;
1367             }
1368 
1369             return result;
1370         },
1371 
1372         /**
1373          * Set the dash style of an object. See {@link JXG.GeometryElement#dash}
1374          * for a list of available dash styles.
1375          * You should use {@link JXG.GeometryElement#setAttribute} instead of this method.
1376          *
1377          * @param {number} dash Indicates the new dash style
1378          * @private
1379          */
1380         setDash: function (dash) {
1381             this.setAttribute({dash: dash});
1382             return this;
1383         },
1384 
1385         /**
1386          * Notify all child elements for updates.
1387          * @private
1388          */
1389         prepareUpdate: function () {
1390             this.needsUpdate = true;
1391             return this;
1392         },
1393 
1394         /**
1395          * Removes the element from the construction.  This only removes the SVG or VML node of the element and its label (if available) from
1396          * the renderer, to remove the element completely you should use {@link JXG.Board#removeObject}.
1397          */
1398         remove: function () {
1399             this.board.renderer.remove(this.board.renderer.getElementById(this.id));
1400 
1401             if (this.hasLabel) {
1402                 this.board.renderer.remove(this.board.renderer.getElementById(this.label.id));
1403             }
1404             return this;
1405         },
1406 
1407         /**
1408          * Returns the coords object where a text that is bound to the element shall be drawn.
1409          * Differs in some cases from the values that getLabelAnchor returns.
1410          * @returns {JXG.Coords} JXG.Coords Place where the text shall be drawn.
1411          * @see JXG.GeometryElement#getLabelAnchor
1412          */
1413         getTextAnchor: function () {
1414             return new Coords(Const.COORDS_BY_USER, [0, 0], this.board);
1415         },
1416 
1417         /**
1418          * Returns the coords object where the label of the element shall be drawn.
1419          * Differs in some cases from the values that getTextAnchor returns.
1420          * @returns {JXG.Coords} JXG.Coords Place where the text shall be drawn.
1421          * @see JXG.GeometryElement#getTextAnchor
1422          */
1423         getLabelAnchor: function () {
1424             return new Coords(Const.COORDS_BY_USER, [0, 0], this.board);
1425         },
1426 
1427         /**
1428          * Determines whether the element has arrows at start or end of the arc.
1429          * If it is set to be a "typical" vector, ie lastArrow == true,
1430          * then the element.type is set to VECTOR.
1431          * @param {Boolean} firstArrow True if there is an arrow at the start of the arc, false otherwise.
1432          * @param {Boolean} lastArrow True if there is an arrow at the end of the arc, false otherwise.
1433          */
1434         setArrow: function (firstArrow, lastArrow) {
1435             this.visProp.firstarrow = firstArrow;
1436             this.visProp.lastarrow = lastArrow;
1437             if (lastArrow) {
1438                 this.type = Const.OBJECT_TYPE_VECTOR;
1439                 this.elType = 'arrow';
1440             }
1441 
1442             this.prepareUpdate().update().updateVisibility().updateRenderer();
1443             return this;
1444         },
1445 
1446         /**
1447          * Creates a gradient nodes in the renderer.
1448          * @see JXG.SVGRenderer#setGradient
1449          * @private
1450          */
1451         createGradient: function () {
1452             var ev_g = Type.evaluate(this.visProp.gradient);
1453             if (ev_g === 'linear' || ev_g === 'radial') {
1454                 this.board.renderer.setGradient(this);
1455             }
1456         },
1457 
1458          /**
1459          * Creates a label element for this geometry element.
1460          * @see #addLabelToElement
1461          */
1462         createLabel: function () {
1463             var attr,
1464                 that = this;
1465 
1466             // this is a dirty hack to resolve the text-dependency. If there is no text element available,
1467             // just don't create a label. This method is usually not called by a user, so we won't throw
1468             // an exception here and simply output a warning via JXG.debug.
1469             if (JXG.elements.text) {
1470                 attr =  Type.deepCopy(this.visProp.label, null);
1471                 attr.id = this.id + 'Label';
1472                 attr.isLabel = true;
1473                 attr.anchor = this;
1474                 attr.priv = this.visProp.priv;
1475 
1476                 if (this.visProp.withlabel) {
1477                     this.label = JXG.elements.text(this.board, [0, 0, function () {
1478                         if (Type.isFunction(that.name)) {
1479                             return that.name();
1480                         }
1481                         return that.name;
1482                     }], attr);
1483                     this.label.needsUpdate = true;
1484                     this.label.dump = false;
1485                     this.label.update();
1486 
1487                     // if (!Type.evaluate(this.visProp.visible)) {
1488                     //     this.label.hiddenByParent = true;
1489                     //     this.label.visPropCalc.visible = false;
1490                     // }
1491                     // this.label.fullUpdate();
1492                     this.hasLabel = true;
1493                 }
1494             } else {
1495                 JXG.debug('JSXGraph: Can\'t create label: text element is not available. Make sure you include base/text');
1496             }
1497 
1498             return this;
1499         },
1500 
1501         /**
1502          * Highlights the element.
1503          * @param {Boolean} [force=false] Force the highlighting
1504          * @returns {JXG.Board}
1505          */
1506         highlight: function (force) {
1507             force = Type.def(force, false);
1508             // I know, we have the JXG.Board.highlightedObjects AND JXG.GeometryElement.highlighted and YES we need both.
1509             // Board.highlightedObjects is for the internal highlighting and GeometryElement.highlighted is for user highlighting
1510             // initiated by the user, e.g. through custom DOM events. We can't just pick one because this would break user
1511             // defined highlighting in many ways:
1512             //  * if overriding the highlight() methods the user had to handle the highlightedObjects stuff, otherwise he'd break
1513             //    everything (e.g. the pie chart example http://jsxgraph.uni-bayreuth.de/wiki/index.php/Pie_chart (not exactly
1514             //    user defined but for this type of chart the highlight method was overridden and not adjusted to the changes in here)
1515             //    where it just kept highlighting until the radius of the pie was far beyond infinity...
1516             //  * user defined highlighting would get pointless, everytime the user highlights something using .highlight(), it would get
1517             //    dehighlighted immediately, because highlight puts the element into highlightedObjects and from there it gets dehighlighted
1518             //    through dehighlightAll.
1519 
1520             // highlight only if not highlighted
1521             if (Type.evaluate(this.visProp.highlight)
1522                     && (!this.highlighted || force)) {
1523                 this.highlighted = true;
1524                 this.board.highlightedObjects[this.id] = this;
1525                 this.board.renderer.highlight(this);
1526             }
1527             return this;
1528         },
1529 
1530         /**
1531          * Uses the "normal" properties of the element.
1532          * @returns {JXG.Board}
1533          */
1534         noHighlight: function () {
1535             // see comment in JXG.GeometryElement.highlight()
1536 
1537             // dehighlight only if not highlighted
1538             if (this.highlighted) {
1539                 this.highlighted = false;
1540                 delete this.board.highlightedObjects[this.id];
1541                 this.board.renderer.noHighlight(this);
1542             }
1543             return this;
1544         },
1545 
1546         /**
1547          * Removes all objects generated by the trace function.
1548          */
1549         clearTrace: function () {
1550             var obj;
1551 
1552             for (obj in this.traces) {
1553                 if (this.traces.hasOwnProperty(obj)) {
1554                     this.board.renderer.remove(this.traces[obj]);
1555                 }
1556             }
1557 
1558             this.numTraces = 0;
1559             return this;
1560         },
1561 
1562         /**
1563          * Copy the element to background. This is used for tracing elements.
1564          * @returns {JXG.GeometryElement} A reference to the element
1565          */
1566         cloneToBackground: function () {
1567             return this;
1568         },
1569 
1570         /**
1571          * Dimensions of the smallest rectangle enclosing the element.
1572          * @returns {Array} The coordinates of the enclosing rectangle in a format
1573          * like the bounding box in {@link JXG.Board#setBoundingBox}.
1574          */
1575         bounds: function () {
1576             return [0, 0, 0, 0];
1577         },
1578 
1579         /**
1580          * Normalize the element's standard form.
1581          * @private
1582          */
1583         normalize: function () {
1584             this.stdform = Mat.normalize(this.stdform);
1585             return this;
1586         },
1587 
1588         /**
1589          * EXPERIMENTAL. Generate JSON object code of visProp and other properties.
1590          * @type string
1591          * @private
1592          * @ignore
1593          * @returns JSON string containing element's properties.
1594          */
1595         toJSON: function () {
1596             var vis, key,
1597                 json = ['{"name":', this.name];
1598 
1599             json.push(', ' + '"id":' + this.id);
1600 
1601             vis = [];
1602             for (key in this.visProp) {
1603                 if (this.visProp.hasOwnProperty(key)) {
1604                     if (Type.exists(this.visProp[key])) {
1605                         vis.push('"' + key + '":' + this.visProp[key]);
1606                     }
1607                 }
1608             }
1609             json.push(', "visProp":{' + vis.toString() + '}');
1610             json.push('}');
1611 
1612             return json.join('');
1613         },
1614 
1615 
1616         /**
1617          * Rotate texts or images by a given degree. Works only for texts where JXG.Text#display equal to "internal".
1618          * @param {number} angle The degree of the rotation (90 means vertical text).
1619          * @see JXG.GeometryElement#rotate
1620          */
1621         addRotation: function (angle) {
1622             var tOffInv, tOff, tS, tSInv, tRot,
1623                 that = this;
1624 
1625             if (((this.elementClass === Const.OBJECT_CLASS_TEXT &&
1626                     Type.evaluate(this.visProp.display) === 'internal') ||
1627                     this.type === Const.OBJECT_TYPE_IMAGE) && angle !== 0) {
1628 
1629                 tOffInv = this.board.create('transform', [
1630                     function () {
1631                         return -that.X();
1632                     }, function () {
1633                         return -that.Y();
1634                     }
1635                 ], {type: 'translate'});
1636 
1637                 tOff = this.board.create('transform', [
1638                     function () {
1639                         return that.X();
1640                     }, function () {
1641                         return that.Y();
1642                     }
1643                 ], {type: 'translate'});
1644 
1645                 tS = this.board.create('transform', [
1646                     function () {
1647                         return that.board.unitX / that.board.unitY;
1648                     }, function () {
1649                         return 1;
1650                     }
1651                 ], {type: 'scale'});
1652 
1653                 tSInv = this.board.create('transform', [
1654                     function () {
1655                         return that.board.unitY / that.board.unitX;
1656                     }, function () {
1657                         return 1;
1658                     }
1659                 ], {type: 'scale'});
1660 
1661                 tRot = this.board.create('transform', [angle * Math.PI / 180], {type: 'rotate'});
1662 
1663                 tOffInv.bindTo(this);
1664                 tS.bindTo(this);
1665                 tRot.bindTo(this);
1666                 tSInv.bindTo(this);
1667                 tOff.bindTo(this);
1668             }
1669 
1670             return this;
1671         },
1672 
1673         /**
1674          * Set the highlightStrokeColor of an element
1675          * @param {String} sColor String which determines the stroke color of an object when its highlighted.
1676          * @see JXG.GeometryElement#highlightStrokeColor
1677          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1678          */
1679         highlightStrokeColor: function (sColor) {
1680             JXG.deprecated('highlightStrokeColor()', 'setAttribute()');
1681             this.setAttribute({highlightStrokeColor: sColor});
1682             return this;
1683         },
1684 
1685         /**
1686          * Set the strokeColor of an element
1687          * @param {String} sColor String which determines the stroke color of an object.
1688          * @see JXG.GeometryElement#strokeColor
1689          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1690          */
1691         strokeColor: function (sColor) {
1692             JXG.deprecated('strokeColor()', 'setAttribute()');
1693             this.setAttribute({strokeColor: sColor});
1694             return this;
1695         },
1696 
1697         /**
1698          * Set the strokeWidth of an element
1699          * @param {Number} width Integer which determines the stroke width of an outline.
1700          * @see JXG.GeometryElement#strokeWidth
1701          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1702          */
1703         strokeWidth: function (width) {
1704             JXG.deprecated('strokeWidth()', 'setAttribute()');
1705             this.setAttribute({strokeWidth: width});
1706             return this;
1707         },
1708 
1709 
1710         /**
1711          * Set the fillColor of an element
1712          * @param {String} fColor String which determines the fill color of an object.
1713          * @see JXG.GeometryElement#fillColor
1714          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1715          */
1716         fillColor: function (fColor) {
1717             JXG.deprecated('fillColor()', 'setAttribute()');
1718             this.setAttribute({fillColor: fColor});
1719             return this;
1720         },
1721 
1722         /**
1723          * Set the highlightFillColor of an element
1724          * @param {String} fColor String which determines the fill color of an object when its highlighted.
1725          * @see JXG.GeometryElement#highlightFillColor
1726          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1727          */
1728         highlightFillColor: function (fColor) {
1729             JXG.deprecated('highlightFillColor()', 'setAttribute()');
1730             this.setAttribute({highlightFillColor: fColor});
1731             return this;
1732         },
1733 
1734         /**
1735          * Set the labelColor of an element
1736          * @param {String} lColor String which determines the text color of an object's label.
1737          * @see JXG.GeometryElement#labelColor
1738          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1739          */
1740         labelColor: function (lColor) {
1741             JXG.deprecated('labelColor()', 'setAttribute()');
1742             this.setAttribute({labelColor: lColor});
1743             return this;
1744         },
1745 
1746         /**
1747          * Set the dash type of an element
1748          * @param {Number} d Integer which determines the way of dashing an element's outline.
1749          * @see JXG.GeometryElement#dash
1750          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1751          */
1752         dash: function (d) {
1753             JXG.deprecated('dash()', 'setAttribute()');
1754             this.setAttribute({dash: d});
1755             return this;
1756         },
1757 
1758         /**
1759          * Set the visibility of an element
1760          * @param {Boolean} v Boolean which determines whether the element is drawn.
1761          * @see JXG.GeometryElement#visible
1762          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1763          */
1764         visible: function (v) {
1765             JXG.deprecated('visible()', 'setAttribute()');
1766             this.setAttribute({visible: v});
1767             return this;
1768         },
1769 
1770         /**
1771          * Set the shadow of an element
1772          * @param {Boolean} s Boolean which determines whether the element has a shadow or not.
1773          * @see JXG.GeometryElement#shadow
1774          * @deprecated Use {@link JXG.GeometryElement#setAttribute}
1775          */
1776         shadow: function (s) {
1777             JXG.deprecated('shadow()', 'setAttribute()');
1778             this.setAttribute({shadow: s});
1779             return this;
1780         },
1781 
1782         /**
1783          * The type of the element as used in {@link JXG.Board#create}.
1784          * @returns {String}
1785          */
1786         getType: function () {
1787             return this.elType;
1788         },
1789 
1790         /**
1791          * List of the element ids resp. values used as parents in {@link JXG.Board#create}.
1792          * @returns {Array}
1793          */
1794         getParents: function () {
1795             return Type.isArray(this.parents) ? this.parents : [];
1796         },
1797 
1798         /**
1799          * Snaps the element to the grid. Only works for points, lines and circles. Points will snap to the grid
1800          * as defined in their properties {@link JXG.Point#snapSizeX} and {@link JXG.Point#snapSizeY}. Lines and circles
1801          * will snap their parent points to the grid, if they have {@link JXG.Point#snapToGrid} set to true.
1802          * @returns {JXG.GeometryElement} Reference to the element.
1803          */
1804         snapToGrid: function () {
1805             return this;
1806         },
1807 
1808         /**
1809          * Snaps the element to points. Only works for points. Points will snap to the next point
1810          * as defined in their properties {@link JXG.Point#attractorDistance} and {@link JXG.Point#attractorUnit}.
1811          * Lines and circles
1812          * will snap their parent points to points.
1813          * @returns {JXG.GeometryElement} Reference to the element.
1814          */
1815         snapToPoints: function () {
1816             return this;
1817         },
1818 
1819         /**
1820          * Retrieve a copy of the current visProp.
1821          * @returns {Object}
1822          */
1823         getAttributes: function () {
1824             var attributes = Type.deepCopy(this.visProp),
1825                 /*
1826                 cleanThis = ['attractors', 'snatchdistance', 'traceattributes', 'frozen',
1827                     'shadow', 'gradientangle', 'gradientsecondopacity', 'gradientpositionx', 'gradientpositiony',
1828                     'needsregularupdate', 'zoom', 'layer', 'offset'],
1829                 */
1830                 cleanThis = [],
1831                 i, len = cleanThis.length;
1832 
1833             attributes.id = this.id;
1834             attributes.name = this.name;
1835 
1836             for (i = 0; i < len; i++) {
1837                 delete attributes[cleanThis[i]];
1838             }
1839 
1840             return attributes;
1841         },
1842 
1843         /**
1844          * Checks whether (x,y) is near the element.
1845          * @param {Number} x Coordinate in x direction, screen coordinates.
1846          * @param {Number} y Coordinate in y direction, screen coordinates.
1847          * @returns {Boolean} True if (x,y) is near the element, False otherwise.
1848          */
1849         hasPoint: function (x, y) {
1850             return false;
1851         },
1852 
1853         /**
1854          * Move an element to its nearest grid point.
1855          * The function uses the coords object of the element as
1856          * its actual position. If there is no coords object, nothing is done.
1857          * @param {Boolean} force force snapping independent from what the snaptogrid attribute says
1858          * @param {Boolean} fromParent True if the drag comes from a child element. This is the case if a line
1859          *    through two points is dragged. In this case we do not try to force the points to stay inside of
1860          *    the visible board, but the distance between the two points stays constant.
1861          * @returns {JXG.GeometryElement} Reference to this element
1862          */
1863         handleSnapToGrid: function (force, fromParent) {
1864             var x, y, ticks,
1865                 //i, len, g, el, p,
1866                 boardBB,
1867                 needsSnapToGrid = false,
1868                 sX = Type.evaluate(this.visProp.snapsizex),
1869                 sY = Type.evaluate(this.visProp.snapsizey);
1870 
1871             if (!Type.exists(this.coords)) {
1872                 return this;
1873             }
1874 
1875             needsSnapToGrid = Type.evaluate(this.visProp.snaptogrid) || force === true;
1876 
1877             if (needsSnapToGrid) {
1878                 x = this.coords.usrCoords[1];
1879                 y = this.coords.usrCoords[2];
1880 
1881                 if (sX <= 0 && this.board.defaultAxes && this.board.defaultAxes.x.defaultTicks) {
1882                     ticks = this.board.defaultAxes.x.defaultTicks;
1883                     sX = ticks.ticksDelta * (Type.evaluate(ticks.visProp.minorticks) + 1);
1884                 }
1885 
1886                 if (sY <= 0 && this.board.defaultAxes && this.board.defaultAxes.y.defaultTicks) {
1887                     ticks = this.board.defaultAxes.y.defaultTicks;
1888                     sY = ticks.ticksDelta * (Type.evaluate(ticks.visProp.minorticks) + 1);
1889                 }
1890 
1891                 // if no valid snap sizes are available, don't change the coords.
1892                 if (sX > 0 && sY > 0) {
1893                     boardBB = this.board.getBoundingBox();
1894                     x = Math.round(x / sX) * sX;
1895                     y = Math.round(y / sY) * sY;
1896 
1897                     // checking whether x and y are still within boundingBox,
1898                     // if not, adjust them to remain within the board
1899                     if (!fromParent) {
1900                         if (x < boardBB[0]) {
1901                             x += sX;
1902                         } else if (x > boardBB[2]) {
1903                             x -= sX;
1904                         }
1905 
1906                         if (y < boardBB[3]) {
1907                             y += sY;
1908                         } else if (y > boardBB[1]) {
1909                             y -= sY;
1910                         }
1911                     }
1912                     this.coords.setCoordinates(Const.COORDS_BY_USER, [x, y]);
1913                 }
1914             }
1915             return this;
1916         },
1917 
1918         /**
1919          * Alias of {@link JXG.EventEmitter.on}.
1920          *
1921          * @name addEvent
1922          * @memberof JXG.GeometryElement
1923          * @function
1924          */
1925         addEvent: JXG.shortcut(JXG.GeometryElement.prototype, 'on'),
1926 
1927         /**
1928          * Alias of {@link JXG.EventEmitter.off}.
1929          *
1930          * @name removeEvent
1931          * @memberof JXG.GeometryElement
1932          * @function
1933          */
1934         removeEvent: JXG.shortcut(JXG.GeometryElement.prototype, 'off'),
1935 
1936         /* **************************
1937          *     EVENT DEFINITION
1938          * for documentation purposes
1939          * ************************** */
1940 
1941         //region Event handler documentation
1942         /**
1943          * @event
1944          * @description This event is fired whenever the user is hovering over an element.
1945          * @name JXG.GeometryElement#over
1946          * @param {Event} e The browser's event object.
1947          */
1948         __evt__over: function (e) { },
1949 
1950         /**
1951          * @event
1952          * @description This event is fired whenever the user puts the mouse over an element.
1953          * @name JXG.GeometryElement#mouseover
1954          * @param {Event} e The browser's event object.
1955          */
1956         __evt__mouseover: function (e) { },
1957 
1958         /**
1959          * @event
1960          * @description This event is fired whenever the user is leaving an element.
1961          * @name JXG.GeometryElement#out
1962          * @param {Event} e The browser's event object.
1963          */
1964         __evt__out: function (e) { },
1965 
1966         /**
1967          * @event
1968          * @description This event is fired whenever the user puts the mouse away from an element.
1969          * @name JXG.GeometryElement#mouseout
1970          * @param {Event} e The browser's event object.
1971          */
1972         __evt__mouseout: function (e) { },
1973 
1974         /**
1975          * @event
1976          * @description This event is fired whenever the user is moving over an element.
1977          * @name JXG.GeometryElement#move
1978          * @param {Event} e The browser's event object.
1979          */
1980         __evt__move: function (e) { },
1981 
1982         /**
1983          * @event
1984          * @description This event is fired whenever the user is moving the mouse over an element.
1985          * @name JXG.GeometryElement#mousemove
1986          * @param {Event} e The browser's event object.
1987          */
1988         __evt__mousemove: function (e) { },
1989 
1990         /**
1991          * @event
1992          * @description This event is fired whenever the user drags an element.
1993          * @name JXG.GeometryElement#drag
1994          * @param {Event} e The browser's event object.
1995          */
1996         __evt__drag: function (e) { },
1997 
1998         /**
1999          * @event
2000          * @description This event is fired whenever the user drags the element with a mouse.
2001          * @name JXG.GeometryElement#mousedrag
2002          * @param {Event} e The browser's event object.
2003          */
2004         __evt__mousedrag: function (e) { },
2005 
2006         /**
2007          * @event
2008          * @description This event is fired whenever the user drags the element on a touch device.
2009          * @name JXG.GeometryElement#touchdrag
2010          * @param {Event} e The browser's event object.
2011          */
2012         __evt__touchdrag: function (e) { },
2013 
2014         /**
2015          * @event
2016          * @description Whenever the user starts to touch or click an element.
2017          * @name JXG.GeometryElement#down
2018          * @param {Event} e The browser's event object.
2019          */
2020         __evt__down: function (e) { },
2021 
2022         /**
2023          * @event
2024          * @description Whenever the user starts to click an element.
2025          * @name JXG.GeometryElement#mousedown
2026          * @param {Event} e The browser's event object.
2027          */
2028         __evt__mousedown: function (e) { },
2029 
2030         /**
2031          * @event
2032          * @description Whenever the user starts to touch an element.
2033          * @name JXG.GeometryElement#touchdown
2034          * @param {Event} e The browser's event object.
2035          */
2036         __evt__touchdown: function (e) { },
2037 
2038         /**
2039          * @event
2040          * @description Whenever the user stops to touch or click an element.
2041          * @name JXG.GeometryElement#up
2042          * @param {Event} e The browser's event object.
2043          */
2044         __evt__up: function (e) { },
2045 
2046         /**
2047          * @event
2048          * @description Whenever the user releases the mousebutton over an element.
2049          * @name JXG.GeometryElement#mouseup
2050          * @param {Event} e The browser's event object.
2051          */
2052         __evt__mouseup: function (e) { },
2053 
2054         /**
2055          * @event
2056          * @description Whenever the user stops touching an element.
2057          * @name JXG.GeometryElement#touchup
2058          * @param {Event} e The browser's event object.
2059          */
2060         __evt__touchup: function (e) {},
2061 
2062         /**
2063          * @event
2064          * @description Notify every time an attribute is changed.
2065          * @name JXG.GeometryElement#attribute
2066          * @param {Object} o A list of changed attributes and their new value.
2067          * @param {Object} el Reference to the element
2068          */
2069         __evt__attribute: function (o, el) {},
2070 
2071         /**
2072          * @event
2073          * @description This is a generic event handler. It exists for every possible attribute that can be set for
2074          * any element, e.g. if you want to be notified everytime an element's strokecolor is changed, is the event
2075          * <tt>attribute:strokecolor</tt>.
2076          * @name JXG.GeometryElement#attribute:<attribute>
2077          * @param val The old value.
2078          * @param nval The new value
2079          * @param {Object} el Reference to the element
2080          */
2081         __evt__attribute_: function (val, nval, el) {},
2082 
2083         /**
2084          * @ignore
2085          */
2086         __evt: function () {}
2087         //endregion
2088 
2089     });
2090 
2091     return JXG.GeometryElement;
2092 });
2093