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