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