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