1 /* 2 Copyright 2008-2013 3 Matthias Ehmann, 4 Michael Gerhaeuser, 5 Carsten Miller, 6 Bianca Valentin, 7 Alfred Wassermann, 8 Peter Wilfahrt 9 10 This file is part of JSXGraph. 11 12 JSXGraph is free software dual licensed under the GNU LGPL or MIT License. 13 14 You can redistribute it and/or modify it under the terms of the 15 16 * GNU Lesser General Public License as published by 17 the Free Software Foundation, either version 3 of the License, or 18 (at your option) any later version 19 OR 20 * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT 21 22 JSXGraph is distributed in the hope that it will be useful, 23 but WITHOUT ANY WARRANTY; without even the implied warranty of 24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 GNU Lesser General Public License for more details. 26 27 You should have received a copy of the GNU Lesser General Public License and 28 the MIT License along with JSXGraph. If not, see <http://www.gnu.org/licenses/> 29 and <http://opensource.org/licenses/MIT/>. 30 */ 31 32 33 /*global JXG: true, define: true*/ 34 /*jslint nomen: true, plusplus: true*/ 35 36 /* depends: 37 jxg 38 base/constants 39 base/coords 40 math/math 41 options 42 parser/geonext 43 utils/event 44 utils/color 45 utils/type 46 */ 47 48 define([ 49 'jxg', 'base/constants', 'base/coords', 'math/math', 'options', 'parser/geonext', 'utils/event', 'utils/color', 'utils/type' 50 ], function (JXG, Const, Coords, Mat, Options, GeonextParser, EventEmitter, Color, Type) { 51 52 "use strict"; 53 54 /** 55 * Constructs a new GeometryElement object. 56 * @class This is the basic class for geometry elements like points, circles and lines. 57 * @constructor 58 * @param {JXG.Board} board Reference to the board the element is constructed on. 59 * @param {Object} attributes Hash of attributes and their values. 60 * @param {Number} type Element type (a <tt>JXG.OBJECT_TYPE_</tt> value). 61 * @param {Number} oclass The element's class (a <tt>JXG.OBJECT_CLASS_</tt> value). 62 * @borrows JXG.EventEmitter#on as this.on 63 * @borrows JXG.EventEmitter#off as this.off 64 * @borrows JXG.EventEmitter#triggerEventHandlers as this.triggerEventHandlers 65 * @borrows JXG.EventEmitter#eventHandlers as this.eventHandlers 66 */ 67 JXG.GeometryElement = function (board, attributes, type, oclass) { 68 var name, key, attr; 69 70 /** 71 * Controls if updates are necessary 72 * @type Boolean 73 * @default true 74 */ 75 this.needsUpdate = true; 76 77 /** 78 * Controls if this element can be dragged. In GEONExT only 79 * free points and gliders can be dragged. 80 * @type Boolean 81 * @default false 82 */ 83 this.isDraggable = false; 84 85 /** 86 * If element is in two dimensional real space this is true, else false. 87 * @type Boolean 88 * @default true 89 */ 90 this.isReal = true; 91 92 /** 93 * Stores all dependent objects to be updated when this point is moved. 94 * @type Object 95 */ 96 this.childElements = {}; 97 98 /** 99 * If element has a label subelement then this property will be set to true. 100 * @type Boolean 101 * @default false 102 */ 103 this.hasLabel = false; 104 105 /** 106 * True, if the element is currently highlighted. 107 * @type Boolean 108 * @default false 109 */ 110 this.highlighted = false; 111 112 /** 113 * Stores all Intersection Objects which in this moment are not real and 114 * so hide this element. 115 * @type Object 116 */ 117 this.notExistingParents = {}; 118 119 /** 120 * Keeps track of all objects drawn as part of the trace of the element. 121 * @see JXG.GeometryElement#traced 122 * @see JXG.GeometryElement#clearTrace 123 * @see JXG.GeometryElement#numTraces 124 * @type Object 125 */ 126 this.traces = {}; 127 128 /** 129 * Counts the number of objects drawn as part of the trace of the element. 130 * @see JXG.GeometryElement#traced 131 * @see JXG.GeometryElement#clearTrace 132 * @see JXG.GeometryElement#traces 133 * @type Number 134 */ 135 this.numTraces = 0; 136 137 /** 138 * Stores the transformations which are applied during update in an array 139 * @type Array 140 * @see JXG.Transformation 141 */ 142 this.transformations = []; 143 144 /** 145 * @type JXG.GeometryElement 146 * @default null 147 * @private 148 */ 149 this.baseElement = null; 150 151 /** 152 * Elements depending on this element are stored here. 153 * @type Object 154 */ 155 this.descendants = {}; 156 157 /** 158 * Elements on which this elements depends on are stored here. 159 * @type Object 160 */ 161 this.ancestors = {}; 162 163 /** 164 * Stores variables for symbolic computations 165 * @type Object 166 */ 167 this.symbolic = {}; 168 169 /** 170 * Stores the rendering node for the element. 171 * @type Object 172 */ 173 this.rendNode = null; 174 175 /** 176 * The string used with {@link JXG.Board#create} 177 * @type String 178 */ 179 this.elType = ''; 180 181 /** 182 * The element is saved with an explicit entry in the file (<tt>true</tt>) or implicitly 183 * via a composition. 184 * @type Boolean 185 * @default true 186 */ 187 this.dump = true; 188 189 /** 190 * Subs contains the subelements, created during the create method. 191 * @type Object 192 */ 193 this.subs = {}; 194 195 /** 196 * The position of this element inside the {@link JXG.Board#objectsList}. 197 * @type {Number} 198 * @default -1 199 * @private 200 */ 201 this._pos = -1; 202 203 /** 204 * [c,b0,b1,a,k,r,q0,q1] 205 * 206 * See 207 * A.E. Middleditch, T.W. Stacey, and S.B. Tor: 208 * "Intersection Algorithms for Lines and Circles", 209 * ACM Transactions on Graphics, Vol. 8, 1, 1989, pp 25-40. 210 * 211 * The meaning of the parameters is: 212 * Circle: points p=[p0,p1] on the circle fulfill 213 * a<p,p> + <b,p> + c = 0 214 * For convenience we also store 215 * r: radius 216 * k: discriminant = sqrt(<b,b>-4ac) 217 * q=[q0,q1] center 218 * 219 * Points have radius = 0. 220 * Lines have radius = infinity. 221 * b: normalized vector, representing the direction of the line. 222 * 223 * Should be put into Coords, when all elements possess Coords. 224 * @type Array 225 * @default [1, 0, 0, 0, 1, 1, 0, 0] 226 */ 227 this.stdform = [1, 0, 0, 0, 1, 1, 0, 0]; 228 229 /** 230 * The methodMap determines which methods can be called from within JessieCode and under which name it 231 * can be used. The map is saved in an object, the name of a property is the name of the method used in JessieCode, 232 * the value of a property is the name of the method in JavaScript. 233 * @type Object 234 */ 235 this.methodMap = { 236 setLabel: 'setLabelText', 237 label: 'label', 238 setName: 'setName', 239 getName: 'getName', 240 addTransform: 'addTransform', 241 setProperty: 'setAttribute', 242 setAttribute: 'setAttribute', 243 animate: 'animate', 244 on: 'on', 245 off: 'off', 246 trigger: 'trigger' 247 }; 248 249 /** 250 * Quadratic form representation of circles (and conics) 251 * @type Array 252 * @default [[1,0,0],[0,1,0],[0,0,1]] 253 */ 254 this.quadraticform = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]; 255 256 /** 257 * An associative array containing all visual properties. 258 * @type Object 259 * @default empty object 260 */ 261 this.visProp = {}; 262 263 EventEmitter.eventify(this); 264 265 /** 266 * Is the mouse over this element? 267 * @type Boolean 268 * @default false 269 */ 270 this.mouseover = false; 271 272 /** 273 * Time stamp containing the last time this element has been dragged. 274 * @type Date 275 * @default creation time 276 */ 277 this.lastDragTime = new Date(); 278 279 if (arguments.length > 0) { 280 /** 281 * Reference to the board associated with the element. 282 * @type JXG.Board 283 */ 284 this.board = board; 285 286 /** 287 * Type of the element. 288 * @constant 289 * @type number 290 */ 291 this.type = type; 292 293 /** 294 * The element's class. 295 * @constant 296 * @type number 297 */ 298 this.elementClass = oclass || Const.OBJECT_CLASS_OTHER; 299 300 /** 301 * Unique identifier for the element. Equivalent to id-attribute of renderer element. 302 * @type String 303 */ 304 this.id = attributes.id; 305 306 name = attributes.name; 307 /* If name is not set or null or even undefined, generate an unique name for this object */ 308 if (!Type.exists(name)) { 309 name = this.board.generateName(this); 310 } 311 312 if (name !== '') { 313 this.board.elementsByName[name] = this; 314 } 315 316 /** 317 * Not necessarily unique name for the element. 318 * @type String 319 * @default Name generated by {@link JXG.Board#generateName}. 320 * @see JXG.Board#generateName 321 */ 322 this.name = name; 323 324 this.needsRegularUpdate = attributes.needsregularupdate; 325 326 // create this.visPropOld and set default values 327 Type.clearVisPropOld(this); 328 329 attr = this.resolveShortcuts(attributes); 330 for (key in attr) { 331 if (attr.hasOwnProperty(key)) { 332 this._set(key, attr[key]); 333 } 334 } 335 336 this.visProp.draft = attr.draft && attr.draft.draft; 337 this.visProp.gradientangle = '270'; 338 this.visProp.gradientsecondopacity = this.visProp.fillopacity; 339 this.visProp.gradientpositionx = 0.5; 340 this.visProp.gradientpositiony = 0.5; 341 } 342 }; 343 344 JXG.extend(JXG.GeometryElement.prototype, /** @lends JXG.GeometryElement.prototype */ { 345 /** 346 * Add an element as a child to the current element. Can be used to model dependencies between geometry elements. 347 * @param {JXG.GeometryElement} obj The dependent object. 348 */ 349 addChild: function (obj) { 350 var el, el2; 351 352 this.childElements[obj.id] = obj; 353 this.addDescendants(obj); 354 obj.ancestors[this.id] = this; 355 356 for (el in this.descendants) { 357 if (this.descendants.hasOwnProperty(el)) { 358 this.descendants[el].ancestors[this.id] = this; 359 360 for (el2 in this.ancestors) { 361 if (this.ancestors.hasOwnProperty(el2)) { 362 this.descendants[el].ancestors[this.ancestors[el2].id] = this.ancestors[el2]; 363 } 364 } 365 } 366 } 367 368 for (el in this.ancestors) { 369 if (this.ancestors.hasOwnProperty(el)) { 370 for (el2 in this.descendants) { 371 if (this.descendants.hasOwnProperty(el2)) { 372 this.ancestors[el].descendants[this.descendants[el2].id] = this.descendants[el2]; 373 } 374 } 375 } 376 } 377 return this; 378 }, 379 380 /** 381 * Adds the given object to the descendants list of this object and all its child objects. 382 * @param {JXG.GeometryElement} obj The element that is to be added to the descendants list. 383 * @private 384 * @return 385 */ 386 addDescendants: function (obj) { 387 var el; 388 389 this.descendants[obj.id] = obj; 390 for (el in obj.childElements) { 391 if (obj.childElements.hasOwnProperty(el)) { 392 this.addDescendants(obj.childElements[el]); 393 } 394 } 395 return this; 396 }, 397 398 /** 399 * Remove an element as a child from the current element. 400 * @param {JXG.GeometryElement} obj The dependent object. 401 */ 402 removeChild: function (obj) { 403 var el, el2; 404 405 delete this.childElements[obj.id]; 406 this.removeDescendants(obj); 407 delete obj.ancestors[this.id]; 408 409 /* 410 // I do not know if these addDescendants stuff has to be adapted to removeChild. A.W. 411 for (el in this.descendants) { 412 if (this.descendants.hasOwnProperty(el)) { 413 delete this.descendants[el].ancestors[this.id]; 414 415 for (el2 in this.ancestors) { 416 if (this.ancestors.hasOwnProperty(el2)) { 417 this.descendants[el].ancestors[this.ancestors[el2].id] = this.ancestors[el2]; 418 } 419 } 420 } 421 } 422 423 for (el in this.ancestors) { 424 if (this.ancestors.hasOwnProperty(el)) { 425 for (el2 in this.descendants) { 426 if (this.descendants.hasOwnProperty(el2)) { 427 this.ancestors[el].descendants[this.descendants[el2].id] = this.descendants[el2]; 428 } 429 } 430 } 431 } 432 */ 433 return this; 434 }, 435 436 /** 437 * Removes the given object from the descendants list of this object and all its child objects. 438 * @param {JXG.GeometryElement} obj The element that is to be removed from the descendants list. 439 * @private 440 * @return 441 */ 442 removeDescendants: function (obj) { 443 var el; 444 445 delete this.descendants[obj.id]; 446 for (el in obj.childElements) { 447 if (obj.childElements.hasOwnProperty(el)) { 448 this.removeDescendants(obj.childElements[el]); 449 } 450 } 451 return this; 452 }, 453 454 /** 455 * Counts the direct children of an object without counting labels. 456 * @private 457 * @return {number} Number of children 458 */ 459 countChildren: function () { 460 var prop, d, 461 s = 0; 462 463 d = this.childElements; 464 for (prop in d) { 465 if (d.hasOwnProperty(prop) && prop.indexOf('Label') < 0) { 466 s++; 467 } 468 } 469 return s; 470 }, 471 472 /** 473 * Returns the elements name, Used in JessieCode. 474 * @returns {String} 475 */ 476 getName: function () { 477 return this.name; 478 }, 479 480 /** 481 * Add transformations to this element. 482 * @param {JXG.Transformation|Array} transform Either one {@link JXG.Transformation} or an array of {@link JXG.Transformation}s. 483 * @returns {JXG.GeometryElement} Reference to the element. 484 */ 485 addTransform: function (transform) { 486 return this; 487 }, 488 489 /** 490 * Decides whether an element can be dragged. This is used in setPositionDirectly methods 491 * where all parent elements are checked if they may be dragged, too. 492 * @private 493 * @return {boolean} 494 */ 495 draggable: function () { 496 return this.isDraggable && !this.visProp.fixed && 497 !this.visProp.frozen && this.type !== Const.OBJECT_TYPE_GLIDER; 498 }, 499 500 /** 501 * Array of strings containing the polynomials defining the element. 502 * Used for determining geometric loci the groebner way. 503 * @returns {Array} An array containing polynomials describing the locus of the current object. 504 * @public 505 */ 506 generatePolynomial: function () { 507 return []; 508 }, 509 510 /** 511 * Animates properties for that object like stroke or fill color, opacity and maybe 512 * even more later. 513 * @param {Object} hash Object containing propiertes with target values for the animation. 514 * @param {number} time Number of milliseconds to complete the animation. 515 * @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> 516 * @returns {JXG.GeometryElement} A reference to the object 517 */ 518 animate: function (hash, time, options) { 519 options = options || {}; 520 var r, p, i, 521 delay = this.board.attr.animationdelay, 522 steps = Math.ceil(time / delay), 523 self = this, 524 525 animateColor = function (startRGB, endRGB, property) { 526 var hsv1, hsv2, sh, ss, sv; 527 hsv1 = Color.rgb2hsv(startRGB); 528 hsv2 = Color.rgb2hsv(endRGB); 529 530 sh = (hsv2[0] - hsv1[0]) / steps; 531 ss = (hsv2[1] - hsv1[1]) / steps; 532 sv = (hsv2[2] - hsv1[2]) / steps; 533 self.animationData[property] = []; 534 535 for (i = 0; i < steps; i++) { 536 self.animationData[property][steps - i - 1] = Color.hsv2rgb(hsv1[0] + (i + 1) * sh, hsv1[1] + (i + 1) * ss, hsv1[2] + (i + 1) * sv); 537 } 538 }, 539 540 animateFloat = function (start, end, property, round) { 541 var tmp, s; 542 543 start = parseFloat(start); 544 end = parseFloat(end); 545 546 // we can't animate without having valid numbers. 547 // And parseFloat returns NaN if the given string doesn't contain 548 // a valid float number. 549 if (isNaN(start) || isNaN(end)) { 550 return; 551 } 552 553 s = (end - start) / steps; 554 self.animationData[property] = []; 555 556 for (i = 0; i < steps; i++) { 557 tmp = start + (i + 1) * s; 558 self.animationData[property][steps - i - 1] = round ? Math.floor(tmp) : tmp; 559 } 560 }; 561 562 this.animationData = {}; 563 564 for (r in hash) { 565 if (hash.hasOwnProperty(r)) { 566 p = r.toLowerCase(); 567 568 switch (p) { 569 case 'strokecolor': 570 case 'fillcolor': 571 animateColor(this.visProp[p], hash[r], p); 572 break; 573 case 'size': 574 if (this.elementClass !== Const.OBJECT_CLASS_POINT) { 575 break; 576 } 577 animateFloat(this.visProp[p], hash[r], p, true); 578 break; 579 case 'strokeopacity': 580 case 'strokewidth': 581 case 'fillopacity': 582 animateFloat(this.visProp[p], hash[r], p, false); 583 break; 584 } 585 } 586 } 587 588 this.animationCallback = options.callback; 589 this.board.addAnimation(this); 590 return this; 591 }, 592 593 /** 594 * General update method. Should be overwritten by the element itself. 595 * Can be used sometimes to commit changes to the object. 596 */ 597 update: function () { 598 if (this.visProp.trace) { 599 this.cloneToBackground(); 600 } 601 return this; 602 }, 603 604 /** 605 * Provide updateRenderer method. 606 * @private 607 */ 608 updateRenderer: function () { 609 return this; 610 }, 611 612 /** 613 * Hide the element. It will still exist but not visible on the board. 614 */ 615 hideElement: function () { 616 this.visProp.visible = false; 617 this.board.renderer.hide(this); 618 619 if (Type.exists(this.label) && this.hasLabel) { 620 this.label.hiddenByParent = true; 621 if (this.label.visProp.visible) { 622 this.label.hideElement(); 623 } 624 } 625 return this; 626 }, 627 628 /** 629 * Make the element visible. 630 */ 631 showElement: function () { 632 this.visProp.visible = true; 633 this.board.renderer.show(this); 634 635 if (Type.exists(this.label) && this.hasLabel && this.label.hiddenByParent) { 636 this.label.hiddenByParent = false; 637 if (!this.label.visProp.visible) { 638 this.label.showElement().updateRenderer(); 639 } 640 } 641 return this; 642 }, 643 644 /** 645 * Sets the value of property <tt>property</tt> to <tt>value</tt>. 646 * @param {String} property The property's name. 647 * @param value The new value 648 * @private 649 */ 650 _set: function (property, value) { 651 property = property.toLocaleLowerCase(); 652 653 // Search for entries in visProp with "color" as part of the property name 654 // and containing a RGBA string 655 if (this.visProp.hasOwnProperty(property) && property.indexOf('color') >= 0 && 656 Type.isString(value) && value.length === 9 && value.charAt(0) === '#') { 657 value = Color.rgba2rgbo(value); 658 this.visProp[property] = value[0]; 659 // Previously: *=. But then, we can only decrease opacity. 660 this.visProp[property.replace('color', 'opacity')] = value[1]; 661 } else { 662 this.visProp[property] = value; 663 } 664 }, 665 666 /** 667 * Resolves property shortcuts like <tt>color</tt> and expands them, e.g. <tt>strokeColor</tt> and <tt>fillColor</tt>. 668 * Writes the expanded properties back to the given <tt>properties</tt>. 669 * @param {Object} properties 670 * @returns {Object} The given parameter with shortcuts expanded. 671 */ 672 resolveShortcuts: function (properties) { 673 var key, i; 674 675 for (key in Options.shortcuts) { 676 if (Options.shortcuts.hasOwnProperty(key)) { 677 if (Type.exists(properties[key])) { 678 for (i = 0; i < Options.shortcuts[key].length; i++) { 679 if (!Type.exists(properties[Options.shortcuts[key][i]])) { 680 properties[Options.shortcuts[key][i]] = properties[key]; 681 } 682 } 683 } 684 } 685 } 686 return properties; 687 }, 688 689 /** 690 * Updates the element's label text, strips all html. 691 * @param {String} str 692 */ 693 setLabelText: function (str) { 694 695 if (Type.exists(this.label)) { 696 str = str.replace(/</g, '<').replace(/>/g, '>'); 697 this.label.setText(str); 698 } 699 700 return this; 701 }, 702 703 /** 704 * Updates the element's label text and the element's attribute "name", strips all html. 705 * @param {String} str 706 */ 707 setName: function (str) { 708 str = str.replace(/</g, '<').replace(/>/g, '>'); 709 this.setLabelText(str); 710 this.setAttribute({name: str}); 711 }, 712 713 /** 714 * Deprecated alias for {@link JXG.GeometryElement#setAttribute}. 715 * @deprecated Use {@link JXG.GeometryElement#setAttribute}. 716 */ 717 setProperty: JXG.shortcut(JXG.GeometryElement.prototype, 'setAttribute'), 718 719 /** 720 * Sets an arbitrary number of attributes. 721 * @param {Object} attributes An object with attributes. 722 * @function 723 * @example 724 * // Set property directly on creation of an element using the attributes object parameter 725 * var board = JXG.JSXGraph.initBoard('jxgbox', {boundingbox: [-1, 5, 5, 1]}; 726 * var p = board.create('point', [2, 2], {visible: false}); 727 * 728 * // Now make this point visible and fixed: 729 * p.setAttribute({ 730 * fixed: true, 731 * visible: true 732 * }); 733 */ 734 setAttribute: function (attributes) { 735 var i, key, value, arg, opacity, pair, oldvalue, 736 properties = {}, 737 makeTicksFunction = function (v) { 738 return function (i) { 739 return v; 740 }; 741 }; 742 743 // normalize the user input 744 for (i = 0; i < arguments.length; i++) { 745 arg = arguments[i]; 746 if (Type.isString(arg)) { 747 // pairRaw is string of the form 'key:value' 748 pair = arg.split(':'); 749 properties[Type.trim(pair[0])] = Type.trim(pair[1]); 750 } else if (!Type.isArray(arg)) { 751 // pairRaw consists of objects of the form {key1:value1,key2:value2,...} 752 JXG.extend(properties, arg); 753 } else { 754 // pairRaw consists of array [key,value] 755 properties[arg[0]] = arg[1]; 756 } 757 } 758 759 // handle shortcuts 760 properties = this.resolveShortcuts(properties); 761 762 for (i in properties) { 763 if (properties.hasOwnProperty(i)) { 764 key = i.replace(/\s+/g, '').toLowerCase(); 765 value = properties[i]; 766 oldvalue = this.visProp[key]; 767 768 switch (key) { 769 case 'name': 770 oldvalue = this.name; 771 delete this.board.elementsByName[this.name]; 772 this.name = value; 773 this.board.elementsByName[this.name] = this; 774 break; 775 case 'needsregularupdate': 776 this.needsRegularUpdate = !(value === 'false' || value === false); 777 this.board.renderer.setBuffering(this, this.needsRegularUpdate ? 'auto' : 'static'); 778 break; 779 case 'labelcolor': 780 value = Color.rgba2rgbo(value); 781 opacity = value[1]; 782 value = value[0]; 783 if (opacity === 0) { 784 if (Type.exists(this.label) && this.hasLabel) { 785 this.label.hideElement(); 786 } 787 } 788 if (Type.exists(this.label) && this.hasLabel) { 789 this.label.visProp.strokecolor = value; 790 this.board.renderer.setObjectStrokeColor(this.label, value, opacity); 791 } 792 if (this.type === Const.OBJECT_TYPE_TEXT) { 793 this.visProp.strokecolor = value; 794 this.visProp.strokeopacity = opacity; 795 this.board.renderer.setObjectStrokeColor(this, this.visProp.strokecolor, this.visProp.strokeopacity); 796 } 797 break; 798 case 'infoboxtext': 799 if (typeof value === 'string') { 800 this.infoboxText = value; 801 } else { 802 this.infoboxText = false; 803 } 804 break; 805 case 'visible': 806 if (value === 'false' || value === false) { 807 this.visProp.visible = false; 808 this.hideElement(); 809 } else if (value === 'true' || value === true) { 810 this.visProp.visible = true; 811 this.showElement(); 812 } 813 break; 814 case 'face': 815 if (this.elementClass === Const.OBJECT_CLASS_POINT) { 816 this.visProp.face = value; 817 this.board.renderer.changePointStyle(this); 818 } 819 break; 820 case 'trace': 821 if (value === 'false' || value === false) { 822 this.clearTrace(); 823 this.visProp.trace = false; 824 } else { 825 this.visProp.trace = true; 826 } 827 break; 828 case 'gradient': 829 this.visProp.gradient = value; 830 this.board.renderer.setGradient(this); 831 break; 832 case 'gradientsecondcolor': 833 value = Color.rgba2rgbo(value); 834 this.visProp.gradientsecondcolor = value[0]; 835 this.visProp.gradientsecondopacity = value[1]; 836 this.board.renderer.updateGradient(this); 837 break; 838 case 'gradientsecondopacity': 839 this.visProp.gradientsecondopacity = value; 840 this.board.renderer.updateGradient(this); 841 break; 842 case 'withlabel': 843 this.visProp.withlabel = value; 844 if (!value) { 845 if (this.label && this.hasLabel) { 846 this.label.hideElement(); 847 } 848 } else { 849 if (this.label) { 850 if (this.visProp.visible) { 851 this.label.showElement(); 852 } 853 } else { 854 this.createLabel(); 855 if (!this.visProp.visible) { 856 this.label.hideElement(); 857 } 858 } 859 } 860 this.hasLabel = value; 861 break; 862 case 'radius': 863 if (this.type === Const.OBJECT_TYPE_ANGLE || this.type === Const.OBJECT_TYPE_SECTOR) { 864 this.setRadius(value); 865 } 866 break; 867 case 'rotate': 868 if ((this.type === Const.OBJECT_TYPE_TEXT && this.visProp.display === 'internal') || 869 this.type === Const.OBJECT_TYPE_IMAGE) { 870 this.addRotation(value); 871 } 872 break; 873 case 'ticksdistance': 874 if (this.type === Const.OBJECT_TYPE_TICKS && typeof value === 'number') { 875 this.ticksFunction = makeTicksFunction(value); 876 } 877 break; 878 case 'generatelabelvalue': 879 if (this.type === Const.OBJECT_TYPE_TICKS && typeof value === 'function') { 880 this.generateLabelValue = value; 881 } 882 break; 883 case 'onpolygon': 884 if (this.type === Const.OBJECT_TYPE_GLIDER) { 885 this.onPolygon = !!value; 886 } 887 break; 888 default: 889 if (Type.exists(this.visProp[key]) && (!JXG.Validator[key] || (JXG.Validator[key] && 890 JXG.Validator[key](value)) || (JXG.Validator[key] && 891 Type.isFunction(value) && JXG.Validator[key](value())))) { 892 value = value.toLowerCase && value.toLowerCase() === 'false' ? false : value; 893 this._set(key, value); 894 } 895 break; 896 } 897 this.triggerEventHandlers(['attribute:' + key], [oldvalue, value, this]); 898 } 899 } 900 901 this.triggerEventHandlers(['attribute'], [properties, this]); 902 903 if (!this.visProp.needsregularupdate) { 904 this.board.fullUpdate(); 905 } else { 906 this.board.update(this); 907 } 908 909 return this; 910 }, 911 912 /** 913 * Deprecated alias for {@link JXG.GeometryElement#getAttribute}. 914 * @deprecated Use {@link JXG.GeometryElement#getAttribute}. 915 */ 916 getProperty: JXG.shortcut(JXG.GeometryElement.prototype, 'getAttribute'), 917 918 /** 919 * Get the value of the property <tt>key</tt>. 920 * @param {String} key The name of the property you are looking for 921 * @returns The value of the property 922 */ 923 getAttribute: function (key) { 924 var result; 925 key = key.toLowerCase(); 926 927 switch (key) { 928 case 'needsregularupdate': 929 result = this.needsRegularUpdate; 930 break; 931 case 'labelcolor': 932 result = this.label.visProp.strokecolor; 933 break; 934 case 'infoboxtext': 935 result = this.infoboxText; 936 break; 937 case 'withlabel': 938 result = this.hasLabel; 939 break; 940 default: 941 result = this.visProp[key]; 942 break; 943 } 944 945 return result; 946 }, 947 948 /** 949 * Set the dash style of an object. See {@link #dash} for a list of available dash styles. 950 * You should use {@link #setAttribute} instead of this method. 951 * @param {number} dash Indicates the new dash style 952 * @private 953 */ 954 setDash: function (dash) { 955 this.setAttribute({dash: dash}); 956 return this; 957 }, 958 959 /** 960 * Notify all child elements for updates. 961 * @private 962 */ 963 prepareUpdate: function () { 964 this.needsUpdate = true; 965 return this; 966 }, 967 968 /** 969 * Removes the element from the construction. This only removes the SVG or VML node of the element and its label (if available) from 970 * the renderer, to remove the element completely you should use {@link JXG.Board#removeObject}. 971 */ 972 remove: function () { 973 this.board.renderer.remove(this.board.renderer.getElementById(this.id)); 974 975 if (this.hasLabel) { 976 this.board.renderer.remove(this.board.renderer.getElementById(this.label.id)); 977 } 978 return this; 979 }, 980 981 /** 982 * Returns the coords object where a text that is bound to the element shall be drawn. 983 * Differs in some cases from the values that getLabelAnchor returns. 984 * @returns {JXG.Coords} JXG.Coords Place where the text shall be drawn. 985 * @see JXG.GeometryElement#getLabelAnchor 986 */ 987 getTextAnchor: function () { 988 return new Coords(Const.COORDS_BY_USER, [0, 0], this.board); 989 }, 990 991 /** 992 * Returns the coords object where the label of the element shall be drawn. 993 * Differs in some cases from the values that getTextAnchor returns. 994 * @returns {JXG.Coords} JXG.Coords Place where the text shall be drawn. 995 * @see JXG.GeometryElement#getTextAnchor 996 */ 997 getLabelAnchor: function () { 998 return new Coords(Const.COORDS_BY_USER, [0, 0], this.board); 999 }, 1000 1001 /** 1002 * Determines whether the element has arrows at start or end of the arc. 1003 * @param {Boolean} firstArrow True if there is an arrow at the start of the arc, false otherwise. 1004 * @param {Boolean} lastArrow True if there is an arrow at the end of the arc, false otherwise. 1005 */ 1006 setArrow: function (firstArrow, lastArrow) { 1007 this.visProp.firstarrow = firstArrow; 1008 this.visProp.lastarrow = lastArrow; 1009 this.prepareUpdate().update(); 1010 return this; 1011 }, 1012 1013 /** 1014 * Creates a gradient nodes in the renderer. 1015 * @see JXG.SVGRenderer#setGradient 1016 * @private 1017 */ 1018 createGradient: function () { 1019 if (this.visProp.gradient === 'linear' || this.visProp.gradient === 'radial') { 1020 this.board.renderer.setGradient(this); 1021 } 1022 }, 1023 1024 /** 1025 * Creates a label element for this geometry element. 1026 * @see #addLabelToElement 1027 */ 1028 createLabel: function () { 1029 var attr, 1030 that = this; 1031 1032 // this is a dirty hack to resolve the text-dependency. If there is no text element available, 1033 // just don't create a label. This method is usually not called by a user, so we won't throw 1034 // an exception here and simply output a warning via JXG.debug. 1035 if (JXG.elements.text) { 1036 attr = Type.deepCopy(this.visProp.label, null); 1037 attr.id = this.id + 'Label'; 1038 attr.isLabel = true; 1039 attr.visible = this.visProp.visible; 1040 attr.anchor = this; 1041 attr.priv = this.visProp.priv; 1042 1043 if (this.visProp.withlabel) { 1044 this.label = JXG.elements.text(this.board, [0, 0, function () { 1045 if (typeof that.name === 'function') { 1046 return that.name(); 1047 } 1048 return that.name; 1049 }], attr); 1050 this.label.needsUpdate = true; 1051 this.label.update(); 1052 1053 this.label.dump = false; 1054 1055 if (!this.visProp.visible) { 1056 this.label.hiddenByParent = true; 1057 this.label.visProp.visible = false; 1058 } 1059 this.hasLabel = true; 1060 } 1061 } else { 1062 JXG.debug('JSXGraph: Can\'t create label: text element is not available. Make sure you include base/text'); 1063 } 1064 1065 return this; 1066 }, 1067 1068 /** 1069 * Highlights the element. 1070 * @param {Boolean} [force=false] Force the highlighting 1071 * @returns {JXG.Board} 1072 */ 1073 highlight: function (force) { 1074 force = Type.def(force, false); 1075 // I know, we have the JXG.Board.highlightedObjects AND JXG.GeometryElement.highlighted and YES we need both. 1076 // Board.highlightedObjects is for the internal highlighting and GeometryElement.highlighted is for user highlighting 1077 // initiated by the user, e.g. through custom DOM events. We can't just pick one because this would break user 1078 // defined highlighting in many ways: 1079 // * if overriding the highlight() methods the user had to handle the highlightedObjects stuff, otherwise he'd break 1080 // everything (e.g. the pie chart example http://jsxgraph.uni-bayreuth.de/wiki/index.php/Pie_chart (not exactly 1081 // user defined but for this type of chart the highlight method was overridden and not adjusted to the changes in here) 1082 // where it just kept highlighting until the radius of the pie was far beyond infinity... 1083 // * user defined highlighting would get pointless, everytime the user highlights something using .highlight(), it would get 1084 // dehighlighted immediately, because highlight puts the element into highlightedObjects and from there it gets dehighlighted 1085 // through dehighlightAll. 1086 1087 // highlight only if not highlighted 1088 if (this.visProp.highlight && (!this.highlighted || force)) { 1089 this.highlighted = true; 1090 this.board.highlightedObjects[this.id] = this; 1091 this.board.renderer.highlight(this); 1092 } 1093 return this; 1094 }, 1095 1096 /** 1097 * Uses the "normal" properties of the element. 1098 * @returns {JXG.Board} 1099 */ 1100 noHighlight: function () { 1101 // see comment in JXG.GeometryElement.highlight() 1102 1103 // dehighlight only if not highlighted 1104 if (this.highlighted) { 1105 this.highlighted = false; 1106 delete this.board.highlightedObjects[this.id]; 1107 this.board.renderer.noHighlight(this); 1108 } 1109 return this; 1110 }, 1111 1112 /** 1113 * Removes all objects generated by the trace function. 1114 */ 1115 clearTrace: function () { 1116 var obj; 1117 1118 for (obj in this.traces) { 1119 if (this.traces.hasOwnProperty(obj)) { 1120 this.board.renderer.remove(this.traces[obj]); 1121 } 1122 } 1123 1124 this.numTraces = 0; 1125 return this; 1126 }, 1127 1128 /** 1129 * Copy the element to background. This is used for tracing elements. 1130 * @returns {JXG.GeometryElement} A reference to the element 1131 */ 1132 cloneToBackground: function () { 1133 return this; 1134 }, 1135 1136 /** 1137 * Dimensions of the smallest rectangle enclosing the element. 1138 * @returns {Array} The coordinates of the enclosing rectangle in a format like the bounding box in {@link JXG.Board#setBoundingBox}. 1139 */ 1140 bounds: function () { 1141 return [0, 0, 0, 0]; 1142 }, 1143 1144 /** 1145 * Normalize the element's standard form. 1146 * @private 1147 */ 1148 normalize: function () { 1149 this.stdform = Mat.normalize(this.stdform); 1150 return this; 1151 }, 1152 1153 /** 1154 * EXPERIMENTAL. Generate JSON object code of visProp and other properties. 1155 * @type string 1156 * @private 1157 * @ignore 1158 * @return JSON string containing element's properties. 1159 */ 1160 toJSON: function () { 1161 var vis, key, 1162 json = ['{"name":', this.name]; 1163 1164 json.push(', ' + '"id":' + this.id); 1165 1166 vis = []; 1167 for (key in this.visProp) { 1168 if (this.visProp.hasOwnProperty(key)) { 1169 if (Type.exists(this.visProp[key])) { 1170 vis.push('"' + key + '":' + this.visProp[key]); 1171 } 1172 } 1173 } 1174 json.push(', "visProp":{' + vis.toString() + '}'); 1175 json.push('}'); 1176 1177 return json.join(''); 1178 }, 1179 1180 1181 /** 1182 * Rotate texts or images by a given degree. Works only for texts where JXG.Text#display equal to "internal". 1183 * @param {number} angle The degree of the rotation (90 means vertical text). 1184 * @see JXG.GeometryElement#rotate 1185 */ 1186 addRotation: function (angle) { 1187 var tOffInv, tOff, tS, tSInv, tRot, 1188 that = this; 1189 1190 if (((this.type === Const.OBJECT_TYPE_TEXT && this.visProp.display === 'internal') || 1191 this.type === Const.OBJECT_TYPE_IMAGE) && angle !== 0) { 1192 1193 tOffInv = this.board.create('transform', [ 1194 function () { 1195 return -that.X(); 1196 }, function () { 1197 return -that.Y(); 1198 } 1199 ], {type: 'translate'}); 1200 1201 tOff = this.board.create('transform', [ 1202 function () { 1203 return that.X(); 1204 }, function () { 1205 return that.Y(); 1206 } 1207 ], {type: 'translate'}); 1208 1209 tS = this.board.create('transform', [ 1210 function () { 1211 return that.board.unitX / that.board.unitY; 1212 }, function () { 1213 return 1; 1214 } 1215 ], {type: 'scale'}); 1216 1217 tSInv = this.board.create('transform', [ 1218 function () { 1219 return that.board.unitY / that.board.unitX; 1220 }, function () { 1221 return 1; 1222 } 1223 ], {type: 'scale'}); 1224 1225 tRot = this.board.create('transform', [angle * Math.PI / 180], {type: 'rotate'}); 1226 1227 tOffInv.bindTo(this); 1228 tS.bindTo(this); 1229 tRot.bindTo(this); 1230 tSInv.bindTo(this); 1231 tOff.bindTo(this); 1232 } 1233 1234 return this; 1235 }, 1236 1237 /** 1238 * Set the highlightStrokeColor of an element 1239 * @param {String} sColor String which determines the stroke color of an object when its highlighted. 1240 * @see JXG.GeometryElement#highlightStrokeColor 1241 * @deprecated Use {@link #setAttribute} 1242 */ 1243 highlightStrokeColor: function (sColor) { 1244 this.setAttribute({highlightStrokeColor: sColor}); 1245 return this; 1246 }, 1247 1248 /** 1249 * Set the strokeColor of an element 1250 * @param {String} sColor String which determines the stroke color of an object. 1251 * @see JXG.GeometryElement#strokeColor 1252 * @deprecated Use {@link #setAttribute} 1253 */ 1254 strokeColor: function (sColor) { 1255 this.setAttribute({strokeColor: sColor}); 1256 return this; 1257 }, 1258 1259 /** 1260 * Set the strokeWidth of an element 1261 * @param {Number} width Integer which determines the stroke width of an outline. 1262 * @see JXG.GeometryElement#strokeWidth 1263 * @deprecated Use {@link #setAttribute} 1264 */ 1265 strokeWidth: function (width) { 1266 this.setAttribute({strokeWidth: width}); 1267 return this; 1268 }, 1269 1270 1271 /** 1272 * Set the fillColor of an element 1273 * @param {String} fColor String which determines the fill color of an object. 1274 * @see JXG.GeometryElement#fillColor 1275 * @deprecated Use {@link #setAttribute} 1276 */ 1277 fillColor: function (fColor) { 1278 this.setAttribute({fillColor: fColor}); 1279 return this; 1280 }, 1281 1282 /** 1283 * Set the highlightFillColor of an element 1284 * @param {String} fColor String which determines the fill color of an object when its highlighted. 1285 * @see JXG.GeometryElement#highlightFillColor 1286 * @deprecated Use {@link #setAttribute} 1287 */ 1288 highlightFillColor: function (fColor) { 1289 this.setAttribute({highlightFillColor: fColor}); 1290 return this; 1291 }, 1292 1293 /** 1294 * Set the labelColor of an element 1295 * @param {String} lColor String which determines the text color of an object's label. 1296 * @see JXG.GeometryElement#labelColor 1297 * @deprecated Use {@link #setAttribute} 1298 */ 1299 labelColor: function (lColor) { 1300 this.setAttribute({labelColor: lColor}); 1301 return this; 1302 }, 1303 1304 /** 1305 * Set the dash type of an element 1306 * @param {Number} d Integer which determines the way of dashing an element's outline. 1307 * @see JXG.GeometryElement#dash 1308 * @deprecated Use {@link #setAttribute} 1309 */ 1310 dash: function (d) { 1311 this.setAttribute({dash: d}); 1312 return this; 1313 }, 1314 1315 /** 1316 * Set the visibility of an element 1317 * @param {Boolean} v Boolean which determines whether the element is drawn. 1318 * @see JXG.GeometryElement#visible 1319 * @deprecated Use {@link #setAttribute} 1320 */ 1321 visible: function (v) { 1322 this.setAttribute({visible: v}); 1323 return this; 1324 }, 1325 1326 /** 1327 * Set the shadow of an element 1328 * @param {Boolean} s Boolean which determines whether the element has a shadow or not. 1329 * @see JXG.GeometryElement#shadow 1330 * @deprecated Use {@link #setAttribute} 1331 */ 1332 shadow: function (s) { 1333 this.setAttribute({shadow: s}); 1334 return this; 1335 }, 1336 1337 /** 1338 * The type of the element as used in {@link JXG.Board#create}. 1339 * @returns {String} 1340 */ 1341 getType: function () { 1342 return this.elType; 1343 }, 1344 1345 /** 1346 * List of the element ids resp. values used as parents in {@link JXG.Board#create}. 1347 * @returns {Array} 1348 */ 1349 getParents: function () { 1350 return Type.isArray(this.parents) ? this.parents : []; 1351 }, 1352 1353 /** 1354 * Snaps the element to the grid. Only works for points, lines and circles. Points will snap to the grid 1355 * as defined in their properties {@link JXG.Point#snapSizeX} and {@link JXG.Point#snapSizeY}. Lines and circles 1356 * will snap their parent points to the grid, if they have {@link JXG.Point#snapToGrid} set to true. 1357 * @returns {JXG.GeometryElement} Reference to the element. 1358 */ 1359 snapToGrid: function () { 1360 return this; 1361 }, 1362 1363 /** 1364 * Snaps the element to points. Only works for points. Points will snap to the next point 1365 * as defined in their properties {@link JXG.Point#attractorDistance} and {@link JXG.Point#attractorUnit}. 1366 * Lines and circles 1367 * will snap their parent points to points. 1368 * @returns {JXG.GeometryElement} Reference to the element. 1369 */ 1370 snapToPoints: function () { 1371 return this; 1372 }, 1373 1374 /** 1375 * Retrieve a copy of the current visProp. 1376 * @returns {Object} 1377 */ 1378 getAttributes: function () { 1379 var attributes = Type.deepCopy(this.visProp), 1380 cleanThis = ['attractors', 'snatchdistance', 'traceattributes', 'frozen', 1381 'shadow', 'gradientangle', 'gradientsecondopacity', 'gradientpositionx', 'gradientpositiony', 1382 'needsregularupdate', 'zoom', 'layer', 'offset'], 1383 i; 1384 1385 attributes.id = this.id; 1386 attributes.name = this.name; 1387 1388 for (i = 0; i < cleanThis.length; i++) { 1389 delete attributes[cleanThis[i]]; 1390 } 1391 1392 return attributes; 1393 }, 1394 1395 /** 1396 * Checks whether (x,y) is near the element. 1397 * @param {Number} x Coordinate in x direction, screen coordinates. 1398 * @param {Number} y Coordinate in y direction, screen coordinates. 1399 * @returns {Boolean} True if (x,y) is near the element, False otherwise. 1400 */ 1401 hasPoint: function (x, y) { 1402 return false; 1403 }, 1404 1405 /** 1406 * Move an element to its nearest grid point. 1407 * The function uses the coords object of the element as 1408 * its actual position. If there is no coords object, nothing is done. 1409 * @param {Boolean} force force snapping independent from what the snaptogrid attribute says 1410 * @returns {JXG.Element} Reference to this element 1411 */ 1412 handleSnapToGrid: function (force) { 1413 var x, y, ticks, 1414 sX = this.visProp.snapsizex, 1415 sY = this.visProp.snapsizey; 1416 1417 if (!JXG.exists(this.coords)) { 1418 return this; 1419 } 1420 1421 if (this.visProp.snaptogrid || force === true) { 1422 x = this.coords.usrCoords[1]; 1423 y = this.coords.usrCoords[2]; 1424 1425 if (sX <= 0 && this.board.defaultAxes && this.board.defaultAxes.x.defaultTicks) { 1426 ticks = this.board.defaultAxes.x.defaultTicks; 1427 sX = ticks.ticksDelta * (ticks.visProp.minorticks + 1); 1428 } 1429 1430 if (sY <= 0 && this.board.defaultAxes && this.board.defaultAxes.y.defaultTicks) { 1431 ticks = this.board.defaultAxes.y.defaultTicks; 1432 sY = ticks.ticksDelta * (ticks.visProp.minorticks + 1); 1433 } 1434 1435 // if no valid snap sizes are available, don't change the coords. 1436 if (sX > 0 && sY > 0) { 1437 this.coords.setCoordinates(Const.COORDS_BY_USER, [Math.round(x / sX) * sX, Math.round(y / sY) * sY]); 1438 } 1439 } 1440 return this; 1441 }, 1442 1443 /** 1444 * Alias of {@link JXG.GeometryElement#on}. 1445 */ 1446 addEvent: JXG.shortcut(JXG.GeometryElement.prototype, 'on'), 1447 1448 /** 1449 * Alias of {@link JXG.GeometryElement#off}. 1450 */ 1451 removeEvent: JXG.shortcut(JXG.GeometryElement.prototype, 'off'), 1452 1453 /* ************************** 1454 * EVENT DEFINITION 1455 * for documentation purposes 1456 * ************************** */ 1457 1458 //region Event handler documentation 1459 /** 1460 * @event 1461 * @description This event is fired whenever the user is hovering over an element. 1462 * @name JXG.GeometryElement#over 1463 * @param {Event} e The browser's event object. 1464 */ 1465 __evt__over: function (e) { }, 1466 1467 /** 1468 * @event 1469 * @description This event is fired whenever the user puts the mouse over an element. 1470 * @name JXG.GeometryElement#mouseover 1471 * @param {Event} e The browser's event object. 1472 */ 1473 __evt__mouseover: function (e) { }, 1474 1475 /** 1476 * @event 1477 * @description This event is fired whenever the user is leaving an element. 1478 * @name JXG.GeometryElement#out 1479 * @param {Event} e The browser's event object. 1480 */ 1481 __evt__out: function (e) { }, 1482 1483 /** 1484 * @event 1485 * @description This event is fired whenever the user puts the mouse away from an element. 1486 * @name JXG.GeometryElement#mouseout 1487 * @param {Event} e The browser's event object. 1488 */ 1489 __evt__mouseout: function (e) { }, 1490 1491 /** 1492 * @event 1493 * @description This event is fired whenever the user is moving over an element. 1494 * @name JXG.GeometryElement#move 1495 * @param {Event} e The browser's event object. 1496 */ 1497 __evt__move: function (e) { }, 1498 1499 /** 1500 * @event 1501 * @description This event is fired whenever the user is moving the mouse over an element. 1502 * @name JXG.GeometryElement#mousemove 1503 * @param {Event} e The browser's event object. 1504 */ 1505 __evt__mousemove: function (e) { }, 1506 1507 /** 1508 * @event 1509 * @description This event is fired whenever the user drags an element. 1510 * @name JXG.GeometryElement#drag 1511 * @param {Event} e The browser's event object. 1512 */ 1513 __evt__drag: function (e) { }, 1514 1515 /** 1516 * @event 1517 * @description This event is fired whenever the user drags the element with a mouse. 1518 * @name JXG.GeometryElement#mousedrag 1519 * @param {Event} e The browser's event object. 1520 */ 1521 __evt__mousedrag: function (e) { }, 1522 1523 /** 1524 * @event 1525 * @description This event is fired whenever the user drags the element on a touch device. 1526 * @name JXG.GeometryElement#touchdrag 1527 * @param {Event} e The browser's event object. 1528 */ 1529 __evt__touchdrag: function (e) { }, 1530 1531 /** 1532 * @event 1533 * @description Whenever the user starts to touch or click an element. 1534 * @name JXG.GeometryElement#down 1535 * @param {Event} e The browser's event object. 1536 */ 1537 __evt__down: function (e) { }, 1538 1539 /** 1540 * @event 1541 * @description Whenever the user starts to click an element. 1542 * @name JXG.GeometryElement#mousedown 1543 * @param {Event} e The browser's event object. 1544 */ 1545 __evt__mousedown: function (e) { }, 1546 1547 /** 1548 * @event 1549 * @description Whenever the user starts to touch an element. 1550 * @name JXG.GeometryElement#touchdown 1551 * @param {Event} e The browser's event object. 1552 */ 1553 __evt__touchdown: function (e) { }, 1554 1555 /** 1556 * @event 1557 * @description Whenever the user stops to touch or click an element. 1558 * @name JXG.GeometryElement#up 1559 * @param {Event} e The browser's event object. 1560 */ 1561 __evt__up: function (e) { }, 1562 1563 /** 1564 * @event 1565 * @description Whenever the user releases the mousebutton over an element. 1566 * @name JXG.GeometryElement#mouseup 1567 * @param {Event} e The browser's event object. 1568 */ 1569 __evt__mouseup: function (e) { }, 1570 1571 /** 1572 * @event 1573 * @description Whenever the user stops touching an element. 1574 * @name JXG.GeometryElement#touchup 1575 * @param {Event} e The browser's event object. 1576 */ 1577 __evt__touchup: function (e) {}, 1578 1579 /** 1580 * @event 1581 * @description Notify everytime an attribute is changed. 1582 * @name JXG.GeometryElement#attribute 1583 * @param {Object} o A list of changed attributes and their new value. 1584 * @param {Object} el Reference to the element 1585 */ 1586 __evt__attribute: function (o, el) {}, 1587 1588 /** 1589 * @event 1590 * @description This is a generic event handler. It exists for every possible attribute that can be set for 1591 * any element, e.g. if you want to be notified everytime an element's strokecolor is changed, is the event 1592 * <tt>attribute:strokecolor</tt>. 1593 * @name JXG.GeometryElement#attribute:<attribute> 1594 * @param val The old value. 1595 * @param nval The new value 1596 * @param {Object} el Reference to the element 1597 */ 1598 __evt__attribute_: function (val, nval, el) {}, 1599 1600 /** 1601 * @ignore 1602 */ 1603 __evt: function () {} 1604 //endregion 1605 1606 }); 1607 1608 return JXG.GeometryElement; 1609 }); 1610