Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/Sketchometry.activity/js/GeonextReader.js
diff options
context:
space:
mode:
Diffstat (limited to 'Sketchometry.activity/js/GeonextReader.js')
-rw-r--r--Sketchometry.activity/js/GeonextReader.js1285
1 files changed, 1285 insertions, 0 deletions
diff --git a/Sketchometry.activity/js/GeonextReader.js b/Sketchometry.activity/js/GeonextReader.js
new file mode 100644
index 0000000..a706845
--- /dev/null
+++ b/Sketchometry.activity/js/GeonextReader.js
@@ -0,0 +1,1285 @@
+/*
+ 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/>.
+ */
+
+/*global JXG: true, XMLSerializer: true, Image: true*/
+/*jslint nomen: true, plusplus: true*/
+
+/* depends:
+ jxg
+ base/constants
+ base/group
+ parser/geonext
+ utils/type
+ utils/color
+ utils/zip
+ utils/base64
+ utils/xml
+ elements:
+ image
+ transform
+ point
+ line
+ circle
+ glider
+ intersection
+ arrowparallel
+ bisector
+ circumcircle
+ circumcenter
+ midpoint
+ reflection
+ mirrorpoint
+ perpendicularsegment
+ parallelpoint
+ perpendicularpoint
+ sector
+ segment
+ polygon
+ curve
+ arrow
+ arc
+ angle
+ text
+ tracecurve
+ grid
+ axis
+ */
+
+(function () {
+
+ "use strict";
+
+ JXG.GeonextReader = function (board, str) {
+ var content;
+
+ this.board = board;
+ content = this.prepareString(str);
+ this.tree = JXG.XML.parse(content);
+ };
+
+ JXG.extend(JXG.GeonextReader.prototype, /** @lends JXG.GeonextReader.prototype */ {
+ changeOriginIds: function (board, id) {
+ if ((id === 'gOOe0') || (id === 'gXOe0') || (id === 'gYOe0') || (id === 'gXLe0') || (id === 'gYLe0')) {
+ return board.id + id;
+ }
+
+ return id;
+ },
+
+ /**
+ * Retrieves data by TagName from an XML node.
+ * @param {Object} node The Node that contains the data we want to get.
+ * @param {String} tag The Name of the tag we are looking for.
+ * @param {Number} [idx=0] getElementsByTagName returns an array; This parameter decides which element to use.
+ * @param {Boolean} [fc=true] If True, the result will be the <tt>data</tt> of <tt>firstChild</tt> instead of the result node.
+ * @returns {Object|Array|String} The gathered data
+ */
+ gEBTN: function (node, tag, idx, fc) {
+ var tmp;
+
+ if (!JXG.exists(node || !node.getElementsByTagName)) {
+ return node;
+ }
+
+ // Default values for optional parameters idx and fc
+ if (!JXG.exists(fc)) {
+ fc = true;
+ }
+
+ idx = idx || 0;
+ tmp = node.getElementsByTagName(tag);
+ if (tmp.length > 0) {
+ tmp = tmp[idx];
+ if (fc && tmp.firstChild) {
+ tmp = tmp.firstChild.data;
+ }
+ }
+
+ return tmp;
+ },
+
+ /**
+ * Set color properties of a geonext element.
+ * Set stroke, fill, lighting, label and draft color attributes.
+ * @param {Object} gxtEl element of which attributes are to set
+ * @param {Object} Data
+ * @returns {Object} Returns gxtEl
+ */
+ colorProperties: function (gxtEl, Data) {
+ var rgbo,
+ color = this.gEBTN(Data, 'color', 0, false);
+
+ //gxtEl.strokewidth = Data.getElementsByTagName('strokewidth')[0].firstChild.data;
+ // colorStroke = strokeColor etc. is here for downwards compatibility:
+ // once upon a time we used to create elements in here using the "new JXG.Element" constructor mechanism
+ // then we changed to board.create + setProperty afterwords
+ // now i want to use the board.create method with an appropriate attributes object to avoid setProperty calls
+ // and as gxtEl happens to be somewhat like an attributes object it's just slightly different so we adjust it
+ // for downwards compatibility during the transformation of this reader we use both properties
+
+ rgbo = JXG.rgba2rgbo(this.gEBTN(color, 'stroke'));
+ gxtEl.strokeColor = rgbo[0];
+ gxtEl.strokeOpacity = rgbo[1];
+
+ rgbo = JXG.rgba2rgbo(this.gEBTN(color, 'lighting'));
+ gxtEl.highlightStrokeColor = rgbo[0];
+ gxtEl.highlightStrokeOpacity = rgbo[1];
+
+ rgbo = JXG.rgba2rgbo(this.gEBTN(color, 'fill'));
+ gxtEl.fillColor = rgbo[0];
+ gxtEl.fillOpacity = rgbo[1];
+
+ gxtEl.highlightFillColor = gxtEl.fillColor;
+ gxtEl.highlightFillOpacity = gxtEl.fillOpacity;
+
+ rgbo = JXG.rgba2rgbo(this.gEBTN(color, 'label'));
+ gxtEl.labelColor = rgbo[0];
+ gxtEl.withLabel = rgbo[1] > 0;
+ gxtEl.labelOpacity = rgbo[1];
+ gxtEl.colorDraft = JXG.rgba2rgbo(this.gEBTN(color, 'draft'))[0];
+
+ // backwards compatibility
+ gxtEl.colorStroke = gxtEl.strokeColor;
+ gxtEl.colorFill = gxtEl.fillColor;
+ gxtEl.colorLabel = gxtEl.labelColor;
+
+ return gxtEl;
+ },
+
+ firstLevelProperties: function (gxtEl, Data) {
+ var n, key, arr;
+
+ if (!JXG.exists(Data) || !JXG.exists(Data.childNodes)) {
+ return gxtEl;
+ }
+
+ arr = Data.childNodes;
+
+ for (n = 0; n < arr.length; n++) {
+ if (JXG.exists(arr[n].firstChild) && arr[n].nodeName !== 'data' && arr[n].nodeName !== 'straight') {
+ key = arr[n].nodeName;
+
+ if (key === 'width') {
+ key = 'strokewidth';
+ }
+ gxtEl[key] = arr[n].firstChild.data;
+ }
+ }
+
+ return gxtEl;
+ },
+
+ /**
+ * Set the defining properties of a geonext element.
+ * Writing the nodeName to ident; setting the name attribute and defining the element id.
+ * @param {Object} gxtEl element of which attributes are to set
+ * @param {Object} Data
+ */
+ defProperties: function (gxtEl, Data) {
+ // 3==TEXT_NODE, 8==COMMENT_NODE
+ if (Data.nodeType === 3 || Data.nodeType === 8) {
+ return null;
+ }
+
+ gxtEl.ident = Data.nodeName;
+
+ if (gxtEl.ident === "text" || gxtEl.ident === "intersection" || gxtEl.ident === "composition") {
+ gxtEl.name = '';
+ } else {
+ gxtEl.name = this.gEBTN(Data, 'name');
+ }
+
+ gxtEl.id = this.gEBTN(Data, 'id');
+
+ return gxtEl;
+ },
+
+ visualProperties: function (gxtEl, Data) {
+ gxtEl.visible = JXG.str2Bool(this.gEBTN(Data, 'visible'));
+ gxtEl.trace = JXG.str2Bool(this.gEBTN(Data, 'trace'));
+
+ return gxtEl;
+ },
+
+ /**
+ * Transforms the Geonext properties to jsxgraph properties
+ * @param {Object} gxtEl
+ * @param {String} [type]
+ * @return {Object} gxtEl
+ */
+ transformProperties: function (gxtEl, type) {
+ var i,
+ facemap = [
+ // 0-2
+ 'cross', 'cross', 'cross',
+ // 3-6
+ 'circle', 'circle', 'circle', 'circle',
+ // 7-9
+ 'square', 'square', 'square',
+ // 10-12
+ 'plus', 'plus', 'plus'
+ ],
+ sizemap = [
+ // 0-2
+ 2, 3, 4,
+ // 3-6
+ 1, 2, 3, 4,
+ // 7-9
+ 2, 3, 4,
+ // 10-12
+ 2, 3, 4
+ ],
+ remove = [
+ 'color', 'dash', 'style', 'style', 'ident', 'colordraft', 'colorstroke', 'colorfill',
+ 'colorlabel', 'active', 'area', 'showinfo', 'showcoord', 'fix'
+ ];
+
+ gxtEl.strokeWidth = gxtEl.strokewidth;
+ gxtEl.face = facemap[parseInt(gxtEl.style, 10)] || 'cross';
+ gxtEl.size = sizemap[parseInt(gxtEl.style, 10)] || 3;
+
+ gxtEl.straightFirst = JXG.str2Bool(gxtEl.straightFirst);
+ gxtEl.straightLast = JXG.str2Bool(gxtEl.straightLast);
+
+ gxtEl.visible = JXG.str2Bool(gxtEl.visible);
+ //gxtEl.withLabel = gxtEl.visible; // withLabel is set in colorProperties()
+ gxtEl.draft = JXG.str2Bool(gxtEl.draft);
+ gxtEl.trace = JXG.str2Bool(gxtEl.trace);
+
+ if (type === 'point') {
+ // Fill properties are ignored by GEONExT
+ gxtEl.fillColor = gxtEl.strokeColor;
+ gxtEl.highlightFillColor = gxtEl.highlightStrokeColor;
+ gxtEl.fillOpacity = gxtEl.strokeOpacity;
+ gxtEl.highlightFillOpacity = gxtEl.highlightStrokeOpacity;
+ }
+
+ if (typeof gxtEl.label === 'string') {
+ delete gxtEl.label;
+ }
+ gxtEl.label = gxtEl.label || {
+ opacity: gxtEl.labelOpacity
+ };
+
+ // clean up
+ for (i = 0; i < remove.length; i++) {
+ delete gxtEl[remove[i]];
+ }
+
+ return gxtEl;
+ },
+
+ readNodes: function (gxtEl, Data, nodeType, prefix) {
+ var key, n,
+ arr = this.gEBTN(Data, nodeType, 0, false).childNodes;
+
+ for (n = 0; n < arr.length; n++) {
+ if (JXG.exists(arr[n].firstChild)) {
+ if (JXG.exists(prefix)) {
+ key = prefix + JXG.capitalize(arr[n].nodeName);
+ } else {
+ key = arr[n].nodeName;
+ }
+ gxtEl[key] = arr[n].firstChild.data;
+ }
+ }
+ return gxtEl;
+ },
+
+ subtreeToString: function (root) {
+ try {
+ // firefox
+ return (new XMLSerializer()).serializeToString(root);
+ } catch (e) {
+ // IE
+ return root.xml;
+ }
+ },
+
+ readImage: function (node) {
+ var pic = '',
+ nod = node;
+
+ if (JXG.exists(nod)) {
+ pic = nod.data;
+ while (JXG.exists(nod.nextSibling)) {
+ nod = nod.nextSibling;
+ pic += nod.data;
+ }
+ }
+ return pic;
+ },
+
+ parseImage: function (board, fileNode, level, x, y, w, h, el) {
+ var tag, id, picStr, tmpImg,
+ im = null;
+
+ if (fileNode === null) {
+ return im;
+ }
+
+ // Background image
+ if (JXG.exists(fileNode.getElementsByTagName('src')[0])) {
+ tag = 'src';
+ } else if (JXG.exists(fileNode.getElementsByTagName('image')[0]) &&
+ JXG.exists(this.gEBTN(fileNode, 'image'))) {
+ tag = 'image';
+ } else {
+ return im;
+ }
+
+ picStr = this.readImage(this.gEBTN(fileNode, tag, 0, false).firstChild);
+ if (picStr !== '') {
+ picStr = 'data:image/png;base64,' + picStr;
+
+ // Background image
+ if (tag === 'src') {
+ x = this.gEBTN(fileNode, 'x');
+ y = this.gEBTN(fileNode, 'y');
+ w = this.gEBTN(fileNode, 'width');
+ h = this.gEBTN(fileNode, 'height');
+ im = board.create('image', [picStr, [x, y], [w, h]], {
+ anchor: el,
+ layer: level
+ });
+
+ return im;
+ }
+ // Image bound to an element
+ // Read the original dimensions, i.e. the ratio h/w with the help of a temporary image.
+ // We have to wait until the image is loaded, therefore
+ // we need "onload".
+ tmpImg = new Image();
+ tmpImg.src = picStr;
+ id = el.id + '_image';
+ tmpImg.onload = function () {
+ // Now, we can read the original dimensions of the image.
+ var xf, yf, wf, hf, im, tRot,
+ wOrg = this.width,
+ hOrg = this.height;
+
+ if (el.elementClass === JXG.OBJECT_CLASS_LINE) {
+ // A line containing an image, runs through the horizontal middle
+ // of the image.
+ xf = function () {
+ return el.point1.X();
+ };
+ wf = function () {
+ return el.point1.Dist(el.point2);
+ };
+ hf = function () {
+ return wf() * hOrg / wOrg;
+ };
+ yf = function () {
+ return el.point1.Y() - hf() * 0.5;
+ };
+
+ im = board.create('image', [picStr, [xf, yf], [wf, hf]], {
+ layer: level,
+ id: id,
+ anchor: el
+ });
+
+ tRot = board.create('transform', [
+ function () {
+ return Math.atan2(el.point2.Y() - el.point1.Y(), el.point2.X() - el.point1.X());
+ },
+ el.point1
+ ], {type: 'rotate'});
+
+ tRot.bindTo(im);
+ el.image = im;
+ } else if (el.elementClass === JXG.OBJECT_CLASS_POINT) {
+ wf = function () {
+ return wOrg / board.unitX;
+ };
+ hf = function () {
+ return hOrg / board.unitY;
+ };
+ xf = function () {
+ return el.X() - wf() * 0.5;
+ };
+ yf = function () {
+ return el.Y() - hf() * 0.5;
+ };
+
+ im = board.create('image', [picStr, [xf, yf], [wf, hf]], {
+ layer: level,
+ id: id,
+ anchor: el
+ });
+ board.renderer.hide(el.label);
+ el.image = im;
+ } else if (el.elementClass === JXG.OBJECT_CLASS_CIRCLE) {
+ // A circle containing an image
+ wf = function () {
+ return 2.0 * el.Radius();
+ };
+ hf = function () {
+ return wf() * hOrg / wOrg;
+ };
+ xf = function () {
+ return el.center.X() - wf() * 0.5;
+ };
+ yf = function () {
+ return el.center.Y() - hf() * 0.5;
+ };
+
+ im = board.create('image', [picStr, [xf, yf], [wf, hf]], {
+ layer: level,
+ id: id,
+ anchor: el
+ });
+ el.image = im;
+ } else {
+ im = board.create('image', [picStr, [x, y], [w, h]], {
+ layer: level,
+ id: id,
+ anchor: el
+ });
+ el.image = im;
+ }
+ };
+
+ return im;
+ }
+ },
+
+ readConditions: function (node) {
+ var i, s, ob,
+ conditions = '';
+
+ if (JXG.exists(node)) {
+ for (i = 0; i < node.getElementsByTagName('data').length; i++) {
+ ob = node.getElementsByTagName('data')[i];
+ s = this.subtreeToString(ob);
+ conditions += s;
+ }
+ }
+
+ return conditions;
+ },
+
+ readViewPort: function (node) {
+ var no,
+ arr = [];
+
+ no = this.gEBTN(node, 'viewport', 0, false);
+
+ if (no) {
+ arr[0] = parseFloat(this.gEBTN(no, 'left'));
+ arr[1] = parseFloat(this.gEBTN(no, 'top'));
+ arr[2] = parseFloat(this.gEBTN(no, 'right'));
+ arr[3] = parseFloat(this.gEBTN(no, 'bottom'));
+ return arr;
+ }
+
+ return [];
+ },
+
+ printDebugMessage: function (outputEl, gxtEl, nodetyp, success) {
+ JXG.debug("* " + success + ": " + nodetyp + " " + gxtEl.name + " " + gxtEl.id);
+ },
+
+ readNode: function (elChildNodes, s, board) {
+ var dataVertex, i, l, x, c, numberDefEls, el, p, inter, rgbo, tmp, v, Data, xmlNode,
+ gxtEl = {};
+
+ Data = elChildNodes[s];
+ gxtEl = this.defProperties(gxtEl, Data);
+
+ // Skip text nodes
+ if (!JXG.exists(gxtEl)) {
+ return;
+ }
+
+ this.printDebugMessage('debug', gxtEl, Data.nodeName.toLowerCase, 'READ:');
+ switch (Data.nodeName.toLowerCase()) {
+ case "point":
+ gxtEl.strokewidth = 1; // Old file format
+ gxtEl = this.colorProperties(gxtEl, Data);
+ gxtEl = this.visualProperties(gxtEl, Data);
+ gxtEl = this.firstLevelProperties(gxtEl, Data);
+ gxtEl = this.readNodes(gxtEl, Data, 'data');
+ gxtEl.fixed = JXG.str2Bool(this.gEBTN(Data, 'fix'));
+ gxtEl = this.transformProperties(gxtEl, 'point');
+
+ //try {
+ p = board.create('point', [parseFloat(gxtEl.x), parseFloat(gxtEl.y)], gxtEl);
+
+ v = function () {
+ return p.visProp.visible;
+ };
+ el = this.parseImage(board, Data, board.options.layer.image, 0, 0, 0, 0, p);
+ this.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
+ break;
+ case "line":
+ gxtEl = this.colorProperties(gxtEl, Data);
+ gxtEl = this.visualProperties(gxtEl, Data);
+ gxtEl = this.firstLevelProperties(gxtEl, Data);
+ gxtEl = this.readNodes(gxtEl, Data, 'data');
+ gxtEl = this.readNodes(gxtEl, Data, 'straight', 'straight');
+ gxtEl = this.transformProperties(gxtEl);
+
+ gxtEl.first = this.changeOriginIds(board, gxtEl.first);
+ gxtEl.last = this.changeOriginIds(board, gxtEl.last);
+
+ l = board.create('line', [gxtEl.first, gxtEl.last], gxtEl);
+
+ this.parseImage(board, Data, board.options.layer.image, 0, 0, 0, 0, l);
+ this.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
+ break;
+ case "circle":
+ gxtEl = this.colorProperties(gxtEl, Data);
+ gxtEl = this.visualProperties(gxtEl, Data);
+ gxtEl = this.firstLevelProperties(gxtEl, Data);
+
+ tmp = this.gEBTN(Data, 'data', 0, false);
+ gxtEl.center = this.changeOriginIds(board, this.gEBTN(tmp, 'midpoint'));
+
+ if (tmp.getElementsByTagName('radius').length > 0) {
+ gxtEl.radius = this.changeOriginIds(board, this.gEBTN(tmp, 'radius'));
+ } else if (tmp.getElementsByTagName('radiusvalue').length > 0) {
+ gxtEl.radius = this.gEBTN(tmp, 'radiusvalue');
+ }
+ gxtEl = this.transformProperties(gxtEl);
+ c = board.create('circle', [gxtEl.center, gxtEl.radius], gxtEl);
+
+ this.parseImage(board, Data, board.options.layer.image, 0, 0, 0, 0, c);
+ this.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
+ break;
+ case "slider":
+ gxtEl.strokewidth = 1; // Old file format
+ gxtEl = this.colorProperties(gxtEl, Data);
+ gxtEl = this.visualProperties(gxtEl, Data);
+ gxtEl = this.firstLevelProperties(gxtEl, Data);
+
+ gxtEl = this.readNodes(gxtEl, Data, 'data');
+ gxtEl.fixed = JXG.str2Bool(this.gEBTN(Data, 'fix'));
+ gxtEl = this.readNodes(gxtEl, Data, 'animate', 'animate');
+ gxtEl = this.transformProperties(gxtEl, 'point');
+ try {
+ gxtEl.parent = this.changeOriginIds(board, gxtEl.parent);
+
+ p = board.create('glider', [parseFloat(gxtEl.x), parseFloat(gxtEl.y), gxtEl.parent], gxtEl);
+ p.onPolygon = JXG.exists(gxtEl.onpolygon) && JXG.str2Bool(gxtEl.onpolygon);
+
+ this.parseImage(board, Data, board.options.layer.point, 0, 0, 0, 0, p);
+
+ this.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
+ } catch (e) {
+ JXG.debug("* Err: Slider " + gxtEl.name + " " + gxtEl.id + ': ' + gxtEl.parent);
+ }
+ break;
+ case "cas":
+ gxtEl = this.colorProperties(gxtEl, Data);
+ gxtEl = this.visualProperties(gxtEl, Data);
+ gxtEl = this.firstLevelProperties(gxtEl, Data);
+ gxtEl.fixed = JXG.str2Bool(Data.getElementsByTagName('fix')[0].firstChild.data);
+ gxtEl = this.readNodes(gxtEl, Data, 'data');
+ gxtEl = this.transformProperties(gxtEl, 'point');
+
+ p = board.create('point', [gxtEl.x, gxtEl.y], gxtEl);
+ this.parseImage(board, Data, board.options.layer.point, 0, 0, 0, 0, p);
+ this.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
+ break;
+ case "intersection":
+ gxtEl.strokewidth = 1; // Old file format
+ gxtEl = this.readNodes(gxtEl, Data, 'data');
+ xmlNode = Data.getElementsByTagName('first')[1];
+
+ gxtEl.outFirst = {};
+ gxtEl.outFirst = this.colorProperties(gxtEl.outFirst, xmlNode);
+ gxtEl.outFirst = this.visualProperties(gxtEl.outFirst, xmlNode);
+ gxtEl.outFirst = this.firstLevelProperties(gxtEl.outFirst, xmlNode);
+ gxtEl.outFirst.fixed = JXG.str2Bool(xmlNode.getElementsByTagName('fix')[0].firstChild.data);
+ gxtEl.outFirst = this.transformProperties(gxtEl.outFirst, 'point');
+ gxtEl.first = this.changeOriginIds(board, gxtEl.first);
+ gxtEl.last = this.changeOriginIds(board, gxtEl.last);
+
+ if ((board.select(gxtEl.first).elementClass === JXG.OBJECT_CLASS_LINE) &&
+ (board.select(gxtEl.last).elementClass === JXG.OBJECT_CLASS_LINE)) {
+ inter = board.create('intersection', [board.objects[gxtEl.first], board.objects[gxtEl.last], 0], gxtEl.outFirst);
+ /* for some reason this if is required */
+ if (gxtEl.outFirst.visible === "false") {
+ inter.hideElement();
+ }
+ } else {
+ xmlNode = Data.getElementsByTagName('last')[1];
+ if (JXG.exists(xmlNode)) {
+ gxtEl.outLast = {};
+ gxtEl.outLast = this.colorProperties(gxtEl.outLast, xmlNode);
+ gxtEl.outLast = this.visualProperties(gxtEl.outLast, xmlNode);
+ gxtEl.outLast = this.firstLevelProperties(gxtEl.outLast, xmlNode);
+ gxtEl.outLast.fixed = JXG.str2Bool(xmlNode.getElementsByTagName('fix')[0].firstChild.data);
+ gxtEl.outLast = this.transformProperties(gxtEl.outLast, 'point');
+
+ inter = board.create('intersection', [board.objects[gxtEl.first], board.objects[gxtEl.last], 0], gxtEl.outFirst);
+ inter = board.create('intersection', [board.objects[gxtEl.first], board.objects[gxtEl.last], 1], gxtEl.outLast);
+ }
+ }
+ this.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
+ break;
+ case "composition":
+ gxtEl = this.readNodes(gxtEl, Data, 'data');
+ gxtEl = this.firstLevelProperties(gxtEl, Data);
+ gxtEl.defEl = [];
+ numberDefEls = 0;
+ xmlNode = Data.getElementsByTagName('data')[0].getElementsByTagName('input');
+ for (i = 0; i < xmlNode.length; i++) {
+ gxtEl.defEl[i] = xmlNode[i].firstChild.data;
+ numberDefEls = i + 1;
+ }
+
+ // every composition produces at least one element and the data for this element is stored
+ // in gxtEl.out. if additional elements are created their data is read in the according case.
+ xmlNode = Data.getElementsByTagName('output')[0];
+ gxtEl.out = {};
+ gxtEl.out = this.colorProperties(gxtEl.out, xmlNode);
+ gxtEl.out = this.visualProperties(gxtEl.out, xmlNode);
+ gxtEl.out = this.firstLevelProperties(gxtEl.out, xmlNode);
+ gxtEl.out = this.transformProperties(gxtEl.out);
+
+ gxtEl.defEl[0] = this.changeOriginIds(board, gxtEl.defEl[0]);
+ gxtEl.defEl[1] = this.changeOriginIds(board, gxtEl.defEl[1]);
+ gxtEl.defEl[2] = this.changeOriginIds(board, gxtEl.defEl[2]);
+
+ switch (gxtEl.type) {
+ // ARROW_PARALLEL
+ case "210070":
+ gxtEl.out.fixed = this.gEBTN(xmlNode, 'fix');
+
+ xmlNode = Data.getElementsByTagName('output')[1];
+ gxtEl.outPoint = {};
+ gxtEl.outPoint = this.defProperties(gxtEl.outPoint, xmlNode);
+ gxtEl.outPoint = this.colorProperties(gxtEl.outPoint, xmlNode);
+ gxtEl.outPoint = this.visualProperties(gxtEl.outPoint, xmlNode);
+ gxtEl.outPoint = this.firstLevelProperties(gxtEl.outPoint, xmlNode);
+ gxtEl.outPoint = this.transformProperties(gxtEl.outPoint);
+ gxtEl.out.point = gxtEl.outPoint;
+
+ board.create('arrowparallel', [gxtEl.defEl[1], gxtEl.defEl[0]], gxtEl.out);
+ break;
+
+ // BISECTOR
+ case "210080":
+ gxtEl.out.straightFirst = false;
+ board.create('bisector', [gxtEl.defEl[0], gxtEl.defEl[1], gxtEl.defEl[2]], gxtEl.out);
+ break;
+
+ // CIRCUMCIRCLE
+ case "210090":
+ xmlNode = Data.getElementsByTagName('output')[1];
+ gxtEl.outCircle = {};
+ gxtEl.outCircle = this.defProperties(gxtEl.outCircle, xmlNode);
+ gxtEl.outCircle = this.colorProperties(gxtEl.outCircle, xmlNode);
+ gxtEl.outCircle = this.visualProperties(gxtEl.outCircle, xmlNode);
+ gxtEl.outCircle = this.firstLevelProperties(gxtEl.outCircle, xmlNode);
+ gxtEl.outCircle = this.transformProperties(gxtEl.outCircle);
+ gxtEl.outCircle.point = gxtEl.out;
+ board.create('circumcircle', [gxtEl.defEl[0], gxtEl.defEl[1], gxtEl.defEl[2]], gxtEl.outCircle);
+ break;
+
+ // CIRCUMCIRCLE_CENTER
+ case "210100":
+ board.create('circumcenter', [gxtEl.defEl[0], gxtEl.defEl[1], gxtEl.defEl[2]], gxtEl.out);
+ break;
+
+ // MIDPOINT
+ case "210110":
+ board.create('midpoint', gxtEl.defEl.slice(0, numberDefEls), gxtEl.out);
+ break;
+
+ // MIRRORLINE
+ case "210120":
+ board.create('reflection', [gxtEl.defEl[1], gxtEl.defEl[0]], gxtEl.out);
+ break;
+
+ // MIRROR_POINT
+ case "210125":
+ board.create('mirrorpoint', [gxtEl.defEl[0], gxtEl.defEl[1]], gxtEl.out);
+ break;
+
+ // NORMAL
+ case "210130":
+ //board.create('normal', [gxtEl.defEl[1], gxtEl.defEl[0]], gxtEl.out);
+ board.create('perpendicularsegment', [gxtEl.defEl[0], gxtEl.defEl[1]], gxtEl.out);
+ break;
+
+ // PARALLEL
+ case "210140":
+ p = board.create('parallelpoint', [gxtEl.defEl[1], gxtEl.defEl[0]], {
+ withLabel: false,
+ visible: false,
+ name: '',
+ fixed: true
+ });
+
+ // GEONExT uses its own parallel construction to make the order
+ // of intersection points compatible.
+ // el = board.create('parallel', [gxtEl.defEl[1], gxtEl.defEl[0]], gxtEl.out);
+ el = board.create('line', [gxtEl.defEl[0], p], gxtEl.out);
+ el.parallelpoint = p;
+ break;
+
+ // PARALLELOGRAM_POINT
+ case "210150":
+ board.create('parallelpoint', gxtEl.defEl.slice(0, numberDefEls), gxtEl.out);
+ break;
+
+ // PERPENDICULAR
+ case "210160":
+ // output[0] was already read and is stored in gxtEl.out
+ gxtEl.out.fixed = this.gEBTN(xmlNode, 'fix');
+
+ xmlNode = Data.getElementsByTagName('output')[1];
+ gxtEl.outLine = {};
+ gxtEl.outLine = this.defProperties(gxtEl.outLine, xmlNode);
+ gxtEl.outLine = this.colorProperties(gxtEl.outLine, xmlNode);
+ gxtEl.outLine = this.visualProperties(gxtEl.outLine, xmlNode);
+ gxtEl.outLine = this.firstLevelProperties(gxtEl.outLine, xmlNode);
+ gxtEl.outLine = this.readNodes(gxtEl.outLine, xmlNode, 'straight', 'straight');
+ gxtEl.outLine = this.transformProperties(gxtEl.outLine);
+ gxtEl.outLine.point = gxtEl.out;
+
+ board.create('perpendicularsegment', [gxtEl.defEl[1], gxtEl.defEl[0]], gxtEl.outLine);
+ break;
+
+ // PERPENDICULAR_POINT
+ case "210170":
+ board.create('perpendicularpoint', [gxtEl.defEl[1], gxtEl.defEl[0]], gxtEl.out);
+ break;
+
+ // ROTATION
+ case "210180":
+ throw new Error('JSXGraph: Element ROTATION not yet implemented.');
+
+ // SECTOR
+ case "210190":
+ // sectors usually provide more than one output element but JSXGraph is not fully compatible
+ // to GEONExT sector elements. GEONExT sectors consist of two lines, a point, and a sector,
+ // JSXGraph uses a curve to display the sector incl. the borders, and
+ // a point and two lines.
+ // Gliders on sectors also run through the borders.
+ gxtEl.out = this.defProperties(gxtEl.out, xmlNode);
+ gxtEl.out.firstArrow = JXG.str2Bool(this.gEBTN(xmlNode, 'firstarrow'));
+ gxtEl.out.lastArrow = JXG.str2Bool(this.gEBTN(xmlNode, 'lastarrow'));
+
+ xmlNode = [];
+ c = [];
+
+ for (i = 0; i < 4; i++) {
+ xmlNode[i] = Data.getElementsByTagName('output')[i];
+ gxtEl.out = {};
+ gxtEl.out = this.defProperties(gxtEl.out, xmlNode[i]);
+ gxtEl.out = this.colorProperties(gxtEl.out, xmlNode[i]);
+ gxtEl.out = this.visualProperties(gxtEl.out, xmlNode[i]);
+ gxtEl.out = this.firstLevelProperties(gxtEl.out, xmlNode[i]);
+ gxtEl.out = this.transformProperties(gxtEl.out);
+ c[i] = gxtEl.out;
+ }
+
+ // i === 0
+ el = board.create('sector', gxtEl.defEl, c[0]);
+
+ // i === 1
+ p = board.create('point', [
+ function () {
+ var p1 = board.select(gxtEl.defEl[1]),
+ p2 = board.select(gxtEl.defEl[2]);
+
+ return p1.X() + (p2.X() - p1.X()) * el.Radius / p1.Dist(p2);
+ },
+ function () {
+ var p1 = board.select(gxtEl.defEl[1]),
+ p2 = board.select(gxtEl.defEl[2]);
+
+ return p1.Y() + (p2.Y() - p1.Y()) * el.Radius / p1.Dist(p2);
+ }
+ ], c[1]);
+
+ // i === 2
+ el = board.create('segment', [gxtEl.defEl[0], gxtEl.defEl[1]], c[2]);
+
+ // i === 3
+ el = board.create('segment', [gxtEl.defEl[1], p], c[3]);
+ break;
+ default:
+ throw new Error('JSXGraph: GEONExT -- element ' + gxtEl.type + ' not implemented.');
+ }
+ this.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
+ break;
+ case "polygon":
+ gxtEl = this.colorProperties(gxtEl, Data);
+ gxtEl = this.firstLevelProperties(gxtEl, Data);
+ dataVertex = [];
+
+ // In Geonext file format the first vertex is equal to the last vertex:
+ for (i = 0; i < Data.getElementsByTagName('data')[0].getElementsByTagName('vertex').length - 1; i++) {
+ dataVertex[i] = Data.getElementsByTagName('data')[0].getElementsByTagName('vertex')[i].firstChild.data;
+ dataVertex[i] = this.changeOriginIds(board, dataVertex[i]);
+ }
+ gxtEl.border = [];
+ gxtEl.borders = {
+ ids: []
+ };
+ for (i = 0; i < Data.getElementsByTagName('border').length; i++) {
+ gxtEl.border[i] = {};
+ xmlNode = Data.getElementsByTagName('border')[i];
+ gxtEl.border[i].id = xmlNode.getElementsByTagName('id')[0].firstChild.data;
+ gxtEl.borders.ids.push(gxtEl.border[i].id);
+ gxtEl.border[i].name = xmlNode.getElementsByTagName('name')[0].firstChild.data;
+ gxtEl.border[i].straightFirst = JXG.str2Bool(xmlNode.getElementsByTagName('straight')[0].getElementsByTagName('first')[0].firstChild.data);
+ gxtEl.border[i].straightLast = JXG.str2Bool(xmlNode.getElementsByTagName('straight')[0].getElementsByTagName('last')[0].firstChild.data);
+ try {
+ gxtEl.border[i].strokeWidth = xmlNode.getElementsByTagName('strokewidth')[0].firstChild.data;
+ } catch (ex) {
+ gxtEl.border[i].strokeWidth = xmlNode.getElementsByTagName('width')[0].firstChild.data;
+ }
+ try {
+ gxtEl.border[i].dash = JXG.str2Bool(xmlNode.getElementsByTagName('dash')[0].firstChild.data);
+ } catch (exc) {}
+
+ gxtEl.border[i].visible = JXG.str2Bool(xmlNode.getElementsByTagName('visible')[0].firstChild.data);
+ gxtEl.border[i].draft = JXG.str2Bool(xmlNode.getElementsByTagName('draft')[0].firstChild.data);
+ gxtEl.border[i].trace = JXG.str2Bool(xmlNode.getElementsByTagName('trace')[0].firstChild.data);
+
+ xmlNode = Data.getElementsByTagName('border')[i].getElementsByTagName('color')[0];
+ rgbo = JXG.rgba2rgbo(xmlNode.getElementsByTagName('stroke')[0].firstChild.data);
+ gxtEl.border[i].strokeColor = rgbo[0];
+ gxtEl.border[i].strokeOpacity = rgbo[1];
+
+ rgbo = JXG.rgba2rgbo(xmlNode.getElementsByTagName('lighting')[0].firstChild.data);
+ gxtEl.border[i].highlightStrokeColor = rgbo[0];
+ gxtEl.border[i].highlightStrokeOpacity = rgbo[1];
+
+ rgbo = JXG.rgba2rgbo(xmlNode.getElementsByTagName('fill')[0].firstChild.data);
+ gxtEl.border[i].fillColor = rgbo[0];
+ gxtEl.border[i].fillOpacity = rgbo[1];
+
+ gxtEl.border[i].highlightFillColor = gxtEl.border[i].fillColor;
+ gxtEl.border[i].highlightFillOpacity = gxtEl.border[i].fillOpacity;
+
+ gxtEl.border[i].labelColor = xmlNode.getElementsByTagName('label')[0].firstChild.data;
+ gxtEl.border[i].colorDraft = xmlNode.getElementsByTagName('draft')[0].firstChild.data;
+ }
+ gxtEl = this.transformProperties(gxtEl);
+ p = board.create('polygon', dataVertex, gxtEl);
+
+ // to emulate the geonext behaviour on invisible polygons
+ // A.W.: Why do we need this?
+ /*
+ if (!gxtEl.visible) {
+ p.setProperty({
+ fillColor: 'none',
+ highlightFillColor: 'none'
+ });
+ }
+ */
+ for (i = 0; i < p.borders.length; i++) {
+ p.borders[i].setProperty(gxtEl.border[i]);
+ }
+
+ this.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
+ break;
+ case "graph":
+ gxtEl = this.colorProperties(gxtEl, Data);
+ gxtEl = this.firstLevelProperties(gxtEl, Data);
+ gxtEl.funct = Data.getElementsByTagName('data')[0].getElementsByTagName('function')[0].firstChild.data;
+ gxtEl.funct = board.jc.snippet(gxtEl.funct, true, 'x', true);
+
+ c = board.create('plot', [gxtEl.funct], {
+ id: gxtEl.id,
+ name: gxtEl.name,
+ strokeColor: gxtEl.strokeColor,
+ strokeWidth: gxtEl.strokeWidth,
+ fillColor: 'none',
+ highlightFillColor: 'none',
+ highlightStrokeColor: gxtEl.highlightStrokeColor,
+ visible: JXG.str2Bool(gxtEl.visible)
+ });
+
+ this.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
+ break;
+ case "arrow":
+ gxtEl = this.colorProperties(gxtEl, Data);
+ gxtEl = this.visualProperties(gxtEl, Data);
+ gxtEl = this.firstLevelProperties(gxtEl, Data);
+ gxtEl = this.readNodes(gxtEl, Data, 'data');
+ gxtEl = this.readNodes(gxtEl, Data, 'straight', 'straight');
+
+ gxtEl = this.transformProperties(gxtEl);
+ gxtEl.first = this.changeOriginIds(board, gxtEl.first);
+ gxtEl.last = this.changeOriginIds(board, gxtEl.last);
+
+ l = board.create('arrow', [gxtEl.first, gxtEl.last], gxtEl);
+
+ this.printDebugMessage('debug', l, Data.nodeName, 'OK');
+ break;
+ case "arc":
+ gxtEl = this.colorProperties(gxtEl, Data);
+ gxtEl = this.visualProperties(gxtEl, Data);
+ gxtEl = this.firstLevelProperties(gxtEl, Data);
+ gxtEl = this.readNodes(gxtEl, Data, 'data');
+
+ // It seems that JSXGraph and GEONExT use opposite directions.
+ gxtEl.firstArrow = JXG.str2Bool(Data.getElementsByTagName('lastarrow')[0].firstChild.data);
+ gxtEl.lastArrow = JXG.str2Bool(Data.getElementsByTagName('firstarrow')[0].firstChild.data);
+
+ gxtEl = this.transformProperties(gxtEl);
+
+ gxtEl.center = this.changeOriginIds(board, gxtEl.midpoint);
+ gxtEl.angle = this.changeOriginIds(board, gxtEl.angle);
+ gxtEl.radius = this.changeOriginIds(board, gxtEl.radius);
+
+ c = board.create('arc', [gxtEl.center, gxtEl.radius, gxtEl.angle], gxtEl);
+
+ this.printDebugMessage('debug', c, Data.nodeName, 'OK');
+ break;
+ case "angle":
+ gxtEl = this.colorProperties(gxtEl, Data);
+ gxtEl = this.visualProperties(gxtEl, Data);
+ gxtEl = this.firstLevelProperties(gxtEl, Data);
+ gxtEl = this.readNodes(gxtEl, Data, 'data');
+ gxtEl = this.transformProperties(gxtEl);
+
+ c = board.create('angle', [gxtEl.first, gxtEl.middle, gxtEl.last], gxtEl);
+ this.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
+ break;
+ case "text":
+ if (gxtEl.id.match(/oldVersion/)) {
+ break;
+ }
+ gxtEl = this.colorProperties(gxtEl, Data);
+ gxtEl = this.visualProperties(gxtEl, Data);
+ gxtEl = this.firstLevelProperties(gxtEl, Data);
+
+ gxtEl = this.readNodes(gxtEl, Data, 'data');
+ try {
+ gxtEl.mpStr = this.subtreeToString(Data.getElementsByTagName('data')[0].getElementsByTagName('mp')[0]);
+ gxtEl.mpStr = gxtEl.mpStr.replace(/<\/?mp>/g, '');
+ } catch (ex1) {
+ gxtEl.mpStr = this.subtreeToString(Data.getElementsByTagName('data')[0].getElementsByTagName('content')[0]);
+ gxtEl.mpStr = gxtEl.mpStr.replace(/<\/?content>/g, '');
+ }
+ gxtEl.fixed = false;
+ try {
+ if (Data.getElementsByTagName('data')[0].getElementsByTagName('parent')[0].firstChild) {
+ gxtEl.parent = Data.getElementsByTagName('data')[0].getElementsByTagName('parent')[0].firstChild.data;
+ gxtEl.fixed = true;
+ }
+ } catch (ex2) {}
+
+ try {
+ gxtEl.condition = Data.getElementsByTagName('condition')[0].firstChild.data;
+ } catch (ex3) {
+ gxtEl.condition = "";
+ }
+ gxtEl.content = Data.getElementsByTagName('content')[0].firstChild.data;
+ try {
+ gxtEl.fixed = Data.getElementsByTagName('fix')[0].firstChild.data;
+ } catch (ex4) {
+ gxtEl.fixed = false;
+ }
+ // not used: gxtEl.digits = Data.getElementsByTagName('cs')[0].firstChild.data;
+ try {
+ gxtEl.autodigits = Data.getElementsByTagName('digits')[0].firstChild.data;
+ } catch (ex5) {
+ gxtEl.autodigits = 2;
+ }
+ gxtEl.parent = this.changeOriginIds(board, gxtEl.parent);
+
+ c = board.create('text', [gxtEl.x, gxtEl.y, gxtEl.mpStr], {
+ anchor: gxtEl.parent,
+ id: gxtEl.id,
+ name: gxtEl.name,
+ digits: gxtEl.autodigits,
+ isLabel: false,
+ strokeColor: gxtEl.colorLabel,
+ fixed: gxtEl.fixed,
+ visible: JXG.str2Bool(gxtEl.visible)
+ });
+ break;
+ case 'parametercurve':
+ gxtEl = this.colorProperties(gxtEl, Data);
+ gxtEl = this.visualProperties(gxtEl, Data);
+ gxtEl = this.firstLevelProperties(gxtEl, Data);
+ gxtEl = this.transformProperties(gxtEl);
+ gxtEl.functionx = Data.getElementsByTagName('functionx')[0].firstChild.data;
+ gxtEl.functiony = Data.getElementsByTagName('functiony')[0].firstChild.data;
+ gxtEl.min = Data.getElementsByTagName('min')[0].firstChild.data;
+ gxtEl.max = Data.getElementsByTagName('max')[0].firstChild.data;
+ gxtEl.fillColor = 'none';
+ gxtEl.highlightFillColor = 'none';
+
+ // intentional
+ /*jslint evil:true*/
+ board.create('curve', [
+ board.jc.snippet(gxtEl.functionx, true, 't', true),
+ board.jc.snippet(gxtEl.functiony, true, 't', true),
+ board.jc.snippet(gxtEl.min, true, '', true),
+ board.jc.snippet(gxtEl.max, true, '', true)
+ ], gxtEl);
+ /*jslint evil:false*/
+ this.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
+ break;
+ case 'tracecurve':
+ gxtEl.tracepoint = Data.getElementsByTagName('tracepoint')[0].firstChild.data;
+ gxtEl.traceslider = Data.getElementsByTagName('traceslider')[0].firstChild.data;
+ board.create('tracecurve', [gxtEl.traceslider, gxtEl.tracepoint], gxtEl);
+ this.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
+ break;
+ case 'group':
+ gxtEl = this.colorProperties(gxtEl, Data);
+ gxtEl = this.firstLevelProperties(gxtEl, Data);
+ gxtEl.members = [];
+
+ for (i = 0; i < Data.getElementsByTagName('data')[0].getElementsByTagName('member').length; i++) {
+ gxtEl.members[i] = Data.getElementsByTagName('data')[0].getElementsByTagName('member')[i].firstChild.data;
+ gxtEl.members[i] = this.changeOriginIds(board, gxtEl.members[i]);
+ }
+
+ c = new JXG.Group(board, gxtEl.id, gxtEl.name, gxtEl.members);
+ this.printDebugMessage('debug', gxtEl, Data.nodeName, 'OK');
+ break;
+ default:
+ JXG.debug("* Err: " + Data.nodeName + " not yet implemented");
+ }
+ },
+
+ /**
+ * Reading the elements of a geonext file
+ */
+ read: function () {
+ var xmlNode, elChildNodes, s, boardData, conditions, tmp,
+ tree = this.tree,
+ board = this.board,
+ strTrue = 'true';
+
+ // maybe this is not necessary as we already provide layer options for sectors and circles via JXG.Options but
+ // maybe these have to be the same for geonext.
+ board.options.layer.sector = board.options.layer.angle;
+ board.options.layer.circle = board.options.layer.angle;
+
+ board.options.line.label.position = 'top';
+
+ boardData = this.gEBTN(tree, 'board', 0, false);
+ conditions = this.readConditions(boardData.getElementsByTagName('conditions')[0]);
+
+ // set the board background color
+ s = this.gEBTN(boardData, 'background', 0, false);
+ s = this.gEBTN(s, 'color', 0, true);
+ tmp = JXG.rgba2rgbo(s);
+ s = JXG.rgbParser(tmp[0]);
+ board.containerObj.style.backgroundColor = 'rgba(' + s[0] + ', ' + s[1] + ', ' + s[2] + ', ' + tmp[1] + ')';
+
+ // resize board
+ if (board.attr.takeSizeFromFile) {
+ board.resizeContainer(this.gEBTN(boardData, 'width'), this.gEBTN(boardData, 'height'));
+ }
+
+ xmlNode = this.gEBTN(boardData, 'coordinates', 0, false);
+
+ tmp = this.readViewPort(xmlNode);
+ if (tmp.length === 4) {
+ board.setBoundingBox(tmp, true);
+ } else {
+ // zoom level
+ tmp = this.gEBTN(xmlNode, 'zoom', 0, false);
+ board.zoomX = parseFloat(this.gEBTN(tmp, 'x'));
+ board.zoomY = parseFloat(this.gEBTN(tmp, 'y'));
+
+ // set the origin
+ tmp = this.gEBTN(xmlNode, 'origin', 0, false);
+ board.origin = {
+ usrCoords: [1, 0, 0],
+ scrCoords: [1, parseFloat(this.gEBTN(tmp, 'x')) * board.zoomX, parseFloat(this.gEBTN(tmp, 'y')) * board.zoomY]
+ };
+
+ // screen to user coordinates conversion
+ tmp = this.gEBTN(xmlNode, 'unit', 0, false);
+ board.unitX = parseFloat(this.gEBTN(tmp, 'x')) * board.zoomX;
+ board.unitY = parseFloat(this.gEBTN(tmp, 'y')) * board.zoomY;
+ }
+
+ if (board.attr.takeSizeFromFile) {
+ board.resizeContainer(this.gEBTN(boardData, 'width'), this.gEBTN(boardData, 'height'));
+ }
+
+ // check and set fontSize
+ if (parseFloat(board.options.text.fontSize) < 0) {
+ board.options.text.fontSize = 12;
+ }
+
+ board.geonextCompatibilityMode = true;
+
+ // jsxgraph chooses an id for the board but we don't want to use it, we want to use
+ // the id stored in the geonext file. if you know why this is required, please note it here.
+ delete JXG.boards[board.id];
+
+ board.id = this.gEBTN(boardData, 'id');
+
+ JXG.boards[board.id] = board;
+
+ // this creates some basic elements present in every geonext construction but not explicitly present in the file
+ board.initGeonextBoard();
+
+ // Update of properties during update() is not necessary in GEONExT files
+ // But it maybe necessary if we construct with JavaScript afterwards
+ board.renderer.enhancedRendering = true;
+
+ // Read background image
+ this.parseImage(board, this.gEBTN(boardData, 'file', 0, false), board.options.layer.image);
+
+ board.options.point.snapToGrid = (this.gEBTN(this.gEBTN(boardData, 'coordinates', 0, false), 'snap') === strTrue);
+
+ // If snapToGrid and snapToPoint are both true, point snapping is enabled
+ if (board.options.point.snapToGrid && this.gEBTN(this.gEBTN(boardData, 'grid', 1, false), 'pointsnap') === strTrue) {
+ board.options.point.snapToGrid = false;
+ board.options.point.snapToPoints = true;
+ board.options.point.attractorDistance = 0.5;
+ }
+
+ xmlNode = this.gEBTN(boardData, 'grid', 1, false);
+ tmp = this.gEBTN(xmlNode, 'x');
+ if (tmp) {
+ board.options.grid.gridX = 1 / parseFloat(tmp);
+ board.options.point.snapSizeX = 1 / parseFloat(tmp);
+ }
+ tmp = this.gEBTN(xmlNode, 'y');
+ if (tmp) {
+ board.options.grid.gridY = 1 / parseFloat(tmp);
+ board.options.point.snapSizeY = 1 / parseFloat(tmp);
+ }
+
+ board.options.grid.gridDash = JXG.str2Bool(this.gEBTN(xmlNode, 'dash'));
+
+ tmp = JXG.rgba2rgbo(this.gEBTN(xmlNode, 'color'));
+ board.options.grid.gridColor = tmp[0];
+ board.options.grid.gridOpacity = tmp[1];
+
+ xmlNode = this.gEBTN(boardData, 'coordinates', 0, false);
+ if (this.gEBTN(xmlNode, 'grid') === strTrue) {
+ board.create('grid', []);
+ }
+
+ if (this.gEBTN(xmlNode, 'coord') === strTrue) {
+ // Hard coded default option
+ board.options.axis.ticks.majorHeight = 10;
+
+ // Hard coded default option
+ board.options.axis.ticks.minorHeight = 4;
+ board.create('axis', [[0, 0], [1, 0]]);
+ board.create('axis', [[0, 0], [0, 1]]);
+ }
+
+ tmp = this.gEBTN(this.gEBTN(boardData, 'background', 0, false), 'color');
+ if (tmp.length === 8) {
+ tmp = '#' + tmp;
+ }
+ board.containerObj.style.backgroundColor = JXG.rgba2rgbo(tmp)[0];
+
+ elChildNodes = tree.getElementsByTagName("elements")[0].childNodes;
+ for (s = 0; s < elChildNodes.length; s++) {
+ this.readNode(elChildNodes, s, board);
+ }
+ board.addConditions(conditions);
+ },
+
+ decodeString: function (str) {
+ var unz;
+
+ if (str.indexOf("<GEONEXT>") < 0) {
+ unz = (new JXG.Util.Unzip(JXG.Util.Base64.decodeAsArray(str))).unzip();
+ if (unz === "") {
+ return str;
+ }
+
+ return unz;
+ }
+
+ return str;
+ },
+
+ prepareString: function (fileStr) {
+ try {
+ if (fileStr.indexOf('GEONEXT') < 0) {
+ // Base64 decoding
+ fileStr = (this.decodeString(fileStr))[0][0];
+ }
+ // Hacks to enable not well formed XML. Will be redone in geonext2JS and Board.addConditions
+ fileStr = this.fixXML(fileStr);
+ } catch (exc2) {
+ fileStr = '';
+ }
+ return fileStr;
+ },
+
+ fixXML: function (str) {
+ 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"],
+
+ list = arr.join('|'),
+ regex = '&lt;(/?(' + list + '))&gt;',
+ expr = new RegExp(regex, 'g');
+
+ // First, we convert all < to &lt; and > to &gt;
+ str = JXG.escapeHTML(str);
+ // Second, we convert all GEONExT tags of the form &lt;tag&gt; back to <tag>
+ str = str.replace(expr, '<$1>');
+
+ // intentional
+ /*jslint regexp:true*/
+ str = str.replace(/(<content>.*)<arc>(.*<\/content>)/g, '$1&lt;arc&gt;$2');
+ str = str.replace(/(<mp>.*)<arc>(.*<\/mpx>)/g, '$1&lt;arc&gt;$2');
+ str = str.replace(/(<mpx>.*)<arc>(.*<\/mpx>)/g, '$1&lt;arc&gt;$2');
+ /*jslint regexp:false*/
+
+ return str;
+ }
+ });
+
+ JXG.registerReader(JXG.GeonextReader, ['gxt', 'geonext']);
+}());