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