      1 /*
      2  Copyright 2008-2025
      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     this.methodMap = {
    159         setAttribute: "setAttribute",
    160         setProperty: "setAttribute",
    161         setParents: "setParents",
    162         add: "add",
    163         remove: "remove",
    164         select: "select"
    165     };
    166
    167     for (e in elements) {
    168         if (elements.hasOwnProperty(e)) {
    169             this.add(e, elements[e]);
    170         }
    171     }
    172
    173     this.dump = true;
    174     this.subs = {};
    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                 this.methodMap[what] = element;
    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                 delete this[what];
    236             }
    237
    238             return found;
    239         },
    240
    241         nameListener: function (oval, nval, el) {
    242             delete this.elementsByName[oval];
    243             this.elementsByName[nval] = el;
    244         },
    245
    246         select: function (filter) {
    247             // for now, hijack JXG.Board's select() method
    248             if (Type.exists(JXG.Board)) {
    249                 return JXG.Board.prototype.select.call(this, filter);
    250             }
    251
    252             return new JXG.Composition();
    253         },
    254
    255         getParents: function () {
    256             return this.parents;
    257         },
    258
    259         getType: function () {
    260             return this.elType;
    261         },
    262
    263         getAttributes: function () {
    264             var attr = {},
    265                 e;
    266
    267             for (e in this.subs) {
    268                 if (this.subs.hasOwnProperty(e)) {
    269                     attr[e] = this.subs[e].visProp;
    270                 }
    271             }
    272
    273             return this.attr;
    274         }
    275     }
    276 );
    277
    278 export default JXG.Composition;
    279