/*
    Copyright 2008-2013
        Matthias Ehmann,
        Michael Gerhaeuser,
        Carsten Miller,
        Bianca Valentin,
        Alfred Wassermann,
        Peter Wilfahrt

    This file is part of JSXGraph.

    JSXGraph is free software dual licensed under the GNU LGPL or MIT License.
    
    You can redistribute it and/or modify it under the terms of the
    
      * GNU Lesser General Public License as published by
        the Free Software Foundation, either version 3 of the License, or
        (at your option) any later version
      OR
      * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT
    
    JSXGraph is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.
    
    You should have received a copy of the GNU Lesser General Public License and
    the MIT License along with JSXGraph. If not, see <http://www.gnu.org/licenses/>
    and <http://opensource.org/licenses/MIT/>.
 */


/**
 * @param {String} string A string containing construction(s) in JSXGraph Construction Syntax.
 * @param {String} mode Possible values seem are "normal" or "macro"
 * @param {Array} params Parameters, only used in macro mode
 * @param {Array} paraIn Parameters, only used in macro mode
 * @param {String} macroName Name of the macro, only used in macro mode
 * @type object
 * @return An object consisting of several arrays (lines, circles, points, angles, ...) where the created elements are stored.
 */
JXG.Board.prototype.construct = function(string, mode, params, paraIn, macroName) {
    var splitted, i, j, output = {}, objName, defElements, obj, type, possibleNames, tmp, noMacro, k,l, pattern, createdNames, found,
        mac, prop, propName, propValue, attributes;
    if(!JXG.exists(mode)) {
        mode = "normal";
    }
    else { // mode = 'macro'
        createdNames = [];
    }
    output.lines = [];
    output.circles = [];
    output.points = [];
    output.intersections = [];
    output.angles = [];
    output.macros = [];
    output.functions = [];
    output.texts = [];
    output.polygons = [];
    if(string.search(/\{/) != -1) { // Macros finden! Innerhalb der {} darf nicht am ; getrennt werden. Noch nicht getestet: mehrere Makros hintereinander in einem construct.
        tmp = string.match(/\{/);
        tmp = tmp.length;
        l=0;
        for(j=0; j<tmp; j++) {
            k = string.slice(l).search(/\{/);
            mac = string.slice(k);
            mac = mac.slice(0,mac.search(/\}/)+1);
            mac = mac.replace(/;/g,'?');   // Achtung! Fragezeichen duerfen daher nicht im Code eines Macros vorkommen!
            string = string.slice(0,k) + mac + string.slice(k+mac.length);
            l=k+1;
        }
    }
    splitted = string.split(';');
    for(i=0; i< splitted.length; i++) {
        // Leerzeichen am Anfang und am Ende entfernen
        splitted[i] = splitted[i].replace (/^\s+/, '').replace (/\s+$/, '');
        if(splitted[i].search(/\{/) != -1) {
            splitted[i] = splitted[i].replace(/\?/g,';');
        }
        if(splitted[i].search(/Macro/) != -1) {
            this.addMacro(splitted[i]);
        }
        else {
            if(splitted[i].length > 0) {
                prop = false;
                if(splitted[i].search(/=/) != -1) {
                    objName = splitted[i].split('=');
                    propValue = objName[1];
                    propValue = propValue.replace (/^\s+/, '').replace (/\s+$/, '');
                    if(objName[0].search(/\./) != -1) {
                        prop = true;
                    
                        objName = objName[0].split('.');
                        propName = objName[objName.length-1];
                        propName = propName.replace (/^\s+/, '').replace (/\s+$/, '');
                        objName.pop();
                        objName = objName.join(".");
                        if(mode == 'macro') {
                            for(j=0; j<params.length; j++) {
                                if(objName == params[j]) {
                                    objName = paraIn[j];
                                }
                            }
                        }                    
                        //alert("_"+objName+"_"+propName+"_"+propValue+"_");
                        //alert(JXG.getReference(this,objName).name);
                        JXG.getReference(this,objName).setProperty(propName+":"+propValue);
                        
                    }
                }
                if(!prop) { // nicht nur eine Eigenschaft setzen, sondern neues Element konstruieren
                    if(splitted[i].search(/=/) != -1) {
                        objName = splitted[i].split('=');
                        splitted[i] = objName[1].replace (/^\s+/, ''); // Leerzeichen am Anfang entfernen
                        objName = objName[0].replace (/\s+$/, ''); // Leerzeichen am Ende entfernen
                    }
                    else {
                        objName = '';
                    }
                    attributes = {};
                    found = true;
                    while(found) {
                        if(splitted[i].search(/(.*)draft$/) != -1) {
                            attributes.draft = true;
                            splitted[i] = RegExp.$1;
                            splitted[i] = splitted[i].replace (/\s+$/, ''); // Leerzeichen am Ende entfernen
                        }
                        if(splitted[i].search(/(.*)invisible$/) != -1) {
                            attributes.visible = false;
                            splitted[i] = RegExp.$1;
                            splitted[i] = splitted[i].replace (/\s+$/, ''); // Leerzeichen am Ende entfernen
                        }
                        if(splitted[i].search(/(.*)nolabel$/) != -1) {
                            attributes.withLabel = false;
                            splitted[i] = RegExp.$1;
                            splitted[i] = splitted[i].replace (/\s+$/, ''); // Leerzeichen am Ende entfernen
                        }
                        if(splitted[i].search(/nolabel|invisible|draft/) == -1) {
                            found = false;
                        }
                    }
                    noMacro = true;
                    if(this.definedMacros) {
                        for(j=0; j<this.definedMacros.macros.length; j++) {
                            pattern = new RegExp("^"+this.definedMacros.macros[j][0]+"\\s*\\(");
                            if(splitted[i].search(pattern) != -1) { // TODO: testen, was mit den Macros xxx und yxxx passiert
                                //alert("MACRO!"+splitted[i]+"_"+this.definedMacros.macros[j][2]);
                                noMacro = false;
                                // Parameter aufdroeseln
                                splitted[i].match(/\((.*)\)/);
                                tmp = RegExp.$1;
                                tmp = tmp.split(',');
                                for(k=0; k < tmp.length; k++) {
                                    tmp[k].match(/\s*(\S*)\s*/);
                                    tmp[k] = RegExp.$1;
                                }
                                output[objName] = this.construct(this.definedMacros.macros[j][2],'macro',this.definedMacros.macros[j][1], tmp, objName);
                                output.macros.push(output[objName]);
                                break;
                            }
                        }
                    }
                    if(noMacro) { // splitted[i] war kein Macro-Aufruf
                        if(splitted[i].search(/^[\[\]].*[\[\]]$/) != -1) { // Gerade, Halbgerade oder Segment
                            splitted[i].match(/([\[\]])(.*)([\[\]])/);
                            attributes.straightFirst = (RegExp.$1 != '[');
                            attributes.straightLast = (RegExp.$3 == '[');
                            defElements = (RegExp.$2).replace (/^\s+/, '').replace (/\s+$/, '');
                            if(defElements.search(/ /) != -1) {
                                defElements.match(/(\S*) +(\S*)/);
                                defElements = [];
                                defElements[0] = RegExp.$1;
                                defElements[1] = RegExp.$2;
                            } // sonst wird die Gerade durch zwei Punkte definiert, die einen Namen haben, der aus nur jeweils einem Buchstaben besteht
                            if(objName != '') {
                                if(!JXG.exists(attributes.withLabel)) {
                                    attributes.withLabel = true;
                                }
                                attributes.name = objName;
                                if(mode == 'macro') {
                                    createdNames.push(objName);
                                }
                            }
                            if(mode == 'macro') {
                                if(macroName != '') {
                                    for(j=0; j<createdNames.length; j++) { // vorher oder nachher?
                                        if(defElements[0] == createdNames[j]) {
                                            defElements[0] = macroName+"."+defElements[0];
                                        }
                                        if(defElements[1] == createdNames[j]) {
                                            defElements[1] = macroName+"."+defElements[1];
                                        }
                                    }
                                }
                                for(j=0; j<params.length; j++) {
                                    if(defElements[0] == params[j]) {
                                        defElements = [paraIn[j], defElements[1]];
                                    }
                                    if(defElements[1] == params[j]) {
                                        defElements = [defElements[0], paraIn[j]];
                                    }
                                }
                                if(macroName != '') {
                                    attributes.id = macroName+"."+objName;
                                }
                            }
                            if(typeof defElements == 'string') {
                                defElements = [JXG.getReference(this,defElements.charAt(0)), JXG.getReference(this,defElements.charAt(1))];
                            }
                            else {
                                defElements = [JXG.getReference(this,defElements[0]), JXG.getReference(this,defElements[1])];
                            }
                            output.lines.push(this.create('line',
                                                    defElements,
                                                    attributes));
                            if(objName != '') {
                                output[objName] = output.lines[output.lines.length-1];
                            }
                        }
                        else if(splitted[i].search(/k\s*\(.*/) != -1) { // Kreis
                            splitted[i].match(/k\s*\(\s*(\S.*\S|\S)\s*,\s*(\S.*\S|\S)\s*\)/);
                            defElements = [];
                            defElements[0] = RegExp.$1;
                            defElements[1] = RegExp.$2;
                            for(j=0; j<=1; j++) {
                                if(defElements[j].search(/[\[\]]/) != -1) { // Linie, definiert durch [P_1 P_2] , ist bei den Parametern dabei
                                    defElements[j].match(/^[\[\]]\s*(\S.*\S)\s*[\[\]]$/);
                                    defElements[j] = RegExp.$1;
                                    if(defElements[j].search(/ /) != -1) {
                                        defElements[j].match(/(\S*) +(\S*)/);
                                        defElements[j] = [];
                                        defElements[j][0] = RegExp.$1;
                                        defElements[j][1] = RegExp.$2;
                                    } // sonst wird die Gerade durch zwei Punkte definiert, die einen Namen haben, der aus nur jeweils einem Buchstaben besteht
                                    if(mode == 'macro') {
                                        if(macroName != '') {
                                            for(k=0; k<createdNames.length; k++) { // vorher oder nachher?
                                                if(defElements[j][0] == createdNames[k]) {
                                                    defElements[j][0] = macroName+"."+defElements[j][0];
                                                }
                                                if(defElements[j][1] == createdNames[k]) {
                                                    defElements[j][1] = macroName+"."+defElements[j][1];
                                                }
                                            }
                                        }
                                        for(k=0; k<params.length; k++) {
                                            if(defElements[j][0] == params[k]) {
                                                defElements[j] = [paraIn[k], defElements[j][1]];
                                            }
                                            if(defElements[j][1] == params[k]) {
                                                defElements[j] = [defElements[j][0], paraIn[k]];
                                            }
                                        }
                                    }
                                    if(typeof defElements[j] == 'string') {
                                        defElements[j] = (function(el, board) { return function() {
                                                                    return JXG.getReference(board,el.charAt(0)).Dist(JXG.getReference(board,el.charAt(1))); // TODO
                                                               }}
                                                  )(defElements[j], this);
                                    }
                                    else {
                                        defElements[j] = (function(el, board) { return function() {
                                                                    return JXG.getReference(board,el[0]).Dist(JXG.getReference(board,el[1])); // TODO
                                                               }}
                                                  )(defElements[j], this);
                                    }
                                    
                                }
                                else if(defElements[j].search(/[0-9\.\s]+/) != -1){ // Radius als Zahl
                                    defElements[j] = 1.0*defElements[j];
                                }
                                else { // Element mit Name
                                    if(mode == 'macro') {
                                        if(macroName != '') {
                                            for(k=0; k<createdNames.length; k++) { // vorher oder nachher?
                                                if(defElements[j] == createdNames[k]) {
                                                    defElements[j] = macroName+"."+createdNames[k];
                                                }
                                            }
                                        }
                                        for(k=0; k<params.length; k++) {
                                            if(defElements[j] == params[k]) {
                                                defElements[j] = paraIn[k];
                                            }
                                        }
                                    }
                                    defElements[j] = JXG.getReference(this,defElements[j]);
                                }
                            }
                            if(objName != '') {
                                if(!JXG.exists(attributes.withLabel)) {
                                    attributes.withLabel = true;
                                }
                                attributes.name = objName;
                                if(mode == 'macro') {
                                    if(macroName != '') {
                                        attributes.id = macroName+"."+objName;
                                    }
                                    createdNames.push(objName);
                                }
                            }
                            output.circles.push(this.create('circle',defElements,attributes));
                            if(objName != '') {
                                output[objName] = output.circles[output.circles.length-1];
                            }
                        }
                        else if(splitted[i].search(/^[A-Z]+.*\(\s*[0-9\.\-]+\s*[,\|]\s*[0-9\.\-]+\s*\)/) != -1
                                && splitted[i].search(/Macro\((.*)\)/) == -1) { // Punkt, startet mit einem Grossbuchstaben! (definiert durch Koordinaten)
                            splitted[i].match(/^([A-Z]+\S*)\s*\(\s*(.*)\s*[,\|]\s*(.*)\s*\)$/);
                            objName = RegExp.$1; // Name
                            attributes.name = objName;
                            if(mode == 'macro') {
                                if(macroName != '') {
                                    attributes.id = macroName+"."+objName;
                                }
                                createdNames.push(objName);
                            }
                            output.points.push(this.create('point',[1.0*RegExp.$2,1.0*RegExp.$3],attributes));
                            output[objName] = output.points[output.points.length-1];
                        }
                        else if(splitted[i].search(/^[A-Z]+.*\(.+(([,\|]\s*[0-9\.\-]+\s*){2})?/) != -1
                                && splitted[i].search(/Macro\((.*)\)/) == -1) { // Gleiter, mit oder ohne Koordinaten
                            splitted[i].match(/([A-Z]+.*)\((.*)\)/);
                            objName = RegExp.$1;
                            defElements = RegExp.$2;
                            objName = objName.replace (/^\s+/, '').replace (/\s+$/, '');
                            defElements = defElements.replace (/^\s+/, '').replace (/\s+$/, '');
                            if(defElements.search(/[,\|]/) != -1) { // Koordinaten angegeben
                                defElements.match(/(\S*)\s*[,\|]\s*([0-9\.]+)\s*[,\|]\s*([0-9\.]+)\s*/);
                                defElements = [];
                                defElements[0] = RegExp.$1;
                                defElements[1] = 1.0*RegExp.$2;
                                defElements[2] = 1.0*RegExp.$3;
                            }
                            else { // keine Koordinaten
                                obj = defElements;
                                defElements = [];
                                defElements[0] = obj; // Name des definierenden Elements
                                defElements[1] = 0; // (0,0) als Gleiterkoordinaten vorgeben...
                                defElements[2] = 0;
                            }
                            attributes.name = objName;
                            if(mode == 'macro') {
                                if(macroName != '') {
                                    for(k=0; k<createdNames.length; k++) { // vorher oder nachher?
                                        if(defElements[0] == createdNames[k]) {
                                            defElements[0] = macroName+"."+createdNames[k];
                                        }
                                    }
                                }
                                for(k=0; k<params.length; k++) {
                                    if(defElements[0] == params[k]) {
                                        defElements[0] = paraIn[k];
                                    }
                                }
                                if(macroName != '') {
                                    attributes.id = macroName+"."+objName;
                                }
                                createdNames.push(objName);
                            }
                            output.points.push(this.create('glider',
                                                                  [defElements[1],defElements[2],JXG.getReference(this,defElements[0])],
                                                                  attributes));
                            output[objName] = output.points[output.points.length-1];
                        }
                        else if(splitted[i].search(/&/) != -1) { // Schnittpunkt
                            splitted[i].match(/(.*)&(.*)/);
                            defElements = [];
                            defElements[0] = RegExp.$1;
                            defElements[1] = RegExp.$2;
                            defElements[0] = defElements[0].replace(/\s+$/, ''); // Leerzeichen am Ende entfernen
                            defElements[1] = defElements[1].replace (/^\s+/, ''); // Leerzeichen am Anfang entfernen
                            if(mode == 'macro') {
                                for(j=0; j<=1; j++) {
                                    if(macroName != '') {
                                        for(k=0; k<createdNames.length; k++) { // vorher oder nachher?
                                            if(defElements[j] == createdNames[k]) {
                                                defElements[j] = macroName+"."+createdNames[k];
                                            }
                                        }
                                    }
                                    for(k=0; k<params.length; k++) {
                                        if(defElements[j] == params[k]) {
                                            defElements[j] = paraIn[k];
                                        }
                                    }
                                }
                            }
                            defElements[0] = JXG.getReference(this,defElements[0]);
                            defElements[1] = JXG.getReference(this,defElements[1]);
                            if ((defElements[0].elementClass==JXG.OBJECT_CLASS_LINE || defElements[0].elementClass==JXG.OBJECT_CLASS_CURVE) &&
                                (defElements[1].elementClass==JXG.OBJECT_CLASS_LINE || defElements[1].elementClass==JXG.OBJECT_CLASS_CURVE)) {
                                if(objName != '') {
                                    attributes.name = objName;
                                    if(mode == 'macro') {
                                        if(macroName != '') {
                                            attributes.id = macroName+"."+objName;
                                        }
                                        createdNames.push(objName);
                                    }
                                }                          
                                obj = this.create('intersection',[defElements[0],defElements[1],0],attributes);
                                output.intersections.push(obj);
                                if(objName != '') {
                                    output[attributes.name] = obj;
                                }
                            }
                            else {
                                if(objName != '') {
                                    attributes.name = objName+"_1";
                                    if(mode == 'macro') {
                                        if(macroName != '') {
                                            attributes.id = macroName+"."+objName+"_1";
                                        }
                                        createdNames.push(objName+"_1");
                                    }
                                }
                                obj = this.create('intersection',[defElements[0],defElements[1],0],attributes);
                                output.intersections.push(obj);
                                if(objName != '') {
                                    output[attributes.name] = obj;
                                }
                                if(objName != '') {
                                    attributes.name = objName+"_2";
                                    if(mode == 'macro') {
                                        if(macroName != '') {
                                            attributes.id = macroName+"."+objName+"_2";
                                        }
                                        createdNames.push(objName+"_2");
                                    }
                                }
                                obj = this.create('intersection',[defElements[0],defElements[1],1],attributes);
                                output.intersections.push(obj);
                                if(objName != '') {
                                    output[attributes.name] = obj;
                                }
                            }
                        }
                        else if(splitted[i].search(/\|[\|_]\s*\(/) != -1) { // Parallele oder Senkrechte
                            splitted[i].match(/\|([\|_])\s*\(\s*(\S*)\s*,\s*(\S*)\s*\)/);
                            type = RegExp.$1;
                            if(type == '|') {
                                type = 'parallel';
                            }
                            else { // type == '_'
                                type = 'normal';
                            }
                            defElements = [];
                            defElements[0] = RegExp.$2;
                            defElements[1] = RegExp.$3;
                            if(mode == 'macro') {
                                for(j=0; j<=1; j++) {
                                    if(macroName != '') {
                                        for(k=0; k<createdNames.length; k++) { // vorher oder nachher?
                                            if(defElements[j] == createdNames[k]) {
                                                defElements[j] = macroName+"."+createdNames[k];
                                            }
                                        }
                                    }
                                    for(k=0; k<params.length; k++) {
                                        if(defElements[j] == params[k]) {
                                            defElements[j] = paraIn[k];
                                        }
                                    }
                                }
                            }
                            if(objName != '') {
                                attributes.name = objName;
                                if(!JXG.exists(attributes.withLabel)) {
                                    attributes.withLabel = true;
                                }
                                if(mode == 'macro') {
                                    if(macroName != '') {
                                        attributes.id = macroName+"."+objName;
                                    }
                                    createdNames.push(objName);
                                }
                            }
                            output.lines.push(this.create(type,
                                                                 [JXG.getReference(this,defElements[0]),JXG.getReference(this,defElements[1])],
                                                                 attributes));

                            if(objName != '') {
                                output[objName] = output.lines[output.lines.length-1];
                            }
                        }
                        else if(splitted[i].search(/^</) != -1) { // Winkel
                            splitted[i].match(/<\s*\(\s*(\S*)\s*,\s*(\S*)\s*,\s*(\S*)\s*\)/);
                            defElements = [];
                            defElements[0] = RegExp.$1;
                            defElements[1] = RegExp.$2;
                            defElements[2] = RegExp.$3;
                            if(mode == 'macro') {
                                for(j=0; j<=2; j++) {
                                    if(macroName != '') {
                                        for(k=0; k<createdNames.length; k++) { // vorher oder nachher?
                                            if(defElements[j] == createdNames[k]) {
                                                defElements[j] = macroName+"."+createdNames[k];
                                            }
                                        }
                                    }
                                    for(k=0; k<params.length; k++) {
                                        if(defElements[j] == params[k]) {
                                            defElements[j] = paraIn[k];
                                        }
                                    }
                                }
                            }
                            if(objName == '') {
                                output.lines.push(this.create('angle',
                                                                    [JXG.getReference(this,defElements[0]),
                                                                     JXG.getReference(this,defElements[1]),
                                                                     JXG.getReference(this,defElements[2])],
                                                                     attributes));
                            }
                            else {
                                possibleNames = ['alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta', 'eta', 'theta',
                                            'iota', 'kappa', 'lambda', 'mu', 'nu', 'xi', 'omicron', 'pi', 'rho',
                                            'sigmaf', 'sigma', 'tau', 'upsilon', 'phi', 'chi', 'psi', 'omega'];
                                type = '';
                                for(j=0; j<possibleNames.length;j++) {
                                    if(objName == possibleNames[j]) {
                                        attributes.text = '&'+objName+';';
                                        attributes.name = '&'+objName+';';
                                        type = 'greek';
                                        break;
                                    }
                                    else {
                                        if(j == possibleNames.length -1) {
                                            attributes.text = objName;
                                            attributes.name = objName;
                                        }
                                    }
                                }
                                if(!JXG.exists(attributes.withLabel)) {
                                    attributes.withLabel = true;
                                }
                                if(mode == 'macro') {
                                    if(macroName != '') {
                                        attributes.id = macroName+"."+objName;
                                    }
                                    createdNames.push(objName);
                                }
                                output.angles.push(this.create('angle',
                                                                     [JXG.getReference(this,defElements[0]),
                                                                      JXG.getReference(this,defElements[1]),
                                                                      JXG.getReference(this,defElements[2])],
                                                                     attributes));
                                output[objName] = output.angles[output.angles.length-1];
                            }
                        }
                        else if(splitted[i].search(/([0-9]+)\/([0-9]+)\(\s*(\S*)\s*,\s*(\S*)\s*\)/) != -1) { // Punkt mit Teilverhaeltnis, z.B. Mittelpunkt
                            defElements = [];
                            defElements[0] = 1.0*(RegExp.$1)/(1.0*(RegExp.$2));
                            defElements[1] = RegExp.$3;
                            defElements[2] = RegExp.$4;
                            if(mode == 'macro') {
                                for(j=1; j<=2; j++) {
                                    if(macroName != '') {
                                        for(k=0; k<createdNames.length; k++) { // vorher oder nachher?
                                            if(defElements[j] == createdNames[k]) {
                                                defElements[j] = macroName+"."+createdNames[k];
                                            }
                                        }
                                    }
                                    for(k=0; k<params.length; k++) {
                                        if(defElements[j] == params[k]) {
                                            defElements[j] = paraIn[k];
                                        }
                                    }
                                }
                            }
                            defElements[1] = JXG.getReference(this,RegExp.$3);
                            defElements[2] = JXG.getReference(this,RegExp.$4);
                            obj = [];
                            obj[0] = (function(el, board) { return function() {
                                                                          return (1-el[0])*el[1].coords.usrCoords[1]+el[0]*el[2].coords.usrCoords[1];
                                                           }}
                                              )(defElements, this);
                            obj[1] = (function(el, board) { return function() {
                                                                          return (1-el[0])*el[1].coords.usrCoords[2]+el[0]*el[2].coords.usrCoords[2];
                                                           }}
                                              )(defElements, this);
                            if(objName != '') {
                                attributes.name = objName;
                                if(mode == 'macro') {
                                    if(macroName != '') {
                                        attributes.id = macroName+"."+objName;
                                    }
                                    createdNames.push(objName);
                                }
                            }
                            output.points.push(this.create('point',[obj[0],obj[1]],attributes));
                            if(objName != '') {
                                output[objName] = output.points[output.points.length-1];
                            }
                        }
                        else if(splitted[i].search(/(\S*)\s*:\s*(.*)/) != -1) { // Funktionsgraph
                            objName = RegExp.$1;
                            tmp = JXG.GeonextParser.geonext2JS(RegExp.$2, this);
                            defElements = [new Function('x','var y = '+tmp+'; return y;')];
                            attributes.name = objName;
                            output.functions.push(this.create('functiongraph',defElements,attributes));
                            output[objName] = output.functions[output.functions.length-1];
                        }
                        else if(splitted[i].search(/#(.*)\(\s*([0-9])\s*[,|]\s*([0-9])\s*\)/) != -1) { // Text element
                            defElements = []; // [0-9\.\-]+
                            defElements[0] = RegExp.$1;
                            defElements[1] = 1.0*RegExp.$2;
                            defElements[2] = 1.0*RegExp.$3;
                            defElements[0] = defElements[0].replace (/^\s+/, '').replace (/\s+$/, ''); // trim
                            output.texts.push(this.create('text',[defElements[1],defElements[2],defElements[0]], attributes));
                        }
                        else if(splitted[i].search(/(\S*)\s*\[(.*)\]/) != -1) { // Polygon
                            attributes.name = RegExp.$1;
                            if(!JXG.exists(attributes.withLabel)) {
                                attributes.withLabel = true;
                            }
                            defElements = RegExp.$2;
                            defElements = defElements.split(',');
                            for(j=0; j<defElements.length; j++) {
                                defElements[j] = defElements[j].replace (/^\s+/, '').replace (/\s+$/, ''); // trim
                                if(mode == 'macro') {
                                    if(macroName != '') {
                                        for(k=0; k<createdNames.length; k++) { // vorher oder nachher?
                                            if(defElements[j] == createdNames[k]) {
                                                defElements[j] = macroName+"."+createdNames[k];
                                            }
                                        }
                                    }
                                    for(k=0; k<params.length; k++) {
                                        if(defElements[j] == params[k]) {
                                            defElements[j] = paraIn[k];
                                        }
                                    }
                                }
                                defElements[j] = JXG.getReference(this,defElements[j]);
                            }
                            output.polygons.push(this.create('polygon',defElements,attributes));
                            output[attributes.name] = output.polygons[output.polygons.length-1];
                        }
                    }
                }
            }
        }
    }
    this.update();
    return output;
};

/**
 * Parses a string like<br />
 * <tt>&lt;macro-name&gt; = Macro(A, B, C) { <Command in JSXGraph Construction syntax>; ...<Command in JXG-Construct syntax>; }</tt><br />
 * and adds it as a macro so it can be used in the JSXGraph Construction Syntax.
 * @param {String} string A string like the one in the methods description.
 * @see #construct
 */
JXG.Board.prototype.addMacro = function(string) {
    var defHead, defBody, defName = '', i;
    string.match(/(.*)\{(.*)\}/);
    defHead = RegExp.$1;
    defBody = RegExp.$2;
    if(defHead.search(/=/) != -1) {
        defHead.match(/\s*(\S*)\s*=.*/);
        defName = RegExp.$1;
        defHead = (defHead.split('='))[1];
    }
    defHead.match(/Macro\((.*)\)/);
    defHead = RegExp.$1;
    defHead = defHead.split(',');
    for(i=0; i < defHead.length; i++) {
        defHead[i].match(/\s*(\S*)\s*/);
        defHead[i] = RegExp.$1;
    }

    if(this.definedMacros == null) {
        this.definedMacros = {};
        this.definedMacros.macros = [];
    }

    this.definedMacros.macros.push([defName, defHead, defBody]);
    if(defName != '') {
        this.definedMacros.defName = this.definedMacros.macros[this.definedMacros.macros.length-1];
    }
};
