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             if (!Type.exists(this[what]) && Type.exists(element)) {
180                 if (Type.exists(element.id)) {
181                     this.elements[element.id] = element;
182                 } else {
183                     this.elements[what] = element;
184                 }
185 
186                 if (Type.exists(element.name)) {
187                     this.elementsByName[element.name] = element;
188                 }
189 
190                 element.on('attribute:name', this.nameListener, this);
191 
192                 this.objectsList.push(element);
193                 this[what] = element;
194                 this.methodMap[what] = element;
195 
196                 return true;
197             }
198 
199             return false;
200         },
201 
202         /**
203          * Remove an element from the composition container.
204          * @param {String} what The name used to access the element.
205          * @returns {Boolean} True, if the element has been removed successfully.
206          */
207         remove: function (what) {
208             var found = false,
209                 e;
210 
211             for (e in this.elements) {
212                 if (this.elements.hasOwnProperty(e)) {
213                     if (this.elements[e].id === this[what].id) {
214                         found = true;
215                         break;
216                     }
217                 }
218             }
219 
220             if (found) {
221                 delete this.elements[this[what].id];
222                 delete this[what];
223             }
224 
225             return found;
226         },
227 
228         nameListener: function (oval, nval, el) {
229             delete this.elementsByName[oval];
230             this.elementsByName[nval] = el;
231         },
232 
233         select: function (filter) {
234             // for now, hijack JXG.Board's select() method
235             if (Type.exists(JXG.Board)) {
236                 return JXG.Board.prototype.select.call(this, filter);
237             }
238 
239             return new JXG.Composition();
240         },
241 
242         getParents: function () {
243             return this.parents;
244         },
245 
246         getType: function () {
247             return this.elType;
248         },
249 
250         getAttributes: function () {
251             var attr = {},
252                 e;
253 
254             for (e in this.subs) {
255                 if (this.subs.hasOwnProperty(e)) {
256                     attr[e] = this.subs[e].visProp;
257                 }
258             }
259 
260             return this.attr;
261         }
262     });
263 
264     return JXG.Composition;
265 });
266