1 /*
  2     Copyright 2010
  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: you can redistribute it and/or modify
 13     it under the terms of the GNU Lesser General Public License as published by
 14     the Free Software Foundation, either version 3 of the License, or
 15     (at your option) any later version.
 16 
 17     JSXGraph is distributed in the hope that it will be useful,
 18     but WITHOUT ANY WARRANTY; without even the implied warranty of
 19     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 20     GNU Lesser General Public License for more details.
 21 
 22     You should have received a copy of the GNU Lesser General Public License
 23     along with JSXGraph.  If not, see <http://www.gnu.org/licenses/>.
 24 */
 25 /**
 26  * @param {String} string A string containing construction(s) in JSXGraph Construction Syntax.
 27  * @param {String} mode Possible values seem are "normal" or "macro"
 28  * @param {Array} params Parameters, only used in macro mode
 29  * @param {Array} paraIn Parameters, only used in macro mode
 30  * @param {String} macroName Name of the macro, only used in macro mode
 31  * @type object
 32  * @return An object consisting of several arrays (lines, circles, points, angles, ...) where the created elements are stored.
 33  */
 34 JXG.Board.prototype.construct = function(string, mode, params, paraIn, macroName) {
 35     var splitted, i, j, output = {}, objName, defElements, obj, type, possibleNames, tmp, noMacro, k,l, pattern, createdNames, found,
 36         mac, prop, propName, propValue, attributes;
 37     if(!JXG.exists(mode)) {
 38         mode = "normal";
 39     }
 40     else { // mode = 'macro'
 41         createdNames = [];
 42     }
 43     output.lines = [];
 44     output.circles = [];
 45     output.points = [];
 46     output.intersections = [];
 47     output.angles = [];
 48     output.macros = [];
 49     output.functions = [];
 50     output.texts = [];
 51     output.polygons = [];
 52     if(string.search(/\{/) != -1) { // Macros finden! Innerhalb der {} darf nicht am ; getrennt werden. Noch nicht getestet: mehrere Makros hintereinander in einem construct.
 53         tmp = string.match(/\{/);
 54         tmp = tmp.length;
 55         l=0;
 56         for(j=0; j<tmp; j++) {
 57             k = string.slice(l).search(/\{/);
 58             mac = string.slice(k);
 59             mac = mac.slice(0,mac.search(/\}/)+1);
 60             mac = mac.replace(/;/g,'?');   // Achtung! Fragezeichen duerfen daher nicht im Code eines Macros vorkommen!
 61             string = string.slice(0,k) + mac + string.slice(k+mac.length);
 62             l=k+1;
 63         }
 64     }
 65     splitted = string.split(';');
 66     for(i=0; i< splitted.length; i++) {
 67         // Leerzeichen am Anfang und am Ende entfernen
 68         splitted[i] = splitted[i].replace (/^\s+/, '').replace (/\s+$/, '');
 69         if(splitted[i].search(/\{/) != -1) {
 70             splitted[i] = splitted[i].replace(/\?/g,';');
 71         }
 72         if(splitted[i].search(/Macro/) != -1) {
 73             this.addMacro(splitted[i]);
 74         }
 75         else {
 76             if(splitted[i].length > 0) {
 77                 prop = false;
 78                 if(splitted[i].search(/=/) != -1) {
 79                     objName = splitted[i].split('=');
 80                     propValue = objName[1];
 81                     propValue = propValue.replace (/^\s+/, '').replace (/\s+$/, '');
 82                     if(objName[0].search(/\./) != -1) {
 83                         prop = true;
 84                     
 85                         objName = objName[0].split('.');
 86                         propName = objName[objName.length-1];
 87                         propName = propName.replace (/^\s+/, '').replace (/\s+$/, '');
 88                         objName.pop();
 89                         objName = objName.join(".");
 90                         if(mode == 'macro') {
 91                             for(j=0; j<params.length; j++) {
 92                                 if(objName == params[j]) {
 93                                     objName = paraIn[j];
 94                                 }
 95                             }
 96                         }                    
 97                         //alert("_"+objName+"_"+propName+"_"+propValue+"_");
 98                         //alert(JXG.getReference(this,objName).name);
 99                         JXG.getReference(this,objName).setProperty(propName+":"+propValue);
100                         
101                     }
102                 }
103                 if(!prop) { // nicht nur eine Eigenschaft setzen, sondern neues Element konstruieren
104                     if(splitted[i].search(/=/) != -1) {
105                         objName = splitted[i].split('=');
106                         splitted[i] = objName[1].replace (/^\s+/, ''); // Leerzeichen am Anfang entfernen
107                         objName = objName[0].replace (/\s+$/, ''); // Leerzeichen am Ende entfernen
108                     }
109                     else {
110                         objName = '';
111                     }
112                     attributes = {};
113                     found = true;
114                     while(found) {
115                         if(splitted[i].search(/(.*)draft$/) != -1) {
116                             attributes.draft = true;
117                             splitted[i] = RegExp.$1;
118                             splitted[i] = splitted[i].replace (/\s+$/, ''); // Leerzeichen am Ende entfernen
119                         }
120                         if(splitted[i].search(/(.*)invisible$/) != -1) {
121                             attributes.visible = false;
122                             splitted[i] = RegExp.$1;
123                             splitted[i] = splitted[i].replace (/\s+$/, ''); // Leerzeichen am Ende entfernen
124                         }
125                         if(splitted[i].search(/(.*)nolabel$/) != -1) {
126                             attributes.withLabel = false;
127                             splitted[i] = RegExp.$1;
128                             splitted[i] = splitted[i].replace (/\s+$/, ''); // Leerzeichen am Ende entfernen
129                         }
130                         if(splitted[i].search(/nolabel|invisible|draft/) == -1) {
131                             found = false;
132                         }
133                     }
134                     noMacro = true;
135                     if(this.definedMacros) {
136                         for(j=0; j<this.definedMacros.macros.length; j++) {
137                             pattern = new RegExp("^"+this.definedMacros.macros[j][0]+"\\s*\\(");
138                             if(splitted[i].search(pattern) != -1) { // TODO: testen, was mit den Macros xxx und yxxx passiert
139                                 //alert("MACRO!"+splitted[i]+"_"+this.definedMacros.macros[j][2]);
140                                 noMacro = false;
141                                 // Parameter aufdroeseln
142                                 splitted[i].match(/\((.*)\)/);
143                                 tmp = RegExp.$1;
144                                 tmp = tmp.split(',');
145                                 for(k=0; k < tmp.length; k++) {
146                                     tmp[k].match(/\s*(\S*)\s*/);
147                                     tmp[k] = RegExp.$1;
148                                 }
149                                 output[objName] = this.construct(this.definedMacros.macros[j][2],'macro',this.definedMacros.macros[j][1], tmp, objName);
150                                 output.macros.push(output[objName]);
151                                 break;
152                             }
153                         }
154                     }
155                     if(noMacro) { // splitted[i] war kein Macro-Aufruf
156                         if(splitted[i].search(/^[\[\]].*[\[\]]$/) != -1) { // Gerade, Halbgerade oder Segment
157                             splitted[i].match(/([\[\]])(.*)([\[\]])/);
158                             attributes.straightFirst = (RegExp.$1 != '[');
159                             attributes.straightLast = (RegExp.$3 == '[');
160                             defElements = (RegExp.$2).replace (/^\s+/, '').replace (/\s+$/, '');
161                             if(defElements.search(/ /) != -1) {
162                                 defElements.match(/(\S*) +(\S*)/);
163                                 defElements = [];
164                                 defElements[0] = RegExp.$1;
165                                 defElements[1] = RegExp.$2;
166                             } // sonst wird die Gerade durch zwei Punkte definiert, die einen Namen haben, der aus nur jeweils einem Buchstaben besteht
167                             if(objName != '') {
168                                 if(!JXG.exists(attributes.withLabel)) {
169                                     attributes.withLabel = true;
170                                 }
171                                 attributes.name = objName;
172                                 if(mode == 'macro') {
173                                     createdNames.push(objName);
174                                 }
175                             }
176                             if(mode == 'macro') {
177                                 if(macroName != '') {
178                                     for(j=0; j<createdNames.length; j++) { // vorher oder nachher?
179                                         if(defElements[0] == createdNames[j]) {
180                                             defElements[0] = macroName+"."+defElements[0];
181                                         }
182                                         if(defElements[1] == createdNames[j]) {
183                                             defElements[1] = macroName+"."+defElements[1];
184                                         }
185                                     }
186                                 }
187                                 for(j=0; j<params.length; j++) {
188                                     if(defElements[0] == params[j]) {
189                                         defElements = [paraIn[j], defElements[1]];
190                                     }
191                                     if(defElements[1] == params[j]) {
192                                         defElements = [defElements[0], paraIn[j]];
193                                     }
194                                 }
195                                 if(macroName != '') {
196                                     attributes.id = macroName+"."+objName;
197                                 }
198                             }
199                             if(typeof defElements == 'string') {
200                                 defElements = [JXG.getReference(this,defElements.charAt(0)), JXG.getReference(this,defElements.charAt(1))];
201                             }
202                             else {
203                                 defElements = [JXG.getReference(this,defElements[0]), JXG.getReference(this,defElements[1])];
204                             }
205                             output.lines.push(this.create('line',
206                                                     defElements,
207                                                     attributes));
208                             if(objName != '') {
209                                 output[objName] = output.lines[output.lines.length-1];
210                             }
211                         }
212                         else if(splitted[i].search(/k\s*\(.*/) != -1) { // Kreis
213                             splitted[i].match(/k\s*\(\s*(\S.*\S|\S)\s*,\s*(\S.*\S|\S)\s*\)/);
214                             defElements = [];
215                             defElements[0] = RegExp.$1;
216                             defElements[1] = RegExp.$2;
217                             for(j=0; j<=1; j++) {
218                                 if(defElements[j].search(/[\[\]]/) != -1) { // Linie, definiert durch [P_1 P_2] , ist bei den Parametern dabei
219                                     defElements[j].match(/^[\[\]]\s*(\S.*\S)\s*[\[\]]$/);
220                                     defElements[j] = RegExp.$1;
221                                     if(defElements[j].search(/ /) != -1) {
222                                         defElements[j].match(/(\S*) +(\S*)/);
223                                         defElements[j] = [];
224                                         defElements[j][0] = RegExp.$1;
225                                         defElements[j][1] = RegExp.$2;
226                                     } // sonst wird die Gerade durch zwei Punkte definiert, die einen Namen haben, der aus nur jeweils einem Buchstaben besteht
227                                     if(mode == 'macro') {
228                                         if(macroName != '') {
229                                             for(k=0; k<createdNames.length; k++) { // vorher oder nachher?
230                                                 if(defElements[j][0] == createdNames[k]) {
231                                                     defElements[j][0] = macroName+"."+defElements[j][0];
232                                                 }
233                                                 if(defElements[j][1] == createdNames[k]) {
234                                                     defElements[j][1] = macroName+"."+defElements[j][1];
235                                                 }
236                                             }
237                                         }
238                                         for(k=0; k<params.length; k++) {
239                                             if(defElements[j][0] == params[k]) {
240                                                 defElements[j] = [paraIn[k], defElements[j][1]];
241                                             }
242                                             if(defElements[j][1] == params[k]) {
243                                                 defElements[j] = [defElements[j][0], paraIn[k]];
244                                             }
245                                         }
246                                     }
247                                     if(typeof defElements[j] == 'string') {
248                                         defElements[j] = (function(el, board) { return function() {
249                                                                     return JXG.getReference(board,el.charAt(0)).Dist(JXG.getReference(board,el.charAt(1))); // TODO
250                                                                }}
251                                                   )(defElements[j], this);
252                                     }
253                                     else {
254                                         defElements[j] = (function(el, board) { return function() {
255                                                                     return JXG.getReference(board,el[0]).Dist(JXG.getReference(board,el[1])); // TODO
256                                                                }}
257                                                   )(defElements[j], this);
258                                     }
259                                     
260                                 }
261                                 else if(defElements[j].search(/[0-9\.\s]+/) != -1){ // Radius als Zahl
262                                     defElements[j] = 1.0*defElements[j];
263                                 }
264                                 else { // Element mit Name
265                                     if(mode == 'macro') {
266                                         if(macroName != '') {
267                                             for(k=0; k<createdNames.length; k++) { // vorher oder nachher?
268                                                 if(defElements[j] == createdNames[k]) {
269                                                     defElements[j] = macroName+"."+createdNames[k];
270                                                 }
271                                             }
272                                         }
273                                         for(k=0; k<params.length; k++) {
274                                             if(defElements[j] == params[k]) {
275                                                 defElements[j] = paraIn[k];
276                                             }
277                                         }
278                                     }
279                                     defElements[j] = JXG.getReference(this,defElements[j]);
280                                 }
281                             }
282                             if(objName != '') {
283                                 if(!JXG.exists(attributes.withLabel)) {
284                                     attributes.withLabel = true;
285                                 }
286                                 attributes.name = objName;
287                                 if(mode == 'macro') {
288                                     if(macroName != '') {
289                                         attributes.id = macroName+"."+objName;
290                                     }
291                                     createdNames.push(objName);
292                                 }
293                             }
294                             output.circles.push(this.create('circle',defElements,attributes));
295                             if(objName != '') {
296                                 output[objName] = output.circles[output.circles.length-1];
297                             }
298                         }
299                         else if(splitted[i].search(/^[A-Z]+.*\(\s*[0-9\.\-]+\s*[,\|]\s*[0-9\.\-]+\s*\)/) != -1
300                                 && splitted[i].search(/Macro\((.*)\)/) == -1) { // Punkt, startet mit einem Grossbuchstaben! (definiert durch Koordinaten)
301                             splitted[i].match(/^([A-Z]+\S*)\s*\(\s*(.*)\s*[,\|]\s*(.*)\s*\)$/);
302                             objName = RegExp.$1; // Name
303                             attributes.name = objName;
304                             if(mode == 'macro') {
305                                 if(macroName != '') {
306                                     attributes.id = macroName+"."+objName;
307                                 }
308                                 createdNames.push(objName);
309                             }
310                             output.points.push(this.create('point',[1.0*RegExp.$2,1.0*RegExp.$3],attributes));
311                             output[objName] = output.points[output.points.length-1];
312                         }
313                         else if(splitted[i].search(/^[A-Z]+.*\(.+(([,\|]\s*[0-9\.\-]+\s*){2})?/) != -1
314                                 && splitted[i].search(/Macro\((.*)\)/) == -1) { // Gleiter, mit oder ohne Koordinaten
315                             splitted[i].match(/([A-Z]+.*)\((.*)\)/);
316                             objName = RegExp.$1;
317                             defElements = RegExp.$2;
318                             objName = objName.replace (/^\s+/, '').replace (/\s+$/, '');
319                             defElements = defElements.replace (/^\s+/, '').replace (/\s+$/, '');
320                             if(defElements.search(/[,\|]/) != -1) { // Koordinaten angegeben
321                                 defElements.match(/(\S*)\s*[,\|]\s*([0-9\.]+)\s*[,\|]\s*([0-9\.]+)\s*/);
322                                 defElements = [];
323                                 defElements[0] = RegExp.$1;
324                                 defElements[1] = 1.0*RegExp.$2;
325                                 defElements[2] = 1.0*RegExp.$3;
326                             }
327                             else { // keine Koordinaten
328                                 obj = defElements;
329                                 defElements = [];
330                                 defElements[0] = obj; // Name des definierenden Elements
331                                 defElements[1] = 0; // (0,0) als Gleiterkoordinaten vorgeben...
332                                 defElements[2] = 0;
333                             }
334                             attributes.name = objName;
335                             if(mode == 'macro') {
336                                 if(macroName != '') {
337                                     for(k=0; k<createdNames.length; k++) { // vorher oder nachher?
338                                         if(defElements[0] == createdNames[k]) {
339                                             defElements[0] = macroName+"."+createdNames[k];
340                                         }
341                                     }
342                                 }
343                                 for(k=0; k<params.length; k++) {
344                                     if(defElements[0] == params[k]) {
345                                         defElements[0] = paraIn[k];
346                                     }
347                                 }
348                                 if(macroName != '') {
349                                     attributes.id = macroName+"."+objName;
350                                 }
351                                 createdNames.push(objName);
352                             }
353                             output.points.push(this.create('glider',
354                                                                   [defElements[1],defElements[2],JXG.getReference(this,defElements[0])],
355                                                                   attributes));
356                             output[objName] = output.points[output.points.length-1];
357                         }
358                         else if(splitted[i].search(/&/) != -1) { // Schnittpunkt
359                             splitted[i].match(/(.*)&(.*)/);
360                             defElements = [];
361                             defElements[0] = RegExp.$1;
362                             defElements[1] = RegExp.$2;
363                             defElements[0] = defElements[0].replace(/\s+$/, ''); // Leerzeichen am Ende entfernen
364                             defElements[1] = defElements[1].replace (/^\s+/, ''); // Leerzeichen am Anfang entfernen
365                             if(mode == 'macro') {
366                                 for(j=0; j<=1; j++) {
367                                     if(macroName != '') {
368                                         for(k=0; k<createdNames.length; k++) { // vorher oder nachher?
369                                             if(defElements[j] == createdNames[k]) {
370                                                 defElements[j] = macroName+"."+createdNames[k];
371                                             }
372                                         }
373                                     }
374                                     for(k=0; k<params.length; k++) {
375                                         if(defElements[j] == params[k]) {
376                                             defElements[j] = paraIn[k];
377                                         }
378                                     }
379                                 }
380                             }
381                             defElements[0] = JXG.getReference(this,defElements[0]);
382                             defElements[1] = JXG.getReference(this,defElements[1]);
383                             if ((defElements[0].elementClass==JXG.OBJECT_CLASS_LINE || defElements[0].elementClass==JXG.OBJECT_CLASS_CURVE) &&
384                                 (defElements[1].elementClass==JXG.OBJECT_CLASS_LINE || defElements[1].elementClass==JXG.OBJECT_CLASS_CURVE)) {
385                                 if(objName != '') {
386                                     attributes.name = objName;
387                                     if(mode == 'macro') {
388                                         if(macroName != '') {
389                                             attributes.id = macroName+"."+objName;
390                                         }
391                                         createdNames.push(objName);
392                                     }
393                                 }                          
394                                 obj = this.create('intersection',[defElements[0],defElements[1],0],attributes);
395                                 output.intersections.push(obj);
396                                 if(objName != '') {
397                                     output[attributes.name] = obj;
398                                 }
399                             }
400                             else {
401                                 if(objName != '') {
402                                     attributes.name = objName+"_1";
403                                     if(mode == 'macro') {
404                                         if(macroName != '') {
405                                             attributes.id = macroName+"."+objName+"_1";
406                                         }
407                                         createdNames.push(objName+"_1");
408                                     }
409                                 }
410                                 obj = this.create('intersection',[defElements[0],defElements[1],0],attributes);
411                                 output.intersections.push(obj);
412                                 if(objName != '') {
413                                     output[attributes.name] = obj;
414                                 }
415                                 if(objName != '') {
416                                     attributes.name = objName+"_2";
417                                     if(mode == 'macro') {
418                                         if(macroName != '') {
419                                             attributes.id = macroName+"."+objName+"_2";
420                                         }
421                                         createdNames.push(objName+"_2");
422                                     }
423                                 }
424                                 obj = this.create('intersection',[defElements[0],defElements[1],1],attributes);
425                                 output.intersections.push(obj);
426                                 if(objName != '') {
427                                     output[attributes.name] = obj;
428                                 }
429                             }
430                         }
431                         else if(splitted[i].search(/\|[\|_]\s*\(/) != -1) { // Parallele oder Senkrechte
432                             splitted[i].match(/\|([\|_])\s*\(\s*(\S*)\s*,\s*(\S*)\s*\)/);
433                             type = RegExp.$1;
434                             if(type == '|') {
435                                 type = 'parallel';
436                             }
437                             else { // type == '_'
438                                 type = 'normal';
439                             }
440                             defElements = [];
441                             defElements[0] = RegExp.$2;
442                             defElements[1] = RegExp.$3;
443                             if(mode == 'macro') {
444                                 for(j=0; j<=1; j++) {
445                                     if(macroName != '') {
446                                         for(k=0; k<createdNames.length; k++) { // vorher oder nachher?
447                                             if(defElements[j] == createdNames[k]) {
448                                                 defElements[j] = macroName+"."+createdNames[k];
449                                             }
450                                         }
451                                     }
452                                     for(k=0; k<params.length; k++) {
453                                         if(defElements[j] == params[k]) {
454                                             defElements[j] = paraIn[k];
455                                         }
456                                     }
457                                 }
458                             }
459                             if(objName != '') {
460                                 attributes.name = objName;
461                                 if(!JXG.exists(attributes.withLabel)) {
462                                     attributes.withLabel = true;
463                                 }
464                                 if(mode == 'macro') {
465                                     if(macroName != '') {
466                                         attributes.id = macroName+"."+objName;
467                                     }
468                                     createdNames.push(objName);
469                                 }
470                             }
471                             output.lines.push(this.create(type,
472                                                                  [JXG.getReference(this,defElements[0]),JXG.getReference(this,defElements[1])],
473                                                                  attributes));
474 
475                             if(objName != '') {
476                                 output[objName] = output.lines[output.lines.length-1];
477                             }
478                         }
479                         else if(splitted[i].search(/^</) != -1) { // Winkel
480                             splitted[i].match(/<\s*\(\s*(\S*)\s*,\s*(\S*)\s*,\s*(\S*)\s*\)/);
481                             defElements = [];
482                             defElements[0] = RegExp.$1;
483                             defElements[1] = RegExp.$2;
484                             defElements[2] = RegExp.$3;
485                             if(mode == 'macro') {
486                                 for(j=0; j<=2; j++) {
487                                     if(macroName != '') {
488                                         for(k=0; k<createdNames.length; k++) { // vorher oder nachher?
489                                             if(defElements[j] == createdNames[k]) {
490                                                 defElements[j] = macroName+"."+createdNames[k];
491                                             }
492                                         }
493                                     }
494                                     for(k=0; k<params.length; k++) {
495                                         if(defElements[j] == params[k]) {
496                                             defElements[j] = paraIn[k];
497                                         }
498                                     }
499                                 }
500                             }
501                             if(objName == '') {
502                                 output.lines.push(this.create('angle',
503                                                                     [JXG.getReference(this,defElements[0]),
504                                                                      JXG.getReference(this,defElements[1]),
505                                                                      JXG.getReference(this,defElements[2])],
506                                                                      attributes));
507                             }
508                             else {
509                                 possibleNames = ['alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta', 'eta', 'theta',
510                                             'iota', 'kappa', 'lambda', 'mu', 'nu', 'xi', 'omicron', 'pi', 'rho',
511                                             'sigmaf', 'sigma', 'tau', 'upsilon', 'phi', 'chi', 'psi', 'omega'];
512                                 type = '';
513                                 for(j=0; j<possibleNames.length;j++) {
514                                     if(objName == possibleNames[j]) {
515                                         attributes.text = '&'+objName+';';
516                                         attributes.name = '&'+objName+';';
517                                         type = 'greek';
518                                         break;
519                                     }
520                                     else {
521                                         if(j == possibleNames.length -1) {
522                                             attributes.text = objName;
523                                             attributes.name = objName;
524                                         }
525                                     }
526                                 }
527                                 if(!JXG.exists(attributes.withLabel)) {
528                                     attributes.withLabel = true;
529                                 }
530                                 if(mode == 'macro') {
531                                     if(macroName != '') {
532                                         attributes.id = macroName+"."+objName;
533                                     }
534                                     createdNames.push(objName);
535                                 }
536                                 output.angles.push(this.create('angle',
537                                                                      [JXG.getReference(this,defElements[0]),
538                                                                       JXG.getReference(this,defElements[1]),
539                                                                       JXG.getReference(this,defElements[2])],
540                                                                      attributes));
541                                 output[objName] = output.angles[output.angles.length-1];
542                             }
543                         }
544                         else if(splitted[i].search(/([0-9]+)\/([0-9]+)\(\s*(\S*)\s*,\s*(\S*)\s*\)/) != -1) { // Punkt mit Teilverhaeltnis, z.B. Mittelpunkt
545                             defElements = [];
546                             defElements[0] = 1.0*(RegExp.$1)/(1.0*(RegExp.$2));
547                             defElements[1] = RegExp.$3;
548                             defElements[2] = RegExp.$4;
549                             if(mode == 'macro') {
550                                 for(j=1; j<=2; j++) {
551                                     if(macroName != '') {
552                                         for(k=0; k<createdNames.length; k++) { // vorher oder nachher?
553                                             if(defElements[j] == createdNames[k]) {
554                                                 defElements[j] = macroName+"."+createdNames[k];
555                                             }
556                                         }
557                                     }
558                                     for(k=0; k<params.length; k++) {
559                                         if(defElements[j] == params[k]) {
560                                             defElements[j] = paraIn[k];
561                                         }
562                                     }
563                                 }
564                             }
565                             defElements[1] = JXG.getReference(this,RegExp.$3);
566                             defElements[2] = JXG.getReference(this,RegExp.$4);
567                             obj = [];
568                             obj[0] = (function(el, board) { return function() {
569                                                                           return (1-el[0])*el[1].coords.usrCoords[1]+el[0]*el[2].coords.usrCoords[1];
570                                                            }}
571                                               )(defElements, this);
572                             obj[1] = (function(el, board) { return function() {
573                                                                           return (1-el[0])*el[1].coords.usrCoords[2]+el[0]*el[2].coords.usrCoords[2];
574                                                            }}
575                                               )(defElements, this);
576                             if(objName != '') {
577                                 attributes.name = objName;
578                                 if(mode == 'macro') {
579                                     if(macroName != '') {
580                                         attributes.id = macroName+"."+objName;
581                                     }
582                                     createdNames.push(objName);
583                                 }
584                             }
585                             output.points.push(this.create('point',[obj[0],obj[1]],attributes));
586                             if(objName != '') {
587                                 output[objName] = output.points[output.points.length-1];
588                             }
589                         }
590                         else if(splitted[i].search(/(\S*)\s*:\s*(.*)/) != -1) { // Funktionsgraph
591                             objName = RegExp.$1;
592                             tmp = JXG.GeonextParser.geonext2JS(RegExp.$2, this);
593                             defElements = [new Function('x','var y = '+tmp+'; return y;')];
594                             attributes.name = objName;
595                             output.functions.push(this.create('functiongraph',defElements,attributes));
596                             output[objName] = output.functions[output.functions.length-1];
597                         }
598                         else if(splitted[i].search(/#(.*)\(\s*([0-9])\s*[,|]\s*([0-9])\s*\)/) != -1) { // Text element
599                             defElements = []; // [0-9\.\-]+
600                             defElements[0] = RegExp.$1;
601                             defElements[1] = 1.0*RegExp.$2;
602                             defElements[2] = 1.0*RegExp.$3;
603                             defElements[0] = defElements[0].replace (/^\s+/, '').replace (/\s+$/, ''); // trim
604                             output.texts.push(this.create('text',[defElements[1],defElements[2],defElements[0]], attributes));
605                         }
606                         else if(splitted[i].search(/(\S*)\s*\[(.*)\]/) != -1) { // Polygon
607                             attributes.name = RegExp.$1;
608                             if(!JXG.exists(attributes.withLabel)) {
609                                 attributes.withLabel = true;
610                             }
611                             defElements = RegExp.$2;
612                             defElements = defElements.split(',');
613                             for(j=0; j<defElements.length; j++) {
614                                 defElements[j] = defElements[j].replace (/^\s+/, '').replace (/\s+$/, ''); // trim
615                                 if(mode == 'macro') {
616                                     if(macroName != '') {
617                                         for(k=0; k<createdNames.length; k++) { // vorher oder nachher?
618                                             if(defElements[j] == createdNames[k]) {
619                                                 defElements[j] = macroName+"."+createdNames[k];
620                                             }
621                                         }
622                                     }
623                                     for(k=0; k<params.length; k++) {
624                                         if(defElements[j] == params[k]) {
625                                             defElements[j] = paraIn[k];
626                                         }
627                                     }
628                                 }
629                                 defElements[j] = JXG.getReference(this,defElements[j]);
630                             }
631                             output.polygons.push(this.create('polygon',defElements,attributes));
632                             output[attributes.name] = output.polygons[output.polygons.length-1];
633                         }
634                     }
635                 }
636             }
637         }
638     }
639     this.update();
640     return output;
641 };
642 
643 /**
644  * Parses a string like<br />
645  * <tt><macro-name> = Macro(A, B, C) { <Command in JSXGraph Construction syntax>; ...<Command in JXG-Construct syntax>; }</tt><br />
646  * and adds it as a macro so it can be used in the JSXGraph Construction Syntax.
647  * @param {String} string A string like the one in the methods description.
648  * @see #construct
649  */
650 JXG.Board.prototype.addMacro = function(string) {
651     var defHead, defBody, defName = '', i;
652     string.match(/(.*)\{(.*)\}/);
653     defHead = RegExp.$1;
654     defBody = RegExp.$2;
655     if(defHead.search(/=/) != -1) {
656         defHead.match(/\s*(\S*)\s*=.*/);
657         defName = RegExp.$1;
658         defHead = (defHead.split('='))[1];
659     }
660     defHead.match(/Macro\((.*)\)/);
661     defHead = RegExp.$1;
662     defHead = defHead.split(',');
663     for(i=0; i < defHead.length; i++) {
664         defHead[i].match(/\s*(\S*)\s*/);
665         defHead[i] = RegExp.$1;
666     }
667 
668     if(this.definedMacros == null) {
669         this.definedMacros = {};
670         this.definedMacros.macros = [];
671     }
672 
673     this.definedMacros.macros.push([defName, defHead, defBody]);
674     if(defName != '') {
675         this.definedMacros.defName = this.definedMacros.macros[this.definedMacros.macros.length-1];
676     }
677 };
678