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