      1 /*
      2     Copyright 2008-2011
      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: you can redistribute it and/or modify
     13     it under the terms of the GNU Lesser General Public License as published by
     14     the Free Software Foundation, either version 3 of the License, or
     15     (at your option) any later version.
     16
     17     JSXGraph is distributed in the hope that it will be useful,
     18     but WITHOUT ANY WARRANTY; without even the implied warranty of
     19     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     20     GNU Lesser General Public License for more details.
     21
     22     You should have received a copy of the GNU Lesser General Public License
     23     along with JSXGraph.  If not, see <http://www.gnu.org/licenses/>.
     24 */
     25 JXG.OBJECT_TYPE_ARC = 1;
     26 JXG.OBJECT_TYPE_ARROW = 2;
     27 JXG.OBJECT_TYPE_AXIS = 3;
     28 JXG.OBJECT_TYPE_AXISPOINT = 4;
     29 JXG.OBJECT_TYPE_TICKS = 5;
     30 JXG.OBJECT_TYPE_CIRCLE = 6;
     31 JXG.OBJECT_TYPE_CONIC = 7;
     32 JXG.OBJECT_TYPE_CURVE = 8;
     33 JXG.OBJECT_TYPE_GLIDER = 9;
     34 JXG.OBJECT_TYPE_IMAGE = 10;
     35 JXG.OBJECT_TYPE_LINE = 11;
     36 JXG.OBJECT_TYPE_POINT = 12;
     37 JXG.OBJECT_TYPE_SLIDER = 13;
     38 JXG.OBJECT_TYPE_CAS = 14;
     39 JXG.OBJECT_TYPE_GXTCAS = 15;
     40 JXG.OBJECT_TYPE_POLYGON = 16;
     41 JXG.OBJECT_TYPE_SECTOR = 17;
     42 JXG.OBJECT_TYPE_TEXT = 18;
     43 JXG.OBJECT_TYPE_ANGLE = 19;
     44 JXG.OBJECT_TYPE_INTERSECTION = 20;
     45 JXG.OBJECT_TYPE_TURTLE = 21;
     46 JXG.OBJECT_TYPE_VECTOR = 22;
     47 JXG.OBJECT_TYPE_OPROJECT = 23;
     48
     49
     50 JXG.OBJECT_CLASS_POINT = 1;
     51 JXG.OBJECT_CLASS_LINE = 2;
     52 JXG.OBJECT_CLASS_CIRCLE = 3;
     53 JXG.OBJECT_CLASS_CURVE = 4;
     54 JXG.OBJECT_CLASS_AREA = 5;
     55 JXG.OBJECT_CLASS_OTHER = 6;
     56
     57 /**
     58  * Constructs a new GeometryElement object.
     59  * @class This is the basic class for geometry elements like points, circles and lines.
     60  * @constructor
     61  * of identical elements on the board. Is not yet implemented for all elements, only points, lines and circle can be traced.
     62  */
     63 JXG.GeometryElement = function (board, attributes, type, oclass) {
     64     var name, key;
     65
     66     /**
     67      * Controls if updates are necessary
     68      * @type Boolean
     69      * @default true
     70      */
     71     this.needsUpdate = true;
     72
     73     /**
     74      * Controls if this element can be dragged. In GEONExT only
     75      * free points and gliders can be dragged.
     76      * @type Boolean
     77      * @default false
     78      */
     79     this.isDraggable = false;
     80
     81     /**
     82      * If element is in two dimensional real space this is true, else false.
     83      * @type Boolean
     84      * @default true
     85      */
     86     this.isReal = true;
     87
     88     /**
     89      * Stores all dependent objects to be updated when this point is moved.
     90      * @type Object
     91      */
     92     this.childElements = {};
     93
     94     /**
     95      * If element has a label subelement then this property will be set to true.
     96      * @type Boolean
     97      * @default false
     98      */
     99     this.hasLabel = false;
    100
    101     /**
    102      * True, if the element is currently highlighted.
    103      * @type Boolean
    104      * @default false
    105      */
    106     this.highlighted = false;
    107
    108     /**
    109      * Stores all Intersection Objects which in this moment are not real and
    110      * so hide this element.
    111      * @type Object
    112      */
    113     this.notExistingParents = {};
    114
    115     /**
    116      * Keeps track of all objects drawn as part of the trace of the element.
    117      * @see JXG.GeometryElement#traced
    118      * @see JXG.GeometryElement#clearTrace
    119      * @see JXG.GeometryElement#numTraces
    120      * @type Object
    121      */
    122     this.traces = {};
    123
    124     /**
    125      * Counts the number of objects drawn as part of the trace of the element.
    126      * @see JXG.GeometryElement#traced
    127      * @see JXG.GeometryElement#clearTrace
    128      * @see JXG.GeometryElement#traces
    129      * @type Number
    130      */
    131     this.numTraces = 0;
    132
    133     /**
    134      * Stores the  transformations which are applied during update in an array
    135      * @type Array
    136      * @see JXG.Transformation
    137      */
    138     this.transformations = [];
    139
    140     /** TODO
    141      * @type JXG.GeometryElement
    142      * @default null
    143      * @private
    144      */
    145     this.baseElement = null;
    146
    147     /**
    148      * Elements depending on this element are stored here.
    149      * @type Object
    150      */
    151     this.descendants = {};
    152
    153     /**
    154      * Elements on which this elements depends on are stored here.
    155      * @type Object
    156      */
    157     this.ancestors = {};
    158
    159     /**
    160      * Stores variables for symbolic computations
    161      * @type Object
    162      */
    163     this.symbolic = {};
    164
    165     /**
    166      * The string used with {@link JXG.Board#create}
    167      * @type String
    168      */
    169     this.elType = '';
    170
    171     /**
    172      * The element is saved with an explicit entry in the file (<tt>true</tt>) or implicitly
    173      * via a composition.
    174      * @type Boolean
    175      * @default true
    176      */
    177     this.dump = true;
    178
    179     /**
    180      * Subs contains the subelements, created during the create method.
    181      * @type Object
    182      */
    183     this.subs = {};
    184
    185     /**
    186      * [c,b0,b1,a,k,r,q0,q1]
    187      *
    188      * See
    189      * A.E. Middleditch, T.W. Stacey, and S.B. Tor:
    190      * "Intersection Algorithms for Lines and Circles",
    191      * ACM Transactions on Graphics, Vol. 8, 1, 1989, pp 25-40.
    192      *
    193      * The meaning of the parameters is:
    194      * Circle: points p=[p0,p1] on the circle fulfill
    195      *  a<p,p> + <b,p> + c = 0
    196      * For convenience we also store
    197      *  r: radius
    198      *  k: discriminant = sqrt(<b,b>-4ac)
    199      *  q=[q0,q1] center
    200      *
    201      * Points have radius = 0.
    202      * Lines have radius = infinity.
    203      * b: normalized vector, representing the direction of the line.
    204      *
    205      * Should be put into Coords, when all elements possess Coords.
    206      * @type Array
    207      * @default [1, 0, 0, 0, 1, 1, 0, 0]
    208      */
    209     this.stdform = [1,0,0,0,1, 1,0,0];
    210
    211     /**
    212      * The methodMap determines which methods can be called from within JessieCode and under which name it
    213      * can be used. The map is saved in an object, the name of a property is the name of the method used in JessieCode,
    214      * the value of a property is the name of the method in JavaScript.
    215      * @type Object
    216      */
    217     this.methodMap = {
    218         setLabel: 'setLabelText',
    219         getName: 'getName'
    220     };
    221
    222     /**
    223      * Quadratic form representation of circles (and conics)
    224      * @type Array
    225      * @default [[1,0,0],[0,1,0],[0,0,1]]
    226      */
    227     this.quadraticform = [[1,0,0],[0,1,0],[0,0,1]];
    228
    229     /**
    230      * An associative array containing all visual properties.
    231      * @type Object
    232      * @default empty object
    233      */
    234     this.visProp = {};
    235
    236     /**
    237      * Stores the eventhandlers attached to the element.
    238      * @type Object
    239      */
    240     this.eventHandlers = {};
    241
    242     /**
    243      * Is the mouse over this element?
    244      * @type Boolean
    245      * @default false
    246      */
    247     this.mouseover = false;
    248
    249     if (arguments.length > 0) {
    250         /**
    251          * Reference to the board associated with the element.
    252          * @type JXG.Board
    253          */
    254         this.board = board;
    255
    256         /**
    257          * Type of the element.
    258          * @constant
    259          * @type number
    260          */
    261         this.type = type;
    262
    263         /**
    264          * The element's class.
    265          * @constant
    266          * @type number
    267          */
    268         this.elementClass = oclass || JXG.OBJECT_CLASS_OTHER;
    269
    270         /**
    271          * Unique identifier for the element. Equivalent to id-attribute of renderer element.
    272          * @type String
    273          */
    274         this.id = attributes.id;
    275
    276         name = attributes.name;
    277         /* If name is not set or null or even undefined, generate an unique name for this object */
    278         if (!JXG.exists(name)) {
    279             name = this.board.generateName(this);
    280         }
    281         this.board.elementsByName[name] = this;
    282
    283         /**
    284          * Not necessarily unique name for the element.
    285          * @type String
    286          * @default Name generated by {@link JXG.Board#generateName}.
    287          * @see JXG.Board#generateName
    288          */
    289         this.name = name;
    290
    291         this.needsRegularUpdate = attributes.needsregularupdate;
    292
    293         JXG.clearVisPropOld(this); // create this.visPropOld and set default values
    294
    295         attributes = this.resolveShortcuts(attributes);
    296         for (key in attributes) {
    297             this._set(key, attributes[key]);
    298         }
    299
    300         // TODO: draft downwards compatibility.
    301         this.visProp.draft = attributes.draft && attributes.draft.draft;
    302
    303         this.visProp.gradientangle = '270';
    304         this.visProp.gradientsecondopacity = this.visProp.fillopacity;
    305         this.visProp.gradientpositionx = 0.5;
    306         this.visProp.gradientpositiony = 0.5;
    307     }
    308 };
    309
    310 JXG.extend(JXG.GeometryElement.prototype, /** @lends JXG.GeometryElement.prototype */ {
    311     /**
    312      * Add an element as a child to the current element. Can be used to model dependencies between geometry elements.
    313      * @param {JXG.GeometryElement} obj The dependent object.
    314      */
    315     addChild: function (obj) {
    316         var el, el2;
    317
    318         this.childElements[obj.id] = obj;
    319
    320         this.addDescendants(obj);
    321
    322         obj.ancestors[this.id] = this;
    323         for (el in this.descendants) {
    324             this.descendants[el].ancestors[this.id] = this;
    325             for (el2 in this.ancestors) {
    326                 this.descendants[el].ancestors[this.ancestors[el2].id] = this.ancestors[el2];
    327             }
    328         }
    329         for (el in this.ancestors) {
    330             for (el2 in this.descendants) {
    331                 this.ancestors[el].descendants[this.descendants[el2].id] = this.descendants[el2];
    332             }
    333         }
    334         return this;
    335     },
    336
    337     /**
    338      * Adds the given object to the descendants list of this object and all its child objects.
    339      * @param {JXG.GeometryElement} obj The element that is to be added to the descendants list.
    340      * @private
    341      * @return
    342      */
    343     addDescendants: function (obj) {
    344         var el;
    345
    346         this.descendants[obj.id] = obj;
    347         for (el in obj.childElements) {
    348             this.addDescendants(obj.childElements[el]);
    349         }
    350         return this;
    351     },
    352
    353     /**
    354      * Counts the direct children of an object without counting labels.
    355      * @private
    356      * @return {number} Number of children
    357      */
    358     countChildren: function () {
    359         var prop, s=0, d;
    360
    361         d = this.childElements;
    362         for (prop in d) {
    363             if (d.hasOwnProperty(prop) && prop.indexOf('Label')<0) {
    364                 s++;
    365             }
    366         }
    367         return s;
    368     },
    369
    370     /**
    371      * Returns the elements name, Used in JessieCode.
    372      * @returns {String}
    373      */
    374     getName: function () {
    375         return this.name;
    376     },
    377
    378     /**
    379      * Decides whether an element can be dragged. This is used in setPositionDirectly methods
    380      * where all parent elements are checked if they may be dragged, too.
    381      * @private
    382      * @return {boolean}
    383      */
    384     draggable: function() {
    385         return this.isDraggable
    386                && !this.visProp.fixed
    387                && !this.visProp.frozen
    388                && this.type != JXG.OBJECT_TYPE_GLIDER;    // Experimentally turned off
    389                // && this.countChildren() <= 1; // Experimentally turned off
    390
    391     },
    392
    393     /**
    394      * Array of strings containing the polynomials defining the element.
    395      * Used for determining geometric loci the groebner way.
    396      * @type Array
    397      * @return An array containing polynomials describing the locus of the current object.
    398      * @private
    399      */
    400     generatePolynomial: function () {
    401         return [];
    402     },
    403
    404     /**
    405      * Animates properties for that object like stroke or fill color, opacity and maybe
    406      * even more later.
    407      * @param {Object} hash Object containing propiertes with target values for the animation.
    408      * @param {number} time Number of milliseconds to complete the animation.
    409      * @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>
    410      * @return A reference to the object
    411      * @type JXG.GeometryElement
    412      */
    413     animate: function (hash, time, options) {
    414         options = options || {};
    415         var r, p,
    416             delay = 35,
    417             steps = Math.ceil(time/(delay * 1.0)),
    418             i, self = this;
    419
    420         this.animationData = {};
    421
    422         var animateColor = function (startRGB, endRGB, property) {
    423                 var hsv1, hsv2, sh, ss, sv;
    424                 hsv1 = JXG.rgb2hsv(startRGB);
    425                 hsv2 = JXG.rgb2hsv(endRGB);
    426
    427                 sh = (hsv2[0]-hsv1[0])/(1.*steps);
    428                 ss = (hsv2[1]-hsv1[1])/(1.*steps);
    429                 sv = (hsv2[2]-hsv1[2])/(1.*steps);
    430                 self.animationData[property] = new Array(steps);
    431                 for (i=0; i<steps; i++) {
    432                     self.animationData[property][steps-i-1] = JXG.hsv2rgb(hsv1[0]+(i+1)*sh, hsv1[1]+(i+1)*ss, hsv1[2]+(i+1)*sv);
    433                 }
    434             },
    435             animateFloat = function (start, end, property) {
    436                 start = parseFloat(start);
    437                 end = parseFloat(end);
    438
    439                 // we can't animate without having valid numbers.
    440                 // And parseFloat returns NaN if the given string doesn't contain
    441                 // a valid float number.
    442                 if (isNaN(start) || isNaN(end))
    443                     return;
    444
    445                 var s = (end - start)/(1.*steps);
    446                 self.animationData[property] = new Array(steps);
    447                 for (i=0; i<steps; i++) {
    448                     self.animationData[property][steps-i-1] = start + (i+1)*s;
    449                 }
    450             };
    451
    452         for (r in hash) {
    453             p = r.toLowerCase();
    454             switch(p) {
    455                 case 'strokecolor':
    456                 case 'fillcolor':
    457                     animateColor(this.visProp[p], hash[r], p);
    458                     break;
    459                 case 'strokeopacity':
    460                 case 'strokewidth':
    461                 case 'fillopacity':
    462                     animateFloat(this.visProp[p], hash[r], p);
    463                     break;
    464             }
    465         }
    466
    467         this.animationCallback = options.callback;
    468         this.board.addAnimation(this);
    469         return this;
    470     },
    471
    472     /**
    473      * General update method. Should be overwritten by the element itself.
    474      * Can be used sometimes to commit changes to the object.
    475      */
    476     update: function () {
    477         if (this.visProp.trace) {
    478             this.cloneToBackground(true);
    479         }
    480         return this;
    481     },
    482
    483     /**
    484      * Provide updateRenderer method.
    485      * @private
    486      */
    487     updateRenderer: function () {
    488         return this;
    489     },
    490
    491     /**
    492      * Hide the element. It will still exist but not visible on the board.
    493      */
    494     hideElement: function () {
    495         this.visProp.visible = false;
    496         this.board.renderer.hide(this);
    497         if (this.label!=null && this.hasLabel) {
    498             this.label.hiddenByParent = true;
    499             if (this.label.content.visProp.visible) {
    500                 this.board.renderer.hide(this.label.content);
    501             }
    502         }
    503         return this;
    504     },
    505
    506     /**
    507      * Make the element visible.
    508      */
    509     showElement: function () {
    510         this.visProp.visible = true;
    511         this.board.renderer.show(this);
    512         if (this.label!=null && this.hasLabel && this.label.hiddenByParent) {
    513             this.label.hiddenByParent = false;
    514             if (this.label.content.visProp.visible) {
    515                 this.board.renderer.show(this.label.content);
    516             }
    517         }
    518         return this;
    519     },
    520
    521     /**
    522      * Sets the value of property <tt>property</tt> to <tt>value</tt>.
    523      * @param {String} property The property's name.
    524      * @param {%} value The new value
    525      * @private
    526      */
    527     _set: function (property, value) {
    528             property = property.toLocaleLowerCase();
    529
    530             // Search for entries in visProp with "color" as part of the property name
    531             // and containing a RGBA string
    532             if (this.visProp.hasOwnProperty(property) && property.indexOf('color') >= 0 &&
    533                 JXG.isString(value) && value.length == 9 && value.charAt(0) === '#') {
    534
    535                 value = JXG.rgba2rgbo(value);
    536                 this.visProp[property] = value[0];
    537                 this.visProp[property.replace('color', 'opacity')] = value[1]; // Previously: *=. But then, we can only decrease opacity.
    538             } else {
    539                 this.visProp[property] = value;
    540             }
    541     },
    542
    543     /**
    544      * Resolves property shortcuts like <tt>color</tt> and expands them, e.g. <tt>strokeColor</tt> and <tt>fillColor</tt>.
    545      * Writes the expanded properties back to the given <tt>properties</tt>.
    546      * @param {Object} properties
    547      * @returns {Object} The given parameter with shortcuts expanded.
    548      */
    549     resolveShortcuts: function(properties) {
    550         var key, i;
    551
    552         for (key in JXG.Options.shortcuts) {
    553             if (JXG.exists(properties[key])) {
    554                 for (i = 0; i < JXG.Options.shortcuts[key].length; i++) {
    555                     if (!JXG.exists(properties[JXG.Options.shortcuts[key][i]])) {
    556                         properties[JXG.Options.shortcuts[key][i]] = properties[key];
    557                     }
    558                 }
    559             }
    560         }
    561         return properties;
    562     },
    563
    564     /**
    565      * Updates the element's label text, strips all html.
    566      * @param {String} str
    567      */
    568     setLabelText: function (str) {
    569         str = str.replace(/</g, '<').replace(/>/g, '>');
    570
    571         if (this.label !== null) {
    572             this.label.content.setText(str);
    573         }
    574
    575         return this;
    576     },
    577
    578     /**
    579      * Sets an arbitrary number of attributes.
    580      * @param {Object} attributes An object with attributes.
    581      * @function
    582      * @example
    583      * // Set property directly on creation of an element using the attributes object parameter
    584      * var board = JXG.JSXGraph.initBoard('jxgbox', {boundingbox: [-1, 5, 5, 1]};
    585      * var p = board.create('point', [2, 2], {visible: false});
    586      *
    587      * // Now make this point visible and fixed:
    588      * p.setProperty({
    589      *     fixed: true,
    590      *     visible: true
    591      * });
    592      */
    593     setAttribute: JXG.shortcut(JXG.GeometryElement.prototype, 'setProperty'),
    594
    595     /**
    596      * Deprecated alias for {@link JXG.GeometryElement#setAttribute}.
    597      * @deprecated Use {@link JXG.GeometryElement#setAttribute}.
    598      */
    599     setProperty: function () {
    600         var i, key, value, arg, opacity, pair, properties = {};
    601
    602         // normalize the user input
    603         for (i = 0; i < arguments.length; i++) {
    604             arg = arguments[i];
    605             if (JXG.isString(arg)) {
    606                 // pairRaw is string of the form 'key:value'
    607                 pair = arg.split(':');
    608                 properties[JXG.trim(pair[0])] = JXG.trim(pair[1]);
    609             } else if (!JXG.isArray(arg)) {
    610                 // pairRaw consists of objects of the form {key1:value1,key2:value2,...}
    611                 JXG.extend(properties, arg);
    612             } else {
    613                 // pairRaw consists of array [key,value]
    614                 properties[arg[0]] = arg[1];
    615             }
    616         }
    617
    618         // handle shortcuts
    619         properties = this.resolveShortcuts(properties);
    620
    621         for (i in properties) {
    622             key = i.replace(/\s+/g, '').toLowerCase();
    623             value = properties[i];
    624
    625             switch(key) {
    626                 case 'name':
    627                     delete this.board.elementsByName[this.name];
    628                     this.name = value;
    629                     this.board.elementsByName[this.name] = this;
    630                     break;
    631                 case 'needsregularupdate':
    632                     this.needsRegularUpdate = !(value == 'false' || value == false);
    633                     this.board.renderer.setBuffering(this, this.needsRegularUpdate ? 'auto' : 'static');
    634                     break;
    635                 case 'labelcolor':
    636                     value = JXG.rgba2rgbo(value);
    637                     opacity = value[1];
    638                     value = value[0];
    639                     if (opacity == 0) {
    640                         if (this.label!=null && this.hasLabel) {
    641                             this.label.content.hideElement();
    642                         }
    643                     }
    644                     if (this.label!=null && this.hasLabel) {
    645                         this.label.color = value;
    646                         this.board.renderer.setObjectStrokeColor(this.label.content, value, opacity);
    647                     }
    648                     if (this.type == JXG.OBJECT_TYPE_TEXT) {
    649                         this.visProp.strokecolor = value;
    650                         this.visProp.strokeopacity = opacity;
    651                         this.board.renderer.setObjectStrokeColor(this, this.visProp.strokecolor, this.visProp.strokeopacity);
    652                     }
    653                     break;
    654                 case 'infoboxtext':
    655                     // TODO: what about functions? numbers? maybe text elements?
    656                     if (typeof(value) == 'string') {
    657                         this.infoboxText = value;
    658                     } else {
    659                         this.infoboxText = false;
    660                     }
    661                     break;
    662                 case 'visible':
    663                     if (value == 'false' || value == false) {
    664                         this.visProp.visible = false;
    665                         this.hideElement();
    666                     } else if (value == 'true' || value == true) {
    667                         this.visProp.visible = true;
    668                         this.showElement();
    669                     }
    670                     break;
    671                 case 'face':
    672                     if (this.elementClass == JXG.OBJECT_CLASS_POINT) {
    673                         this.visProp.face = value;
    674                         this.board.renderer.changePointStyle(this);
    675                     }
    676                     break;
    677                 case 'trace':
    678                     if (value == 'false' || value == false) {
    679                         this.clearTrace();
    680                         this.visProp.trace = false;
    681                     } else {
    682                         this.visProp.trace = true;
    683                     }
    684                     break;
    685                 case 'gradient':
    686                     this.visProp.gradient = value;
    687                     this.board.renderer.setGradient(this);
    688                     break;
    689                 case 'gradientsecondcolor':
    690                     value = JXG.rgba2rgbo(value);
    691                     this.visProp.gradientsecondcolor = value[0];
    692                     this.visProp.gradientsecondopacity = value[1];
    693                     this.board.renderer.updateGradient(this);
    694                     break;
    695                 case 'gradientsecondopacity':
    696                     this.visProp.gradientsecondopacity = value;
    697                     this.board.renderer.updateGradient(this);
    698                     break;
    699                 case 'withlabel':
    700                     this.visProp.withlabel = value;
    701                     if (!value) {
    702                         if (this.label && this.label.content && this.hasLabel) {
    703                             this.label.content.hideElement();
    704                         }
    705                     } else {
    706                         if (this.label && this.label.content) {
    707                             if (this.visProp.visible) {
    708                                 this.label.content.showElement();
    709                             }
    710                         } else {
    711                             this.createLabel();
    712                             if (!this.visProp.visible) {
    713                                 this.label.content.hideElement();
    714                             }
    715                         }
    716                     }
    717                     this.hasLabel = value;
    718                     break;
    719                 default:
    720                     if (JXG.exists(this.visProp[key]) && (!JXG.Validator[key] || (JXG.Validator[key] && JXG.Validator[key](value)) || (JXG.Validator[key] && JXG.isFunction(value) && JXG.Validator[key](value())))) {
    721                         value = value.toLowerCase && value.toLowerCase() === 'false' ? false : value;
    722                         this._set(key, value);
    723                     }
    724                     break;
    725             }
    726         }
    727
    728         if (this.type == JXG.OBJECT_TYPE_AXIS)
    729             this.board.fullUpdate();
    730
    731         this.board.update(this);
    732         return this;
    733     },
    734
    735     /**
    736      * Get the value of the property <tt>key</tt>.
    737      * @param {String} key The name of the property you are looking for
    738      * @returns The value of the property
    739      */
    740     getAttribute: JXG.shortcut(JXG.GeometryElement.prototype, 'getProperty'),
    741
    742     /**
    743      * Deprecated alias for {@link JXG.GeometryElement#getAttribute}.
    744      * @deprecated Use {@link JXG.GeometryElement#getAttribute}.
    745      */
    746     getProperty: function (key) {
    747         var result;
    748         key = key.toLowerCase();
    749
    750         switch (key) {
    751             case 'needsregularupdate':
    752                 result = this.needsRegularUpdate;
    753                 break;
    754             case 'labelcolor':
    755                 result = this.label.color;
    756                 break;
    757             case 'infoboxtext':
    758                 result = this.infoboxText;
    759                 break;
    760             case 'withlabel':
    761                 result = this.hasLabel;
    762                 break;
    763             default:
    764                 result = this.visProp[key];
    765                 break;
    766         }
    767
    768         return result;
    769     },
    770
    771     /**
    772      * Set the dash style of an object. See {@link #dash} for a list of available dash styles.
    773      * You should use {@link #setProperty} instead of this method.
    774      * @param {number} dash Indicates the new dash style
    775      * @private
    776      */
    777     setDash: function (dash) {
    778         this.setProperty({dash: dash});
    779         return this;
    780     },
    781
    782     /**
    783      * Notify all child elements for updates.
    784      * @private
    785      */
    786     prepareUpdate: function () {
    787         this.needsUpdate = true;
    788         return this;
    789     },
    790
    791     /**
    792      * Removes the element from the construction.  This only removes the SVG or VML node of the element and its label (if available) from
    793      * the renderer, to remove the element completely you should use {@link JXG.Board#removeObject}.
    794      */
    795     remove: function () {
    796         this.board.renderer.remove(this.board.renderer.getElementById(this.id));
    797
    798         if (this.hasLabel) {
    799             this.board.renderer.remove(this.board.renderer.getElementById(this.label.content.id));
    800         }
    801         return this;
    802     },
    803
    804     /**
    805      * Returns the coords object where a text that is bound to the element shall be drawn.
    806      * Differs in some cases from the values that getLabelAnchor returns.
    807      * @returns {JXG.Coords} JXG.Coords Place where the text shall be drawn.
    808      * @see JXG.GeometryElement#getLabelAnchor
    809      */
    810     getTextAnchor: function () {
    811         return new JXG.Coords(JXG.COORDS_BY_USER, [0,0], this.board);
    812     },
    813
    814     /**
    815      * Returns the coords object where the label of the element shall be drawn.
    816      * Differs in some cases from the values that getTextAnchor returns.
    817      * @returns {JXG.Coords} JXG.Coords Place where the text shall be drawn.
    818      * @see JXG.GeometryElement#getTextAnchor
    819      */
    820     getLabelAnchor: function () {
    821         return new JXG.Coords(JXG.COORDS_BY_USER, [0,0], this.board);
    822     },
    823
    824     /**
    825      * TODO
    826      * Was hat das hier verloren? "Straights" gibts doch nur fuer Lines oder?
    827      * Sollte das dann nicht nur in Line.js zu finden sein? --michael
    828      * @private
    829      */
    830     setStraight: function (x,y) {
    831         return this;
    832     },
    833
    834     /**
    835      * Determines whether the arc has arrows at start or end of the arc.
    836      * @param {bool} firstArrow True if there is an arrow at the start of the arc, false otherwise.
    837      * @param {bool} lastArrow True if there is an arrow at the end of the arc, false otherwise.
    838      * Is stored at visProp['firstarrow'] and visProp['lastarrow']
    839      */
    840     setArrow: function (firstArrow, lastArrow) {
    841         this.visProp.firstarrow = firstArrow;
    842         this.visProp.lastarrow = lastArrow;
    843         this.prepareUpdate().update();
    844         return this;
    845     },
    846
    847     /**
    848      * Creates a gradient nodes in the renderer.
    849      * @see JXG.SVGRenderer#setGradient
    850      * @private
    851      */
    852     createGradient: function() {
    853         if (this.visProp.gradient === 'linear' || this.visProp.gradient === 'radial' ) {
    854             this.board.renderer.setGradient(this);
    855         }
    856     },
    857
    858     /**
    859      * Creates a label element for this geometry element.
    860      * @see #addLabelToElement
    861      */
    862     createLabel: function () {
    863         var attr = {};
    864
    865         attr =  JXG.deepCopy(this.visProp.label, null);
    866         attr.id = this.id + 'Label';
    867         attr.isLabel = true;
    868         attr.visible = this.visProp.visible;
    869         attr.anchor = this;
    870
    871         this.nameHTML = JXG.GeonextParser.replaceSup(JXG.GeonextParser.replaceSub(this.name));
    872         this.label = {};
    873
    874         if (this.visProp.withlabel) {
    875             this.label.relativeCoords = [0, 0];
    876
    877             this.label.content = JXG.createText(this.board,
    878                 [this.label.relativeCoords[0], -this.label.relativeCoords[1], this.nameHTML],
    879                 attr);
    880
    881             this.label.content.dump = false;
    882             this.label.color = this.label.content.visProp.strokecolor;
    883
    884             if (!this.visProp.visible) {
    885                 this.label.hiddenByParent = true;
    886                 this.label.content.visProp.visible = false;
    887             }
    888             this.hasLabel = true;
    889         }
    890         return this;
    891     },
    892
    893     /**
    894      * Highlights the element.
    895      */
    896     highlight: function () {
    897         // I know, we have the JXG.Board.highlightedObjects AND JXG.GeometryElement.highlighted and YES we need both.
    898         // Board.highlightedObjects is for the internal highlighting and GeometryElement.highlighted is for user highlighting
    899         // initiated by the user, e.g. through custom DOM events. We can't just pick one because this would break user
    900         // defined highlighting in many ways:
    901         //  * if overriding the highlight() methods the user had to handle the highlightedObjects stuff, otherwise he'd break
    902         //    everything (e.g. the pie chart example http://jsxgraph.uni-bayreuth.de/wiki/index.php/Pie_chart (not exactly
    903         //    user defined but for this type of chart the highlight method was overridden and not adjusted to the changes in here)
    904         //    where it just kept highlighting until the radius of the pie was far beyond infinity...
    905         //  * user defined highlighting would get pointless, everytime the user highlights something using .highlight(), it would get
    906         //    dehighlighted immediately, because highlight puts the element into highlightedObjects and from there it gets dehighlighted
    907         //    through dehighlightAll.
    908         if (!this.highlighted) { // highlight only if not highlighted
    909             this.highlighted = true;
    910             this.board.renderer.highlight(this);
    911         }
    912         return this;
    913     },
    914
    915     /**
    916      * Uses the "normal" properties of the element.
    917      */
    918     noHighlight: function () {
    919         // see comment in JXG.GeometryElement.highlight()
    920         if (this.highlighted) { // highlight only if not highlighted
    921             this.highlighted = false;
    922             this.board.renderer.noHighlight(this);
    923         }
    924         return this;
    925     },
    926
    927     /**
    928      * Removes all objects generated by the trace function.
    929      */
    930     clearTrace: function () {
    931         var obj;
    932         for (obj in this.traces) {
    933             this.board.renderer.remove(this.traces[obj]);
    934         }
    935         this.numTraces = 0;
    936         return this;
    937     },
    938
    939     /**
    940      * Copy the element to background. This is used for tracing elements.
    941      * @returns {JXG.GeometryElement} A reference to the element
    942      */
    943     cloneToBackground: function () {
    944         return this;
    945     },
    946
    947     /**
    948      * Dimensions of the smallest rectangle enclosing the element.
    949      * @returns {Array} The coordinates of the enclosing rectangle in a format like the bounding box in {@link JXG.Board#setBoundingBox}.
    950      */
    951     bounds: function () { },
    952
    953     /**
    954      * Normalize the element's standard form.
    955      * @private
    956      */
    957     normalize: function () {
    958         this.stdform = JXG.Math.normalize(this.stdform);
    959         return this;
    960     },
    961
    962     /**
    963      * EXPERIMENTAL. Generate JSON object code of visProp and other properties.
    964      * @type string
    965      * @private
    966      * @ignore
    967      * @return JSON string containing element's properties.
    968      */
    969     toJSON: function () {
    970         var json = '{"name":' + this.name;
    971         json += ', ' + '"id":' + this.id;
    972
    973         var vis = [];
    974         for (var key in this.visProp) {
    975             if (this.visProp[key]!=null) {
    976                 vis.push('"' + key + '":' + this.visProp[key]);
    977             }
    978         }
    979         json += ', "visProp":{'+vis.toString()+'}';
    980         json +='}';
    981
    982         return json;
    983     },
    984
    985     /**
    986      * Set the highlightStrokeColor of an element
    987      * @param {String} sColor String which determines the stroke color of an object when its highlighted.
    988      * @see JXG.GeometryElement#highlightStrokeColor
    989      */
    990     highlightStrokeColor: function (sColor) {
    991         this.setProperty({highlightStrokeColor:sColor});
    992         return this;
    993     },
    994
    995     /**
    996      * Set the strokeColor of an element
    997      * @param {String} sColor String which determines the stroke color of an object.
    998      * @see JXG.GeometryElement#strokeColor
    999      */
    1000     strokeColor: function (sColor) {
    1001         this.setProperty({strokeColor:sColor});
    1002         return this;
    1003     },
    1004
    1005     /**
    1006      * Set the strokeWidth of an element
    1007      * @param {Number} width Integer which determines the stroke width of an outline.
    1008      * @see JXG.GeometryElement#strokeWidth
    1009      */
    1010     strokeWidth: function (width) {
    1011         this.setProperty({strokeWidth:width});
    1012         return this;
    1013     },
    1014
    1015
    1016     /**
    1017      * Set the fillColor of an element
    1018      * @param {String} fColor String which determines the fill color of an object.
    1019      * @see JXG.GeometryElement#fillColor
    1020      */
    1021     fillColor: function (fColor) {
    1022         this.setProperty({fillColor:fColor});
    1023         return this;
    1024     },
    1025
    1026     /**
    1027      * Set the highlightFillColor of an element
    1028      * @param {String} fColor String which determines the fill color of an object when its highlighted.
    1029      * @see JXG.GeometryElement#highlightFillColor
    1030      */
    1031     highlightFillColor: function (fColor) {
    1032         this.setProperty({highlightFillColor:fColor});
    1033         return this;
    1034     },
    1035
    1036     /**
    1037      * Set the labelColor of an element
    1038      * @param {String} lColor String which determines the text color of an object's label.
    1039      * @see JXG.GeometryElement#labelColor
    1040      */
    1041     labelColor: function (lColor) {
    1042         this.setProperty({labelColor:lColor});
    1043         return this;
    1044     },
    1045
    1046     /**
    1047      * Set the dash type of an element
    1048      * @param {Number} d Integer which determines the way of dashing an element's outline.
    1049      * @see JXG.GeometryElement#dash
    1050      */
    1051     dash: function (d) {
    1052         this.setProperty({dash:d});
    1053         return this;
    1054     },
    1055
    1056     /**
    1057      * Set the visibility of an element
    1058      * @param {Boolean} v Boolean which determines whether the element is drawn.
    1059      * @see JXG.GeometryElement#visible
    1060      */
    1061     visible: function (v) {
    1062         this.setProperty({visible:v});
    1063         return this;
    1064     },
    1065
    1066     /**
    1067      * Set the shadow of an element
    1068      * @param {Boolean} s Boolean which determines whether the element has a shadow or not.
    1069      * @see JXG.GeometryElement#shadow
    1070      */
    1071     shadow: function (s) {
    1072         this.setProperty({shadow:s});
    1073         return this;
    1074     },
    1075
    1076     /**
    1077      * The type of the element as used in {@link JXG.Board#create}.
    1078      * @returns {String}
    1079      */
    1080     getType: function () {
    1081         return this.elType;
    1082     },
    1083
    1084     /**
    1085      * List of the element ids resp. values used as parents in {@link JXG.Board#create}.
    1086      * @returns {Array}
    1087      */
    1088     getParents: function () {
    1089         return this.parents;
    1090     },
    1091
    1092     /**
    1093      * Retrieve a copy of the current visProp.
    1094      * @returns {Object}
    1095      */
    1096     getAttributes: function () {
    1097         var attributes = JXG.deepCopy(this.visProp),
    1098             cleanThis = ['attractors', 'attractordistance', 'snatchdistance', 'traceattributes', 'frozen',
    1099                 'shadow', 'gradientangle', 'gradientsecondopacity', 'gradientpositionx', 'gradientpositiony',
    1100                 'needsregularupdate', 'zoom', 'layer', 'labeloffsets'],
    1101             i;
    1102
    1103         attributes.id = this.id;
    1104         attributes.name = this.name;
    1105
    1106         for (i = 0; i < cleanThis.length; i++) {
    1107             delete attributes[cleanThis[i]];
    1108         }
    1109
    1110         return attributes;
    1111     },
    1112
    1113     /**
    1114      * Checks whether (x,y) is near the element.
    1115      * @param {Number} x Coordinate in x direction, screen coordinates.
    1116      * @param {Number} y Coordinate in y direction, screen coordinates.
    1117      * @returns {Boolean} True if (x,y) is near the element, False otherwise.
    1118      */
    1119     hasPoint: function (x, y) {
    1120         return false;
    1121     },
    1122
    1123     /**
    1124      * Triggers all event handlers of this element for a given event.
    1125      * @param {String} event
    1126      */
    1127     triggerEventHandlers: function (event) {
    1128         var i;
    1129
    1130         if (JXG.isArray(this.eventHandlers[event])) {
    1131             for (i = 0; i < this.eventHandlers[event].length; i++) {
    1132                 this.eventHandlers[event][i].call(this);
    1133             }
    1134         }
    1135     },
    1136
    1137     /**
    1138      * Register a new event handler
    1139      * @param {String} event
    1140      * @param {Function} handler
    1141      */
    1142     on: function (event, handler) {
    1143         if (!JXG.isArray(this.eventHandlers[event])) {
    1144             this.eventHandlers[event] = [];
    1145         }
    1146
    1147         this.eventHandlers[event].push(handler);
    1148     },
    1149
    1150     /**
    1151      * Alias of {@link JXG.GeometryElement#on}.
    1152      */
    1153     addEvent: JXG.shortcut(JXG.GeometryElement.prototype, 'on'),
    1154
    1155     /**
    1156      * Unregister an event handler.
    1157      * @param {String} event
    1158      * @param {Function} handler The same function used in {@link JXG.GeometryElement#on}.
    1159      */
    1160     off: function (event, handler) {
    1161         var i;
    1162
    1163         if (!event || !JXG.isArray(this.eventHandlers[event])) {
    1164             return;
    1165         }
    1166
    1167         if (handler) {
    1168             i = JXG.indexOf(this.eventHandlers[event], handler);
    1169             if (i > -1) {
    1170                 this.eventHandlers[event].splice(i, 1);
    1171             }
    1172         } else {
    1173             this.eventHandlers[event].length = 0;
    1174         }
    1175     },
    1176
    1177     /**
    1178      * Alias of {@link JXG.GeometryElement#off}.
    1179      */
    1180     removeEvent: JXG.shortcut(JXG.GeometryElement.prototype, 'off')
    1181 });
    1182