1 /* 2 Copyright 2008-2026 3 Matthias Ehmann, 4 Michael Gerhaeuser, 5 Carsten Miller, 6 Bianca Valentin, 7 Alfred Wassermann, 8 Peter Wilfahrt 9 10 This file is part of JSXGraph. 11 12 JSXGraph is free software dual licensed under the GNU LGPL or MIT License. 13 14 You can redistribute it and/or modify it under the terms of the 15 16 * GNU Lesser General Public License as published by 17 the Free Software Foundation, either version 3 of the License, or 18 (at your option) any later version 19 OR 20 * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT 21 22 JSXGraph is distributed in the hope that it will be useful, 23 but WITHOUT ANY WARRANTY; without even the implied warranty of 24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 GNU Lesser General Public License for more details. 26 27 You should have received a copy of the GNU Lesser General Public License and 28 the MIT License along with JSXGraph. If not, see <https://www.gnu.org/licenses/> 29 and <https://opensource.org/licenses/MIT/>. 30 */ 31 32 /*global JXG: true, define: true*/ 33 /*jslint nomen: true, plusplus: true*/ 34 35 import JXG from "../jxg.js"; 36 import Type from "../utils/type.js"; 37 38 /** 39 * A composition is a simple container that manages none or more {@link JXG.GeometryElement}s. 40 * @param {Object} elements A list of elements with a descriptive name for the element as the key and a reference 41 * to the element as the value of every list entry. The name is used to access the element later on. 42 * @example 43 * var p1 = board.create('point', [1, 2]), 44 * p2 = board.create('point', [2, 3]), 45 * c = new JXG.Composition({ 46 * start: p1, 47 * end: p2 48 * }); 49 * 50 * // moves p1 to [3, 3] 51 * c.start.moveTo([3, 3]); 52 * @class JXG.Composition 53 */ 54 JXG.Composition = function (elements) { 55 var e, 56 that = this, 57 genericMethods = [ 58 /** 59 * Invokes setAttribute for every stored element with a setAttribute method and hands over the given arguments. 60 * See {@link JXG.GeometryElement#setAttribute} for further description, valid parameters and return values. 61 * @name setAttribute 62 * @memberOf JXG.Composition.prototype 63 * @function 64 */ 65 "setAttribute", 66 67 /** 68 * Invokes setParents for every stored element with a setParents method and hands over the given arguments. 69 * See {@link JXG.GeometryElement#setParents} for further description, valid parameters and return values. 70 * @name setParents 71 * @memberOf JXG.Composition.prototype 72 * @function 73 */ 74 "setParents", 75 76 /** 77 * Invokes prepareUpdate for every stored element with a prepareUpdate method and hands over the given arguments. 78 * See {@link JXG.GeometryElement#prepareUpdate} for further description, valid parameters and return values. 79 * @name prepareUpdate 80 * @memberOf JXG.Composition.prototype 81 * @function 82 */ 83 "prepareUpdate", 84 85 /** 86 * Invokes updateRenderer for every stored element with a updateRenderer method and hands over the given arguments. 87 * See {@link JXG.GeometryElement#updateRenderer} for further description, valid parameters and return values. 88 * @name updateRenderer 89 * @memberOf JXG.Composition.prototype 90 * @function 91 */ 92 "updateRenderer", 93 94 /** 95 * Invokes update for every stored element with a update method and hands over the given arguments. 96 * See {@link JXG.GeometryElement#update} for further description, valid parameters and return values. 97 * @name update 98 * @memberOf JXG.Composition.prototype 99 * @function 100 */ 101 "update", 102 103 /** 104 * Invokes fullUpdate for every stored element with a fullUpdate method and hands over the given arguments. 105 * See {@link JXG.GeometryElement#fullUpdate} for further description, valid parameters and return values. 106 * @name fullUpdate 107 * @memberOf JXG.Composition.prototype 108 * @function 109 */ 110 "fullUpdate", 111 112 /** 113 * Invokes highlight for every stored element with a highlight method and hands over the given arguments. 114 * See {@link JXG.GeometryElement#highlight} for further description, valid parameters and return values. 115 * @name highlight 116 * @memberOf JXG.Composition.prototype 117 * @function 118 */ 119 "highlight", 120 121 /** 122 * Invokes noHighlight for every stored element with a noHighlight method and hands over the given arguments. 123 * See {@link JXG.GeometryElement#noHighlight} for further description, valid parameters and return values. 124 * @name noHighlight 125 * @memberOf JXG.Composition.prototype 126 * @function 127 */ 128 "noHighlight" 129 ], 130 generateMethod = function (what) { 131 return function () { 132 var i; 133 134 for (i in that.elements) { 135 if (that.elements.hasOwnProperty(i)) { 136 if (Type.exists(that.elements[i][what])) { 137 that.elements[i][what].apply(that.elements[i], arguments); 138 } 139 } 140 } 141 return that; 142 }; 143 }; 144 145 for (e = 0; e < genericMethods.length; e++) { 146 this[genericMethods[e]] = generateMethod(genericMethods[e]); 147 } 148 149 this.elements = {}; 150 this.objects = this.elements; 151 152 this.elementsByName = {}; 153 this.objectsList = []; 154 155 // unused, required for select() 156 this.groups = {}; 157 158 for (e in elements) { 159 if (elements.hasOwnProperty(e)) { 160 this.add(e, elements[e]); 161 } 162 } 163 164 this.dump = true; 165 this.subs = {}; 166 }; 167 168 Type.copyMethodMap(JXG.Composition, { 169 setAttribute: "setAttribute", 170 setProperty: "setAttribute", 171 setParents: "setParents", 172 add: "add", 173 remove: "remove", 174 select: "select" 175 }); 176 177 JXG.extend( 178 JXG.Composition.prototype, 179 /** @lends JXG.Composition.prototype */ { 180 /** 181 * Adds an element to the composition container. 182 * @param {String} what Descriptive name for the element, e.g. <em>startpoint</em> or <em>area</em>. This is used to 183 * access the element later on. There are some reserved names: <em>elements, add, remove, update, prepareUpdate, 184 * updateRenderer, highlight, noHighlight</em>, and all names that would form invalid object property names in 185 * JavaScript. 186 * @param {JXG.GeometryElement|JXG.Composition} element A reference to the element that is to be added. This can be 187 * another composition, too. 188 * @returns {Boolean} True, if the element was added successfully. Reasons why adding the element failed include 189 * using a reserved name and providing an invalid element. 190 */ 191 add: function (what, element) { 192 if (!Type.exists(this[what]) && Type.exists(element)) { 193 if (Type.exists(element.id)) { 194 this.elements[element.id] = element; 195 } else { 196 this.elements[what] = element; 197 } 198 199 if (Type.exists(element.name)) { 200 this.elementsByName[element.name] = element; 201 } 202 203 element.on("attribute:name", this.nameListener, this); 204 205 this.objectsList.push(element); 206 this[what] = element; 207 Type.extendInstanceMethodMap(this, what, what); 208 209 return true; 210 } 211 212 return false; 213 }, 214 215 /** 216 * Remove an element from the composition container. 217 * @param {String} what The name used to access the element. 218 * @returns {Boolean} True, if the element has been removed successfully. 219 */ 220 remove: function (what) { 221 var found = false, 222 e; 223 224 for (e in this.elements) { 225 if (this.elements.hasOwnProperty(e)) { 226 if (this.elements[e].id === this[what].id) { 227 found = true; 228 break; 229 } 230 } 231 } 232 233 if (found) { 234 delete this.elements[this[what].id]; 235 if (Type.exists(this[what].name)) { 236 delete this.elementsByName[this[what].name]; 237 } 238 Type.removeElementFromArray(this.objectsList, this[what]); 239 if (this.hasOwnProperty("methodMap")) { 240 delete this.methodMap[what]; 241 } 242 delete this[what]; 243 } 244 245 return found; 246 }, 247 248 nameListener: function (oval, nval, el) { 249 delete this.elementsByName[oval]; 250 this.elementsByName[nval] = el; 251 }, 252 253 select: function (filter) { 254 // for now, hijack JXG.Board's select() method 255 if (Type.exists(JXG.Board)) { 256 return JXG.Board.prototype.select.call(this, filter); 257 } 258 259 return new JXG.Composition(); 260 }, 261 262 getParents: function () { 263 return this.parents; 264 }, 265 266 getType: function () { 267 return this.elType; 268 }, 269 270 getAttributes: function () { 271 var attr = {}, 272 e; 273 274 for (e in this.subs) { 275 if (this.subs.hasOwnProperty(e)) { 276 attr[e] = this.subs[e].visProp; 277 } 278 } 279 280 return this.attr; 281 } 282 } 283 ); 284 285 export default JXG.Composition; 286