jsxgraph 1.4.4 → 1.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +24 -0
  2. package/build/Readme.md +17 -0
  3. package/build/bin/jsxcompressor.js +1 -0
  4. package/build/bin/jsxgraphcore-min.js +7 -0
  5. package/build/bin/jsxgraphcore.js +83017 -0
  6. package/build/bin/jsxgraphcore.min.js +67 -0
  7. package/build/compressor.build.json +14 -0
  8. package/build/compressor.deps.js +8 -0
  9. package/build/core-amd.build.json +14 -0
  10. package/build/core-amd.deps.js +81 -0
  11. package/build/core.build.json +14 -0
  12. package/build/core.deps.js +102 -0
  13. package/build/plot.build.json +15 -0
  14. package/distrib/README.md +24 -0
  15. package/distrib/geogebra.min.js +1 -1
  16. package/distrib/geonext.min.js +1 -1
  17. package/distrib/index.d.ts +28 -7
  18. package/distrib/intergeo.min.js +1 -1
  19. package/distrib/jsxgraph.css +6 -0
  20. package/distrib/jsxgraphcore.js +3 -3
  21. package/distrib/jsxgraphsrc.js +3558 -1857
  22. package/distrib/sketch.min.js +1 -1
  23. package/package.json +1 -1
  24. package/src/3d/box3d.js +43 -38
  25. package/src/3d/curve3d.js +157 -53
  26. package/src/3d/element3d.js +84 -0
  27. package/src/3d/linspace3d.js +427 -146
  28. package/src/3d/point3d.js +360 -123
  29. package/src/3d/surface3d.js +140 -42
  30. package/src/3d/view3d.js +319 -164
  31. package/src/base/board.js +163 -78
  32. package/src/base/constants.js +8 -1
  33. package/src/base/curve.js +51 -9
  34. package/src/base/polygon.js +111 -69
  35. package/src/base/text.js +122 -19
  36. package/src/base/ticks.js +12 -3
  37. package/src/element/button.js +7 -3
  38. package/src/element/checkbox.js +8 -4
  39. package/src/element/composition.js +7 -1
  40. package/src/element/conic.js +2 -2
  41. package/src/element/input.js +9 -5
  42. package/src/jsxgraph.js +4 -1
  43. package/src/loadjsxgraph.js +13 -8
  44. package/src/math/clip.js +34 -134
  45. package/src/math/geometry.js +204 -3
  46. package/src/math/math.js +1 -2
  47. package/src/math/nlp.js +865 -774
  48. package/src/options.js +185 -12
  49. package/src/options3d.js +24 -7
  50. package/src/parser/jessiecode.js +9 -3
  51. package/src/renderer/abstract.js +26 -15
  52. package/src/renderer/canvas.js +2 -2
  53. package/src/renderer/svg.js +15 -2
  54. package/src/utils/env.js +36 -53
  55. package/src/utils/type.js +75 -2
  56. package/src/3d/threed.js +0 -38
