jsxgraph 1.3.0 → 1.4.1
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.
- package/README.md +2 -2
- package/distrib/README.md +2 -2
- package/distrib/geogebra.min.js +3 -3
- package/distrib/geonext.min.js +3 -3
- package/distrib/index.d.ts +651 -448
- package/distrib/intergeo.min.js +3 -3
- package/distrib/jsxgraph.css +4 -13
- package/distrib/jsxgraphcore.js +9 -25
- package/distrib/jsxgraphsrc.js +28203 -25234
- package/distrib/sketch.min.js +3 -3
- package/package.json +11 -22
- package/src/base/board.js +674 -469
- package/src/base/chart.js +1 -1
- package/src/base/circle.js +23 -1
- package/src/base/composition.js +1 -1
- package/src/base/constants.js +32 -11
- package/src/base/coords.js +4 -4
- package/src/base/coordselement.js +51 -24
- package/src/base/curve.js +308 -117
- package/src/base/element.js +79 -50
- package/src/base/foreignobject.js +463 -0
- package/src/base/group.js +2 -2
- package/src/base/image.js +12 -15
- package/src/base/line.js +108 -21
- package/src/base/point.js +24 -28
- package/src/base/polygon.js +75 -18
- package/src/base/text.js +116 -63
- package/src/base/ticks.js +29 -11
- package/src/base/transformation.js +1 -1
- package/src/base/turtle.js +1 -1
- package/src/element/arc.js +1 -1
- package/src/element/button.js +29 -2
- package/src/element/checkbox.js +31 -1
- package/src/element/comb.js +5 -2
- package/src/element/composition.js +127 -19
- package/src/element/conic.js +46 -5
- package/src/element/input.js +32 -1
- package/src/element/locus.js +2 -2
- package/src/element/measure.js +9 -4
- package/src/element/sector.js +46 -12
- package/src/element/slider.js +17 -12
- package/src/element/slopetriangle.js +1 -1
- package/src/jsxgraph.js +28 -9
- package/src/jxg.js +4 -3
- package/src/loadjsxgraph.js +3 -2
- package/src/math/bst.js +1 -1
- package/src/math/clip.js +550 -345
- package/src/math/complex.js +1 -1
- package/src/math/extrapolate.js +1 -1
- package/src/math/geometry.js +155 -126
- package/src/math/ia.js +290 -21
- package/src/math/math.js +30 -1
- package/src/math/metapost.js +1 -1
- package/src/math/nlp.js +25 -5
- package/src/math/numerics.js +183 -4
- package/src/math/plot.js +4 -4
- package/src/math/poly.js +1 -1
- package/src/math/probfuncs.js +2 -1
- package/src/math/qdt.js +68 -62
- package/src/math/statistics.js +58 -7
- package/src/math/symbolic.js +1 -1
- package/src/options.js +377 -114
- package/src/parser/ca.js +1297 -1291
- package/src/parser/datasource.js +1 -1
- package/src/parser/geonext.js +6 -6
- package/src/parser/jessiecode.js +275 -79
- package/src/parser/jessiescript.js +1 -1
- package/src/reader/cinderella.js +1 -1
- package/src/reader/file.js +1 -1
- package/src/reader/geogebra.js +1 -1
- package/src/reader/geonext.js +1 -1
- package/src/reader/graph.js +1 -1
- package/src/reader/intergeo.js +1 -1
- package/src/reader/sketch.js +21 -13
- package/src/renderer/abstract.js +21 -6
- package/src/renderer/canvas.js +22 -15
- package/src/renderer/no.js +11 -4
- package/src/renderer/svg.js +76 -53
- package/src/renderer/vml.js +36 -32
- package/src/server/server.js +1 -1
- package/src/utils/base64.js +2 -2
- package/src/utils/color.js +148 -3
- package/src/utils/dump.js +21 -15
- package/src/utils/env.js +63 -104
- package/src/utils/event.js +1 -1
- package/src/utils/expect.js +1 -1
- package/src/utils/type.js +30 -19
- package/src/utils/xml.js +1 -1
- package/src/utils/zip.js +1 -1
- package/CHANGELOG.md +0 -1281
package/src/base/board.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2008-
|
|
2
|
+
Copyright 2008-2022
|
|
3
3
|
Matthias Ehmann,
|
|
4
4
|
Michael Gerhaeuser,
|
|
5
5
|
Carsten Miller,
|
|
@@ -65,10 +65,10 @@
|
|
|
65
65
|
define([
|
|
66
66
|
'jxg', 'base/constants', 'base/coords', 'options', 'math/numerics', 'math/math', 'math/geometry', 'math/complex',
|
|
67
67
|
'math/statistics',
|
|
68
|
-
'parser/jessiecode', '
|
|
69
|
-
'base/
|
|
70
|
-
], function (JXG, Const, Coords, Options, Numerics, Mat, Geometry, Complex, Statistics, JessieCode,
|
|
71
|
-
EventEmitter, Env,
|
|
68
|
+
'parser/jessiecode', 'utils/color', 'utils/type', 'utils/event', 'utils/env',
|
|
69
|
+
'base/composition'
|
|
70
|
+
], function (JXG, Const, Coords, Options, Numerics, Mat, Geometry, Complex, Statistics, JessieCode, Color, Type,
|
|
71
|
+
EventEmitter, Env, Composition) {
|
|
72
72
|
|
|
73
73
|
'use strict';
|
|
74
74
|
|
|
@@ -120,20 +120,20 @@ define([
|
|
|
120
120
|
this.BOARD_MODE_MOVE_ORIGIN = 0x0002;
|
|
121
121
|
|
|
122
122
|
/**
|
|
123
|
-
* Update is made with
|
|
123
|
+
* Update is made with high quality, e.g. graphs are evaluated at much more points.
|
|
124
124
|
* @type Number
|
|
125
125
|
* @constant
|
|
126
126
|
* @see JXG.Board#updateQuality
|
|
127
127
|
*/
|
|
128
|
-
this.
|
|
128
|
+
this.BOARD_MODE_ZOOM = 0x0011;
|
|
129
129
|
|
|
130
130
|
/**
|
|
131
|
-
* Update is made with
|
|
131
|
+
* Update is made with low quality, e.g. graphs are evaluated at a lesser amount of points.
|
|
132
132
|
* @type Number
|
|
133
133
|
* @constant
|
|
134
134
|
* @see JXG.Board#updateQuality
|
|
135
135
|
*/
|
|
136
|
-
this.
|
|
136
|
+
this.BOARD_QUALITY_LOW = 0x1;
|
|
137
137
|
|
|
138
138
|
/**
|
|
139
139
|
* Update is made with high quality, e.g. graphs are evaluated at much more points.
|
|
@@ -141,7 +141,7 @@ define([
|
|
|
141
141
|
* @constant
|
|
142
142
|
* @see JXG.Board#updateQuality
|
|
143
143
|
*/
|
|
144
|
-
this.
|
|
144
|
+
this.BOARD_QUALITY_HIGH = 0x2;
|
|
145
145
|
|
|
146
146
|
/**
|
|
147
147
|
* Pointer to the document element containing the board.
|
|
@@ -174,11 +174,16 @@ define([
|
|
|
174
174
|
/**
|
|
175
175
|
* A reference to this boards renderer.
|
|
176
176
|
* @type JXG.AbstractRenderer
|
|
177
|
+
* @name JXG.Board#renderer
|
|
178
|
+
* @private
|
|
179
|
+
* @ignore
|
|
177
180
|
*/
|
|
178
181
|
this.renderer = renderer;
|
|
179
182
|
|
|
180
183
|
/**
|
|
181
184
|
* Grids keeps track of all grids attached to this board.
|
|
185
|
+
* @type Array
|
|
186
|
+
* @private
|
|
182
187
|
*/
|
|
183
188
|
this.grids = [];
|
|
184
189
|
|
|
@@ -204,6 +209,7 @@ define([
|
|
|
204
209
|
* usrCoords and scrCoords. usrCoords always equals [1, 0, 0] and scrCoords
|
|
205
210
|
* stores the boards origin in homogeneous screen coordinates.
|
|
206
211
|
* @type Object
|
|
212
|
+
* @private
|
|
207
213
|
*/
|
|
208
214
|
this.origin = {};
|
|
209
215
|
this.origin.usrCoords = [1, 0, 0];
|
|
@@ -212,44 +218,56 @@ define([
|
|
|
212
218
|
/**
|
|
213
219
|
* Zoom factor in X direction. It only stores the zoom factor to be able
|
|
214
220
|
* to get back to 100% in zoom100().
|
|
221
|
+
* @name JXG.Board.zoomX
|
|
215
222
|
* @type Number
|
|
223
|
+
* @private
|
|
224
|
+
* @ignore
|
|
216
225
|
*/
|
|
217
226
|
this.zoomX = zoomX;
|
|
218
227
|
|
|
219
228
|
/**
|
|
220
229
|
* Zoom factor in Y direction. It only stores the zoom factor to be able
|
|
221
230
|
* to get back to 100% in zoom100().
|
|
231
|
+
* @name JXG.Board.zoomY
|
|
222
232
|
* @type Number
|
|
233
|
+
* @private
|
|
234
|
+
* @ignore
|
|
223
235
|
*/
|
|
224
236
|
this.zoomY = zoomY;
|
|
225
237
|
|
|
226
238
|
/**
|
|
227
239
|
* The number of pixels which represent one unit in user-coordinates in x direction.
|
|
228
240
|
* @type Number
|
|
241
|
+
* @private
|
|
229
242
|
*/
|
|
230
243
|
this.unitX = unitX * this.zoomX;
|
|
231
244
|
|
|
232
245
|
/**
|
|
233
246
|
* The number of pixels which represent one unit in user-coordinates in y direction.
|
|
234
247
|
* @type Number
|
|
248
|
+
* @private
|
|
235
249
|
*/
|
|
236
250
|
this.unitY = unitY * this.zoomY;
|
|
237
251
|
|
|
238
252
|
/**
|
|
239
253
|
* Keep aspect ratio if bounding box is set and the width/height ratio differs from the
|
|
240
254
|
* width/height ratio of the canvas.
|
|
255
|
+
* @type Boolean
|
|
256
|
+
* @private
|
|
241
257
|
*/
|
|
242
258
|
this.keepaspectratio = false;
|
|
243
259
|
|
|
244
260
|
/**
|
|
245
261
|
* Canvas width.
|
|
246
262
|
* @type Number
|
|
263
|
+
* @private
|
|
247
264
|
*/
|
|
248
265
|
this.canvasWidth = canvasWidth;
|
|
249
266
|
|
|
250
267
|
/**
|
|
251
268
|
* Canvas Height
|
|
252
269
|
* @type Number
|
|
270
|
+
* @private
|
|
253
271
|
*/
|
|
254
272
|
this.canvasHeight = canvasHeight;
|
|
255
273
|
|
|
@@ -379,15 +397,15 @@ define([
|
|
|
379
397
|
|
|
380
398
|
/**
|
|
381
399
|
* References to the object that is dragged with the mouse on the board.
|
|
382
|
-
* @type
|
|
383
|
-
* @see
|
|
400
|
+
* @type JXG.GeometryElement
|
|
401
|
+
* @see JXG.Board#touches
|
|
384
402
|
*/
|
|
385
403
|
this.mouse = {};
|
|
386
404
|
|
|
387
405
|
/**
|
|
388
406
|
* Keeps track on touched elements, like {@link JXG.Board#mouse} does for mouse events.
|
|
389
407
|
* @type Array
|
|
390
|
-
* @see
|
|
408
|
+
* @see JXG.Board#mouse
|
|
391
409
|
*/
|
|
392
410
|
this.touches = [];
|
|
393
411
|
|
|
@@ -412,6 +430,13 @@ define([
|
|
|
412
430
|
*/
|
|
413
431
|
this.touchMoveLast = 0;
|
|
414
432
|
|
|
433
|
+
/**
|
|
434
|
+
* Contains the pointerId of the last touchMove event which was not thrown away or since
|
|
435
|
+
* touchStart because Android's Webkit browser fires too much of them.
|
|
436
|
+
* @type Number
|
|
437
|
+
*/
|
|
438
|
+
this.touchMoveLastId = Infinity;
|
|
439
|
+
|
|
415
440
|
/**
|
|
416
441
|
* Contains the last time (epoch, msec) since the last getCoordsTopLeftCorner call which was not thrown away.
|
|
417
442
|
* @type Number
|
|
@@ -482,7 +507,7 @@ define([
|
|
|
482
507
|
|
|
483
508
|
/**
|
|
484
509
|
* A flag which stores if the board registered pointer events.
|
|
485
|
-
* @type
|
|
510
|
+
* @type Boolean
|
|
486
511
|
* @default false
|
|
487
512
|
*/
|
|
488
513
|
this.hasPointerHandlers = false;
|
|
@@ -503,14 +528,14 @@ define([
|
|
|
503
528
|
|
|
504
529
|
/**
|
|
505
530
|
* A flag which tells us if the board has a pointerUp event registered at the moment.
|
|
506
|
-
* @type
|
|
531
|
+
* @type Boolean
|
|
507
532
|
* @default false
|
|
508
533
|
*/
|
|
509
534
|
this.hasPointerUp = false;
|
|
510
535
|
|
|
511
536
|
/**
|
|
512
537
|
* Offset for large coords elements like images
|
|
513
|
-
* @type
|
|
538
|
+
* @type Array
|
|
514
539
|
* @private
|
|
515
540
|
* @default [0, 0]
|
|
516
541
|
*/
|
|
@@ -518,31 +543,36 @@ define([
|
|
|
518
543
|
|
|
519
544
|
/**
|
|
520
545
|
* Stores the input device used in the last down or move event.
|
|
521
|
-
* @type
|
|
546
|
+
* @type String
|
|
522
547
|
* @private
|
|
523
548
|
* @default 'mouse'
|
|
524
549
|
*/
|
|
525
550
|
this._inputDevice = 'mouse';
|
|
526
551
|
|
|
552
|
+
/**
|
|
553
|
+
* Keeps a list of pointer devices which are currently touching the screen.
|
|
554
|
+
* @type Array
|
|
555
|
+
* @private
|
|
556
|
+
*/
|
|
527
557
|
this._board_touches = [];
|
|
528
558
|
|
|
529
559
|
/**
|
|
530
560
|
* A flag which tells us if the board is in the selecting mode
|
|
531
|
-
* @type
|
|
561
|
+
* @type Boolean
|
|
532
562
|
* @default false
|
|
533
563
|
*/
|
|
534
564
|
this.selectingMode = false;
|
|
535
565
|
|
|
536
566
|
/**
|
|
537
567
|
* A flag which tells us if the user is selecting
|
|
538
|
-
* @type
|
|
568
|
+
* @type Boolean
|
|
539
569
|
* @default false
|
|
540
570
|
*/
|
|
541
571
|
this.isSelecting = false;
|
|
542
572
|
|
|
543
573
|
/**
|
|
544
574
|
* A flag which tells us if the user is scrolling the viewport
|
|
545
|
-
* @type
|
|
575
|
+
* @type Boolean
|
|
546
576
|
* @private
|
|
547
577
|
* @default false
|
|
548
578
|
* @see JXG.Board#scrollListener
|
|
@@ -551,7 +581,7 @@ define([
|
|
|
551
581
|
|
|
552
582
|
/**
|
|
553
583
|
* A flag which tells us if a resize is in process
|
|
554
|
-
* @type
|
|
584
|
+
* @type Boolean
|
|
555
585
|
* @private
|
|
556
586
|
* @default false
|
|
557
587
|
* @see JXG.Board#resizeListener
|
|
@@ -560,7 +590,7 @@ define([
|
|
|
560
590
|
|
|
561
591
|
/**
|
|
562
592
|
* A bounding box for the selection
|
|
563
|
-
* @type
|
|
593
|
+
* @type Array
|
|
564
594
|
* @default [ [0,0], [0,0] ]
|
|
565
595
|
*/
|
|
566
596
|
this.selectingBox = [[0, 0], [0, 0]];
|
|
@@ -764,17 +794,20 @@ define([
|
|
|
764
794
|
* @see JXG.Board#mouseMoveListener
|
|
765
795
|
*/
|
|
766
796
|
checkFrameRate: function(evt) {
|
|
767
|
-
var
|
|
797
|
+
var handleEvt = false,
|
|
798
|
+
time = new Date().getTime();
|
|
768
799
|
|
|
769
|
-
if ((
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
return false;
|
|
800
|
+
if (Type.exists(evt.pointerId) && this.touchMoveLastId !== evt.pointerId) {
|
|
801
|
+
handleEvt = true;
|
|
802
|
+
this.touchMoveLastId = evt.pointerId;
|
|
774
803
|
}
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
804
|
+
if (!handleEvt && (time - this.touchMoveLast) * this.attr.maxframerate >= 1000) {
|
|
805
|
+
handleEvt = true;
|
|
806
|
+
}
|
|
807
|
+
if (handleEvt) {
|
|
808
|
+
this.touchMoveLast = time;
|
|
809
|
+
}
|
|
810
|
+
return handleEvt;
|
|
778
811
|
},
|
|
779
812
|
|
|
780
813
|
/**
|
|
@@ -903,7 +936,7 @@ define([
|
|
|
903
936
|
absPos,
|
|
904
937
|
v;
|
|
905
938
|
|
|
906
|
-
//
|
|
939
|
+
// Position of cursor using clientX/Y
|
|
907
940
|
absPos = Env.getPosition(e, i, this.document);
|
|
908
941
|
|
|
909
942
|
/**
|
|
@@ -912,6 +945,7 @@ define([
|
|
|
912
945
|
if (!Type.exists(this.cssTransMat)) {
|
|
913
946
|
this.updateCSSTransforms();
|
|
914
947
|
}
|
|
948
|
+
// Position relative to the top left corner
|
|
915
949
|
v = [1, absPos[0] - cPos[0], absPos[1] - cPos[1]];
|
|
916
950
|
v = Mat.matVecMult(this.cssTransMat, v);
|
|
917
951
|
v[1] /= v[0];
|
|
@@ -1129,8 +1163,8 @@ define([
|
|
|
1129
1163
|
* @param {Object} o The touch object that is dragged: {JXG.Board#touches}.
|
|
1130
1164
|
* @param {Object} evt The event object that lead to this movement.
|
|
1131
1165
|
*/
|
|
1132
|
-
twoFingerMove: function (
|
|
1133
|
-
var
|
|
1166
|
+
twoFingerMove: function (o, id, evt) {
|
|
1167
|
+
var drag;
|
|
1134
1168
|
|
|
1135
1169
|
if (Type.exists(o) && Type.exists(o.obj)) {
|
|
1136
1170
|
drag = o.obj;
|
|
@@ -1138,60 +1172,51 @@ define([
|
|
|
1138
1172
|
return;
|
|
1139
1173
|
}
|
|
1140
1174
|
|
|
1141
|
-
// New finger position
|
|
1142
|
-
np1c = new Coords(Const.COORDS_BY_SCREEN, this.getScrCoordsOfMouse(p1[0], p1[1]), this);
|
|
1143
|
-
np2c = new Coords(Const.COORDS_BY_SCREEN, this.getScrCoordsOfMouse(p2[0], p2[1]), this);
|
|
1144
|
-
|
|
1145
1175
|
if (drag.elementClass === Const.OBJECT_CLASS_LINE ||
|
|
1146
|
-
|
|
1147
|
-
this.twoFingerTouchObject(
|
|
1176
|
+
drag.type === Const.OBJECT_TYPE_POLYGON) {
|
|
1177
|
+
this.twoFingerTouchObject(o.targets, drag, id);
|
|
1148
1178
|
} else if (drag.elementClass === Const.OBJECT_CLASS_CIRCLE) {
|
|
1149
|
-
this.twoFingerTouchCircle(
|
|
1179
|
+
this.twoFingerTouchCircle(o.targets, drag, id);
|
|
1150
1180
|
}
|
|
1151
1181
|
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
o.targets[0].Yprev = np1c.scrCoords[2];
|
|
1156
|
-
o.targets[1].Xprev = np2c.scrCoords[1];
|
|
1157
|
-
o.targets[1].Yprev = np2c.scrCoords[2];
|
|
1182
|
+
if (evt) {
|
|
1183
|
+
drag.triggerEventHandlers(['touchdrag', 'drag'], [evt]);
|
|
1184
|
+
}
|
|
1158
1185
|
},
|
|
1159
1186
|
|
|
1160
1187
|
/**
|
|
1161
1188
|
* Moves, rotates and scales a line or polygon with two fingers.
|
|
1162
|
-
* @param {
|
|
1163
|
-
* @param {JXG.Coords} np2c x,y coordinates of second touch
|
|
1164
|
-
* @param {object} o The touch object that is dragged: {JXG.Board#touches}.
|
|
1189
|
+
* @param {Array} tar Array conatining touch event objects: {JXG.Board#touches.targets}.
|
|
1165
1190
|
* @param {object} drag The object that is dragged:
|
|
1166
|
-
* @param {
|
|
1191
|
+
* @param {Number} id pointerId of the event. In case of old touch event this is emulated.
|
|
1167
1192
|
*/
|
|
1168
|
-
twoFingerTouchObject: function (
|
|
1169
|
-
var
|
|
1170
|
-
nmid, omid, nd, od,
|
|
1193
|
+
twoFingerTouchObject: function (tar, drag, id) {
|
|
1194
|
+
var np, op, nd, od,
|
|
1171
1195
|
d, alpha,
|
|
1172
|
-
S, t1,
|
|
1196
|
+
S, t1, t3, t4, t5,
|
|
1173
1197
|
ar, i, len,
|
|
1174
|
-
|
|
1198
|
+
fixEl, moveEl, fix;
|
|
1175
1199
|
|
|
1176
|
-
if (Type.exists(
|
|
1177
|
-
|
|
1178
|
-
!isNaN(o.targets[0].Xprev + o.targets[0].Yprev + o.targets[1].Xprev + o.targets[1].Yprev)) {
|
|
1200
|
+
if (Type.exists(tar[0]) && Type.exists(tar[1]) &&
|
|
1201
|
+
!isNaN(tar[0].Xprev + tar[0].Yprev + tar[1].Xprev + tar[1].Yprev)) {
|
|
1179
1202
|
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1203
|
+
if (id === tar[0].num) {
|
|
1204
|
+
fixEl = tar[1];
|
|
1205
|
+
moveEl = tar[0];
|
|
1206
|
+
} else {
|
|
1207
|
+
fixEl = tar[0];
|
|
1208
|
+
moveEl = tar[1];
|
|
1209
|
+
}
|
|
1187
1210
|
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1211
|
+
fix = (new Coords(Const.COORDS_BY_SCREEN, [fixEl.Xprev, fixEl.Yprev], this)).usrCoords;
|
|
1212
|
+
// Previous finger position
|
|
1213
|
+
op = (new Coords(Const.COORDS_BY_SCREEN, [moveEl.Xprev, moveEl.Yprev], this)).usrCoords;
|
|
1214
|
+
// New finger position
|
|
1215
|
+
np = (new Coords(Const.COORDS_BY_SCREEN, [moveEl.X, moveEl.Y], this)).usrCoords;
|
|
1191
1216
|
|
|
1192
1217
|
// Old and new directions
|
|
1193
|
-
od = Mat.crossProduct(
|
|
1194
|
-
nd = Mat.crossProduct(
|
|
1218
|
+
od = Mat.crossProduct(fix, op);
|
|
1219
|
+
nd = Mat.crossProduct(fix, np);
|
|
1195
1220
|
|
|
1196
1221
|
// Intersection between the two directions
|
|
1197
1222
|
S = Mat.crossProduct(od, nd);
|
|
@@ -1201,45 +1226,18 @@ define([
|
|
|
1201
1226
|
return;
|
|
1202
1227
|
}
|
|
1203
1228
|
|
|
1204
|
-
|
|
1205
|
-
S[1] /= S[0];
|
|
1206
|
-
S[2] /= S[0];
|
|
1229
|
+
alpha = Geometry.rad(op.slice(1), fix.slice(1), np.slice(1));
|
|
1207
1230
|
|
|
1208
|
-
|
|
1209
|
-
// iOS touch events contain the angle for free
|
|
1210
|
-
alpha = evt.rotation - this.previousRotation;
|
|
1211
|
-
this.previousRotation = evt.rotation;
|
|
1212
|
-
alpha *= -pi180;
|
|
1213
|
-
} else {
|
|
1214
|
-
// For pointer events, the rotation angle has to be calculated.
|
|
1215
|
-
alpha = Geometry.rad(omid.slice(1), S.slice(1), nmid.slice(1));
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
t1 = this.create('transform', [alpha, S[1], S[2]], {type: 'rotate'});
|
|
1219
|
-
|
|
1220
|
-
// Old midpoint of fingers after first transformation:
|
|
1231
|
+
t1 = this.create('transform', [alpha, [fix[1], fix[2]]], {type: 'rotate'});
|
|
1221
1232
|
t1.update();
|
|
1222
|
-
omid = Mat.matVecMult(t1.matrix, omid);
|
|
1223
|
-
omid[1] /= omid[0];
|
|
1224
|
-
omid[2] /= omid[0];
|
|
1225
1233
|
|
|
1226
|
-
// Shift to the new mid point
|
|
1227
|
-
t2 = this.create('transform', [nmid[1] - omid[1], nmid[2] - omid[2]], {type: 'translate'});
|
|
1228
|
-
t2.update();
|
|
1229
|
-
|
|
1230
|
-
t1.melt(t2);
|
|
1231
1234
|
if (Type.evaluate(drag.visProp.scalable)) {
|
|
1232
1235
|
// Scale
|
|
1233
|
-
|
|
1234
|
-
d = evt.scale / this.previousScale;
|
|
1235
|
-
this.previousScale = evt.scale;
|
|
1236
|
-
} else {
|
|
1237
|
-
d = Geometry.distance(np1, np2) / Geometry.distance(op1, op2);
|
|
1238
|
-
}
|
|
1236
|
+
d = Geometry.distance(np, fix) / Geometry.distance(op, fix);
|
|
1239
1237
|
|
|
1240
|
-
t3 = this.create('transform', [-
|
|
1238
|
+
t3 = this.create('transform', [-fix[1], -fix[2]], {type: 'translate'});
|
|
1241
1239
|
t4 = this.create('transform', [d, d], {type: 'scale'});
|
|
1242
|
-
t5 = this.create('transform', [
|
|
1240
|
+
t5 = this.create('transform', [fix[1], fix[2]], {type: 'translate'});
|
|
1243
1241
|
t1.melt(t3).melt(t4).melt(t5);
|
|
1244
1242
|
}
|
|
1245
1243
|
|
|
@@ -1270,46 +1268,48 @@ define([
|
|
|
1270
1268
|
|
|
1271
1269
|
/*
|
|
1272
1270
|
* Moves, rotates and scales a circle with two fingers.
|
|
1273
|
-
* @param {
|
|
1274
|
-
* @param {JXG.Coords} np2c x,y coordinates of second touch
|
|
1275
|
-
* @param {object} o The touch object that is dragged: {JXG.Board#touches}.
|
|
1271
|
+
* @param {Array} tar Array conatining touch event objects: {JXG.Board#touches.targets}.
|
|
1276
1272
|
* @param {object} drag The object that is dragged:
|
|
1273
|
+
* @param {Number} id pointerId of the event. In case of old touch event this is emulated.
|
|
1277
1274
|
*/
|
|
1278
|
-
twoFingerTouchCircle: function (
|
|
1279
|
-
var
|
|
1280
|
-
d, alpha, t1, t2, t3, t4
|
|
1275
|
+
twoFingerTouchCircle: function (tar, drag, id) {
|
|
1276
|
+
var fixEl, moveEl, np, op, fix,
|
|
1277
|
+
d, alpha, t1, t2, t3, t4;
|
|
1281
1278
|
|
|
1282
|
-
if (drag.method === 'pointCircle' ||
|
|
1283
|
-
drag.method === 'pointLine') {
|
|
1279
|
+
if (drag.method === 'pointCircle' || drag.method === 'pointLine') {
|
|
1284
1280
|
return;
|
|
1285
1281
|
}
|
|
1286
1282
|
|
|
1287
|
-
if (Type.exists(
|
|
1288
|
-
|
|
1289
|
-
|
|
1283
|
+
if (Type.exists(tar[0]) && Type.exists(tar[1]) &&
|
|
1284
|
+
!isNaN(tar[0].Xprev + tar[0].Yprev + tar[1].Xprev + tar[1].Yprev)) {
|
|
1285
|
+
|
|
1286
|
+
if (id === tar[0].num) {
|
|
1287
|
+
fixEl = tar[1];
|
|
1288
|
+
moveEl = tar[0];
|
|
1289
|
+
} else {
|
|
1290
|
+
fixEl = tar[0];
|
|
1291
|
+
moveEl = tar[1];
|
|
1292
|
+
}
|
|
1290
1293
|
|
|
1291
|
-
|
|
1292
|
-
np2 = np2c.usrCoords;
|
|
1294
|
+
fix = (new Coords(Const.COORDS_BY_SCREEN, [fixEl.Xprev, fixEl.Yprev], this)).usrCoords;
|
|
1293
1295
|
// Previous finger position
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
+
op = (new Coords(Const.COORDS_BY_SCREEN, [moveEl.Xprev, moveEl.Yprev], this)).usrCoords;
|
|
1297
|
+
// New finger position
|
|
1298
|
+
np = (new Coords(Const.COORDS_BY_SCREEN, [moveEl.X, moveEl.Y], this)).usrCoords;
|
|
1296
1299
|
|
|
1297
|
-
|
|
1298
|
-
t1 = this.create('transform', [np1[1] - op1[1], np1[2] - op1[2]], {type: 'translate'});
|
|
1299
|
-
alpha = Geometry.rad(op2.slice(1), np1.slice(1), np2.slice(1));
|
|
1300
|
+
alpha = Geometry.rad(op.slice(1), fix.slice(1), np.slice(1));
|
|
1300
1301
|
|
|
1301
1302
|
// Rotate and scale by the movement of the second finger
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
t1.melt(t2)
|
|
1305
|
-
|
|
1303
|
+
t1 = this.create('transform', [-fix[1], -fix[2]], {type: 'translate'});
|
|
1304
|
+
t2 = this.create('transform', [alpha], {type: 'rotate'});
|
|
1305
|
+
t1.melt(t2);
|
|
1306
1306
|
if (Type.evaluate(drag.visProp.scalable)) {
|
|
1307
|
-
d = Geometry.distance(
|
|
1308
|
-
|
|
1309
|
-
t1.melt(
|
|
1307
|
+
d = Geometry.distance(fix, np) / Geometry.distance(fix, op);
|
|
1308
|
+
t3 = this.create('transform', [d, d], {type: 'scale'});
|
|
1309
|
+
t1.melt(t3);
|
|
1310
1310
|
}
|
|
1311
|
-
|
|
1312
|
-
t1.melt(
|
|
1311
|
+
t4 = this.create('transform', [fix[1], fix[2]], {type: 'translate'});
|
|
1312
|
+
t1.melt(t4);
|
|
1313
1313
|
|
|
1314
1314
|
if (drag.center.draggable()) {
|
|
1315
1315
|
t1.applyOnce([drag.center]);
|
|
@@ -1460,20 +1460,20 @@ define([
|
|
|
1460
1460
|
* @param {Object} evt Event from touchStartListener
|
|
1461
1461
|
* @return {Boolean} returns if the origin is moved.
|
|
1462
1462
|
*/
|
|
1463
|
-
|
|
1463
|
+
touchStartMoveOriginOneFinger: function (evt) {
|
|
1464
1464
|
var touches = evt[JXG.touchProperty],
|
|
1465
|
-
|
|
1465
|
+
conditions, pos;
|
|
1466
1466
|
|
|
1467
|
-
|
|
1467
|
+
conditions = this.attr.pan.enabled &&
|
|
1468
1468
|
!this.attr.pan.needtwofingers &&
|
|
1469
1469
|
touches.length === 1;
|
|
1470
1470
|
|
|
1471
|
-
if (
|
|
1471
|
+
if (conditions) {
|
|
1472
1472
|
pos = this.getMousePosition(evt, 0);
|
|
1473
1473
|
this.initMoveOrigin(pos[0], pos[1]);
|
|
1474
1474
|
}
|
|
1475
1475
|
|
|
1476
|
-
return
|
|
1476
|
+
return conditions;
|
|
1477
1477
|
},
|
|
1478
1478
|
|
|
1479
1479
|
/**
|
|
@@ -1714,7 +1714,6 @@ define([
|
|
|
1714
1714
|
} else {
|
|
1715
1715
|
Env.removeEvent(this.containerObj, 'pointerdown', this.pointerDownListener, this);
|
|
1716
1716
|
Env.removeEvent(moveTarget, 'pointermove', this.pointerMoveListener, this);
|
|
1717
|
-
// Env.removeEvent(this.containerObj, 'pointerout', this.pointerOutListener, this);
|
|
1718
1717
|
}
|
|
1719
1718
|
|
|
1720
1719
|
Env.removeEvent(this.containerObj, 'mousewheel', this.mouseWheelListener, this);
|
|
@@ -1722,9 +1721,10 @@ define([
|
|
|
1722
1721
|
|
|
1723
1722
|
if (this.hasPointerUp) {
|
|
1724
1723
|
if (window.navigator.msPointerEnabled) { // IE10-
|
|
1725
|
-
Env.removeEvent(this.document, 'MSPointerUp',
|
|
1724
|
+
Env.removeEvent(this.document, 'MSPointerUp', this.pointerUpListener, this);
|
|
1726
1725
|
} else {
|
|
1727
|
-
Env.removeEvent(this.document, 'pointerup',
|
|
1726
|
+
Env.removeEvent(this.document, 'pointerup', this.pointerUpListener, this);
|
|
1727
|
+
Env.removeEvent(this.document, 'pointercancel', this.pointerUpListener, this);
|
|
1728
1728
|
}
|
|
1729
1729
|
this.hasPointerUp = false;
|
|
1730
1730
|
}
|
|
@@ -1976,30 +1976,19 @@ define([
|
|
|
1976
1976
|
return false;
|
|
1977
1977
|
},
|
|
1978
1978
|
|
|
1979
|
+
/*
|
|
1980
|
+
* Pointer events
|
|
1981
|
+
*/
|
|
1982
|
+
|
|
1979
1983
|
/**
|
|
1980
|
-
* Fix for Firefox browser: When using a second finger, the
|
|
1981
|
-
* touch event for the first finger is sent again.
|
|
1982
1984
|
*
|
|
1983
|
-
*
|
|
1985
|
+
* Check if pointer event is already registered in {@link JXG.Board#_board_touches}.
|
|
1986
|
+
*
|
|
1987
|
+
* @param {Object} evt Event object
|
|
1984
1988
|
* @return {Boolean} true if down event has already been sent.
|
|
1985
1989
|
* @private
|
|
1986
1990
|
*/
|
|
1987
|
-
|
|
1988
|
-
var i;
|
|
1989
|
-
|
|
1990
|
-
for (i = 0; i < this._board_touches.length; i++) {
|
|
1991
|
-
if (this._board_touches[i].pointerId === evt.pointerId) {
|
|
1992
|
-
return true;
|
|
1993
|
-
}
|
|
1994
|
-
}
|
|
1995
|
-
|
|
1996
|
-
return false;
|
|
1997
|
-
},
|
|
1998
|
-
|
|
1999
|
-
/**
|
|
2000
|
-
* pointer-Events
|
|
2001
|
-
*/
|
|
2002
|
-
_pointerIsTouchRegistered: function(evt) {
|
|
1991
|
+
_isPointerRegistered: function(evt) {
|
|
2003
1992
|
var i, len = this._board_touches.length;
|
|
2004
1993
|
|
|
2005
1994
|
for (i = 0; i < len; i++) {
|
|
@@ -2010,7 +1999,18 @@ define([
|
|
|
2010
1999
|
return false;
|
|
2011
2000
|
},
|
|
2012
2001
|
|
|
2013
|
-
|
|
2002
|
+
/**
|
|
2003
|
+
*
|
|
2004
|
+
* Store the position of a pointer event.
|
|
2005
|
+
* If not yet done, registers a pointer event in {@link JXG.Board#_board_touches}.
|
|
2006
|
+
* Allows to follow the path of that finger on the screen.
|
|
2007
|
+
* Only two simultaneous touches are supported.
|
|
2008
|
+
*
|
|
2009
|
+
* @param {Object} evt Event object
|
|
2010
|
+
* @returns {JXG.Board} Reference to the board
|
|
2011
|
+
* @private
|
|
2012
|
+
*/
|
|
2013
|
+
_pointerStorePosition: function (evt) {
|
|
2014
2014
|
var i, found;
|
|
2015
2015
|
|
|
2016
2016
|
for (i = 0, found = false; i < this._board_touches.length; i++) {
|
|
@@ -2022,7 +2022,8 @@ define([
|
|
|
2022
2022
|
}
|
|
2023
2023
|
}
|
|
2024
2024
|
|
|
2025
|
-
|
|
2025
|
+
// Restrict the number of simultaneous touches to 2
|
|
2026
|
+
if (!found && this._board_touches.length < 2) {
|
|
2026
2027
|
this._board_touches.push({
|
|
2027
2028
|
pointerId: evt.pointerId,
|
|
2028
2029
|
clientX: evt.clientX,
|
|
@@ -2033,7 +2034,15 @@ define([
|
|
|
2033
2034
|
return this;
|
|
2034
2035
|
},
|
|
2035
2036
|
|
|
2036
|
-
|
|
2037
|
+
/**
|
|
2038
|
+
* Deregisters a pointer event in {@link JXG.Board#_board_touches}.
|
|
2039
|
+
* It happens if a finger has been lifted from the screen.
|
|
2040
|
+
*
|
|
2041
|
+
* @param {Object} evt Event object
|
|
2042
|
+
* @returns {JXG.Board} Reference to the board
|
|
2043
|
+
* @private
|
|
2044
|
+
*/
|
|
2045
|
+
_pointerRemoveTouches: function (evt) {
|
|
2037
2046
|
var i;
|
|
2038
2047
|
for (i = 0; i < this._board_touches.length; i++) {
|
|
2039
2048
|
if (this._board_touches[i].pointerId === evt.pointerId) {
|
|
@@ -2045,6 +2054,22 @@ define([
|
|
|
2045
2054
|
return this;
|
|
2046
2055
|
},
|
|
2047
2056
|
|
|
2057
|
+
/**
|
|
2058
|
+
* Remove all registered fingers from {@link JXG.Board#_board_touches}.
|
|
2059
|
+
* This might be necessary if too many fingers have been registered.
|
|
2060
|
+
* @returns {JXG.Board} Reference to the board
|
|
2061
|
+
* @private
|
|
2062
|
+
*/
|
|
2063
|
+
_pointerClearTouches: function() {
|
|
2064
|
+
if (this._board_touches.length > 0) {
|
|
2065
|
+
this.dehighlightAll();
|
|
2066
|
+
}
|
|
2067
|
+
this.updateQuality = this.BOARD_QUALITY_HIGH;
|
|
2068
|
+
this.mode = this.BOARD_MODE_NONE;
|
|
2069
|
+
this._board_touches = [];
|
|
2070
|
+
this.touches = [];
|
|
2071
|
+
},
|
|
2072
|
+
|
|
2048
2073
|
/**
|
|
2049
2074
|
* Determine which input device is used for this action.
|
|
2050
2075
|
* Possible devices are 'touch', 'pen' and 'mouse'.
|
|
@@ -2062,7 +2087,7 @@ define([
|
|
|
2062
2087
|
*/
|
|
2063
2088
|
_getPointerInputDevice: function(evt) {
|
|
2064
2089
|
if (Env.isBrowser) {
|
|
2065
|
-
if (evt.pointerType === 'touch' ||
|
|
2090
|
+
if (evt.pointerType === 'touch' || // New
|
|
2066
2091
|
(window.navigator.msMaxTouchPoints && // Old
|
|
2067
2092
|
window.navigator.msMaxTouchPoints > 1)) {
|
|
2068
2093
|
return 'touch';
|
|
@@ -2085,20 +2110,28 @@ define([
|
|
|
2085
2110
|
*/
|
|
2086
2111
|
pointerDownListener: function (evt, object) {
|
|
2087
2112
|
var i, j, k, pos, elements, sel,
|
|
2088
|
-
|
|
2113
|
+
target_obj,
|
|
2114
|
+
type = 'mouse', // Used in case of no browser
|
|
2089
2115
|
found, target;
|
|
2090
2116
|
|
|
2091
|
-
//
|
|
2092
|
-
//
|
|
2093
|
-
if (!object && this.
|
|
2117
|
+
// Fix for Firefox browser: When using a second finger, the
|
|
2118
|
+
// touch event for the first finger is sent again.
|
|
2119
|
+
if (!object && this._isPointerRegistered(evt)) {
|
|
2094
2120
|
return false;
|
|
2095
2121
|
}
|
|
2096
2122
|
|
|
2123
|
+
if (!object && evt.isPrimary) {
|
|
2124
|
+
// First finger down. To be on the safe side this._board_touches is cleared.
|
|
2125
|
+
this._pointerClearTouches();
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2097
2128
|
if (!this.hasPointerUp) {
|
|
2098
2129
|
if (window.navigator.msPointerEnabled) { // IE10-
|
|
2099
|
-
Env.addEvent(this.document, 'MSPointerUp',
|
|
2130
|
+
Env.addEvent(this.document, 'MSPointerUp', this.pointerUpListener, this);
|
|
2100
2131
|
} else {
|
|
2101
|
-
|
|
2132
|
+
// 'pointercancel' is fired e.g. if the finger leaves the browser and drags down the system menu on Android
|
|
2133
|
+
Env.addEvent(this.document, 'pointerup', this.pointerUpListener, this);
|
|
2134
|
+
Env.addEvent(this.document, 'pointercancel', this.pointerUpListener, this);
|
|
2102
2135
|
}
|
|
2103
2136
|
this.hasPointerUp = true;
|
|
2104
2137
|
}
|
|
@@ -2128,14 +2161,14 @@ define([
|
|
|
2128
2161
|
type = this._inputDevice;
|
|
2129
2162
|
this.options.precision.hasPoint = this.options.precision[type];
|
|
2130
2163
|
|
|
2131
|
-
//
|
|
2132
|
-
//
|
|
2164
|
+
// Handling of multi touch with pointer events should be easier than the touch events.
|
|
2165
|
+
// Every pointer device has its own pointerId, e.g. the mouse
|
|
2166
|
+
// always has id 1 or 0, fingers and pens get unique ids every time a pointerDown event is fired and they will
|
|
2133
2167
|
// keep this id until a pointerUp event is fired. What we have to do here is:
|
|
2134
2168
|
// 1. collect all elements under the current pointer
|
|
2135
2169
|
// 2. run through the touches control structure
|
|
2136
2170
|
// a. look for the object collected in step 1.
|
|
2137
2171
|
// b. if an object is found, check the number of pointers. If appropriate, add the pointer.
|
|
2138
|
-
|
|
2139
2172
|
pos = this.getMousePosition(evt);
|
|
2140
2173
|
|
|
2141
2174
|
// selection
|
|
@@ -2153,45 +2186,41 @@ define([
|
|
|
2153
2186
|
elements = this.initMoveObject(pos[0], pos[1], evt, type);
|
|
2154
2187
|
}
|
|
2155
2188
|
|
|
2189
|
+
target_obj = {
|
|
2190
|
+
num: evt.pointerId,
|
|
2191
|
+
X: pos[0],
|
|
2192
|
+
Y: pos[1],
|
|
2193
|
+
Xprev: NaN,
|
|
2194
|
+
Yprev: NaN,
|
|
2195
|
+
Xstart: [],
|
|
2196
|
+
Ystart: [],
|
|
2197
|
+
Zstart: []
|
|
2198
|
+
};
|
|
2199
|
+
|
|
2156
2200
|
// If no draggable object can be found, get out here immediately
|
|
2157
2201
|
if (elements.length > 0) {
|
|
2158
2202
|
// check touches structure
|
|
2159
2203
|
target = elements[elements.length - 1];
|
|
2160
2204
|
found = false;
|
|
2205
|
+
|
|
2206
|
+
// Reminder: this.touches is the list of elements which
|
|
2207
|
+
// currently "possess" a pointer (mouse, pen, finger)
|
|
2161
2208
|
for (i = 0; i < this.touches.length; i++) {
|
|
2162
|
-
//
|
|
2209
|
+
// An element receives a further touch, i.e.
|
|
2210
|
+
// the target is already in our touches array, add the pointer to the existing touch
|
|
2163
2211
|
if (this.touches[i].obj === target) {
|
|
2164
2212
|
j = i;
|
|
2165
|
-
k = this.touches[i].targets.push(
|
|
2166
|
-
num: evt.pointerId,
|
|
2167
|
-
X: pos[0],
|
|
2168
|
-
Y: pos[1],
|
|
2169
|
-
Xprev: NaN,
|
|
2170
|
-
Yprev: NaN,
|
|
2171
|
-
Xstart: [],
|
|
2172
|
-
Ystart: [],
|
|
2173
|
-
Zstart: []
|
|
2174
|
-
}) - 1;
|
|
2175
|
-
|
|
2213
|
+
k = this.touches[i].targets.push(target_obj) - 1;
|
|
2176
2214
|
found = true;
|
|
2177
2215
|
break;
|
|
2178
2216
|
}
|
|
2179
2217
|
}
|
|
2180
|
-
|
|
2181
2218
|
if (!found) {
|
|
2219
|
+
// An new element hae been touched.
|
|
2182
2220
|
k = 0;
|
|
2183
2221
|
j = this.touches.push({
|
|
2184
2222
|
obj: target,
|
|
2185
|
-
targets: [
|
|
2186
|
-
num: evt.pointerId,
|
|
2187
|
-
X: pos[0],
|
|
2188
|
-
Y: pos[1],
|
|
2189
|
-
Xprev: NaN,
|
|
2190
|
-
Yprev: NaN,
|
|
2191
|
-
Xstart: [],
|
|
2192
|
-
Ystart: [],
|
|
2193
|
-
Zstart: []
|
|
2194
|
-
}]
|
|
2223
|
+
targets: [target_obj]
|
|
2195
2224
|
}) - 1;
|
|
2196
2225
|
}
|
|
2197
2226
|
|
|
@@ -2215,19 +2244,29 @@ define([
|
|
|
2215
2244
|
evt.stopPropagation();
|
|
2216
2245
|
}
|
|
2217
2246
|
|
|
2218
|
-
if (Env.isBrowser
|
|
2247
|
+
if (!Env.isBrowser) {
|
|
2248
|
+
return false;
|
|
2249
|
+
}
|
|
2250
|
+
if (this._getPointerInputDevice(evt) !== 'touch') {
|
|
2219
2251
|
if (this.mode === this.BOARD_MODE_NONE) {
|
|
2220
2252
|
this.mouseOriginMoveStart(evt);
|
|
2221
2253
|
}
|
|
2222
2254
|
} else {
|
|
2223
|
-
this.
|
|
2255
|
+
this._pointerStorePosition(evt);
|
|
2224
2256
|
evt.touches = this._board_touches;
|
|
2225
2257
|
|
|
2226
|
-
//
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2258
|
+
// Touch events on empty areas of the board are handled here, see also touchStartListener
|
|
2259
|
+
// 1. case: one finger. If allowed, this triggers pan with one finger
|
|
2260
|
+
if (evt.touches.length === 1 &&
|
|
2261
|
+
this.mode === this.BOARD_MODE_NONE &&
|
|
2262
|
+
this.touchStartMoveOriginOneFinger(evt)) {
|
|
2263
|
+
// Empty by purpose
|
|
2264
|
+
} else if (evt.touches.length === 2 &&
|
|
2265
|
+
(this.mode === this.BOARD_MODE_NONE || this.mode === this.BOARD_MODE_MOVE_ORIGIN)
|
|
2266
|
+
) {
|
|
2267
|
+
// 2. case: two fingers: pinch to zoom or pan with two fingers needed.
|
|
2268
|
+
// This happens when the second finger hits the device. First, the
|
|
2269
|
+
// "one finger pan mode" has to be cancelled.
|
|
2231
2270
|
if (this.mode === this.BOARD_MODE_MOVE_ORIGIN) {
|
|
2232
2271
|
this.originMoveEnd();
|
|
2233
2272
|
}
|
|
@@ -2240,20 +2279,20 @@ define([
|
|
|
2240
2279
|
return false;
|
|
2241
2280
|
},
|
|
2242
2281
|
|
|
2243
|
-
/**
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
pointerOutListener: function (evt) {
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
},
|
|
2282
|
+
// /**
|
|
2283
|
+
// * Called if pointer leaves an HTML tag. It is called by the inner-most tag.
|
|
2284
|
+
// * That means, if a JSXGraph text, i.e. an HTML div, is placed close
|
|
2285
|
+
// * to the border of the board, this pointerout event will be ignored.
|
|
2286
|
+
// * @param {Event} evt
|
|
2287
|
+
// * @return {Boolean}
|
|
2288
|
+
// */
|
|
2289
|
+
// pointerOutListener: function (evt) {
|
|
2290
|
+
// if (evt.target === this.containerObj ||
|
|
2291
|
+
// (this.renderer.type === 'svg' && evt.target === this.renderer.foreignObjLayer)) {
|
|
2292
|
+
// this.pointerUpListener(evt);
|
|
2293
|
+
// }
|
|
2294
|
+
// return this.mode === this.BOARD_MODE_NONE;
|
|
2295
|
+
// },
|
|
2257
2296
|
|
|
2258
2297
|
/**
|
|
2259
2298
|
* Called periodically by the browser while the user moves a pointing device across the screen.
|
|
@@ -2261,10 +2300,10 @@ define([
|
|
|
2261
2300
|
* @returns {Boolean}
|
|
2262
2301
|
*/
|
|
2263
2302
|
pointerMoveListener: function (evt) {
|
|
2264
|
-
var i, j, pos,
|
|
2303
|
+
var i, j, pos, touchTargets,
|
|
2265
2304
|
type = 'mouse'; // in case of no browser
|
|
2266
2305
|
|
|
2267
|
-
if (this._getPointerInputDevice(evt) === 'touch' && !this.
|
|
2306
|
+
if (this._getPointerInputDevice(evt) === 'touch' && !this._isPointerRegistered(evt)) {
|
|
2268
2307
|
// Test, if there was a previous down event of this _getPointerId
|
|
2269
2308
|
// (in case it is a touch event).
|
|
2270
2309
|
// Otherwise this move event is ignored. This is necessary e.g. for sketchometry.
|
|
@@ -2298,45 +2337,37 @@ define([
|
|
|
2298
2337
|
this.triggerEventHandlers(['touchmoveselecting', 'moveselecting', 'pointermoveselecting'], [evt, this.mode]);
|
|
2299
2338
|
} else if (!this.mouseOriginMove(evt)) {
|
|
2300
2339
|
if (this.mode === this.BOARD_MODE_DRAG) {
|
|
2301
|
-
//
|
|
2340
|
+
// Run through all jsxgraph elements which are touched by at least one finger.
|
|
2302
2341
|
for (i = 0; i < this.touches.length; i++) {
|
|
2342
|
+
touchTargets = this.touches[i].targets;
|
|
2303
2343
|
// Run through all touch events which have been started on this jsxgraph element.
|
|
2304
|
-
for (j = 0; j <
|
|
2305
|
-
if (
|
|
2306
|
-
if (this.touches[i].targets.length === 1) {
|
|
2307
|
-
|
|
2308
|
-
// Touch by one finger: this is possible for all elements that can be dragged
|
|
2309
|
-
this.touches[i].targets[j].X = evt.pageX;
|
|
2310
|
-
this.touches[i].targets[j].Y = evt.pageY;
|
|
2311
|
-
pos = this.getMousePosition(evt);
|
|
2312
|
-
this.moveObject(pos[0], pos[1], this.touches[i], evt, type);
|
|
2344
|
+
for (j = 0; j < touchTargets.length; j++) {
|
|
2345
|
+
if (touchTargets[j].num === evt.pointerId) {
|
|
2313
2346
|
|
|
2314
|
-
|
|
2347
|
+
pos = this.getMousePosition(evt);
|
|
2348
|
+
touchTargets[j].X = pos[0];
|
|
2349
|
+
touchTargets[j].Y = pos[1];
|
|
2315
2350
|
|
|
2351
|
+
if (touchTargets.length === 1) {
|
|
2352
|
+
// Touch by one finger: this is possible for all elements that can be dragged
|
|
2353
|
+
this.moveObject(pos[0], pos[1], this.touches[i], evt, type);
|
|
2354
|
+
} else if (touchTargets.length === 2) {
|
|
2316
2355
|
// Touch by two fingers: e.g. moving lines
|
|
2317
|
-
this.touches[i].
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
this.getMousePosition({
|
|
2322
|
-
clientX: this.touches[i].targets[0].X,
|
|
2323
|
-
clientY: this.touches[i].targets[0].Y
|
|
2324
|
-
}),
|
|
2325
|
-
this.getMousePosition({
|
|
2326
|
-
clientX: this.touches[i].targets[1].X,
|
|
2327
|
-
clientY: this.touches[i].targets[1].Y
|
|
2328
|
-
}),
|
|
2329
|
-
this.touches[i], evt);
|
|
2356
|
+
this.twoFingerMove(this.touches[i], evt.pointerId, evt);
|
|
2357
|
+
|
|
2358
|
+
touchTargets[j].Xprev = pos[0];
|
|
2359
|
+
touchTargets[j].Yprev = pos[1];
|
|
2330
2360
|
}
|
|
2331
2361
|
|
|
2332
|
-
//
|
|
2362
|
+
// There is only one pointer in the evt object, so there's no point in looking further
|
|
2333
2363
|
break;
|
|
2334
2364
|
}
|
|
2335
2365
|
}
|
|
2336
2366
|
}
|
|
2337
2367
|
} else {
|
|
2338
2368
|
if (this._getPointerInputDevice(evt) === 'touch') {
|
|
2339
|
-
this.
|
|
2369
|
+
this._pointerStorePosition(evt);
|
|
2370
|
+
|
|
2340
2371
|
if (this._board_touches.length === 2) {
|
|
2341
2372
|
evt.touches = this._board_touches;
|
|
2342
2373
|
this.gestureChangeListener(evt);
|
|
@@ -2366,21 +2397,20 @@ define([
|
|
|
2366
2397
|
* @returns {Boolean}
|
|
2367
2398
|
*/
|
|
2368
2399
|
pointerUpListener: function (evt) {
|
|
2369
|
-
var i, j, found;
|
|
2400
|
+
var i, j, found, touchTargets;
|
|
2370
2401
|
|
|
2371
2402
|
this.triggerEventHandlers(['touchend', 'up', 'pointerup', 'MSPointerUp'], [evt]);
|
|
2372
2403
|
this.displayInfobox(false);
|
|
2373
2404
|
|
|
2374
2405
|
if (evt) {
|
|
2375
2406
|
for (i = 0; i < this.touches.length; i++) {
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
if (
|
|
2407
|
+
touchTargets = this.touches[i].targets;
|
|
2408
|
+
for (j = 0; j < touchTargets.length; j++) {
|
|
2409
|
+
if (touchTargets[j].num === evt.pointerId) {
|
|
2410
|
+
touchTargets.splice(j, 1);
|
|
2411
|
+
if (touchTargets.length === 0) {
|
|
2381
2412
|
this.touches.splice(i, 1);
|
|
2382
2413
|
}
|
|
2383
|
-
|
|
2384
2414
|
break;
|
|
2385
2415
|
}
|
|
2386
2416
|
}
|
|
@@ -2408,27 +2438,27 @@ define([
|
|
|
2408
2438
|
}
|
|
2409
2439
|
}
|
|
2410
2440
|
|
|
2411
|
-
this.
|
|
2412
|
-
|
|
2413
|
-
// if (this.touches.length === 0) {
|
|
2414
|
-
if (this._board_touches.length === 0) {
|
|
2441
|
+
// this._pointerRemoveTouches(evt);
|
|
2442
|
+
// if (this._board_touches.length === 0) {
|
|
2415
2443
|
if (this.hasPointerUp) {
|
|
2416
2444
|
if (window.navigator.msPointerEnabled) { // IE10-
|
|
2417
|
-
Env.removeEvent(this.document, 'MSPointerUp',
|
|
2445
|
+
Env.removeEvent(this.document, 'MSPointerUp', this.pointerUpListener, this);
|
|
2418
2446
|
} else {
|
|
2419
|
-
Env.removeEvent(this.document, 'pointerup',
|
|
2447
|
+
Env.removeEvent(this.document, 'pointerup', this.pointerUpListener, this);
|
|
2448
|
+
Env.removeEvent(this.document, 'pointercancel', this.pointerUpListener, this);
|
|
2420
2449
|
}
|
|
2421
2450
|
this.hasPointerUp = false;
|
|
2422
2451
|
}
|
|
2423
2452
|
|
|
2424
|
-
this.dehighlightAll();
|
|
2425
|
-
this.updateQuality = this.BOARD_QUALITY_HIGH;
|
|
2426
|
-
this.mode = this.BOARD_MODE_NONE;
|
|
2453
|
+
// this.dehighlightAll();
|
|
2454
|
+
// this.updateQuality = this.BOARD_QUALITY_HIGH;
|
|
2455
|
+
// this.mode = this.BOARD_MODE_NONE;
|
|
2427
2456
|
|
|
2428
2457
|
this.originMoveEnd();
|
|
2429
2458
|
this.update();
|
|
2430
|
-
}
|
|
2431
|
-
|
|
2459
|
+
// }
|
|
2460
|
+
// After one finger leaves the screen the gesture is stopped.
|
|
2461
|
+
this._pointerClearTouches();
|
|
2432
2462
|
return true;
|
|
2433
2463
|
},
|
|
2434
2464
|
|
|
@@ -2446,7 +2476,7 @@ define([
|
|
|
2446
2476
|
eps = this.options.precision.touch,
|
|
2447
2477
|
obj, found, targets,
|
|
2448
2478
|
evtTouches = evt[JXG.touchProperty],
|
|
2449
|
-
target;
|
|
2479
|
+
target, touchTargets;
|
|
2450
2480
|
|
|
2451
2481
|
if (!this.hasTouchEnd) {
|
|
2452
2482
|
Env.addEvent(this.document, 'touchend', this.touchEndListener, this);
|
|
@@ -2494,19 +2524,19 @@ define([
|
|
|
2494
2524
|
}
|
|
2495
2525
|
|
|
2496
2526
|
for (i = 0; i < this.touches.length; i++) {
|
|
2497
|
-
|
|
2498
|
-
|
|
2527
|
+
touchTargets = this.touches[i].targets;
|
|
2528
|
+
for (j = 0; j < touchTargets.length; j++) {
|
|
2529
|
+
touchTargets[j].num = -1;
|
|
2499
2530
|
eps = this.options.precision.touch;
|
|
2500
2531
|
|
|
2501
2532
|
do {
|
|
2502
2533
|
for (k = 0; k < evtTouches.length; k++) {
|
|
2503
2534
|
// find the new targettouches
|
|
2504
|
-
if (Math.abs(Math.pow(evtTouches[k].screenX -
|
|
2505
|
-
Math.pow(evtTouches[k].screenY -
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
this.touches[i].targets[j].Y = evtTouches[k].screenY;
|
|
2535
|
+
if (Math.abs(Math.pow(evtTouches[k].screenX - touchTargets[j].X, 2) +
|
|
2536
|
+
Math.pow(evtTouches[k].screenY - touchTargets[j].Y, 2)) < eps * eps) {
|
|
2537
|
+
touchTargets[j].num = k;
|
|
2538
|
+
touchTargets[j].X = evtTouches[k].screenX;
|
|
2539
|
+
touchTargets[j].Y = evtTouches[k].screenY;
|
|
2510
2540
|
evtTouches[k].jxg_isused = true;
|
|
2511
2541
|
break;
|
|
2512
2542
|
}
|
|
@@ -2514,13 +2544,13 @@ define([
|
|
|
2514
2544
|
|
|
2515
2545
|
eps *= 2;
|
|
2516
2546
|
|
|
2517
|
-
} while (
|
|
2547
|
+
} while (touchTargets[j].num === -1 &&
|
|
2518
2548
|
eps < this.options.precision.touchMax);
|
|
2519
2549
|
|
|
2520
|
-
if (
|
|
2550
|
+
if (touchTargets[j].num === -1) {
|
|
2521
2551
|
JXG.debug('i couldn\'t find a targettouches for target no ' + j + ' on ' + this.touches[i].obj.name + ' (' + this.touches[i].obj.id + '). Removed the target.');
|
|
2522
2552
|
JXG.debug('eps = ' + eps + ', touchMax = ' + Options.precision.touchMax);
|
|
2523
|
-
|
|
2553
|
+
touchTargets.splice(i, 1);
|
|
2524
2554
|
}
|
|
2525
2555
|
|
|
2526
2556
|
}
|
|
@@ -2546,20 +2576,22 @@ define([
|
|
|
2546
2576
|
elements = this.initMoveObject(pos[0], pos[1], evt, 'touch');
|
|
2547
2577
|
if (elements.length !== 0) {
|
|
2548
2578
|
obj = elements[elements.length - 1];
|
|
2579
|
+
target = {num: i,
|
|
2580
|
+
X: evtTouches[i].screenX,
|
|
2581
|
+
Y: evtTouches[i].screenY,
|
|
2582
|
+
Xprev: NaN,
|
|
2583
|
+
Yprev: NaN,
|
|
2584
|
+
Xstart: [],
|
|
2585
|
+
Ystart: [],
|
|
2586
|
+
Zstart: []
|
|
2587
|
+
};
|
|
2549
2588
|
|
|
2550
2589
|
if (Type.isPoint(obj) ||
|
|
2551
2590
|
obj.elementClass === Const.OBJECT_CLASS_TEXT ||
|
|
2552
2591
|
obj.type === Const.OBJECT_TYPE_TICKS ||
|
|
2553
2592
|
obj.type === Const.OBJECT_TYPE_IMAGE) {
|
|
2554
|
-
//
|
|
2555
|
-
targets = [
|
|
2556
|
-
X: evtTouches[i].screenX,
|
|
2557
|
-
Y: evtTouches[i].screenY,
|
|
2558
|
-
Xprev: NaN,
|
|
2559
|
-
Yprev: NaN,
|
|
2560
|
-
Xstart: [],
|
|
2561
|
-
Ystart: [],
|
|
2562
|
-
Zstart: [] }];
|
|
2593
|
+
// It's a point, so it's single touch, so we just push it to our touches
|
|
2594
|
+
targets = [obj];
|
|
2563
2595
|
|
|
2564
2596
|
// For the UNDO/REDO of object moves
|
|
2565
2597
|
this.saveStartPos(obj, targets[0]);
|
|
@@ -2579,15 +2611,6 @@ define([
|
|
|
2579
2611
|
found = true;
|
|
2580
2612
|
// only add it, if we don't have two targets in there already
|
|
2581
2613
|
if (this.touches[j].targets.length === 1) {
|
|
2582
|
-
target = {num: i,
|
|
2583
|
-
X: evtTouches[i].screenX,
|
|
2584
|
-
Y: evtTouches[i].screenY,
|
|
2585
|
-
Xprev: NaN,
|
|
2586
|
-
Yprev: NaN,
|
|
2587
|
-
Xstart: [],
|
|
2588
|
-
Ystart: [],
|
|
2589
|
-
Zstart: [] };
|
|
2590
|
-
|
|
2591
2614
|
// For the UNDO/REDO of object moves
|
|
2592
2615
|
this.saveStartPos(obj, target);
|
|
2593
2616
|
this.touches[j].targets.push(target);
|
|
@@ -2601,14 +2624,7 @@ define([
|
|
|
2601
2624
|
// IF there is a second touch targetting this line, we will find it later on, and then add it to
|
|
2602
2625
|
// the touches control object.
|
|
2603
2626
|
if (!found) {
|
|
2604
|
-
targets = [
|
|
2605
|
-
X: evtTouches[i].screenX,
|
|
2606
|
-
Y: evtTouches[i].screenY,
|
|
2607
|
-
Xprev: NaN,
|
|
2608
|
-
Yprev: NaN,
|
|
2609
|
-
Xstart: [],
|
|
2610
|
-
Ystart: [],
|
|
2611
|
-
Zstart: [] }];
|
|
2627
|
+
targets = [target];
|
|
2612
2628
|
|
|
2613
2629
|
// For the UNDO/REDO of object moves
|
|
2614
2630
|
this.saveStartPos(obj, targets[0]);
|
|
@@ -2629,12 +2645,10 @@ define([
|
|
|
2629
2645
|
|
|
2630
2646
|
// Touch events on empty areas of the board are handled here:
|
|
2631
2647
|
// 1. case: one finger. If allowed, this triggers pan with one finger
|
|
2632
|
-
if (this.mode === this.BOARD_MODE_NONE && this.
|
|
2648
|
+
if (evtTouches.length === 1 && this.mode === this.BOARD_MODE_NONE && this.touchStartMoveOriginOneFinger(evt)) {
|
|
2633
2649
|
} else if (evtTouches.length === 2 &&
|
|
2634
|
-
(this.mode === this.BOARD_MODE_NONE ||
|
|
2635
|
-
|
|
2636
|
-
(this.mode === this.BOARD_MODE_DRAG && this.touches.length == 1) */
|
|
2637
|
-
)) {
|
|
2650
|
+
(this.mode === this.BOARD_MODE_NONE || this.mode === this.BOARD_MODE_MOVE_ORIGIN)
|
|
2651
|
+
) {
|
|
2638
2652
|
// 2. case: two fingers: pinch to zoom or pan with two fingers needed.
|
|
2639
2653
|
// This happens when the second finger hits the device. First, the
|
|
2640
2654
|
// "one finger pan mode" has to be cancelled.
|
|
@@ -2657,7 +2671,8 @@ define([
|
|
|
2657
2671
|
* @returns {Boolean}
|
|
2658
2672
|
*/
|
|
2659
2673
|
touchMoveListener: function (evt) {
|
|
2660
|
-
var i, pos1, pos2,
|
|
2674
|
+
var i, pos1, pos2,
|
|
2675
|
+
touchTargets,
|
|
2661
2676
|
evtTouches = evt[JXG.touchProperty];
|
|
2662
2677
|
|
|
2663
2678
|
if (!this.checkFrameRate(evt)) {
|
|
@@ -2694,40 +2709,52 @@ define([
|
|
|
2694
2709
|
// Runs over through all elements which are touched
|
|
2695
2710
|
// by at least one finger.
|
|
2696
2711
|
for (i = 0; i < this.touches.length; i++) {
|
|
2697
|
-
|
|
2712
|
+
touchTargets = this.touches[i].targets;
|
|
2713
|
+
if (touchTargets.length === 1) {
|
|
2714
|
+
|
|
2715
|
+
|
|
2698
2716
|
// Touch by one finger: this is possible for all elements that can be dragged
|
|
2699
|
-
if (evtTouches[
|
|
2700
|
-
pos1 = this.getMousePosition(evt,
|
|
2717
|
+
if (evtTouches[touchTargets[0].num]) {
|
|
2718
|
+
pos1 = this.getMousePosition(evt, touchTargets[0].num);
|
|
2701
2719
|
if (pos1[0] < 0 || pos1[0] > this.canvasWidth ||
|
|
2702
2720
|
pos1[1] < 0 || pos1[1] > this.canvasHeight) {
|
|
2703
2721
|
return;
|
|
2704
2722
|
}
|
|
2705
|
-
|
|
2706
|
-
|
|
2723
|
+
touchTargets[0].X = pos1[0];
|
|
2724
|
+
touchTargets[0].Y = pos1[1];
|
|
2707
2725
|
this.moveObject(pos1[0], pos1[1], this.touches[i], evt, 'touch');
|
|
2708
2726
|
}
|
|
2709
2727
|
|
|
2710
|
-
} else if (
|
|
2711
|
-
|
|
2712
|
-
|
|
2728
|
+
} else if (touchTargets.length === 2 &&
|
|
2729
|
+
touchTargets[0].num > -1 &&
|
|
2730
|
+
touchTargets[1].num > -1) {
|
|
2731
|
+
|
|
2713
2732
|
// Touch by two fingers: moving lines, ...
|
|
2714
|
-
if (evtTouches[
|
|
2715
|
-
evtTouches[
|
|
2733
|
+
if (evtTouches[touchTargets[0].num] &&
|
|
2734
|
+
evtTouches[touchTargets[1].num]) {
|
|
2716
2735
|
|
|
2717
2736
|
// Get coordinates of the two touches
|
|
2718
|
-
pos1 = this.getMousePosition(evt,
|
|
2719
|
-
pos2 = this.getMousePosition(evt,
|
|
2737
|
+
pos1 = this.getMousePosition(evt, touchTargets[0].num);
|
|
2738
|
+
pos2 = this.getMousePosition(evt, touchTargets[1].num);
|
|
2720
2739
|
if (pos1[0] < 0 || pos1[0] > this.canvasWidth ||
|
|
2721
2740
|
pos1[1] < 0 || pos1[1] > this.canvasHeight ||
|
|
2722
2741
|
pos2[0] < 0 || pos2[0] > this.canvasWidth ||
|
|
2723
2742
|
pos2[1] < 0 || pos2[1] > this.canvasHeight) {
|
|
2724
2743
|
return;
|
|
2725
2744
|
}
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2745
|
+
|
|
2746
|
+
touchTargets[0].X = pos1[0];
|
|
2747
|
+
touchTargets[0].Y = pos1[1];
|
|
2748
|
+
touchTargets[1].X = pos2[0];
|
|
2749
|
+
touchTargets[1].Y = pos2[1];
|
|
2750
|
+
|
|
2751
|
+
this.twoFingerMove(this.touches[i], touchTargets[0].num, evt);
|
|
2752
|
+
this.twoFingerMove(this.touches[i], touchTargets[1].num);
|
|
2753
|
+
|
|
2754
|
+
touchTargets[0].Xprev = pos1[0];
|
|
2755
|
+
touchTargets[0].Yprev = pos1[1];
|
|
2756
|
+
touchTargets[1].Xprev = pos2[0];
|
|
2757
|
+
touchTargets[1].Yprev = pos2[1];
|
|
2731
2758
|
}
|
|
2732
2759
|
}
|
|
2733
2760
|
}
|
|
@@ -2762,7 +2789,8 @@ define([
|
|
|
2762
2789
|
var i, j, k,
|
|
2763
2790
|
eps = this.options.precision.touch,
|
|
2764
2791
|
tmpTouches = [], found, foundNumber,
|
|
2765
|
-
evtTouches = evt && evt[JXG.touchProperty]
|
|
2792
|
+
evtTouches = evt && evt[JXG.touchProperty],
|
|
2793
|
+
touchTargets;
|
|
2766
2794
|
|
|
2767
2795
|
this.triggerEventHandlers(['touchend', 'up'], [evt]);
|
|
2768
2796
|
this.displayInfobox(false);
|
|
@@ -2798,15 +2826,16 @@ define([
|
|
|
2798
2826
|
// could all targets of the current this.touches.obj be assigned to targettouches?
|
|
2799
2827
|
found = false;
|
|
2800
2828
|
foundNumber = 0;
|
|
2829
|
+
touchTargets = tmpTouches[i].targets;
|
|
2801
2830
|
|
|
2802
|
-
for (j = 0; j <
|
|
2803
|
-
|
|
2831
|
+
for (j = 0; j < touchTargets.length; j++) {
|
|
2832
|
+
touchTargets[j].found = false;
|
|
2804
2833
|
for (k = 0; k < evtTouches.length; k++) {
|
|
2805
|
-
if (Math.abs(Math.pow(evtTouches[k].screenX -
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2834
|
+
if (Math.abs(Math.pow(evtTouches[k].screenX - touchTargets[j].X, 2) + Math.pow(evtTouches[k].screenY - touchTargets[j].Y, 2)) < eps * eps) {
|
|
2835
|
+
touchTargets[j].found = true;
|
|
2836
|
+
touchTargets[j].num = k;
|
|
2837
|
+
touchTargets[j].X = evtTouches[k].screenX;
|
|
2838
|
+
touchTargets[j].Y = evtTouches[k].screenY;
|
|
2810
2839
|
foundNumber += 1;
|
|
2811
2840
|
break;
|
|
2812
2841
|
}
|
|
@@ -2814,9 +2843,9 @@ define([
|
|
|
2814
2843
|
}
|
|
2815
2844
|
|
|
2816
2845
|
if (Type.isPoint(tmpTouches[i].obj)) {
|
|
2817
|
-
found = (
|
|
2846
|
+
found = (touchTargets[0] && touchTargets[0].found);
|
|
2818
2847
|
} else if (tmpTouches[i].obj.elementClass === Const.OBJECT_CLASS_LINE) {
|
|
2819
|
-
found = (
|
|
2848
|
+
found = (touchTargets[0] && touchTargets[0].found) || (touchTargets[1] && touchTargets[1].found);
|
|
2820
2849
|
} else if (tmpTouches[i].obj.elementClass === Const.OBJECT_CLASS_CIRCLE) {
|
|
2821
2850
|
found = foundNumber === 1 || foundNumber === 3;
|
|
2822
2851
|
}
|
|
@@ -2828,17 +2857,17 @@ define([
|
|
|
2828
2857
|
targets: []
|
|
2829
2858
|
});
|
|
2830
2859
|
|
|
2831
|
-
for (j = 0; j <
|
|
2832
|
-
if (
|
|
2860
|
+
for (j = 0; j < touchTargets.length; j++) {
|
|
2861
|
+
if (touchTargets[j].found) {
|
|
2833
2862
|
this.touches[this.touches.length - 1].targets.push({
|
|
2834
|
-
num:
|
|
2835
|
-
X:
|
|
2836
|
-
Y:
|
|
2863
|
+
num: touchTargets[j].num,
|
|
2864
|
+
X: touchTargets[j].screenX,
|
|
2865
|
+
Y: touchTargets[j].screenY,
|
|
2837
2866
|
Xprev: NaN,
|
|
2838
2867
|
Yprev: NaN,
|
|
2839
|
-
Xstart:
|
|
2840
|
-
Ystart:
|
|
2841
|
-
Zstart:
|
|
2868
|
+
Xstart: touchTargets[j].Xstart,
|
|
2869
|
+
Ystart: touchTargets[j].Ystart,
|
|
2870
|
+
Zstart: touchTargets[j].Zstart
|
|
2842
2871
|
});
|
|
2843
2872
|
}
|
|
2844
2873
|
}
|
|
@@ -2928,6 +2957,7 @@ define([
|
|
|
2928
2957
|
this.mode = this.BOARD_MODE_NONE;
|
|
2929
2958
|
result = true;
|
|
2930
2959
|
} else {
|
|
2960
|
+
/** @ignore */
|
|
2931
2961
|
this.mouse = {
|
|
2932
2962
|
obj: null,
|
|
2933
2963
|
targets: [{
|
|
@@ -3052,6 +3082,7 @@ define([
|
|
|
3052
3082
|
}
|
|
3053
3083
|
|
|
3054
3084
|
// release dragged mouse object
|
|
3085
|
+
/** @ignore */
|
|
3055
3086
|
this.mouse = null;
|
|
3056
3087
|
},
|
|
3057
3088
|
|
|
@@ -3080,6 +3111,7 @@ define([
|
|
|
3080
3111
|
evt.preventDefault();
|
|
3081
3112
|
return false;
|
|
3082
3113
|
},
|
|
3114
|
+
|
|
3083
3115
|
/**
|
|
3084
3116
|
* Allow moving of JSXGraph elements with arrow keys
|
|
3085
3117
|
* and zooming of the construction with + / -.
|
|
@@ -3096,7 +3128,11 @@ define([
|
|
|
3096
3128
|
*/
|
|
3097
3129
|
keyDownListener: function (evt) {
|
|
3098
3130
|
var id_node = evt.target.id,
|
|
3099
|
-
id, el,
|
|
3131
|
+
id, el, res,
|
|
3132
|
+
sX = 0,
|
|
3133
|
+
sY = 0,
|
|
3134
|
+
// dx, dy are provided in screen units and
|
|
3135
|
+
// are converted to user coordinates
|
|
3100
3136
|
dx = Type.evaluate(this.attr.keyboard.dx) / this.unitX,
|
|
3101
3137
|
dy = Type.evaluate(this.attr.keyboard.dy) / this.unitY,
|
|
3102
3138
|
doZoom = false,
|
|
@@ -3130,6 +3166,40 @@ define([
|
|
|
3130
3166
|
this.clickRightArrow();
|
|
3131
3167
|
}
|
|
3132
3168
|
} else {
|
|
3169
|
+
// Adapt dx, dy to snapToGrid and attractToGrid
|
|
3170
|
+
// snapToGrid has priority.
|
|
3171
|
+
if (Type.exists(el.visProp)) {
|
|
3172
|
+
if (Type.exists(el.visProp.snaptogrid) &&
|
|
3173
|
+
el.visProp.snaptogrid &&
|
|
3174
|
+
Type.evaluate(el.visProp.snapsizex) &&
|
|
3175
|
+
Type.evaluate(el.visProp.snapsizey)) {
|
|
3176
|
+
|
|
3177
|
+
// Adapt dx, dy such that snapToGrid is possible
|
|
3178
|
+
res = el.getSnapSizes();
|
|
3179
|
+
sX = res[0];
|
|
3180
|
+
sY = res[1];
|
|
3181
|
+
dx = Math.max(sX, dx);
|
|
3182
|
+
dy = Math.max(sY, dy);
|
|
3183
|
+
|
|
3184
|
+
} else if (Type.exists(el.visProp.attracttogrid) &&
|
|
3185
|
+
el.visProp.attracttogrid &&
|
|
3186
|
+
Type.evaluate(el.visProp.attractordistance) &&
|
|
3187
|
+
Type.evaluate(el.visProp.attractorunit)) {
|
|
3188
|
+
|
|
3189
|
+
// Adapt dx, dy such that attractToGrid is possible
|
|
3190
|
+
sX = 1.1 * Type.evaluate(el.visProp.attractordistance);
|
|
3191
|
+
sY = sX;
|
|
3192
|
+
|
|
3193
|
+
if (Type.evaluate(el.visProp.attractorunit) === 'screen') {
|
|
3194
|
+
sX /= this.unitX;
|
|
3195
|
+
sY /= this.unitX;
|
|
3196
|
+
}
|
|
3197
|
+
dx = Math.max(sX, dx);
|
|
3198
|
+
dy = Math.max(sY, dy);
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
}
|
|
3202
|
+
|
|
3133
3203
|
if (evt.keyCode === 38) { // up
|
|
3134
3204
|
dir = [0, dy];
|
|
3135
3205
|
} else if (evt.keyCode === 40) { // down
|
|
@@ -3144,7 +3214,7 @@ define([
|
|
|
3144
3214
|
this.zoomIn();
|
|
3145
3215
|
} else if (doZoom && evt.key === '-') { // -
|
|
3146
3216
|
this.zoomOut();
|
|
3147
|
-
} else if (doZoom && evt.key === 'o') {
|
|
3217
|
+
} else if (doZoom && evt.key === 'o') { // o
|
|
3148
3218
|
this.zoom100();
|
|
3149
3219
|
}
|
|
3150
3220
|
if (dir && el.isDraggable &&
|
|
@@ -3163,6 +3233,10 @@ define([
|
|
|
3163
3233
|
// For coordsElement setPosition has to call setPositionDirectly.
|
|
3164
3234
|
// Otherwise the position is set by a translation.
|
|
3165
3235
|
el.setPosition(JXG.COORDS_BY_USER, dir);
|
|
3236
|
+
if (Type.exists(el.coords)) {
|
|
3237
|
+
this.updateInfobox(el);
|
|
3238
|
+
}
|
|
3239
|
+
this.triggerEventHandlers(['hit'], [evt, el]);
|
|
3166
3240
|
}
|
|
3167
3241
|
}
|
|
3168
3242
|
|
|
@@ -3194,6 +3268,10 @@ define([
|
|
|
3194
3268
|
if (Type.exists(el.highlight)) {
|
|
3195
3269
|
el.highlight(true);
|
|
3196
3270
|
}
|
|
3271
|
+
if (Type.exists(el.coords)) {
|
|
3272
|
+
this.updateInfobox(el);
|
|
3273
|
+
}
|
|
3274
|
+
this.triggerEventHandlers(['hit'], [evt, el]);
|
|
3197
3275
|
},
|
|
3198
3276
|
|
|
3199
3277
|
/**
|
|
@@ -3206,7 +3284,7 @@ define([
|
|
|
3206
3284
|
*
|
|
3207
3285
|
* @param {Event} evt The browser's event object
|
|
3208
3286
|
*/
|
|
3209
|
-
|
|
3287
|
+
keyFocusOutListener: function (evt) {
|
|
3210
3288
|
if (!this.attr.keyboard.enabled) {
|
|
3211
3289
|
return false;
|
|
3212
3290
|
}
|
|
@@ -3216,6 +3294,7 @@ define([
|
|
|
3216
3294
|
// id = id_node.replace(this.containerObj.id + '_', '');
|
|
3217
3295
|
// el = this.select(id);
|
|
3218
3296
|
this.dehighlightAll();
|
|
3297
|
+
this.displayInfobox(false);
|
|
3219
3298
|
},
|
|
3220
3299
|
|
|
3221
3300
|
/**
|
|
@@ -3232,34 +3311,45 @@ define([
|
|
|
3232
3311
|
*
|
|
3233
3312
|
*/
|
|
3234
3313
|
updateContainerDims: function() {
|
|
3235
|
-
var
|
|
3314
|
+
var w, h,
|
|
3315
|
+
bb, css;
|
|
3236
3316
|
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3317
|
+
// Get size of the board's container div
|
|
3318
|
+
bb = this.containerObj.getBoundingClientRect();
|
|
3319
|
+
w = bb.width;
|
|
3320
|
+
h = bb.height;
|
|
3321
|
+
|
|
3322
|
+
// Subtract the border size
|
|
3323
|
+
if (window && window.getComputedStyle) {
|
|
3324
|
+
css = window.getComputedStyle(this.containerObj, null);
|
|
3325
|
+
w -= parseFloat(css.getPropertyValue('border-left-width')) + parseFloat(css.getPropertyValue('border-right-width'));
|
|
3326
|
+
h -= parseFloat(css.getPropertyValue('border-top-width')) + parseFloat(css.getPropertyValue('border-bottom-width'));
|
|
3327
|
+
}
|
|
3328
|
+
|
|
3329
|
+
// If div is invisible - do nothing
|
|
3330
|
+
if (w <= 0 || h <= 0) {
|
|
3241
3331
|
return;
|
|
3242
3332
|
}
|
|
3243
3333
|
|
|
3244
3334
|
// If bounding box is not yet initialized, do it now.
|
|
3245
3335
|
if (isNaN(this.getBoundingBox()[0])) {
|
|
3246
|
-
this.setBoundingBox(this.attr.boundingbox);
|
|
3336
|
+
this.setBoundingBox(this.attr.boundingbox, this.keepaspectratio, 'keep');
|
|
3247
3337
|
}
|
|
3248
3338
|
|
|
3249
|
-
//
|
|
3339
|
+
// Do nothing if the dimension did not change since being visible
|
|
3250
3340
|
// the last time. Note that if the div had display:none in the mean time,
|
|
3251
|
-
// we did not store this.
|
|
3252
|
-
if (Type.exists(this.
|
|
3253
|
-
this.
|
|
3341
|
+
// we did not store this._prevDim.
|
|
3342
|
+
if (Type.exists(this._prevDim) &&
|
|
3343
|
+
this._prevDim.w === w && this._prevDim.h === h) {
|
|
3254
3344
|
return;
|
|
3255
3345
|
}
|
|
3256
|
-
|
|
3257
|
-
//
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3346
|
+
|
|
3347
|
+
// Set the size of the SVG or canvas element
|
|
3348
|
+
this.resizeContainer(w, h, true);
|
|
3349
|
+
this._prevDim = {
|
|
3350
|
+
w: w,
|
|
3351
|
+
h: h
|
|
3352
|
+
};
|
|
3263
3353
|
},
|
|
3264
3354
|
|
|
3265
3355
|
/**
|
|
@@ -3288,8 +3378,13 @@ define([
|
|
|
3288
3378
|
if (!that._isResizing) {
|
|
3289
3379
|
that._isResizing = true;
|
|
3290
3380
|
window.setTimeout(function() {
|
|
3291
|
-
|
|
3292
|
-
|
|
3381
|
+
try {
|
|
3382
|
+
that.updateContainerDims();
|
|
3383
|
+
} catch (err) {
|
|
3384
|
+
that.stopResizeObserver();
|
|
3385
|
+
} finally {
|
|
3386
|
+
that._isResizing = false;
|
|
3387
|
+
}
|
|
3293
3388
|
}, that.attr.resize.throttle);
|
|
3294
3389
|
}
|
|
3295
3390
|
});
|
|
@@ -3378,13 +3473,12 @@ define([
|
|
|
3378
3473
|
this.intersectionObserver = new IntersectionObserver(function(entries) {
|
|
3379
3474
|
// If bounding box is not yet initialized, do it now.
|
|
3380
3475
|
if (isNaN(that.getBoundingBox()[0])) {
|
|
3381
|
-
that.setBoundingBox(that.attr.boundingbox);
|
|
3382
3476
|
that.updateContainerDims();
|
|
3383
3477
|
}
|
|
3384
3478
|
}, options);
|
|
3385
3479
|
this.intersectionObserver.observe(that.containerObj);
|
|
3386
3480
|
} catch (err) {
|
|
3387
|
-
console.log('
|
|
3481
|
+
console.log('JSXGraph: IntersectionObserver not available in this browser.');
|
|
3388
3482
|
}
|
|
3389
3483
|
},
|
|
3390
3484
|
|
|
@@ -3419,7 +3513,7 @@ define([
|
|
|
3419
3513
|
* Infobox close to points in which the points' coordinates are displayed.
|
|
3420
3514
|
* This is simply a JXG.Text element. Access through board.infobox.
|
|
3421
3515
|
* Uses CSS class .JXGinfobox.
|
|
3422
|
-
* @type
|
|
3516
|
+
* @type JXG.Text
|
|
3423
3517
|
*
|
|
3424
3518
|
*/
|
|
3425
3519
|
this.infobox = this.create('text', [0, 0, '0,0'], attr);
|
|
@@ -3611,11 +3705,11 @@ define([
|
|
|
3611
3705
|
* // Shorter version:
|
|
3612
3706
|
* //somePoint = board.create('point', a, {name:'SomePoint',size:4});
|
|
3613
3707
|
* });
|
|
3614
|
-
*
|
|
3708
|
+
*
|
|
3615
3709
|
* })();
|
|
3616
3710
|
*
|
|
3617
3711
|
* </script><pre>
|
|
3618
|
-
*
|
|
3712
|
+
*
|
|
3619
3713
|
* @see JXG.Board#getScrCoordsOfMouse
|
|
3620
3714
|
* @see JXG.Board#getAllUnderMouse
|
|
3621
3715
|
*/
|
|
@@ -3737,7 +3831,7 @@ define([
|
|
|
3737
3831
|
addConditions: function (str) {
|
|
3738
3832
|
var term, m, left, right, name, el, property,
|
|
3739
3833
|
functions = [],
|
|
3740
|
-
plaintext = 'var el, x, y, c, rgbo;\n',
|
|
3834
|
+
// plaintext = 'var el, x, y, c, rgbo;\n',
|
|
3741
3835
|
i = str.indexOf('<data>'),
|
|
3742
3836
|
j = str.indexOf('<' + '/data>'),
|
|
3743
3837
|
|
|
@@ -3821,7 +3915,7 @@ define([
|
|
|
3821
3915
|
if (!Type.exists(this.elementsByName[name])) {
|
|
3822
3916
|
JXG.debug("debug conditions: |" + name + "| undefined");
|
|
3823
3917
|
} else {
|
|
3824
|
-
plaintext += "el = this.objects[\"" + el.id + "\"];\n";
|
|
3918
|
+
// plaintext += "el = this.objects[\"" + el.id + "\"];\n";
|
|
3825
3919
|
|
|
3826
3920
|
switch (property) {
|
|
3827
3921
|
case 'x':
|
|
@@ -3948,9 +4042,7 @@ define([
|
|
|
3948
4042
|
tr = (bb[1] - y) / (bb[1] - bb[3]);
|
|
3949
4043
|
}
|
|
3950
4044
|
|
|
3951
|
-
this.setBoundingBox([bb[0] + dX * lr, bb[1] - dY * tr, bb[2] - dX * (1 - lr), bb[3] + dY * (1 - tr)],
|
|
3952
|
-
this.zoomX *= zX;
|
|
3953
|
-
this.zoomY *= zY;
|
|
4045
|
+
this.setBoundingBox([bb[0] + dX * lr, bb[1] - dY * tr, bb[2] - dX * (1 - lr), bb[3] + dY * (1 - tr)], this.keepaspectratio, 'update');
|
|
3954
4046
|
return this.applyZoom();
|
|
3955
4047
|
},
|
|
3956
4048
|
|
|
@@ -3981,25 +4073,32 @@ define([
|
|
|
3981
4073
|
tr = (bb[1] - y) / (bb[1] - bb[3]);
|
|
3982
4074
|
}
|
|
3983
4075
|
|
|
3984
|
-
this.setBoundingBox([bb[0] + dX * lr, bb[1] - dY * tr, bb[2] - dX * (1 - lr), bb[3] + dY * (1 - tr)],
|
|
3985
|
-
this.zoomX /= zX;
|
|
3986
|
-
this.zoomY /= zY;
|
|
4076
|
+
this.setBoundingBox([bb[0] + dX * lr, bb[1] - dY * tr, bb[2] - dX * (1 - lr), bb[3] + dY * (1 - tr)], this.keepaspectratio, 'update');
|
|
3987
4077
|
|
|
3988
4078
|
return this.applyZoom();
|
|
3989
4079
|
},
|
|
3990
4080
|
|
|
3991
4081
|
/**
|
|
3992
|
-
*
|
|
4082
|
+
* Reset the zoom level to the original zoom level from initBoard();
|
|
4083
|
+
* Additionally, if the board as been initialized with a boundingBox (which is the default),
|
|
4084
|
+
* restore the viewport to the original viewport during initialization. Otherwise,
|
|
4085
|
+
* (i.e. if the board as been initialized with unitX/Y and originX/Y),
|
|
4086
|
+
* just set the zoom level to 100%.
|
|
4087
|
+
*
|
|
3993
4088
|
* @returns {JXG.Board} Reference to the board
|
|
3994
4089
|
*/
|
|
3995
4090
|
zoom100: function () {
|
|
3996
|
-
var bb
|
|
3997
|
-
dX = (bb[2] - bb[0]) * (1.0 - this.zoomX) * 0.5,
|
|
3998
|
-
dY = (bb[1] - bb[3]) * (1.0 - this.zoomY) * 0.5;
|
|
4091
|
+
var bb, dX, dY;
|
|
3999
4092
|
|
|
4000
|
-
this.
|
|
4001
|
-
|
|
4002
|
-
|
|
4093
|
+
if (Type.exists(this.attr.boundingbox)) {
|
|
4094
|
+
this.setBoundingBox(this.attr.boundingbox, this.keepaspectratio, 'reset');
|
|
4095
|
+
} else {
|
|
4096
|
+
// Board has been set up with unitX/Y and originX/Y
|
|
4097
|
+
bb = this.getBoundingBox();
|
|
4098
|
+
dX = (bb[2] - bb[0]) * (1.0 - this.zoomX) * 0.5;
|
|
4099
|
+
dY = (bb[1] - bb[3]) * (1.0 - this.zoomY) * 0.5;
|
|
4100
|
+
this.setBoundingBox([bb[0] + dX, bb[1] - dY, bb[2] - dX, bb[3] + dY], this.keepaspectratio, 'reset');
|
|
4101
|
+
}
|
|
4003
4102
|
return this.applyZoom();
|
|
4004
4103
|
},
|
|
4005
4104
|
|
|
@@ -4036,10 +4135,7 @@ define([
|
|
|
4036
4135
|
borderX = border / this.unitX;
|
|
4037
4136
|
borderY = border / this.unitY;
|
|
4038
4137
|
|
|
4039
|
-
this.
|
|
4040
|
-
this.zoomY = 1.0;
|
|
4041
|
-
|
|
4042
|
-
this.setBoundingBox([minX - borderX, maxY + borderY, maxX + borderX, minY - borderY], true);
|
|
4138
|
+
this.setBoundingBox([minX - borderX, maxY + borderY, maxX + borderX, minY - borderY], this.keepaspectratio, 'update');
|
|
4043
4139
|
|
|
4044
4140
|
return this.applyZoom();
|
|
4045
4141
|
},
|
|
@@ -4072,53 +4168,12 @@ define([
|
|
|
4072
4168
|
}
|
|
4073
4169
|
|
|
4074
4170
|
if (Type.isArray(newBBox)) {
|
|
4075
|
-
this.zoomX = 1.0;
|
|
4076
|
-
this.zoomY = 1.0;
|
|
4077
4171
|
cx = 0.5 * (newBBox[0] + newBBox[2]);
|
|
4078
4172
|
cy = 0.5 * (newBBox[1] + newBBox[3]);
|
|
4079
4173
|
dx = 1.5 * (newBBox[2] - newBBox[0]) * 0.5;
|
|
4080
4174
|
dy = 1.5 * (newBBox[1] - newBBox[3]) * 0.5;
|
|
4081
4175
|
d = Math.max(dx, dy);
|
|
4082
|
-
this.setBoundingBox([cx - d, cy + d, cx + d, cy - d],
|
|
4083
|
-
}
|
|
4084
|
-
|
|
4085
|
-
return this;
|
|
4086
|
-
},
|
|
4087
|
-
|
|
4088
|
-
zoomElementsOld: function (elements) {
|
|
4089
|
-
var i, j, e, box,
|
|
4090
|
-
newBBox = [0, 0, 0, 0],
|
|
4091
|
-
dir = [1, -1, -1, 1];
|
|
4092
|
-
|
|
4093
|
-
if (!Type.isArray(elements) || elements.length === 0) {
|
|
4094
|
-
return this;
|
|
4095
|
-
}
|
|
4096
|
-
|
|
4097
|
-
for (i = 0; i < elements.length; i++) {
|
|
4098
|
-
e = this.select(elements[i]);
|
|
4099
|
-
|
|
4100
|
-
box = e.bounds();
|
|
4101
|
-
if (Type.isArray(box)) {
|
|
4102
|
-
if (Type.isArray(newBBox)) {
|
|
4103
|
-
for (j = 0; j < 4; j++) {
|
|
4104
|
-
if (dir[j] * box[j] < dir[j] * newBBox[j]) {
|
|
4105
|
-
newBBox[j] = box[j];
|
|
4106
|
-
}
|
|
4107
|
-
}
|
|
4108
|
-
} else {
|
|
4109
|
-
newBBox = box;
|
|
4110
|
-
}
|
|
4111
|
-
}
|
|
4112
|
-
}
|
|
4113
|
-
|
|
4114
|
-
if (Type.isArray(newBBox)) {
|
|
4115
|
-
for (j = 0; j < 4; j++) {
|
|
4116
|
-
newBBox[j] -= dir[j];
|
|
4117
|
-
}
|
|
4118
|
-
|
|
4119
|
-
this.zoomX = 1.0;
|
|
4120
|
-
this.zoomY = 1.0;
|
|
4121
|
-
this.setBoundingBox(newBBox, true);
|
|
4176
|
+
this.setBoundingBox([cx - d, cy + d, cx + d, cy - d], this.keepaspectratio, 'update');
|
|
4122
4177
|
}
|
|
4123
4178
|
|
|
4124
4179
|
return this;
|
|
@@ -4336,12 +4391,37 @@ define([
|
|
|
4336
4391
|
*/
|
|
4337
4392
|
resizeContainer: function (canvasWidth, canvasHeight, dontset, dontSetBoundingBox) {
|
|
4338
4393
|
var box;
|
|
4394
|
+
// w, h, cx, cy;
|
|
4395
|
+
// box_act,
|
|
4396
|
+
// shift_x = 0,
|
|
4397
|
+
// shift_y = 0;
|
|
4339
4398
|
|
|
4340
4399
|
if (!dontSetBoundingBox) {
|
|
4341
|
-
|
|
4400
|
+
// box_act = this.getBoundingBox(); // This is the actual bounding box.
|
|
4401
|
+
box = this.getBoundingBox(); // This is the actual bounding box.
|
|
4342
4402
|
}
|
|
4343
|
-
|
|
4344
|
-
this.
|
|
4403
|
+
|
|
4404
|
+
this.canvasWidth = parseFloat(canvasWidth);
|
|
4405
|
+
this.canvasHeight = parseFloat(canvasHeight);
|
|
4406
|
+
|
|
4407
|
+
// if (!dontSetBoundingBox) {
|
|
4408
|
+
// box = this.attr.boundingbox; // This is the intended bounding box.
|
|
4409
|
+
|
|
4410
|
+
// // The shift values compensate the follow-up correction
|
|
4411
|
+
// // in setBoundingBox in case of "this.keepaspectratio==true"
|
|
4412
|
+
// // Otherwise, shift_x and shift_y will be zero.
|
|
4413
|
+
// // Obsolet since setBoundingBox centers in case of "this.keepaspectratio==true".
|
|
4414
|
+
// // shift_x = box_act[0] - box[0] / this.zoomX;
|
|
4415
|
+
// // shift_y = box_act[1] - box[1] / this.zoomY;
|
|
4416
|
+
|
|
4417
|
+
// cx = (box[2] + box[0]) * 0.5; // + shift_x;
|
|
4418
|
+
// cy = (box[3] + box[1]) * 0.5; // + shift_y;
|
|
4419
|
+
|
|
4420
|
+
// w = (box[2] - box[0]) * 0.5 / this.zoomX;
|
|
4421
|
+
// h = (box[1] - box[3]) * 0.5 / this.zoomY;
|
|
4422
|
+
|
|
4423
|
+
// box = [cx - w, cy + h, cx + w, cy - h];
|
|
4424
|
+
// }
|
|
4345
4425
|
|
|
4346
4426
|
if (!dontset) {
|
|
4347
4427
|
this.containerObj.style.width = (this.canvasWidth) + 'px';
|
|
@@ -4350,7 +4430,7 @@ define([
|
|
|
4350
4430
|
this.renderer.resize(this.canvasWidth, this.canvasHeight);
|
|
4351
4431
|
|
|
4352
4432
|
if (!dontSetBoundingBox) {
|
|
4353
|
-
this.setBoundingBox(box, this.keepaspectratio);
|
|
4433
|
+
this.setBoundingBox(box, this.keepaspectratio, 'keep');
|
|
4354
4434
|
}
|
|
4355
4435
|
|
|
4356
4436
|
return this;
|
|
@@ -4457,13 +4537,13 @@ define([
|
|
|
4457
4537
|
|
|
4458
4538
|
for (el = 0; el < this.objectsList.length; el++) {
|
|
4459
4539
|
pEl = this.objectsList[el];
|
|
4460
|
-
if (this.needsFullUpdate && pEl.elementClass
|
|
4540
|
+
if (this.needsFullUpdate && pEl.elementClass === Const.OBJECT_CLASS_TEXT) {
|
|
4461
4541
|
pEl.updateSize();
|
|
4462
4542
|
}
|
|
4463
4543
|
|
|
4464
4544
|
// For updates of an element we distinguish if the dragged element is updated or
|
|
4465
4545
|
// other elements are updated.
|
|
4466
|
-
// The difference lies in the treatment of gliders.
|
|
4546
|
+
// The difference lies in the treatment of gliders and points based on transformations.
|
|
4467
4547
|
pEl.update(!Type.exists(drag) || pEl.id !== drag.id)
|
|
4468
4548
|
.updateVisibility();
|
|
4469
4549
|
}
|
|
@@ -4837,15 +4917,21 @@ define([
|
|
|
4837
4917
|
* @param {Array} bbox New bounding box [x1,y1,x2,y2]
|
|
4838
4918
|
* @param {Boolean} [keepaspectratio=false] If set to true, the aspect ratio will be 1:1, but
|
|
4839
4919
|
* the resulting viewport may be larger.
|
|
4920
|
+
* @param {String} [setZoom='reset'] Reset, keep or update the zoom level of the board. 'reset'
|
|
4921
|
+
* sets {@link JXG.Board#zoomX} and {@link JXG.Board#zoomY} to the start values (or 1.0).
|
|
4922
|
+
* 'update' adapts these values accoring to the new bounding box and 'keep' does nothing.
|
|
4840
4923
|
* @returns {JXG.Board} Reference to the board
|
|
4841
4924
|
*/
|
|
4842
|
-
setBoundingBox: function (bbox, keepaspectratio) {
|
|
4843
|
-
var h, w,
|
|
4925
|
+
setBoundingBox: function (bbox, keepaspectratio, setZoom) {
|
|
4926
|
+
var h, w, ux, uy,
|
|
4927
|
+
offX = 0,
|
|
4928
|
+
offY = 0,
|
|
4844
4929
|
dim = Env.getDimensions(this.container, this.document);
|
|
4845
4930
|
|
|
4846
4931
|
if (!Type.isArray(bbox)) {
|
|
4847
4932
|
return this;
|
|
4848
4933
|
}
|
|
4934
|
+
|
|
4849
4935
|
if (bbox[0] < this.maxboundingbox[0] ||
|
|
4850
4936
|
bbox[1] > this.maxboundingbox[1] ||
|
|
4851
4937
|
bbox[2] > this.maxboundingbox[2] ||
|
|
@@ -4853,20 +4939,28 @@ define([
|
|
|
4853
4939
|
return this;
|
|
4854
4940
|
}
|
|
4855
4941
|
|
|
4856
|
-
|
|
4942
|
+
if (!Type.exists(setZoom)) {
|
|
4943
|
+
setZoom = 'reset';
|
|
4944
|
+
}
|
|
4945
|
+
|
|
4946
|
+
ux = this.unitX;
|
|
4947
|
+
uy = this.unitY;
|
|
4857
4948
|
|
|
4858
4949
|
this.canvasWidth = parseInt(dim.width, 10);
|
|
4859
4950
|
this.canvasHeight = parseInt(dim.height, 10);
|
|
4860
4951
|
w = this.canvasWidth;
|
|
4861
4952
|
h = this.canvasHeight;
|
|
4862
|
-
|
|
4863
4953
|
if (keepaspectratio) {
|
|
4864
4954
|
this.unitX = w / (bbox[2] - bbox[0]);
|
|
4865
4955
|
this.unitY = h / (bbox[1] - bbox[3]);
|
|
4866
4956
|
if (Math.abs(this.unitX) < Math.abs(this.unitY)) {
|
|
4867
4957
|
this.unitY = Math.abs(this.unitX) * this.unitY / Math.abs(this.unitY);
|
|
4958
|
+
// Add the additional units in equal portions above and below
|
|
4959
|
+
offY = (h / this.unitY - (bbox[1] - bbox[3])) * 0.5;
|
|
4868
4960
|
} else {
|
|
4869
4961
|
this.unitX = Math.abs(this.unitY) * this.unitX / Math.abs(this.unitX);
|
|
4962
|
+
// Add the additional units in equal portions left and right
|
|
4963
|
+
offX = (w / this.unitX - (bbox[2] - bbox[0])) * 0.5;
|
|
4870
4964
|
}
|
|
4871
4965
|
this.keepaspectratio = true;
|
|
4872
4966
|
} else {
|
|
@@ -4875,7 +4969,15 @@ define([
|
|
|
4875
4969
|
this.keepaspectratio = false;
|
|
4876
4970
|
}
|
|
4877
4971
|
|
|
4878
|
-
this.moveOrigin(-this.unitX * bbox[0], this.unitY * bbox[1]);
|
|
4972
|
+
this.moveOrigin(-this.unitX * (bbox[0] - offX), this.unitY * (bbox[1] + offY));
|
|
4973
|
+
|
|
4974
|
+
if (setZoom === 'update') {
|
|
4975
|
+
this.zoomX *= this.unitX / ux;
|
|
4976
|
+
this.zoomY *= this.unitY / uy;
|
|
4977
|
+
} else if (setZoom === 'reset') {
|
|
4978
|
+
this.zoomX = Type.exists(this.attr.zoomx) ? this.attr.zoomx : 1.0;
|
|
4979
|
+
this.zoomY = Type.exists(this.attr.zoomy) ? this.attr.zoomy : 1.0;
|
|
4980
|
+
}
|
|
4879
4981
|
|
|
4880
4982
|
return this;
|
|
4881
4983
|
},
|
|
@@ -5200,7 +5302,7 @@ define([
|
|
|
5200
5302
|
for (i = 0; i < l; i++) {
|
|
5201
5303
|
olist[flist[i].id] = flist[i];
|
|
5202
5304
|
}
|
|
5203
|
-
s = new
|
|
5305
|
+
s = new Composition(olist);
|
|
5204
5306
|
// it's an element which has been deleted (and still hangs around, e.g. in an attractor list
|
|
5205
5307
|
} else if (Type.isObject(s) && Type.exists(s.id) && !Type.exists(this.objects[s.id])) {
|
|
5206
5308
|
s = null;
|
|
@@ -5547,7 +5649,7 @@ define([
|
|
|
5547
5649
|
* @name JXG.Board#move
|
|
5548
5650
|
* @param {Event} e The browser's event object.
|
|
5549
5651
|
* @param {Number} mode The mode the board currently is in
|
|
5550
|
-
* @see
|
|
5652
|
+
* @see JXG.Board#mode
|
|
5551
5653
|
*/
|
|
5552
5654
|
__evt__move: function (e, mode) { },
|
|
5553
5655
|
|
|
@@ -5557,7 +5659,7 @@ define([
|
|
|
5557
5659
|
* @name JXG.Board#mousemove
|
|
5558
5660
|
* @param {Event} e The browser's event object.
|
|
5559
5661
|
* @param {Number} mode The mode the board currently is in
|
|
5560
|
-
* @see
|
|
5662
|
+
* @see JXG.Board#mode
|
|
5561
5663
|
*/
|
|
5562
5664
|
__evt__mousemove: function (e, mode) { },
|
|
5563
5665
|
|
|
@@ -5567,7 +5669,7 @@ define([
|
|
|
5567
5669
|
* @name JXG.Board#penmove
|
|
5568
5670
|
* @param {Event} e The browser's event object.
|
|
5569
5671
|
* @param {Number} mode The mode the board currently is in
|
|
5570
|
-
* @see
|
|
5672
|
+
* @see JXG.Board#mode
|
|
5571
5673
|
*/
|
|
5572
5674
|
__evt__penmove: function (e, mode) { },
|
|
5573
5675
|
|
|
@@ -5578,7 +5680,7 @@ define([
|
|
|
5578
5680
|
* @name JXG.Board#pointermove
|
|
5579
5681
|
* @param {Event} e The browser's event object.
|
|
5580
5682
|
* @param {Number} mode The mode the board currently is in
|
|
5581
|
-
* @see
|
|
5683
|
+
* @see JXG.Board#mode
|
|
5582
5684
|
*/
|
|
5583
5685
|
__evt__pointermove: function (e, mode) { },
|
|
5584
5686
|
|
|
@@ -5588,7 +5690,7 @@ define([
|
|
|
5588
5690
|
* @name JXG.Board#touchmove
|
|
5589
5691
|
* @param {Event} e The browser's event object.
|
|
5590
5692
|
* @param {Number} mode The mode the board currently is in
|
|
5591
|
-
* @see
|
|
5693
|
+
* @see JXG.Board#mode
|
|
5592
5694
|
*/
|
|
5593
5695
|
__evt__touchmove: function (e, mode) { },
|
|
5594
5696
|
|
|
@@ -5758,6 +5860,12 @@ define([
|
|
|
5758
5860
|
* <p>
|
|
5759
5861
|
* The wrapping div has the CSS class 'jxgbox_wrap_private' which is
|
|
5760
5862
|
* defined in the file 'jsxgraph.css'
|
|
5863
|
+
* <p>
|
|
5864
|
+
* This feature is not available on iPhones (as of December 2021).
|
|
5865
|
+
*
|
|
5866
|
+
* @param {String} id (Optional) id of the div element which is brought to fullscreen.
|
|
5867
|
+
* If not provided, this defaults to the JSXGraph div. However, it may be necessary for the aspect ratio trick
|
|
5868
|
+
* which using padding-bottom/top and an out div element. Then, the id of the outer div has to be supplied.
|
|
5761
5869
|
*
|
|
5762
5870
|
* @return {JXG.Board} Reference to the board
|
|
5763
5871
|
*
|
|
@@ -5779,29 +5887,76 @@ define([
|
|
|
5779
5887
|
* var p = board.create('point', [0, 1]);
|
|
5780
5888
|
* board_d5bab8b6 = board;
|
|
5781
5889
|
* })();
|
|
5782
|
-
* <button onClick="board_d5bab8b6.toFullscreen()">Fullscreen</button>
|
|
5783
|
-
*
|
|
5784
5890
|
* </script>
|
|
5891
|
+
* <button onClick="board_d5bab8b6.toFullscreen()">Fullscreen</button>
|
|
5785
5892
|
* <pre>
|
|
5786
5893
|
*
|
|
5894
|
+
* @example
|
|
5895
|
+
* <div id='outer' style='max-width: 500px; margin: 0 auto;'>
|
|
5896
|
+
* <div id='jxgbox' class='jxgbox' style='height: 0; padding-bottom: 100%'></div>
|
|
5897
|
+
* </div>
|
|
5898
|
+
* <button onClick="board.toFullscreen('outer')">Fullscreen</button>
|
|
5899
|
+
*
|
|
5900
|
+
* <script language="Javascript" type='text/javascript'>
|
|
5901
|
+
* var board = JXG.JSXGraph.initBoard('jxgbox', {
|
|
5902
|
+
* axis:true,
|
|
5903
|
+
* boundingbox:[-5,5,5,-5],
|
|
5904
|
+
* fullscreen: { id: 'outer' },
|
|
5905
|
+
* showFullscreen: true
|
|
5906
|
+
* });
|
|
5907
|
+
* var p = board.create('point', [-2, 3], {});
|
|
5908
|
+
* </script>
|
|
5909
|
+
*
|
|
5910
|
+
* </pre><div id="JXG7103f6b_outer" style='max-width: 500px; margin: 0 auto;'>
|
|
5911
|
+
* <div id="JXG7103f6be-6993-4ff8-8133-c78e50a8afac" class="jxgbox" style="height: 0; padding-bottom: 100%;"></div>
|
|
5912
|
+
* </div>
|
|
5913
|
+
* <button onClick="board_JXG7103f6be.toFullscreen('JXG7103f6b_outer')">Fullscreen</button>
|
|
5914
|
+
* <script type="text/javascript">
|
|
5915
|
+
* var board_JXG7103f6be;
|
|
5916
|
+
* (function() {
|
|
5917
|
+
* var board = JXG.JSXGraph.initBoard('JXG7103f6be-6993-4ff8-8133-c78e50a8afac',
|
|
5918
|
+
* {boundingbox: [-8, 8, 8,-8], axis: true, fullscreen: { id: 'JXG7103f6b_outer' }, showFullscreen: true,
|
|
5919
|
+
* showcopyright: false, shownavigation: false});
|
|
5920
|
+
* var p = board.create('point', [-2, 3], {});
|
|
5921
|
+
* board_JXG7103f6be = board;
|
|
5922
|
+
* })();
|
|
5923
|
+
*
|
|
5924
|
+
* </script><pre>
|
|
5925
|
+
*
|
|
5926
|
+
*
|
|
5787
5927
|
*/
|
|
5788
|
-
toFullscreen: function() {
|
|
5789
|
-
var
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5928
|
+
toFullscreen: function (id) {
|
|
5929
|
+
var wrap_id, wrap_node, inner_node;
|
|
5930
|
+
|
|
5931
|
+
id = id || this.container;
|
|
5932
|
+
this._fullscreen_inner_id = id;
|
|
5933
|
+
inner_node = document.getElementById(id);
|
|
5934
|
+
wrap_id = 'fullscreenwrap_' + id;
|
|
5793
5935
|
|
|
5794
|
-
//
|
|
5795
|
-
if (
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5936
|
+
// Wrap a div around the JSXGraph div.
|
|
5937
|
+
if (this.document.getElementById(wrap_id)) {
|
|
5938
|
+
wrap_node = this.document.getElementById(wrap_id);
|
|
5939
|
+
} else {
|
|
5940
|
+
wrap_node = document.createElement('div');
|
|
5941
|
+
wrap_node.classList.add('JXG_wrap_private');
|
|
5942
|
+
wrap_node.setAttribute('id', wrap_id);
|
|
5943
|
+
inner_node.parentNode.insertBefore(wrap_node, inner_node);
|
|
5944
|
+
wrap_node.appendChild(inner_node);
|
|
5801
5945
|
}
|
|
5802
5946
|
|
|
5803
|
-
//
|
|
5804
|
-
|
|
5947
|
+
// Get the real width and height of the JSXGraph div
|
|
5948
|
+
// and determine the scaling and vertical shift amount
|
|
5949
|
+
this._fullscreen_res = Env._getScaleFactors(inner_node);
|
|
5950
|
+
|
|
5951
|
+
// Trigger fullscreen mode
|
|
5952
|
+
wrap_node.requestFullscreen = wrap_node.requestFullscreen ||
|
|
5953
|
+
wrap_node.webkitRequestFullscreen ||
|
|
5954
|
+
wrap_node.mozRequestFullScreen ||
|
|
5955
|
+
wrap_node.msRequestFullscreen;
|
|
5956
|
+
|
|
5957
|
+
if (wrap_node.requestFullscreen) {
|
|
5958
|
+
wrap_node.requestFullscreen();
|
|
5959
|
+
}
|
|
5805
5960
|
|
|
5806
5961
|
return this;
|
|
5807
5962
|
},
|
|
@@ -5811,21 +5966,71 @@ define([
|
|
|
5811
5966
|
* which are applied to the JSXGraph canvas have to be reread.
|
|
5812
5967
|
* Otherwise the position of upper left corner is wrongly interpreted.
|
|
5813
5968
|
*
|
|
5814
|
-
* @param {Object} evt fullscreen event object
|
|
5969
|
+
* @param {Object} evt fullscreen event object (unused)
|
|
5815
5970
|
*/
|
|
5816
|
-
fullscreenListener: function(evt) {
|
|
5817
|
-
var
|
|
5971
|
+
fullscreenListener: function (evt) {
|
|
5972
|
+
var res, inner_id, inner_node;
|
|
5973
|
+
|
|
5974
|
+
inner_id = this._fullscreen_inner_id;
|
|
5975
|
+
if (!Type.exists(inner_id)) {
|
|
5976
|
+
return;
|
|
5977
|
+
}
|
|
5818
5978
|
|
|
5979
|
+
document.fullscreenElement = document.fullscreenElement ||
|
|
5980
|
+
document.webkitFullscreenElement ||
|
|
5981
|
+
document.mozFullscreenElement ||
|
|
5982
|
+
document.msFullscreenElement;
|
|
5983
|
+
|
|
5984
|
+
inner_node = document.getElementById(inner_id);
|
|
5819
5985
|
// If full screen mode is started we have to remove CSS margin around the JSXGraph div.
|
|
5820
5986
|
// Otherwise, the positioning of the fullscreen div will be false.
|
|
5821
5987
|
// When leaving the fullscreen mode, the margin is put back in.
|
|
5988
|
+
if (document.fullscreenElement) {
|
|
5989
|
+
// Just entered fullscreen mode
|
|
5990
|
+
|
|
5991
|
+
// Get the data computed in board.toFullscreen()
|
|
5992
|
+
res = this._fullscreen_res;
|
|
5993
|
+
|
|
5994
|
+
// Store the scaling data.
|
|
5995
|
+
// It is used in AbstractRenderer.updateText to restore the scaling matrix
|
|
5996
|
+
// which is removed by MathJax.
|
|
5997
|
+
// Further, the CSS margin has to be removed when in fullscreen mode,
|
|
5998
|
+
// and must be restored later.
|
|
5999
|
+
inner_node._cssFullscreenStore = {
|
|
6000
|
+
id: document.fullscreenElement.id,
|
|
6001
|
+
isFullscreen: true,
|
|
6002
|
+
margin: inner_node.style.margin,
|
|
6003
|
+
width: inner_node.style.width,
|
|
6004
|
+
scale: res.scale,
|
|
6005
|
+
vshift: res.vshift
|
|
6006
|
+
};
|
|
6007
|
+
|
|
6008
|
+
inner_node.style.margin = '';
|
|
6009
|
+
inner_node.style.width = res.width + 'px';
|
|
6010
|
+
|
|
6011
|
+
// Do the shifting and scaling via CSS pseudo rules
|
|
6012
|
+
// We do this after fullscreen mode has been established to get the correct size
|
|
6013
|
+
// of the JSXGraph div.
|
|
6014
|
+
Env.scaleJSXGraphDiv(document.fullscreenElement.id, inner_id, res.scale, res.vshift);
|
|
6015
|
+
|
|
6016
|
+
// Clear document.fullscreenElement, because Safari doesn't to it and
|
|
6017
|
+
// when leaving full screen mode it is still set.
|
|
6018
|
+
document.fullscreenElement = null;
|
|
6019
|
+
|
|
6020
|
+
} else if (Type.exists(inner_node._cssFullscreenStore)) {
|
|
6021
|
+
// Just left the fullscreen mode
|
|
6022
|
+
|
|
6023
|
+
// Remove the CSS rules added in Env.scaleJSXGraphDiv
|
|
6024
|
+
try {
|
|
6025
|
+
document.styleSheets[document.styleSheets.length - 1].deleteRule(0);
|
|
6026
|
+
} catch (err) {
|
|
6027
|
+
console.log('JSXGraph: Could not remove CSS rules for full screen mode');
|
|
6028
|
+
}
|
|
6029
|
+
|
|
6030
|
+
inner_node._cssFullscreenStore.isFullscreen = false;
|
|
6031
|
+
inner_node.style.margin = inner_node._cssFullscreenStore.margin;
|
|
6032
|
+
inner_node.style.width = inner_node._cssFullscreenStore.width;
|
|
5822
6033
|
|
|
5823
|
-
if (Type.exists(this._cssFullscreenStore) && this._cssFullscreenStore.isFullscreen) {
|
|
5824
|
-
el._cssFullscreenStore.isFullscreen = false;
|
|
5825
|
-
el.style.margin = this._cssFullscreenStore.margin;
|
|
5826
|
-
} else {
|
|
5827
|
-
el._cssFullscreenStore.isFullscreen = true;
|
|
5828
|
-
el.style.margin = '';
|
|
5829
6034
|
}
|
|
5830
6035
|
|
|
5831
6036
|
this.updateCSSTransforms();
|