1 /*
  2     Copyright 2008 - 2011
  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 JXG.debug = function() {};
 26 JXG.GeonextReader = {
 27 
 28     changeOriginIds: function (board, id) {
 29         if ((id == 'gOOe0') || (id == 'gXOe0') || (id == 'gYOe0') || (id == 'gXLe0') || (id == 'gYLe0')) {
 30             return board.id + id;
 31         } else {
 32             return id;
 33         }
 34     },
 35 
 36     /**
 37      * Retrieves data by TagName from an XML node.
 38      * @param {Node} node The Node that contains the data we want to get.
 39      * @param {String} tag The Name of the tag we are looking for.
 40      * @param {Number} [idx=0] getElementsByTagName returns an array; This parameter decides which element to use.
 41      * @param {Boolean} [fc=true] If True, the result will be the <tt>data</tt> of <tt>firstChild</tt> instead of the result node.
 42      * @returns {String} The gathered data
 43      */
 44     gEBTN: function (node, tag, idx, fc) {
 45         var tmp;
 46 
 47         if (!JXG.exists(node || !node.getElementsByTagName )/* || !JXG.exists(node.getElementsByTagName)*/) {
 48             return '';
 49         }
 50         // Default values for optional parameters idx and fc
 51         if (!JXG.exists(fc)) {
 52             fc = true;
 53         }
 54         idx = idx || 0;
 55         tmp = node.getElementsByTagName(tag);
 56         if (tmp.length > 0) {
 57             tmp = tmp[idx];
 58             if (fc && tmp.firstChild) {
 59                 tmp = tmp.firstChild.data;
 60             }
 61         }
 62         return tmp;
 63     },
 64 
 65     /**
 66      * Set color properties of a geonext element.
 67      * Set stroke, fill, lighting, label and draft color attributes.
 68      * @param {Object} gxtEl element of which attributes are to set
 69      */
 70     colorProperties: function (gxtEl, Data) {
 71         var color = this.gEBTN(Data, 'color', 0, false),
 72             rgbo;
 73 
 74         //gxtEl.strokewidth = Data.getElementsByTagName('strokewidth')[0].firstChild.data;
 75         // colorStroke = strokeColor etc. is here for downwards compatibility:
 76         // once upon a time we used to create elements in here using the "new JXG.Element" constructor mechanism
 77         // then we changed to board.create + setProperty afterwords
 78         // now i want to use the board.create method with an appropriate attributes object to avoid setProperty calls
 79         // and as gxtEl happens to be somewhat like an attributes object it's  just slightly different so we adjust it
 80         // for downwards compatibility during the transformation of this reader we use both properties
 81 
 82         rgbo = JXG.rgba2rgbo(this.gEBTN(color, 'stroke'));
 83         gxtEl.strokeColor = rgbo[0];
 84         gxtEl.strokeOpacity = rgbo[1];
 85 
 86         rgbo = JXG.rgba2rgbo(this.gEBTN(color, 'lighting'));
 87         gxtEl.highlightStrokeColor = rgbo[0];
 88         gxtEl.highlightStrokeOpacity = rgbo[1];
 89 
 90         rgbo = JXG.rgba2rgbo(this.gEBTN(color, 'fill'));
 91         gxtEl.fillColor = rgbo[0];
 92         gxtEl.fillOpacity = rgbo[1];
 93 
 94         gxtEl.highlightFillColor = gxtEl.fillColor;
 95         gxtEl.highlightFillOpacity = gxtEl.fillOpacity;
 96 
 97         gxtEl.labelColor = JXG.rgba2rgbo(this.gEBTN(color, 'label'))[0];
 98         gxtEl.colorDraft = JXG.rgba2rgbo(this.gEBTN(color, 'draft'))[0];
 99 
100         // GEONExT hides labels by setting opacity to 0.
101         if (JXG.rgba2rgbo(this.gEBTN(color, 'label'))[1]==0) {
102             gxtEl.withLabel = false;
103         } else {
104             gxtEl.withLabel = true;
105         }
106         
107         // backwards compatibility
108         gxtEl.colorStroke = gxtEl.strokeColor;
109         gxtEl.colorFill = gxtEl.fillColor;
110         gxtEl.colorLabel = gxtEl.labelColor;
111 
112         return gxtEl;
113     },
114 
115     firstLevelProperties: function (gxtEl, Data) {
116         if (!JXG.exists(Data) || !JXG.exists(Data.childNodes))
117             return gxtEl;
118         
119         var arr = Data.childNodes, n, key;
120 
121         for (n = 0; n < arr.length; n++) {
122             if (JXG.exists(arr[n].firstChild) && arr[n].nodeName !== 'data' && arr[n].nodeName !== 'straight') {
123                 key = arr[n].nodeName;
124                 if (key=='width') {
125                     key = 'strokewidth';
126                 }
127                 gxtEl[key] = arr[n].firstChild.data;
128             }
129         }
130         
131         return gxtEl;
132     },
133 
134     /**
135      * Set the defining properties of a geonext element.
136      * Writing the nodeName to ident; setting the name attribute and defining the element id.
137      * @param {Object} gxtEl element of which attributes are to set
138      */
139     defProperties: function (gxtEl, Data) {
140         // 3==TEXT_NODE, 8==COMMENT_NODE
141         if (Data.nodeType==3 || Data.nodeType==8 ) {
142             return null;
143         }
144 
145         gxtEl.ident = Data.nodeName;
146         
147         if(gxtEl.ident == "text" || gxtEl.ident == "intersection" || gxtEl.ident == "composition") {
148             gxtEl.name = '';
149         } else {
150             gxtEl.name = this.gEBTN(Data, 'name');
151         }
152         
153         gxtEl.id = this.gEBTN(Data, 'id');
154 
155         return gxtEl;
156     },
157 
158     visualProperties: function (gxtEl, Data) {
159         gxtEl.visible = JXG.str2Bool(this.gEBTN(Data, 'visible'));
160         gxtEl.trace = JXG.str2Bool(this.gEBTN(Data, 'trace'));
161         
162         return gxtEl;
163     },
164 
165     transformProperties: function (gxtEl, type) {
166         var facemap = [
167                 // 0-2
168                 'cross', 'cross', 'cross',
169                 // 3-6
170                 'circle', 'circle', 'circle', 'circle',
171                 // 7-9
172                 'square', 'square', 'square',
173                 // 10-12
174                 'plus', 'plus', 'plus'
175             ], sizemap = [
176                 // 0-2
177                 2, 3, 4,
178                 // 3-6
179                 1, 2, 3, 4,
180                 // 7-9
181                 2, 3, 4,
182                 // 10-12
183                 2, 3, 4
184             ];
185 
186         gxtEl.strokeWidth = gxtEl.strokewidth;
187         gxtEl.face = facemap[parseInt(gxtEl.style, 10)] || 'cross';
188         gxtEl.size = sizemap[parseInt(gxtEl.style, 10)] || 3;
189 
190         gxtEl.straightFirst = JXG.str2Bool(gxtEl.straightFirst);
191         gxtEl.straightLast = JXG.str2Bool(gxtEl.straightLast);
192 
193         gxtEl.visible = JXG.str2Bool(gxtEl.visible);
194         //gxtEl.withLabel = gxtEl.visible;           // withLabel is set in colorProperties()
195         gxtEl.draft = JXG.str2Bool(gxtEl.draft);
196         gxtEl.trace = JXG.str2Bool(gxtEl.trace);
197         
198         if (type==='point') {
199             // Fill properties are ignored by GEONExT
200             gxtEl.fillColor = gxtEl.strokeColor;
201             gxtEl.highlightFillColor = gxtEl.highlightStrokeColor;
202             gxtEl.fillOpacity = gxtEl.strokeOpacity;
203             gxtEl.highlightFillOpacity = gxtEl.highlightStrokeOpacity;
204         }
205 
206         if (typeof(gxtEl.label === 'string')) {
207             delete(gxtEl.label);
208         }
209 
210         delete gxtEl.color;
211         return gxtEl;
212     },
213 
214     readNodes: function (gxtEl, Data, nodeType, prefix) {
215         var arr = this.gEBTN(Data, nodeType, 0, false).childNodes,
216             key, n;
217 
218         for (n = 0; n < arr.length; n++) {
219             if (arr[n].firstChild != null) {
220                 if (prefix != null) {
221                     key = prefix + JXG.capitalize(arr[n].nodeName);
222                 } else {
223                     key = arr[n].nodeName;
224                 }
225                 gxtEl[key] = arr[n].firstChild.data;
226             }
227         }
228         return gxtEl;
229     },
230 
231     subtreeToString: function (root) {
232         try {
233             // firefox
234             return (new XMLSerializer()).serializeToString(root);
235         } catch (e) {
236             // IE
237             return root.xml;
238         }
239     },
240 
241     readImage: function (node) {
242         var pic = '',
243             nod = node;
244 
245         if (nod != null) {
246             pic = nod.data;
247             while (nod.nextSibling != null) {
248                 nod = nod.nextSibling;
249                 pic += nod.data;
250             }
251         }
252         return pic;
253     },
254 
255     parseImage: function (board, fileNode, level, x, y, w, h, el) {
256         var tag, id, im, picStr, tmpImg;
257 
258         if (fileNode == null) {
259             return null;
260         }
261 
262         if (fileNode.getElementsByTagName('src')[0] != null) {  // Background image
263             tag = 'src';
264         } else if (fileNode.getElementsByTagName('image')[0] != null 
265                        && this.gEBTN(fileNode, 'image') != 'null') {
266             tag = 'image';
267         } else {
268             return null;
269         }
270 
271         picStr = this.readImage(this.gEBTN(fileNode, tag, 0, false).firstChild);
272         if (picStr!='') {
273             picStr = 'data:image/png;base64,' + picStr;
274             if (tag=='src') {  // Background image
275                 x = this.gEBTN(fileNode, 'x');
276                 y = this.gEBTN(fileNode, 'y');
277                 w = this.gEBTN(fileNode, 'width');
278                 h = this.gEBTN(fileNode, 'height');
279                 im = board.create('image', [picStr,[x, y],[w, h]], {
280                         anchor: el, layer: level
281                         });
282                 return im;
283             } else {
284                 // Image bound to an element
285                 // Read the original dimensions, i.e. the ratio h/w with the help of a temporary image.
286                 // We have to wait until the image is loaded, therefore
287                 // we need "onload".
288                 tmpImg = new Image();
289                 tmpImg.src = picStr;
290                 id = el.id + '_image';
291                 tmpImg.onload = function(){
292                     // Now, we can read the original dimensions of the image.
293                     var wOrg = this.width,
294                         hOrg = this.height,
295                         xf, yf, wf, hf, im, tRot;
296                         
297                     if (el.elementClass == JXG.OBJECT_CLASS_LINE) {
298                         // A line containing an image, runs through the horizontal middle
299                         // of the image.
300                         xf = function(){ return el.point1.X(); };
301                         wf = function(){ return el.point1.Dist(el.point2); };
302                         hf = function(){ return wf() * hOrg / wOrg; };
303                         yf = function(){ return el.point1.Y() - hf() * 0.5; };
304                         im = board.create('image', [picStr, [xf,yf], [wf,hf]], {
305                                 layer: level,
306                                 id: id,
307                                 anchor: el
308                             });
309                         tRot = board.create('transform', [
310                                 function () {
311                                     return Math.atan2(el.point2.Y()-el.point1.Y(), el.point2.X()-el.point1.X())
312                                 },
313                                 el.point1
314                             ], {
315                                 type:'rotate'
316                             });
317                         tRot.bindTo(im);
318                         el.image = im;
319                     } else if (el.elementClass == JXG.OBJECT_CLASS_POINT) {
320                         wf = function(){ return wOrg / board.unitX; };
321                         hf = function(){ return hOrg / board.unitY; };
322                         xf = function(){ return el.X() - wf() * 0.5; };
323                         yf = function(){ return el.Y() - hf() * 0.5; };
324 
325                         im = board.create('image', [picStr, [xf,yf], [wf,hf]], {
326                                 layer: level,
327                                 id: id,
328                                 anchor: el
329                             });
330                         board.renderer.hide(el.label.content);
331                         el.image = im;
332                     } else if (el.elementClass == JXG.OBJECT_CLASS_CIRCLE) {
333                         // A circle containing an image
334                         wf = function(){ return 2.0 * el.Radius(); };
335                         hf = function(){ return wf() * hOrg / wOrg; };
336                         xf = function(){ return el.center.X() - wf() * 0.5; };
337                         yf = function(){ return el.center.Y() - hf() * 0.5; };
338                         im = board.create('image', [picStr, [xf,yf], [wf,hf]], {
339                             layer: level,
340                             id: id,
341                             anchor: el
342                         });
343                         el.image = im;
344                     } else {
345                         im = board.create('image', [picStr, [x, y], [w, h]], {
346                             layer: level,
347                             id: id,
348                             anchor: el
349                         });
350                         el.image = im;
351                     }
352                 };
353             }
354             return im;
355         }
356     },
357 
358     readConditions: function(node) {
359         var i, s, ob,
360             conditions = '';
361 
362         if (JXG.exists(node)) {
363             for(i = 0; i < node.getElementsByTagName('data').length; i++) {
364                 ob = node.getElementsByTagName('data')[i];
365                 s = this.subtreeToString(ob);
366                 conditions += s;
367             }
368         }
369 
370         return conditions;
371     },
372 
373     readViewPort: function(node) {
374         var no, arr=[];
375         no = this.gEBTN(node, 'viewport', 0, false);
376 
377         if (no) {
378             arr[0] = parseFloat(this.gEBTN(no, 'left'));
379             arr[1] = parseFloat(this.gEBTN(no, 'top'));
380             arr[2] = parseFloat(this.gEBTN(no, 'right'));
381             arr[3] = parseFloat(this.gEBTN(no, 'bottom'));
382             return arr;
383         } else {
384             return [];
385         }
386     },
387     
388     printDebugMessage: function(outputEl,gxtEl,nodetyp,success) {
389         JXG.debug("* " + success + ":  " + nodetyp + " " + gxtEl.name + " " + gxtEl.id + "<br>\n");
390     },
391 
392     /**
393      * Reading the elements of a geonext file
394      * @param {XMLTree} tree expects the content of the parsed geonext file returned by function parseFF/parseIE
395      * @param {Object} board board object
396      */
397     readGeonext: function(tree, board) {
398         var xmlNode, elChildNodes,
399             s, Data, inter, boardData, el, p,
400             conditions, tmp, strTrue = 'true', gxtReader = this;
401 
402         // maybe this is not necessary as we already provide layer options for sectors and circles via JXG.Options but
403         // maybe these have to be the same for geonext.
404         board.options.layer.sector = board.options.layer.angle;
405         board.options.layer.circle = board.options.layer.angle;
406         
407         board.options.line.label.position = 'top';
408 
409         boardData = this.gEBTN(tree, 'board', 0, false);
410         conditions = this.readConditions(boardData.getElementsByTagName('conditions')[0]);
411 
412         // resize board
413         if (board.options.takeSizeFromFile) {
414             board.resizeContainer(this.gEBTN(boardData, 'width'), this.gEBTN(boardData, 'height'));
415         }
416 
417         xmlNode = this.gEBTN(boardData, 'coordinates', 0, false);
418 
419         tmp = this.readViewPort(xmlNode);
420         if (tmp.length==4) {
421             board.setBoundingBox(tmp, false);
422         } else {
423             // zoom level
424             tmp = this.gEBTN(xmlNode, 'zoom', 0, false);
425             board.zoomX = parseFloat(this.gEBTN(tmp, 'x'));
426             board.zoomY = parseFloat(this.gEBTN(tmp, 'y'));
427 
428             // set the origin
429             tmp = this.gEBTN(xmlNode, 'origin', 0, false);
430             board.origin = {
431                 usrCoords: [1, 0, 0],
432                 scrCoords: [1, parseFloat(this.gEBTN(tmp, 'x'))*board.zoomX, parseFloat(this.gEBTN(tmp, 'y'))*board.zoomY]
433             };
434 
435             // screen to user coordinates conversion
436             tmp = this.gEBTN(xmlNode, 'unit', 0, false);
437             board.unitX = parseFloat(this.gEBTN(tmp, 'x'))*board.zoomX;
438             board.unitY = parseFloat(this.gEBTN(tmp, 'y'))*board.zoomY;
439         }
440 
441         if (board.options.takeSizeFromFile) {
442             board.resizeContainer(this.gEBTN(boardData, 'width'), this.gEBTN(boardData, 'height'));
443         }
444         
445         // check and set fontSize
446         if (!(parseFloat(board.options.text.fontSize) > 0)) {
447             board.options.text.fontSize = 12;
448         }
449 
450         board.geonextCompatibilityMode = true;
451 
452         // jsxgraph chooses an id for the board but we don't want to use it, we want to use
453         // the id stored in the geonext file. if you know why this is required, please note it here.
454         delete(JXG.JSXGraph.boards[board.id]);
455         board.id = this.gEBTN(boardData, 'id');
456         JXG.JSXGraph.boards[board.id] = board;
457 
458         // this creates some basic elements present in every geonext construction but not explicitly present in the file
459         board.initGeonextBoard();
460         
461         // Update of properties during update() is not necessary in GEONExT files
462         // But it maybe necessary if we construct with JavaScript afterwards
463         board.renderer.enhancedRendering = true;
464 
465         // Read background image
466         this.parseImage(board, this.gEBTN(boardData, 'file', 0, false), board.options.layer['image']);
467 
468         board.options.point.snapToGrid = (this.gEBTN(this.gEBTN(boardData, 'coordinates', 0, false), 'snap') == strTrue);
469         //
470         // TODO: Missing jsxgraph feature snapToPoint
471         // If snapToGrid and snapToPoint are both true, point snapping is enabled
472         if (board.options.point.snapToGrid && this.gEBTN(this.gEBTN(boardData, 'grid', 1, false), 'pointsnap') == strTrue) {
473             board.options.point.snapToGrid = false;
474             board.options.point.snapToPoints = true;
475             board.options.point.attractorDistance = 0.5;
476         }
477         //board.options.grid.snapToGrid = false;
478 
479         xmlNode = this.gEBTN(boardData, 'grid', 1, false);
480         tmp = this.gEBTN(xmlNode,  'x');
481         if (tmp) {
482             board.options.grid.gridX = 1 / parseFloat(tmp);
483             board.options.point.snapSizeX = parseFloat(tmp);
484         }
485         tmp = this.gEBTN(xmlNode,  'y');
486         if (tmp) {
487             board.options.grid.gridY = 1 / parseFloat(tmp);
488             board.options.point.snapSizeY = parseFloat(tmp);
489         }
490         //board.calculateSnapSizes();             // Seems not to be correct
491         board.options.grid.gridDash = JXG.str2Bool(this.gEBTN(xmlNode, 'dash'));
492 
493         tmp = JXG.rgba2rgbo(this.gEBTN(xmlNode, 'color'));
494         board.options.grid.gridColor = tmp[0];
495         board.options.grid.gridOpacity = tmp[1];
496 
497         xmlNode = this.gEBTN(boardData, 'coordinates', 0, false);
498         if (this.gEBTN(xmlNode, 'grid') == strTrue) {
499             board.create('grid', []);
500         }
501 
502         if (this.gEBTN(xmlNode, 'coord') == strTrue) {
503             board.options.axis.ticks.majorHeight = 10;        // Hard coded default option
504             board.options.axis.ticks.minorHeight = 4;         // Hard coded default option
505             board.create('axis', [[0, 0], [1, 0]]);
506             board.create('axis', [[0, 0], [0, 1]]);
507         }
508         
509         tmp = this.gEBTN(this.gEBTN(boardData, 'background', 0, false), 'color');
510         if (tmp.length==8) { tmp = '#'+tmp; }
511         board.containerObj.style.backgroundColor = JXG.rgba2rgbo(tmp)[0];
512 
513         elChildNodes = tree.getElementsByTagName("elements")[0].childNodes;
514         for (s = 0; s < elChildNodes.length; s++) {
515             (function (s) {
516                 var i, gxtEl = {},
517                     l, x, y, c, numberDefEls,
518                     el, p, inter, rgbo, tmp;
519 
520                 Data = elChildNodes[s];
521                 gxtEl = gxtReader.defProperties(gxtEl, Data);
522 
523                 // Skip text nodes
524                 if (!JXG.exists(gxtEl)) {
525                     return;
526                 }
527 
528                 gxtReader.printDebugMessage('debug', gxtEl, Data.nodeName.toLowerCase, 'READ:');
529                 switch (Data.nodeName.toLowerCase()) {
530                     case "point":
531                         gxtEl.strokewidth = 1; // Old file format
532                         gxtEl = gxtReader.colorProperties(gxtEl, Data);
533                         gxtEl = gxtReader.visualProperties(gxtEl, Data);
534                         gxtEl = gxtReader.firstLevelProperties(gxtEl, Data);
535                         gxtEl = gxtReader.readNodes(gxtEl, Data, 'data');
536                         gxtEl.fixed = JXG.str2Bool(gxtReader.gEBTN(Data, 'fix'));
537                         gxtEl = gxtReader.transformProperties(gxtEl, 'point');
538 
539                         //try {
540                             p = board.create('point', [parseFloat(gxtEl.x), parseFloat(gxtEl.y)], gxtEl);
541 
542                             var v = function(){ return p.visProp.visible; };
543                             el = gxtReader.parseImage(board, Data, board.options.layer['image'], 0, 0, 0, 0, p);
544                             gxtReader.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
545                         /*
546                         if (JXG.exists(el)) {
547                             el.visProp.visible = function() { return p.visProp.visible; };
548                             alert(p.visProp.visible);
549                             if (el.visProp.visible()) {el.showElement();} else {el.hideElement();}
550                         }
551                         */
552                         //} catch(e) {
553                         //    JXG.debug(e);
554                         //}
555                         break;
556                     case "line":
557                         gxtEl = gxtReader.colorProperties(gxtEl, Data);
558                         gxtEl = gxtReader.visualProperties(gxtEl, Data);
559                         gxtEl = gxtReader.firstLevelProperties(gxtEl, Data);
560                         gxtEl = gxtReader.readNodes(gxtEl, Data, 'data');
561                         gxtEl = gxtReader.readNodes(gxtEl, Data, 'straight', 'straight');
562                         gxtEl = gxtReader.transformProperties(gxtEl);
563 
564                         gxtEl.first = gxtReader.changeOriginIds(board, gxtEl.first);
565                         gxtEl.last = gxtReader.changeOriginIds(board, gxtEl.last);
566 
567                         l = board.create('line', [gxtEl.first, gxtEl.last], gxtEl);
568 
569                         gxtReader.parseImage(board, Data, board.options.layer['image'], 0, 0, 0, 0, l);
570                         gxtReader.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
571                         break;
572                     case "circle":
573                         gxtEl = gxtReader.colorProperties(gxtEl, Data);
574                         gxtEl = gxtReader.visualProperties(gxtEl, Data);
575                         gxtEl = gxtReader.firstLevelProperties(gxtEl, Data);
576                         
577                         tmp = gxtReader.gEBTN(Data, 'data', 0, false);
578                         gxtEl.center = gxtReader.changeOriginIds(board, gxtReader.gEBTN(tmp, 'midpoint'));
579 
580                         if (tmp.getElementsByTagName('radius').length > 0) {
581                             gxtEl.radius = gxtReader.changeOriginIds(board, gxtReader.gEBTN(tmp, 'radius'));
582                         } else if (tmp.getElementsByTagName('radiusvalue').length > 0) {
583                             gxtEl.radius = gxtReader.gEBTN(tmp, 'radiusvalue');
584                         }
585                         gxtEl = gxtReader.transformProperties(gxtEl);
586                         c = board.create('circle', [gxtEl.center, gxtEl.radius], gxtEl);
587 
588                         gxtReader.parseImage(board, Data, board.options.layer['image'], 0, 0, 0, 0, c);
589                         gxtReader.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
590                         break;
591                     case "slider":
592                         gxtEl.strokewidth = 1; // Old file format
593                         gxtEl = gxtReader.colorProperties(gxtEl, Data);
594                         gxtEl = gxtReader.visualProperties(gxtEl, Data);
595                         gxtEl = gxtReader.firstLevelProperties(gxtEl, Data);
596 
597                         gxtEl = gxtReader.readNodes(gxtEl, Data, 'data');
598                         gxtEl.fixed = JXG.str2Bool(gxtReader.gEBTN(Data, 'fix'));
599                         gxtEl = gxtReader.readNodes(gxtEl, Data, 'animate', 'animate');
600                         gxtEl = gxtReader.transformProperties(gxtEl, 'point');
601                         try {
602                             gxtEl.parent = gxtReader.changeOriginIds(board, gxtEl.parent);
603 
604                             // if (board.isSuspendedUpdate) { board.unsuspendUpdate().suspendUpdate(); }
605                             p = board.create('glider', [parseFloat(gxtEl.x), parseFloat(gxtEl.y), gxtEl.parent], gxtEl);
606                             p.onPolygon = JXG.exists(gxtEl.onpolygon) && JXG.str2Bool(gxtEl.onpolygon);
607                             
608                             gxtReader.parseImage(board, Data, board.options.layer['point'], 0, 0, 0, 0, p);
609                             
610                             //if (board.isSuspendedUpdate) { board.unsuspendUpdate().suspendUpdate(); }
611                             
612                             gxtReader.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
613                         } catch(e) {
614                             JXG.debug("* <b>Err:</b>  Slider " + gxtEl.name + " " + gxtEl.id + ': '+ gxtEl.parent +"<br>\n");
615                         }
616                         break;
617                     case "cas":
618                         gxtEl = gxtReader.colorProperties(gxtEl, Data);
619                         gxtEl = gxtReader.visualProperties(gxtEl, Data);
620                         gxtEl = gxtReader.firstLevelProperties(gxtEl, Data);
621                         gxtEl.fixed = JXG.str2Bool(Data.getElementsByTagName('fix')[0].firstChild.data);
622                         gxtEl = gxtReader.readNodes(gxtEl, Data, 'data');
623                         gxtEl = gxtReader.transformProperties(gxtEl, 'point');
624 
625 						if (false) { // Handle weird element names
626 							gxtEl.x = JXG.GeonextParser.gxt2jc(gxtEl.x, board);
627 							gxtEl.y = JXG.GeonextParser.gxt2jc(gxtEl.y, board);
628 						} else {  // Workaround until the jessiecode compiler is 100% compatible
629 							gxtEl.x = JXG.GeonextParser.geonext2JS(gxtEl.x, board);
630 							gxtEl.x = new Function('return ' + gxtEl.x + ';');
631 							gxtEl.y = JXG.GeonextParser.geonext2JS(gxtEl.y, board);
632 							gxtEl.y = new Function('return ' + gxtEl.y + ';');
633 						}
634 
635                         /*
636                         p = board.create('point', [parseFloat(gxtEl.xval), parseFloat(gxtEl.yval)], gxtEl);
637                         p.addConstraint([gxtEl.x, gxtEl.y]);
638                         p.type = JXG.OBJECT_TYPE_CAS;
639                         */
640                         p = board.create('point', [gxtEl.x, gxtEl.y], gxtEl);
641                         gxtReader.parseImage(board, Data, board.options.layer['point'], 0, 0, 0, 0, p);
642                         gxtReader.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
643                         break;
644                     case "intersection":
645                         gxtEl.strokewidth = 1; // Old file format
646                         gxtEl = gxtReader.readNodes(gxtEl, Data, 'data');
647                         xmlNode = Data.getElementsByTagName('first')[1];
648 
649                         gxtEl.outFirst = {};
650                         gxtEl.outFirst = gxtReader.colorProperties(gxtEl.outFirst, xmlNode);
651                         gxtEl.outFirst = gxtReader.visualProperties(gxtEl.outFirst, xmlNode);
652                         gxtEl.outFirst = gxtReader.firstLevelProperties(gxtEl.outFirst, xmlNode); 
653                         gxtEl.outFirst.fixed = JXG.str2Bool(xmlNode.getElementsByTagName('fix')[0].firstChild.data);
654                         gxtEl.outFirst = gxtReader.transformProperties(gxtEl.outFirst, 'point');
655                         gxtEl.first = gxtReader.changeOriginIds(board, gxtEl.first);
656                         gxtEl.last = gxtReader.changeOriginIds(board, gxtEl.last);
657 
658                         //if ((board.objects[gxtEl.first].type == JXG.OBJECT_TYPE_LINE || board.objects[gxtEl.first].type == JXG.OBJECT_TYPE_ARROW)
659                         // && (board.objects[gxtEl.last].type == JXG.OBJECT_TYPE_LINE || board.objects[gxtEl.last].type == JXG.OBJECT_TYPE_ARROW)) {
660                         if ((JXG.getReference(board, gxtEl.first).elementClass == JXG.OBJECT_CLASS_LINE)
661                             && (JXG.getReference(board, gxtEl.last).elementClass == JXG.OBJECT_CLASS_LINE)) {
662                             /*
663                             inter = new JXG.Intersection(board, gxtEl.id, board.objects[gxtEl.first],
664                                     board.objects[gxtEl.last], gxtEl.outFirst.id, '',
665                                     gxtEl.outFirst.name, '');
666                             inter.p.setProperty(gxtEl.outFirst);
667                             */
668                             inter = board.create('intersection', [board.objects[gxtEl.first], board.objects[gxtEl.last], 0], gxtEl.outFirst);
669                             /* offensichtlich braucht man dieses if doch */
670                             if (gxtEl.outFirst.visible == "false") {
671                                 inter.hideElement();
672                             }
673                         } else {
674                             xmlNode = Data.getElementsByTagName('last')[1];
675                             if (JXG.exists(xmlNode)) {
676                                 gxtEl.outLast = {};
677                                 gxtEl.outLast = gxtReader.colorProperties(gxtEl.outLast, xmlNode);
678                                 gxtEl.outLast = gxtReader.visualProperties(gxtEl.outLast, xmlNode);
679                                 gxtEl.outLast = gxtReader.firstLevelProperties(gxtEl.outLast, xmlNode);
680                                 gxtEl.outLast.fixed = JXG.str2Bool(xmlNode.getElementsByTagName('fix')[0].firstChild.data);
681                                 gxtEl.outLast = gxtReader.transformProperties(gxtEl.outLast, 'point');
682                             /*
683                                 inter = new JXG.Intersection(board, gxtEl.id, board.objects[gxtEl.first],
684                                     board.objects[gxtEl.last], gxtEl.outFirst.id, gxtEl.outLast.id,
685                                     gxtEl.outFirst.name, gxtEl.outLast.name);
686                                 inter.p1.setProperty(gxtEl.outFirst);
687                                 inter.p2.setProperty(gxtEl.outLast);
688                             */
689                                 inter = board.create('intersection', [board.objects[gxtEl.first], board.objects[gxtEl.last], 0], gxtEl.outFirst);
690                                 inter = board.create('intersection', [board.objects[gxtEl.first], board.objects[gxtEl.last], 1], gxtEl.outLast);
691                             }
692                         }
693                         gxtReader.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
694                         break;
695                     case "composition":
696                         gxtEl = gxtReader.readNodes(gxtEl, Data, 'data');
697                         gxtEl = gxtReader.firstLevelProperties(gxtEl, Data);
698                         gxtEl.defEl = [];
699                         numberDefEls = 0;
700                         xmlNode = Data.getElementsByTagName('data')[0].getElementsByTagName('input');
701                         for (i = 0; i < xmlNode.length; i++) {
702                             gxtEl.defEl[i] = xmlNode[i].firstChild.data;
703                             numberDefEls = i + 1;
704                         }
705 
706                         // every composition produces at least one element and the data for this element is stored
707                         // in gxtEl.out. if additional elements are created their data is read in the according case.
708                         xmlNode = Data.getElementsByTagName('output')[0];
709                         gxtEl.out = {};
710                         gxtEl.out = gxtReader.colorProperties(gxtEl.out, xmlNode);
711                         gxtEl.out = gxtReader.visualProperties(gxtEl.out, xmlNode);
712                         gxtEl.out = gxtReader.firstLevelProperties(gxtEl.out, xmlNode);
713                         gxtEl.out = gxtReader.transformProperties(gxtEl.out);
714 
715                         gxtEl.defEl[0] = gxtReader.changeOriginIds(board, gxtEl.defEl[0]);
716                         gxtEl.defEl[1] = gxtReader.changeOriginIds(board, gxtEl.defEl[1]);
717                         gxtEl.defEl[2] = gxtReader.changeOriginIds(board, gxtEl.defEl[2]);
718 
719                         // if (board.isSuspendedUpdate) { board.unsuspendUpdate().suspendUpdate(); }
720                         switch (gxtEl.type) {
721                             // ARROW_PARALLEL
722                             case "210070":
723                                 gxtEl.out.fixed = gxtReader.gEBTN(xmlNode, 'fix');
724 
725                                 xmlNode = Data.getElementsByTagName('output')[1];
726                                 gxtEl.outPoint = {};
727                                 gxtEl.outPoint = gxtReader.defProperties(gxtEl.outPoint, xmlNode);
728                                 gxtEl.outPoint = gxtReader.colorProperties(gxtEl.outPoint, xmlNode);
729                                 gxtEl.outPoint = gxtReader.visualProperties(gxtEl.outPoint, xmlNode);
730                                 gxtEl.outPoint = gxtReader.firstLevelProperties(gxtEl.outPoint, xmlNode);
731                                 gxtEl.outPoint = gxtReader.transformProperties(gxtEl.outPoint);
732                                 gxtEl.out.point = gxtEl.outPoint;
733 
734                                 board.create('arrowparallel', [gxtEl.defEl[1], gxtEl.defEl[0]], gxtEl.out);
735                                 break;
736 
737                             // BISECTOR
738                             case "210080":
739                                 gxtEl.out.straightFirst = false;
740                                 board.create('bisector', [gxtEl.defEl[0], gxtEl.defEl[1], gxtEl.defEl[2]], gxtEl.out);
741                                 break;
742 
743                             // CIRCUMCIRCLE
744                             case "210090":
745                                 xmlNode = Data.getElementsByTagName('output')[1];
746                                 gxtEl.outCircle = {};
747                                 gxtEl.outCircle = gxtReader.defProperties(gxtEl.outCircle, xmlNode);
748                                 gxtEl.outCircle = gxtReader.colorProperties(gxtEl.outCircle, xmlNode);
749                                 gxtEl.outCircle = gxtReader.visualProperties(gxtEl.outCircle, xmlNode);
750                                 gxtEl.outCircle = gxtReader.firstLevelProperties(gxtEl.outCircle, xmlNode);
751                                 gxtEl.outCircle = gxtReader.transformProperties(gxtEl.outCircle);
752                                 gxtEl.outCircle.point = gxtEl.out;
753                                 board.create('circumcircle', [gxtEl.defEl[0], gxtEl.defEl[1], gxtEl.defEl[2]], gxtEl.outCircle);
754                                 break;
755 
756                             // CIRCUMCIRCLE_CENTER
757                             case "210100":
758                                 board.create('circumcenter', [gxtEl.defEl[0], gxtEl.defEl[1], gxtEl.defEl[2]], gxtEl.out);
759                                 break;
760 
761                             // MIDPOINT
762                             case "210110":
763                                 board.create('midpoint', gxtEl.defEl.slice(0, numberDefEls), gxtEl.out);
764                                 break;
765 
766                              // MIRRORLINE
767                             case "210120":
768                                 board.create('reflection', [gxtEl.defEl[1], gxtEl.defEl[0]], gxtEl.out);
769                                 break;
770 
771                             // MIRROR_POINT
772                             case "210125":
773                                 board.create('mirrorpoint', [gxtEl.defEl[0], gxtEl.defEl[1]], gxtEl.out);
774                                 break;
775 
776                             // NORMAL
777                             case "210130":
778                                 //board.create('normal', [gxtEl.defEl[1], gxtEl.defEl[0]], gxtEl.out);
779                                 board.create('perpendicularsegment', [gxtEl.defEl[0], gxtEl.defEl[1]], gxtEl.out);
780                                 break;
781 
782                             // PARALLEL
783                             case "210140":
784                                 p =  board.create('parallelpoint', [gxtEl.defEl[1], gxtEl.defEl[0]], 
785                                         {withLabel:false, visible:false, name:'', fixed:true});
786                                 
787                                 // GEONExT uses its own parallel construction to make the order
788                                 // of intersection points compatible.
789                                 // el = board.create('parallel', [gxtEl.defEl[1], gxtEl.defEl[0]], gxtEl.out);
790                                 el = board.create('line', [gxtEl.defEl[0], p], gxtEl.out);  
791                                 el.parallelpoint = p;
792                                 break;
793 
794                             // PARALLELOGRAM_POINT
795                             case "210150":
796                                 board.create('parallelpoint', gxtEl.defEl.slice(0, numberDefEls), gxtEl.out);
797                                 break;
798 
799                             // PERPENDICULAR
800                             case "210160":
801                                 // output[0] was already read and is stored in gxtEl.out
802                                 gxtEl.out.fixed = gxtReader.gEBTN(xmlNode, 'fix');
803 
804                                 xmlNode = Data.getElementsByTagName('output')[1];
805                                 gxtEl.outLine = {};
806                                 gxtEl.outLine = gxtReader.defProperties(gxtEl.outLine, xmlNode);
807                                 gxtEl.outLine = gxtReader.colorProperties(gxtEl.outLine, xmlNode);
808                                 gxtEl.outLine = gxtReader.visualProperties(gxtEl.outLine, xmlNode);
809                                 gxtEl.outLine = gxtReader.firstLevelProperties(gxtEl.outLine, xmlNode);
810                                 gxtEl.outLine = gxtReader.readNodes(gxtEl.outLine, xmlNode, 'straight', 'straight');
811                                 gxtEl.outLine = gxtReader.transformProperties(gxtEl.outLine);
812                                 gxtEl.outLine.point = gxtEl.out;
813 
814                                 board.create('perpendicularsegment', [gxtEl.defEl[1], gxtEl.defEl[0]], gxtEl.outLine);
815                                 break;
816 
817                             // PERPENDICULAR_POINT
818                             case "210170":
819                                 board.create('perpendicularpoint', [gxtEl.defEl[1], gxtEl.defEl[0]], gxtEl.out);
820                                 break;
821 
822                             // ROTATION
823                             case "210180":
824                                 throw new Error('JSXGraph: Element ROTATION not yet implemented.');
825                                 break;
826 
827                             // SECTOR
828                             case "210190":
829                                 // sectors usually provide more than one output element but JSXGraph is not fully compatible
830                                 // to GEONExT sector elements. GEONExT sectors consist of two lines, a point, and a sector,
831                                 // JSXGraph uses a curve to display the sector incl. the borders, and
832                                 // a point and two lines. 
833                                 // Gliders on sectors also run through the borders.
834                                 gxtEl.out = gxtReader.defProperties(gxtEl.out, xmlNode);
835                                 gxtEl.out.firstArrow = JXG.str2Bool(gxtReader.gEBTN(xmlNode, 'firstarrow'));
836                                 gxtEl.out.lastArrow = JXG.str2Bool(gxtReader.gEBTN(xmlNode, 'lastarrow'));
837 
838                                 // el = board.create('sector', gxtEl.defEl, gxtEl.out);
839                                 for (i=0; i<4;i++) {
840                                     xmlNode = Data.getElementsByTagName('output')[i];
841                                     gxtEl.out = gxtReader.defProperties(gxtEl.out, xmlNode);
842                                     gxtEl.out = gxtReader.colorProperties(gxtEl.out, xmlNode);
843                                     gxtEl.out = gxtReader.visualProperties(gxtEl.out, xmlNode);
844                                     gxtEl.out = gxtReader.firstLevelProperties(gxtEl.out, xmlNode);
845                                     gxtEl.out = gxtReader.transformProperties(gxtEl.out);
846                                     
847                                     if (i==0) {
848                                         el = board.create('sector', gxtEl.defEl, gxtEl.out);
849                                     } else if (i==1) {
850                                         p = board.create('point', [
851                                             function(){ 
852                                                 var p1 = JXG.getRef(board,gxtEl.defEl[1]), p2 = JXG.getRef(board,gxtEl.defEl[2]);
853                                                 return p1.X() + (p2.X()-p1.X())*el.Radius/p1.Dist(p2);
854                                             },
855                                             function(){ 
856                                                 var p1 = JXG.getRef(board,gxtEl.defEl[1]), p2 = JXG.getRef(board,gxtEl.defEl[2]);
857                                                 return p1.Y() + (p2.Y()-p1.Y())*el.Radius/p1.Dist(p2);
858                                             }], gxtEl.out);
859                                         //p = JXG.getReference(board,gxtEl.defEl[2]);
860                                     } else if (i==2) {
861                                         el = board.create('segment', [gxtEl.defEl[0], gxtEl.defEl[1]], gxtEl.out);
862                                     } else if (i==3) {
863                                         el = board.create('segment', [gxtEl.defEl[1], p], gxtEl.out);
864                                     }
865                                 }
866                                 break;
867                             default:
868                                 throw new Error("JSXGraph: GEONExT-Element " + gxtEl.type + ' not implemented.');
869                                 break;
870                         }
871 
872                         // if (board.isSuspendedUpdate) { board.unsuspendUpdate().suspendUpdate(); }
873                         gxtReader.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
874                         break;
875                     case "polygon":
876                         gxtEl = gxtReader.colorProperties(gxtEl, Data);
877                         gxtEl = gxtReader.firstLevelProperties(gxtEl, Data);
878                         var dataVertex = [];
879                         // In Geonext file format the first vertex is equal to the last vertex:
880                         for (i = 0; i < Data.getElementsByTagName('data')[0].getElementsByTagName('vertex').length-1; i++) {
881                             dataVertex[i] = Data.getElementsByTagName('data')[0].getElementsByTagName('vertex')[i].firstChild.data;
882                             dataVertex[i] = gxtReader.changeOriginIds(board, dataVertex[i]);
883                         }
884                         gxtEl.border = [];
885                         gxtEl.borders = {
886                             ids: []
887                         };
888                         for (i = 0; i < Data.getElementsByTagName('border').length; i++) {
889                             gxtEl.border[i] = {};
890                             xmlNode = Data.getElementsByTagName('border')[i];
891                             gxtEl.border[i].id = xmlNode.getElementsByTagName('id')[0].firstChild.data;
892                             gxtEl.borders.ids.push(gxtEl.border[i].id);
893                             gxtEl.border[i].name = xmlNode.getElementsByTagName('name')[0].firstChild.data;
894                             gxtEl.border[i].straightFirst = JXG.str2Bool(xmlNode.getElementsByTagName('straight')[0].getElementsByTagName('first')[0].firstChild.data);
895                             gxtEl.border[i].straightLast = JXG.str2Bool(xmlNode.getElementsByTagName('straight')[0].getElementsByTagName('last')[0].firstChild.data);
896                             try {
897                                 gxtEl.border[i].strokeWidth = xmlNode.getElementsByTagName('strokewidth')[0].firstChild.data;
898                             } catch(e) {
899                                 gxtEl.border[i].strokeWidth = xmlNode.getElementsByTagName('width')[0].firstChild.data;
900                             }
901                             try {
902                                 gxtEl.border[i].dash = JXG.str2Bool(xmlNode.getElementsByTagName('dash')[0].firstChild.data);
903                             } catch(e) {}
904                             gxtEl.border[i].visible = JXG.str2Bool(xmlNode.getElementsByTagName('visible')[0].firstChild.data);
905                             gxtEl.border[i].draft = JXG.str2Bool(xmlNode.getElementsByTagName('draft')[0].firstChild.data);
906                             gxtEl.border[i].trace = JXG.str2Bool(xmlNode.getElementsByTagName('trace')[0].firstChild.data);
907 
908                             xmlNode = Data.getElementsByTagName('border')[i].getElementsByTagName('color')[0];
909                             rgbo = JXG.rgba2rgbo(xmlNode.getElementsByTagName('stroke')[0].firstChild.data);
910                             gxtEl.border[i].strokeColor = rgbo[0];
911                             gxtEl.border[i].strokeOpacity = rgbo[1];
912 
913                             rgbo = JXG.rgba2rgbo(xmlNode.getElementsByTagName('lighting')[0].firstChild.data);
914                             gxtEl.border[i].highlightStrokeColor = rgbo[0];
915                             gxtEl.border[i].highlightStrokeOpacity = rgbo[1];
916 
917                             rgbo = JXG.rgba2rgbo(xmlNode.getElementsByTagName('fill')[0].firstChild.data);
918                             gxtEl.border[i].fillColor = rgbo[0];
919                             gxtEl.border[i].fillOpacity = rgbo[1];
920 
921                             gxtEl.border[i].highlightFillColor = gxtEl.border[i].fillColor;
922                             gxtEl.border[i].highlightFillOpacity = gxtEl.border[i].fillOpacity;
923 
924                             gxtEl.border[i].labelColor = xmlNode.getElementsByTagName('label')[0].firstChild.data;
925                             gxtEl.border[i].colorDraft = xmlNode.getElementsByTagName('draft')[0].firstChild.data;
926                         }
927                         gxtEl = gxtReader.transformProperties(gxtEl);
928                         p = board.create('polygon', dataVertex, gxtEl);
929 
930                         // to emulate the geonext behaviour on invisible polygons
931                         // A.W.: Why do we need this?
932 /*                        
933                         if (!gxtEl.visible) {
934                             p.setProperty({
935                                 fillColor: 'none',
936                                 highlightFillColor: 'none'
937                             });
938                         }
939 */
940                         for (i = 0; i < p.borders.length; i++) {
941                             p.borders[i].setProperty(gxtEl.border[i]);
942                         }
943                             
944                         gxtReader.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
945                         break;
946                     case "graph":
947                         gxtEl = gxtReader.colorProperties(gxtEl, Data);
948                         gxtEl = gxtReader.firstLevelProperties(gxtEl, Data);
949                         gxtEl.funct = Data.getElementsByTagName('data')[0].getElementsByTagName('function')[0].firstChild.data;
950 						if (false) {
951 							gxtEl.funct = JXG.GeonextParser.gxt2jc(gxtEl.funct, board); // Handle weird element names
952 						} else { // Workaround until the jessiecode compiler is 100% compatible
953 							gxtEl.funct = JXG.GeonextParser.geonext2JS(gxtEl.funct, board);
954 							gxtEl.funct = new Function('x', 'return ' + gxtEl.funct + ';');
955 						}
956 						
957                         c = board.create('curve', ['x', gxtEl.funct], {
958                                 id: gxtEl.id,
959                                 name: gxtEl.name,
960                                 strokeColor: gxtEl.strokeColor,
961                                 strokeWidth: gxtEl.strokeWidth,
962                                 fillColor: 'none',
963                                 highlightFillColor: 'none',
964                                 highlightStrokeColor: gxtEl.highlightStrokeColor,
965                                 visible: JXG.str2Bool(gxtEl.visible)
966                             });
967 
968                         gxtReader.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
969                         break;
970                     case "arrow":
971                         gxtEl = gxtReader.colorProperties(gxtEl, Data);
972                         gxtEl = gxtReader.visualProperties(gxtEl, Data);
973                         gxtEl = gxtReader.firstLevelProperties(gxtEl, Data);
974                         gxtEl = gxtReader.readNodes(gxtEl, Data, 'data');
975                         gxtEl = gxtReader.readNodes(gxtEl, Data, 'straight', 'straight');
976 
977                         gxtEl = gxtReader.transformProperties(gxtEl);
978                         gxtEl.first = gxtReader.changeOriginIds(board, gxtEl.first);
979                         gxtEl.last = gxtReader.changeOriginIds(board, gxtEl.last);
980 
981                         l = board.create('arrow', [gxtEl.first, gxtEl.last], gxtEl);
982 
983                         gxtReader.printDebugMessage('debug', l, Data.nodeName, 'OK');
984                         break;
985                     case "arc":
986                         gxtEl = gxtReader.colorProperties(gxtEl, Data);
987                         gxtEl = gxtReader.visualProperties(gxtEl, Data);
988                         gxtEl = gxtReader.firstLevelProperties(gxtEl, Data);
989                         gxtEl = gxtReader.readNodes(gxtEl, Data, 'data');
990 
991                         gxtEl.firstArrow = JXG.str2Bool(Data.getElementsByTagName('lastarrow')[0].firstChild.data);   // It seems that JSXGraph and GEONExT
992                         gxtEl.lastArrow = JXG.str2Bool(Data.getElementsByTagName('firstarrow')[0].firstChild.data);   // use opposite directions.
993 
994                         gxtEl = gxtReader.transformProperties(gxtEl);
995 
996                         gxtEl.center = gxtReader.changeOriginIds(board, gxtEl.midpoint);
997                         gxtEl.angle = gxtReader.changeOriginIds(board, gxtEl.angle);
998                         gxtEl.radius = gxtReader.changeOriginIds(board, gxtEl.radius);
999 
1000                         c = board.create('arc', [gxtEl.center, gxtEl.radius, gxtEl.angle], gxtEl);
1001 
1002                         gxtReader.printDebugMessage('debug', c, Data.nodeName, 'OK');
1003                         break;
1004                     case "angle":
1005                         gxtEl = gxtReader.colorProperties(gxtEl, Data);
1006                         gxtEl = gxtReader.visualProperties(gxtEl, Data);
1007                         gxtEl = gxtReader.firstLevelProperties(gxtEl, Data);
1008                         gxtEl = gxtReader.readNodes(gxtEl, Data, 'data');
1009                         gxtEl = gxtReader.transformProperties(gxtEl);
1010 
1011                         tmp = gxtEl.name;
1012                         try {
1013                             gxtEl.name = Data.getElementsByTagName('text')[0].firstChild.data;
1014                         } catch (e) {
1015                             gxtEl.name = '';
1016                         }
1017                         c = board.create('angle', [gxtEl.first, gxtEl.middle, gxtEl.last], gxtEl);
1018                         c.setProperty({name:tmp});
1019 
1020                         gxtReader.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
1021                         break;
1022                     case "text":
1023                         if (gxtEl.id.match(/oldVersion/)) {
1024                             break;
1025                         }
1026                         gxtEl = gxtReader.colorProperties(gxtEl, Data);
1027                         gxtEl = gxtReader.visualProperties(gxtEl, Data);
1028                         gxtEl = gxtReader.firstLevelProperties(gxtEl, Data);
1029 
1030                         gxtEl = gxtReader.readNodes(gxtEl, Data, 'data');
1031                         try {
1032                             gxtEl.mpStr = gxtReader.subtreeToString(Data.getElementsByTagName('data')[0].getElementsByTagName('mp')[0]);
1033                             gxtEl.mpStr = gxtEl.mpStr.replace(/<\/?mp>/g, '');
1034                         } catch (e) {
1035                             gxtEl.mpStr = gxtReader.subtreeToString(Data.getElementsByTagName('data')[0].getElementsByTagName('content')[0]);
1036                             gxtEl.mpStr = gxtEl.mpStr.replace(/<\/?content>/g, '');
1037                         }
1038 						gxtEl.fixed = false;
1039                         try {
1040                             if (Data.getElementsByTagName('data')[0].getElementsByTagName('parent')[0].firstChild) {
1041                                 gxtEl.parent = Data.getElementsByTagName('data')[0].getElementsByTagName('parent')[0].firstChild.data;
1042 								gxtEl.fixed = true;
1043                             }
1044                         } catch (e) {
1045                         }
1046                         
1047                         try {
1048                             gxtEl.condition = Data.getElementsByTagName('condition')[0].firstChild.data;
1049                         } catch (e) {
1050                             gxtEl.condition = "";
1051                         }
1052                         gxtEl.content = Data.getElementsByTagName('content')[0].firstChild.data;
1053                         try {
1054                             gxtEl.fixed = Data.getElementsByTagName('fix')[0].firstChild.data;
1055                         } catch (e) {
1056                             gxtEl.fixed = false;
1057                         }
1058                         // not used: gxtEl.digits = Data.getElementsByTagName('cs')[0].firstChild.data;
1059                         try {
1060                             gxtEl.autodigits = Data.getElementsByTagName('digits')[0].firstChild.data;
1061                         } catch (e) {
1062                             gxtEl.autodigits = 2;
1063                         }
1064                         gxtEl.parent = gxtReader.changeOriginIds(board, gxtEl.parent);
1065                         
1066                         c = board.create('text', [gxtEl.x, gxtEl.y, gxtEl.mpStr], {
1067                                 anchor: gxtEl.parent,
1068                                 id: gxtEl.id,
1069                                 name: gxtEl.name,
1070                                 digits: gxtEl.autodigits,
1071                                 isLabel: false,
1072                                 strokeColor: gxtEl.colorLabel,
1073 								fixed: gxtEl.fixed,
1074                                 visible: JXG.str2Bool(gxtEl.visible)
1075                             });
1076                         break;
1077                     case 'parametercurve':
1078                         gxtEl = gxtReader.colorProperties(gxtEl, Data);
1079                         gxtEl = gxtReader.visualProperties(gxtEl, Data);
1080                         gxtEl = gxtReader.firstLevelProperties(gxtEl, Data);
1081                         gxtEl = gxtReader.transformProperties(gxtEl);                        
1082                         gxtEl.functionx = Data.getElementsByTagName('functionx')[0].firstChild.data;
1083                         gxtEl.functiony = Data.getElementsByTagName('functiony')[0].firstChild.data;
1084                         gxtEl.min = Data.getElementsByTagName('min')[0].firstChild.data;
1085                         gxtEl.max = Data.getElementsByTagName('max')[0].firstChild.data;
1086 						/*
1087 						gxtEl.functionx = JXG.GeonextParser.gxt2jc(gxtEl.functionx, board);
1088 						gxtEl.functiony = JXG.GeonextParser.gxt2jc(gxtEl.functiony, board);
1089 						gxtEl.min = JXG.GeonextParser.gxt2jc(gxtEl.min, board);
1090 						gxtEl.max = JXG.GeonextParser.gxt2jc(gxtEl.max, board);
1091 						*/
1092                         gxtEl.fillColor = 'none';
1093                         gxtEl.highlightFillColor = 'none';
1094 
1095                         board.create('curve', 
1096                                     [ new Function('t', 'return ' + JXG.GeonextParser.geonext2JS(gxtEl.functionx, board) + ';' ),
1097                                       new Function('t', 'return ' + JXG.GeonextParser.geonext2JS(gxtEl.functiony, board) + ';' ),
1098                                       new Function('return ' + JXG.GeonextParser.geonext2JS(gxtEl.min, board) + ';' ),
1099                                       new Function('return ' + JXG.GeonextParser.geonext2JS(gxtEl.max, board) + ';' )
1100                                     ], gxtEl); 
1101                         /*
1102                         c = new JXG.Curve(board, [
1103                             't',gxtEl.functionx,gxtEl.functiony,gxtEl.min,gxtEl.max
1104                         ], gxtEl.id, gxtEl.name);
1105                         c.setProperty('strokeColor:' + gxtEl.colorStroke, 'strokeWidth:' + gxtEl.strokewidth, 'fillColor:none',
1106                                 'highlightStrokeColor:' + gxtEl.highlightStrokeColor);
1107                         */
1108                         gxtReader.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
1109                         break;
1110                     case 'tracecurve':
1111                         gxtEl.tracepoint = Data.getElementsByTagName('tracepoint')[0].firstChild.data;
1112                         gxtEl.traceslider = Data.getElementsByTagName('traceslider')[0].firstChild.data;
1113                         board.create('tracecurve', [gxtEl.traceslider, gxtEl.tracepoint], gxtEl);
1114                         // JXG.getRef(board, gxtEl.tracepoint).setProperty({trace:true});
1115                         gxtReader.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
1116                         break;
1117                     case 'group':
1118                         gxtEl = gxtReader.colorProperties(gxtEl, Data);
1119                         gxtEl = gxtReader.firstLevelProperties(gxtEl, Data);
1120                         gxtEl.members = [
1121                         ];
1122                         for (i = 0; i < Data.getElementsByTagName('data')[0].getElementsByTagName('member').length; i++) {
1123                             gxtEl.members[i] = Data.getElementsByTagName('data')[0].getElementsByTagName('member')[i].firstChild.data;
1124                             gxtEl.members[i] = gxtReader.changeOriginIds(board, gxtEl.members[i]);
1125                         }
1126                         c = new JXG.Group(board, gxtEl.id, gxtEl.name, gxtEl.members);
1127                         gxtReader.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
1128                         break;
1129                     default:
1130                         JXG.debug("* <b>Err:</b> " + Data.nodeName + " not yet implemented <br>\n");
1131                 }
1132                 delete(gxtEl);
1133             })(s);
1134         }
1135         board.addConditions(conditions);
1136     },
1137 
1138     decodeString: function(str) {
1139         var unz;
1140         if (str.indexOf("<GEONEXT>")<0){
1141             unz = (new JXG.Util.Unzip(JXG.Util.Base64.decodeAsArray(str))).unzip(); // war Gunzip ME
1142             if (unz=="")
1143                 return str;
1144             else
1145                 return unz;
1146         } else {
1147             return str;
1148         }
1149     },
1150 
1151     prepareString: function(fileStr){
1152         try {
1153             if (fileStr.indexOf('GEONEXT')<0) {
1154                 fileStr = (this.decodeString(fileStr))[0][0];  // Base64 decoding
1155             }
1156             // Hacks to enable not well formed XML. Will be redone in Algebra.geonext2JS and Board.addConditions
1157             fileStr = this.fixXML(fileStr);
1158         } catch(e) {
1159             fileStr = '';
1160         }
1161         return fileStr;
1162     },
1163 
1164     fixXML: function(str) {
1165         var arr = ["active", "angle", "animate", "animated", "arc", "area", "arrow", "author", "autodigits", "axis", "back", "background", "board", "border", "bottom", "buttonsize", "cas", "circle", "color", "comment", "composition", "condition", "conditions", "content", "continuous", "control", "coord", "coordinates", "cross", "cs", "dash", "data", "description", "digits", "direction", "draft", "editable", "elements", "event", "file", "fill", "first", "firstarrow", "fix", "fontsize", "free", "full", "function", "functionx", "functiony", "GEONEXT", "graph", "grid", "group", "height", "id", "image", "info", "information", "input", "intersection", "item", "jsf", "label", "last", "lastarrow", "left", "lefttoolbar", "lighting", "line", "loop", "max", "maximized", "member", "middle", "midpoint", "min", "modifier", "modus", "mp", "mpx", "multi", "name", "onpolygon", "order", "origin", "output", "overline", "parametercurve", "parent", "point", "pointsnap", "polygon", "position", "radius", "radiusnum", "radiusvalue", "right", "section", "selectedlefttoolbar", "showconstruction", "showcoord", "showinfo", "showunit", "showx", "showy", "size", "slider", "snap", "speed", "src", "start", "stop", "straight", "stroke", "strokewidth", "style", "term", "text", "top", "trace", "tracecurve", "tracepoint", "traceslider", "type", "unit", "value", "VERSION", "vertex", "viewport", "visible", "width", "wot", "x", "xooy", "xval", "y", "yval", "zoom"],
1166                 list = arr.join('|'),
1167                 regex = '\<(/?('+list+'))\>',
1168                 expr = new RegExp(regex,'g');
1169 
1170         // First, we convert all < to < and > to >
1171         str = JXG.escapeHTML(str);
1172         // Second, we convert all GEONExT tags of the form <tag> back to <tag>
1173         str = str.replace(expr,'<$1>');
1174 
1175         str = str.replace(/(<content>.*)<arc>(.*<\/content>)/g,'$1<arc>$2');
1176         str = str.replace(/(<mp>.*)<arc>(.*<\/mpx>)/g,'$1<arc>$2');
1177         str = str.replace(/(<mpx>.*)<arc>(.*<\/mpx>)/g,'$1<arc>$2');
1178         return str;
1179     }
1180 
1181 }; // end: GeonextReader
1182