aotrautils 0.0.44 → 0.0.47
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/README.md +4 -4
- aotrautils/aotrautils.build.js +937 -1533
- aotrautils/package.json +2 -2
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-20:58:07)»*/
|
|
3
4
|
/*-----------------------------------------------------------------------------*/
|
|
4
5
|
/* ## Utility global methods in a browser (htmljs) client environment.
|
|
5
6
|
*
|
|
@@ -9,7 +10,7 @@
|
|
|
9
10
|
*
|
|
10
11
|
* # Library name : «aotrautils»
|
|
11
12
|
* # Library license : HGPL(Help Burma) (see aotra README information for details : https://alqemia.com/aotra.js )
|
|
12
|
-
* # Author name : Jérémie Ratomposon (massively helped by
|
|
13
|
+
* # Author name : Jérémie Ratomposon (massively helped by his native country free education system)
|
|
13
14
|
* # Author email : info@alqemia.com
|
|
14
15
|
* # Organization name : Alqemia
|
|
15
16
|
* # Organization email : admin@alqemia.com
|
|
@@ -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
6139
|
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
radio.indexValue=i;
|
|
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+="
|
|
6260
|
+
let style="";
|
|
6261
|
+
style+="position:absolute;";
|
|
6262
|
+
style+="left:50%;top:50%;";
|
|
6263
|
+
style+="z-index:" + (MAX_Z_INDEX_OVERLAY_LOCAL) + ";";
|
|
6205
6264
|
style+="z-index:" + (MAX_Z_INDEX_OVERLAY_LOCAL) + ";";
|
|
6206
|
-
style+="width:"+
|
|
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,63 +6768,416 @@ 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
6771
|
|
|
6695
6772
|
|
|
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
6773
|
|
|
6713
6774
|
|
|
6714
|
-
// COMPATIBILITY browser javascript / nodejs environment :
|
|
6715
6775
|
|
|
6716
|
-
|
|
6776
|
+
// -Client :
|
|
6717
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
|
+
});
|
|
6718
6801
|
|
|
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
|
+
// });
|
|
6719
6843
|
|
|
6720
|
-
|
|
6721
|
-
|
|
6844
|
+
|
|
6845
|
+
aotraClient.client.socketToServer=socketToServer;
|
|
6846
|
+
|
|
6847
|
+
return aotraClient;
|
|
6848
|
+
};
|
|
6849
|
+
|
|
6850
|
+
|
|
6851
|
+
return aotraClient;
|
|
6852
|
+
}
|
|
6722
6853
|
|
|
6723
6854
|
|
|
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
6855
|
|
|
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
6856
|
|
|
6857
|
+
// ========================= FUSRODA CLIENT : =========================
|
|
6746
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;
|
|
6747
6870
|
|
|
6748
|
-
|
|
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-20:58:07)»*/
|
|
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 his native country free education system)
|
|
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
|
+
//=========================================================================
|
|
6749
7181
|
|
|
6750
7182
|
|
|
6751
7183
|
// ==================== COMMONS Geometry ====================
|
|
@@ -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){
|
|
@@ -7740,1287 +8180,154 @@ Math.getBidimensional8Direction=(currentPosition,destination,invertYAxis=false)=
|
|
|
7740
8180
|
intersectionXMin=xMin2; // Basically zone2 x !
|
|
7741
8181
|
intersectionXMax=xMax2;
|
|
7742
8182
|
// We calculate edges :
|
|
7743
|
-
hasXAxisWholeZone2=true;
|
|
7744
|
-
}
|
|
7745
|
-
}
|
|
7746
|
-
|
|
7747
|
-
let w=intersectionXMax-intersectionXMin;
|
|
7748
|
-
let x=(intersectionXMax+intersectionXMin)*.5; // (The average)
|
|
7749
|
-
|
|
7750
|
-
|
|
7751
|
-
// Vertical axis :
|
|
7752
|
-
|
|
7753
|
-
let hasYAxisWholeZone1=false;
|
|
7754
|
-
let hasYAxisWholeZone2=false;
|
|
7755
|
-
|
|
7756
|
-
let demiHeight1=zone1.h*.5;
|
|
7757
|
-
let demiHeight2=zone2.h*.5;
|
|
7758
|
-
|
|
7759
|
-
let yMin1=zone1.y-demiHeight1;
|
|
7760
|
-
let yMax1=zone1.y+demiHeight1;
|
|
7761
|
-
let yMin2=zone2.y-demiHeight2;
|
|
7762
|
-
let yMax2=zone2.y+demiHeight2;
|
|
7763
|
-
|
|
7764
|
-
|
|
7765
|
-
let deltaYMins=yMin2-yMin1;
|
|
7766
|
-
let deltaYMaxs=yMax2-yMax1;
|
|
7767
|
-
|
|
7768
|
-
let deltaYMinsSign=(deltaYMins<0?-1:1);
|
|
7769
|
-
let deltaYMaxsSign=(deltaYMaxs<0?-1:1);
|
|
7770
|
-
let intersectionYMin=0;
|
|
7771
|
-
let intersectionYMax=0;
|
|
7772
|
-
if(deltaYMinsSign==deltaYMaxsSign){ // case «parallelogram»
|
|
7773
|
-
if(deltaYMinsSign<0){ // case /=/ parallelogram
|
|
7774
|
-
intersectionYMin=yMin1;
|
|
7775
|
-
intersectionYMax=yMax2;
|
|
7776
|
-
// We calculate edges :
|
|
7777
|
-
edges.push("bottom"); // Here the Y axis is NOT inverted !
|
|
7778
|
-
}else{ // case \=\ parallelogram
|
|
7779
|
-
intersectionYMin=yMin2;
|
|
7780
|
-
intersectionYMax=yMax1;
|
|
7781
|
-
// We calculate edges :
|
|
7782
|
-
edges.push("top"); // Here the Y axis is NOT inverted !
|
|
7783
|
-
}
|
|
7784
|
-
}else{ // case «trapeze»
|
|
7785
|
-
if(deltaYMinsSign<0){ // case /=\ trapeze
|
|
7786
|
-
intersectionYMin=yMin1; // Basically zone2 y !
|
|
7787
|
-
intersectionYMax=yMax1;
|
|
7788
|
-
// We calculate edges :
|
|
7789
|
-
hasYAxisWholeZone1=true;
|
|
7790
|
-
}else{ // case \=/ trapeze
|
|
7791
|
-
intersectionYMin=yMin2; // Basically zone2 y !
|
|
7792
|
-
intersectionYMax=yMax2;
|
|
7793
|
-
// We calculate edges :
|
|
7794
|
-
hasYAxisWholeZone2=true;
|
|
7795
|
-
}
|
|
7796
|
-
}
|
|
7797
|
-
|
|
7798
|
-
let h=intersectionYMax-intersectionYMin;
|
|
7799
|
-
let y=(intersectionYMax+intersectionYMin)*.5; // (The average)
|
|
7800
|
-
|
|
7801
|
-
// We calculate edges :
|
|
7802
|
-
if(hasXAxisWholeZone1 && hasYAxisWholeZone1) edges.push("wholeZone1");
|
|
7803
|
-
if(hasXAxisWholeZone2 && hasYAxisWholeZone2) edges.push("wholeZone2");
|
|
7804
|
-
|
|
7805
|
-
return {x:x,y:y,w:w,h:h,edges:edges};
|
|
7806
|
-
}
|
|
7807
|
-
|
|
7808
|
-
/*public static*/window.areZonesIdentical=function(zone1, zone2){
|
|
7809
|
-
return (zone1==zone2 || (zone1.x==zone2.x || zone1.y==zone2.y || zone1.w==zone2.w || zone1.h==zone2.h));
|
|
7810
|
-
}
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
// 3D :
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
/*
|
|
7818
|
-
// (needs the jsgl library file !)
|
|
7819
|
-
// UNUSED
|
|
7820
|
-
function screenToSpherePt(mouseX, mouseY) {
|
|
7821
|
-
var p = { x: camera_mat.e12, y: camera_mat.e13, z: camera_mat.e14 + 1 };
|
|
7822
|
-
// camera dir
|
|
7823
|
-
var r = { x: camera_mat.e0, y: camera_mat.e1, z: camera_mat.e2 };
|
|
7824
|
-
var up = { x: camera_mat.e4, y: camera_mat.e5, z: camera_mat.e6 };
|
|
7825
|
-
var right = { x: camera_mat.e8, y: camera_mat.e9, z: camera_mat.e10 };
|
|
7826
|
-
var tan_half = Math.tan(horizontal_fov_radians / 2);
|
|
7827
|
-
r = jsgl.vectorAdd(r, jsgl.vectorScale(right, mouseX * tan_half));
|
|
7828
|
-
r = jsgl.vectorAdd(r, jsgl.vectorScale(up, mouseY * tan_half));
|
|
7829
|
-
r = jsgl.vectorNormalize(r);
|
|
7830
|
-
|
|
7831
|
-
return rayVsUnitSphereClosestPoint(p, r);
|
|
7832
|
-
}
|
|
7833
|
-
*/
|
|
7834
|
-
|
|
7835
|
-
/*
|
|
7836
|
-
// (needs the jsgl library file !)
|
|
7837
|
-
// UNUSED
|
|
7838
|
-
// Return the first exterior hit or closest point between the unit
|
|
7839
|
-
// sphere and the ray starting at p and going in the r direction.
|
|
7840
|
-
function rayVsUnitSphereClosestPoint(p, r) {
|
|
7841
|
-
var p_len2 = jsgl.dotProduct(p, p);
|
|
7842
|
-
if (p_len2 < 1) {
|
|
7843
|
-
// Ray is inside sphere, no exterior hit.
|
|
7844
|
-
return null;
|
|
7845
|
-
}
|
|
7846
|
-
|
|
7847
|
-
var along_ray = -jsgl.dotProduct(p, r);
|
|
7848
|
-
if (along_ray < 0) {
|
|
7849
|
-
// Behind ray start-point.
|
|
7850
|
-
return null;
|
|
7851
|
-
}
|
|
7852
|
-
|
|
7853
|
-
var perp = jsgl.vectorAdd(p, jsgl.vectorScale(r, along_ray));
|
|
7854
|
-
var perp_len2 = jsgl.dotProduct(perp, perp);
|
|
7855
|
-
if (perp_len2 >= 0.999999) {
|
|
7856
|
-
// Return the closest point.
|
|
7857
|
-
return jsgl.vectorNormalize(perp);
|
|
7858
|
-
}
|
|
7859
|
-
|
|
7860
|
-
// Compute intersection.
|
|
7861
|
-
var e = Math.sqrt(1 - jsgl.dotProduct(perp, perp));
|
|
7862
|
-
var hit = jsgl.vectorAdd(p, jsgl.vectorScale(r, (along_ray - e)));
|
|
7863
|
-
return jsgl.vectorNormalize(hit);
|
|
7864
|
-
}
|
|
7865
|
-
*/
|
|
7866
|
-
|
|
7867
|
-
|
|
7868
|
-
|
|
7869
|
-
|
|
7870
|
-
|
|
7871
|
-
|
|
7872
|
-
|
|
7873
|
-
|
|
7874
|
-
|
|
7875
|
-
|
|
7876
|
-
|
|
7877
|
-
|
|
7878
|
-
|
|
7879
|
-
|
|
7880
|
-
|
|
7881
|
-
|
|
7882
|
-
|
|
7883
|
-
|
|
7884
|
-
|
|
7885
|
-
|
|
7886
|
-
|
|
7887
|
-
// MUST REMAIN AT THE END OF THIS LIBRARY FILE !
|
|
7888
|
-
|
|
7889
|
-
AOTRAUTILS_GEOMETRY_LIB_IS_LOADED=true;/*utils SERVER library associated with aotra version : «1.0.0.000 (11/01/2022-00:18:33)»*/
|
|
7890
|
-
/*-----------------------------------------------------------------------------*/
|
|
7891
|
-
|
|
7892
|
-
|
|
7893
|
-
/* ## Utility global methods in a javascript, console (nodejs) server, or vanilla javascript with no browser environment.
|
|
7894
|
-
*
|
|
7895
|
-
* This set of methods gathers utility generic-purpose methods usable in any JS project.
|
|
7896
|
-
* Several authors of snippets published freely on the Internet contributed to this library.
|
|
7897
|
-
* Feel free to use/modify-enhance/publish them under the terms of its license.
|
|
7898
|
-
*
|
|
7899
|
-
* # Library name : «aotrautils»
|
|
7900
|
-
* # Library license : HGPL(Help Burma) (see aotra README information for details : https://alqemia.com/aotra.js )
|
|
7901
|
-
* # Author name : Jérémie Ratomposon (massively helped by its programming egos legions)
|
|
7902
|
-
* # Author email : info@alqemia.com
|
|
7903
|
-
* # Organization name : Alqemia
|
|
7904
|
-
* # Organization email : admin@alqemia.com
|
|
7905
|
-
* # Organization website : https://alqemia.com
|
|
7906
|
-
*
|
|
7907
|
-
*
|
|
7908
|
-
*/
|
|
7909
|
-
|
|
7910
|
-
|
|
7911
|
-
|
|
7912
|
-
// COMPATIBILITY browser javascript / nodejs environment :
|
|
7913
|
-
|
|
7914
|
-
if(typeof(window)==="undefined") window=global;
|
|
7915
|
-
|
|
7916
|
-
|
|
7917
|
-
|
|
7918
|
-
|
|
7919
|
-
// =================================================================================
|
|
7920
|
-
// NODEJS UTILS
|
|
7921
|
-
|
|
7922
|
-
const FILE_ENCODING="utf8";
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
// Nodejs filesystem utils :
|
|
7926
|
-
if(typeof(fs)==="undefined"){
|
|
7927
|
-
// TRACE
|
|
7928
|
-
console.log("WARN : Could not find the nodejs dependency «fs», aborting persister setup.");
|
|
7929
|
-
window.getPersister=()=>{ return null; };
|
|
7930
|
-
|
|
7931
|
-
}else{
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
window.getPersister=function(dataDirPath,suffix=""){
|
|
7935
|
-
|
|
7936
|
-
let self={
|
|
7937
|
-
|
|
7938
|
-
dataDirPath:dataDirPath,
|
|
7939
|
-
suffix:suffix,
|
|
7940
|
-
|
|
7941
|
-
//FILE_NAME_PATTERN:"data.clientId.repositoryName.json",
|
|
7942
|
-
/*private*/getPath:function(clientId,repositoryName){
|
|
7943
|
-
// let path=self.FILE_NAME_PATTERN.replace(new RegExp("@clientId@","g"),clientId);
|
|
7944
|
-
let path=`${self.dataDirPath}`
|
|
7945
|
-
+ (blank(self.suffix)?"":(self.suffix+"."))
|
|
7946
|
-
+`${clientId}.${repositoryName}.json`;
|
|
7947
|
-
return path;
|
|
7948
|
-
},
|
|
7949
|
-
|
|
7950
|
-
readTreeObjectFromFile:function(clientId, repositoryName, CLASSNAME_ATTR_NAME=DEFAULT_CLASSNAME_ATTR_NAME){
|
|
7951
|
-
|
|
7952
|
-
let path=self.getPath(clientId,repositoryName);
|
|
7953
|
-
|
|
7954
|
-
let resultFlat=null;
|
|
7955
|
-
|
|
7956
|
-
try{
|
|
7957
|
-
resultFlat=fs.readFileSync(path, FILE_ENCODING);
|
|
7958
|
-
|
|
7959
|
-
}catch(error){
|
|
7960
|
-
// TRACE
|
|
7961
|
-
console.log("ERROR : Could not read file «"+path+"».");
|
|
7962
|
-
|
|
7963
|
-
return null;
|
|
7964
|
-
}
|
|
7965
|
-
|
|
7966
|
-
|
|
7967
|
-
let resultData={};
|
|
7968
|
-
if(!empty(resultFlat)) resultData=JSON.parse(resultFlat);
|
|
7969
|
-
|
|
7970
|
-
|
|
7971
|
-
if(!empty(resultData) && isFlatMap(resultData)){
|
|
7972
|
-
|
|
7973
|
-
resultData=getAsTreeStructure(resultData,true
|
|
7974
|
-
// We have to keep the type information, here too ! (in the sub-objects)
|
|
7975
|
-
,false);
|
|
7976
|
-
|
|
7977
|
-
}
|
|
7978
|
-
|
|
7979
|
-
return resultData;
|
|
7980
|
-
},
|
|
7981
|
-
|
|
7982
|
-
|
|
7983
|
-
saveDataToFileForClient:function(clientId,repositoryName,dataFlatForClient,forceKeepUnflatten=false,doOnSuccess=null){
|
|
7984
|
-
|
|
7985
|
-
|
|
7986
|
-
|
|
7987
|
-
if(!empty(dataFlatForClient) && !isFlatMap(dataFlatForClient) && !forceKeepUnflatten){
|
|
7988
|
-
dataFlatForClient=getAsFlatStructure(dataFlatForClient,true);
|
|
7989
|
-
}
|
|
7990
|
-
|
|
7991
|
-
// reserved characters : -/\^$*+?.()|[]{}
|
|
7992
|
-
// CANNOT USE stringifyObject(...) function because we are in a common, lower-level library !
|
|
7993
|
-
let dataFlatStr=JSON.stringify(dataFlatForClient)
|
|
7994
|
-
// We «aerate» the produced JSON :
|
|
7995
|
-
.replace(/":[\w]*\{/gim,"\":{\n").replace(/,"/gim,",\n\"")
|
|
7996
|
-
// ...except for inline, escaped JSON string representations :
|
|
7997
|
-
.replace(/\\\":[\w]*\{\n/gim,"\\\":{");
|
|
7998
|
-
// NO : .replace(/}/gim,"}\n");
|
|
7999
|
-
|
|
8000
|
-
|
|
8001
|
-
let path=self.getPath(clientId,repositoryName);
|
|
8002
|
-
|
|
8003
|
-
fs.writeFile(path, dataFlatStr, FILE_ENCODING, (error) => {
|
|
8004
|
-
if(error){
|
|
8005
|
-
// TRACE
|
|
8006
|
-
console.log("ERROR : Could not write file «"+path+"»:",error);
|
|
8007
|
-
throw error;
|
|
8008
|
-
}
|
|
8009
|
-
if(doOnSuccess) doOnSuccess(dataFlatForClient);
|
|
8010
|
-
});
|
|
8011
|
-
}
|
|
8012
|
-
|
|
8013
|
-
};
|
|
8014
|
-
|
|
8015
|
-
|
|
8016
|
-
return self;
|
|
8017
|
-
};
|
|
8018
|
-
|
|
8019
|
-
}
|
|
8020
|
-
|
|
8021
|
-
|
|
8022
|
-
|
|
8023
|
-
// Nodejs server launching helper functions :
|
|
8024
|
-
//Networking management :
|
|
8025
|
-
//- WEBSOCKETS AND NODEJS :
|
|
8026
|
-
|
|
8027
|
-
// -Server :
|
|
8028
|
-
|
|
8029
|
-
getServerParams=function(portParam,isSecureParam,certPathParam,keyPathParam){
|
|
8030
|
-
|
|
8031
|
-
// Node dependencies :
|
|
8032
|
-
// https=require("https");
|
|
8033
|
-
// fs=require("fs");
|
|
8034
|
-
|
|
8035
|
-
if(!https){
|
|
8036
|
-
// TRACE
|
|
8037
|
-
console.log("WARN : Could not find the nodejs dependency «https», aborting SSL setup.");
|
|
8038
|
-
return null;
|
|
8039
|
-
}
|
|
8040
|
-
if(!fs){
|
|
8041
|
-
// TRACE
|
|
8042
|
-
console.log("WARN : Could not find the nodejs dependency «fs», aborting SSL setup.");
|
|
8043
|
-
return null;
|
|
8044
|
-
}
|
|
8045
|
-
|
|
8046
|
-
var result={};
|
|
8047
|
-
|
|
8048
|
-
// We read the command-line arguments if needed :
|
|
8049
|
-
var argCLPort;
|
|
8050
|
-
var argCLCertPath;
|
|
8051
|
-
var argCLKeyPath;
|
|
8052
|
-
process.argv.forEach(function (val, i){
|
|
8053
|
-
if(i<=1) return;
|
|
8054
|
-
else if(i==2) argCLPort=val;
|
|
8055
|
-
else if(i==3) argCLCertPath=val;
|
|
8056
|
-
else if(i==4) argCLKeyPath=val;
|
|
8057
|
-
});
|
|
8058
|
-
|
|
8059
|
-
// Console, command-line arguments OVERRIDE parameters values :
|
|
8060
|
-
|
|
8061
|
-
result.port=nonull(argCLPort,portParam);
|
|
8062
|
-
result.certPath=null;
|
|
8063
|
-
result.keyPath=null;
|
|
8064
|
-
result.isSecure=isSecureParam;
|
|
8065
|
-
|
|
8066
|
-
if(isSecureParam){
|
|
8067
|
-
result.certPath=nonull(argCLCertPath,certPathParam);
|
|
8068
|
-
result.keyPath=nonull(argCLKeyPath,keyPathParam);
|
|
8069
|
-
}
|
|
8070
|
-
|
|
8071
|
-
// Eventual encryption options :
|
|
8072
|
-
result.sslOptions=null;
|
|
8073
|
-
if(isSecureParam){
|
|
8074
|
-
result.sslOptions={
|
|
8075
|
-
cert: fs.readFileSync(certPath),
|
|
8076
|
-
key: fs.readFileSync(keyPath),
|
|
8077
|
-
};
|
|
8078
|
-
}
|
|
8079
|
-
|
|
8080
|
-
return result;
|
|
8081
|
-
}
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
// NODE ONLY SERVER / CLIENTS :
|
|
8085
|
-
WebsocketImplementation={
|
|
8086
|
-
|
|
8087
|
-
isNode:true,
|
|
8088
|
-
|
|
8089
|
-
// COMMON METHODS
|
|
8090
|
-
/*private static*/isInRoom(clientSocket, clientsRoomsTag){
|
|
8091
|
-
return !clientsRoomsTag || empty(clientsRoomsTag) || contains(clientsRoomsTag, clientSocket.clientRoomTag);
|
|
8092
|
-
},
|
|
8093
|
-
|
|
8094
|
-
// NODE SERVER
|
|
8095
|
-
|
|
8096
|
-
getStatic:(isNode=true)=>{
|
|
8097
|
-
|
|
8098
|
-
WebsocketImplementation.isNode=isNode;
|
|
8099
|
-
|
|
8100
|
-
// OLD : socket.io :
|
|
8101
|
-
// https://stackoverflow.com/questions/31156884/how-to-use-https-on-node-js-using-express-socket-io
|
|
8102
|
-
// https://stackoverflow.com/questions/6599470/node-js-socket-io-with-ssl
|
|
8103
|
-
// https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener
|
|
8104
|
-
// if(typeof(socketIO)==="undefined"){
|
|
8105
|
-
// // TRACE
|
|
8106
|
-
// console.log("«socket.io» SERVER library not called yet, calling it now.");
|
|
8107
|
-
// socketIO=require("socket.io");
|
|
8108
|
-
// return socketIO;
|
|
8109
|
-
// }
|
|
8110
|
-
// if(typeof(socketIO)==="undefined"){
|
|
8111
|
-
// // TRACE
|
|
8112
|
-
// console.log("ERROR : «socket.io» SERVER library not found. Cannot launch nodejs server. Aborting.");
|
|
8113
|
-
// return null;
|
|
8114
|
-
// }
|
|
8115
|
-
|
|
8116
|
-
// *********************************************************************************
|
|
8117
|
-
|
|
8118
|
-
// NEW : ws :
|
|
8119
|
-
// https://github.com/websockets/ws#installing
|
|
8120
|
-
// https://github.com/websockets/ws/blob/master/doc/ws.md#event-message
|
|
8121
|
-
// ON BROWSER SIDE : Native Websockets :
|
|
8122
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
|
|
8123
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
|
|
8124
|
-
if(typeof(WebSocket)==="undefined"){
|
|
8125
|
-
WebSocket=require("ws");
|
|
8126
|
-
// TRACE
|
|
8127
|
-
console.log("«ws» SERVER library not called yet, calling it now.");
|
|
8128
|
-
return WebsocketImplementation;
|
|
8129
|
-
}
|
|
8130
|
-
if(typeof(WebSocket)==="undefined"){
|
|
8131
|
-
// TRACE
|
|
8132
|
-
console.log("ERROR : «ws» SERVER library not found. Cannot launch nodejs server. Aborting.");
|
|
8133
|
-
return null;
|
|
8134
|
-
}
|
|
8135
|
-
|
|
8136
|
-
return WebsocketImplementation;
|
|
8137
|
-
},
|
|
8138
|
-
|
|
8139
|
-
|
|
8140
|
-
getServer:(listenableServer)=>{
|
|
8141
|
-
|
|
8142
|
-
if(!WebsocketImplementation.isNode){
|
|
8143
|
-
// TRACE
|
|
8144
|
-
throw new Error("ERROR : SERVER : Server launch is not supported in a non-nodejs context.");
|
|
8145
|
-
}
|
|
8146
|
-
|
|
8147
|
-
// OLD : socket.io :
|
|
8148
|
-
// // Loading socket.io
|
|
8149
|
-
// let serverSocket=socketIO.listen(listenableServer);
|
|
8150
|
-
|
|
8151
|
-
const serverSocket=new WebSocket.Server({ "server":listenableServer });
|
|
8152
|
-
|
|
8153
|
-
|
|
8154
|
-
const nodeServerInstance={
|
|
8155
|
-
|
|
8156
|
-
clientTimeoutMillis:20000,
|
|
8157
|
-
|
|
8158
|
-
// clientsSockets:[],
|
|
8159
|
-
|
|
8160
|
-
serverSocket:serverSocket,
|
|
8161
|
-
|
|
8162
|
-
receptionEntryPoints:[],
|
|
8163
|
-
|
|
8164
|
-
|
|
8165
|
-
|
|
8166
|
-
receive:(channelName, doOnIncomingMessage, clientsRoomsTag=null)=>{
|
|
8167
|
-
|
|
8168
|
-
const receptionEntryPoint={
|
|
8169
|
-
channelName:channelName,
|
|
8170
|
-
clientsRoomsTag:clientsRoomsTag,
|
|
8171
|
-
execute:(clientSocketParam)=>{
|
|
8172
|
-
|
|
8173
|
-
|
|
8174
|
-
// With «ws» library we have no choive than register message events inside a «connection» event !
|
|
8175
|
-
// nodeServerInstance.onConnectionToClient((serverParam, clientSocketParam)=>{
|
|
8176
|
-
|
|
8177
|
-
// TODO : Find a way to remove this !
|
|
8178
|
-
|
|
8179
|
-
clientSocketParam.on("message", (dataWrapped)=>{
|
|
8180
|
-
|
|
8181
|
-
dataWrapped=JSON.parse(dataWrapped);
|
|
8182
|
-
|
|
8183
|
-
|
|
8184
|
-
// Channel information is stored in exchanged data :
|
|
8185
|
-
if(dataWrapped.channelName!==receptionEntryPoint.channelName) return;
|
|
8186
|
-
|
|
8187
|
-
|
|
8188
|
-
// nodeServerInstance.serverSocket.clients.forEach((clientSocket)=>{
|
|
8189
|
-
|
|
8190
|
-
const clientSocket=clientSocketParam;
|
|
8191
|
-
|
|
8192
|
-
// Room information is stored in client socket object :
|
|
8193
|
-
if(!WebsocketImplementation.isInRoom(clientSocket, receptionEntryPoint.clientsRoomsTag)) return;
|
|
8194
|
-
|
|
8195
|
-
|
|
8196
|
-
|
|
8197
|
-
doOnIncomingMessage(dataWrapped.data, clientSocket);
|
|
8198
|
-
|
|
8199
|
-
});
|
|
8200
|
-
|
|
8201
|
-
|
|
8202
|
-
// });
|
|
8203
|
-
|
|
8204
|
-
|
|
8205
|
-
|
|
8206
|
-
},
|
|
8207
|
-
};
|
|
8208
|
-
|
|
8209
|
-
|
|
8210
|
-
// DBG
|
|
8211
|
-
console.log("ADD RECEPTION ENTRY POINT channelName:«"+channelName+"»! nodeServerInstance.receptionEntryPoints.length:",nodeServerInstance.receptionEntryPoints.length);
|
|
8212
|
-
|
|
8213
|
-
nodeServerInstance.receptionEntryPoints.push(receptionEntryPoint);
|
|
8214
|
-
|
|
8215
|
-
|
|
8216
|
-
return nodeServerInstance;
|
|
8217
|
-
},
|
|
8218
|
-
|
|
8219
|
-
|
|
8220
|
-
send:(channelName, data, clientsRoomsTag=null, clientSocketParam=null)=>{
|
|
8221
|
-
|
|
8222
|
-
if(!clientSocketParam){
|
|
8223
|
-
|
|
8224
|
-
nodeServerInstance.serverSocket.clients.forEach((clientSocket)=>{
|
|
8225
|
-
|
|
8226
|
-
if(clientSocket.readyState!==WebSocket.OPEN) return;
|
|
8227
|
-
|
|
8228
|
-
// Room information is stored in client socket object :
|
|
8229
|
-
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
8230
|
-
|
|
8231
|
-
// Channel information is stored in exchanged data :
|
|
8232
|
-
let dataWrapped={channelName:channelName, data:data};
|
|
8233
|
-
|
|
8234
|
-
|
|
8235
|
-
dataWrapped=JSON.stringify(dataWrapped);
|
|
8236
|
-
|
|
8237
|
-
clientSocket.send(dataWrapped);
|
|
8238
|
-
|
|
8239
|
-
});
|
|
8240
|
-
|
|
8241
|
-
}else{
|
|
8242
|
-
|
|
8243
|
-
let clientSocket=clientSocketParam;
|
|
8244
|
-
if(clientSocket.readyState!==WebSocket.OPEN) return;
|
|
8245
|
-
|
|
8246
|
-
// Room information is stored in client socket object :
|
|
8247
|
-
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
8248
|
-
|
|
8249
|
-
// Channel information is stored in exchanged data :
|
|
8250
|
-
let dataWrapped={channelName:channelName, data:data};
|
|
8251
|
-
|
|
8252
|
-
|
|
8253
|
-
dataWrapped=JSON.stringify(dataWrapped);
|
|
8254
|
-
|
|
8255
|
-
clientSocket.send(dataWrapped);
|
|
8256
|
-
|
|
8257
|
-
}
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
return nodeServerInstance;
|
|
8261
|
-
},
|
|
8262
|
-
|
|
8263
|
-
|
|
8264
|
-
onConnectionToClient:(doOnConnection)=>{
|
|
8265
|
-
// «connection» is the only event fired by the serverSocket :
|
|
8266
|
-
nodeServerInstance.serverSocket.on("connection", (clientSocket)=>{
|
|
8267
|
-
|
|
8268
|
-
// DBG
|
|
8269
|
-
console.log("SERVER : ON CONNECTION !");
|
|
8270
|
-
|
|
8271
|
-
|
|
8272
|
-
// if(contains(nodeServerInstance.clientsSockets, clientSocket)) return;
|
|
8273
|
-
// nodeServerInstance.clientsSockets.push(clientSocket);
|
|
8274
|
-
|
|
8275
|
-
|
|
8276
|
-
// DBG
|
|
8277
|
-
console.log("nodeServerInstance.receptionEntryPoints.length:",nodeServerInstance.receptionEntryPoints.length);
|
|
8278
|
-
|
|
8279
|
-
|
|
8280
|
-
|
|
8281
|
-
// We execute the events registration listeners entry points:
|
|
8282
|
-
foreach(nodeServerInstance.receptionEntryPoints,(receptionEntryPoint)=>{
|
|
8283
|
-
|
|
8284
|
-
// DBG
|
|
8285
|
-
console.log("ENTRY POINT !");
|
|
8286
|
-
|
|
8287
|
-
receptionEntryPoint.execute(clientSocket);
|
|
8288
|
-
});
|
|
8289
|
-
|
|
8290
|
-
|
|
8291
|
-
doOnConnection(nodeServerInstance, clientSocket);
|
|
8292
|
-
|
|
8293
|
-
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
// To make the server aware of the clients connections states :
|
|
8299
|
-
clientSocket.stateCheckInterval=setInterval(()=>{
|
|
8300
|
-
if (clientSocket.isAlive===false){
|
|
8301
|
-
clearInterval(clientSocket.stateCheckInterval);
|
|
8302
|
-
|
|
8303
|
-
return clientSocket.terminate();
|
|
8304
|
-
}
|
|
8305
|
-
clientSocket.isAlive=false;
|
|
8306
|
-
try{
|
|
8307
|
-
clientSocket.ping();
|
|
8308
|
-
}catch(error){
|
|
8309
|
-
lognow("ERROR : A problem occurred when tried to ping client socket : ",error);
|
|
8310
|
-
// We effectively end the client connection to this server :
|
|
8311
|
-
clientSocket.terminate();
|
|
8312
|
-
}
|
|
8313
|
-
}, nodeServerInstance.clientTimeoutMillis);
|
|
8314
|
-
clientSocket.on("pong",()=>{
|
|
8315
|
-
clientSocket.isAlive=true;
|
|
8316
|
-
});
|
|
8317
|
-
|
|
8318
|
-
|
|
8319
|
-
|
|
8320
|
-
});
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
return nodeServerInstance;
|
|
8324
|
-
},
|
|
8325
|
-
|
|
8326
|
-
onFinalize:(doOnFinalizeServer)=>{
|
|
8327
|
-
|
|
8328
|
-
doOnFinalizeServer(nodeServerInstance);
|
|
8329
|
-
|
|
8330
|
-
// TRACE
|
|
8331
|
-
console.log("INFO : SERVER : Node server setup complete.");
|
|
8332
|
-
|
|
8333
|
-
return nodeServerInstance;
|
|
8334
|
-
},
|
|
8335
|
-
|
|
8336
|
-
addToRoom:(clientSocket,clientRoomTag)=>{
|
|
8337
|
-
clientSocket.clientRoomTag=clientRoomTag;
|
|
8338
|
-
},
|
|
8339
|
-
|
|
8340
|
-
|
|
8341
|
-
|
|
8342
|
-
};
|
|
8343
|
-
|
|
8344
|
-
// Join room server part protocol :
|
|
8345
|
-
nodeServerInstance.receive("protocol",(message, clientSocket)=>{
|
|
8346
|
-
|
|
8347
|
-
if(message.type!=="joinRoom" || !clientSocket) return;
|
|
8348
|
-
|
|
8349
|
-
nodeServerInstance.addToRoom(clientSocket, message.clientRoomTag);
|
|
8350
|
-
|
|
8351
|
-
});
|
|
8352
|
-
|
|
8353
|
-
|
|
8354
|
-
|
|
8355
|
-
// To make the server aware of the clients connections states :
|
|
8356
|
-
nodeServerInstance.serverSocket.on("close", function close() {
|
|
8357
|
-
nodeServerInstance.serverSocket.clients.forEach((clientSocket)=>{
|
|
8358
|
-
clearInterval(clientSocket.stateCheckInterval);
|
|
8359
|
-
});
|
|
8360
|
-
});
|
|
8361
|
-
|
|
8362
|
-
|
|
8363
|
-
|
|
8364
|
-
return nodeServerInstance;
|
|
8365
|
-
},
|
|
8366
|
-
|
|
8367
|
-
|
|
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
|
-
|
|
8377
|
-
|
|
8378
|
-
|
|
8379
|
-
|
|
8380
|
-
// NODE CLIENT
|
|
8381
|
-
/*private*/connectToServerFromNode:(serverURL, port, timeout)=>{
|
|
8382
|
-
|
|
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
|
-
|
|
8402
|
-
// NEW : ws :
|
|
8403
|
-
if(typeof(WebSocket)==="undefined"){
|
|
8404
|
-
// TRACE
|
|
8405
|
-
lognow("ERROR : CLIENT : Could not find websocket client lib, aborting client connection.");
|
|
8406
|
-
return null;
|
|
8407
|
-
}
|
|
8408
|
-
|
|
8409
|
-
const clientSocket=new WebSocket(serverURL+":"+port,/*WORKAROUND:*/{ rejectUnauthorized:false });
|
|
8410
|
-
|
|
8411
|
-
|
|
8412
|
-
const nodeClientInstance={
|
|
8413
|
-
clientSocket:clientSocket,
|
|
8414
|
-
|
|
8415
|
-
receptionEntryPoints:[],
|
|
8416
|
-
|
|
8417
|
-
|
|
8418
|
-
receive:(channelName, doOnIncomingMessage, clientsRoomsTag=null)=>{
|
|
8419
|
-
|
|
8420
|
-
|
|
8421
|
-
const receptionEntryPoint={
|
|
8422
|
-
channelName:channelName,
|
|
8423
|
-
clientsRoomsTag:clientsRoomsTag,
|
|
8424
|
-
execute:(clientSocketParam)=>{
|
|
8425
|
-
|
|
8426
|
-
|
|
8427
|
-
nodeClientInstance.clientSocket.on("message", (dataWrapped)=>{
|
|
8428
|
-
|
|
8429
|
-
dataWrapped=JSON.parse(dataWrapped);
|
|
8430
|
-
|
|
8431
|
-
|
|
8432
|
-
// Channel information is stored in exchanged data :
|
|
8433
|
-
if(dataWrapped.channelName!==channelName) return;
|
|
8434
|
-
|
|
8435
|
-
let clientSocket=nodeClientInstance.clientSocket;
|
|
8436
|
-
|
|
8437
|
-
// Room information is stored in client socket object :
|
|
8438
|
-
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
8439
|
-
|
|
8440
|
-
doOnIncomingMessage(dataWrapped.data, clientSocket);
|
|
8441
|
-
|
|
8442
|
-
|
|
8443
|
-
});
|
|
8444
|
-
|
|
8445
|
-
}
|
|
8446
|
-
};
|
|
8447
|
-
|
|
8448
|
-
|
|
8449
|
-
nodeClientInstance.receptionEntryPoints.push(receptionEntryPoint);
|
|
8450
|
-
|
|
8451
|
-
|
|
8452
|
-
return nodeClientInstance;
|
|
8453
|
-
},
|
|
8454
|
-
|
|
8455
|
-
|
|
8456
|
-
send:(channelName, data, clientsRoomsTag=null)=>{
|
|
8457
|
-
|
|
8458
|
-
let clientSocket=nodeClientInstance.clientSocket;
|
|
8459
|
-
|
|
8460
|
-
|
|
8461
|
-
// // DBG
|
|
8462
|
-
// console.log("(NODE CLIENT) TRYING TO SEND : clientSocket.readyState:",clientSocket.readyState);
|
|
8463
|
-
|
|
8464
|
-
|
|
8465
|
-
if(clientSocket.readyState!==WebSocket.OPEN) return;
|
|
8466
|
-
|
|
8467
|
-
// Room information is stored in client socket object :
|
|
8468
|
-
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
8469
|
-
|
|
8470
|
-
// Channel information is stored in exchanged data :
|
|
8471
|
-
let dataWrapped={channelName:channelName, data:data};
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
// // DBG
|
|
8475
|
-
// console.log("(NODE CLIENT) SENDING DATA ! dataWrapped:",dataWrapped);
|
|
8476
|
-
|
|
8477
|
-
|
|
8478
|
-
dataWrapped=JSON.stringify(dataWrapped);
|
|
8479
|
-
|
|
8480
|
-
|
|
8481
|
-
// // DBG
|
|
8482
|
-
// console.log("(NODE CLIENT) SENDING DATA ! channelName:«"+channelName+"» ; clientsRoomsTag:«"+clientsRoomsTag+"»");
|
|
8483
|
-
// console.log("(NODE CLIENT) SENDING DATA ! dataWrapped:",dataWrapped);
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
clientSocket.send(dataWrapped);
|
|
8487
|
-
|
|
8488
|
-
return nodeClientInstance;
|
|
8489
|
-
},
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
join:(clientRoomTag)=>{
|
|
8493
|
-
// Join room client part protocol :
|
|
8494
|
-
const message={type:"joinRoom",clientRoomTag:clientRoomTag};
|
|
8495
|
-
nodeClientInstance.send("protocol",message);
|
|
8496
|
-
|
|
8497
|
-
},
|
|
8498
|
-
|
|
8499
|
-
|
|
8500
|
-
onConnectionToServer:(doOnConnection)=>{
|
|
8501
|
-
|
|
8502
|
-
nodeClientInstance.clientSocket.on("open", ()=>{
|
|
8503
|
-
|
|
8504
|
-
let clientSocket=nodeClientInstance.clientSocket;
|
|
8505
|
-
|
|
8506
|
-
doOnConnection(nodeClientInstance, clientSocket);
|
|
8507
|
-
|
|
8508
|
-
// We execute the listeners entry points:
|
|
8509
|
-
foreach(nodeClientInstance.receptionEntryPoints,(receptionEntryPoint)=>{
|
|
8510
|
-
receptionEntryPoint.execute(clientSocket);
|
|
8511
|
-
});
|
|
8512
|
-
|
|
8513
|
-
});
|
|
8514
|
-
},
|
|
8515
|
-
|
|
8516
|
-
|
|
8517
|
-
};
|
|
8518
|
-
|
|
8519
|
-
|
|
8520
|
-
return nodeClientInstance;
|
|
8521
|
-
},
|
|
8522
|
-
|
|
8523
|
-
|
|
8524
|
-
|
|
8525
|
-
// BROWSER CLIENT
|
|
8526
|
-
|
|
8527
|
-
/*private*/connectToServerFromBrowser:(serverURL, port, timeout)=>{
|
|
8528
|
-
|
|
8529
|
-
// NEW : ws :
|
|
8530
|
-
if(typeof(WebSocket)==="undefined"){
|
|
8531
|
-
// TRACE
|
|
8532
|
-
lognow("ERROR : CLIENT : Could not find websocket client lib, aborting client connection.");
|
|
8533
|
-
return null;
|
|
8534
|
-
}
|
|
8535
|
-
|
|
8536
|
-
const clientSocket=new WebSocket(serverURL+":"+port,["wss"]);
|
|
8537
|
-
|
|
8538
|
-
|
|
8539
|
-
const browserInstance={
|
|
8540
|
-
clientSocket:clientSocket,
|
|
8541
|
-
|
|
8542
|
-
|
|
8543
|
-
receive:(channelName, doOnIncomingMessage, clientsRoomsTag=null)=>{
|
|
8544
|
-
|
|
8545
|
-
browserInstance.clientSocket.addEventListener("message", (event)=>{
|
|
8546
|
-
|
|
8547
|
-
let dataWrapped=JSON.parse(event.data);
|
|
8548
|
-
|
|
8549
|
-
|
|
8550
|
-
// Channel information is stored in exchanged data :
|
|
8551
|
-
if(dataWrapped.channelName!==channelName) return;
|
|
8552
|
-
|
|
8553
|
-
let clientSocket=browserInstance.clientSocket;
|
|
8554
|
-
|
|
8555
|
-
// Room information is stored in client socket object :
|
|
8556
|
-
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
8557
|
-
|
|
8558
|
-
doOnIncomingMessage(dataWrapped.data, clientSocket);
|
|
8559
|
-
|
|
8560
|
-
});
|
|
8561
|
-
|
|
8562
|
-
return browserInstance;
|
|
8563
|
-
},
|
|
8564
|
-
|
|
8565
|
-
|
|
8566
|
-
send:(channelName, data, clientsRoomsTag=null)=>{
|
|
8567
|
-
|
|
8568
|
-
let clientSocket=browserInstance.clientSocket;
|
|
8569
|
-
|
|
8570
|
-
|
|
8571
|
-
// // DBG
|
|
8572
|
-
// console.log("(BROWSER) TRYING TO SEND : clientSocket.readyState:",clientSocket.readyState);
|
|
8573
|
-
|
|
8574
|
-
|
|
8575
|
-
if(clientSocket.readyState!==WebSocket.OPEN) return;
|
|
8576
|
-
|
|
8577
|
-
// Room information is stored in client socket object :
|
|
8578
|
-
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
8579
|
-
|
|
8580
|
-
// Channel information is stored in exchanged data :
|
|
8581
|
-
let dataWrapped={channelName:channelName, data:data};
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
// // DBG
|
|
8585
|
-
// console.log("(BROWSER) SENDING... : dataWrapped :",dataWrapped);
|
|
8586
|
-
|
|
8587
|
-
|
|
8588
|
-
dataWrapped=JSON.stringify(dataWrapped);
|
|
8589
|
-
|
|
8590
|
-
clientSocket.send(dataWrapped);
|
|
8591
|
-
|
|
8592
|
-
return browserInstance;
|
|
8593
|
-
},
|
|
8594
|
-
|
|
8595
|
-
|
|
8596
|
-
join:(clientRoomTag)=>{
|
|
8597
|
-
// Join room client part protocol :
|
|
8598
|
-
const message={type:"joinRoom",clientRoomTag:clientRoomTag};
|
|
8599
|
-
browserInstance.send("protocol",message);
|
|
8600
|
-
},
|
|
8601
|
-
|
|
8602
|
-
|
|
8603
|
-
onConnectionToServer:(doOnConnection)=>{
|
|
8604
|
-
browserInstance.clientSocket.addEventListener("open",doOnConnection);
|
|
8605
|
-
},
|
|
8606
|
-
|
|
8607
|
-
|
|
8608
|
-
};
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
return browserInstance;
|
|
8612
|
-
},
|
|
8613
|
-
|
|
8614
|
-
};
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /*OPTIONAL*/sslOptions=null){
|
|
8619
|
-
|
|
8620
|
-
const EXCLUDED_FILENAMES_PARTS=[".saltkey."];
|
|
8621
|
-
|
|
8622
|
-
|
|
8623
|
-
if(typeof(https)==="undefined"){
|
|
8624
|
-
// TRACE
|
|
8625
|
-
console.log("«https» SERVER library not called yet, calling it now.");
|
|
8626
|
-
https=require("https");
|
|
8627
|
-
}
|
|
8628
|
-
if(typeof(http)==="undefined"){
|
|
8629
|
-
// TRACE
|
|
8630
|
-
console.log("«http» SERVER library not called yet, calling it now.");
|
|
8631
|
-
http=require("http");
|
|
8632
|
-
}
|
|
8633
|
-
|
|
8634
|
-
|
|
8635
|
-
const handler=function(request, response){
|
|
8636
|
-
|
|
8637
|
-
const url=request.url;
|
|
8638
|
-
|
|
8639
|
-
let isURLInExclusionZone=!!foreach(EXCLUDED_FILENAMES_PARTS,(excludedStr)=>{
|
|
8640
|
-
if(contains(url,excludedStr)){
|
|
8641
|
-
return true;
|
|
8642
|
-
}
|
|
8643
|
-
});
|
|
8644
|
-
|
|
8645
|
-
if(isURLInExclusionZone){
|
|
8646
|
-
// TRACE
|
|
8647
|
-
console.log("ERROR 403 forbidden access error :");
|
|
8648
|
-
console.log(error);
|
|
8649
|
-
|
|
8650
|
-
response.writeHead(403);
|
|
8651
|
-
response.end("Sorry, cannot access resource : error: "+error.code+" ..\n");
|
|
8652
|
-
response.end();
|
|
8653
|
-
return;
|
|
8654
|
-
}
|
|
8655
|
-
|
|
8656
|
-
|
|
8657
|
-
const urlFile="." + url;
|
|
8658
|
-
|
|
8659
|
-
let filePath=urlFile.indexOf("?")!==-1?urlFile.split("?")[0]:urlFile;
|
|
8660
|
-
if(filePath == "./") filePath="./index.html";
|
|
8661
|
-
|
|
8662
|
-
let extname=path.extname(filePath);
|
|
8663
|
-
let contentType="text/html";
|
|
8664
|
-
|
|
8665
|
-
|
|
8666
|
-
fs.readFile(filePath, function(error, fileContent){
|
|
8667
|
-
if(error){
|
|
8668
|
-
if(error.code == "ENOENT"){
|
|
8669
|
-
// TRACE
|
|
8670
|
-
console.log("ERROR 404 file not found :"+filePath);
|
|
8671
|
-
|
|
8672
|
-
fs.readFile("./404.html", function(error, fileContent){
|
|
8673
|
-
response.writeHead(200, { "Content-Type": contentType });
|
|
8674
|
-
response.end(fileContent, "utf-8");
|
|
8675
|
-
});
|
|
8676
|
-
|
|
8677
|
-
}else {
|
|
8678
|
-
|
|
8679
|
-
// TRACE
|
|
8680
|
-
console.log("ERROR 500 server error :");
|
|
8681
|
-
console.log(error);
|
|
8682
|
-
|
|
8683
|
-
response.writeHead(500);
|
|
8684
|
-
response.end("Sorry, check with the site admin for error: "+error.code+" ..\n");
|
|
8685
|
-
response.end();
|
|
8686
|
-
|
|
8687
|
-
}
|
|
8688
|
-
}else {
|
|
8689
|
-
|
|
8690
|
-
// TRACE
|
|
8691
|
-
console.log("INFO 200 OK :"+filePath);
|
|
8692
|
-
|
|
8693
|
-
response.writeHead(200, { "Content-Type": contentType });
|
|
8694
|
-
response.end(fileContent, "utf-8");
|
|
8695
|
-
|
|
8696
|
-
// res.writeHead(200);
|
|
8697
|
-
// res.end("hello world\n");
|
|
8698
|
-
// res.sendFile(__dirname + "/public/index.html");
|
|
8699
|
-
|
|
8700
|
-
}
|
|
8701
|
-
});
|
|
8702
|
-
|
|
8703
|
-
};
|
|
8704
|
-
|
|
8705
|
-
|
|
8706
|
-
let listenableServer;
|
|
8707
|
-
if(sslOptions){
|
|
8708
|
-
let httpsServer=https.createServer(sslOptions, handler).listen(port);
|
|
8709
|
-
// TRACE
|
|
8710
|
-
console.log("INFO : SERVER : HTTPS Server launched and listening on port " + port + "!");
|
|
8711
|
-
listenableServer=httpsServer;
|
|
8712
|
-
}else{
|
|
8713
|
-
let httpServer=http.createServer(handler).listen(port);
|
|
8714
|
-
// TRACE
|
|
8715
|
-
console.log("INFO : SERVER : HTTP Server launched and listening on port " + port + "!");
|
|
8716
|
-
listenableServer=httpServer;
|
|
8183
|
+
hasXAxisWholeZone2=true;
|
|
8184
|
+
}
|
|
8717
8185
|
}
|
|
8718
8186
|
|
|
8719
|
-
|
|
8720
|
-
|
|
8721
|
-
|
|
8722
|
-
// When a client connects, we execute the callback :
|
|
8723
|
-
// CAUTION : MUST BE CALLED ONLY ONCE !
|
|
8724
|
-
server.onConnectionToClient((serverParam, clientSocketParam)=>{
|
|
8725
|
-
if(doOnConnect) doOnConnect(serverParam, clientSocketParam);
|
|
8726
|
-
});
|
|
8727
|
-
|
|
8728
|
-
|
|
8729
|
-
server.onFinalize((serverParam)=>{
|
|
8730
|
-
if(doOnFinalizeServer) doOnFinalizeServer(serverParam);
|
|
8731
|
-
});
|
|
8732
|
-
|
|
8733
|
-
|
|
8734
|
-
// TRACE
|
|
8735
|
-
console.log("INFO : SERVER : Generic Nodejs server launched and listening on port " + port + "!");
|
|
8187
|
+
let w=intersectionXMax-intersectionXMin;
|
|
8188
|
+
let x=(intersectionXMax+intersectionXMin)*.5; // (The average)
|
|
8736
8189
|
|
|
8737
8190
|
|
|
8738
|
-
|
|
8739
|
-
}
|
|
8191
|
+
// Vertical axis :
|
|
8740
8192
|
|
|
8193
|
+
let hasYAxisWholeZone1=false;
|
|
8194
|
+
let hasYAxisWholeZone2=false;
|
|
8741
8195
|
|
|
8742
|
-
|
|
8196
|
+
let demiHeight1=zone1.h*.5;
|
|
8197
|
+
let demiHeight2=zone2.h*.5;
|
|
8743
8198
|
|
|
8744
|
-
|
|
8745
|
-
|
|
8746
|
-
|
|
8747
|
-
|
|
8748
|
-
// EXAMPLE : node orita-srv.js hash orita.global@saltkey 1234567890
|
|
8749
|
-
console.log("Server launched.");
|
|
8750
|
-
|
|
8751
|
-
// We read the command-line arguments if needed :
|
|
8752
|
-
|
|
8753
|
-
let argCLPort;
|
|
8754
|
-
let argCLCertPath;
|
|
8755
|
-
let argCLKeyPath;
|
|
8756
|
-
let isForceUnsecure;
|
|
8757
|
-
|
|
8758
|
-
let isHashAsked=false;
|
|
8759
|
-
let clearTextParam=null;
|
|
8760
|
-
let persisterId=null;
|
|
8761
|
-
process.argv.forEach(function (val, i){
|
|
8762
|
-
if(!val) return;
|
|
8763
|
-
// 0 corresponds to «node / nodejs»
|
|
8764
|
-
if(i<=1) return; // 1 corresponds to « <server.js> »
|
|
8765
|
-
else if(i==2){
|
|
8766
|
-
if(val!=="hash") argCLPort=val;
|
|
8767
|
-
else isHashAsked=true;
|
|
8768
|
-
}else if(i==3){
|
|
8769
|
-
if(!isHashAsked) argCLCertPath=val;
|
|
8770
|
-
else persisterId=val;
|
|
8771
|
-
}else if(i==4){
|
|
8772
|
-
if(!isHashAsked) argCLKeyPath=val;
|
|
8773
|
-
else clearTextParam=val;
|
|
8774
|
-
}
|
|
8775
|
-
});
|
|
8199
|
+
let yMin1=zone1.y-demiHeight1;
|
|
8200
|
+
let yMax1=zone1.y+demiHeight1;
|
|
8201
|
+
let yMin2=zone2.y-demiHeight2;
|
|
8202
|
+
let yMax2=zone2.y+demiHeight2;
|
|
8776
8203
|
|
|
8777
|
-
isForceUnsecure=(argCLCertPath==="unsecure");
|
|
8778
|
-
|
|
8779
|
-
let aotraNodeServer={};
|
|
8780
|
-
aotraNodeServer.serverManager={ start:()=>{/*DEFAULT START FUNCTION, WILL BE OVERRIDEN LATER*/}};
|
|
8781
8204
|
|
|
8782
|
-
|
|
8783
|
-
|
|
8784
|
-
|
|
8785
|
-
|
|
8786
|
-
|
|
8787
|
-
|
|
8788
|
-
|
|
8789
|
-
|
|
8205
|
+
let deltaYMins=yMin2-yMin1;
|
|
8206
|
+
let deltaYMaxs=yMax2-yMax1;
|
|
8207
|
+
|
|
8208
|
+
let deltaYMinsSign=(deltaYMins<0?-1:1);
|
|
8209
|
+
let deltaYMaxsSign=(deltaYMaxs<0?-1:1);
|
|
8210
|
+
let intersectionYMin=0;
|
|
8211
|
+
let intersectionYMax=0;
|
|
8212
|
+
if(deltaYMinsSign==deltaYMaxsSign){ // case «parallelogram»
|
|
8213
|
+
if(deltaYMinsSign<0){ // case /=/ parallelogram
|
|
8214
|
+
intersectionYMin=yMin1;
|
|
8215
|
+
intersectionYMax=yMax2;
|
|
8216
|
+
// We calculate edges :
|
|
8217
|
+
edges.push("bottom"); // Here the Y axis is NOT inverted !
|
|
8218
|
+
}else{ // case \=\ parallelogram
|
|
8219
|
+
intersectionYMin=yMin2;
|
|
8220
|
+
intersectionYMax=yMax1;
|
|
8221
|
+
// We calculate edges :
|
|
8222
|
+
edges.push("top"); // Here the Y axis is NOT inverted !
|
|
8790
8223
|
}
|
|
8791
|
-
|
|
8792
|
-
|
|
8793
|
-
|
|
8794
|
-
|
|
8795
|
-
//
|
|
8796
|
-
|
|
8797
|
-
|
|
8798
|
-
|
|
8799
|
-
|
|
8800
|
-
|
|
8801
|
-
|
|
8224
|
+
}else{ // case «trapeze»
|
|
8225
|
+
if(deltaYMinsSign<0){ // case /=\ trapeze
|
|
8226
|
+
intersectionYMin=yMin1; // Basically zone2 y !
|
|
8227
|
+
intersectionYMax=yMax1;
|
|
8228
|
+
// We calculate edges :
|
|
8229
|
+
hasYAxisWholeZone1=true;
|
|
8230
|
+
}else{ // case \=/ trapeze
|
|
8231
|
+
intersectionYMin=yMin2; // Basically zone2 y !
|
|
8232
|
+
intersectionYMax=yMax2;
|
|
8233
|
+
// We calculate edges :
|
|
8234
|
+
hasYAxisWholeZone2=true;
|
|
8802
8235
|
}
|
|
8803
|
-
const globalSaltkey=globalSaltkeyObject.saltkey;
|
|
8804
|
-
|
|
8805
|
-
let firstHash=getHashedString(clearTextParam);
|
|
8806
|
-
|
|
8807
|
-
let generatedHash=getHashedString( firstHash + globalSaltkey, true);// (we use the heavy treatment thing.)
|
|
8808
|
-
globalSaltkeyObject.hashes.push(generatedHash);
|
|
8809
|
-
|
|
8810
|
-
// We update the repository :
|
|
8811
|
-
persister.saveDataToFileForClient(persisterClientId,persisterRepositoryName,globalSaltkeyObject,false,()=>{
|
|
8812
|
-
// TRACE
|
|
8813
|
-
console.log("INFO : Hash added to repository and saved successfully.");
|
|
8814
|
-
});
|
|
8815
|
-
|
|
8816
|
-
// OUTPUT
|
|
8817
|
-
console.log("Here is your key : share it with your clients but DO NOT LEAK IT !\n********************\n"+clearTextParam+"\n********************\n");
|
|
8818
|
-
|
|
8819
|
-
return aotraNodeServer;
|
|
8820
8236
|
}
|
|
8821
8237
|
|
|
8238
|
+
let h=intersectionYMax-intersectionYMin;
|
|
8239
|
+
let y=(intersectionYMax+intersectionYMin)*.5; // (The average)
|
|
8822
8240
|
|
|
8823
|
-
|
|
8824
|
-
|
|
8825
|
-
|
|
8826
|
-
const DEFAULT_CERT_PATH=nonull(argCLCertPath,"cert.pem");
|
|
8827
|
-
const DEFAULT_KEY_PATH=nonull(argCLKeyPath,"key.key");
|
|
8828
|
-
|
|
8829
|
-
|
|
8830
|
-
let port=portParam ? portParam : DEFAULT_PORT;
|
|
8831
|
-
let certPath=null;
|
|
8832
|
-
let keyPath=null;
|
|
8833
|
-
|
|
8834
|
-
if(!isForceUnsecure){
|
|
8835
|
-
certPath=certPathParam?certPathParam:DEFAULT_CERT_PATH;
|
|
8836
|
-
keyPath=keyPathParam?keyPathParam:DEFAULT_KEY_PATH;
|
|
8837
|
-
}
|
|
8838
|
-
|
|
8241
|
+
// We calculate edges :
|
|
8242
|
+
if(hasXAxisWholeZone1 && hasYAxisWholeZone1) edges.push("wholeZone1");
|
|
8243
|
+
if(hasXAxisWholeZone2 && hasYAxisWholeZone2) edges.push("wholeZone2");
|
|
8839
8244
|
|
|
8840
|
-
|
|
8841
|
-
|
|
8842
|
-
// UNUSEFUL :
|
|
8843
|
-
// aotraNodeServer.serverManager.mainClientsSockets=[];
|
|
8844
|
-
aotraNodeServer.serverManager.start=function(){
|
|
8245
|
+
return {x:x,y:y,w:w,h:h,edges:edges};
|
|
8246
|
+
}
|
|
8845
8247
|
|
|
8846
|
-
|
|
8847
|
-
|
|
8848
|
-
|
|
8849
|
-
if(!fs){
|
|
8850
|
-
// TRACE
|
|
8851
|
-
lognow("ERROR : «fs» node subsystem not present, cannot access files. Aborting SSL configuration of server.");
|
|
8852
|
-
}else{
|
|
8853
|
-
try{
|
|
8854
|
-
sslOptions={
|
|
8855
|
-
cert: fs.readFileSync(certPath),
|
|
8856
|
-
key: fs.readFileSync(keyPath),
|
|
8857
|
-
};
|
|
8858
|
-
}catch(exception){
|
|
8859
|
-
// TRACE
|
|
8860
|
-
lognow("ERROR : Could not open SSL files certPath:«"+certPath+"» or keyPath:«"+keyPath+"». Aborting SSL configuration of server.");
|
|
8861
|
-
}
|
|
8862
|
-
}
|
|
8863
|
-
}
|
|
8864
|
-
|
|
8865
|
-
aotraNodeServer.server=launchNodeHTTPServer(port, doOnClientConnection, doOnFinalizeServer, sslOptions);
|
|
8866
|
-
|
|
8248
|
+
/*public static*/window.areZonesIdentical=function(zone1, zone2){
|
|
8249
|
+
return (zone1==zone2 || (zone1.x==zone2.x || zone1.y==zone2.y || zone1.w==zone2.w || zone1.h==zone2.h));
|
|
8250
|
+
}
|
|
8867
8251
|
|
|
8868
|
-
return aotraNodeServer;
|
|
8869
|
-
};
|
|
8870
8252
|
|
|
8871
|
-
return aotraNodeServer;
|
|
8872
|
-
}
|
|
8873
8253
|
|
|
8254
|
+
// 3D :
|
|
8874
8255
|
|
|
8875
|
-
// -Client :
|
|
8876
8256
|
|
|
8877
|
-
|
|
8878
|
-
|
|
8879
|
-
|
|
8257
|
+
/*
|
|
8258
|
+
// (needs the jsgl library file !)
|
|
8259
|
+
// UNUSED
|
|
8260
|
+
function screenToSpherePt(mouseX, mouseY) {
|
|
8261
|
+
var p = { x: camera_mat.e12, y: camera_mat.e13, z: camera_mat.e14 + 1 };
|
|
8262
|
+
// camera dir
|
|
8263
|
+
var r = { x: camera_mat.e0, y: camera_mat.e1, z: camera_mat.e2 };
|
|
8264
|
+
var up = { x: camera_mat.e4, y: camera_mat.e5, z: camera_mat.e6 };
|
|
8265
|
+
var right = { x: camera_mat.e8, y: camera_mat.e9, z: camera_mat.e10 };
|
|
8266
|
+
var tan_half = Math.tan(horizontal_fov_radians / 2);
|
|
8267
|
+
r = jsgl.vectorAdd(r, jsgl.vectorScale(right, mouseX * tan_half));
|
|
8268
|
+
r = jsgl.vectorAdd(r, jsgl.vectorScale(up, mouseY * tan_half));
|
|
8269
|
+
r = jsgl.vectorNormalize(r);
|
|
8880
8270
|
|
|
8881
|
-
|
|
8882
|
-
|
|
8883
|
-
|
|
8884
|
-
|
|
8885
|
-
|
|
8886
|
-
let socketToServer=WebsocketImplementation.getStatic(isNode).connectToServer(url, port, timeout);
|
|
8887
|
-
if(!socketToServer){
|
|
8888
|
-
// TRACE
|
|
8889
|
-
lognow("ERROR : CLIENT : Could not get socketToServer, aborting client connection.");
|
|
8890
|
-
return null;
|
|
8891
|
-
}
|
|
8892
|
-
|
|
8893
|
-
// When client is connected, we execute the callback :
|
|
8894
|
-
// CAUTION : MUST BE CALLED ONLY ONCE !
|
|
8895
|
-
socketToServer.onConnectionToServer(()=>{
|
|
8896
|
-
if(doOnServerConnection){
|
|
8897
|
-
if(selfParam) doOnServerConnection.apply(selfParam,[socketToServer]);
|
|
8898
|
-
else doOnServerConnection(socketToServer);
|
|
8899
|
-
}
|
|
8900
|
-
});
|
|
8271
|
+
return rayVsUnitSphereClosestPoint(p, r);
|
|
8272
|
+
}
|
|
8273
|
+
*/
|
|
8901
8274
|
|
|
8902
|
-
|
|
8903
|
-
|
|
8904
|
-
|
|
8905
|
-
//
|
|
8906
|
-
//
|
|
8907
|
-
|
|
8275
|
+
/*
|
|
8276
|
+
// (needs the jsgl library file !)
|
|
8277
|
+
// UNUSED
|
|
8278
|
+
// Return the first exterior hit or closest point between the unit
|
|
8279
|
+
// sphere and the ray starting at p and going in the r direction.
|
|
8280
|
+
function rayVsUnitSphereClosestPoint(p, r) {
|
|
8281
|
+
var p_len2 = jsgl.dotProduct(p, p);
|
|
8282
|
+
if (p_len2 < 1) {
|
|
8283
|
+
// Ray is inside sphere, no exterior hit.
|
|
8284
|
+
return null;
|
|
8285
|
+
}
|
|
8908
8286
|
|
|
8287
|
+
var along_ray = -jsgl.dotProduct(p, r);
|
|
8288
|
+
if (along_ray < 0) {
|
|
8289
|
+
// Behind ray start-point.
|
|
8290
|
+
return null;
|
|
8291
|
+
}
|
|
8909
8292
|
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
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
|
-
|
|
8923
|
-
}
|
|
8293
|
+
var perp = jsgl.vectorAdd(p, jsgl.vectorScale(r, along_ray));
|
|
8294
|
+
var perp_len2 = jsgl.dotProduct(perp, perp);
|
|
8295
|
+
if (perp_len2 >= 0.999999) {
|
|
8296
|
+
// Return the closest point.
|
|
8297
|
+
return jsgl.vectorNormalize(perp);
|
|
8298
|
+
}
|
|
8924
8299
|
|
|
8925
|
-
|
|
8926
|
-
|
|
8927
|
-
|
|
8928
|
-
|
|
8929
|
-
};
|
|
8930
|
-
|
|
8931
|
-
|
|
8932
|
-
return aotraClient;
|
|
8300
|
+
// Compute intersection.
|
|
8301
|
+
var e = Math.sqrt(1 - jsgl.dotProduct(perp, perp));
|
|
8302
|
+
var hit = jsgl.vectorAdd(p, jsgl.vectorScale(r, (along_ray - e)));
|
|
8303
|
+
return jsgl.vectorNormalize(hit);
|
|
8933
8304
|
}
|
|
8305
|
+
*/
|
|
8934
8306
|
|
|
8935
8307
|
|
|
8936
8308
|
|
|
8937
8309
|
|
|
8938
|
-
// ===================================================================================================
|
|
8939
|
-
|
|
8940
|
-
/*FUSRODA server stands from FSRD SERVER, for Fucking Simple Remote Desktop SERVER*/
|
|
8941
|
-
createFusrodaServer=function(certPathParam=null,keyPathParam=null,portParam=6001){
|
|
8942
|
-
|
|
8943
|
-
// https://www.npmjs.com/package/screenshot-desktop
|
|
8944
|
-
// https://github.com/octalmage/robotjs
|
|
8945
|
-
// npm install --save screenshot-desktop robotjs
|
|
8946
|
-
|
|
8947
|
-
const screenshot=require("screenshot-desktop");
|
|
8948
8310
|
|
|
8949
|
-
const REFRESH_SCREENSHOTS_MILLIS=500;
|
|
8950
8311
|
|
|
8951
|
-
|
|
8952
|
-
const server=initNodeServer(
|
|
8953
|
-
// On client connection :
|
|
8954
|
-
// (serverParam, clientSocketParam)=>{},
|
|
8955
|
-
null,
|
|
8956
|
-
// On server finalization :
|
|
8957
|
-
(serverParam)=>{
|
|
8958
8312
|
|
|
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) => {
|
|
8970
|
-
|
|
8971
|
-
const data={image:img};
|
|
8972
|
-
serverParam.send("data_fusroda", data, "clients");
|
|
8973
|
-
|
|
8974
|
-
serverParam.isScreenshotStarted=false;
|
|
8975
8313
|
|
|
8976
|
-
}).catch((error) => {
|
|
8977
|
-
// TRACE
|
|
8978
|
-
lognow("ERROR : Error during screenshot :",error);
|
|
8979
|
-
});
|
|
8980
|
-
|
|
8981
|
-
},REFRESH_SCREENSHOTS_MILLIS);
|
|
8982
|
-
|
|
8983
|
-
|
|
8984
|
-
},portParam,certPathParam,keyPathParam);
|
|
8985
8314
|
|
|
8986
8315
|
|
|
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
8316
|
|
|
8997
|
-
return server;
|
|
8998
|
-
}
|
|
8999
8317
|
|
|
9000
8318
|
|
|
9001
8319
|
|
|
9002
|
-
createFusrodaClient=function(doOnDataReception, urlParam=null,portParam=6001){
|
|
9003
|
-
|
|
9004
|
-
const isNode=false;
|
|
9005
|
-
const client=WebsocketImplementation.getStatic(isNode).connectToServer(urlParam, portParam);
|
|
9006
|
-
client.onConnectionToServer((nodeClientInstance)=>{
|
|
9007
|
-
|
|
9008
|
-
nodeClientInstance.send("protocol_fusroda");
|
|
9009
8320
|
|
|
9010
|
-
nodeClientInstance.receive("data_fusroda", doOnDataReception, "clients");
|
|
9011
|
-
|
|
9012
|
-
});
|
|
9013
|
-
|
|
9014
|
-
return client;
|
|
9015
|
-
}
|
|
9016
8321
|
|
|
9017
8322
|
|
|
9018
8323
|
|
|
9019
8324
|
|
|
9020
8325
|
|
|
9021
8326
|
|
|
8327
|
+
// MUST REMAIN AT THE END OF THIS LIBRARY FILE !
|
|
9022
8328
|
|
|
9023
|
-
|
|
8329
|
+
AOTRAUTILS_GEOMETRY_LIB_IS_LOADED=true;
|
|
8330
|
+
/*utils COMMONS library associated with aotra version : «1.0.0.000 (11/07/2022-20:58:07)»*/
|
|
9024
8331
|
/*-----------------------------------------------------------------------------*/
|
|
9025
8332
|
|
|
9026
8333
|
|
|
@@ -9032,7 +8339,7 @@ createFusrodaClient=function(doOnDataReception, urlParam=null,portParam=6001){
|
|
|
9032
8339
|
*
|
|
9033
8340
|
* # Library name : «aotrautils»
|
|
9034
8341
|
* # Library license : HGPL(Help Burma) (see aotra README information for details : https://alqemia.com/aotra.js )
|
|
9035
|
-
* # Author name : Jérémie Ratomposon (massively helped by
|
|
8342
|
+
* # Author name : Jérémie Ratomposon (massively helped by his native country free education system)
|
|
9036
8343
|
* # Author email : info@alqemia.com
|
|
9037
8344
|
* # Organization name : Alqemia
|
|
9038
8345
|
* # Organization email : admin@alqemia.com
|
|
@@ -10884,9 +10191,9 @@ window.LZWString={
|
|
|
10884
10191
|
|
|
10885
10192
|
readBits : function(numBits, data){
|
|
10886
10193
|
var res=0;
|
|
10887
|
-
var
|
|
10194
|
+
var maxPower=Math.pow(2,numBits);
|
|
10888
10195
|
var power=1;
|
|
10889
|
-
while (power!=
|
|
10196
|
+
while (power!=maxPower){
|
|
10890
10197
|
res |= this.readBit(data) * power;
|
|
10891
10198
|
power <<= 1;
|
|
10892
10199
|
}
|
|
@@ -11230,6 +10537,31 @@ window.copy=function(array){
|
|
|
11230
10537
|
return result;
|
|
11231
10538
|
}
|
|
11232
10539
|
|
|
10540
|
+
|
|
10541
|
+
|
|
10542
|
+
window.removeByIndex=function(array, elementIndex){
|
|
10543
|
+
if(array instanceof Array){
|
|
10544
|
+
if(empty(array)) return false;
|
|
10545
|
+
if(elementIndex<0) return false;
|
|
10546
|
+
array.splice(elementIndex, 1);
|
|
10547
|
+
return true;
|
|
10548
|
+
}
|
|
10549
|
+
let hasDeleted=false;
|
|
10550
|
+
// Case of regular objects that are like associative arrays :
|
|
10551
|
+
let i=0;
|
|
10552
|
+
for (key in array){
|
|
10553
|
+
if(!array.hasOwnProperty(key)) continue;
|
|
10554
|
+
if(i===elementIndex){
|
|
10555
|
+
delete array[key];
|
|
10556
|
+
hasDeleted=true;
|
|
10557
|
+
break;
|
|
10558
|
+
}
|
|
10559
|
+
i++;
|
|
10560
|
+
}
|
|
10561
|
+
return hasDeleted;
|
|
10562
|
+
}
|
|
10563
|
+
|
|
10564
|
+
|
|
11233
10565
|
window.remove=function(array, element){
|
|
11234
10566
|
if(array instanceof Array){
|
|
11235
10567
|
if(empty(array)) return false;
|
|
@@ -11335,15 +10667,41 @@ Math.minInArray=function(arrayOfValues){
|
|
|
11335
10667
|
return min;
|
|
11336
10668
|
};
|
|
11337
10669
|
|
|
10670
|
+
|
|
10671
|
+
|
|
11338
10672
|
// TODO : FIXME : Create a namespace MathUtils. and ArraysUtils. and sort out the corresponding static methods.
|
|
11339
|
-
Math.
|
|
11340
|
-
|
|
11341
|
-
|
|
11342
|
-
|
|
10673
|
+
Math.getRandomsInArray=function(arrayOfValues, numberOfItemsToGet=1){
|
|
10674
|
+
|
|
10675
|
+
if(numberOfItemsToGet<0 || empty(arrayOfValues)) return [];
|
|
10676
|
+
const size=getArraySize(arrayOfValues);
|
|
10677
|
+
// We skip a costly random calculation if we have only one element in array :
|
|
10678
|
+
if(size===1) return [valueAt(arrayOfValues,0)];
|
|
10679
|
+
|
|
10680
|
+
const indexesPool=[];
|
|
10681
|
+
for(let i=0;i<size;i++) indexesPool.push(i);
|
|
10682
|
+
|
|
10683
|
+
const results=[];
|
|
10684
|
+
for(let i=0;i<numberOfItemsToGet;i++){
|
|
10685
|
+
if(empty(indexesPool)) break;
|
|
10686
|
+
const chosenIndex=Math.getRandomInArray(indexesPool,true);
|
|
10687
|
+
const item=valueAt(arrayOfValues,chosenIndex);
|
|
10688
|
+
results.push(item);
|
|
10689
|
+
}
|
|
10690
|
+
|
|
10691
|
+
return results;
|
|
10692
|
+
}
|
|
10693
|
+
|
|
10694
|
+
// TODO : FIXME : Create a namespace MathUtils. and ArraysUtils. and sort out the corresponding static methods.
|
|
10695
|
+
Math.getRandomInArray=function(arrayOfValues,removeFromArray=false){
|
|
10696
|
+
if(empty(arrayOfValues)) return null;
|
|
10697
|
+
const size=getArraySize(arrayOfValues);
|
|
11343
10698
|
// We skip a costly random calculation if we have only one element in array :
|
|
11344
10699
|
if(size===1) return valueAt(arrayOfValues,0);
|
|
11345
|
-
|
|
11346
|
-
|
|
10700
|
+
|
|
10701
|
+
const chosenIndex=Math.getRandomInt(size-1,0);
|
|
10702
|
+
const result=valueAt(arrayOfValues, chosenIndex);
|
|
10703
|
+
if(removeFromArray) removeByIndex(arrayOfValues, chosenIndex);
|
|
10704
|
+
return result;
|
|
11347
10705
|
}
|
|
11348
10706
|
|
|
11349
10707
|
// This function is for arrays, like Array or Objects used as associative arrays you want to test as holding elements :
|
|
@@ -11956,6 +11314,52 @@ Math.demiCurve=function(min, max, value, strategy="triangle", precision=2){
|
|
|
11956
11314
|
return Math.roundTo(cursorFactor,precision);
|
|
11957
11315
|
}
|
|
11958
11316
|
|
|
11317
|
+
// Not the same as curve, where min and max are the minimum and maximum values taken by returned value :
|
|
11318
|
+
Math.curveTriggered=function(minTriggerFactor, maxTriggerFactor, valueFactorParam, strategy="linear"){
|
|
11319
|
+
const valueFactor=Math.coerceInRange(valueFactorParam,0,1);
|
|
11320
|
+
let result=1;
|
|
11321
|
+
if(valueFactor<minTriggerFactor){
|
|
11322
|
+
// Ascending slope :
|
|
11323
|
+
if(minTriggerFactor==0) return 1;
|
|
11324
|
+
result=valueFactor/minTriggerFactor;
|
|
11325
|
+
}else{
|
|
11326
|
+
if(maxTriggerFactor==0) return 1;
|
|
11327
|
+
if(maxTriggerFactor<valueFactor){
|
|
11328
|
+
// Descending slope :
|
|
11329
|
+
result=1-((valueFactor-maxTriggerFactor)/(1-maxTriggerFactor));
|
|
11330
|
+
}else{
|
|
11331
|
+
// Plateau :
|
|
11332
|
+
return 1;
|
|
11333
|
+
}
|
|
11334
|
+
}
|
|
11335
|
+
return result;
|
|
11336
|
+
}
|
|
11337
|
+
|
|
11338
|
+
Math.slopeTriggered=function(minTriggerFactor, maxTriggerFactor, valueFactorParam, direction="ascending", strategy="linear"){
|
|
11339
|
+
|
|
11340
|
+
const valueFactor=Math.coerceInRange(valueFactorParam,0,1);
|
|
11341
|
+
|
|
11342
|
+
if(valueFactor<minTriggerFactor){
|
|
11343
|
+
// Beginning plateau :
|
|
11344
|
+
if(direction=="ascending") return 0;
|
|
11345
|
+
else return 1;
|
|
11346
|
+
}else if(maxTriggerFactor<valueFactor){
|
|
11347
|
+
// Ending plateau :
|
|
11348
|
+
if(direction=="ascending") return 1;
|
|
11349
|
+
else return 0;
|
|
11350
|
+
}
|
|
11351
|
+
|
|
11352
|
+
// Intermediate slope :
|
|
11353
|
+
const total=(maxTriggerFactor-minTriggerFactor);
|
|
11354
|
+
if(total==0) return ((direction=="ascending")?1:0);
|
|
11355
|
+
|
|
11356
|
+
const factor=((valueFactor-minTriggerFactor)/total);
|
|
11357
|
+
|
|
11358
|
+
return ((direction=="ascending")?factor:(1-factor));
|
|
11359
|
+
}
|
|
11360
|
+
|
|
11361
|
+
|
|
11362
|
+
|
|
11959
11363
|
Math.curve=function(min, max, value, strategy="triangle",
|
|
11960
11364
|
// It is actually where to place the «summit» of the curve, before returning to 0,
|
|
11961
11365
|
// the factor value where the values starts decreasing, after having increased all along, and then goes back to 0:
|