aotrautils 0.0.44 → 0.0.45
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.
- aotrautils/aotrautils.build.js +1271 -564
- aotrautils/package.json +1 -1
aotrautils/aotrautils.build.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
/*utils CLIENT library associated with aotra version : «1.0.0.000 (11/07/2022-00:48:13)»*/
|
|
3
4
|
/*-----------------------------------------------------------------------------*/
|
|
4
5
|
/* ## Utility global methods in a browser (htmljs) client environment.
|
|
5
6
|
*
|
|
@@ -35,6 +36,9 @@ if(typeof(window)==="undefined") window=global;
|
|
|
35
36
|
const APOSTROPHE="’";
|
|
36
37
|
PERFORM_TESTS_ON_LIBRARY=false;
|
|
37
38
|
|
|
39
|
+
const isUserMediaAvailable=!!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia && navigator.mediaDevices.enumerateDevices);
|
|
40
|
+
|
|
41
|
+
|
|
38
42
|
// Dependances-optionalizing mechanisms :
|
|
39
43
|
if(typeof aotest === "undefined"){
|
|
40
44
|
aotest=function(testcase,testedFunction){ return testedFunction; };
|
|
@@ -1960,11 +1964,11 @@ if(typeof(window)!=="undefined") window.URL=window.URL || window.webkitURL;
|
|
|
1960
1964
|
//navigator.getUserMedia=navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
|
|
1961
1965
|
//window.AudioContext=window.AudioContext || window.webkitAudioContext;
|
|
1962
1966
|
|
|
1963
|
-
function hasGetUserMedia(){
|
|
1964
|
-
// OLD : (DEPRECATED)
|
|
1965
|
-
|
|
1966
|
-
return !!/*<-=to force a boolean result*/(navigator.mediaDevices && navigator.mediaDevices.getUserMedia);
|
|
1967
|
-
}
|
|
1967
|
+
//function hasGetUserMedia(){
|
|
1968
|
+
// // OLD : (DEPRECATED)
|
|
1969
|
+
//// return !!/*<-=to force a boolean result*/(navigator.getUserMedia);
|
|
1970
|
+
// return !!/*<-=to force a boolean result*/(navigator.mediaDevices && navigator.mediaDevices.getUserMedia);
|
|
1971
|
+
//}
|
|
1968
1972
|
|
|
1969
1973
|
|
|
1970
1974
|
|
|
@@ -3082,6 +3086,7 @@ function filterPoints(allPoints ,ctx/*DBG*/){
|
|
|
3082
3086
|
|
|
3083
3087
|
|
|
3084
3088
|
|
|
3089
|
+
|
|
3085
3090
|
//Deprecated : use getMediaHandler directly
|
|
3086
3091
|
/*public*/getWebcamHandler=function(
|
|
3087
3092
|
// videoTagId
|
|
@@ -3101,6 +3106,12 @@ function filterPoints(allPoints ,ctx/*DBG*/){
|
|
|
3101
3106
|
/*OPTIONAL*/doFinallyWhenMultipleDevices=null
|
|
3102
3107
|
){
|
|
3103
3108
|
|
|
3109
|
+
if(!isUserMediaAvailable){
|
|
3110
|
+
// TRACE
|
|
3111
|
+
log("ERROR : User medias is not supported in your browser (or are you running in a non-https context ?). Aborting");
|
|
3112
|
+
return null;
|
|
3113
|
+
}
|
|
3114
|
+
|
|
3104
3115
|
const cameras=[];
|
|
3105
3116
|
const microphones=[];
|
|
3106
3117
|
navigator.mediaDevices.enumerateDevices().then(function(devices){
|
|
@@ -3110,14 +3121,10 @@ function filterPoints(allPoints ,ctx/*DBG*/){
|
|
|
3110
3121
|
else if(d.kind==="audioinput") microphones.push(d);
|
|
3111
3122
|
});
|
|
3112
3123
|
|
|
3113
|
-
|
|
3114
|
-
// DBG
|
|
3115
|
-
//alert(JSON.stringify(cameras));
|
|
3116
|
-
|
|
3117
3124
|
// CAUTION : On today this method only handles media handlers with a single track :
|
|
3118
3125
|
|
|
3119
3126
|
let constraints={video:(videoConfigParam!=null), audio:(audioConfigParam!=null)}; // Default constraints
|
|
3120
|
-
if(videoConfigParam){
|
|
3127
|
+
if(!empty(cameras) && videoConfigParam){
|
|
3121
3128
|
let webcamIndex=videoConfigParam.webcamIndex;
|
|
3122
3129
|
if(webcamIndex!=null){
|
|
3123
3130
|
const deviceIndex=Math.min(cameras.length-1, webcamIndex);
|
|
@@ -3127,7 +3134,7 @@ function filterPoints(allPoints ,ctx/*DBG*/){
|
|
|
3127
3134
|
}
|
|
3128
3135
|
};
|
|
3129
3136
|
}
|
|
3130
|
-
}else if(audioConfigParam){
|
|
3137
|
+
}else if(!empty(microphones) && audioConfigParam){
|
|
3131
3138
|
let microphoneIndex=audioConfigParam.microphoneIndex;
|
|
3132
3139
|
if(microphoneIndex!=null){
|
|
3133
3140
|
const deviceIndex=Math.min(microphones.length-1, microphoneIndex);
|
|
@@ -3195,9 +3202,10 @@ function filterPoints(allPoints ,ctx/*DBG*/){
|
|
|
3195
3202
|
return null;
|
|
3196
3203
|
}
|
|
3197
3204
|
|
|
3198
|
-
|
|
3205
|
+
|
|
3206
|
+
if(!isUserMediaAvailable){
|
|
3199
3207
|
// TRACE
|
|
3200
|
-
log("ERROR :
|
|
3208
|
+
log("ERROR : User medias is not supported in your browser (or are you running in a non-https context ?). Aborting");
|
|
3201
3209
|
return null;
|
|
3202
3210
|
}
|
|
3203
3211
|
|
|
@@ -3747,34 +3755,51 @@ getColorFingersPointer=(mediaHandler,colorsPattern="rgb")=>{
|
|
|
3747
3755
|
//Sound handling :
|
|
3748
3756
|
|
|
3749
3757
|
|
|
3750
|
-
function playAudioData(audioData
|
|
3758
|
+
function playAudioData(audioData,audioCtxParam=null,audioBufferSizeParam=null,numberOfAudioChannelsParam=null,sampleRateParam=null,
|
|
3759
|
+
//sourceParam=null, bufferParam=null,
|
|
3760
|
+
doOnEndPlayingSample=null,
|
|
3761
|
+
playbackRateParam=2
|
|
3762
|
+
){
|
|
3751
3763
|
|
|
3752
3764
|
const DEFAULT_AUDIO_BUFFER_SIZE=4096;
|
|
3753
|
-
let audioBufferSize=audioBufferSizeParam?audioBufferSizeParam:DEFAULT_AUDIO_BUFFER_SIZE;
|
|
3754
|
-
let numberOfAudioChannelsOUT=numberOfAudioChannelsParam?numberOfAudioChannelsParam:1;//default is «mono»
|
|
3755
3765
|
|
|
3756
3766
|
// To read the audio data :
|
|
3757
3767
|
// simply perform a reading of mediaHandler.audioData
|
|
3758
3768
|
// To play the sound :
|
|
3759
3769
|
|
|
3760
3770
|
let audioCtx=nonull(audioCtxParam,new AudioContext());
|
|
3761
|
-
|
|
3762
|
-
let
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3771
|
+
let audioBufferSize=nonull(audioBufferSizeParam,DEFAULT_AUDIO_BUFFER_SIZE);
|
|
3772
|
+
let numberOfAudioChannels=nonull(numberOfAudioChannelsParam,1);//default is «mono»
|
|
3773
|
+
let sampleRate=nonull(sampleRateParam,audioCtx.sampleRate);
|
|
3774
|
+
|
|
3775
|
+
// We have to recreate them each time ! :
|
|
3776
|
+
let source=audioCtx.createBufferSource();
|
|
3777
|
+
let buffer=audioCtx.createBuffer(numberOfAudioChannels, audioBufferSize, sampleRate); // Only one channel for «mono»
|
|
3778
|
+
|
|
3779
|
+
let data=buffer.getChannelData(0);
|
|
3780
|
+
for(let i=0; i<audioBufferSize; i++){
|
|
3781
|
+
data[i]=audioData[i];
|
|
3782
|
+
}
|
|
3783
|
+
if(!source.buffer){
|
|
3784
|
+
source.playbackRate.value=playbackRateParam;
|
|
3785
|
+
source.buffer=buffer;
|
|
3786
|
+
source.connect(audioCtx.destination);
|
|
3787
|
+
// TODO : FIXME : DOES NOT CURRENTLY WORK :
|
|
3788
|
+
// BECAUSE OF INTERACTION-FIRST RESTRICTIONS WILL MAKE IT NOT WORK !
|
|
3789
|
+
// OLD : source.start(0);
|
|
3790
|
+
source.start();
|
|
3791
|
+
|
|
3792
|
+
// // DBG
|
|
3793
|
+
// lognow("audioSource STARTED : ",source);
|
|
3794
|
+
}
|
|
3773
3795
|
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3796
|
+
|
|
3797
|
+
if(doOnEndPlayingSample){
|
|
3798
|
+
const playingDurationMillis=(audioBufferSize/sampleRate);
|
|
3799
|
+
// DBG
|
|
3800
|
+
// setTimeout(doOnEndPlayingSample,playingDurationMillis*1000); (because Hertz)
|
|
3801
|
+
doOnEndPlayingSample();
|
|
3802
|
+
}
|
|
3778
3803
|
|
|
3779
3804
|
// OLD :
|
|
3780
3805
|
// // Fill the buffer with data from array;
|
|
@@ -3934,6 +3959,7 @@ function loadSound(filePath,doOnLoaded=null,doOnEnded=null){
|
|
|
3934
3959
|
isReady:false,
|
|
3935
3960
|
speed:1,
|
|
3936
3961
|
volume:1,
|
|
3962
|
+
isPlaying:false,
|
|
3937
3963
|
mainLoopSound:null,
|
|
3938
3964
|
setVolume:function(newVolume=null){
|
|
3939
3965
|
if(newVolume) self.volume=newVolume;
|
|
@@ -3947,12 +3973,15 @@ function loadSound(filePath,doOnLoaded=null,doOnEnded=null){
|
|
|
3947
3973
|
},
|
|
3948
3974
|
play:function(){
|
|
3949
3975
|
if(!self.nativeAudioElement.paused) return self;
|
|
3950
|
-
self.nativeAudioElement.play()
|
|
3976
|
+
self.nativeAudioElement.play().then(()=>{
|
|
3977
|
+
self.isPlaying=true;
|
|
3978
|
+
});
|
|
3951
3979
|
return self;
|
|
3952
3980
|
},
|
|
3953
3981
|
pause:function(){
|
|
3954
|
-
if(self.nativeAudioElement.paused) return self;
|
|
3982
|
+
if(self.nativeAudioElement.paused || !self.isPlaying) return self;
|
|
3955
3983
|
self.nativeAudioElement.pause();
|
|
3984
|
+
self.isPlaying=false;
|
|
3956
3985
|
return self;
|
|
3957
3986
|
},
|
|
3958
3987
|
resumeLoop:function(){
|
|
@@ -3962,7 +3991,7 @@ function loadSound(filePath,doOnLoaded=null,doOnEnded=null){
|
|
|
3962
3991
|
return self;
|
|
3963
3992
|
},
|
|
3964
3993
|
pauseLoop:function(){
|
|
3965
|
-
if(!self.isLoop || !self.mainLoopSound) return self;
|
|
3994
|
+
if(!self.isLoop || !self.mainLoopSound || !self.isPlaying) return self;
|
|
3966
3995
|
self.mainLoopSound.pause();
|
|
3967
3996
|
self.pause();
|
|
3968
3997
|
return self;
|
|
@@ -4170,7 +4199,7 @@ function getCenteredZoneCoords(xParam,yParam,width,height,center={x:"left",y:"to
|
|
|
4170
4199
|
}
|
|
4171
4200
|
|
|
4172
4201
|
|
|
4173
|
-
function drawImageAndCenter(ctx,image,xParam,yParam,center={x:"left",y:"top"},
|
|
4202
|
+
function drawImageAndCenter(ctx,image,xParam=0,yParam=0,zooms={zx:1,zy:1},center={x:"left",y:"top"},
|
|
4174
4203
|
scaleFactorW=1,scaleFactorH=1,
|
|
4175
4204
|
sizeW=null,sizeH=null,
|
|
4176
4205
|
opacity=1,
|
|
@@ -4193,6 +4222,8 @@ function drawImageAndCenter(ctx,image,xParam,yParam,center={x:"left",y:"top"},
|
|
|
4193
4222
|
ctx.globalAlpha=opacity;
|
|
4194
4223
|
}
|
|
4195
4224
|
|
|
4225
|
+
// TODO : FIXME : use zooms parameter !
|
|
4226
|
+
|
|
4196
4227
|
if(opacity!==0){
|
|
4197
4228
|
|
|
4198
4229
|
if(alphaAngleRadians==null){
|
|
@@ -4353,8 +4384,12 @@ function getSpriteMonoThreaded(imagesPool,/*OPTIONAL*/refreshMillis=null,
|
|
|
4353
4384
|
// drawingWidth, drawingHeight);
|
|
4354
4385
|
|
|
4355
4386
|
|
|
4356
|
-
|
|
4357
|
-
|
|
4387
|
+
const xImage=xParam+self.xOffset;
|
|
4388
|
+
const yImage=yParam+self.yOffset;
|
|
4389
|
+
|
|
4390
|
+
drawImageAndCenter(ctx, nonull(img,self.imagesPool),
|
|
4391
|
+
xImage, yImage,
|
|
4392
|
+
{zx:1,zy:1},
|
|
4358
4393
|
self.center,
|
|
4359
4394
|
(self.scaleFactorW*self.zoom),
|
|
4360
4395
|
(self.scaleFactorH*self.zoom),
|
|
@@ -4387,9 +4422,11 @@ function getSpriteMonoThreaded(imagesPool,/*OPTIONAL*/refreshMillis=null,
|
|
|
4387
4422
|
}
|
|
4388
4423
|
}
|
|
4389
4424
|
|
|
4425
|
+
// DOES NOT WORK :
|
|
4426
|
+
// return ctx.getImageData(clipX,clipY,drawingWidth,drawingHeight);
|
|
4427
|
+
// }else{
|
|
4428
|
+
// return ctx.getImageData(0,0,drawingWidth,drawingHeight);
|
|
4390
4429
|
}
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
4430
|
},
|
|
4394
4431
|
|
|
4395
4432
|
};
|
|
@@ -4451,20 +4488,22 @@ function getMonoThreadedTimeout(doOnStop=null, delayMillis=null){
|
|
|
4451
4488
|
|
|
4452
4489
|
|
|
4453
4490
|
|
|
4454
|
-
function getMonoThreadedRoutine(doOnEachStepCallback,terminateFunction=null,doOnStop=null,refreshMillis=null,startDependsOnParentOnly=false){
|
|
4491
|
+
function getMonoThreadedRoutine(doOnEachStepCallback,terminateFunction=null,doOnStop=null,refreshMillis=null,startDependsOnParentOnly=false,doOnSuspend=null,doOnResume=null){
|
|
4455
4492
|
|
|
4456
|
-
// Three-states : started,
|
|
4493
|
+
// Three-states : started, paused, stopped.
|
|
4457
4494
|
|
|
4458
4495
|
let self={
|
|
4459
4496
|
|
|
4460
4497
|
startDependsOnParentOnly:startDependsOnParentOnly,
|
|
4461
4498
|
started:false,
|
|
4462
|
-
|
|
4499
|
+
paused:false,
|
|
4463
4500
|
time:getNow(),
|
|
4464
4501
|
refreshMillis:refreshMillis,
|
|
4465
4502
|
doOnEachStepCallback:doOnEachStepCallback,
|
|
4466
4503
|
terminateFunction:terminateFunction,
|
|
4467
4504
|
doOnStop:doOnStop,
|
|
4505
|
+
doOnSuspend:doOnSuspend,
|
|
4506
|
+
doOnResume:doOnResume,
|
|
4468
4507
|
durationTimeFactorHolder:{durationTimeFactor:1},
|
|
4469
4508
|
setDurationTimeFactorHolder:function(durationTimeFactorHolder){
|
|
4470
4509
|
self.durationTimeFactorHolder=durationTimeFactorHolder;
|
|
@@ -4473,44 +4512,50 @@ function getMonoThreadedRoutine(doOnEachStepCallback,terminateFunction=null,doOn
|
|
|
4473
4512
|
isStarted:function(){
|
|
4474
4513
|
return self.started || self.startDependsOnParentOnly;
|
|
4475
4514
|
},
|
|
4476
|
-
start:function(callingObject=null){
|
|
4515
|
+
start:function(callingObject=null,args=null){
|
|
4477
4516
|
|
|
4478
4517
|
// if(self.isStarted()) return;
|
|
4479
|
-
if(self.terminateFunction && self.terminateFunction(callingObject)){
|
|
4518
|
+
if(self.terminateFunction && self.terminateFunction(callingObject,args)){
|
|
4480
4519
|
|
|
4481
4520
|
// CAUTION : Even if the routine is «pre-terminated» before even starting
|
|
4482
4521
|
// (ie. its stop conditions are fullfilled even before it has started)
|
|
4483
4522
|
// we all the same have to trigger its stop treatments :
|
|
4484
|
-
if(self.doOnStop) self.doOnStop(callingObject);
|
|
4523
|
+
if(self.doOnStop) self.doOnStop(callingObject,args);
|
|
4485
4524
|
|
|
4486
4525
|
return;
|
|
4487
4526
|
}
|
|
4488
4527
|
|
|
4489
4528
|
self.started=true;
|
|
4490
4529
|
|
|
4491
|
-
self.
|
|
4530
|
+
self.paused=false;
|
|
4492
4531
|
|
|
4493
4532
|
return self;
|
|
4494
4533
|
},
|
|
4495
|
-
stop:function(callingObject=null){
|
|
4534
|
+
stop:function(callingObject=null,args=null){
|
|
4496
4535
|
|
|
4497
4536
|
if(!self.isStarted()) return;
|
|
4498
4537
|
self.started=false;
|
|
4499
4538
|
|
|
4500
|
-
if(self.doOnStop) self.doOnStop(callingObject);
|
|
4539
|
+
if(self.doOnStop) self.doOnStop(callingObject,args);
|
|
4540
|
+
|
|
4541
|
+
},
|
|
4542
|
+
pause:function(callingObject=null,args=null){
|
|
4543
|
+
self.paused=true;
|
|
4501
4544
|
|
|
4545
|
+
if(self.doOnSuspend) self.doOnSuspend(callingObject,args);
|
|
4502
4546
|
},
|
|
4503
|
-
|
|
4504
|
-
self.
|
|
4547
|
+
resume:function(callingObject=null,args=null){
|
|
4548
|
+
self.paused=false;
|
|
4549
|
+
|
|
4550
|
+
if(self.doOnResume) self.doOnResume(callingObject,args);
|
|
4505
4551
|
},
|
|
4506
|
-
|
|
4507
|
-
self.
|
|
4552
|
+
isPaused:function(){
|
|
4553
|
+
return self.paused;
|
|
4508
4554
|
},
|
|
4509
|
-
|
|
4510
4555
|
doStep:function(callingObject=null,args=null){
|
|
4511
4556
|
|
|
4512
4557
|
|
|
4513
|
-
if(!self.isStarted() || self.
|
|
4558
|
+
if(!self.isStarted() || self.paused) return;
|
|
4514
4559
|
|
|
4515
4560
|
// Looping index with a delay :
|
|
4516
4561
|
|
|
@@ -4521,8 +4566,8 @@ function getMonoThreadedRoutine(doOnEachStepCallback,terminateFunction=null,doOn
|
|
|
4521
4566
|
}
|
|
4522
4567
|
|
|
4523
4568
|
// We perform the step :
|
|
4524
|
-
if(self.terminateFunction && self.terminateFunction(callingObject)){
|
|
4525
|
-
self.stop(callingObject);
|
|
4569
|
+
if(self.terminateFunction && self.terminateFunction(callingObject,args)){
|
|
4570
|
+
self.stop(callingObject,args);
|
|
4526
4571
|
return;
|
|
4527
4572
|
}
|
|
4528
4573
|
|
|
@@ -4545,7 +4590,7 @@ function getMonoThreadedRoutine(doOnEachStepCallback,terminateFunction=null,doOn
|
|
|
4545
4590
|
function getMonoThreadedGoToGoal(actualValue,goalValue,refreshMillis=null,totalTimeMillis=1000,mode="linear",doOnEachStepCallback=null,doOnStop=null){
|
|
4546
4591
|
|
|
4547
4592
|
// const STEP_PRECISION=4;
|
|
4548
|
-
// Three-states : started,
|
|
4593
|
+
// Three-states : started, paused, stopped.
|
|
4549
4594
|
|
|
4550
4595
|
let self={
|
|
4551
4596
|
|
|
@@ -4555,7 +4600,7 @@ function getMonoThreadedGoToGoal(actualValue,goalValue,refreshMillis=null,totalT
|
|
|
4555
4600
|
value:actualValue,
|
|
4556
4601
|
goalValue:goalValue,
|
|
4557
4602
|
started:false,
|
|
4558
|
-
|
|
4603
|
+
paused:false,
|
|
4559
4604
|
time:getNow(),
|
|
4560
4605
|
refreshMillis:refreshMillis,
|
|
4561
4606
|
doOnEachStepCallback:doOnEachStepCallback,
|
|
@@ -4591,39 +4636,39 @@ function getMonoThreadedGoToGoal(actualValue,goalValue,refreshMillis=null,totalT
|
|
|
4591
4636
|
|
|
4592
4637
|
return self;
|
|
4593
4638
|
},
|
|
4594
|
-
start:function(callingObject){
|
|
4639
|
+
start:function(callingObject=null,args=null){
|
|
4595
4640
|
|
|
4596
4641
|
if( self.value===self.goalValue
|
|
4597
4642
|
// || self.isStarted();
|
|
4598
|
-
|| self.terminateFunction(callingObject)){
|
|
4643
|
+
|| self.terminateFunction(callingObject,args)){
|
|
4599
4644
|
|
|
4600
4645
|
// CAUTION : Even if the routine is «pre-terminated» before even starting
|
|
4601
4646
|
// (ie. its stop conditions are fullfilled even before it has started)
|
|
4602
4647
|
// we all the same have to trigger its stop treatments :
|
|
4603
|
-
if(self.doOnStop) self.doOnStop(callingObject);
|
|
4648
|
+
if(self.doOnStop) self.doOnStop(callingObject,args);
|
|
4604
4649
|
|
|
4605
4650
|
return;
|
|
4606
4651
|
}
|
|
4607
4652
|
|
|
4608
4653
|
self.started=true;
|
|
4609
4654
|
|
|
4610
|
-
self.
|
|
4655
|
+
self.paused=false;
|
|
4611
4656
|
|
|
4612
4657
|
return self;
|
|
4613
4658
|
},
|
|
4614
|
-
stop:function(callingObject=null){
|
|
4659
|
+
stop:function(callingObject=null,args=null){
|
|
4615
4660
|
|
|
4616
4661
|
if(!self.isStarted()) return;
|
|
4617
4662
|
self.started=false;
|
|
4618
4663
|
|
|
4619
|
-
if(self.doOnStop) self.doOnStop(callingObject);
|
|
4664
|
+
if(self.doOnStop) self.doOnStop(callingObject,args);
|
|
4620
4665
|
|
|
4621
4666
|
},
|
|
4622
|
-
|
|
4623
|
-
self.
|
|
4667
|
+
pause:function(){
|
|
4668
|
+
self.paused=true;
|
|
4624
4669
|
},
|
|
4625
4670
|
resume:function(){
|
|
4626
|
-
self.
|
|
4671
|
+
self.paused=false;
|
|
4627
4672
|
},
|
|
4628
4673
|
terminateFunction:function(){
|
|
4629
4674
|
|
|
@@ -4643,7 +4688,7 @@ function getMonoThreadedGoToGoal(actualValue,goalValue,refreshMillis=null,totalT
|
|
|
4643
4688
|
|
|
4644
4689
|
doStep:function(callingObject=null,args=null){
|
|
4645
4690
|
|
|
4646
|
-
if(!self.isStarted() || self.
|
|
4691
|
+
if(!self.isStarted() || self.paused) return self.value;
|
|
4647
4692
|
|
|
4648
4693
|
// Looping index with a delay :
|
|
4649
4694
|
|
|
@@ -5951,15 +5996,26 @@ function normalizeDisplayQuotesForJSONParsing(str){
|
|
|
5951
5996
|
|
|
5952
5997
|
//A polyvalent popup :
|
|
5953
5998
|
//CAUTION : Non-blocking of execution ! (uses callbacks)
|
|
5954
|
-
function promptWindow(label,type=null,defaultValue=null,
|
|
5999
|
+
function promptWindow(label,type=null,defaultValue=null,
|
|
6000
|
+
doOnOK=null,doOnCancel=null,
|
|
6001
|
+
config={displayMode:"popup",backgroundColor1:"#B3DCED",font:"helvetica",width:"80%",height:"80%"}){
|
|
5955
6002
|
|
|
6003
|
+
const LABEL_AND_DEFAULT_VALUE_SEPARATOR=":";
|
|
5956
6004
|
const MAX_Z_INDEX_OVERLAY_LOCAL=9999;// (Repeated because must be stand-alone constant...)
|
|
5957
|
-
const
|
|
5958
|
-
const
|
|
6005
|
+
const promptWindowWidth=(config && config.width?config.width:"80%");
|
|
6006
|
+
const promptWindowHeight=(config && config.height?config.height:"80%");
|
|
6007
|
+
// DOES NOT WORK : const promptWindowWidth="auto", promptWindowHeight="auto";
|
|
6008
|
+
|
|
6009
|
+
|
|
5959
6010
|
// const POSITION_X="7.5%";
|
|
5960
6011
|
// const POSITION_Y="5%";
|
|
5961
6012
|
const SPECIAL_SEPARATOR="@@@";
|
|
5962
6013
|
|
|
6014
|
+
|
|
6015
|
+
let resultPromiseOK=null;
|
|
6016
|
+
let resultPromiseCancel=null;
|
|
6017
|
+
|
|
6018
|
+
|
|
5963
6019
|
let displayMode=config && config.displayMode ? config.displayMode:"popup";
|
|
5964
6020
|
let backgroundColor1=config && config.backgroundColor1 ? config.backgroundColor1:"#B3DCED";
|
|
5965
6021
|
let backgroundColor2=config && config.backgroundColor2 ? config.backgroundColor2:"#29B8E5";
|
|
@@ -5975,12 +6031,11 @@ function promptWindow(label,type=null,defaultValue=null,doOnOK=null,doOnCancel=n
|
|
|
5975
6031
|
};
|
|
5976
6032
|
|
|
5977
6033
|
//Each field rendering :
|
|
5978
|
-
/* private */
|
|
6034
|
+
/* private */const getRenderedInputElement=function(inputType, defaultValueParam=null,/* OPTIONAL */labelParam=null){
|
|
5979
6035
|
|
|
5980
|
-
const LABEL_AND_DEFAULT_VALUE_SEPARATOR=":";
|
|
5981
6036
|
const DISABLED_OPACITY=0.6;
|
|
5982
6037
|
|
|
5983
|
-
|
|
6038
|
+
let inputElement=null;
|
|
5984
6039
|
if(contains(inputType, "password")){
|
|
5985
6040
|
inputElement=document.createElement("input");
|
|
5986
6041
|
if(defaultValueParam) inputElement.value=defaultValueParam;
|
|
@@ -6000,7 +6055,7 @@ function promptWindow(label,type=null,defaultValue=null,doOnOK=null,doOnCancel=n
|
|
|
6000
6055
|
|
|
6001
6056
|
} else if(contains(inputType, "uniqueChoice") || contains(inputType, "multipleChoice")){ // If we have a group of inputs for (a) value(s) outcome :
|
|
6002
6057
|
|
|
6003
|
-
|
|
6058
|
+
let choicesLabelsStrs=defaultValueParam;
|
|
6004
6059
|
if(typeof(defaultValueParam) === "string"){
|
|
6005
6060
|
choicesLabelsStrs=parseJSON(defaultValueParam);
|
|
6006
6061
|
}
|
|
@@ -6008,97 +6063,99 @@ function promptWindow(label,type=null,defaultValue=null,doOnOK=null,doOnCancel=n
|
|
|
6008
6063
|
inputElement=document.createElement("p");
|
|
6009
6064
|
inputElement.style.display="flex";
|
|
6010
6065
|
|
|
6011
|
-
|
|
6012
6066
|
|
|
6013
6067
|
// Represents a list with a unique or a multiple outcome value(s) :
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
let radioButtonContainer=document.createElement("div");
|
|
6017
|
-
radioButtonContainer.style.width="200px";
|
|
6018
|
-
// radioButtonContainer.style.height="400px";
|
|
6019
|
-
radioButtonContainer.style["padding-right"]="10px";
|
|
6020
|
-
radioButtonContainer.style["text-align"]="center";
|
|
6021
|
-
radioButtonContainer.style["font-size"]="10px";
|
|
6022
|
-
radioButtonContainer.style["line-height"]="10px";
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
let radio=document.createElement("input");
|
|
6068
|
+
|
|
6069
|
+
resultPromiseOK=new Promise((resolve, reject)=>{
|
|
6026
6070
|
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6071
|
+
foreach(choicesLabelsStrs,(choiceLabelStr,i)=>{
|
|
6072
|
+
|
|
6073
|
+
// Radio image card :
|
|
6074
|
+
const radioButtonContainer=document.createElement("div");
|
|
6075
|
+
radioButtonContainer.style.width="200px";
|
|
6076
|
+
// radioButtonContainer.style.height="400px";
|
|
6077
|
+
radioButtonContainer.style["padding-right"]="10px";
|
|
6078
|
+
radioButtonContainer.style["text-align"]="center";
|
|
6079
|
+
radioButtonContainer.style["font-size"]="10px";
|
|
6080
|
+
radioButtonContainer.style["line-height"]="10px";
|
|
6081
|
+
inputElement.appendChild(radioButtonContainer);
|
|
6082
|
+
|
|
6083
|
+
|
|
6084
|
+
|
|
6085
|
+
let radio=document.createElement("input");
|
|
6086
|
+
if(contains(inputType, "multipleChoice")){ // multipleChoice :
|
|
6087
|
+
radio.type="checkbox";
|
|
6088
|
+
radio.id="checkboxMultipleChoice_" + i;
|
|
6039
6089
|
|
|
6040
|
-
|
|
6041
|
-
radioButtonContainer.style.width=choiceLabelStr.width;
|
|
6042
|
-
if(choiceLabelStr.height)
|
|
6043
|
-
radioButtonContainer.style.height=choiceLabelStr.height;
|
|
6090
|
+
} else { // uniqueChoice :
|
|
6044
6091
|
|
|
6092
|
+
if(contains(inputType, "icons")){
|
|
6093
|
+
|
|
6094
|
+
radio.type="image";
|
|
6095
|
+
if(typeof(choiceLabelStr) === "string"){
|
|
6096
|
+
radio.src=choiceLabelStr.trim();
|
|
6097
|
+
}else{
|
|
6098
|
+
|
|
6099
|
+
if(choiceLabelStr.width) radioButtonContainer.style.width=choiceLabelStr.width;
|
|
6100
|
+
if(choiceLabelStr.height) radioButtonContainer.style.height=choiceLabelStr.height;
|
|
6101
|
+
|
|
6102
|
+
radio.src=choiceLabelStr.src.trim();
|
|
6103
|
+
|
|
6104
|
+
if(choiceLabelStr.inactive){
|
|
6105
|
+
radio.disabled=true;
|
|
6106
|
+
radio.style.opacity=DISABLED_OPACITY;
|
|
6107
|
+
}
|
|
6108
|
+
}
|
|
6109
|
+
|
|
6110
|
+
// Icon substitutes itself to the OK button :
|
|
6111
|
+
|
|
6112
|
+
radio.onclick=function(event){
|
|
6113
|
+
let promptedValue=event.target.indexValue;
|
|
6114
|
+
|
|
6115
|
+
if(!validationFunction(promptedValue)){
|
|
6116
|
+
// TRACE
|
|
6117
|
+
alert(validationFunction.errorMessage);
|
|
6118
|
+
return;
|
|
6119
|
+
}
|
|
6120
|
+
|
|
6121
|
+
if(doOnOK){
|
|
6122
|
+
const doOnOKResult=doOnOK(promptedValue);
|
|
6123
|
+
resolve({value:promptedValue,result:doOnOKResult});
|
|
6124
|
+
}
|
|
6125
|
+
closeWindow();
|
|
6126
|
+
|
|
6127
|
+
};
|
|
6128
|
+
|
|
6129
|
+
|
|
6130
|
+
}else{
|
|
6131
|
+
radio.type="radio";
|
|
6132
|
+
}
|
|
6045
6133
|
|
|
6046
|
-
radio.
|
|
6134
|
+
radio.id="radioButtonUniqueChoice_" + i;
|
|
6135
|
+
radio.name="radioButtonsUniqueChoice";
|
|
6047
6136
|
|
|
6048
|
-
if(choiceLabelStr.inactive){
|
|
6049
|
-
radio.disabled=true;
|
|
6050
|
-
radio.style.opacity=DISABLED_OPACITY;
|
|
6051
|
-
}
|
|
6052
6137
|
}
|
|
6053
6138
|
|
|
6054
|
-
// Icon substitutes itself to the OK button :
|
|
6055
|
-
radio.onclick=function(event){
|
|
6056
|
-
var promptedValue=event.target.indexValue;
|
|
6057
|
-
|
|
6058
|
-
if(!validationFunction(promptedValue)){
|
|
6059
|
-
// TRACE
|
|
6060
|
-
alert(validationFunction.errorMessage);
|
|
6061
|
-
return;
|
|
6062
|
-
}
|
|
6063
|
-
|
|
6064
|
-
if(doOnOK){
|
|
6065
|
-
doOnOK(promptedValue);
|
|
6066
|
-
}
|
|
6067
|
-
closeWindow();
|
|
6068
|
-
};
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
}else{
|
|
6074
6139
|
|
|
6075
|
-
radio.
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
radioButtonContainer.appendChild(radio);
|
|
6087
|
-
|
|
6088
|
-
var choiceLabel=document.createElement("label");
|
|
6089
|
-
if(typeof(choiceLabelStr) === "string"){
|
|
6090
|
-
choiceLabel.innerHTML=choiceLabelStr.trim();
|
|
6091
|
-
}else{
|
|
6092
|
-
choiceLabel.innerHTML=choiceLabelStr.html.trim();
|
|
6093
|
-
}
|
|
6094
|
-
choiceLabel.htmlFor=radio.id;
|
|
6095
|
-
radioButtonContainer.appendChild(choiceLabel);
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
inputElement.appendChild(radioButtonContainer);
|
|
6140
|
+
radio.indexValue=i;
|
|
6141
|
+
radioButtonContainer.appendChild(radio);
|
|
6142
|
+
|
|
6143
|
+
let choiceLabel=document.createElement("label");
|
|
6144
|
+
if(typeof(choiceLabelStr)==="string"){
|
|
6145
|
+
choiceLabel.innerHTML=choiceLabelStr.trim();
|
|
6146
|
+
}else{
|
|
6147
|
+
choiceLabel.innerHTML=choiceLabelStr.html.trim();
|
|
6148
|
+
}
|
|
6149
|
+
choiceLabel.htmlFor=radio.id;
|
|
6150
|
+
radioButtonContainer.appendChild(choiceLabel);
|
|
6099
6151
|
|
|
6100
|
-
|
|
6152
|
+
|
|
6153
|
+
|
|
6154
|
+
});
|
|
6155
|
+
|
|
6101
6156
|
});
|
|
6157
|
+
|
|
6158
|
+
|
|
6102
6159
|
|
|
6103
6160
|
// We redefine the function used to retrieve entered value :
|
|
6104
6161
|
if(contains(inputType, "icons")){
|
|
@@ -6108,11 +6165,11 @@ function promptWindow(label,type=null,defaultValue=null,doOnOK=null,doOnCancel=n
|
|
|
6108
6165
|
}else{
|
|
6109
6166
|
|
|
6110
6167
|
inputElement.getResultValue=function(){
|
|
6111
|
-
|
|
6168
|
+
let result="";
|
|
6112
6169
|
|
|
6113
6170
|
// OLD :
|
|
6114
6171
|
// jQuery(inputElement).find("input:checked").each(function(){
|
|
6115
|
-
//
|
|
6172
|
+
// let radioSlct=jQuery(this);
|
|
6116
6173
|
// result+=(nothing(result) ? "" : ",") + radioSlct.get(0).indexValue;
|
|
6117
6174
|
// });
|
|
6118
6175
|
|
|
@@ -6127,9 +6184,7 @@ function promptWindow(label,type=null,defaultValue=null,doOnOK=null,doOnCancel=n
|
|
|
6127
6184
|
}
|
|
6128
6185
|
|
|
6129
6186
|
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
} else { // DEFAULT TYPE IS TEXT AREA :
|
|
6187
|
+
} else if(contains(inputType, "textarea")){
|
|
6133
6188
|
|
|
6134
6189
|
inputElement=document.createElement("textarea");
|
|
6135
6190
|
if(defaultValueParam)
|
|
@@ -6138,14 +6193,16 @@ function promptWindow(label,type=null,defaultValue=null,doOnOK=null,doOnCancel=n
|
|
|
6138
6193
|
inputElement.rows="20";
|
|
6139
6194
|
|
|
6140
6195
|
}
|
|
6196
|
+
// DEFAULT TYPE IS "yesno"
|
|
6141
6197
|
|
|
6198
|
+
|
|
6142
6199
|
inputElement.style.padding="4px";
|
|
6143
6200
|
inputElement.style.margin="0";
|
|
6144
6201
|
|
|
6145
6202
|
// The default function used to retrieve entered value :
|
|
6146
6203
|
if(!inputElement.getResultValue){
|
|
6147
6204
|
inputElement.getResultValue=function(){
|
|
6148
|
-
|
|
6205
|
+
let result=inputElement.value;
|
|
6149
6206
|
return result;
|
|
6150
6207
|
};
|
|
6151
6208
|
}
|
|
@@ -6180,10 +6237,12 @@ function promptWindow(label,type=null,defaultValue=null,doOnOK=null,doOnCancel=n
|
|
|
6180
6237
|
if(contains(displayMode,":")){
|
|
6181
6238
|
let s=displayMode.split(":");
|
|
6182
6239
|
divModal.style.opacity=s[1];
|
|
6240
|
+
}else{
|
|
6241
|
+
divModal.style.opacity="0.85";// Default modal transparency value
|
|
6183
6242
|
}
|
|
6184
6243
|
|
|
6185
6244
|
divModal.appendChild(divWindow);
|
|
6186
|
-
//
|
|
6245
|
+
// parent.appendChild(divModal);
|
|
6187
6246
|
|
|
6188
6247
|
appendAsFirstChildOf(divModal, parent);
|
|
6189
6248
|
}else{
|
|
@@ -6198,12 +6257,12 @@ function promptWindow(label,type=null,defaultValue=null,doOnOK=null,doOnCancel=n
|
|
|
6198
6257
|
|
|
6199
6258
|
divWindow.className="promptWindowClass";
|
|
6200
6259
|
|
|
6201
|
-
|
|
6202
|
-
style+="position:
|
|
6203
|
-
|
|
6204
|
-
style+="margin:auto;";
|
|
6260
|
+
let style="";
|
|
6261
|
+
style+="position:absolute;";
|
|
6262
|
+
style+="left:50%;top:50%;";
|
|
6205
6263
|
style+="z-index:" + (MAX_Z_INDEX_OVERLAY_LOCAL) + ";";
|
|
6206
|
-
style+="
|
|
6264
|
+
style+="z-index:" + (MAX_Z_INDEX_OVERLAY_LOCAL) + ";";
|
|
6265
|
+
style+="width:"+promptWindowWidth+";height:"+promptWindowHeight+"; ";
|
|
6207
6266
|
style+="border:#888888 8px dotted;padding:10px;opacity:1;";
|
|
6208
6267
|
style+="font-family:"+font+";";
|
|
6209
6268
|
style+="color:"+textColor+";";
|
|
@@ -6219,10 +6278,12 @@ function promptWindow(label,type=null,defaultValue=null,doOnOK=null,doOnCancel=n
|
|
|
6219
6278
|
|
|
6220
6279
|
divWindow.style=style;
|
|
6221
6280
|
|
|
6222
|
-
|
|
6281
|
+
divWindow.style["margin-left"]=(-divWindow.offsetWidth/2)+"px";
|
|
6282
|
+
divWindow.style["margin-top"]= (-divWindow.offsetHeight/2)+"px";
|
|
6283
|
+
|
|
6223
6284
|
|
|
6224
6285
|
// The label for the title of the window :
|
|
6225
|
-
|
|
6286
|
+
let labelElement=document.createElement("div");
|
|
6226
6287
|
labelElement.innerHTML=label;
|
|
6227
6288
|
labelElement.style="padding-top:0;padding-bottom:10px;";
|
|
6228
6289
|
// Positioning :
|
|
@@ -6232,21 +6293,21 @@ function promptWindow(label,type=null,defaultValue=null,doOnOK=null,doOnCancel=n
|
|
|
6232
6293
|
// The container containing all the elements :
|
|
6233
6294
|
let divContainer=document.createElement("div");
|
|
6234
6295
|
divContainer.id="divPromptContainer";
|
|
6235
|
-
divContainer.style="overflow-y:auto;width:100%;height:
|
|
6296
|
+
divContainer.style="overflow-y:auto;width:100%;max-height:83%;";
|
|
6236
6297
|
divWindow.appendChild(divContainer);
|
|
6237
6298
|
|
|
6238
6299
|
|
|
6239
6300
|
|
|
6240
6301
|
|
|
6241
6302
|
// Eventual validation :
|
|
6242
|
-
|
|
6303
|
+
let validationFunction=function(value){
|
|
6243
6304
|
return true;/* DO NOTHING */
|
|
6244
6305
|
};
|
|
6245
6306
|
validationFunction.errorMessage="";
|
|
6246
6307
|
if(!nothing(type) && contains(type, ":")){
|
|
6247
|
-
|
|
6308
|
+
let typeParams=type.split(":");
|
|
6248
6309
|
if(typeParams.length > 1){
|
|
6249
|
-
|
|
6310
|
+
let validationType=typeParams[1];
|
|
6250
6311
|
|
|
6251
6312
|
if(validationType === "email"){
|
|
6252
6313
|
validationFunction=function(value){
|
|
@@ -6259,19 +6320,20 @@ function promptWindow(label,type=null,defaultValue=null,doOnOK=null,doOnCancel=n
|
|
|
6259
6320
|
|
|
6260
6321
|
|
|
6261
6322
|
// We define the global function used to retrieve entered value :
|
|
6262
|
-
|
|
6263
|
-
|
|
6323
|
+
let getGlobalResultValue=null;
|
|
6324
|
+
const inputElements=[];
|
|
6264
6325
|
|
|
6265
6326
|
// Case the window is actually a form of several fields :
|
|
6266
6327
|
if(!nothing(type) && containsIgnoreCase(type, "form{")){
|
|
6267
|
-
|
|
6328
|
+
|
|
6329
|
+
let namesWithTypesAndDefaultValuesStr=type.replace("form{", "{");
|
|
6268
6330
|
try {
|
|
6269
6331
|
|
|
6270
|
-
|
|
6332
|
+
let defaultValues=new Array();
|
|
6271
6333
|
if(!nothing(defaultValue)) defaultValues=defaultValue.split(SPECIAL_SEPARATOR);
|
|
6272
6334
|
|
|
6273
|
-
|
|
6274
|
-
|
|
6335
|
+
let namesWithTypesAndDefaultValues=parseJSON(namesWithTypesAndDefaultValuesStr);
|
|
6336
|
+
let j=0;
|
|
6275
6337
|
foreach(namesWithTypesAndDefaultValues,(inputItemDescriptionString,key)=>{
|
|
6276
6338
|
|
|
6277
6339
|
let typeInputItem=inputItemDescriptionString;
|
|
@@ -6293,9 +6355,9 @@ function promptWindow(label,type=null,defaultValue=null,doOnOK=null,doOnCancel=n
|
|
|
6293
6355
|
|
|
6294
6356
|
// We redefine the global function used to retrieve entered value :
|
|
6295
6357
|
getGlobalResultValue=function(){
|
|
6296
|
-
|
|
6358
|
+
let result="";
|
|
6297
6359
|
|
|
6298
|
-
for(
|
|
6360
|
+
for(let k=0; k<inputElements.length; k++){
|
|
6299
6361
|
result+=(nothing(result) ? "" : SPECIAL_SEPARATOR) + inputElements[k].getResultValue();
|
|
6300
6362
|
}
|
|
6301
6363
|
|
|
@@ -6303,22 +6365,22 @@ function promptWindow(label,type=null,defaultValue=null,doOnOK=null,doOnCancel=n
|
|
|
6303
6365
|
};
|
|
6304
6366
|
|
|
6305
6367
|
} catch (e1){
|
|
6306
|
-
|
|
6368
|
+
let errorDiv=document.createElement("div");
|
|
6307
6369
|
errorDiv.innerHTML="Error parsing JSON «" + namesWithTypesAndDefaultValuesStr + "» : ERROR «:" + e1 + "»";
|
|
6308
6370
|
inputElements.push(errorDiv);
|
|
6309
6371
|
}
|
|
6310
6372
|
|
|
6311
6373
|
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
//
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
|
|
6318
|
-
|
|
6319
|
-
}else { // Case of a single field :
|
|
6374
|
+
} else if(type==="yesno"){ // Case yes/no
|
|
6375
|
+
|
|
6376
|
+
// We redefine the global function used to retrieve entered value :
|
|
6377
|
+
getGlobalResultValue=function(){
|
|
6378
|
+
return null;
|
|
6379
|
+
};
|
|
6380
|
+
|
|
6381
|
+
} else { // Case of a single field :
|
|
6320
6382
|
|
|
6321
|
-
|
|
6383
|
+
let singleInputElement=getRenderedInputElement(type, defaultValue);
|
|
6322
6384
|
inputElements.push(singleInputElement);
|
|
6323
6385
|
|
|
6324
6386
|
// Focus on password field :
|
|
@@ -6335,50 +6397,65 @@ function promptWindow(label,type=null,defaultValue=null,doOnOK=null,doOnCancel=n
|
|
|
6335
6397
|
// TODO : FIXME : Focus on text field is not working.... :,-(
|
|
6336
6398
|
// inputElements[inputElements.length - 1].focus();
|
|
6337
6399
|
|
|
6338
|
-
|
|
6400
|
+
// This button is displayed in all cases :
|
|
6401
|
+
let buttonCancel=document.createElement("button");
|
|
6339
6402
|
buttonCancel.innerHTML="Cancel";
|
|
6340
|
-
buttonCancel.style="display:block;float:right;
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
var buttonOK=document.createElement("button");
|
|
6355
|
-
buttonOK.innerHTML="OK";
|
|
6356
|
-
buttonOK.style="display:block;margin-top:8px;margin-bottom:10px;";
|
|
6357
|
-
buttonOK.onclick=function(){
|
|
6358
|
-
var promptedValue=getGlobalResultValue();
|
|
6359
|
-
|
|
6360
|
-
if(promptedValue!=null && !validationFunction(promptedValue)){
|
|
6361
|
-
// TRACE
|
|
6362
|
-
alert(validationFunction.errorMessage);
|
|
6363
|
-
return;
|
|
6364
|
-
}
|
|
6403
|
+
buttonCancel.style="display:block;float:right;";
|
|
6404
|
+
resultPromiseCancel=new Promise((resolve, reject)=>{
|
|
6405
|
+
buttonCancel.onclick=function(){
|
|
6406
|
+
let promptedValue=getGlobalResultValue();
|
|
6407
|
+
if(doOnCancel){
|
|
6408
|
+
const doOnCancelResult=doOnCancel(promptedValue);
|
|
6409
|
+
resolve({value:promptedValue,result:doOnCancelResult});
|
|
6410
|
+
}
|
|
6411
|
+
closeWindow();
|
|
6412
|
+
};
|
|
6413
|
+
});
|
|
6414
|
+
divWindow.appendChild(buttonCancel);
|
|
6415
|
+
|
|
6365
6416
|
|
|
6366
|
-
if(doOnOK){
|
|
6367
|
-
doOnOK(promptedValue);
|
|
6368
|
-
}
|
|
6369
|
-
closeWindow();
|
|
6370
|
-
};
|
|
6371
6417
|
|
|
6372
|
-
|
|
6373
|
-
|
|
6418
|
+
// We hide this OK button, when we are in a "radio-image" unique selection case,
|
|
6419
|
+
// since the doOnOK() is triggered when we click on the "radio-image" icon,
|
|
6420
|
+
// thus rendering this OK button unuseful ! :
|
|
6421
|
+
if(nothing(type) || !contains(type, "uniqueChoice")){
|
|
6422
|
+
|
|
6423
|
+
let buttonOK=document.createElement("button");
|
|
6424
|
+
buttonOK.innerHTML="OK";
|
|
6425
|
+
buttonOK.style="display:block;margin-bottom:10px;";
|
|
6426
|
+
|
|
6427
|
+
resultPromiseOK=new Promise((resolve, reject)=>{
|
|
6428
|
+
|
|
6429
|
+
buttonOK.onclick=function(){
|
|
6430
|
+
let promptedValue=getGlobalResultValue();
|
|
6431
|
+
|
|
6432
|
+
if(promptedValue!=null && !validationFunction(promptedValue)){
|
|
6433
|
+
// TRACE
|
|
6434
|
+
alert(validationFunction.errorMessage);
|
|
6435
|
+
return;
|
|
6436
|
+
}
|
|
6437
|
+
|
|
6438
|
+
if(doOnOK){
|
|
6439
|
+
const doOnOKResult=doOnOK(promptedValue);
|
|
6440
|
+
resolve({value:promptedValue,result:doOnOKResult});
|
|
6441
|
+
}
|
|
6442
|
+
closeWindow();
|
|
6443
|
+
};
|
|
6444
|
+
|
|
6445
|
+
});
|
|
6446
|
+
divWindow.appendChild(buttonOK);
|
|
6447
|
+
}
|
|
6448
|
+
|
|
6449
|
+
// Final integration of the generated input elements :
|
|
6450
|
+
for(let i=0; i<inputElements.length; i++){
|
|
6374
6451
|
if(0<i){
|
|
6375
|
-
|
|
6452
|
+
const br=document.createElement("br");
|
|
6376
6453
|
divContainer.appendChild(br);
|
|
6377
6454
|
}
|
|
6378
6455
|
|
|
6379
6456
|
// The label :
|
|
6380
6457
|
if(inputElements[i].label){
|
|
6381
|
-
|
|
6458
|
+
const divLabel=document.createElement("div");
|
|
6382
6459
|
divLabel.innerHTML=inputElements[i].label;
|
|
6383
6460
|
divContainer.appendChild(divLabel);
|
|
6384
6461
|
}
|
|
@@ -6386,14 +6463,16 @@ function promptWindow(label,type=null,defaultValue=null,doOnOK=null,doOnCancel=n
|
|
|
6386
6463
|
divContainer.appendChild(inputElements[i]);
|
|
6387
6464
|
}
|
|
6388
6465
|
|
|
6389
|
-
// Positioning :
|
|
6390
|
-
divWindow.appendChild(buttonCancel);
|
|
6391
|
-
divWindow.appendChild(buttonOK);
|
|
6392
|
-
|
|
6393
6466
|
|
|
6467
|
+
return {
|
|
6468
|
+
toPromise:(issue="OK")=>(issue=="OK"?resultPromiseOK:resultPromiseCancel),
|
|
6469
|
+
toPromises:()=>{ return {OK:resultPromiseOK,cancel:resultPromiseCancel}; }
|
|
6470
|
+
};
|
|
6394
6471
|
}
|
|
6395
6472
|
|
|
6396
6473
|
|
|
6474
|
+
|
|
6475
|
+
|
|
6397
6476
|
getInputCoords=function(event){
|
|
6398
6477
|
return (((event.clientX && event.clientY) || !event.touches) ? event : event.touches.item(0) );
|
|
6399
6478
|
}
|
|
@@ -6689,66 +6768,419 @@ function getPointsFromSVGDString(str){
|
|
|
6689
6768
|
|
|
6690
6769
|
|
|
6691
6770
|
|
|
6692
|
-
/*utils GEOMETRY library associated with aotra version : «1.0.0.000 (11/01/2022-00:18:33)»*/
|
|
6693
|
-
/*-----------------------------------------------------------------------------*/
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
/* ## Utility global methods in a javascript, at least console (nodejs) server, or vanilla javascript with no browser environment.
|
|
6697
|
-
*
|
|
6698
|
-
* This set of methods gathers utility generic-purpose methods usable in any JS project.
|
|
6699
|
-
* Several authors of snippets published freely on the Internet contributed to this library.
|
|
6700
|
-
* Feel free to use/modify-enhance/publish them under the terms of its license.
|
|
6701
|
-
*
|
|
6702
|
-
* # Library name : «aotrautils»
|
|
6703
|
-
* # Library license : HGPL(Help Burma) (see aotra README information for details : https://alqemia.com/aotra.js )
|
|
6704
|
-
* # Author name : Jérémie Ratomposon (massively helped by its programming egos legions)
|
|
6705
|
-
* # Author email : info@alqemia.com
|
|
6706
|
-
* # Organization name : Alqemia
|
|
6707
|
-
* # Organization email : admin@alqemia.com
|
|
6708
|
-
* # Organization website : https://alqemia.com
|
|
6709
|
-
*
|
|
6710
|
-
*
|
|
6711
|
-
*/
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
// COMPATIBILITY browser javascript / nodejs environment :
|
|
6715
|
-
|
|
6716
|
-
if(typeof(window)==="undefined") window=global;
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
6771
|
|
|
6720
|
-
//=========================================================================
|
|
6721
|
-
// GLOBAL CONSTANTS :
|
|
6722
6772
|
|
|
6723
6773
|
|
|
6724
|
-
Math.TAU=6.28318530718; // == Math.PI*2
|
|
6725
|
-
Math.PSI=1.57079632679; // == Math.PI/2
|
|
6726
|
-
Math.TAN_RATIOS_DEGREES={
|
|
6727
|
-
22.5:0.4142135624,
|
|
6728
|
-
45:1,
|
|
6729
|
-
67.5:2.4142135624,
|
|
6730
|
-
112.5:-2.4142135624,
|
|
6731
|
-
135:-1,
|
|
6732
|
-
157.5:-0.4142135624,
|
|
6733
|
-
202.5:0.4142135624,
|
|
6734
|
-
225:1,
|
|
6735
|
-
247.5:2.4142135624,
|
|
6736
|
-
292.5:-2.4142135624,
|
|
6737
|
-
315:-1,
|
|
6738
|
-
337.5:-0.4142135624
|
|
6739
|
-
};
|
|
6740
|
-
Math.LN_2_INVERTED=1.44269504089; // == 1/ln(2)
|
|
6741
6774
|
|
|
6742
|
-
Math.INVERSE_OF_SQUARE_OF_TWO=0.707106781; // == 1/sqrt(2)
|
|
6743
|
-
Math.RATIO_TO_DEGREES=57.295779513; // == 180/PI
|
|
6744
|
-
Math.RATIO_TO_RADIANS=0.017453293; // == PI/180
|
|
6745
6775
|
|
|
6776
|
+
// -Client :
|
|
6746
6777
|
|
|
6778
|
+
// TODO : Use everywhere it's applicable !
|
|
6779
|
+
initClient=function(isNodeContext=true, useSocketIOImplementation=false, doOnServerConnection=null, url, /*OPTIONAL*/port=25000, isSecure=true, /*OPTIONAL*/timeout=10000, /*OPTIONAL*/selfParam=null){
|
|
6780
|
+
let aotraClient={};
|
|
6781
|
+
|
|
6782
|
+
aotraClient.client={};
|
|
6783
|
+
aotraClient.client.start=function(){
|
|
6784
|
+
|
|
6785
|
+
|
|
6786
|
+
let socketToServer=WebsocketImplementation.getStatic(isNodeContext,useSocketIOImplementation).connectToServer(url, port, isSecure, timeout);
|
|
6787
|
+
if(!socketToServer){
|
|
6788
|
+
// TRACE
|
|
6789
|
+
lognow("ERROR : CLIENT : Could not get socketToServer, aborting client connection.");
|
|
6790
|
+
return null;
|
|
6791
|
+
}
|
|
6792
|
+
|
|
6793
|
+
// When client is connected, we execute the callback :
|
|
6794
|
+
// CAUTION : MUST BE CALLED ONLY ONCE !
|
|
6795
|
+
socketToServer.onConnectionToServer(()=>{
|
|
6796
|
+
if(doOnServerConnection){
|
|
6797
|
+
if(selfParam) doOnServerConnection.apply(selfParam,[socketToServer]);
|
|
6798
|
+
else doOnServerConnection(socketToServer);
|
|
6799
|
+
}
|
|
6800
|
+
});
|
|
6747
6801
|
|
|
6748
|
-
|
|
6802
|
+
// // Errors handling :
|
|
6803
|
+
// if(doOnError && useSocketIOImplementation /*TODO : FIXME: FOR NOW, ONLY WORKS FOR SOCKET.IO IMPLEMENTATION !'*/){
|
|
6804
|
+
//
|
|
6805
|
+
//// // DBG
|
|
6806
|
+
//// lognow(">>> serverSocket.connected:"+serverSocket.connected);
|
|
6807
|
+
//// lognow(">>> serverSocket:",serverSocket);
|
|
6808
|
+
//
|
|
6809
|
+
//
|
|
6810
|
+
// const errorMethod=function(){
|
|
6811
|
+
// // TRACE
|
|
6812
|
+
// lognow("ERROR : CLIENT : Client encountered an error while trying to connect to server.");
|
|
6813
|
+
//
|
|
6814
|
+
// if(selfParam) doOnError.apply(selfParam);
|
|
6815
|
+
// else doOnError();
|
|
6816
|
+
// };
|
|
6817
|
+
//
|
|
6818
|
+
// socketToServer.clientSocket.on("connect_error", errorMethod);
|
|
6819
|
+
//
|
|
6820
|
+
//// // DOES NOT SEEM TO WORK :
|
|
6821
|
+
//// socketToServer.on("connect_failed", errorMethod);
|
|
6822
|
+
//// // DOES NOT SEEM TO WORK :
|
|
6823
|
+
//// socketToServer.on("error", errorMethod);
|
|
6824
|
+
//
|
|
6825
|
+
// }
|
|
6826
|
+
//
|
|
6827
|
+
// // Data messages handling :
|
|
6828
|
+
// if(doOnMessage){
|
|
6829
|
+
// socketToServer.onIncomingMessage((message)=>{
|
|
6830
|
+
// if(selfParam) doOnMessage.apply(selfParam,[message]);
|
|
6831
|
+
// else doOnMessage(message);
|
|
6832
|
+
// });
|
|
6833
|
+
// }
|
|
6834
|
+
|
|
6835
|
+
// DBG
|
|
6836
|
+
lognow("aotraClient.clientSocket:",aotraClient.clientSocket);
|
|
6837
|
+
|
|
6838
|
+
// DBG
|
|
6839
|
+
// aotraClient.clientSocket.addEventListener("close",()=>{
|
|
6840
|
+
// // TRACE
|
|
6841
|
+
// lognow("WARN : CONNECTION CLOSED !");
|
|
6842
|
+
// });
|
|
6749
6843
|
|
|
6844
|
+
|
|
6845
|
+
aotraClient.client.socketToServer=socketToServer;
|
|
6846
|
+
|
|
6847
|
+
return aotraClient;
|
|
6848
|
+
};
|
|
6849
|
+
|
|
6850
|
+
|
|
6851
|
+
return aotraClient;
|
|
6852
|
+
}
|
|
6750
6853
|
|
|
6751
|
-
|
|
6854
|
+
|
|
6855
|
+
|
|
6856
|
+
|
|
6857
|
+
// ========================= FUSRODA CLIENT : =========================
|
|
6858
|
+
|
|
6859
|
+
class VNCFrame2D{
|
|
6860
|
+
|
|
6861
|
+
constructor(url, port=6080, isSecure=true, htmlConfig={parentElement:null,imageMode:"CSSBackground"}, mouseEventsRefreshMillis=100){
|
|
6862
|
+
|
|
6863
|
+
|
|
6864
|
+
this.url=url;
|
|
6865
|
+
this.port=port;
|
|
6866
|
+
this.isSecure=isSecure;
|
|
6867
|
+
this.parentElement=nonull(htmlConfig.parentElement, document.body);
|
|
6868
|
+
this.imageMode=htmlConfig.imageMode;
|
|
6869
|
+
this.mouseEventsRefreshMillis=mouseEventsRefreshMillis;
|
|
6870
|
+
|
|
6871
|
+
|
|
6872
|
+
this.fusrodaClient=null;
|
|
6873
|
+
|
|
6874
|
+
|
|
6875
|
+
this.screenAndAudioConfig=null;
|
|
6876
|
+
this.image=null;
|
|
6877
|
+
self.audioCtx=null;
|
|
6878
|
+
|
|
6879
|
+
if(this.imageMode=="canvas"){
|
|
6880
|
+
this.canvas=document.createElement("canvas");
|
|
6881
|
+
this.parentElement.appendChild(this.canvas);
|
|
6882
|
+
}
|
|
6883
|
+
|
|
6884
|
+
this.lastPointerMoveTime=null;
|
|
6885
|
+
|
|
6886
|
+
}
|
|
6887
|
+
|
|
6888
|
+
|
|
6889
|
+
start(){
|
|
6890
|
+
|
|
6891
|
+
const self=this;
|
|
6892
|
+
this.fusrodaClient=createFusrodaClient(
|
|
6893
|
+
// doOnClientReady:
|
|
6894
|
+
(messageConfig)=>{
|
|
6895
|
+
|
|
6896
|
+
self.screenAndAudioConfig=messageConfig;
|
|
6897
|
+
|
|
6898
|
+
if(self.imageMode=="canvas"){
|
|
6899
|
+
self.image=new Image();
|
|
6900
|
+
self.image.addEventListener("load",(event)=>{
|
|
6901
|
+
|
|
6902
|
+
|
|
6903
|
+
|
|
6904
|
+
const image=event.target;
|
|
6905
|
+
image.isImageLoading=false;
|
|
6906
|
+
|
|
6907
|
+
if(!self.canvasSizeHasBeenSet){
|
|
6908
|
+
self.canvas.width=image.width;
|
|
6909
|
+
self.canvas.height=image.height;
|
|
6910
|
+
self.canvasSizeHasBeenSet=true;
|
|
6911
|
+
}
|
|
6912
|
+
|
|
6913
|
+
self.canvas.getContext("2d").drawImage(image,0,0);
|
|
6914
|
+
|
|
6915
|
+
|
|
6916
|
+
self.fusrodaClient.client.socketToServer.send("screenFrameRequest", {});
|
|
6917
|
+
|
|
6918
|
+
});
|
|
6919
|
+
|
|
6920
|
+
}
|
|
6921
|
+
|
|
6922
|
+
|
|
6923
|
+
self.audioCtx=new AudioContext({sampleRate: self.screenAndAudioConfig.sampleRate});
|
|
6924
|
+
|
|
6925
|
+
|
|
6926
|
+
},// doOnDataReception:
|
|
6927
|
+
{
|
|
6928
|
+
"image":(imageDataStr)=>{
|
|
6929
|
+
|
|
6930
|
+
|
|
6931
|
+
const base64String="data:image/png;base64,"+imageDataStr;
|
|
6932
|
+
if(self.imageMode=="canvas"){
|
|
6933
|
+
if(self.image.complete){
|
|
6934
|
+
self.image.src=base64String;
|
|
6935
|
+
self.image.isImageLoading=true;
|
|
6936
|
+
}
|
|
6937
|
+
}else{
|
|
6938
|
+
self.parentElement.style["background-image"]="url('"+base64String+"');";
|
|
6939
|
+
}
|
|
6940
|
+
|
|
6941
|
+
|
|
6942
|
+
},
|
|
6943
|
+
"sound":(soundDataStr)=>{
|
|
6944
|
+
const soundData=getDecodedArrayFromSoundDataString(soundDataStr);
|
|
6945
|
+
playAudioData(soundData,self.audioCtx,self.screenAndAudioConfig.audioBufferSize,1,self.screenAndAudioConfig.sampleRate,()=>{
|
|
6946
|
+
// doOnEndedPlaying
|
|
6947
|
+
|
|
6948
|
+
self.fusrodaClient.client.socketToServer.send("soundSampleRequest", {});
|
|
6949
|
+
|
|
6950
|
+
});
|
|
6951
|
+
}
|
|
6952
|
+
}, this.url, this.port, this.isSecure);
|
|
6953
|
+
|
|
6954
|
+
|
|
6955
|
+
// TODO : FIXME : DUPLICATED CODE !
|
|
6956
|
+
this.parentElement.addEventListener("pointermove",(event)=>{
|
|
6957
|
+
|
|
6958
|
+
const config=self.screenAndAudioConfig;
|
|
6959
|
+
if(!config || !self.canvas.width) return;
|
|
6960
|
+
|
|
6961
|
+
|
|
6962
|
+
if(!hasDelayPassed(self.lastPointerMoveTime,self.mouseEventsRefreshMillis)) return;
|
|
6963
|
+
self.lastPointerMoveTime=getNow();
|
|
6964
|
+
|
|
6965
|
+
|
|
6966
|
+
const width=config.width;
|
|
6967
|
+
const height=config.height;
|
|
6968
|
+
|
|
6969
|
+
const x=Math.floor((event.clientX/self.canvas.width)*width);
|
|
6970
|
+
const y=Math.floor((event.clientY/self.canvas.height)*height);
|
|
6971
|
+
|
|
6972
|
+
|
|
6973
|
+
self.fusrodaClient.sendMouseMoveEvent({x:x,y:y});
|
|
6974
|
+
|
|
6975
|
+
// DBG
|
|
6976
|
+
lognow("sendMouseMoveEvent:"+x+";"+y);
|
|
6977
|
+
|
|
6978
|
+
});
|
|
6979
|
+
// TODO : FIXME : DUPLICATED CODE !
|
|
6980
|
+
this.parentElement.addEventListener("mousedown",(event)=>{
|
|
6981
|
+
const mouseButton=event.button;
|
|
6982
|
+
self.fusrodaClient.sendMouseClickEvent({mouseButton:mouseButton});
|
|
6983
|
+
|
|
6984
|
+
// DBG
|
|
6985
|
+
lognow("mousedown:",event);
|
|
6986
|
+
});
|
|
6987
|
+
// TODO : FIXME : DUPLICATED CODE !
|
|
6988
|
+
this.parentElement.addEventListener("mouseup",(event)=>{
|
|
6989
|
+
const mouseButton=event.button;
|
|
6990
|
+
self.fusrodaClient.sendMouseReleasedEvent({mouseButton:mouseButton});
|
|
6991
|
+
});
|
|
6992
|
+
// TODO : FIXME : DUPLICATED CODE !
|
|
6993
|
+
const WHEEL_EVENT_FACTOR=.01;
|
|
6994
|
+
this.parentElement.addEventListener("wheel",(event)=>{
|
|
6995
|
+
const wheelAmount=event.deltaY*WHEEL_EVENT_FACTOR;
|
|
6996
|
+
self.fusrodaClient.sendWheelEvent({wheelAmount:wheelAmount});
|
|
6997
|
+
|
|
6998
|
+
// DBG
|
|
6999
|
+
lognow("wheel:",event);
|
|
7000
|
+
});
|
|
7001
|
+
// TODO : FIXME : DUPLICATED CODE !
|
|
7002
|
+
// To remove the local contextual menu :
|
|
7003
|
+
this.parentElement.addEventListener("contextmenu",(event)=>{event.preventDefault();});
|
|
7004
|
+
|
|
7005
|
+
// TODO : FIXME : DUPLICATED CODE !
|
|
7006
|
+
this.parentElement.addEventListener("keydown",(event)=>{
|
|
7007
|
+
let keyChar=event.key;
|
|
7008
|
+
const keyCode=event.keyCode;
|
|
7009
|
+
|
|
7010
|
+
// To avoid server-side «A JSONObject text must end with '}'» errors :
|
|
7011
|
+
if(keyChar==="{") keyChar="left_brace";
|
|
7012
|
+
if(keyChar==="}") keyChar="right_brace";
|
|
7013
|
+
self.fusrodaClient.sendKeyboardPressedEvent({keyChar:keyChar,keyCode:keyCode});
|
|
7014
|
+
});
|
|
7015
|
+
// TODO : FIXME : DUPLICATED CODE !
|
|
7016
|
+
this.parentElement.addEventListener("keyup",(event)=>{
|
|
7017
|
+
let keyChar=event.key;
|
|
7018
|
+
const keyCode=event.keyCode;
|
|
7019
|
+
// DBG
|
|
7020
|
+
lognow("!!! keydown KEYUP event:[event.key:"+event.key+"][keyCode:"+keyCode+"][keyChar:"+keyChar+"]",event);
|
|
7021
|
+
|
|
7022
|
+
// To avoid server-side «A JSONObject text must end with '}'» errors :
|
|
7023
|
+
if(keyChar==="{") keyChar="left_brace";
|
|
7024
|
+
if(keyChar==="}") keyChar="right_brace";
|
|
7025
|
+
self.fusrodaClient.sendKeyboardReleasedEvent({keyChar:keyChar,keyCode:keyCode});
|
|
7026
|
+
});
|
|
7027
|
+
|
|
7028
|
+
|
|
7029
|
+
|
|
7030
|
+
this.fusrodaClient.client.start();
|
|
7031
|
+
|
|
7032
|
+
|
|
7033
|
+
return this;
|
|
7034
|
+
}
|
|
7035
|
+
|
|
7036
|
+
}
|
|
7037
|
+
|
|
7038
|
+
|
|
7039
|
+
|
|
7040
|
+
/*private*/getDecodedArrayFromSoundDataString=function(soundDataStr){
|
|
7041
|
+
const decodedIntsArray=[];
|
|
7042
|
+
|
|
7043
|
+
const decodedString=atob(soundDataStr);
|
|
7044
|
+
|
|
7045
|
+
//// DBG
|
|
7046
|
+
//const decodedString=soundDataStr;
|
|
7047
|
+
|
|
7048
|
+
for(let i=0;i<decodedString.length;i++){
|
|
7049
|
+
const dataInt=decodedString.charCodeAt(i)-127;
|
|
7050
|
+
|
|
7051
|
+
// CAUTION : audio needs to be in [-1.0; 1.0]
|
|
7052
|
+
decodedIntsArray.push(dataInt/127);
|
|
7053
|
+
}
|
|
7054
|
+
|
|
7055
|
+
return decodedIntsArray;
|
|
7056
|
+
}
|
|
7057
|
+
|
|
7058
|
+
|
|
7059
|
+
|
|
7060
|
+
createFusrodaClient=function(doOnClientReady, doOnDataReception, urlParam=null, portParam=6080){
|
|
7061
|
+
// CAUTION : WORKS BETTER WHEN UNSECURE, BUT 'NEEDS CLIENT BROWSER TO ALLOW MIXED (SECURE/UNSECURE) CONTENT !
|
|
7062
|
+
|
|
7063
|
+
const isSecure=(contains(urlParam.toLowerCase(),"https") || contains(urlParam.toLowerCase(),"wss"));
|
|
7064
|
+
|
|
7065
|
+
|
|
7066
|
+
const fusrodaClient=initClient(false,true, (browserClientInstance)=>{
|
|
7067
|
+
|
|
7068
|
+
// DBG
|
|
7069
|
+
lognow("SETTING UP");
|
|
7070
|
+
|
|
7071
|
+
//0)
|
|
7072
|
+
fusrodaClient.client.socketToServer.send("protocol", { type:"hello" });
|
|
7073
|
+
//1)
|
|
7074
|
+
browserClientInstance.receive("protocolConfig", (messageConfig)=>{
|
|
7075
|
+
//2)
|
|
7076
|
+
fusrodaClient.client.socketToServer.send("screenFrameRequest", {});
|
|
7077
|
+
browserClientInstance.receive("imagePacket", (doOnDataReception && doOnDataReception["image"]?doOnDataReception["image"]:()=>{/*DO NOTHING*/}));
|
|
7078
|
+
|
|
7079
|
+
//2)
|
|
7080
|
+
fusrodaClient.client.socketToServer.send("soundSampleRequest", {});
|
|
7081
|
+
browserClientInstance.receive("soundPacket", (doOnDataReception && doOnDataReception["sound"]?doOnDataReception["sound"]:()=>{/*DO NOTHING*/}));
|
|
7082
|
+
|
|
7083
|
+
|
|
7084
|
+
doOnClientReady(messageConfig);
|
|
7085
|
+
});
|
|
7086
|
+
|
|
7087
|
+
|
|
7088
|
+
}, urlParam, portParam, isSecure);
|
|
7089
|
+
|
|
7090
|
+
fusrodaClient.sendMouseMoveEvent=(mouseEvent)=>{
|
|
7091
|
+
fusrodaClient.client.socketToServer.send("mouseMoveEvent", mouseEvent);
|
|
7092
|
+
};
|
|
7093
|
+
|
|
7094
|
+
fusrodaClient.sendMouseClickEvent=(mouseEvent)=>{
|
|
7095
|
+
fusrodaClient.client.socketToServer.send("mouseClickEvent", mouseEvent);
|
|
7096
|
+
};
|
|
7097
|
+
fusrodaClient.sendMouseReleasedEvent=(mouseEvent)=>{
|
|
7098
|
+
fusrodaClient.client.socketToServer.send("mouseReleasedEvent", mouseEvent);
|
|
7099
|
+
};
|
|
7100
|
+
fusrodaClient.sendWheelEvent=(wheelEvent)=>{
|
|
7101
|
+
fusrodaClient.client.socketToServer.send("wheelEvent", wheelEvent);
|
|
7102
|
+
};
|
|
7103
|
+
|
|
7104
|
+
fusrodaClient.sendKeyboardPressedEvent=(keyboardEvent)=>{
|
|
7105
|
+
fusrodaClient.client.socketToServer.send("keyboardPressedEvent", keyboardEvent);
|
|
7106
|
+
};
|
|
7107
|
+
fusrodaClient.sendKeyboardReleasedEvent=(keyboardEvent)=>{
|
|
7108
|
+
fusrodaClient.client.socketToServer.send("keyboardReleasedEvent", keyboardEvent);
|
|
7109
|
+
};
|
|
7110
|
+
|
|
7111
|
+
|
|
7112
|
+
|
|
7113
|
+
// // DBG
|
|
7114
|
+
// fusrodaClient.clientSocket.addEventListener("close",()=>{
|
|
7115
|
+
// lognow("CONNECTION CLOSED !");
|
|
7116
|
+
// });
|
|
7117
|
+
|
|
7118
|
+
return fusrodaClient;
|
|
7119
|
+
}
|
|
7120
|
+
|
|
7121
|
+
|
|
7122
|
+
|
|
7123
|
+
|
|
7124
|
+
/*utils GEOMETRY library associated with aotra version : «1.0.0.000 (11/07/2022-00:48:13)»*/
|
|
7125
|
+
/*-----------------------------------------------------------------------------*/
|
|
7126
|
+
|
|
7127
|
+
|
|
7128
|
+
/* ## Utility global methods in a javascript, at least console (nodejs) server, or vanilla javascript with no browser environment.
|
|
7129
|
+
*
|
|
7130
|
+
* This set of methods gathers utility generic-purpose methods usable in any JS project.
|
|
7131
|
+
* Several authors of snippets published freely on the Internet contributed to this library.
|
|
7132
|
+
* Feel free to use/modify-enhance/publish them under the terms of its license.
|
|
7133
|
+
*
|
|
7134
|
+
* # Library name : «aotrautils»
|
|
7135
|
+
* # Library license : HGPL(Help Burma) (see aotra README information for details : https://alqemia.com/aotra.js )
|
|
7136
|
+
* # Author name : Jérémie Ratomposon (massively helped by its programming egos legions)
|
|
7137
|
+
* # Author email : info@alqemia.com
|
|
7138
|
+
* # Organization name : Alqemia
|
|
7139
|
+
* # Organization email : admin@alqemia.com
|
|
7140
|
+
* # Organization website : https://alqemia.com
|
|
7141
|
+
*
|
|
7142
|
+
*
|
|
7143
|
+
*/
|
|
7144
|
+
|
|
7145
|
+
|
|
7146
|
+
// COMPATIBILITY browser javascript / nodejs environment :
|
|
7147
|
+
|
|
7148
|
+
if(typeof(window)==="undefined") window=global;
|
|
7149
|
+
|
|
7150
|
+
|
|
7151
|
+
|
|
7152
|
+
//=========================================================================
|
|
7153
|
+
// GLOBAL CONSTANTS :
|
|
7154
|
+
|
|
7155
|
+
|
|
7156
|
+
Math.TAU=6.28318530718; // == Math.PI*2
|
|
7157
|
+
Math.PSI=1.57079632679; // == Math.PI/2
|
|
7158
|
+
Math.TAN_RATIOS_DEGREES={
|
|
7159
|
+
22.5:0.4142135624,
|
|
7160
|
+
45:1,
|
|
7161
|
+
67.5:2.4142135624,
|
|
7162
|
+
112.5:-2.4142135624,
|
|
7163
|
+
135:-1,
|
|
7164
|
+
157.5:-0.4142135624,
|
|
7165
|
+
202.5:0.4142135624,
|
|
7166
|
+
225:1,
|
|
7167
|
+
247.5:2.4142135624,
|
|
7168
|
+
292.5:-2.4142135624,
|
|
7169
|
+
315:-1,
|
|
7170
|
+
337.5:-0.4142135624
|
|
7171
|
+
};
|
|
7172
|
+
Math.LN_2_INVERTED=1.44269504089; // == 1/ln(2)
|
|
7173
|
+
|
|
7174
|
+
Math.INVERSE_OF_SQUARE_OF_TWO=0.707106781; // == 1/sqrt(2)
|
|
7175
|
+
Math.RATIO_TO_DEGREES=57.295779513; // == 180/PI
|
|
7176
|
+
Math.RATIO_TO_RADIANS=0.017453293; // == PI/180
|
|
7177
|
+
|
|
7178
|
+
|
|
7179
|
+
|
|
7180
|
+
//=========================================================================
|
|
7181
|
+
|
|
7182
|
+
|
|
7183
|
+
// ==================== COMMONS Geometry ====================
|
|
6752
7184
|
|
|
6753
7185
|
|
|
6754
7186
|
|
|
@@ -6893,6 +7325,14 @@ Math.polarPositionRadiansToCartesianPosition3D=(distance,gamma,alpha,xOffset=0,y
|
|
|
6893
7325
|
return {x:x,y:y,z:z};
|
|
6894
7326
|
}
|
|
6895
7327
|
|
|
7328
|
+
// DOES NOT SEEM TO WORK :
|
|
7329
|
+
//Math.getArcCoordinatesOnSphereFromCartesianVector=function(vector3D,sphereRadius=1){
|
|
7330
|
+
// const zAxisAngle=(vector3D.x==0?Math.PI*.5:Math.atan(vector3D.y/vector3D.x));
|
|
7331
|
+
// const yAxisAngle=(vector3D.z==0?0:Math.atan(vector3D.x/vector3D.z));
|
|
7332
|
+
// const y=yAxisAngle*sphereRadius;
|
|
7333
|
+
// const x=zAxisAngle*sphereRadius;
|
|
7334
|
+
// return {x:x,y:y};
|
|
7335
|
+
//}
|
|
6896
7336
|
|
|
6897
7337
|
|
|
6898
7338
|
Math.expApprox=function(x){
|
|
@@ -7005,9 +7445,9 @@ window.calculateAngleRadians2D=function(p1, p2, approximate=false){
|
|
|
7005
7445
|
//}
|
|
7006
7446
|
|
|
7007
7447
|
window.calculateAngleRadians3D=function(p1, p2, approximate=false){
|
|
7008
|
-
let a=calculateAngleRadians2D({x:p1.x, y:p1.
|
|
7009
|
-
let b=calculateAngleRadians2D({x:p1.
|
|
7010
|
-
let g=calculateAngleRadians2D({x:p1.
|
|
7448
|
+
let a=calculateAngleRadians2D({x:p1.x, y:p1.y}, {x:p2.x, y:p2.y}, approximate); // alpha => angle [x,y]
|
|
7449
|
+
let b=calculateAngleRadians2D({x:p1.z, y:p1.y}, {x:p2.z, y:p2.y}, approximate); // beta => angle [z,y]
|
|
7450
|
+
let g=calculateAngleRadians2D({x:p1.x, y:p1.z}, {x:p2.x, y:p2.z}, approximate); // gamma => angle [x,z]
|
|
7011
7451
|
return {a:a, b:b, g:g};
|
|
7012
7452
|
}
|
|
7013
7453
|
|
|
@@ -7017,15 +7457,21 @@ window.calculateLinearlyMovedPoint2DPolar=function(currentPoint, angleRadians, l
|
|
|
7017
7457
|
return {x:x, y:y};
|
|
7018
7458
|
}
|
|
7019
7459
|
|
|
7460
|
+
|
|
7461
|
+
// NEW : WE ACTUALL ONLY NEED 2 ANGLES ! BETA angle in plan [Z,Y] AND GAMMA angle in plan [X,Z] !
|
|
7020
7462
|
window.calculateLinearlyMovedPoint3DPolar=function(currentPoint, anglesRadians, linearMotion){
|
|
7463
|
+
|
|
7464
|
+
// IN THIS NORM (MOBILE NORM:)
|
|
7465
|
+
// alpha => angle [x,y]
|
|
7466
|
+
// beta => angle [z,y]
|
|
7467
|
+
// gamma => angle [x,z]
|
|
7468
|
+
|
|
7469
|
+
let translatedBeta= calculateLinearlyMovedPoint2DPolar({x:currentPoint.z, y:currentPoint.y}, anglesRadians.b, linearMotion);
|
|
7470
|
+
let translatedGamma=calculateLinearlyMovedPoint2DPolar({x:currentPoint.x, y:currentPoint.z}, anglesRadians.g, linearMotion);
|
|
7021
7471
|
|
|
7022
|
-
let
|
|
7023
|
-
let translatedBeta
|
|
7024
|
-
let
|
|
7025
|
-
|
|
7026
|
-
let x=translatedAlpha.x+translatedBeta.x;
|
|
7027
|
-
let y= translatedBeta.y+translatedGamma.y;
|
|
7028
|
-
let z=translatedAlpha.y +translatedGamma.x;
|
|
7472
|
+
let x=translatedGamma.x;
|
|
7473
|
+
let y=translatedBeta.y;
|
|
7474
|
+
let z=translatedBeta.x;
|
|
7029
7475
|
|
|
7030
7476
|
return {x:x, y:y, z:z};
|
|
7031
7477
|
}
|
|
@@ -7493,12 +7939,6 @@ Math.getQuadrant=function(centerPoint,point){
|
|
|
7493
7939
|
|
|
7494
7940
|
|
|
7495
7941
|
|
|
7496
|
-
|
|
7497
|
-
|
|
7498
|
-
|
|
7499
|
-
|
|
7500
|
-
|
|
7501
|
-
|
|
7502
7942
|
Math.compareDistances=function(p1, p2, distance, comparison){
|
|
7503
7943
|
let distanceSquare=Math.pow(distance, 2);
|
|
7504
7944
|
let diffSquare=Math.getSquaredDistance(p1, p2);
|
|
@@ -7516,7 +7956,7 @@ Math.compareDistances=function(p1, p2, distance, comparison){
|
|
|
7516
7956
|
};
|
|
7517
7957
|
|
|
7518
7958
|
Math.getSquaredDistance=function(p1, p2){
|
|
7519
|
-
return Math.pow(Math.abs(
|
|
7959
|
+
return Math.pow(Math.abs(p2.x-p1.x), 2) + Math.pow(Math.abs(p2.y-p1.y), 2) + ((p1.z!=null && p2.z!=null)?Math.pow(Math.abs(p2.z-p1.z), 2):0);
|
|
7520
7960
|
};
|
|
7521
7961
|
|
|
7522
7962
|
Math.getDistance=function(p1, p2){
|
|
@@ -7886,7 +8326,8 @@ function rayVsUnitSphereClosestPoint(p, r) {
|
|
|
7886
8326
|
|
|
7887
8327
|
// MUST REMAIN AT THE END OF THIS LIBRARY FILE !
|
|
7888
8328
|
|
|
7889
|
-
AOTRAUTILS_GEOMETRY_LIB_IS_LOADED=true
|
|
8329
|
+
AOTRAUTILS_GEOMETRY_LIB_IS_LOADED=true;
|
|
8330
|
+
/*utils SERVER library associated with aotra version : «1.0.0.000 (11/07/2022-00:48:13)»*/
|
|
7890
8331
|
/*-----------------------------------------------------------------------------*/
|
|
7891
8332
|
|
|
7892
8333
|
|
|
@@ -8084,23 +8525,31 @@ getServerParams=function(portParam,isSecureParam,certPathParam,keyPathParam){
|
|
|
8084
8525
|
// NODE ONLY SERVER / CLIENTS :
|
|
8085
8526
|
WebsocketImplementation={
|
|
8086
8527
|
|
|
8087
|
-
|
|
8528
|
+
isNodeContext:true,
|
|
8529
|
+
useSocketIOImplementation:false,
|
|
8530
|
+
useFlatStrings:false,
|
|
8088
8531
|
|
|
8089
8532
|
// COMMON METHODS
|
|
8090
8533
|
/*private static*/isInRoom(clientSocket, clientsRoomsTag){
|
|
8091
8534
|
return !clientsRoomsTag || empty(clientsRoomsTag) || contains(clientsRoomsTag, clientSocket.clientRoomTag);
|
|
8092
8535
|
},
|
|
8093
8536
|
|
|
8094
|
-
// NODE SERVER
|
|
8095
8537
|
|
|
8096
|
-
|
|
8538
|
+
//
|
|
8539
|
+
// NODE SERVER
|
|
8540
|
+
//
|
|
8541
|
+
getStatic:(isNodeContext=true, useSocketIOImplementation=false)=>{
|
|
8097
8542
|
|
|
8098
|
-
WebsocketImplementation.
|
|
8543
|
+
WebsocketImplementation.isNodeContext=isNodeContext;
|
|
8099
8544
|
|
|
8545
|
+
// cf. https://socket.io/docs/v4/client-socket-instance/
|
|
8546
|
+
WebsocketImplementation.useSocketIOImplementation=useSocketIOImplementation;
|
|
8547
|
+
|
|
8100
8548
|
// OLD : socket.io :
|
|
8101
8549
|
// https://stackoverflow.com/questions/31156884/how-to-use-https-on-node-js-using-express-socket-io
|
|
8102
8550
|
// https://stackoverflow.com/questions/6599470/node-js-socket-io-with-ssl
|
|
8103
8551
|
// https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener
|
|
8552
|
+
|
|
8104
8553
|
// if(typeof(socketIO)==="undefined"){
|
|
8105
8554
|
// // TRACE
|
|
8106
8555
|
// console.log("«socket.io» SERVER library not called yet, calling it now.");
|
|
@@ -8139,30 +8588,38 @@ WebsocketImplementation={
|
|
|
8139
8588
|
|
|
8140
8589
|
getServer:(listenableServer)=>{
|
|
8141
8590
|
|
|
8142
|
-
if(!WebsocketImplementation.
|
|
8591
|
+
if(!WebsocketImplementation.isNodeContext){
|
|
8143
8592
|
// TRACE
|
|
8144
|
-
throw new Error("ERROR : SERVER : Server launch is not supported in a non-nodejs context.");
|
|
8593
|
+
throw new Error("ERROR : SERVER : Server launch is not supported in a non-nodejs context for any implementation.");
|
|
8145
8594
|
}
|
|
8146
8595
|
|
|
8147
|
-
//
|
|
8148
|
-
|
|
8149
|
-
|
|
8150
|
-
|
|
8151
|
-
|
|
8596
|
+
// TODO : FIXME : Use one single interface !
|
|
8597
|
+
// NODE SERVER MODE ONLY :
|
|
8598
|
+
let serverSocket;
|
|
8599
|
+
if(!WebsocketImplementation.useSocketIOImplementation){
|
|
8600
|
+
serverSocket=new WebSocket.Server({ "server":listenableServer });
|
|
8601
|
+
}else{
|
|
8602
|
+
// NOW : socket.io :
|
|
8603
|
+
// Loading socket.io
|
|
8604
|
+
serverSocket=socketIO.listen(listenableServer);
|
|
8605
|
+
|
|
8606
|
+
// Setting up the disctonnect event :
|
|
8607
|
+
serverSocket.on("endConnection",()=>{
|
|
8608
|
+
serverSocket.disconnect();
|
|
8609
|
+
});
|
|
8610
|
+
|
|
8611
|
+
}
|
|
8152
8612
|
|
|
8153
|
-
|
|
8613
|
+
// NODE SERVER INSTANCE :
|
|
8154
8614
|
const nodeServerInstance={
|
|
8155
8615
|
|
|
8156
|
-
|
|
8157
|
-
|
|
8158
|
-
// clientsSockets:[],
|
|
8616
|
+
onClientLostListeners:[],
|
|
8159
8617
|
|
|
8618
|
+
clientTimeoutMillis:20000,
|
|
8619
|
+
// clientsSockets:[],
|
|
8160
8620
|
serverSocket:serverSocket,
|
|
8161
|
-
|
|
8162
8621
|
receptionEntryPoints:[],
|
|
8163
8622
|
|
|
8164
|
-
|
|
8165
|
-
|
|
8166
8623
|
receive:(channelName, doOnIncomingMessage, clientsRoomsTag=null)=>{
|
|
8167
8624
|
|
|
8168
8625
|
const receptionEntryPoint={
|
|
@@ -8172,41 +8629,40 @@ WebsocketImplementation={
|
|
|
8172
8629
|
|
|
8173
8630
|
|
|
8174
8631
|
// With «ws» library we have no choive than register message events inside a «connection» event !
|
|
8175
|
-
//
|
|
8632
|
+
// nodeServerInstance.onConnectionToClient((serverParam, clientSocketParam)=>{
|
|
8633
|
+
|
|
8634
|
+
// TODO : Find a way to remove this !
|
|
8635
|
+
const doOnMessage=(message)=>{
|
|
8636
|
+
|
|
8637
|
+
// dataWrapped=JSON.parse(dataWrapped);
|
|
8638
|
+
// dataWrapped=getAt(dataWrapped,0);// We get the root element
|
|
8639
|
+
const dataWrapped=(WebsocketImplementation.useFlatStrings?JSON.parse(message):message);
|
|
8176
8640
|
|
|
8177
|
-
|
|
8641
|
+
|
|
8642
|
+
// Channel information is stored in exchanged data :
|
|
8643
|
+
if(dataWrapped.channelName!==receptionEntryPoint.channelName) return;
|
|
8178
8644
|
|
|
8179
|
-
|
|
8180
|
-
|
|
8181
|
-
dataWrapped=JSON.parse(dataWrapped);
|
|
8182
|
-
|
|
8183
|
-
|
|
8184
|
-
// Channel information is stored in exchanged data :
|
|
8185
|
-
if(dataWrapped.channelName!==receptionEntryPoint.channelName) return;
|
|
8645
|
+
|
|
8646
|
+
const clientSocket=clientSocketParam;
|
|
8186
8647
|
|
|
8648
|
+
// TODO : FIXME : Use one single interface !
|
|
8649
|
+
// Room information is stored in client socket object :
|
|
8650
|
+
if(!WebsocketImplementation.isInRoom(clientSocket, receptionEntryPoint.clientsRoomsTag)) return;
|
|
8651
|
+
|
|
8652
|
+
doOnIncomingMessage(dataWrapped.data, clientSocket);
|
|
8187
8653
|
|
|
8188
|
-
|
|
8189
|
-
|
|
8190
|
-
const clientSocket=clientSocketParam;
|
|
8654
|
+
};
|
|
8191
8655
|
|
|
8192
|
-
// Room information is stored in client socket object :
|
|
8193
|
-
if(!WebsocketImplementation.isInRoom(clientSocket, receptionEntryPoint.clientsRoomsTag)) return;
|
|
8194
8656
|
|
|
8195
|
-
|
|
8196
|
-
|
|
8197
|
-
|
|
8198
|
-
|
|
8199
|
-
});
|
|
8200
|
-
|
|
8201
|
-
|
|
8202
|
-
// });
|
|
8657
|
+
if(!WebsocketImplementation.useSocketIOImplementation) clientSocketParam.addEventListener("message", doOnMessage);
|
|
8658
|
+
else clientSocketParam.on(channelName, doOnMessage);
|
|
8659
|
+
|
|
8203
8660
|
|
|
8204
8661
|
|
|
8205
8662
|
|
|
8206
8663
|
},
|
|
8207
8664
|
};
|
|
8208
8665
|
|
|
8209
|
-
|
|
8210
8666
|
// DBG
|
|
8211
8667
|
console.log("ADD RECEPTION ENTRY POINT channelName:«"+channelName+"»! nodeServerInstance.receptionEntryPoints.length:",nodeServerInstance.receptionEntryPoints.length);
|
|
8212
8668
|
|
|
@@ -8219,11 +8675,22 @@ WebsocketImplementation={
|
|
|
8219
8675
|
|
|
8220
8676
|
send:(channelName, data, clientsRoomsTag=null, clientSocketParam=null)=>{
|
|
8221
8677
|
|
|
8678
|
+
|
|
8222
8679
|
if(!clientSocketParam){
|
|
8223
8680
|
|
|
8224
|
-
|
|
8681
|
+
// TODO : FIXME : Use one single interface !
|
|
8682
|
+
let serverClients;
|
|
8683
|
+
if(!WebsocketImplementation.useSocketIOImplementation) serverClients=nodeServerInstance.serverSocket.clients;
|
|
8684
|
+
else serverClients=nodeServerInstance.serverSocket.sockets.clients();
|
|
8685
|
+
|
|
8686
|
+
serverClients.forEach((clientSocket)=>{
|
|
8687
|
+
|
|
8688
|
+
|
|
8689
|
+
// TODO : FIXME : Use one single interface !
|
|
8690
|
+
if(!WebsocketImplementation.useSocketIOImplementation) if(clientSocket.readyState!==WebSocket.OPEN) return;
|
|
8691
|
+
else if(clientSocket.connected) return;
|
|
8692
|
+
|
|
8225
8693
|
|
|
8226
|
-
if(clientSocket.readyState!==WebSocket.OPEN) return;
|
|
8227
8694
|
|
|
8228
8695
|
// Room information is stored in client socket object :
|
|
8229
8696
|
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
@@ -8234,14 +8701,19 @@ WebsocketImplementation={
|
|
|
8234
8701
|
|
|
8235
8702
|
dataWrapped=JSON.stringify(dataWrapped);
|
|
8236
8703
|
|
|
8237
|
-
|
|
8238
|
-
|
|
8704
|
+
// TODO : FIXME : Use one single interface !
|
|
8705
|
+
if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.send(dataWrapped);
|
|
8706
|
+
else clientSocket.emit(channelName,dataWrapped);
|
|
8707
|
+
|
|
8239
8708
|
});
|
|
8240
8709
|
|
|
8241
8710
|
}else{
|
|
8242
8711
|
|
|
8712
|
+
// TODO : FIXME : Use one single interface !
|
|
8243
8713
|
let clientSocket=clientSocketParam;
|
|
8244
|
-
|
|
8714
|
+
if(!WebsocketImplementation.useSocketIOImplementation) if(clientSocket.readyState!==WebSocket.OPEN) return;
|
|
8715
|
+
else if(clientSocket.connected) return;
|
|
8716
|
+
|
|
8245
8717
|
|
|
8246
8718
|
// Room information is stored in client socket object :
|
|
8247
8719
|
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
@@ -8249,10 +8721,12 @@ WebsocketImplementation={
|
|
|
8249
8721
|
// Channel information is stored in exchanged data :
|
|
8250
8722
|
let dataWrapped={channelName:channelName, data:data};
|
|
8251
8723
|
|
|
8252
|
-
|
|
8253
8724
|
dataWrapped=JSON.stringify(dataWrapped);
|
|
8254
8725
|
|
|
8255
|
-
|
|
8726
|
+
// TODO : FIXME : Use one single interface !
|
|
8727
|
+
if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.send(dataWrapped);
|
|
8728
|
+
else clientSocket.emit(channelName,dataWrapped);
|
|
8729
|
+
|
|
8256
8730
|
|
|
8257
8731
|
}
|
|
8258
8732
|
|
|
@@ -8293,23 +8767,45 @@ WebsocketImplementation={
|
|
|
8293
8767
|
|
|
8294
8768
|
|
|
8295
8769
|
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
8770
|
// To make the server aware of the clients connections states :
|
|
8299
8771
|
clientSocket.stateCheckInterval=setInterval(()=>{
|
|
8772
|
+
|
|
8773
|
+
|
|
8300
8774
|
if (clientSocket.isAlive===false){
|
|
8301
8775
|
clearInterval(clientSocket.stateCheckInterval);
|
|
8302
8776
|
|
|
8303
|
-
|
|
8777
|
+
// TODO : FIXME : DUPLICATED CODE :
|
|
8778
|
+
// TODO : FIXME : Use one single interface !
|
|
8779
|
+
if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.terminate();
|
|
8780
|
+
else clientSocket.emit("endConnection");
|
|
8781
|
+
|
|
8782
|
+
// TODO : FIXME : DUPLICATED CODE :
|
|
8783
|
+
if(!empty(nodeServerInstance.onClientLostListeners))
|
|
8784
|
+
foreach(nodeServerInstance.onClientLostListeners,l=>{l.execute(clientSocket);});
|
|
8785
|
+
|
|
8786
|
+
return;
|
|
8304
8787
|
}
|
|
8788
|
+
|
|
8305
8789
|
clientSocket.isAlive=false;
|
|
8306
8790
|
try{
|
|
8307
8791
|
clientSocket.ping();
|
|
8308
8792
|
}catch(error){
|
|
8309
8793
|
lognow("ERROR : A problem occurred when tried to ping client socket : ",error);
|
|
8310
8794
|
// We effectively end the client connection to this server :
|
|
8311
|
-
|
|
8795
|
+
|
|
8796
|
+
// TODO : FIXME : DUPLICATED CODE :
|
|
8797
|
+
// TODO : FIXME : Use one single interface !
|
|
8798
|
+
if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.terminate();
|
|
8799
|
+
else clientSocket.emit("endConnection");
|
|
8800
|
+
|
|
8801
|
+
// TODO : FIXME : DUPLICATED CODE :
|
|
8802
|
+
if(!empty(nodeServerInstance.onClientLostListeners))
|
|
8803
|
+
foreach(nodeServerInstance.onClientLostListeners,l=>{l.execute(clientSocket);});
|
|
8804
|
+
|
|
8805
|
+
return;
|
|
8312
8806
|
}
|
|
8807
|
+
|
|
8808
|
+
|
|
8313
8809
|
}, nodeServerInstance.clientTimeoutMillis);
|
|
8314
8810
|
clientSocket.on("pong",()=>{
|
|
8315
8811
|
clientSocket.isAlive=true;
|
|
@@ -8354,7 +8850,12 @@ WebsocketImplementation={
|
|
|
8354
8850
|
|
|
8355
8851
|
// To make the server aware of the clients connections states :
|
|
8356
8852
|
nodeServerInstance.serverSocket.on("close", function close() {
|
|
8357
|
-
|
|
8853
|
+
|
|
8854
|
+
// TODO : FIXME : Use one single interface !
|
|
8855
|
+
if(!WebsocketImplementation.useSocketIOImplementation) serverClients=nodeServerInstance.serverSocket.clients;
|
|
8856
|
+
else serverClients=nodeServerInstance.serverSocket.sockets.clients();
|
|
8857
|
+
|
|
8858
|
+
serverClients.forEach((clientSocket)=>{
|
|
8358
8859
|
clearInterval(clientSocket.stateCheckInterval);
|
|
8359
8860
|
});
|
|
8360
8861
|
});
|
|
@@ -8365,39 +8866,26 @@ WebsocketImplementation={
|
|
|
8365
8866
|
},
|
|
8366
8867
|
|
|
8367
8868
|
|
|
8368
|
-
// CLIENT
|
|
8369
|
-
connectToServer:(serverURL, port, timeout)=>{
|
|
8370
|
-
if(WebsocketImplementation.isNode)
|
|
8371
|
-
return WebsocketImplementation.connectToServerFromNode(serverURL, port, timeout);
|
|
8372
|
-
// else
|
|
8373
|
-
return WebsocketImplementation.connectToServerFromBrowser(serverURL, port, timeout);
|
|
8374
|
-
},
|
|
8375
|
-
|
|
8376
|
-
|
|
8869
|
+
// NODE / BROWSER CLIENT CONNECTS TO SERVER MAIN ENTRYPOINT:
|
|
8870
|
+
connectToServer:(serverURL, port, isSecure=false, timeout)=>{
|
|
8377
8871
|
|
|
8872
|
+
// TRACE
|
|
8873
|
+
lognow("INFO : Using websocket implementation : "+(WebsocketImplementation.isNodeContext?"node (server-side)":"browser (client-side)"));
|
|
8874
|
+
|
|
8875
|
+
if(WebsocketImplementation.isNodeContext)
|
|
8876
|
+
return WebsocketImplementation.connectToServerFromNode(serverURL, port, isSecure, timeout);
|
|
8877
|
+
else
|
|
8878
|
+
return WebsocketImplementation.connectToServerFromBrowser(serverURL, port, isSecure, timeout);
|
|
8879
|
+
},
|
|
8378
8880
|
|
|
8379
8881
|
|
|
8882
|
+
|
|
8883
|
+
//
|
|
8380
8884
|
// NODE CLIENT
|
|
8381
|
-
|
|
8885
|
+
//
|
|
8886
|
+
/*private*/connectToServerFromNode:(serverURL, port, isSecure, timeout)=>{
|
|
8887
|
+
|
|
8382
8888
|
|
|
8383
|
-
// OLD : socket.io :
|
|
8384
|
-
//client on server-side:
|
|
8385
|
-
//if(typeof(socketIO)!=="undefined"){
|
|
8386
|
-
// // Node client :
|
|
8387
|
-
// connectToServer=function (server, port, timeout=10000){
|
|
8388
|
-
// return socketIO.connect(server + ":" + port,{timeout: timeout});;
|
|
8389
|
-
// }
|
|
8390
|
-
//}else
|
|
8391
|
-
// if(typeof(io)!=="undefined"){
|
|
8392
|
-
// // Browser client :
|
|
8393
|
-
// connectToServer=function (server, port, timeout=10000){
|
|
8394
|
-
// return io.connect(server + ":" + port,{timeout: timeout});
|
|
8395
|
-
//// ALTERNATIVE : return io(server + ":" + port,{timeout: timeout});
|
|
8396
|
-
// };
|
|
8397
|
-
// }else{
|
|
8398
|
-
// // TRACE
|
|
8399
|
-
// console.log("ERROR : Could not initialize socket !");
|
|
8400
|
-
// }
|
|
8401
8889
|
|
|
8402
8890
|
// NEW : ws :
|
|
8403
8891
|
if(typeof(WebSocket)==="undefined"){
|
|
@@ -8405,32 +8893,46 @@ WebsocketImplementation={
|
|
|
8405
8893
|
lognow("ERROR : CLIENT : Could not find websocket client lib, aborting client connection.");
|
|
8406
8894
|
return null;
|
|
8407
8895
|
}
|
|
8408
|
-
|
|
8409
|
-
const clientSocket=new WebSocket(serverURL+":"+port,/*WORKAROUND:*/{ rejectUnauthorized:false });
|
|
8410
|
-
|
|
8411
8896
|
|
|
8897
|
+
// NODE CLIENT MODE ONLY :
|
|
8898
|
+
let clientSocket;
|
|
8899
|
+
if(!WebsocketImplementation.useSocketIOImplementation){
|
|
8900
|
+
clientSocket=new WebSocket(serverURL+":"+port,/*WORKAROUND:*/{ rejectUnauthorized:false, secure: isSecure });
|
|
8901
|
+
}else{
|
|
8902
|
+
// NOW : socket.io :
|
|
8903
|
+
//client on server-side:
|
|
8904
|
+
if(WebsocketImplementation.isNodeContext && typeof(socketIO)!=="undefined"){
|
|
8905
|
+
// // Node client :
|
|
8906
|
+
// connectToServer=function (serverURL, port, isSecure, timeout=10000){
|
|
8907
|
+
// return socketIO.connect(serverURL + ":" + port,{timeout: timeout});
|
|
8908
|
+
// }
|
|
8909
|
+
clientSocket=socketIO.connect(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
|
|
8910
|
+
}
|
|
8911
|
+
}
|
|
8912
|
+
|
|
8913
|
+
// NODE CLIENT INSTANCE :
|
|
8412
8914
|
const nodeClientInstance={
|
|
8413
8915
|
clientSocket:clientSocket,
|
|
8414
8916
|
|
|
8415
8917
|
receptionEntryPoints:[],
|
|
8416
|
-
|
|
8417
8918
|
|
|
8418
8919
|
receive:(channelName, doOnIncomingMessage, clientsRoomsTag=null)=>{
|
|
8419
8920
|
|
|
8420
|
-
|
|
8421
8921
|
const receptionEntryPoint={
|
|
8422
8922
|
channelName:channelName,
|
|
8423
8923
|
clientsRoomsTag:clientsRoomsTag,
|
|
8424
8924
|
execute:(clientSocketParam)=>{
|
|
8425
8925
|
|
|
8426
|
-
|
|
8427
|
-
nodeClientInstance.clientSocket.on("message", (dataWrapped)=>{
|
|
8926
|
+
const doOnMessage=(message)=>{
|
|
8428
8927
|
|
|
8429
|
-
|
|
8928
|
+
// dataWrapped=JSON.parse(dataWrapped);
|
|
8929
|
+
// dataWrapped=getAt(dataWrapped,0);// We get the root element
|
|
8930
|
+
|
|
8931
|
+
const dataWrapped=(WebsocketImplementation.useFlatStrings?JSON.parse(message):message);
|
|
8430
8932
|
|
|
8431
8933
|
|
|
8432
8934
|
// Channel information is stored in exchanged data :
|
|
8433
|
-
if(dataWrapped.channelName!==channelName) return;
|
|
8935
|
+
if(dataWrapped.channelName && dataWrapped.channelName!==channelName) return;
|
|
8434
8936
|
|
|
8435
8937
|
let clientSocket=nodeClientInstance.clientSocket;
|
|
8436
8938
|
|
|
@@ -8439,8 +8941,11 @@ WebsocketImplementation={
|
|
|
8439
8941
|
|
|
8440
8942
|
doOnIncomingMessage(dataWrapped.data, clientSocket);
|
|
8441
8943
|
|
|
8442
|
-
|
|
8443
|
-
|
|
8944
|
+
};
|
|
8945
|
+
|
|
8946
|
+
if(!WebsocketImplementation.useSocketIOImplementation) nodeClientInstance.clientSocket.addEventListener("message", doOnMessage);
|
|
8947
|
+
else nodeClientInstance.clientSocket.on(channelName, doOnMessage);
|
|
8948
|
+
|
|
8444
8949
|
|
|
8445
8950
|
}
|
|
8446
8951
|
};
|
|
@@ -8448,7 +8953,6 @@ WebsocketImplementation={
|
|
|
8448
8953
|
|
|
8449
8954
|
nodeClientInstance.receptionEntryPoints.push(receptionEntryPoint);
|
|
8450
8955
|
|
|
8451
|
-
|
|
8452
8956
|
return nodeClientInstance;
|
|
8453
8957
|
},
|
|
8454
8958
|
|
|
@@ -8461,8 +8965,10 @@ WebsocketImplementation={
|
|
|
8461
8965
|
// // DBG
|
|
8462
8966
|
// console.log("(NODE CLIENT) TRYING TO SEND : clientSocket.readyState:",clientSocket.readyState);
|
|
8463
8967
|
|
|
8464
|
-
|
|
8465
|
-
|
|
8968
|
+
// TODO : FIXME : Use one single interface !
|
|
8969
|
+
if(!WebsocketImplementation.useSocketIOImplementation) if(clientSocket.readyState!==WebSocket.OPEN) return;
|
|
8970
|
+
else if(clientSocket.connected) return;
|
|
8971
|
+
|
|
8466
8972
|
|
|
8467
8973
|
// Room information is stored in client socket object :
|
|
8468
8974
|
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
@@ -8482,8 +8988,9 @@ WebsocketImplementation={
|
|
|
8482
8988
|
// console.log("(NODE CLIENT) SENDING DATA ! channelName:«"+channelName+"» ; clientsRoomsTag:«"+clientsRoomsTag+"»");
|
|
8483
8989
|
// console.log("(NODE CLIENT) SENDING DATA ! dataWrapped:",dataWrapped);
|
|
8484
8990
|
|
|
8485
|
-
|
|
8486
|
-
|
|
8991
|
+
// TODO : FIXME : Use one single interface !
|
|
8992
|
+
if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.send(dataWrapped);
|
|
8993
|
+
else clientSocket.emit(channelName,dataWrapped);
|
|
8487
8994
|
|
|
8488
8995
|
return nodeClientInstance;
|
|
8489
8996
|
},
|
|
@@ -8492,14 +8999,19 @@ WebsocketImplementation={
|
|
|
8492
8999
|
join:(clientRoomTag)=>{
|
|
8493
9000
|
// Join room client part protocol :
|
|
8494
9001
|
const message={type:"joinRoom",clientRoomTag:clientRoomTag};
|
|
9002
|
+
|
|
8495
9003
|
nodeClientInstance.send("protocol",message);
|
|
8496
9004
|
|
|
8497
9005
|
},
|
|
8498
9006
|
|
|
8499
9007
|
|
|
8500
9008
|
onConnectionToServer:(doOnConnection)=>{
|
|
8501
|
-
|
|
8502
|
-
|
|
9009
|
+
|
|
9010
|
+
const doAllOnConnection=()=>{
|
|
9011
|
+
|
|
9012
|
+
// To avoid triggering this event several times, depending on the implementation :
|
|
9013
|
+
if(nodeClientInstance.hasConnectEventFired) return;
|
|
9014
|
+
nodeClientInstance.hasConnectEventFired=true;
|
|
8503
9015
|
|
|
8504
9016
|
let clientSocket=nodeClientInstance.clientSocket;
|
|
8505
9017
|
|
|
@@ -8510,7 +9022,11 @@ WebsocketImplementation={
|
|
|
8510
9022
|
receptionEntryPoint.execute(clientSocket);
|
|
8511
9023
|
});
|
|
8512
9024
|
|
|
8513
|
-
}
|
|
9025
|
+
};
|
|
9026
|
+
|
|
9027
|
+
if(!WebsocketImplementation.useSocketIOImplementation) nodeClientInstance.clientSocket.addEventListener("open",doAllOnConnection);
|
|
9028
|
+
else nodeClientInstance.clientSocket.on("connect",doAllOnConnection);
|
|
9029
|
+
|
|
8514
9030
|
},
|
|
8515
9031
|
|
|
8516
9032
|
|
|
@@ -8524,7 +9040,7 @@ WebsocketImplementation={
|
|
|
8524
9040
|
|
|
8525
9041
|
// BROWSER CLIENT
|
|
8526
9042
|
|
|
8527
|
-
/*private*/connectToServerFromBrowser:(serverURL, port, timeout)=>{
|
|
9043
|
+
/*private*/connectToServerFromBrowser:(serverURL, port, isSecure, timeout)=>{
|
|
8528
9044
|
|
|
8529
9045
|
// NEW : ws :
|
|
8530
9046
|
if(typeof(WebSocket)==="undefined"){
|
|
@@ -8532,23 +9048,49 @@ WebsocketImplementation={
|
|
|
8532
9048
|
lognow("ERROR : CLIENT : Could not find websocket client lib, aborting client connection.");
|
|
8533
9049
|
return null;
|
|
8534
9050
|
}
|
|
8535
|
-
|
|
8536
|
-
|
|
9051
|
+
|
|
9052
|
+
// TODO : FIXME : Use one single interface !
|
|
9053
|
+
// BROWSER CLIENT MODE ONLY :
|
|
9054
|
+
let clientSocket;
|
|
9055
|
+
if(!WebsocketImplementation.useSocketIOImplementation){
|
|
9056
|
+
clientSocket=new WebSocket(serverURL+":"+port,["ws","wss"]);
|
|
9057
|
+
}else if(typeof(io)!=="undefined"){
|
|
9058
|
+
// NOW : socket.io :
|
|
9059
|
+
// // Browser client :
|
|
9060
|
+
// connectToServer=function (server, port, isSecure, timeout=10000){
|
|
9061
|
+
// return io.connect(serverURL + ":" + port,{timeout: timeout});
|
|
9062
|
+
//// ALTERNATIVE : return io(serverURL + ":" + port,{timeout: timeout});
|
|
9063
|
+
// };
|
|
9064
|
+
// }else{
|
|
9065
|
+
// // TRACE
|
|
9066
|
+
// console.log("ERROR : Could not initialize socket !");
|
|
9067
|
+
clientSocket=io.connect(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
|
|
9068
|
+
}
|
|
8537
9069
|
|
|
8538
9070
|
|
|
9071
|
+
// BROWSER CLIENT INSTANCE :
|
|
8539
9072
|
const browserInstance={
|
|
8540
9073
|
clientSocket:clientSocket,
|
|
8541
|
-
|
|
8542
9074
|
|
|
8543
9075
|
receive:(channelName, doOnIncomingMessage, clientsRoomsTag=null)=>{
|
|
9076
|
+
|
|
9077
|
+
// DBG
|
|
9078
|
+
lognow("!!! SETTING UP RECEIVE for :",channelName);
|
|
8544
9079
|
|
|
8545
|
-
|
|
9080
|
+
const doOnMessage=(message)=>{
|
|
8546
9081
|
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
9082
|
+
// // DBG
|
|
9083
|
+
// lognow("!!! RECEIVED : message",message);
|
|
9084
|
+
|
|
9085
|
+
// OLD : let dataWrapped=JSON.parse(event.data);
|
|
9086
|
+
// const dataWrapped=JSON.parse(message);
|
|
9087
|
+
// dataWrapped=getAt(dataWrapped,0);// We get the root element
|
|
9088
|
+
|
|
9089
|
+
const dataWrapped=(WebsocketImplementation.useFlatStrings?JSON.parse(message):message);
|
|
9090
|
+
|
|
9091
|
+
|
|
8550
9092
|
// Channel information is stored in exchanged data :
|
|
8551
|
-
if(dataWrapped.channelName!==channelName) return;
|
|
9093
|
+
if(dataWrapped.channelName && dataWrapped.channelName!==channelName) return;
|
|
8552
9094
|
|
|
8553
9095
|
let clientSocket=browserInstance.clientSocket;
|
|
8554
9096
|
|
|
@@ -8557,9 +9099,14 @@ WebsocketImplementation={
|
|
|
8557
9099
|
|
|
8558
9100
|
doOnIncomingMessage(dataWrapped.data, clientSocket);
|
|
8559
9101
|
|
|
8560
|
-
}
|
|
9102
|
+
};
|
|
9103
|
+
|
|
9104
|
+
|
|
9105
|
+
if(!WebsocketImplementation.useSocketIOImplementation) browserInstance.clientSocket.addEventListener("message", doOnMessage);
|
|
9106
|
+
else browserInstance.clientSocket.on(channelName, doOnMessage);
|
|
9107
|
+
|
|
8561
9108
|
|
|
8562
|
-
|
|
9109
|
+
return browserInstance;
|
|
8563
9110
|
},
|
|
8564
9111
|
|
|
8565
9112
|
|
|
@@ -8571,8 +9118,10 @@ WebsocketImplementation={
|
|
|
8571
9118
|
// // DBG
|
|
8572
9119
|
// console.log("(BROWSER) TRYING TO SEND : clientSocket.readyState:",clientSocket.readyState);
|
|
8573
9120
|
|
|
8574
|
-
|
|
8575
|
-
if(clientSocket.readyState!==WebSocket.OPEN) return;
|
|
9121
|
+
// TODO : FIXME : Use one single interface !
|
|
9122
|
+
if(!WebsocketImplementation.useSocketIOImplementation) if(clientSocket.readyState!==WebSocket.OPEN) return;
|
|
9123
|
+
else if(clientSocket.connected) return;
|
|
9124
|
+
|
|
8576
9125
|
|
|
8577
9126
|
// Room information is stored in client socket object :
|
|
8578
9127
|
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
@@ -8587,7 +9136,11 @@ WebsocketImplementation={
|
|
|
8587
9136
|
|
|
8588
9137
|
dataWrapped=JSON.stringify(dataWrapped);
|
|
8589
9138
|
|
|
8590
|
-
|
|
9139
|
+
|
|
9140
|
+
// TODO : FIXME : Use one single interface !
|
|
9141
|
+
if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.send(dataWrapped);
|
|
9142
|
+
else clientSocket.emit(channelName,dataWrapped);
|
|
9143
|
+
|
|
8591
9144
|
|
|
8592
9145
|
return browserInstance;
|
|
8593
9146
|
},
|
|
@@ -8601,7 +9154,15 @@ WebsocketImplementation={
|
|
|
8601
9154
|
|
|
8602
9155
|
|
|
8603
9156
|
onConnectionToServer:(doOnConnection)=>{
|
|
8604
|
-
|
|
9157
|
+
|
|
9158
|
+
// To avoid triggering this event several times, depending on the implementation :
|
|
9159
|
+
if(browserInstance.hasConnectEventFired) return;
|
|
9160
|
+
browserInstance.hasConnectEventFired=true;
|
|
9161
|
+
|
|
9162
|
+
if(!WebsocketImplementation.useSocketIOImplementation) browserInstance.clientSocket.addEventListener("open",doOnConnection);
|
|
9163
|
+
else browserInstance.clientSocket.on("connect",doOnConnection);
|
|
9164
|
+
|
|
9165
|
+
|
|
8605
9166
|
},
|
|
8606
9167
|
|
|
8607
9168
|
|
|
@@ -8732,7 +9293,7 @@ launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /
|
|
|
8732
9293
|
|
|
8733
9294
|
|
|
8734
9295
|
// TRACE
|
|
8735
|
-
console.log("INFO : SERVER : Generic Nodejs server launched and listening on port
|
|
9296
|
+
console.log("INFO : SERVER : Generic Nodejs server launched and listening on port:" + port + "!");
|
|
8736
9297
|
|
|
8737
9298
|
|
|
8738
9299
|
return server;
|
|
@@ -8743,7 +9304,7 @@ initNodeServer=function(doOnClientConnection=null, doOnFinalizeServer=null, /*OP
|
|
|
8743
9304
|
|
|
8744
9305
|
// TRACE
|
|
8745
9306
|
console.log("Server launched.");
|
|
8746
|
-
console.log("Usage :
|
|
9307
|
+
console.log("Usage : node <server.js> conf {port:[port], sslCertPath:[ssl certificate path | unsecure ], sslKeyPath:[ssl key path], serverConfig:[JSON server configuration]}");
|
|
8747
9308
|
console.log("Or (to generate password hash) : node <server.js> hash <clientId@repositoryName> <clearText>");
|
|
8748
9309
|
// EXAMPLE : node orita-srv.js hash orita.global@saltkey 1234567890
|
|
8749
9310
|
console.log("Server launched.");
|
|
@@ -8753,30 +9314,46 @@ initNodeServer=function(doOnClientConnection=null, doOnFinalizeServer=null, /*OP
|
|
|
8753
9314
|
let argCLPort;
|
|
8754
9315
|
let argCLCertPath;
|
|
8755
9316
|
let argCLKeyPath;
|
|
9317
|
+
let serverConfig={};
|
|
8756
9318
|
let isForceUnsecure;
|
|
8757
9319
|
|
|
8758
9320
|
let isHashAsked=false;
|
|
8759
9321
|
let clearTextParam=null;
|
|
8760
9322
|
let persisterId=null;
|
|
9323
|
+
|
|
9324
|
+
|
|
8761
9325
|
process.argv.forEach(function (val, i){
|
|
8762
9326
|
if(!val) return;
|
|
8763
9327
|
// 0 corresponds to «node / nodejs»
|
|
8764
9328
|
if(i<=1) return; // 1 corresponds to « <server.js> »
|
|
8765
9329
|
else if(i==2){
|
|
8766
|
-
|
|
8767
|
-
else isHashAsked=true;
|
|
9330
|
+
if(val==="hash") isHashAsked=true;
|
|
8768
9331
|
}else if(i==3){
|
|
8769
|
-
if(!isHashAsked)
|
|
8770
|
-
|
|
9332
|
+
if(!isHashAsked){
|
|
9333
|
+
try{
|
|
9334
|
+
const jsonConf=JSON.parse(val);
|
|
9335
|
+
argCLPort=jsonConf.port;
|
|
9336
|
+
argCLCertPath=jsonConf.sslCertPath;
|
|
9337
|
+
argCLKeyPath=jsonConf.sslKeyPath;
|
|
9338
|
+
try{
|
|
9339
|
+
serverConfig=nonull({},JSON.parse(jsonConf.serverConfig));
|
|
9340
|
+
}catch(err1){
|
|
9341
|
+
lognow("ERROR : Cannot parse JSON string «"+jsonConf.serverConfig+"».",err1);
|
|
9342
|
+
}
|
|
9343
|
+
}catch(err2){
|
|
9344
|
+
lognow("ERROR : Cannot parse JSON string «"+val+"».",err2);
|
|
9345
|
+
isHashAsked=true;
|
|
9346
|
+
}
|
|
9347
|
+
} else persisterId=val;
|
|
8771
9348
|
}else if(i==4){
|
|
8772
|
-
if(
|
|
8773
|
-
else clearTextParam=val;
|
|
9349
|
+
if(isHashAsked) clearTextParam=val;
|
|
8774
9350
|
}
|
|
8775
9351
|
});
|
|
8776
|
-
|
|
8777
9352
|
isForceUnsecure=(argCLCertPath==="unsecure");
|
|
8778
|
-
|
|
8779
|
-
|
|
9353
|
+
|
|
9354
|
+
|
|
9355
|
+
|
|
9356
|
+
const aotraNodeServer={config:serverConfig};
|
|
8780
9357
|
aotraNodeServer.serverManager={ start:()=>{/*DEFAULT START FUNCTION, WILL BE OVERRIDEN LATER*/}};
|
|
8781
9358
|
|
|
8782
9359
|
if(isHashAsked){
|
|
@@ -8820,8 +9397,6 @@ initNodeServer=function(doOnClientConnection=null, doOnFinalizeServer=null, /*OP
|
|
|
8820
9397
|
}
|
|
8821
9398
|
|
|
8822
9399
|
|
|
8823
|
-
|
|
8824
|
-
|
|
8825
9400
|
const DEFAULT_PORT=nonull(argCLPort,25000);
|
|
8826
9401
|
const DEFAULT_CERT_PATH=nonull(argCLCertPath,"cert.pem");
|
|
8827
9402
|
const DEFAULT_KEY_PATH=nonull(argCLKeyPath,"key.key");
|
|
@@ -8838,9 +9413,9 @@ initNodeServer=function(doOnClientConnection=null, doOnFinalizeServer=null, /*OP
|
|
|
8838
9413
|
|
|
8839
9414
|
|
|
8840
9415
|
// UNUSEFUL :
|
|
8841
|
-
//
|
|
9416
|
+
//aotraNodeServer.serverManager.microClientsSockets=[];
|
|
8842
9417
|
// UNUSEFUL :
|
|
8843
|
-
//
|
|
9418
|
+
//aotraNodeServer.serverManager.mainClientsSockets=[];
|
|
8844
9419
|
aotraNodeServer.serverManager.start=function(){
|
|
8845
9420
|
|
|
8846
9421
|
// Eventual encryption options :
|
|
@@ -8871,147 +9446,181 @@ initNodeServer=function(doOnClientConnection=null, doOnFinalizeServer=null, /*OP
|
|
|
8871
9446
|
return aotraNodeServer;
|
|
8872
9447
|
}
|
|
8873
9448
|
|
|
9449
|
+
// ========================= FUSRODA SERVER : =========================
|
|
8874
9450
|
|
|
8875
|
-
//
|
|
9451
|
+
//
|
|
9452
|
+
// DOES NOT WORK : USE Java FusrodaServer instead :
|
|
9453
|
+
//
|
|
9454
|
+
///*FUSRODA server stands from FSRD SERVER, for Fucking Simple Remote Desktop SERVER*/
|
|
9455
|
+
//createFusrodaServer=function(certPathParam=null,keyPathParam=null,portParam=6080){
|
|
9456
|
+
//
|
|
9457
|
+
// // https://www.npmjs.com/package/screenshot-desktop
|
|
9458
|
+
// // https://github.com/octalmage/robotjs
|
|
9459
|
+
// // npm install --save screenshot-desktop
|
|
9460
|
+
// //
|
|
9461
|
+
// // sudo apt-get install libxtst-dev libx11-dev
|
|
9462
|
+
// // npm install --save robotjs
|
|
9463
|
+
//
|
|
9464
|
+
// // http://getrobot.net/docs/usage.html
|
|
9465
|
+
// //
|
|
9466
|
+
// // apt-get install build-essential python libxt-dev libxtst-dev libxinerama-dev -y
|
|
9467
|
+
//
|
|
9468
|
+
// const screenshot=require("screenshot-desktop");
|
|
9469
|
+
//
|
|
9470
|
+
// const REFRESH_SCREENSHOTS_MILLIS=500;
|
|
9471
|
+
//
|
|
9472
|
+
//
|
|
9473
|
+
// const server=initNodeServer(
|
|
9474
|
+
// // On client connection :
|
|
9475
|
+
//// (serverParam, clientSocketParam)=>{},
|
|
9476
|
+
// null,
|
|
9477
|
+
// // On server finalization :
|
|
9478
|
+
// (serverParam)=>{
|
|
9479
|
+
//
|
|
9480
|
+
// serverParam.receive("protocol_fusroda", (message, clientSocket)=> {
|
|
9481
|
+
// serverParam.addToRoom(clientSocket,"clients");
|
|
9482
|
+
// });
|
|
9483
|
+
//
|
|
9484
|
+
//
|
|
9485
|
+
// serverParam.sendScreenshotsRoutine=setInterval(()=>{
|
|
9486
|
+
// if(serverParam.isScreenshotStarted) return;
|
|
9487
|
+
//
|
|
9488
|
+
// serverParam.isScreenshotStarted=true;
|
|
9489
|
+
//
|
|
9490
|
+
// screenshot().then((img) => {
|
|
9491
|
+
//
|
|
9492
|
+
// const data={image:img,messageType:"imageData"};
|
|
9493
|
+
// serverParam.send("message", data, "clients");
|
|
9494
|
+
//
|
|
9495
|
+
// serverParam.isScreenshotStarted=false;
|
|
9496
|
+
//
|
|
9497
|
+
// }).catch((error) => {
|
|
9498
|
+
// // TRACE
|
|
9499
|
+
// lognow("ERROR : Error during screenshot :",error);
|
|
9500
|
+
// });
|
|
9501
|
+
//
|
|
9502
|
+
// },REFRESH_SCREENSHOTS_MILLIS);
|
|
9503
|
+
//
|
|
9504
|
+
//
|
|
9505
|
+
// },portParam,certPathParam,keyPathParam);
|
|
9506
|
+
//
|
|
9507
|
+
//
|
|
9508
|
+
//// const doOnConnect=(serverParam, clientSocketParam)=>{
|
|
9509
|
+
//// };
|
|
9510
|
+
//// const doOnFinalizeServer=(serverParam)=>{
|
|
9511
|
+
//// /*DO NOTHING*/
|
|
9512
|
+
//// };
|
|
9513
|
+
//// const server={};
|
|
9514
|
+
//// server.start=(port=6080)=>{
|
|
9515
|
+
//// server.httpServer=launchNodeHTTPServer(port, doOnConnect, doOnFinalizeServer, sslOptions);
|
|
9516
|
+
//// };
|
|
9517
|
+
//
|
|
9518
|
+
// return server;
|
|
9519
|
+
//}
|
|
8876
9520
|
|
|
8877
|
-
// TODO : Use everywhere it's applicable !
|
|
8878
|
-
initClient=function(isNode=true, doOnServerConnection=null, doOnError=null, url, /*OPTIONAL*/port=25000, /*OPTIONAL*/timeout=10000, /*OPTIONAL*/selfParam=null){
|
|
8879
|
-
let aotraClient={};
|
|
8880
9521
|
|
|
8881
|
-
|
|
8882
|
-
|
|
8883
|
-
|
|
8884
|
-
|
|
9522
|
+
// ========================= UTILITY SERVERSIDE METHODS : =========================
|
|
9523
|
+
|
|
9524
|
+
class ListManager{
|
|
9525
|
+
constructor(config){
|
|
9526
|
+
this.config=config;
|
|
9527
|
+
this.maxItemsNumber=nonull(this.config.max,999);
|
|
9528
|
+
this.simultaneousItemsNumber=nonull(this.config.simultaneous,1);
|
|
9529
|
+
this.sessionDurationSeconds=nonull(this.config.duration,null);
|
|
9530
|
+
this.mode=nonull(this.config.mode,"startAtConnexion");
|
|
8885
9531
|
|
|
8886
|
-
|
|
8887
|
-
|
|
9532
|
+
this.itemsInfos={};
|
|
9533
|
+
this.time=null;
|
|
9534
|
+
this.started=false;
|
|
9535
|
+
}
|
|
9536
|
+
|
|
9537
|
+
addItem(id,item){
|
|
9538
|
+
if(id==null){
|
|
8888
9539
|
// TRACE
|
|
8889
|
-
lognow("ERROR :
|
|
8890
|
-
return
|
|
9540
|
+
lognow("ERROR : Cannot add item with no id.");
|
|
9541
|
+
return;
|
|
8891
9542
|
}
|
|
8892
|
-
|
|
8893
|
-
|
|
8894
|
-
|
|
8895
|
-
|
|
8896
|
-
if(doOnServerConnection){
|
|
8897
|
-
if(selfParam) doOnServerConnection.apply(selfParam,[socketToServer]);
|
|
8898
|
-
else doOnServerConnection(socketToServer);
|
|
8899
|
-
}
|
|
8900
|
-
});
|
|
8901
|
-
|
|
8902
|
-
// Errors handling :
|
|
8903
|
-
if(doOnError){
|
|
8904
|
-
|
|
8905
|
-
// // DBG
|
|
8906
|
-
// lognow(">>> serverSocket.connected:"+serverSocket.connected);
|
|
8907
|
-
// lognow(">>> serverSocket:",serverSocket);
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
const errorMethod=function(){
|
|
8911
|
-
// TRACE
|
|
8912
|
-
lognow("ERROR : CLIENT : Client encountered an error while trying to connect to server.");
|
|
8913
|
-
|
|
8914
|
-
if(selfParam) doOnError.apply(selfParam);
|
|
8915
|
-
else doOnError();
|
|
8916
|
-
};
|
|
8917
|
-
|
|
8918
|
-
// // DOES NOT SEEM TO WORK :
|
|
8919
|
-
// socketToServer.on("connect_failed", errorMethod);
|
|
8920
|
-
// // DOES NOT SEEM TO WORK :
|
|
8921
|
-
// socketToServer.on("error", errorMethod);
|
|
8922
|
-
|
|
9543
|
+
if(!item){
|
|
9544
|
+
// TRACE
|
|
9545
|
+
lognow("ERROR : Cannot add null item.");
|
|
9546
|
+
return;
|
|
8923
9547
|
}
|
|
8924
|
-
|
|
8925
|
-
|
|
8926
|
-
|
|
8927
|
-
|
|
8928
|
-
|
|
8929
|
-
|
|
8930
|
-
|
|
9548
|
+
const numberOfItemsCurrently=countKeys(this.itemsInfos);
|
|
9549
|
+
if(this.maxItemsNumber<=numberOfItemsCurrently){
|
|
9550
|
+
// TRACE
|
|
9551
|
+
lognow("ERROR : Cannot add item with id «"+id+"», list already full.");
|
|
9552
|
+
return;
|
|
9553
|
+
}
|
|
9554
|
+
if(numberOfItemsCurrently==0 && this.mode==="startAtConnexion"){
|
|
9555
|
+
this.startSession();
|
|
9556
|
+
}
|
|
9557
|
+
this.itemsInfos[id]={
|
|
9558
|
+
item:item,
|
|
9559
|
+
time:getNow(),
|
|
9560
|
+
};
|
|
9561
|
+
}
|
|
8931
9562
|
|
|
8932
|
-
|
|
8933
|
-
|
|
8934
|
-
|
|
8935
|
-
|
|
8936
|
-
|
|
8937
|
-
|
|
8938
|
-
// ===================================================================================================
|
|
8939
|
-
|
|
8940
|
-
/*FUSRODA server stands from FSRD SERVER, for Fucking Simple Remote Desktop SERVER*/
|
|
8941
|
-
createFusrodaServer=function(certPathParam=null,keyPathParam=null,portParam=6001){
|
|
9563
|
+
startSession(){
|
|
9564
|
+
this.time==getNow();
|
|
9565
|
+
this.started=true;
|
|
9566
|
+
}
|
|
8942
9567
|
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
9568
|
+
stopSession(){
|
|
9569
|
+
this.started=false;
|
|
9570
|
+
}
|
|
8946
9571
|
|
|
8947
|
-
|
|
8948
|
-
|
|
8949
|
-
|
|
8950
|
-
|
|
9572
|
+
isSessionActive(){
|
|
9573
|
+
if(!this.sessionDurationSeconds) return true;
|
|
9574
|
+
if(!this.started) return false;
|
|
9575
|
+
return !hasDelayPassed(this.time, this.sessionDurationSeconds*1000);
|
|
9576
|
+
}
|
|
8951
9577
|
|
|
8952
|
-
|
|
8953
|
-
|
|
8954
|
-
|
|
8955
|
-
|
|
8956
|
-
|
|
8957
|
-
|
|
8958
|
-
|
|
8959
|
-
serverParam.receive("protocol_fusroda", (message, clientSocket)=> {
|
|
8960
|
-
serverParam.addToRoom(clientSocket,"clients");
|
|
8961
|
-
});
|
|
8962
|
-
|
|
8963
|
-
|
|
8964
|
-
serverParam.sendScreenshotsRoutine=setInterval(()=>{
|
|
8965
|
-
if(serverParam.isScreenshotStarted) return;
|
|
8966
|
-
|
|
8967
|
-
serverParam.isScreenshotStarted=true;
|
|
8968
|
-
|
|
8969
|
-
screenshot().then((img) => {
|
|
9578
|
+
isClientActive(clientId){
|
|
9579
|
+
if(!clientsListManager.isSessionActive()) return false;
|
|
9580
|
+
const clientPosition=this.getItemPosition(clientId);
|
|
9581
|
+
const result=(clientPosition<=this.maxItemsNumber);
|
|
9582
|
+
return result;
|
|
9583
|
+
}
|
|
8970
9584
|
|
|
8971
|
-
|
|
8972
|
-
|
|
8973
|
-
|
|
8974
|
-
|
|
8975
|
-
|
|
8976
|
-
|
|
8977
|
-
|
|
8978
|
-
|
|
8979
|
-
|
|
8980
|
-
|
|
8981
|
-
|
|
8982
|
-
|
|
8983
|
-
|
|
8984
|
-
},portParam,certPathParam,keyPathParam);
|
|
8985
|
-
|
|
8986
|
-
|
|
8987
|
-
// const doOnConnect=(serverParam, clientSocketParam)=>{
|
|
8988
|
-
// };
|
|
8989
|
-
// const doOnFinalizeServer=(serverParam)=>{
|
|
8990
|
-
// /*DO NOTHING*/
|
|
8991
|
-
// };
|
|
8992
|
-
// const server={};
|
|
8993
|
-
// server.start=(port=6001)=>{
|
|
8994
|
-
// server.httpServer=launchNodeHTTPServer(port, doOnConnect, doOnFinalizeServer, sslOptions);
|
|
8995
|
-
// };
|
|
8996
|
-
|
|
8997
|
-
return server;
|
|
8998
|
-
}
|
|
8999
|
-
|
|
9585
|
+
removeItem(id){
|
|
9586
|
+
if(id==null){
|
|
9587
|
+
// TRACE
|
|
9588
|
+
lognow("ERROR : Cannot remove item, no id specified.");
|
|
9589
|
+
return;
|
|
9590
|
+
}
|
|
9591
|
+
if(!this.itemsInfos[id]){
|
|
9592
|
+
// TRACE
|
|
9593
|
+
lognow("ERROR : Cannot remove item, item not found for id «"+id+"».");
|
|
9594
|
+
return;
|
|
9595
|
+
}
|
|
9596
|
+
delete this.itemsInfos[id];
|
|
9597
|
+
}
|
|
9000
9598
|
|
|
9599
|
+
// Goes from 1 to <length>:
|
|
9600
|
+
getItemPosition(id){
|
|
9601
|
+
if(id==null){
|
|
9602
|
+
// TRACE
|
|
9603
|
+
lognow("ERROR : Cannot calculate item position, no id specified.");
|
|
9604
|
+
return;
|
|
9605
|
+
}
|
|
9606
|
+
if(!this.itemsInfos[id]){
|
|
9607
|
+
// TRACE
|
|
9608
|
+
lognow("ERROR : Cannot calculate item position, item not found for id «"+id+"».");
|
|
9609
|
+
return;
|
|
9610
|
+
}
|
|
9611
|
+
let result=0;
|
|
9612
|
+
foreach(this.itemsInfos,(itemInfo,key)=>{
|
|
9613
|
+
result++;
|
|
9614
|
+
if(id==key) return "break";
|
|
9615
|
+
},null,(itemInfo1,itemInfo2)=>item1.time<item2.time);
|
|
9616
|
+
return result;
|
|
9617
|
+
}
|
|
9001
9618
|
|
|
9002
|
-
createFusrodaClient=function(doOnDataReception, urlParam=null,portParam=6001){
|
|
9003
9619
|
|
|
9004
|
-
|
|
9005
|
-
const client=WebsocketImplementation.getStatic(isNode).connectToServer(urlParam, portParam);
|
|
9006
|
-
client.onConnectionToServer((nodeClientInstance)=>{
|
|
9007
|
-
|
|
9008
|
-
nodeClientInstance.send("protocol_fusroda");
|
|
9620
|
+
}
|
|
9009
9621
|
|
|
9010
|
-
|
|
9011
|
-
|
|
9012
|
-
});
|
|
9013
|
-
|
|
9014
|
-
return client;
|
|
9622
|
+
function getListManager(config){
|
|
9623
|
+
return new ListManager(config);
|
|
9015
9624
|
}
|
|
9016
9625
|
|
|
9017
9626
|
|
|
@@ -9020,7 +9629,8 @@ createFusrodaClient=function(doOnDataReception, urlParam=null,portParam=6001){
|
|
|
9020
9629
|
|
|
9021
9630
|
|
|
9022
9631
|
|
|
9023
|
-
|
|
9632
|
+
|
|
9633
|
+
/*utils COMMONS library associated with aotra version : «1.0.0.000 (11/07/2022-00:48:13)»*/
|
|
9024
9634
|
/*-----------------------------------------------------------------------------*/
|
|
9025
9635
|
|
|
9026
9636
|
|
|
@@ -10884,9 +11494,9 @@ window.LZWString={
|
|
|
10884
11494
|
|
|
10885
11495
|
readBits : function(numBits, data){
|
|
10886
11496
|
var res=0;
|
|
10887
|
-
var
|
|
11497
|
+
var maxPower=Math.pow(2,numBits);
|
|
10888
11498
|
var power=1;
|
|
10889
|
-
while (power!=
|
|
11499
|
+
while (power!=maxPower){
|
|
10890
11500
|
res |= this.readBit(data) * power;
|
|
10891
11501
|
power <<= 1;
|
|
10892
11502
|
}
|
|
@@ -11230,6 +11840,31 @@ window.copy=function(array){
|
|
|
11230
11840
|
return result;
|
|
11231
11841
|
}
|
|
11232
11842
|
|
|
11843
|
+
|
|
11844
|
+
|
|
11845
|
+
window.removeByIndex=function(array, elementIndex){
|
|
11846
|
+
if(array instanceof Array){
|
|
11847
|
+
if(empty(array)) return false;
|
|
11848
|
+
if(elementIndex<0) return false;
|
|
11849
|
+
array.splice(elementIndex, 1);
|
|
11850
|
+
return true;
|
|
11851
|
+
}
|
|
11852
|
+
let hasDeleted=false;
|
|
11853
|
+
// Case of regular objects that are like associative arrays :
|
|
11854
|
+
let i=0;
|
|
11855
|
+
for (key in array){
|
|
11856
|
+
if(!array.hasOwnProperty(key)) continue;
|
|
11857
|
+
if(i===elementIndex){
|
|
11858
|
+
delete array[key];
|
|
11859
|
+
hasDeleted=true;
|
|
11860
|
+
break;
|
|
11861
|
+
}
|
|
11862
|
+
i++;
|
|
11863
|
+
}
|
|
11864
|
+
return hasDeleted;
|
|
11865
|
+
}
|
|
11866
|
+
|
|
11867
|
+
|
|
11233
11868
|
window.remove=function(array, element){
|
|
11234
11869
|
if(array instanceof Array){
|
|
11235
11870
|
if(empty(array)) return false;
|
|
@@ -11335,15 +11970,41 @@ Math.minInArray=function(arrayOfValues){
|
|
|
11335
11970
|
return min;
|
|
11336
11971
|
};
|
|
11337
11972
|
|
|
11973
|
+
|
|
11974
|
+
|
|
11338
11975
|
// TODO : FIXME : Create a namespace MathUtils. and ArraysUtils. and sort out the corresponding static methods.
|
|
11339
|
-
Math.
|
|
11340
|
-
|
|
11341
|
-
|
|
11342
|
-
|
|
11976
|
+
Math.getRandomsInArray=function(arrayOfValues, numberOfItemsToGet=1){
|
|
11977
|
+
|
|
11978
|
+
if(numberOfItemsToGet<0 || empty(arrayOfValues)) return [];
|
|
11979
|
+
const size=getArraySize(arrayOfValues);
|
|
11980
|
+
// We skip a costly random calculation if we have only one element in array :
|
|
11981
|
+
if(size===1) return [valueAt(arrayOfValues,0)];
|
|
11982
|
+
|
|
11983
|
+
const indexesPool=[];
|
|
11984
|
+
for(let i=0;i<size;i++) indexesPool.push(i);
|
|
11985
|
+
|
|
11986
|
+
const results=[];
|
|
11987
|
+
for(let i=0;i<numberOfItemsToGet;i++){
|
|
11988
|
+
if(empty(indexesPool)) break;
|
|
11989
|
+
const chosenIndex=Math.getRandomInArray(indexesPool,true);
|
|
11990
|
+
const item=valueAt(arrayOfValues,chosenIndex);
|
|
11991
|
+
results.push(item);
|
|
11992
|
+
}
|
|
11993
|
+
|
|
11994
|
+
return results;
|
|
11995
|
+
}
|
|
11996
|
+
|
|
11997
|
+
// TODO : FIXME : Create a namespace MathUtils. and ArraysUtils. and sort out the corresponding static methods.
|
|
11998
|
+
Math.getRandomInArray=function(arrayOfValues,removeFromArray=false){
|
|
11999
|
+
if(empty(arrayOfValues)) return null;
|
|
12000
|
+
const size=getArraySize(arrayOfValues);
|
|
11343
12001
|
// We skip a costly random calculation if we have only one element in array :
|
|
11344
12002
|
if(size===1) return valueAt(arrayOfValues,0);
|
|
11345
|
-
|
|
11346
|
-
|
|
12003
|
+
|
|
12004
|
+
const chosenIndex=Math.getRandomInt(size-1,0);
|
|
12005
|
+
const result=valueAt(arrayOfValues, chosenIndex);
|
|
12006
|
+
if(removeFromArray) removeByIndex(arrayOfValues, chosenIndex);
|
|
12007
|
+
return result;
|
|
11347
12008
|
}
|
|
11348
12009
|
|
|
11349
12010
|
// This function is for arrays, like Array or Objects used as associative arrays you want to test as holding elements :
|
|
@@ -11956,6 +12617,52 @@ Math.demiCurve=function(min, max, value, strategy="triangle", precision=2){
|
|
|
11956
12617
|
return Math.roundTo(cursorFactor,precision);
|
|
11957
12618
|
}
|
|
11958
12619
|
|
|
12620
|
+
// Not the same as curve, where min and max are the minimum and maximum values taken by returned value :
|
|
12621
|
+
Math.curveTriggered=function(minTriggerFactor, maxTriggerFactor, valueFactorParam, strategy="linear"){
|
|
12622
|
+
const valueFactor=Math.coerceInRange(valueFactorParam,0,1);
|
|
12623
|
+
let result=1;
|
|
12624
|
+
if(valueFactor<minTriggerFactor){
|
|
12625
|
+
// Ascending slope :
|
|
12626
|
+
if(minTriggerFactor==0) return 1;
|
|
12627
|
+
result=valueFactor/minTriggerFactor;
|
|
12628
|
+
}else{
|
|
12629
|
+
if(maxTriggerFactor==0) return 1;
|
|
12630
|
+
if(maxTriggerFactor<valueFactor){
|
|
12631
|
+
// Descending slope :
|
|
12632
|
+
result=1-((valueFactor-maxTriggerFactor)/(1-maxTriggerFactor));
|
|
12633
|
+
}else{
|
|
12634
|
+
// Plateau :
|
|
12635
|
+
return 1;
|
|
12636
|
+
}
|
|
12637
|
+
}
|
|
12638
|
+
return result;
|
|
12639
|
+
}
|
|
12640
|
+
|
|
12641
|
+
Math.slopeTriggered=function(minTriggerFactor, maxTriggerFactor, valueFactorParam, direction="ascending", strategy="linear"){
|
|
12642
|
+
|
|
12643
|
+
const valueFactor=Math.coerceInRange(valueFactorParam,0,1);
|
|
12644
|
+
|
|
12645
|
+
if(valueFactor<minTriggerFactor){
|
|
12646
|
+
// Beginning plateau :
|
|
12647
|
+
if(direction=="ascending") return 0;
|
|
12648
|
+
else return 1;
|
|
12649
|
+
}else if(maxTriggerFactor<valueFactor){
|
|
12650
|
+
// Ending plateau :
|
|
12651
|
+
if(direction=="ascending") return 1;
|
|
12652
|
+
else return 0;
|
|
12653
|
+
}
|
|
12654
|
+
|
|
12655
|
+
// Intermediate slope :
|
|
12656
|
+
const total=(maxTriggerFactor-minTriggerFactor);
|
|
12657
|
+
if(total==0) return ((direction=="ascending")?1:0);
|
|
12658
|
+
|
|
12659
|
+
const factor=((valueFactor-minTriggerFactor)/total);
|
|
12660
|
+
|
|
12661
|
+
return ((direction=="ascending")?factor:(1-factor));
|
|
12662
|
+
}
|
|
12663
|
+
|
|
12664
|
+
|
|
12665
|
+
|
|
11959
12666
|
Math.curve=function(min, max, value, strategy="triangle",
|
|
11960
12667
|
// It is actually where to place the «summit» of the curve, before returning to 0,
|
|
11961
12668
|
// the factor value where the values starts decreasing, after having increased all along, and then goes back to 0:
|