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