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