aotrautils 0.0.45 → 0.0.48
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 +6 -1309
- aotrautils/package.json +2 -2
aotrautils/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
Aotrautils library client
|
|
1
|
+
Aotrautils library client-side
|
|
2
2
|
=========
|
|
3
3
|
|
|
4
|
-
The aotrautils library to be used in plain javascript or nodejs projects. Provides
|
|
5
|
-
|
|
4
|
+
The aotrautils library to be used in plain javascript or nodejs projects. Provides useful functions like foreach, the aotest automated testing framework, and the flat map to json and json to flap map converter utils.
|
|
5
|
+
Plus some useful client-side exclusive functions and methods.
|
|
6
6
|
|
|
7
7
|
#Project license text : see doc/HGPL_LICENSE.txt file
|
|
8
8
|
|
|
9
|
-
# Author name : Jérémie Ratomposon (massively helped by
|
|
9
|
+
# Author name : Jérémie Ratomposon (massively helped by his native country free education system)
|
|
10
10
|
# Author email : info@alqemia.com
|
|
11
11
|
# Organization name : Alqemia
|
|
12
12
|
# Organization email : info@alqemia.com
|
aotrautils/aotrautils.build.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
/*utils CLIENT library associated with aotra version : «1.0.0.000 (11/07/2022-
|
|
3
|
+
/*utils CLIENT library associated with aotra version : «1.0.0.000 (11/07/2022-21:05:49)»*/
|
|
4
4
|
/*-----------------------------------------------------------------------------*/
|
|
5
5
|
/* ## Utility global methods in a browser (htmljs) client environment.
|
|
6
6
|
*
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* # Library name : «aotrautils»
|
|
12
12
|
* # Library license : HGPL(Help Burma) (see aotra README information for details : https://alqemia.com/aotra.js )
|
|
13
|
-
* # 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)
|
|
14
14
|
* # Author email : info@alqemia.com
|
|
15
15
|
* # Organization name : Alqemia
|
|
16
16
|
* # Organization email : admin@alqemia.com
|
|
@@ -7121,7 +7121,7 @@ createFusrodaClient=function(doOnClientReady, doOnDataReception, urlParam=null,
|
|
|
7121
7121
|
|
|
7122
7122
|
|
|
7123
7123
|
|
|
7124
|
-
/*utils GEOMETRY library associated with aotra version : «1.0.0.000 (11/07/2022-
|
|
7124
|
+
/*utils GEOMETRY library associated with aotra version : «1.0.0.000 (11/07/2022-21:05:49)»*/
|
|
7125
7125
|
/*-----------------------------------------------------------------------------*/
|
|
7126
7126
|
|
|
7127
7127
|
|
|
@@ -7133,7 +7133,7 @@ createFusrodaClient=function(doOnClientReady, doOnDataReception, urlParam=null,
|
|
|
7133
7133
|
*
|
|
7134
7134
|
* # Library name : «aotrautils»
|
|
7135
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
|
|
7136
|
+
* # Author name : Jérémie Ratomposon (massively helped by his native country free education system)
|
|
7137
7137
|
* # Author email : info@alqemia.com
|
|
7138
7138
|
* # Organization name : Alqemia
|
|
7139
7139
|
* # Organization email : admin@alqemia.com
|
|
@@ -8327,1310 +8327,7 @@ function rayVsUnitSphereClosestPoint(p, r) {
|
|
|
8327
8327
|
// MUST REMAIN AT THE END OF THIS LIBRARY FILE !
|
|
8328
8328
|
|
|
8329
8329
|
AOTRAUTILS_GEOMETRY_LIB_IS_LOADED=true;
|
|
8330
|
-
/*utils
|
|
8331
|
-
/*-----------------------------------------------------------------------------*/
|
|
8332
|
-
|
|
8333
|
-
|
|
8334
|
-
/* ## Utility global methods in a javascript, console (nodejs) server, or vanilla javascript with no browser environment.
|
|
8335
|
-
*
|
|
8336
|
-
* This set of methods gathers utility generic-purpose methods usable in any JS project.
|
|
8337
|
-
* Several authors of snippets published freely on the Internet contributed to this library.
|
|
8338
|
-
* Feel free to use/modify-enhance/publish them under the terms of its license.
|
|
8339
|
-
*
|
|
8340
|
-
* # Library name : «aotrautils»
|
|
8341
|
-
* # Library license : HGPL(Help Burma) (see aotra README information for details : https://alqemia.com/aotra.js )
|
|
8342
|
-
* # Author name : Jérémie Ratomposon (massively helped by its programming egos legions)
|
|
8343
|
-
* # Author email : info@alqemia.com
|
|
8344
|
-
* # Organization name : Alqemia
|
|
8345
|
-
* # Organization email : admin@alqemia.com
|
|
8346
|
-
* # Organization website : https://alqemia.com
|
|
8347
|
-
*
|
|
8348
|
-
*
|
|
8349
|
-
*/
|
|
8350
|
-
|
|
8351
|
-
|
|
8352
|
-
|
|
8353
|
-
// COMPATIBILITY browser javascript / nodejs environment :
|
|
8354
|
-
|
|
8355
|
-
if(typeof(window)==="undefined") window=global;
|
|
8356
|
-
|
|
8357
|
-
|
|
8358
|
-
|
|
8359
|
-
|
|
8360
|
-
// =================================================================================
|
|
8361
|
-
// NODEJS UTILS
|
|
8362
|
-
|
|
8363
|
-
const FILE_ENCODING="utf8";
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
// Nodejs filesystem utils :
|
|
8367
|
-
if(typeof(fs)==="undefined"){
|
|
8368
|
-
// TRACE
|
|
8369
|
-
console.log("WARN : Could not find the nodejs dependency «fs», aborting persister setup.");
|
|
8370
|
-
window.getPersister=()=>{ return null; };
|
|
8371
|
-
|
|
8372
|
-
}else{
|
|
8373
|
-
|
|
8374
|
-
|
|
8375
|
-
window.getPersister=function(dataDirPath,suffix=""){
|
|
8376
|
-
|
|
8377
|
-
let self={
|
|
8378
|
-
|
|
8379
|
-
dataDirPath:dataDirPath,
|
|
8380
|
-
suffix:suffix,
|
|
8381
|
-
|
|
8382
|
-
//FILE_NAME_PATTERN:"data.clientId.repositoryName.json",
|
|
8383
|
-
/*private*/getPath:function(clientId,repositoryName){
|
|
8384
|
-
// let path=self.FILE_NAME_PATTERN.replace(new RegExp("@clientId@","g"),clientId);
|
|
8385
|
-
let path=`${self.dataDirPath}`
|
|
8386
|
-
+ (blank(self.suffix)?"":(self.suffix+"."))
|
|
8387
|
-
+`${clientId}.${repositoryName}.json`;
|
|
8388
|
-
return path;
|
|
8389
|
-
},
|
|
8390
|
-
|
|
8391
|
-
readTreeObjectFromFile:function(clientId, repositoryName, CLASSNAME_ATTR_NAME=DEFAULT_CLASSNAME_ATTR_NAME){
|
|
8392
|
-
|
|
8393
|
-
let path=self.getPath(clientId,repositoryName);
|
|
8394
|
-
|
|
8395
|
-
let resultFlat=null;
|
|
8396
|
-
|
|
8397
|
-
try{
|
|
8398
|
-
resultFlat=fs.readFileSync(path, FILE_ENCODING);
|
|
8399
|
-
|
|
8400
|
-
}catch(error){
|
|
8401
|
-
// TRACE
|
|
8402
|
-
console.log("ERROR : Could not read file «"+path+"».");
|
|
8403
|
-
|
|
8404
|
-
return null;
|
|
8405
|
-
}
|
|
8406
|
-
|
|
8407
|
-
|
|
8408
|
-
let resultData={};
|
|
8409
|
-
if(!empty(resultFlat)) resultData=JSON.parse(resultFlat);
|
|
8410
|
-
|
|
8411
|
-
|
|
8412
|
-
if(!empty(resultData) && isFlatMap(resultData)){
|
|
8413
|
-
|
|
8414
|
-
resultData=getAsTreeStructure(resultData,true
|
|
8415
|
-
// We have to keep the type information, here too ! (in the sub-objects)
|
|
8416
|
-
,false);
|
|
8417
|
-
|
|
8418
|
-
}
|
|
8419
|
-
|
|
8420
|
-
return resultData;
|
|
8421
|
-
},
|
|
8422
|
-
|
|
8423
|
-
|
|
8424
|
-
saveDataToFileForClient:function(clientId,repositoryName,dataFlatForClient,forceKeepUnflatten=false,doOnSuccess=null){
|
|
8425
|
-
|
|
8426
|
-
|
|
8427
|
-
|
|
8428
|
-
if(!empty(dataFlatForClient) && !isFlatMap(dataFlatForClient) && !forceKeepUnflatten){
|
|
8429
|
-
dataFlatForClient=getAsFlatStructure(dataFlatForClient,true);
|
|
8430
|
-
}
|
|
8431
|
-
|
|
8432
|
-
// reserved characters : -/\^$*+?.()|[]{}
|
|
8433
|
-
// CANNOT USE stringifyObject(...) function because we are in a common, lower-level library !
|
|
8434
|
-
let dataFlatStr=JSON.stringify(dataFlatForClient)
|
|
8435
|
-
// We «aerate» the produced JSON :
|
|
8436
|
-
.replace(/":[\w]*\{/gim,"\":{\n").replace(/,"/gim,",\n\"")
|
|
8437
|
-
// ...except for inline, escaped JSON string representations :
|
|
8438
|
-
.replace(/\\\":[\w]*\{\n/gim,"\\\":{");
|
|
8439
|
-
// NO : .replace(/}/gim,"}\n");
|
|
8440
|
-
|
|
8441
|
-
|
|
8442
|
-
let path=self.getPath(clientId,repositoryName);
|
|
8443
|
-
|
|
8444
|
-
fs.writeFile(path, dataFlatStr, FILE_ENCODING, (error) => {
|
|
8445
|
-
if(error){
|
|
8446
|
-
// TRACE
|
|
8447
|
-
console.log("ERROR : Could not write file «"+path+"»:",error);
|
|
8448
|
-
throw error;
|
|
8449
|
-
}
|
|
8450
|
-
if(doOnSuccess) doOnSuccess(dataFlatForClient);
|
|
8451
|
-
});
|
|
8452
|
-
}
|
|
8453
|
-
|
|
8454
|
-
};
|
|
8455
|
-
|
|
8456
|
-
|
|
8457
|
-
return self;
|
|
8458
|
-
};
|
|
8459
|
-
|
|
8460
|
-
}
|
|
8461
|
-
|
|
8462
|
-
|
|
8463
|
-
|
|
8464
|
-
// Nodejs server launching helper functions :
|
|
8465
|
-
//Networking management :
|
|
8466
|
-
//- WEBSOCKETS AND NODEJS :
|
|
8467
|
-
|
|
8468
|
-
// -Server :
|
|
8469
|
-
|
|
8470
|
-
getServerParams=function(portParam,isSecureParam,certPathParam,keyPathParam){
|
|
8471
|
-
|
|
8472
|
-
// Node dependencies :
|
|
8473
|
-
// https=require("https");
|
|
8474
|
-
// fs=require("fs");
|
|
8475
|
-
|
|
8476
|
-
if(!https){
|
|
8477
|
-
// TRACE
|
|
8478
|
-
console.log("WARN : Could not find the nodejs dependency «https», aborting SSL setup.");
|
|
8479
|
-
return null;
|
|
8480
|
-
}
|
|
8481
|
-
if(!fs){
|
|
8482
|
-
// TRACE
|
|
8483
|
-
console.log("WARN : Could not find the nodejs dependency «fs», aborting SSL setup.");
|
|
8484
|
-
return null;
|
|
8485
|
-
}
|
|
8486
|
-
|
|
8487
|
-
var result={};
|
|
8488
|
-
|
|
8489
|
-
// We read the command-line arguments if needed :
|
|
8490
|
-
var argCLPort;
|
|
8491
|
-
var argCLCertPath;
|
|
8492
|
-
var argCLKeyPath;
|
|
8493
|
-
process.argv.forEach(function (val, i){
|
|
8494
|
-
if(i<=1) return;
|
|
8495
|
-
else if(i==2) argCLPort=val;
|
|
8496
|
-
else if(i==3) argCLCertPath=val;
|
|
8497
|
-
else if(i==4) argCLKeyPath=val;
|
|
8498
|
-
});
|
|
8499
|
-
|
|
8500
|
-
// Console, command-line arguments OVERRIDE parameters values :
|
|
8501
|
-
|
|
8502
|
-
result.port=nonull(argCLPort,portParam);
|
|
8503
|
-
result.certPath=null;
|
|
8504
|
-
result.keyPath=null;
|
|
8505
|
-
result.isSecure=isSecureParam;
|
|
8506
|
-
|
|
8507
|
-
if(isSecureParam){
|
|
8508
|
-
result.certPath=nonull(argCLCertPath,certPathParam);
|
|
8509
|
-
result.keyPath=nonull(argCLKeyPath,keyPathParam);
|
|
8510
|
-
}
|
|
8511
|
-
|
|
8512
|
-
// Eventual encryption options :
|
|
8513
|
-
result.sslOptions=null;
|
|
8514
|
-
if(isSecureParam){
|
|
8515
|
-
result.sslOptions={
|
|
8516
|
-
cert: fs.readFileSync(certPath),
|
|
8517
|
-
key: fs.readFileSync(keyPath),
|
|
8518
|
-
};
|
|
8519
|
-
}
|
|
8520
|
-
|
|
8521
|
-
return result;
|
|
8522
|
-
}
|
|
8523
|
-
|
|
8524
|
-
|
|
8525
|
-
// NODE ONLY SERVER / CLIENTS :
|
|
8526
|
-
WebsocketImplementation={
|
|
8527
|
-
|
|
8528
|
-
isNodeContext:true,
|
|
8529
|
-
useSocketIOImplementation:false,
|
|
8530
|
-
useFlatStrings:false,
|
|
8531
|
-
|
|
8532
|
-
// COMMON METHODS
|
|
8533
|
-
/*private static*/isInRoom(clientSocket, clientsRoomsTag){
|
|
8534
|
-
return !clientsRoomsTag || empty(clientsRoomsTag) || contains(clientsRoomsTag, clientSocket.clientRoomTag);
|
|
8535
|
-
},
|
|
8536
|
-
|
|
8537
|
-
|
|
8538
|
-
//
|
|
8539
|
-
// NODE SERVER
|
|
8540
|
-
//
|
|
8541
|
-
getStatic:(isNodeContext=true, useSocketIOImplementation=false)=>{
|
|
8542
|
-
|
|
8543
|
-
WebsocketImplementation.isNodeContext=isNodeContext;
|
|
8544
|
-
|
|
8545
|
-
// cf. https://socket.io/docs/v4/client-socket-instance/
|
|
8546
|
-
WebsocketImplementation.useSocketIOImplementation=useSocketIOImplementation;
|
|
8547
|
-
|
|
8548
|
-
// OLD : socket.io :
|
|
8549
|
-
// https://stackoverflow.com/questions/31156884/how-to-use-https-on-node-js-using-express-socket-io
|
|
8550
|
-
// https://stackoverflow.com/questions/6599470/node-js-socket-io-with-ssl
|
|
8551
|
-
// https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener
|
|
8552
|
-
|
|
8553
|
-
// if(typeof(socketIO)==="undefined"){
|
|
8554
|
-
// // TRACE
|
|
8555
|
-
// console.log("«socket.io» SERVER library not called yet, calling it now.");
|
|
8556
|
-
// socketIO=require("socket.io");
|
|
8557
|
-
// return socketIO;
|
|
8558
|
-
// }
|
|
8559
|
-
// if(typeof(socketIO)==="undefined"){
|
|
8560
|
-
// // TRACE
|
|
8561
|
-
// console.log("ERROR : «socket.io» SERVER library not found. Cannot launch nodejs server. Aborting.");
|
|
8562
|
-
// return null;
|
|
8563
|
-
// }
|
|
8564
|
-
|
|
8565
|
-
// *********************************************************************************
|
|
8566
|
-
|
|
8567
|
-
// NEW : ws :
|
|
8568
|
-
// https://github.com/websockets/ws#installing
|
|
8569
|
-
// https://github.com/websockets/ws/blob/master/doc/ws.md#event-message
|
|
8570
|
-
// ON BROWSER SIDE : Native Websockets :
|
|
8571
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
|
|
8572
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
|
|
8573
|
-
if(typeof(WebSocket)==="undefined"){
|
|
8574
|
-
WebSocket=require("ws");
|
|
8575
|
-
// TRACE
|
|
8576
|
-
console.log("«ws» SERVER library not called yet, calling it now.");
|
|
8577
|
-
return WebsocketImplementation;
|
|
8578
|
-
}
|
|
8579
|
-
if(typeof(WebSocket)==="undefined"){
|
|
8580
|
-
// TRACE
|
|
8581
|
-
console.log("ERROR : «ws» SERVER library not found. Cannot launch nodejs server. Aborting.");
|
|
8582
|
-
return null;
|
|
8583
|
-
}
|
|
8584
|
-
|
|
8585
|
-
return WebsocketImplementation;
|
|
8586
|
-
},
|
|
8587
|
-
|
|
8588
|
-
|
|
8589
|
-
getServer:(listenableServer)=>{
|
|
8590
|
-
|
|
8591
|
-
if(!WebsocketImplementation.isNodeContext){
|
|
8592
|
-
// TRACE
|
|
8593
|
-
throw new Error("ERROR : SERVER : Server launch is not supported in a non-nodejs context for any implementation.");
|
|
8594
|
-
}
|
|
8595
|
-
|
|
8596
|
-
// TODO : FIXME : Use one single interface !
|
|
8597
|
-
// NODE SERVER MODE ONLY :
|
|
8598
|
-
let serverSocket;
|
|
8599
|
-
if(!WebsocketImplementation.useSocketIOImplementation){
|
|
8600
|
-
serverSocket=new WebSocket.Server({ "server":listenableServer });
|
|
8601
|
-
}else{
|
|
8602
|
-
// NOW : socket.io :
|
|
8603
|
-
// Loading socket.io
|
|
8604
|
-
serverSocket=socketIO.listen(listenableServer);
|
|
8605
|
-
|
|
8606
|
-
// Setting up the disctonnect event :
|
|
8607
|
-
serverSocket.on("endConnection",()=>{
|
|
8608
|
-
serverSocket.disconnect();
|
|
8609
|
-
});
|
|
8610
|
-
|
|
8611
|
-
}
|
|
8612
|
-
|
|
8613
|
-
// NODE SERVER INSTANCE :
|
|
8614
|
-
const nodeServerInstance={
|
|
8615
|
-
|
|
8616
|
-
onClientLostListeners:[],
|
|
8617
|
-
|
|
8618
|
-
clientTimeoutMillis:20000,
|
|
8619
|
-
// clientsSockets:[],
|
|
8620
|
-
serverSocket:serverSocket,
|
|
8621
|
-
receptionEntryPoints:[],
|
|
8622
|
-
|
|
8623
|
-
receive:(channelName, doOnIncomingMessage, clientsRoomsTag=null)=>{
|
|
8624
|
-
|
|
8625
|
-
const receptionEntryPoint={
|
|
8626
|
-
channelName:channelName,
|
|
8627
|
-
clientsRoomsTag:clientsRoomsTag,
|
|
8628
|
-
execute:(clientSocketParam)=>{
|
|
8629
|
-
|
|
8630
|
-
|
|
8631
|
-
// With «ws» library we have no choive than register message events inside a «connection» event !
|
|
8632
|
-
// nodeServerInstance.onConnectionToClient((serverParam, clientSocketParam)=>{
|
|
8633
|
-
|
|
8634
|
-
// TODO : Find a way to remove this !
|
|
8635
|
-
const doOnMessage=(message)=>{
|
|
8636
|
-
|
|
8637
|
-
// dataWrapped=JSON.parse(dataWrapped);
|
|
8638
|
-
// dataWrapped=getAt(dataWrapped,0);// We get the root element
|
|
8639
|
-
const dataWrapped=(WebsocketImplementation.useFlatStrings?JSON.parse(message):message);
|
|
8640
|
-
|
|
8641
|
-
|
|
8642
|
-
// Channel information is stored in exchanged data :
|
|
8643
|
-
if(dataWrapped.channelName!==receptionEntryPoint.channelName) return;
|
|
8644
|
-
|
|
8645
|
-
|
|
8646
|
-
const clientSocket=clientSocketParam;
|
|
8647
|
-
|
|
8648
|
-
// TODO : FIXME : Use one single interface !
|
|
8649
|
-
// Room information is stored in client socket object :
|
|
8650
|
-
if(!WebsocketImplementation.isInRoom(clientSocket, receptionEntryPoint.clientsRoomsTag)) return;
|
|
8651
|
-
|
|
8652
|
-
doOnIncomingMessage(dataWrapped.data, clientSocket);
|
|
8653
|
-
|
|
8654
|
-
};
|
|
8655
|
-
|
|
8656
|
-
|
|
8657
|
-
if(!WebsocketImplementation.useSocketIOImplementation) clientSocketParam.addEventListener("message", doOnMessage);
|
|
8658
|
-
else clientSocketParam.on(channelName, doOnMessage);
|
|
8659
|
-
|
|
8660
|
-
|
|
8661
|
-
|
|
8662
|
-
|
|
8663
|
-
},
|
|
8664
|
-
};
|
|
8665
|
-
|
|
8666
|
-
// DBG
|
|
8667
|
-
console.log("ADD RECEPTION ENTRY POINT channelName:«"+channelName+"»! nodeServerInstance.receptionEntryPoints.length:",nodeServerInstance.receptionEntryPoints.length);
|
|
8668
|
-
|
|
8669
|
-
nodeServerInstance.receptionEntryPoints.push(receptionEntryPoint);
|
|
8670
|
-
|
|
8671
|
-
|
|
8672
|
-
return nodeServerInstance;
|
|
8673
|
-
},
|
|
8674
|
-
|
|
8675
|
-
|
|
8676
|
-
send:(channelName, data, clientsRoomsTag=null, clientSocketParam=null)=>{
|
|
8677
|
-
|
|
8678
|
-
|
|
8679
|
-
if(!clientSocketParam){
|
|
8680
|
-
|
|
8681
|
-
// TODO : FIXME : Use one single interface !
|
|
8682
|
-
let serverClients;
|
|
8683
|
-
if(!WebsocketImplementation.useSocketIOImplementation) serverClients=nodeServerInstance.serverSocket.clients;
|
|
8684
|
-
else serverClients=nodeServerInstance.serverSocket.sockets.clients();
|
|
8685
|
-
|
|
8686
|
-
serverClients.forEach((clientSocket)=>{
|
|
8687
|
-
|
|
8688
|
-
|
|
8689
|
-
// TODO : FIXME : Use one single interface !
|
|
8690
|
-
if(!WebsocketImplementation.useSocketIOImplementation) if(clientSocket.readyState!==WebSocket.OPEN) return;
|
|
8691
|
-
else if(clientSocket.connected) return;
|
|
8692
|
-
|
|
8693
|
-
|
|
8694
|
-
|
|
8695
|
-
// Room information is stored in client socket object :
|
|
8696
|
-
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
8697
|
-
|
|
8698
|
-
// Channel information is stored in exchanged data :
|
|
8699
|
-
let dataWrapped={channelName:channelName, data:data};
|
|
8700
|
-
|
|
8701
|
-
|
|
8702
|
-
dataWrapped=JSON.stringify(dataWrapped);
|
|
8703
|
-
|
|
8704
|
-
// TODO : FIXME : Use one single interface !
|
|
8705
|
-
if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.send(dataWrapped);
|
|
8706
|
-
else clientSocket.emit(channelName,dataWrapped);
|
|
8707
|
-
|
|
8708
|
-
});
|
|
8709
|
-
|
|
8710
|
-
}else{
|
|
8711
|
-
|
|
8712
|
-
// TODO : FIXME : Use one single interface !
|
|
8713
|
-
let clientSocket=clientSocketParam;
|
|
8714
|
-
if(!WebsocketImplementation.useSocketIOImplementation) if(clientSocket.readyState!==WebSocket.OPEN) return;
|
|
8715
|
-
else if(clientSocket.connected) return;
|
|
8716
|
-
|
|
8717
|
-
|
|
8718
|
-
// Room information is stored in client socket object :
|
|
8719
|
-
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
8720
|
-
|
|
8721
|
-
// Channel information is stored in exchanged data :
|
|
8722
|
-
let dataWrapped={channelName:channelName, data:data};
|
|
8723
|
-
|
|
8724
|
-
dataWrapped=JSON.stringify(dataWrapped);
|
|
8725
|
-
|
|
8726
|
-
// TODO : FIXME : Use one single interface !
|
|
8727
|
-
if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.send(dataWrapped);
|
|
8728
|
-
else clientSocket.emit(channelName,dataWrapped);
|
|
8729
|
-
|
|
8730
|
-
|
|
8731
|
-
}
|
|
8732
|
-
|
|
8733
|
-
|
|
8734
|
-
return nodeServerInstance;
|
|
8735
|
-
},
|
|
8736
|
-
|
|
8737
|
-
|
|
8738
|
-
onConnectionToClient:(doOnConnection)=>{
|
|
8739
|
-
// «connection» is the only event fired by the serverSocket :
|
|
8740
|
-
nodeServerInstance.serverSocket.on("connection", (clientSocket)=>{
|
|
8741
|
-
|
|
8742
|
-
// DBG
|
|
8743
|
-
console.log("SERVER : ON CONNECTION !");
|
|
8744
|
-
|
|
8745
|
-
|
|
8746
|
-
// if(contains(nodeServerInstance.clientsSockets, clientSocket)) return;
|
|
8747
|
-
// nodeServerInstance.clientsSockets.push(clientSocket);
|
|
8748
|
-
|
|
8749
|
-
|
|
8750
|
-
// DBG
|
|
8751
|
-
console.log("nodeServerInstance.receptionEntryPoints.length:",nodeServerInstance.receptionEntryPoints.length);
|
|
8752
|
-
|
|
8753
|
-
|
|
8754
|
-
|
|
8755
|
-
// We execute the events registration listeners entry points:
|
|
8756
|
-
foreach(nodeServerInstance.receptionEntryPoints,(receptionEntryPoint)=>{
|
|
8757
|
-
|
|
8758
|
-
// DBG
|
|
8759
|
-
console.log("ENTRY POINT !");
|
|
8760
|
-
|
|
8761
|
-
receptionEntryPoint.execute(clientSocket);
|
|
8762
|
-
});
|
|
8763
|
-
|
|
8764
|
-
|
|
8765
|
-
doOnConnection(nodeServerInstance, clientSocket);
|
|
8766
|
-
|
|
8767
|
-
|
|
8768
|
-
|
|
8769
|
-
|
|
8770
|
-
// To make the server aware of the clients connections states :
|
|
8771
|
-
clientSocket.stateCheckInterval=setInterval(()=>{
|
|
8772
|
-
|
|
8773
|
-
|
|
8774
|
-
if (clientSocket.isAlive===false){
|
|
8775
|
-
clearInterval(clientSocket.stateCheckInterval);
|
|
8776
|
-
|
|
8777
|
-
// TODO : FIXME : DUPLICATED CODE :
|
|
8778
|
-
// TODO : FIXME : Use one single interface !
|
|
8779
|
-
if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.terminate();
|
|
8780
|
-
else clientSocket.emit("endConnection");
|
|
8781
|
-
|
|
8782
|
-
// TODO : FIXME : DUPLICATED CODE :
|
|
8783
|
-
if(!empty(nodeServerInstance.onClientLostListeners))
|
|
8784
|
-
foreach(nodeServerInstance.onClientLostListeners,l=>{l.execute(clientSocket);});
|
|
8785
|
-
|
|
8786
|
-
return;
|
|
8787
|
-
}
|
|
8788
|
-
|
|
8789
|
-
clientSocket.isAlive=false;
|
|
8790
|
-
try{
|
|
8791
|
-
clientSocket.ping();
|
|
8792
|
-
}catch(error){
|
|
8793
|
-
lognow("ERROR : A problem occurred when tried to ping client socket : ",error);
|
|
8794
|
-
// We effectively end the client connection to this server :
|
|
8795
|
-
|
|
8796
|
-
// TODO : FIXME : DUPLICATED CODE :
|
|
8797
|
-
// TODO : FIXME : Use one single interface !
|
|
8798
|
-
if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.terminate();
|
|
8799
|
-
else clientSocket.emit("endConnection");
|
|
8800
|
-
|
|
8801
|
-
// TODO : FIXME : DUPLICATED CODE :
|
|
8802
|
-
if(!empty(nodeServerInstance.onClientLostListeners))
|
|
8803
|
-
foreach(nodeServerInstance.onClientLostListeners,l=>{l.execute(clientSocket);});
|
|
8804
|
-
|
|
8805
|
-
return;
|
|
8806
|
-
}
|
|
8807
|
-
|
|
8808
|
-
|
|
8809
|
-
}, nodeServerInstance.clientTimeoutMillis);
|
|
8810
|
-
clientSocket.on("pong",()=>{
|
|
8811
|
-
clientSocket.isAlive=true;
|
|
8812
|
-
});
|
|
8813
|
-
|
|
8814
|
-
|
|
8815
|
-
|
|
8816
|
-
});
|
|
8817
|
-
|
|
8818
|
-
|
|
8819
|
-
return nodeServerInstance;
|
|
8820
|
-
},
|
|
8821
|
-
|
|
8822
|
-
onFinalize:(doOnFinalizeServer)=>{
|
|
8823
|
-
|
|
8824
|
-
doOnFinalizeServer(nodeServerInstance);
|
|
8825
|
-
|
|
8826
|
-
// TRACE
|
|
8827
|
-
console.log("INFO : SERVER : Node server setup complete.");
|
|
8828
|
-
|
|
8829
|
-
return nodeServerInstance;
|
|
8830
|
-
},
|
|
8831
|
-
|
|
8832
|
-
addToRoom:(clientSocket,clientRoomTag)=>{
|
|
8833
|
-
clientSocket.clientRoomTag=clientRoomTag;
|
|
8834
|
-
},
|
|
8835
|
-
|
|
8836
|
-
|
|
8837
|
-
|
|
8838
|
-
};
|
|
8839
|
-
|
|
8840
|
-
// Join room server part protocol :
|
|
8841
|
-
nodeServerInstance.receive("protocol",(message, clientSocket)=>{
|
|
8842
|
-
|
|
8843
|
-
if(message.type!=="joinRoom" || !clientSocket) return;
|
|
8844
|
-
|
|
8845
|
-
nodeServerInstance.addToRoom(clientSocket, message.clientRoomTag);
|
|
8846
|
-
|
|
8847
|
-
});
|
|
8848
|
-
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
// To make the server aware of the clients connections states :
|
|
8852
|
-
nodeServerInstance.serverSocket.on("close", function close() {
|
|
8853
|
-
|
|
8854
|
-
// TODO : FIXME : Use one single interface !
|
|
8855
|
-
if(!WebsocketImplementation.useSocketIOImplementation) serverClients=nodeServerInstance.serverSocket.clients;
|
|
8856
|
-
else serverClients=nodeServerInstance.serverSocket.sockets.clients();
|
|
8857
|
-
|
|
8858
|
-
serverClients.forEach((clientSocket)=>{
|
|
8859
|
-
clearInterval(clientSocket.stateCheckInterval);
|
|
8860
|
-
});
|
|
8861
|
-
});
|
|
8862
|
-
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
return nodeServerInstance;
|
|
8866
|
-
},
|
|
8867
|
-
|
|
8868
|
-
|
|
8869
|
-
// NODE / BROWSER CLIENT CONNECTS TO SERVER MAIN ENTRYPOINT:
|
|
8870
|
-
connectToServer:(serverURL, port, isSecure=false, timeout)=>{
|
|
8871
|
-
|
|
8872
|
-
// TRACE
|
|
8873
|
-
lognow("INFO : Using websocket implementation : "+(WebsocketImplementation.isNodeContext?"node (server-side)":"browser (client-side)"));
|
|
8874
|
-
|
|
8875
|
-
if(WebsocketImplementation.isNodeContext)
|
|
8876
|
-
return WebsocketImplementation.connectToServerFromNode(serverURL, port, isSecure, timeout);
|
|
8877
|
-
else
|
|
8878
|
-
return WebsocketImplementation.connectToServerFromBrowser(serverURL, port, isSecure, timeout);
|
|
8879
|
-
},
|
|
8880
|
-
|
|
8881
|
-
|
|
8882
|
-
|
|
8883
|
-
//
|
|
8884
|
-
// NODE CLIENT
|
|
8885
|
-
//
|
|
8886
|
-
/*private*/connectToServerFromNode:(serverURL, port, isSecure, timeout)=>{
|
|
8887
|
-
|
|
8888
|
-
|
|
8889
|
-
|
|
8890
|
-
// NEW : ws :
|
|
8891
|
-
if(typeof(WebSocket)==="undefined"){
|
|
8892
|
-
// TRACE
|
|
8893
|
-
lognow("ERROR : CLIENT : Could not find websocket client lib, aborting client connection.");
|
|
8894
|
-
return null;
|
|
8895
|
-
}
|
|
8896
|
-
|
|
8897
|
-
// NODE CLIENT MODE ONLY :
|
|
8898
|
-
let clientSocket;
|
|
8899
|
-
if(!WebsocketImplementation.useSocketIOImplementation){
|
|
8900
|
-
clientSocket=new WebSocket(serverURL+":"+port,/*WORKAROUND:*/{ rejectUnauthorized:false, secure: isSecure });
|
|
8901
|
-
}else{
|
|
8902
|
-
// NOW : socket.io :
|
|
8903
|
-
//client on server-side:
|
|
8904
|
-
if(WebsocketImplementation.isNodeContext && typeof(socketIO)!=="undefined"){
|
|
8905
|
-
// // Node client :
|
|
8906
|
-
// connectToServer=function (serverURL, port, isSecure, timeout=10000){
|
|
8907
|
-
// return socketIO.connect(serverURL + ":" + port,{timeout: timeout});
|
|
8908
|
-
// }
|
|
8909
|
-
clientSocket=socketIO.connect(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
|
|
8910
|
-
}
|
|
8911
|
-
}
|
|
8912
|
-
|
|
8913
|
-
// NODE CLIENT INSTANCE :
|
|
8914
|
-
const nodeClientInstance={
|
|
8915
|
-
clientSocket:clientSocket,
|
|
8916
|
-
|
|
8917
|
-
receptionEntryPoints:[],
|
|
8918
|
-
|
|
8919
|
-
receive:(channelName, doOnIncomingMessage, clientsRoomsTag=null)=>{
|
|
8920
|
-
|
|
8921
|
-
const receptionEntryPoint={
|
|
8922
|
-
channelName:channelName,
|
|
8923
|
-
clientsRoomsTag:clientsRoomsTag,
|
|
8924
|
-
execute:(clientSocketParam)=>{
|
|
8925
|
-
|
|
8926
|
-
const doOnMessage=(message)=>{
|
|
8927
|
-
|
|
8928
|
-
// dataWrapped=JSON.parse(dataWrapped);
|
|
8929
|
-
// dataWrapped=getAt(dataWrapped,0);// We get the root element
|
|
8930
|
-
|
|
8931
|
-
const dataWrapped=(WebsocketImplementation.useFlatStrings?JSON.parse(message):message);
|
|
8932
|
-
|
|
8933
|
-
|
|
8934
|
-
// Channel information is stored in exchanged data :
|
|
8935
|
-
if(dataWrapped.channelName && dataWrapped.channelName!==channelName) return;
|
|
8936
|
-
|
|
8937
|
-
let clientSocket=nodeClientInstance.clientSocket;
|
|
8938
|
-
|
|
8939
|
-
// Room information is stored in client socket object :
|
|
8940
|
-
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
8941
|
-
|
|
8942
|
-
doOnIncomingMessage(dataWrapped.data, clientSocket);
|
|
8943
|
-
|
|
8944
|
-
};
|
|
8945
|
-
|
|
8946
|
-
if(!WebsocketImplementation.useSocketIOImplementation) nodeClientInstance.clientSocket.addEventListener("message", doOnMessage);
|
|
8947
|
-
else nodeClientInstance.clientSocket.on(channelName, doOnMessage);
|
|
8948
|
-
|
|
8949
|
-
|
|
8950
|
-
}
|
|
8951
|
-
};
|
|
8952
|
-
|
|
8953
|
-
|
|
8954
|
-
nodeClientInstance.receptionEntryPoints.push(receptionEntryPoint);
|
|
8955
|
-
|
|
8956
|
-
return nodeClientInstance;
|
|
8957
|
-
},
|
|
8958
|
-
|
|
8959
|
-
|
|
8960
|
-
send:(channelName, data, clientsRoomsTag=null)=>{
|
|
8961
|
-
|
|
8962
|
-
let clientSocket=nodeClientInstance.clientSocket;
|
|
8963
|
-
|
|
8964
|
-
|
|
8965
|
-
// // DBG
|
|
8966
|
-
// console.log("(NODE CLIENT) TRYING TO SEND : clientSocket.readyState:",clientSocket.readyState);
|
|
8967
|
-
|
|
8968
|
-
// TODO : FIXME : Use one single interface !
|
|
8969
|
-
if(!WebsocketImplementation.useSocketIOImplementation) if(clientSocket.readyState!==WebSocket.OPEN) return;
|
|
8970
|
-
else if(clientSocket.connected) return;
|
|
8971
|
-
|
|
8972
|
-
|
|
8973
|
-
// Room information is stored in client socket object :
|
|
8974
|
-
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
8975
|
-
|
|
8976
|
-
// Channel information is stored in exchanged data :
|
|
8977
|
-
let dataWrapped={channelName:channelName, data:data};
|
|
8978
|
-
|
|
8979
|
-
|
|
8980
|
-
// // DBG
|
|
8981
|
-
// console.log("(NODE CLIENT) SENDING DATA ! dataWrapped:",dataWrapped);
|
|
8982
|
-
|
|
8983
|
-
|
|
8984
|
-
dataWrapped=JSON.stringify(dataWrapped);
|
|
8985
|
-
|
|
8986
|
-
|
|
8987
|
-
// // DBG
|
|
8988
|
-
// console.log("(NODE CLIENT) SENDING DATA ! channelName:«"+channelName+"» ; clientsRoomsTag:«"+clientsRoomsTag+"»");
|
|
8989
|
-
// console.log("(NODE CLIENT) SENDING DATA ! dataWrapped:",dataWrapped);
|
|
8990
|
-
|
|
8991
|
-
// TODO : FIXME : Use one single interface !
|
|
8992
|
-
if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.send(dataWrapped);
|
|
8993
|
-
else clientSocket.emit(channelName,dataWrapped);
|
|
8994
|
-
|
|
8995
|
-
return nodeClientInstance;
|
|
8996
|
-
},
|
|
8997
|
-
|
|
8998
|
-
|
|
8999
|
-
join:(clientRoomTag)=>{
|
|
9000
|
-
// Join room client part protocol :
|
|
9001
|
-
const message={type:"joinRoom",clientRoomTag:clientRoomTag};
|
|
9002
|
-
|
|
9003
|
-
nodeClientInstance.send("protocol",message);
|
|
9004
|
-
|
|
9005
|
-
},
|
|
9006
|
-
|
|
9007
|
-
|
|
9008
|
-
onConnectionToServer:(doOnConnection)=>{
|
|
9009
|
-
|
|
9010
|
-
const doAllOnConnection=()=>{
|
|
9011
|
-
|
|
9012
|
-
// To avoid triggering this event several times, depending on the implementation :
|
|
9013
|
-
if(nodeClientInstance.hasConnectEventFired) return;
|
|
9014
|
-
nodeClientInstance.hasConnectEventFired=true;
|
|
9015
|
-
|
|
9016
|
-
let clientSocket=nodeClientInstance.clientSocket;
|
|
9017
|
-
|
|
9018
|
-
doOnConnection(nodeClientInstance, clientSocket);
|
|
9019
|
-
|
|
9020
|
-
// We execute the listeners entry points:
|
|
9021
|
-
foreach(nodeClientInstance.receptionEntryPoints,(receptionEntryPoint)=>{
|
|
9022
|
-
receptionEntryPoint.execute(clientSocket);
|
|
9023
|
-
});
|
|
9024
|
-
|
|
9025
|
-
};
|
|
9026
|
-
|
|
9027
|
-
if(!WebsocketImplementation.useSocketIOImplementation) nodeClientInstance.clientSocket.addEventListener("open",doAllOnConnection);
|
|
9028
|
-
else nodeClientInstance.clientSocket.on("connect",doAllOnConnection);
|
|
9029
|
-
|
|
9030
|
-
},
|
|
9031
|
-
|
|
9032
|
-
|
|
9033
|
-
};
|
|
9034
|
-
|
|
9035
|
-
|
|
9036
|
-
return nodeClientInstance;
|
|
9037
|
-
},
|
|
9038
|
-
|
|
9039
|
-
|
|
9040
|
-
|
|
9041
|
-
// BROWSER CLIENT
|
|
9042
|
-
|
|
9043
|
-
/*private*/connectToServerFromBrowser:(serverURL, port, isSecure, timeout)=>{
|
|
9044
|
-
|
|
9045
|
-
// NEW : ws :
|
|
9046
|
-
if(typeof(WebSocket)==="undefined"){
|
|
9047
|
-
// TRACE
|
|
9048
|
-
lognow("ERROR : CLIENT : Could not find websocket client lib, aborting client connection.");
|
|
9049
|
-
return null;
|
|
9050
|
-
}
|
|
9051
|
-
|
|
9052
|
-
// TODO : FIXME : Use one single interface !
|
|
9053
|
-
// BROWSER CLIENT MODE ONLY :
|
|
9054
|
-
let clientSocket;
|
|
9055
|
-
if(!WebsocketImplementation.useSocketIOImplementation){
|
|
9056
|
-
clientSocket=new WebSocket(serverURL+":"+port,["ws","wss"]);
|
|
9057
|
-
}else if(typeof(io)!=="undefined"){
|
|
9058
|
-
// NOW : socket.io :
|
|
9059
|
-
// // Browser client :
|
|
9060
|
-
// connectToServer=function (server, port, isSecure, timeout=10000){
|
|
9061
|
-
// return io.connect(serverURL + ":" + port,{timeout: timeout});
|
|
9062
|
-
//// ALTERNATIVE : return io(serverURL + ":" + port,{timeout: timeout});
|
|
9063
|
-
// };
|
|
9064
|
-
// }else{
|
|
9065
|
-
// // TRACE
|
|
9066
|
-
// console.log("ERROR : Could not initialize socket !");
|
|
9067
|
-
clientSocket=io.connect(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
|
|
9068
|
-
}
|
|
9069
|
-
|
|
9070
|
-
|
|
9071
|
-
// BROWSER CLIENT INSTANCE :
|
|
9072
|
-
const browserInstance={
|
|
9073
|
-
clientSocket:clientSocket,
|
|
9074
|
-
|
|
9075
|
-
receive:(channelName, doOnIncomingMessage, clientsRoomsTag=null)=>{
|
|
9076
|
-
|
|
9077
|
-
// DBG
|
|
9078
|
-
lognow("!!! SETTING UP RECEIVE for :",channelName);
|
|
9079
|
-
|
|
9080
|
-
const doOnMessage=(message)=>{
|
|
9081
|
-
|
|
9082
|
-
// // DBG
|
|
9083
|
-
// lognow("!!! RECEIVED : message",message);
|
|
9084
|
-
|
|
9085
|
-
// OLD : let dataWrapped=JSON.parse(event.data);
|
|
9086
|
-
// const dataWrapped=JSON.parse(message);
|
|
9087
|
-
// dataWrapped=getAt(dataWrapped,0);// We get the root element
|
|
9088
|
-
|
|
9089
|
-
const dataWrapped=(WebsocketImplementation.useFlatStrings?JSON.parse(message):message);
|
|
9090
|
-
|
|
9091
|
-
|
|
9092
|
-
// Channel information is stored in exchanged data :
|
|
9093
|
-
if(dataWrapped.channelName && dataWrapped.channelName!==channelName) return;
|
|
9094
|
-
|
|
9095
|
-
let clientSocket=browserInstance.clientSocket;
|
|
9096
|
-
|
|
9097
|
-
// Room information is stored in client socket object :
|
|
9098
|
-
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
9099
|
-
|
|
9100
|
-
doOnIncomingMessage(dataWrapped.data, clientSocket);
|
|
9101
|
-
|
|
9102
|
-
};
|
|
9103
|
-
|
|
9104
|
-
|
|
9105
|
-
if(!WebsocketImplementation.useSocketIOImplementation) browserInstance.clientSocket.addEventListener("message", doOnMessage);
|
|
9106
|
-
else browserInstance.clientSocket.on(channelName, doOnMessage);
|
|
9107
|
-
|
|
9108
|
-
|
|
9109
|
-
return browserInstance;
|
|
9110
|
-
},
|
|
9111
|
-
|
|
9112
|
-
|
|
9113
|
-
send:(channelName, data, clientsRoomsTag=null)=>{
|
|
9114
|
-
|
|
9115
|
-
let clientSocket=browserInstance.clientSocket;
|
|
9116
|
-
|
|
9117
|
-
|
|
9118
|
-
// // DBG
|
|
9119
|
-
// console.log("(BROWSER) TRYING TO SEND : clientSocket.readyState:",clientSocket.readyState);
|
|
9120
|
-
|
|
9121
|
-
// TODO : FIXME : Use one single interface !
|
|
9122
|
-
if(!WebsocketImplementation.useSocketIOImplementation) if(clientSocket.readyState!==WebSocket.OPEN) return;
|
|
9123
|
-
else if(clientSocket.connected) return;
|
|
9124
|
-
|
|
9125
|
-
|
|
9126
|
-
// Room information is stored in client socket object :
|
|
9127
|
-
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
9128
|
-
|
|
9129
|
-
// Channel information is stored in exchanged data :
|
|
9130
|
-
let dataWrapped={channelName:channelName, data:data};
|
|
9131
|
-
|
|
9132
|
-
|
|
9133
|
-
// // DBG
|
|
9134
|
-
// console.log("(BROWSER) SENDING... : dataWrapped :",dataWrapped);
|
|
9135
|
-
|
|
9136
|
-
|
|
9137
|
-
dataWrapped=JSON.stringify(dataWrapped);
|
|
9138
|
-
|
|
9139
|
-
|
|
9140
|
-
// TODO : FIXME : Use one single interface !
|
|
9141
|
-
if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.send(dataWrapped);
|
|
9142
|
-
else clientSocket.emit(channelName,dataWrapped);
|
|
9143
|
-
|
|
9144
|
-
|
|
9145
|
-
return browserInstance;
|
|
9146
|
-
},
|
|
9147
|
-
|
|
9148
|
-
|
|
9149
|
-
join:(clientRoomTag)=>{
|
|
9150
|
-
// Join room client part protocol :
|
|
9151
|
-
const message={type:"joinRoom",clientRoomTag:clientRoomTag};
|
|
9152
|
-
browserInstance.send("protocol",message);
|
|
9153
|
-
},
|
|
9154
|
-
|
|
9155
|
-
|
|
9156
|
-
onConnectionToServer:(doOnConnection)=>{
|
|
9157
|
-
|
|
9158
|
-
// To avoid triggering this event several times, depending on the implementation :
|
|
9159
|
-
if(browserInstance.hasConnectEventFired) return;
|
|
9160
|
-
browserInstance.hasConnectEventFired=true;
|
|
9161
|
-
|
|
9162
|
-
if(!WebsocketImplementation.useSocketIOImplementation) browserInstance.clientSocket.addEventListener("open",doOnConnection);
|
|
9163
|
-
else browserInstance.clientSocket.on("connect",doOnConnection);
|
|
9164
|
-
|
|
9165
|
-
|
|
9166
|
-
},
|
|
9167
|
-
|
|
9168
|
-
|
|
9169
|
-
};
|
|
9170
|
-
|
|
9171
|
-
|
|
9172
|
-
return browserInstance;
|
|
9173
|
-
},
|
|
9174
|
-
|
|
9175
|
-
};
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /*OPTIONAL*/sslOptions=null){
|
|
9180
|
-
|
|
9181
|
-
const EXCLUDED_FILENAMES_PARTS=[".saltkey."];
|
|
9182
|
-
|
|
9183
|
-
|
|
9184
|
-
if(typeof(https)==="undefined"){
|
|
9185
|
-
// TRACE
|
|
9186
|
-
console.log("«https» SERVER library not called yet, calling it now.");
|
|
9187
|
-
https=require("https");
|
|
9188
|
-
}
|
|
9189
|
-
if(typeof(http)==="undefined"){
|
|
9190
|
-
// TRACE
|
|
9191
|
-
console.log("«http» SERVER library not called yet, calling it now.");
|
|
9192
|
-
http=require("http");
|
|
9193
|
-
}
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
const handler=function(request, response){
|
|
9197
|
-
|
|
9198
|
-
const url=request.url;
|
|
9199
|
-
|
|
9200
|
-
let isURLInExclusionZone=!!foreach(EXCLUDED_FILENAMES_PARTS,(excludedStr)=>{
|
|
9201
|
-
if(contains(url,excludedStr)){
|
|
9202
|
-
return true;
|
|
9203
|
-
}
|
|
9204
|
-
});
|
|
9205
|
-
|
|
9206
|
-
if(isURLInExclusionZone){
|
|
9207
|
-
// TRACE
|
|
9208
|
-
console.log("ERROR 403 forbidden access error :");
|
|
9209
|
-
console.log(error);
|
|
9210
|
-
|
|
9211
|
-
response.writeHead(403);
|
|
9212
|
-
response.end("Sorry, cannot access resource : error: "+error.code+" ..\n");
|
|
9213
|
-
response.end();
|
|
9214
|
-
return;
|
|
9215
|
-
}
|
|
9216
|
-
|
|
9217
|
-
|
|
9218
|
-
const urlFile="." + url;
|
|
9219
|
-
|
|
9220
|
-
let filePath=urlFile.indexOf("?")!==-1?urlFile.split("?")[0]:urlFile;
|
|
9221
|
-
if(filePath == "./") filePath="./index.html";
|
|
9222
|
-
|
|
9223
|
-
let extname=path.extname(filePath);
|
|
9224
|
-
let contentType="text/html";
|
|
9225
|
-
|
|
9226
|
-
|
|
9227
|
-
fs.readFile(filePath, function(error, fileContent){
|
|
9228
|
-
if(error){
|
|
9229
|
-
if(error.code == "ENOENT"){
|
|
9230
|
-
// TRACE
|
|
9231
|
-
console.log("ERROR 404 file not found :"+filePath);
|
|
9232
|
-
|
|
9233
|
-
fs.readFile("./404.html", function(error, fileContent){
|
|
9234
|
-
response.writeHead(200, { "Content-Type": contentType });
|
|
9235
|
-
response.end(fileContent, "utf-8");
|
|
9236
|
-
});
|
|
9237
|
-
|
|
9238
|
-
}else {
|
|
9239
|
-
|
|
9240
|
-
// TRACE
|
|
9241
|
-
console.log("ERROR 500 server error :");
|
|
9242
|
-
console.log(error);
|
|
9243
|
-
|
|
9244
|
-
response.writeHead(500);
|
|
9245
|
-
response.end("Sorry, check with the site admin for error: "+error.code+" ..\n");
|
|
9246
|
-
response.end();
|
|
9247
|
-
|
|
9248
|
-
}
|
|
9249
|
-
}else {
|
|
9250
|
-
|
|
9251
|
-
// TRACE
|
|
9252
|
-
console.log("INFO 200 OK :"+filePath);
|
|
9253
|
-
|
|
9254
|
-
response.writeHead(200, { "Content-Type": contentType });
|
|
9255
|
-
response.end(fileContent, "utf-8");
|
|
9256
|
-
|
|
9257
|
-
// res.writeHead(200);
|
|
9258
|
-
// res.end("hello world\n");
|
|
9259
|
-
// res.sendFile(__dirname + "/public/index.html");
|
|
9260
|
-
|
|
9261
|
-
}
|
|
9262
|
-
});
|
|
9263
|
-
|
|
9264
|
-
};
|
|
9265
|
-
|
|
9266
|
-
|
|
9267
|
-
let listenableServer;
|
|
9268
|
-
if(sslOptions){
|
|
9269
|
-
let httpsServer=https.createServer(sslOptions, handler).listen(port);
|
|
9270
|
-
// TRACE
|
|
9271
|
-
console.log("INFO : SERVER : HTTPS Server launched and listening on port " + port + "!");
|
|
9272
|
-
listenableServer=httpsServer;
|
|
9273
|
-
}else{
|
|
9274
|
-
let httpServer=http.createServer(handler).listen(port);
|
|
9275
|
-
// TRACE
|
|
9276
|
-
console.log("INFO : SERVER : HTTP Server launched and listening on port " + port + "!");
|
|
9277
|
-
listenableServer=httpServer;
|
|
9278
|
-
}
|
|
9279
|
-
|
|
9280
|
-
|
|
9281
|
-
const server=WebsocketImplementation.getStatic(true).getServer(listenableServer);
|
|
9282
|
-
|
|
9283
|
-
// When a client connects, we execute the callback :
|
|
9284
|
-
// CAUTION : MUST BE CALLED ONLY ONCE !
|
|
9285
|
-
server.onConnectionToClient((serverParam, clientSocketParam)=>{
|
|
9286
|
-
if(doOnConnect) doOnConnect(serverParam, clientSocketParam);
|
|
9287
|
-
});
|
|
9288
|
-
|
|
9289
|
-
|
|
9290
|
-
server.onFinalize((serverParam)=>{
|
|
9291
|
-
if(doOnFinalizeServer) doOnFinalizeServer(serverParam);
|
|
9292
|
-
});
|
|
9293
|
-
|
|
9294
|
-
|
|
9295
|
-
// TRACE
|
|
9296
|
-
console.log("INFO : SERVER : Generic Nodejs server launched and listening on port:" + port + "!");
|
|
9297
|
-
|
|
9298
|
-
|
|
9299
|
-
return server;
|
|
9300
|
-
}
|
|
9301
|
-
|
|
9302
|
-
|
|
9303
|
-
initNodeServer=function(doOnClientConnection=null, doOnFinalizeServer=null, /*OPTIONAL*/portParam, /*OPTIONAL*/certPathParam, /*OPTIONAL*/keyPathParam){
|
|
9304
|
-
|
|
9305
|
-
// TRACE
|
|
9306
|
-
console.log("Server launched.");
|
|
9307
|
-
console.log("Usage : node <server.js> conf {port:[port], sslCertPath:[ssl certificate path | unsecure ], sslKeyPath:[ssl key path], serverConfig:[JSON server configuration]}");
|
|
9308
|
-
console.log("Or (to generate password hash) : node <server.js> hash <clientId@repositoryName> <clearText>");
|
|
9309
|
-
// EXAMPLE : node orita-srv.js hash orita.global@saltkey 1234567890
|
|
9310
|
-
console.log("Server launched.");
|
|
9311
|
-
|
|
9312
|
-
// We read the command-line arguments if needed :
|
|
9313
|
-
|
|
9314
|
-
let argCLPort;
|
|
9315
|
-
let argCLCertPath;
|
|
9316
|
-
let argCLKeyPath;
|
|
9317
|
-
let serverConfig={};
|
|
9318
|
-
let isForceUnsecure;
|
|
9319
|
-
|
|
9320
|
-
let isHashAsked=false;
|
|
9321
|
-
let clearTextParam=null;
|
|
9322
|
-
let persisterId=null;
|
|
9323
|
-
|
|
9324
|
-
|
|
9325
|
-
process.argv.forEach(function (val, i){
|
|
9326
|
-
if(!val) return;
|
|
9327
|
-
// 0 corresponds to «node / nodejs»
|
|
9328
|
-
if(i<=1) return; // 1 corresponds to « <server.js> »
|
|
9329
|
-
else if(i==2){
|
|
9330
|
-
if(val==="hash") isHashAsked=true;
|
|
9331
|
-
}else if(i==3){
|
|
9332
|
-
if(!isHashAsked){
|
|
9333
|
-
try{
|
|
9334
|
-
const jsonConf=JSON.parse(val);
|
|
9335
|
-
argCLPort=jsonConf.port;
|
|
9336
|
-
argCLCertPath=jsonConf.sslCertPath;
|
|
9337
|
-
argCLKeyPath=jsonConf.sslKeyPath;
|
|
9338
|
-
try{
|
|
9339
|
-
serverConfig=nonull({},JSON.parse(jsonConf.serverConfig));
|
|
9340
|
-
}catch(err1){
|
|
9341
|
-
lognow("ERROR : Cannot parse JSON string «"+jsonConf.serverConfig+"».",err1);
|
|
9342
|
-
}
|
|
9343
|
-
}catch(err2){
|
|
9344
|
-
lognow("ERROR : Cannot parse JSON string «"+val+"».",err2);
|
|
9345
|
-
isHashAsked=true;
|
|
9346
|
-
}
|
|
9347
|
-
} else persisterId=val;
|
|
9348
|
-
}else if(i==4){
|
|
9349
|
-
if(isHashAsked) clearTextParam=val;
|
|
9350
|
-
}
|
|
9351
|
-
});
|
|
9352
|
-
isForceUnsecure=(argCLCertPath==="unsecure");
|
|
9353
|
-
|
|
9354
|
-
|
|
9355
|
-
|
|
9356
|
-
const aotraNodeServer={config:serverConfig};
|
|
9357
|
-
aotraNodeServer.serverManager={ start:()=>{/*DEFAULT START FUNCTION, WILL BE OVERRIDEN LATER*/}};
|
|
9358
|
-
|
|
9359
|
-
if(isHashAsked){
|
|
9360
|
-
// We isntanciate a temporary persister just to read the saltkey file:
|
|
9361
|
-
const persister=getPersister("./");
|
|
9362
|
-
let persisterIdSplits=persisterId.split("@");
|
|
9363
|
-
if(empty(persisterIdSplits) || persisterIdSplits.length!=2){
|
|
9364
|
-
// TRACE
|
|
9365
|
-
console.log("ERROR : No persister repository IDs provided correctly. Cannot read saltkey. Aborting hash generation.");
|
|
9366
|
-
return aotraNodeServer;
|
|
9367
|
-
}
|
|
9368
|
-
const persisterClientId=persisterIdSplits[0];
|
|
9369
|
-
const persisterRepositoryName=persisterIdSplits[1];
|
|
9370
|
-
let globalSaltkeyObject=persister.readTreeObjectFromFile(persisterClientId, persisterRepositoryName);
|
|
9371
|
-
if(!globalSaltkeyObject || !globalSaltkeyObject.saltkey){
|
|
9372
|
-
// TRACE
|
|
9373
|
-
console.log("WARN : No saltkey found. Generating one now.");
|
|
9374
|
-
globalSaltkeyObject={saltkey:getUUID(), hashes:[]};
|
|
9375
|
-
persister.saveDataToFileForClient(persisterClientId,persisterRepositoryName,globalSaltkeyObject,false,()=>{
|
|
9376
|
-
// TRACE
|
|
9377
|
-
console.log("INFO : Saltkey generated and saved successfully.");
|
|
9378
|
-
});
|
|
9379
|
-
}
|
|
9380
|
-
const globalSaltkey=globalSaltkeyObject.saltkey;
|
|
9381
|
-
|
|
9382
|
-
let firstHash=getHashedString(clearTextParam);
|
|
9383
|
-
|
|
9384
|
-
let generatedHash=getHashedString( firstHash + globalSaltkey, true);// (we use the heavy treatment thing.)
|
|
9385
|
-
globalSaltkeyObject.hashes.push(generatedHash);
|
|
9386
|
-
|
|
9387
|
-
// We update the repository :
|
|
9388
|
-
persister.saveDataToFileForClient(persisterClientId,persisterRepositoryName,globalSaltkeyObject,false,()=>{
|
|
9389
|
-
// TRACE
|
|
9390
|
-
console.log("INFO : Hash added to repository and saved successfully.");
|
|
9391
|
-
});
|
|
9392
|
-
|
|
9393
|
-
// OUTPUT
|
|
9394
|
-
console.log("Here is your key : share it with your clients but DO NOT LEAK IT !\n********************\n"+clearTextParam+"\n********************\n");
|
|
9395
|
-
|
|
9396
|
-
return aotraNodeServer;
|
|
9397
|
-
}
|
|
9398
|
-
|
|
9399
|
-
|
|
9400
|
-
const DEFAULT_PORT=nonull(argCLPort,25000);
|
|
9401
|
-
const DEFAULT_CERT_PATH=nonull(argCLCertPath,"cert.pem");
|
|
9402
|
-
const DEFAULT_KEY_PATH=nonull(argCLKeyPath,"key.key");
|
|
9403
|
-
|
|
9404
|
-
|
|
9405
|
-
let port=portParam ? portParam : DEFAULT_PORT;
|
|
9406
|
-
let certPath=null;
|
|
9407
|
-
let keyPath=null;
|
|
9408
|
-
|
|
9409
|
-
if(!isForceUnsecure){
|
|
9410
|
-
certPath=certPathParam?certPathParam:DEFAULT_CERT_PATH;
|
|
9411
|
-
keyPath=keyPathParam?keyPathParam:DEFAULT_KEY_PATH;
|
|
9412
|
-
}
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
// UNUSEFUL :
|
|
9416
|
-
//aotraNodeServer.serverManager.microClientsSockets=[];
|
|
9417
|
-
// UNUSEFUL :
|
|
9418
|
-
//aotraNodeServer.serverManager.mainClientsSockets=[];
|
|
9419
|
-
aotraNodeServer.serverManager.start=function(){
|
|
9420
|
-
|
|
9421
|
-
// Eventual encryption options :
|
|
9422
|
-
let sslOptions=null;
|
|
9423
|
-
if(!isForceUnsecure){
|
|
9424
|
-
if(!fs){
|
|
9425
|
-
// TRACE
|
|
9426
|
-
lognow("ERROR : «fs» node subsystem not present, cannot access files. Aborting SSL configuration of server.");
|
|
9427
|
-
}else{
|
|
9428
|
-
try{
|
|
9429
|
-
sslOptions={
|
|
9430
|
-
cert: fs.readFileSync(certPath),
|
|
9431
|
-
key: fs.readFileSync(keyPath),
|
|
9432
|
-
};
|
|
9433
|
-
}catch(exception){
|
|
9434
|
-
// TRACE
|
|
9435
|
-
lognow("ERROR : Could not open SSL files certPath:«"+certPath+"» or keyPath:«"+keyPath+"». Aborting SSL configuration of server.");
|
|
9436
|
-
}
|
|
9437
|
-
}
|
|
9438
|
-
}
|
|
9439
|
-
|
|
9440
|
-
aotraNodeServer.server=launchNodeHTTPServer(port, doOnClientConnection, doOnFinalizeServer, sslOptions);
|
|
9441
|
-
|
|
9442
|
-
|
|
9443
|
-
return aotraNodeServer;
|
|
9444
|
-
};
|
|
9445
|
-
|
|
9446
|
-
return aotraNodeServer;
|
|
9447
|
-
}
|
|
9448
|
-
|
|
9449
|
-
// ========================= FUSRODA SERVER : =========================
|
|
9450
|
-
|
|
9451
|
-
//
|
|
9452
|
-
// DOES NOT WORK : USE Java FusrodaServer instead :
|
|
9453
|
-
//
|
|
9454
|
-
///*FUSRODA server stands from FSRD SERVER, for Fucking Simple Remote Desktop SERVER*/
|
|
9455
|
-
//createFusrodaServer=function(certPathParam=null,keyPathParam=null,portParam=6080){
|
|
9456
|
-
//
|
|
9457
|
-
// // https://www.npmjs.com/package/screenshot-desktop
|
|
9458
|
-
// // https://github.com/octalmage/robotjs
|
|
9459
|
-
// // npm install --save screenshot-desktop
|
|
9460
|
-
// //
|
|
9461
|
-
// // sudo apt-get install libxtst-dev libx11-dev
|
|
9462
|
-
// // npm install --save robotjs
|
|
9463
|
-
//
|
|
9464
|
-
// // http://getrobot.net/docs/usage.html
|
|
9465
|
-
// //
|
|
9466
|
-
// // apt-get install build-essential python libxt-dev libxtst-dev libxinerama-dev -y
|
|
9467
|
-
//
|
|
9468
|
-
// const screenshot=require("screenshot-desktop");
|
|
9469
|
-
//
|
|
9470
|
-
// const REFRESH_SCREENSHOTS_MILLIS=500;
|
|
9471
|
-
//
|
|
9472
|
-
//
|
|
9473
|
-
// const server=initNodeServer(
|
|
9474
|
-
// // On client connection :
|
|
9475
|
-
//// (serverParam, clientSocketParam)=>{},
|
|
9476
|
-
// null,
|
|
9477
|
-
// // On server finalization :
|
|
9478
|
-
// (serverParam)=>{
|
|
9479
|
-
//
|
|
9480
|
-
// serverParam.receive("protocol_fusroda", (message, clientSocket)=> {
|
|
9481
|
-
// serverParam.addToRoom(clientSocket,"clients");
|
|
9482
|
-
// });
|
|
9483
|
-
//
|
|
9484
|
-
//
|
|
9485
|
-
// serverParam.sendScreenshotsRoutine=setInterval(()=>{
|
|
9486
|
-
// if(serverParam.isScreenshotStarted) return;
|
|
9487
|
-
//
|
|
9488
|
-
// serverParam.isScreenshotStarted=true;
|
|
9489
|
-
//
|
|
9490
|
-
// screenshot().then((img) => {
|
|
9491
|
-
//
|
|
9492
|
-
// const data={image:img,messageType:"imageData"};
|
|
9493
|
-
// serverParam.send("message", data, "clients");
|
|
9494
|
-
//
|
|
9495
|
-
// serverParam.isScreenshotStarted=false;
|
|
9496
|
-
//
|
|
9497
|
-
// }).catch((error) => {
|
|
9498
|
-
// // TRACE
|
|
9499
|
-
// lognow("ERROR : Error during screenshot :",error);
|
|
9500
|
-
// });
|
|
9501
|
-
//
|
|
9502
|
-
// },REFRESH_SCREENSHOTS_MILLIS);
|
|
9503
|
-
//
|
|
9504
|
-
//
|
|
9505
|
-
// },portParam,certPathParam,keyPathParam);
|
|
9506
|
-
//
|
|
9507
|
-
//
|
|
9508
|
-
//// const doOnConnect=(serverParam, clientSocketParam)=>{
|
|
9509
|
-
//// };
|
|
9510
|
-
//// const doOnFinalizeServer=(serverParam)=>{
|
|
9511
|
-
//// /*DO NOTHING*/
|
|
9512
|
-
//// };
|
|
9513
|
-
//// const server={};
|
|
9514
|
-
//// server.start=(port=6080)=>{
|
|
9515
|
-
//// server.httpServer=launchNodeHTTPServer(port, doOnConnect, doOnFinalizeServer, sslOptions);
|
|
9516
|
-
//// };
|
|
9517
|
-
//
|
|
9518
|
-
// return server;
|
|
9519
|
-
//}
|
|
9520
|
-
|
|
9521
|
-
|
|
9522
|
-
// ========================= UTILITY SERVERSIDE METHODS : =========================
|
|
9523
|
-
|
|
9524
|
-
class ListManager{
|
|
9525
|
-
constructor(config){
|
|
9526
|
-
this.config=config;
|
|
9527
|
-
this.maxItemsNumber=nonull(this.config.max,999);
|
|
9528
|
-
this.simultaneousItemsNumber=nonull(this.config.simultaneous,1);
|
|
9529
|
-
this.sessionDurationSeconds=nonull(this.config.duration,null);
|
|
9530
|
-
this.mode=nonull(this.config.mode,"startAtConnexion");
|
|
9531
|
-
|
|
9532
|
-
this.itemsInfos={};
|
|
9533
|
-
this.time=null;
|
|
9534
|
-
this.started=false;
|
|
9535
|
-
}
|
|
9536
|
-
|
|
9537
|
-
addItem(id,item){
|
|
9538
|
-
if(id==null){
|
|
9539
|
-
// TRACE
|
|
9540
|
-
lognow("ERROR : Cannot add item with no id.");
|
|
9541
|
-
return;
|
|
9542
|
-
}
|
|
9543
|
-
if(!item){
|
|
9544
|
-
// TRACE
|
|
9545
|
-
lognow("ERROR : Cannot add null item.");
|
|
9546
|
-
return;
|
|
9547
|
-
}
|
|
9548
|
-
const numberOfItemsCurrently=countKeys(this.itemsInfos);
|
|
9549
|
-
if(this.maxItemsNumber<=numberOfItemsCurrently){
|
|
9550
|
-
// TRACE
|
|
9551
|
-
lognow("ERROR : Cannot add item with id «"+id+"», list already full.");
|
|
9552
|
-
return;
|
|
9553
|
-
}
|
|
9554
|
-
if(numberOfItemsCurrently==0 && this.mode==="startAtConnexion"){
|
|
9555
|
-
this.startSession();
|
|
9556
|
-
}
|
|
9557
|
-
this.itemsInfos[id]={
|
|
9558
|
-
item:item,
|
|
9559
|
-
time:getNow(),
|
|
9560
|
-
};
|
|
9561
|
-
}
|
|
9562
|
-
|
|
9563
|
-
startSession(){
|
|
9564
|
-
this.time==getNow();
|
|
9565
|
-
this.started=true;
|
|
9566
|
-
}
|
|
9567
|
-
|
|
9568
|
-
stopSession(){
|
|
9569
|
-
this.started=false;
|
|
9570
|
-
}
|
|
9571
|
-
|
|
9572
|
-
isSessionActive(){
|
|
9573
|
-
if(!this.sessionDurationSeconds) return true;
|
|
9574
|
-
if(!this.started) return false;
|
|
9575
|
-
return !hasDelayPassed(this.time, this.sessionDurationSeconds*1000);
|
|
9576
|
-
}
|
|
9577
|
-
|
|
9578
|
-
isClientActive(clientId){
|
|
9579
|
-
if(!clientsListManager.isSessionActive()) return false;
|
|
9580
|
-
const clientPosition=this.getItemPosition(clientId);
|
|
9581
|
-
const result=(clientPosition<=this.maxItemsNumber);
|
|
9582
|
-
return result;
|
|
9583
|
-
}
|
|
9584
|
-
|
|
9585
|
-
removeItem(id){
|
|
9586
|
-
if(id==null){
|
|
9587
|
-
// TRACE
|
|
9588
|
-
lognow("ERROR : Cannot remove item, no id specified.");
|
|
9589
|
-
return;
|
|
9590
|
-
}
|
|
9591
|
-
if(!this.itemsInfos[id]){
|
|
9592
|
-
// TRACE
|
|
9593
|
-
lognow("ERROR : Cannot remove item, item not found for id «"+id+"».");
|
|
9594
|
-
return;
|
|
9595
|
-
}
|
|
9596
|
-
delete this.itemsInfos[id];
|
|
9597
|
-
}
|
|
9598
|
-
|
|
9599
|
-
// Goes from 1 to <length>:
|
|
9600
|
-
getItemPosition(id){
|
|
9601
|
-
if(id==null){
|
|
9602
|
-
// TRACE
|
|
9603
|
-
lognow("ERROR : Cannot calculate item position, no id specified.");
|
|
9604
|
-
return;
|
|
9605
|
-
}
|
|
9606
|
-
if(!this.itemsInfos[id]){
|
|
9607
|
-
// TRACE
|
|
9608
|
-
lognow("ERROR : Cannot calculate item position, item not found for id «"+id+"».");
|
|
9609
|
-
return;
|
|
9610
|
-
}
|
|
9611
|
-
let result=0;
|
|
9612
|
-
foreach(this.itemsInfos,(itemInfo,key)=>{
|
|
9613
|
-
result++;
|
|
9614
|
-
if(id==key) return "break";
|
|
9615
|
-
},null,(itemInfo1,itemInfo2)=>item1.time<item2.time);
|
|
9616
|
-
return result;
|
|
9617
|
-
}
|
|
9618
|
-
|
|
9619
|
-
|
|
9620
|
-
}
|
|
9621
|
-
|
|
9622
|
-
function getListManager(config){
|
|
9623
|
-
return new ListManager(config);
|
|
9624
|
-
}
|
|
9625
|
-
|
|
9626
|
-
|
|
9627
|
-
|
|
9628
|
-
|
|
9629
|
-
|
|
9630
|
-
|
|
9631
|
-
|
|
9632
|
-
|
|
9633
|
-
/*utils COMMONS library associated with aotra version : «1.0.0.000 (11/07/2022-00:48:13)»*/
|
|
8330
|
+
/*utils COMMONS library associated with aotra version : «1.0.0.000 (11/07/2022-21:05:49)»*/
|
|
9634
8331
|
/*-----------------------------------------------------------------------------*/
|
|
9635
8332
|
|
|
9636
8333
|
|
|
@@ -9642,7 +8339,7 @@ function getListManager(config){
|
|
|
9642
8339
|
*
|
|
9643
8340
|
* # Library name : «aotrautils»
|
|
9644
8341
|
* # Library license : HGPL(Help Burma) (see aotra README information for details : https://alqemia.com/aotra.js )
|
|
9645
|
-
* # 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)
|
|
9646
8343
|
* # Author email : info@alqemia.com
|
|
9647
8344
|
* # Organization name : Alqemia
|
|
9648
8345
|
* # Organization email : admin@alqemia.com
|
aotrautils/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aotrautils",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.48",
|
|
4
4
|
"main": "aotrautils.build.js",
|
|
5
|
-
"description": "A library for vanilla javascript utils used in aotra javascript CMS",
|
|
5
|
+
"description": "A library for vanilla javascript utils (client-side) used in aotra javascript CMS",
|
|
6
6
|
"author": "Jeremie Ratomposon <info@alqemia.com> (https://alqemia.com)",
|
|
7
7
|
"license": "HGPL-1.0",
|
|
8
8
|
"homepage": "https://alqemia.com",
|