@@ -80,6 +80,14 @@ define([
80
80
  this.vertices = [];
81
81
  for (i = 0; i < vertices.length; i++) {
82
82
  this.vertices[i] = this.board.select(vertices[i]);
83
+
84
+ // The _is_new flag is replaced by _is_new_pol.
85
+ // Otherwise, the polygon would disappear if the last border element
86
+ // is removed (and the point has been provided by coordinates)
87
+ if (this.vertices[i]._is_new) {
88
+ delete this.vertices[i]._is_new;
89
+ this.vertices[i]._is_new_pol = true;
90
+ }
83
91
  }
84
92
 
85
93
  // Close the polygon
@@ -127,9 +135,9 @@ define([
127
135
  // - add points (supplied as coordinate arrays by the user and created by Type.providePoints) as children to the polygon
128
136
  for (i = 0; i < this.vertices.length - 1; i++) {
129
137
  p = this.board.select(this.vertices[i]);
130
- if (Type.exists(p._is_new)) {
138
+ if (Type.exists(p._is_new_pol)) {
131
139
  this.addChild(p);
132
- delete p._is_new;
140
+ delete p._is_new_pol;
133
141
  } else {
134
142
  p.addChild(this);
135
143
  }
@@ -165,10 +173,7 @@ define([
165
173
  JXG.extend(JXG.Polygon.prototype, /** @lends JXG.Polygon.prototype */ {
166
174
 
167
175
  /**
168
- * Decides if a point (x,y) is inside of the polygon.
169
- * Implements W. Randolf Franklin's pnpoly method.
170
- *
171
- * See <a href="https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html">https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html</a>.
176
+ * Wrapper for JXG.Math.Geometry.pnpoly.
172
177
  *
173
178
  * @param {Number} x_in x-coordinate (screen or user coordinates)
174
179
  * @param {Number} y_in y-coordinate (screen or user coordinates)
@@ -176,7 +181,9 @@ define([
176
181
  * Possible values are <b>JXG.COORDS_BY_USER</b> and <b>JXG.COORDS_BY_SCREEN</b>.
177
182
  * Default value is JXG.COORDS_BY_SCREEN
178
183
  *
179
- * @returns {Boolean} if (x,y) is inside of the polygon.
184
+ * @returns {Boolean} if (x_in, y_in) is inside of the polygon.
185
+ * @see JXG.Math.Geometry#pnpoly
186
+ *
180
187
  * @example
181
188
  * var pol = board.create('polygon', [[-1,2], [2,2], [-1,4]]);
182
189
  * var p = board.create('point', [4, 3]);
@@ -202,31 +209,7 @@ define([
202
209
  *
203
210
  */
204
211
  pnpoly: function(x_in, y_in, coord_type) {
205
- var i, j, len,
206
- x, y, crds,
207
- v = this.vertices,
208
- isIn = false;
209
-
210
- if (coord_type === Const.COORDS_BY_USER) {
211
- crds = new Coords(Const.COORDS_BY_USER, [x_in, y_in], this.board);
212
- x = crds.scrCoords[1];
213
- y = crds.scrCoords[2];
214
- } else {
215
- x = x_in;
216
- y = y_in;
217
- }
218
-
219
- len = this.vertices.length;
220
- for (i = 0, j = len - 2; i < len - 1; j = i++) {
221
- if (((v[i].coords.scrCoords[2] > y) !== (v[j].coords.scrCoords[2] > y)) &&
222
- (x < (v[j].coords.scrCoords[1] - v[i].coords.scrCoords[1]) *
223
- (y - v[i].coords.scrCoords[2]) / (v[j].coords.scrCoords[2] - v[i].coords.scrCoords[2]) + v[i].coords.scrCoords[1])
224
- ) {
225
- isIn = !isIn;
226
- }
227
- }
228
-
229
- return isIn;
212
+ return Geometry.pnpoly(x_in, y_in, this.vertices, coord_type);
230
213
  },
231
214
 
232
215
  /**
@@ -590,9 +573,14 @@ define([
590
573
  *
591
574
  */
592
575
  addPoints: function (p) {
593
- var args = Array.prototype.slice.call(arguments);
576
+ var idx, args = Array.prototype.slice.call(arguments);
594
577
 
595
- return this.insertPoints.apply(this, [this.vertices.length - 2].concat(args));
578
+ if (this.elType === 'polygonalchain') {
579
+ idx = this.vertices.length - 1;
580
+ } else {
581
+ idx = this.vertices.length - 2;
582
+ }
583
+ return this.insertPoints.apply(this, [idx].concat(args));
596
584
  },
597
585
 
598
586
  /**
@@ -630,38 +618,65 @@ define([
630
618
  *
631
619
  */
632
620
  insertPoints: function (idx, p) {
633
- var i, le;
621
+ var i, le, last, start, q;
634
622
 
635
623
  if (arguments.length === 0) {
636
624
  return this;
637
625
  }
638
626
 
627
+ last = this.vertices.length - 1;
628
+ if (this.elType === 'polygon') {
629
+ last--;
630
+ }
639
631
 
640
- if (idx < -1 || idx > this.vertices.length - 2) {
632
+ // Wrong insertion index, get out of here
633
+ if (idx < -1 || idx > last) {
641
634
  return this;
642
635
  }
643
636
 
644
637
  le = arguments.length - 1;
645
638
  for (i = 1; i < le + 1; i++) {
646
- this.vertices.splice(idx + i, 0,
647
- Type.providePoints(this.board, [arguments[i]], {}, 'polygon', ['vertices'])[0]
648
- );
649
- }
650
- if (idx === -1) {
651
- this.vertices[this.vertices.length - 1] = this.vertices[0];
639
+ q = Type.providePoints(this.board, [arguments[i]], {}, 'polygon', ['vertices'])[0];
640
+ if (q._is_new) {
641
+ // Add the point as child of the polygon, but not of the borders.
642
+ this.addChild(q);
643
+ delete q._is_new;
644
+ }
645
+ this.vertices.splice(idx + i, 0, q);
652
646
  }
647
+
653
648
  if (this.withLines) {
654
- if (idx < 0) {
655
- this.borders[this.borders.length - 1].point2 = this.vertices[this.vertices.length - 1];
649
+ start = idx + 1;
650
+ if (this.elType === 'polygon') {
651
+ if (idx < 0) {
652
+ // Add point(s) in the front
653
+ this.vertices[this.vertices.length - 1] = this.vertices[0];
654
+ this.borders[this.borders.length - 1].point2 = this.vertices[this.vertices.length - 1];
655
+ } else {
656
+ // Insert point(s) (middle or end)
657
+ this.borders[idx].point2 = this.vertices[start];
658
+ }
656
659
  } else {
657
- this.borders[idx].point2 = this.vertices[idx + 1];
660
+ // Add point(s) in the front: do nothing
661
+ // Else:
662
+ if (idx >= 0) {
663
+ if (idx < this.borders.length) {
664
+ // Insert point(s) in the middle
665
+ this.borders[idx].point2 = this.vertices[start];
666
+ } else {
667
+ // Add point at the end
668
+ start = idx;
669
+ }
670
+ }
658
671
  }
659
- for (i = idx + 1; i < idx + 1 + le; i++) {
672
+ for (i = start; i < start + le; i++) {
660
673
  this.borders.splice(i, 0,
661
674
  this.board.create('segment', [this.vertices[i], this.vertices[i + 1]], this.attr_line)
662
675
  );
663
676
  }
664
677
  }
678
+ this.inherits = [];
679
+ this.inherits.push(this.vertices, this.borders);
665
680
  this.board.update();
666
681
 
667
682
  return this;
@@ -673,7 +688,8 @@ define([
673
688
  * @returns {JXG.Polygon} Reference to the polygon
674
689
  */
675
690
  removePoints: function (p) {
676
- var i, j, idx, nvertices = [], nborders = [],
691
+ var i, j, j0, idx, firstPoint,
692
+ nvertices = [], nborders = [],
677
693
  nidx = [], partition = [];
678
694
 
679
695
  // Partition:
@@ -684,8 +700,11 @@ define([
684
700
  // this gives us the borders, that can be removed and the borders we have to create.
685
701
 
686
702
 
687
- // Remove the last vertex which is identical to the first
688
- this.vertices = this.vertices.slice(0, this.vertices.length - 1);
703
+ // In case of polygon: remove the last vertex from the list of vertices since
704
+ // it is identical to the first
705
+ if (this.elType === 'polygon') {
706
+ firstPoint = this.vertices.pop();
707
+ }
689
708
 
690
709
  // Collect all valid parameters as indices in nidx
691
710
  for (i = 0; i < arguments.length; i++) {
@@ -693,12 +712,19 @@ define([
693
712
  if (Type.isPoint(idx)) {
694
713
  idx = this.findPoint(idx);
695
714
  }
696
-
697
715
  if (Type.isNumber(idx) && idx > -1 && idx < this.vertices.length && Type.indexOf(nidx, idx) === -1) {
698
716
  nidx.push(idx);
699
717
  }
700
718
  }
701
719
 
720
+ if (nidx.length === 0) {
721
+ // Wrong index, get out of here
722
+ if (this.elType === 'polygon') {
723
+ this.vertices.push(firstPoint);
724
+ }
725
+ return this;
726
+ }
727
+
702
728
  // Remove the polygon from each removed point's children
703
729
  for (i = 0; i < nidx.length; i++) {
704
730
  this.vertices[nidx[i]].removeChild(this);
@@ -709,7 +735,7 @@ define([
709
735
  nvertices = this.vertices.slice();
710
736
  nborders = this.borders.slice();
711
737
 
712
- // Initialize the partition
738
+ // Initialize the partition with an array containing the last point to be removed
713
739
  if (this.withLines) {
714
740
  partition.push([nidx[nidx.length - 1]]);
715
741
  }
@@ -719,7 +745,9 @@ define([
719
745
  for (i = nidx.length - 1; i > -1; i--) {
720
746
  nvertices[nidx[i]] = -1;
721
747
 
722
- if (this.withLines && (nidx[i] - 1 > nidx[i - 1])) {
748
+ // Find gaps between the list of points to be removed.
749
+ // In this case a new partition is added.
750
+ if (this.withLines && nidx.length > 1 && (nidx[i] - 1 > nidx[i - 1])) {
723
751
  partition[partition.length - 1][1] = nidx[i];
724
752
  partition.push([nidx[i - 1]]);
725
753
  }
@@ -737,7 +765,11 @@ define([
737
765
  this.vertices.push(nvertices[i]);
738
766
  }
739
767
  }
740
- if (this.vertices[this.vertices.length - 1].id !== this.vertices[0].id) {
768
+
769
+ // Close the polygon again
770
+ if (this.elType === 'polygon' &&
771
+ this.vertices.length > 1 &&
772
+ this.vertices[this.vertices.length - 1].id !== this.vertices[0].id) {
741
773
  this.vertices.push(this.vertices[0]);
742
774
  }
743
775
 
@@ -747,23 +779,28 @@ define([
747
779
  for (j = partition[i][1] - 1; j < partition[i][0] + 1; j++) {
748
780
  // special cases
749
781
  if (j < 0) {
750
- // first vertex is removed, so the last border has to be removed, too
751
- j = 0;
752
- this.board.removeObject(this.borders[nborders.length - 1]);
753
- nborders[nborders.length - 1] = -1;
754
- } else if (j > nborders.length - 1) {
755
- j = nborders.length - 1;
782
+ if (this.elType === 'polygon') {
783
+ // First vertex is removed, so the last border has to be removed, too
784
+ this.board.removeObject(this.borders[nborders.length - 1]);
785
+ nborders[nborders.length - 1] = -1;
786
+ }
787
+ } else if (j < nborders.length) {
788
+ this.board.removeObject(this.borders[j]);
789
+ nborders[j] = -1;
756
790
  }
757
-
758
- this.board.removeObject(this.borders[j]);
759
- nborders[j] = -1;
760
791
  }
761
792
 
762
- // only create the new segment if it's not the closing border. the closing border is getting a special treatment at the end
763
- // the if clause is newer than the min/max calls inside createSegment; i'm sure this makes the min/max calls obsolete, but
764
- // just to be sure...
793
+ // Only create the new segment if it's not the closing border.
794
+ // The closing border is getting a special treatment at the end.
765
795
  if (partition[i][1] !== 0 && partition[i][0] !== nvertices.length - 1) {
766
- nborders[partition[i][0] - 1] = this.board.create('segment', [nvertices[Math.max(partition[i][1] - 1, 0)], nvertices[Math.min(partition[i][0] + 1, this.vertices.length - 1)]], this.attr_line);
796
+ // nborders[partition[i][0] - 1] = this.board.create('segment', [
797
+ // nvertices[Math.max(partition[i][1] - 1, 0)],
798
+ // nvertices[Math.min(partition[i][0] + 1, this.vertices.length - 1)]
799
+ // ], this.attr_line);
800
+ nborders[partition[i][0] - 1] = this.board.create('segment', [
801
+ nvertices[partition[i][1] - 1],
802
+ nvertices[partition[i][0] + 1]
803
+ ], this.attr_line);
767
804
  }
768
805
  }
769
806
 
@@ -775,10 +812,16 @@ define([
775
812
  }
776
813
 
777
814
  // if the first and/or the last vertex is removed, the closing border is created at the end.
778
- if (partition[0][1] === this.vertices.length - 1 || partition[partition.length - 1][1] === 0) {
779
- this.borders.push(this.board.create('segment', [this.vertices[0], this.vertices[this.vertices.length - 2]], this.attr_line));
815
+ if (this.elType === 'polygon' &&
816
+ this.vertices.length > 2 && // Avoid trivial case of polygon with 1 vertex
817
+ (partition[0][1] === this.vertices.length - 1 || partition[partition.length - 1][1] === 0)) {
818
+ this.borders.push(this.board.create('segment',
819
+ [this.vertices[0], this.vertices[this.vertices.length - 2]],
820
+ this.attr_line));
780
821
  }
781
822
  }
823
+ this.inherits = [];
824
+ this.inherits.push(this.vertices, this.borders);
782
825
 
783
826
  this.board.update();
784
827
 
@@ -1025,7 +1068,6 @@ define([
1025
1068
  return this.sutherlandHodgman(polygon);
1026
1069
  }
1027
1070
 
1028
-
1029
1071
  });
1030
1072
 
1031
1073
 
package/src/base/text.js CHANGED
@@ -196,42 +196,63 @@ define([
196
196
  var updateText, resolvedText,
197
197
  ev_p = Type.evaluate(this.visProp.parse),
198
198
  ev_um = Type.evaluate(this.visProp.usemathjax),
199
- ev_uk = Type.evaluate(this.visProp.usekatex);
199
+ ev_uk = Type.evaluate(this.visProp.usekatex),
200
+ convertJessieCode = false;
200
201
 
201
202
  this.orgText = text;
203
+
202
204
  if (Type.isFunction(text)) {
205
+ // <value> tags will not be evaluated if text is provided by a function
203
206
  this.updateText = function () {
204
- resolvedText = text().toString();
207
+ resolvedText = text().toString(); // Evaluate function
205
208
  if (ev_p && !ev_um && !ev_uk) {
206
209
  this.plaintext = this.replaceSub(this.replaceSup(this.convertGeonextAndSketchometry2CSS(resolvedText)));
207
210
  } else {
208
211
  this.plaintext = resolvedText;
209
212
  }
210
213
  };
211
- } else if (Type.isString(text) && !ev_p) { // Do not parse
212
- this.updateText = function () {
213
- this.plaintext = text;
214
- };
215
- } else { // Parse
214
+
215
+ } else {
216
+
216
217
  if (Type.isNumber(text)) {
217
218
  this.content = Type.toFixed(text, Type.evaluate(this.visProp.digits));
218
- } else {
219
- if (Type.evaluate(this.visProp.useasciimathml)) {
220
- // Convert via ASCIIMathML
219
+ } else if (Type.isString(text) && ev_p) {
220
+
221
+ if (Type.evaluate(this.visProp.useasciimathml)) { // ASCIIMathML
221
222
  this.content = "'`" + text + "`'";
222
- } else if (ev_um || ev_uk) {
223
- this.content = "'" + text + "'";
223
+
224
+ } else if (ev_um || ev_uk) { // MathJax or KaTeX
225
+ // Replace value-tags by functions
226
+ this.content = this.valueTagToJessieCode(text);
227
+ this.content = this.content.replace(/\\/g, "\\\\"); // Replace single backshlash by double
228
+
224
229
  } else {
230
+ // No TeX involved.
225
231
  // Converts GEONExT syntax into JavaScript string
226
232
  // Short math is allowed
233
+ // Replace value-tags by functions
227
234
  // Avoid geonext2JS calls
228
- this.content = this.generateTerm(text, true, true);
235
+ this.content = this.poorMansTeX(this.valueTagToJessieCode(text));
229
236
  }
237
+ convertJessieCode = true;
238
+ }
239
+
240
+ // Generate function which returns the text to be displayed
241
+ if (convertJessieCode) {
242
+
243
+ // Convert JessieCode to JS function
244
+ updateText = this.board.jc.snippet(this.content, true, '', false);
245
+
246
+ // Ticks have been esacped in valueTagToJessieCode
247
+ this.updateText = function () {
248
+ this.plaintext = this.unescapeTicks(updateText());
249
+ };
250
+ } else {
251
+ this.updateText = function () {
252
+ this.plaintext = text;
253
+ };
254
+
230
255
  }
231
- updateText = this.board.jc.snippet(this.content, true, '', false);
232
- this.updateText = function () {
233
- this.plaintext = updateText();
234
- };
235
256
  }
236
257
  },
237
258
 
@@ -584,11 +605,16 @@ define([
584
605
  * Converts the GEONExT syntax of the <value> terms into JavaScript.
585
606
  * Also, all Objects whose name appears in the term are searched and
586
607
  * the text is added as child to these objects.
608
+ * This method is called if the attribute parse==true is set.
587
609
  *
588
610
  * @param{String} contentStr String to be parsed
589
611
  * @param{Boolean} [expand] Optional flag if shortened math syntax is allowed (e.g. 3x instead of 3*x).
590
612
  * @param{Boolean} [avoidGeonext2JS] Optional flag if geonext2JS should be called. For backwards compatibility
591
613
  * this has to be set explicitely to true.
614
+ * @param{Boolean} [outputTeX] Optional flag which has to be true if the resulting term will be sent to MathJax or KaTeX.
615
+ * If true, "_" and "^" are NOT replaced by HTML tags sub and sup. Default: false, i.e. the replacement is done.
616
+ * This flag allows the combination of &lt;value&gt; tag containing calculations with TeX output.
617
+ *
592
618
  * @private
593
619
  * @see JXG.GeonextParser.geonext2JS
594
620
  */
@@ -596,13 +622,17 @@ define([
596
622
  var res, term, i, j,
597
623
  plaintext = '""';
598
624
 
599
- // revert possible jc replacement
625
+ // Revert possible jc replacement
600
626
  contentStr = contentStr || '';
601
627
  contentStr = contentStr.replace(/\r/g, '');
602
628
  contentStr = contentStr.replace(/\n/g, '');
603
629
  contentStr = contentStr.replace(/"/g, '\'');
604
630
  contentStr = contentStr.replace(/'/g, "\\'");
605
631
 
632
+ // Old GEONExT syntax, not (yet) supported as TeX output.
633
+ // Otherwise, the else clause should be used.
634
+ // That means, i.e. the <arc> tag and <sqrt> tag are not
635
+ // converted into TeX syntax.
606
636
  contentStr = contentStr.replace(/&amp;arc;/g, '&ang;');
607
637
  contentStr = contentStr.replace(/<arc\s*\/>/g, '&ang;');
608
638
  contentStr = contentStr.replace(/&lt;arc\s*\/&gt;/g, '&ang;');
@@ -617,6 +647,8 @@ define([
617
647
  if (i >= 0) {
618
648
  while (i >= 0) {
619
649
  plaintext += ' + "' + this.replaceSub(this.replaceSup(contentStr.slice(0, i))) + '"';
650
+ // plaintext += ' + "' + this.replaceSub(contentStr.slice(0, i)) + '"';
651
+
620
652
  term = contentStr.slice(i + 7, j);
621
653
  term = term.replace(/\s+/g, ''); // Remove all whitespace
622
654
  if (expand === true) {
@@ -652,13 +684,84 @@ define([
652
684
  plaintext += ' + "' + this.replaceSub(this.replaceSup(contentStr)) + '"';
653
685
  plaintext = this.convertGeonextAndSketchometry2CSS(plaintext);
654
686
 
655
- // This should replace &amp;pi; by &pi;
687
+ // This should replace e.g. &amp;pi; by &pi;
656
688
  plaintext = plaintext.replace(/&amp;/g, '&');
657
689
  plaintext = plaintext.replace(/"/g, "'");
658
690
 
659
691
  return plaintext;
660
692
  },
661
693
 
694
+ valueTagToJessieCode: function(contentStr) {
695
+ var res, term, i, j,
696
+ expandShortMath = true,
697
+ textComps = [],
698
+ tick = '"';
699
+
700
+ contentStr = contentStr || '';
701
+ contentStr = contentStr.replace(/\r/g, '');
702
+ contentStr = contentStr.replace(/\n/g, '');
703
+
704
+ contentStr = contentStr.replace(/&lt;value&gt;/g, '<value>');
705
+ contentStr = contentStr.replace(/&lt;\/value&gt;/g, '</value>');
706
+
707
+ // Convert content of value tag (GEONExT/JessieCode) syntax into JavaScript syntax
708
+ i = contentStr.indexOf('<value>');
709
+ j = contentStr.indexOf('</value>');
710
+ if (i >= 0) {
711
+ while (i >= 0) {
712
+ // Add string fragment before <value> tag
713
+ textComps.push(tick + this.escapeTicks(contentStr.slice(0, i)) + tick);
714
+
715
+ term = contentStr.slice(i + 7, j);
716
+ term = term.replace(/\s+/g, ''); // Remove all whitespace
717
+ if (expandShortMath === true) {
718
+ term = this.expandShortMath(term);
719
+ }
720
+ res = term;
721
+ res = res.replace(/\\"/g, "'").replace(/\\'/g, "'");
722
+
723
+ // Hack: apply rounding once only.
724
+ if (res.indexOf('toFixed') < 0) {
725
+ // Output of a value tag
726
+ // Run the JessieCode parser
727
+ if (Type.isNumber((Type.bind(this.board.jc.snippet(res, true, '', false), this))())) {
728
+ // Output is number
729
+ textComps.push('(' + res + ').toFixed(' + (Type.evaluate(this.visProp.digits)) + ')');
730
+ } else {
731
+ // Output is a string
732
+ textComps.push('(' + res + ')');
733
+ }
734
+ } else {
735
+ textComps.push('(' + res + ')');
736
+ }
737
+ contentStr = contentStr.slice(j + 8);
738
+ i = contentStr.indexOf('<value>');
739
+ j = contentStr.indexOf('</value>');
740
+ }
741
+ }
742
+ // Add trailing string fragment
743
+ textComps.push(tick + this.escapeTicks(contentStr) + tick);
744
+
745
+ return textComps.join(' + ').replace(/&amp;/g, '&');
746
+ },
747
+
748
+ poorMansTeX: function(s) {
749
+ s = s.replace(/<arc\s*\/*>/g, '&ang;')
750
+ .replace(/&lt;arc\s*\/*&gt;/g, '&ang;')
751
+ .replace(/<sqrt\s*\/*>/g, '&radic;')
752
+ .replace(/&lt;sqrt\s*\/*&gt;/g, '&radic;');
753
+
754
+ return this.convertGeonextAndSketchometry2CSS(this.replaceSub(this.replaceSup(s)));
755
+ },
756
+
757
+ escapeTicks: function(s) {
758
+ return s.replace(/"/g, '%22').replace(/'/g, '%27');
759
+ },
760
+
761
+ unescapeTicks: function(s) {
762
+ return s.replace(/%22/g, '"').replace(/%27/g, "'");
763
+ },
764
+
662
765
  /**
663
766
  * Converts the GEONExT tags <overline> and <arrow> to
664
767
  * HTML span tags with proper CSS formatting.
package/src/base/ticks.js CHANGED
@@ -659,6 +659,7 @@ define([
659
659
  } else if (!ev_it) {
660
660
  ticksDelta /= (ev_mt + 1);
661
661
  }
662
+ // Now, ticksdelta is the distance between two minor ticks
662
663
  this.ticksDelta = ticksDelta;
663
664
 
664
665
  if (ticksDelta < Mat.eps) {
@@ -754,6 +755,8 @@ define([
754
755
  */
755
756
  processTickPosition: function (coordsZero, tickPosition, ticksDelta, deltas) {
756
757
  var x, y, tickCoords, ti,
758
+ isLabelPosition,
759
+ ticksPerLabel = Type.evaluate(this.visProp.ticksperlabel),
757
760
  labelVal = null;
758
761
 
759
762
  // Calculates tick coordinates
@@ -776,13 +779,19 @@ define([
776
779
  // a multiple of the number of minorticks+1
777
780
  tickCoords.major = Math.round(tickPosition / ticksDelta) % (Type.evaluate(this.visProp.minorticks) + 1) === 0;
778
781
 
782
+ if (!ticksPerLabel) {
783
+ // In case of null, 0 or false, majorTicks are labelled
784
+ ticksPerLabel = Type.evaluate(this.visProp.minorticks) + 1;
785
+ }
786
+ isLabelPosition = Math.round(tickPosition / ticksDelta) % (ticksPerLabel) === 0;
787
+
779
788
  // Compute the start position and the end position of a tick.
780
789
  // If both positions are out of the canvas, ti is empty.
781
790
  ti = this.createTickPath(tickCoords, tickCoords.major);
782
791
  if (ti.length === 3) {
783
792
  this.ticks.push(ti);
784
- if (tickCoords.major && Type.evaluate(this.visProp.drawlabels)) {
785
- // major tick label
793
+ if (isLabelPosition && Type.evaluate(this.visProp.drawlabels)) {
794
+ // Create a label at this position
786
795
  this.labelsData.push(
787
796
  this.generateLabelData(
788
797
  this.generateLabelText(tickCoords, coordsZero, labelVal),
@@ -1022,7 +1031,7 @@ define([
1022
1031
 
1023
1032
  // if value is Number
1024
1033
  if (Type.isNumber(value)) {
1025
- labelText = (Math.round(value * 1.e13) / 1.e13).toString();
1034
+ labelText = (Math.round(value * 1.e11) / 1.e11).toString();
1026
1035
  if (labelText.length > Type.evaluate(this.visProp.maxlabellength) ||
1027
1036
  labelText.indexOf('e') !== -1) {
1028
1037
 
@@ -71,7 +71,7 @@ define([
71
71
  * @constructor
72
72
  * @type JXG.Text
73
73
  *
74
- * @param {number,function_number,function_String_function} x,y,label,handler Parent elements for button elements.
74
+ * @param {number,function_number,function_String,function_function} x,y,label,handler Parent elements for button elements.
75
75
  * <p>
76
76
  * x and y are the coordinates of the lower left corner of the text box.
77
77
  * The position of the text is fixed,
@@ -205,18 +205,22 @@ define([
205
205
  // "\nPossible parents are: [x, y, label, handler]");
206
206
  //}
207
207
 
208
+ // 1. Create empty button
208
209
  par = [parents[0], parents[1], '<button type="button" style="width:100%;"></button>'];
209
-
210
210
  t = board.create('text', par, attr);
211
211
  t.type = Type.OBJECT_TYPE_BUTTON;
212
212
 
213
213
  t.rendNodeButton = t.rendNode.childNodes[0];
214
214
  t.rendNodeButton.id = t.rendNode.id + '_button';
215
- t.rendNodeButton.innerHTML = parents[2];
215
+ // t.rendNodeButton.innerHTML = parents[2];
216
216
 
217
217
  t.rendNodeTag = t.rendNodeButton; // Needed for unified treatment in setAttribute
218
218
  t.rendNodeTag.disabled = !!attr.disabled;
219
219
 
220
+ // 2. Set parents[2] (string|function) as content of the button.
221
+ // abstract.js selects the correct DOM element for the update
222
+ t.setText(parents[2]);
223
+
220
224
  // This sets the font-size of the button text
221
225
  t.visPropOld.fontsize = "0px";
222
226
  board.renderer.updateTextStyle(t, false);
@@ -69,13 +69,13 @@ define([
69
69
  * @constructor
70
70
  * @type JXG.Text
71
71
  *
72
- * @param {number,function_number,function_String_String} x,y,label Parent elements for checkbox elements.
72
+ * @param {number,function_number,function_String,function} x,y,label Parent elements for checkbox elements.
73
73
  * <p>
74
74
  * x and y are the coordinates of the lower left corner of the text box.
75
75
  * The position of the text is fixed,
76
76
  * x and y are numbers. The position is variable if x or y are functions.
77
77
  * <p>
78
- * The label of the input element may be given as string.
78
+ * The label of the input element may be given as string or function.
79
79
  * <p>
80
80
  * The value of the checkbox can be controlled with the attribute <tt>checked</tt>
81
81
  * <p>The HTML node can be accessed with <tt>element.rendNodeCheckbox</tt>
@@ -184,7 +184,7 @@ define([
184
184
  '</span>'
185
185
  ];
186
186
 
187
- //t = JXG.createText(board, par, attr);
187
+ // 1. Create checkbox element with empty label
188
188
  t = board.create('text', par, attr);
189
189
  t.type = Type.OBJECT_TYPE_CHECKBOX;
190
190
 
@@ -194,11 +194,15 @@ define([
194
194
  t.rendNodeTag = t.rendNodeCheckbox; // Needed for unified treatment in setAttribute
195
195
  t.rendNodeTag.disabled = !!attr.disabled;
196
196
 
197
- t.rendNodeLabel.innerHTML = parents[2];
197
+ // t.rendNodeLabel.innerHTML = parents[2];
198
198
  t.rendNodeCheckbox.id = t.rendNode.id + '_checkbox';
199
199
  t.rendNodeLabel.id = t.rendNode.id + '_label';
200
200
  t.rendNodeLabel.setAttribute('for', t.rendNodeCheckbox.id);
201
201
 
202
+ // 2. Set parents[2] (string|function) as label of the checkbox element.
203
+ // abstract.js selects the correct DOM element for the update
204
+ t.setText(parents[2]);
205
+
202
206
  // This sets the font-size of the checkbox itself
203
207
  t.visPropOld.fontsize = "0px";
204
208
  board.renderer.updateTextStyle(t, false);