aotrautils 0.0.55 → 0.0.56

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.
@@ -1,6 +1,6 @@
1
1
 
2
2
 
3
- /*utils CLIENT library associated with aotra version : «1.0.0.000 (12/07/2022-01:46:29)»*/
3
+ /*utils CLIENT library associated with aotra version : «1.0.0.000 (12/07/2022-23:38:27)»*/
4
4
  /*-----------------------------------------------------------------------------*/
5
5
  /* ## Utility global methods in a browser (htmljs) client environment.
6
6
  *
@@ -6509,6 +6509,9 @@ initClient=function(isNodeContext=true, useSocketIOImplementation=false, doOnSer
6509
6509
  aotraClient.client={};
6510
6510
  aotraClient.client.start=function(){
6511
6511
 
6512
+ // DBG
6513
+ lognow("INFO : Setting up Websocket implementation server :...");
6514
+
6512
6515
 
6513
6516
  let socketToServer=WebsocketImplementation.getStatic(isNodeContext,useSocketIOImplementation).connectToServer(url, port, isSecure, timeout);
6514
6517
  if(!socketToServer){
@@ -6848,7 +6851,7 @@ createFusrodaClient=function(doOnClientReady, doOnDataReception, urlParam=null,
6848
6851
 
6849
6852
 
6850
6853
 
6851
- /*utils GEOMETRY library associated with aotra version : «1.0.0.000 (12/07/2022-01:46:29)»*/
6854
+ /*utils GEOMETRY library associated with aotra version : «1.0.0.000 (12/07/2022-23:38:27)»*/
6852
6855
  /*-----------------------------------------------------------------------------*/
6853
6856
 
6854
6857
 
@@ -8054,7 +8057,1307 @@ function rayVsUnitSphereClosestPoint(p, r) {
8054
8057
  // MUST REMAIN AT THE END OF THIS LIBRARY FILE !
8055
8058
 
8056
8059
  AOTRAUTILS_GEOMETRY_LIB_IS_LOADED=true;
8057
- /*utils COMMONS library associated with aotra version : «1.0.0.000 (12/07/2022-01:46:29)»*/
8060
+ /*utils SERVER library associated with aotra version : «1.0.0.000 (12/07/2022-23:38:27)»*/
8061
+ /*-----------------------------------------------------------------------------*/
8062
+
8063
+
8064
+ /* ## Utility global methods in a javascript, console (nodejs) server, or vanilla javascript with no browser environment.
8065
+ *
8066
+ * This set of methods gathers utility generic-purpose methods usable in any JS project.
8067
+ * Several authors of snippets published freely on the Internet contributed to this library.
8068
+ * Feel free to use/modify-enhance/publish them under the terms of its license.
8069
+ *
8070
+ * # Library name : «aotrautils»
8071
+ * # Library license : HGPL(Help Burma) (see aotra README information for details : https://alqemia.com/aotra.js )
8072
+ * # Author name : Jérémie Ratomposon (massively helped by his native country free education system)
8073
+ * # Author email : info@alqemia.com
8074
+ * # Organization name : Alqemia
8075
+ * # Organization email : admin@alqemia.com
8076
+ * # Organization website : https://alqemia.com
8077
+ *
8078
+ *
8079
+ */
8080
+
8081
+
8082
+
8083
+ // COMPATIBILITY browser javascript / nodejs environment :
8084
+
8085
+ if(typeof(window)==="undefined") window=global;
8086
+
8087
+
8088
+
8089
+
8090
+ // =================================================================================
8091
+ // NODEJS UTILS
8092
+
8093
+ const FILE_ENCODING="utf8";
8094
+
8095
+
8096
+ // Nodejs filesystem utils :
8097
+ if(typeof(fs)==="undefined"){
8098
+ // TRACE
8099
+ console.log("WARN : Could not find the nodejs dependency «fs», aborting persister setup.");
8100
+ window.getPersister=()=>{ return null; };
8101
+
8102
+ }else{
8103
+
8104
+
8105
+ window.getPersister=function(dataDirPath,suffix=""){
8106
+
8107
+ let self={
8108
+
8109
+ dataDirPath:dataDirPath,
8110
+ suffix:suffix,
8111
+
8112
+ //FILE_NAME_PATTERN:"data.clientId.repositoryName.json",
8113
+ /*private*/getPath:function(clientId,repositoryName){
8114
+ // let path=self.FILE_NAME_PATTERN.replace(new RegExp("@clientId@","g"),clientId);
8115
+ let path=`${self.dataDirPath}`
8116
+ + (blank(self.suffix)?"":(self.suffix+"."))
8117
+ +`${clientId}.${repositoryName}.json`;
8118
+ return path;
8119
+ },
8120
+
8121
+ readTreeObjectFromFile:function(clientId, repositoryName, CLASSNAME_ATTR_NAME=DEFAULT_CLASSNAME_ATTR_NAME){
8122
+
8123
+ let path=self.getPath(clientId,repositoryName);
8124
+
8125
+ let resultFlat=null;
8126
+
8127
+ try{
8128
+ resultFlat=fs.readFileSync(path, FILE_ENCODING);
8129
+
8130
+ }catch(error){
8131
+ // TRACE
8132
+ console.log("ERROR : Could not read file «"+path+"».");
8133
+
8134
+ return null;
8135
+ }
8136
+
8137
+
8138
+ let resultData={};
8139
+ if(!empty(resultFlat)) resultData=JSON.parse(resultFlat);
8140
+
8141
+
8142
+ if(!empty(resultData) && isFlatMap(resultData)){
8143
+
8144
+ resultData=getAsTreeStructure(resultData,true
8145
+ // We have to keep the type information, here too ! (in the sub-objects)
8146
+ ,false);
8147
+
8148
+ }
8149
+
8150
+ return resultData;
8151
+ },
8152
+
8153
+
8154
+ saveDataToFileForClient:function(clientId,repositoryName,dataFlatForClient,forceKeepUnflatten=false,doOnSuccess=null){
8155
+
8156
+
8157
+
8158
+ if(!empty(dataFlatForClient) && !isFlatMap(dataFlatForClient) && !forceKeepUnflatten){
8159
+ dataFlatForClient=getAsFlatStructure(dataFlatForClient,true);
8160
+ }
8161
+
8162
+ // reserved characters : -/\^$*+?.()|[]{}
8163
+ // CANNOT USE stringifyObject(...) function because we are in a common, lower-level library !
8164
+ let dataFlatStr=JSON.stringify(dataFlatForClient)
8165
+ // We «aerate» the produced JSON :
8166
+ .replace(/":[\w]*\{/gim,"\":{\n").replace(/,"/gim,",\n\"")
8167
+ // ...except for inline, escaped JSON string representations :
8168
+ .replace(/\\\":[\w]*\{\n/gim,"\\\":{");
8169
+ // NO : .replace(/}/gim,"}\n");
8170
+
8171
+
8172
+ let path=self.getPath(clientId,repositoryName);
8173
+
8174
+ fs.writeFile(path, dataFlatStr, FILE_ENCODING, (error) => {
8175
+ if(error){
8176
+ // TRACE
8177
+ console.log("ERROR : Could not write file «"+path+"»:",error);
8178
+ throw error;
8179
+ }
8180
+ if(doOnSuccess) doOnSuccess(dataFlatForClient);
8181
+ });
8182
+ }
8183
+
8184
+ };
8185
+
8186
+
8187
+ return self;
8188
+ };
8189
+
8190
+ }
8191
+
8192
+
8193
+
8194
+ // Nodejs server launching helper functions :
8195
+ //Networking management :
8196
+ //- WEBSOCKETS AND NODEJS :
8197
+
8198
+ // -Server :
8199
+
8200
+ // UNUSED : (BUT KEEP CODE!!)
8201
+ //getServerParams=function(portParam,isSecureParam,certPathParam,keyPathParam){
8202
+ //
8203
+ // // Node dependencies :
8204
+ // // https=require("https");
8205
+ // // fs=require("fs");
8206
+ //
8207
+ // if(!https){
8208
+ // // TRACE
8209
+ // console.log("WARN : Could not find the nodejs dependency «https», aborting SSL setup.");
8210
+ // return null;
8211
+ // }
8212
+ // if(!fs){
8213
+ // // TRACE
8214
+ // console.log("WARN : Could not find the nodejs dependency «fs», aborting SSL setup.");
8215
+ // return null;
8216
+ // }
8217
+ //
8218
+ // var result={};
8219
+ //
8220
+ // // We read the command-line arguments if needed :
8221
+ // var argCLPort;
8222
+ // var argCLCertPath;
8223
+ // var argCLKeyPath;
8224
+ // process.argv.forEach(function (val, i){
8225
+ // if(i<=1) return;
8226
+ // else if(i==2) argCLPort=val;
8227
+ // else if(i==3) argCLCertPath=val;
8228
+ // else if(i==4) argCLKeyPath=val;
8229
+ // });
8230
+ //
8231
+ // // Console, command-line arguments OVERRIDE parameters values :
8232
+ //
8233
+ // result.port=nonull(argCLPort,portParam);
8234
+ // result.certPath=null;
8235
+ // result.keyPath=null;
8236
+ // result.isSecure=isSecureParam;
8237
+ //
8238
+ // if(isSecureParam){
8239
+ // result.certPath=nonull(argCLCertPath,certPathParam);
8240
+ // result.keyPath=nonull(argCLKeyPath,keyPathParam);
8241
+ // }
8242
+ //
8243
+ // // Eventual encryption options :
8244
+ // result.sslOptions=null;
8245
+ // if(isSecureParam){
8246
+ // result.sslOptions={
8247
+ // cert: fs.readFileSync(certPath),
8248
+ // key: fs.readFileSync(keyPath),
8249
+ // };
8250
+ // }
8251
+ //
8252
+ // return result;
8253
+ //}
8254
+
8255
+
8256
+ // NODE ONLY SERVER / CLIENTS :
8257
+ WebsocketImplementation={
8258
+
8259
+ isNodeContext:true,
8260
+ useSocketIOImplementation:false,
8261
+ useFlatStrings:false,
8262
+
8263
+ // COMMON METHODS
8264
+ /*private static*/isInRoom(clientSocket, clientsRoomsTag){
8265
+ return !clientsRoomsTag || empty(clientsRoomsTag) || contains(clientsRoomsTag, clientSocket.clientRoomTag);
8266
+ },
8267
+
8268
+
8269
+ //
8270
+ // NODE SERVER
8271
+ //
8272
+ getStatic:(isNodeContext=true, useSocketIOImplementation=false)=>{
8273
+
8274
+ WebsocketImplementation.isNodeContext=isNodeContext;
8275
+
8276
+ // cf. https://socket.io/docs/v4/client-socket-instance/
8277
+ WebsocketImplementation.useSocketIOImplementation=useSocketIOImplementation;
8278
+
8279
+ // OLD : socket.io :
8280
+ // https://stackoverflow.com/questions/31156884/how-to-use-https-on-node-js-using-express-socket-io
8281
+ // https://stackoverflow.com/questions/6599470/node-js-socket-io-with-ssl
8282
+ // https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener
8283
+
8284
+ // if(typeof(socketIO)==="undefined"){
8285
+ // // TRACE
8286
+ // console.log("«socket.io» SERVER library not called yet, calling it now.");
8287
+ // socketIO=require("socket.io");
8288
+ // return socketIO;
8289
+ // }
8290
+ // if(typeof(socketIO)==="undefined"){
8291
+ // // TRACE
8292
+ // console.log("ERROR : «socket.io» SERVER library not found. Cannot launch nodejs server. Aborting.");
8293
+ // return null;
8294
+ // }
8295
+
8296
+ // *********************************************************************************
8297
+
8298
+ // NEW : ws :
8299
+ // https://github.com/websockets/ws#installing
8300
+ // https://github.com/websockets/ws/blob/master/doc/ws.md#event-message
8301
+ // ON BROWSER SIDE : Native Websockets :
8302
+ // https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
8303
+ // https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
8304
+ if(typeof(WebSocket)==="undefined"){
8305
+ WebSocket=require("ws");
8306
+ // TRACE
8307
+ console.log("«ws» SERVER library not called yet, calling it now.");
8308
+ return WebsocketImplementation;
8309
+ }
8310
+ if(typeof(WebSocket)==="undefined"){
8311
+ // TRACE
8312
+ console.log("ERROR : «ws» SERVER library not found. Cannot launch nodejs server. Aborting.");
8313
+ return null;
8314
+ }
8315
+
8316
+ return WebsocketImplementation;
8317
+ },
8318
+
8319
+
8320
+ getServer:(listenableServer)=>{
8321
+
8322
+ if(!WebsocketImplementation.isNodeContext){
8323
+ // TRACE
8324
+ throw new Error("ERROR : SERVER : Server launch is not supported in a non-nodejs context for any implementation.");
8325
+ }
8326
+
8327
+ // TODO : FIXME : Use one single interface !
8328
+ // NODE SERVER MODE ONLY :
8329
+ let serverSocket;
8330
+ if(!WebsocketImplementation.useSocketIOImplementation){
8331
+ serverSocket=new WebSocket.Server({ "server":listenableServer });
8332
+ }else{
8333
+ // NOW : socket.io :
8334
+ // Loading socket.io
8335
+ serverSocket=socketIO.listen(listenableServer);
8336
+
8337
+ // Setting up the disctonnect event :
8338
+ serverSocket.on("endConnection",()=>{
8339
+ serverSocket.disconnect();
8340
+ });
8341
+
8342
+ }
8343
+
8344
+ // NODE SERVER INSTANCE :
8345
+ const nodeServerInstance={
8346
+
8347
+ onClientLostListeners:[],
8348
+
8349
+ clientTimeoutMillis:20000,
8350
+ // clientsSockets:[],
8351
+ serverSocket:serverSocket,
8352
+ receptionEntryPoints:[],
8353
+
8354
+ receive:(channelName, doOnIncomingMessage, clientsRoomsTag=null)=>{
8355
+
8356
+ const receptionEntryPoint={
8357
+ channelName:channelName,
8358
+ clientsRoomsTag:clientsRoomsTag,
8359
+ execute:(clientSocketParam)=>{
8360
+
8361
+
8362
+ // With «ws» library we have no choive than register message events inside a «connection» event !
8363
+ // nodeServerInstance.onConnectionToClient((serverParam, clientSocketParam)=>{
8364
+
8365
+ // TODO : Find a way to remove this !
8366
+ const doOnMessage=(message)=>{
8367
+
8368
+ // dataWrapped=JSON.parse(dataWrapped);
8369
+ // dataWrapped=getAt(dataWrapped,0);// We get the root element
8370
+ const dataWrapped=(WebsocketImplementation.useFlatStrings?JSON.parse(message):message);
8371
+
8372
+
8373
+ // Channel information is stored in exchanged data :
8374
+ if(dataWrapped.channelName!==receptionEntryPoint.channelName) return;
8375
+
8376
+
8377
+ const clientSocket=clientSocketParam;
8378
+
8379
+ // TODO : FIXME : Use one single interface !
8380
+ // Room information is stored in client socket object :
8381
+ if(!WebsocketImplementation.isInRoom(clientSocket, receptionEntryPoint.clientsRoomsTag)) return;
8382
+
8383
+ doOnIncomingMessage(dataWrapped.data, clientSocket);
8384
+
8385
+ };
8386
+
8387
+
8388
+ if(!WebsocketImplementation.useSocketIOImplementation) clientSocketParam.addEventListener("message", doOnMessage);
8389
+ else clientSocketParam.on(channelName, doOnMessage);
8390
+
8391
+
8392
+
8393
+
8394
+ },
8395
+ };
8396
+
8397
+
8398
+ nodeServerInstance.receptionEntryPoints.push(receptionEntryPoint);
8399
+
8400
+
8401
+ // DBG
8402
+ console.log("ADD RECEPTION ENTRY POINT channelName:«"+channelName+"»! nodeServerInstance.receptionEntryPoints.length:",nodeServerInstance.receptionEntryPoints.length);
8403
+
8404
+
8405
+ return nodeServerInstance;
8406
+ },
8407
+
8408
+
8409
+ send:(channelName, data, clientsRoomsTag=null, clientSocketParam=null)=>{
8410
+
8411
+
8412
+ if(!clientSocketParam){
8413
+
8414
+ // TODO : FIXME : Use one single interface !
8415
+ let serverClients;
8416
+ if(!WebsocketImplementation.useSocketIOImplementation) serverClients=nodeServerInstance.serverSocket.clients;
8417
+ else serverClients=nodeServerInstance.serverSocket.sockets.clients();
8418
+
8419
+ serverClients.forEach((clientSocket)=>{
8420
+
8421
+
8422
+ // TODO : FIXME : Use one single interface !
8423
+ if(!WebsocketImplementation.useSocketIOImplementation) if(clientSocket.readyState!==WebSocket.OPEN) return;
8424
+ else if(clientSocket.connected) return;
8425
+
8426
+
8427
+
8428
+ // Room information is stored in client socket object :
8429
+ if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
8430
+
8431
+ // Channel information is stored in exchanged data :
8432
+ let dataWrapped={channelName:channelName, data:data};
8433
+
8434
+
8435
+ dataWrapped=JSON.stringify(dataWrapped);
8436
+
8437
+ // TODO : FIXME : Use one single interface !
8438
+ if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.send(dataWrapped);
8439
+ else clientSocket.emit(channelName,dataWrapped);
8440
+
8441
+ });
8442
+
8443
+ }else{
8444
+
8445
+ // TODO : FIXME : Use one single interface !
8446
+ let clientSocket=clientSocketParam;
8447
+ if(!WebsocketImplementation.useSocketIOImplementation) if(clientSocket.readyState!==WebSocket.OPEN) return;
8448
+ else if(clientSocket.connected) return;
8449
+
8450
+
8451
+ // Room information is stored in client socket object :
8452
+ if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
8453
+
8454
+ // Channel information is stored in exchanged data :
8455
+ let dataWrapped={channelName:channelName, data:data};
8456
+
8457
+ dataWrapped=JSON.stringify(dataWrapped);
8458
+
8459
+ // TODO : FIXME : Use one single interface !
8460
+ if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.send(dataWrapped);
8461
+ else clientSocket.emit(channelName,dataWrapped);
8462
+
8463
+
8464
+ }
8465
+
8466
+
8467
+ return nodeServerInstance;
8468
+ },
8469
+
8470
+
8471
+ onConnectionToClient:(doOnConnection)=>{
8472
+ // «connection» is the only event fired by the serverSocket :
8473
+ nodeServerInstance.serverSocket.on("connection", (clientSocket)=>{
8474
+
8475
+ // DBG
8476
+ console.log("SERVER : ON CONNECTION !");
8477
+
8478
+
8479
+ // if(contains(nodeServerInstance.clientsSockets, clientSocket)) return;
8480
+ // nodeServerInstance.clientsSockets.push(clientSocket);
8481
+
8482
+
8483
+ // DBG
8484
+ console.log("nodeServerInstance.receptionEntryPoints.length:",nodeServerInstance.receptionEntryPoints.length);
8485
+
8486
+
8487
+
8488
+ // We execute the events registration listeners entry points:
8489
+ foreach(nodeServerInstance.receptionEntryPoints,(receptionEntryPoint)=>{
8490
+
8491
+ // DBG
8492
+ console.log("ENTRY POINT !");
8493
+
8494
+ receptionEntryPoint.execute(clientSocket);
8495
+ });
8496
+
8497
+
8498
+ doOnConnection(nodeServerInstance, clientSocket);
8499
+
8500
+
8501
+
8502
+
8503
+ // To make the server aware of the clients connections states :
8504
+ clientSocket.stateCheckInterval=setInterval(()=>{
8505
+
8506
+
8507
+ if (clientSocket.isAlive===false){
8508
+ clearInterval(clientSocket.stateCheckInterval);
8509
+
8510
+ // TODO : FIXME : DUPLICATED CODE :
8511
+ // TODO : FIXME : Use one single interface !
8512
+ if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.terminate();
8513
+ else clientSocket.emit("endConnection");
8514
+
8515
+ // TODO : FIXME : DUPLICATED CODE :
8516
+ if(!empty(nodeServerInstance.onClientLostListeners))
8517
+ foreach(nodeServerInstance.onClientLostListeners,l=>{l.execute(clientSocket);});
8518
+
8519
+ return;
8520
+ }
8521
+
8522
+ clientSocket.isAlive=false;
8523
+ try{
8524
+ clientSocket.ping();
8525
+ }catch(error){
8526
+ lognow("ERROR : A problem occurred when tried to ping client socket : ",error);
8527
+ // We effectively end the client connection to this server :
8528
+
8529
+ // TODO : FIXME : DUPLICATED CODE :
8530
+ // TODO : FIXME : Use one single interface !
8531
+ if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.terminate();
8532
+ else clientSocket.emit("endConnection");
8533
+
8534
+ // TODO : FIXME : DUPLICATED CODE :
8535
+ if(!empty(nodeServerInstance.onClientLostListeners))
8536
+ foreach(nodeServerInstance.onClientLostListeners,l=>{l.execute(clientSocket);});
8537
+
8538
+ return;
8539
+ }
8540
+
8541
+
8542
+ }, nodeServerInstance.clientTimeoutMillis);
8543
+ clientSocket.on("pong",()=>{
8544
+ clientSocket.isAlive=true;
8545
+ });
8546
+
8547
+
8548
+
8549
+ });
8550
+
8551
+
8552
+ return nodeServerInstance;
8553
+ },
8554
+
8555
+ onFinalize:(doOnFinalizeServer)=>{
8556
+
8557
+ doOnFinalizeServer(nodeServerInstance);
8558
+
8559
+ // TRACE
8560
+ console.log("INFO : SERVER : Node server setup complete.");
8561
+
8562
+ return nodeServerInstance;
8563
+ },
8564
+
8565
+ addToRoom:(clientSocket,clientRoomTag)=>{
8566
+ clientSocket.clientRoomTag=clientRoomTag;
8567
+ },
8568
+
8569
+
8570
+
8571
+ };
8572
+
8573
+ // Join room server part protocol :
8574
+ nodeServerInstance.receive("protocol",(message, clientSocket)=>{
8575
+
8576
+ if(message.type!=="joinRoom" || !clientSocket) return;
8577
+
8578
+ nodeServerInstance.addToRoom(clientSocket, message.clientRoomTag);
8579
+
8580
+ });
8581
+
8582
+
8583
+
8584
+ // To make the server aware of the clients connections states :
8585
+ nodeServerInstance.serverSocket.on("close", function close() {
8586
+
8587
+ // TODO : FIXME : Use one single interface !
8588
+ if(!WebsocketImplementation.useSocketIOImplementation) serverClients=nodeServerInstance.serverSocket.clients;
8589
+ else serverClients=nodeServerInstance.serverSocket.sockets.clients();
8590
+
8591
+ serverClients.forEach((clientSocket)=>{
8592
+ clearInterval(clientSocket.stateCheckInterval);
8593
+ });
8594
+ });
8595
+
8596
+
8597
+
8598
+ return nodeServerInstance;
8599
+ },
8600
+
8601
+
8602
+ // NODE / BROWSER CLIENT CONNECTS TO SERVER MAIN ENTRYPOINT:
8603
+ connectToServer:(serverURL, port, isSecure=false, timeout)=>{
8604
+
8605
+ // TRACE
8606
+ lognow("INFO : Using websocket implementation : "+(WebsocketImplementation.isNodeContext?"node (server-side)":"browser (client-side)"));
8607
+
8608
+ if(WebsocketImplementation.isNodeContext)
8609
+ return WebsocketImplementation.connectToServerFromNode(serverURL, port, isSecure, timeout);
8610
+ else
8611
+ return WebsocketImplementation.connectToServerFromBrowser(serverURL, port, isSecure, timeout);
8612
+ },
8613
+
8614
+
8615
+
8616
+ //
8617
+ // NODE CLIENT
8618
+ //
8619
+ /*private*/connectToServerFromNode:(serverURL, port, isSecure, timeout)=>{
8620
+
8621
+
8622
+
8623
+ // NEW : ws :
8624
+ if(typeof(WebSocket)==="undefined"){
8625
+ // TRACE
8626
+ lognow("ERROR : CLIENT : Could not find websocket client lib, aborting client connection.");
8627
+ return null;
8628
+ }
8629
+
8630
+ // NODE CLIENT MODE ONLY :
8631
+ let clientSocket;
8632
+ if(!WebsocketImplementation.useSocketIOImplementation){
8633
+ clientSocket=new WebSocket(serverURL+":"+port,/*WORKAROUND:*/{ rejectUnauthorized:false, secure: isSecure });
8634
+ }else{
8635
+ // NOW : socket.io :
8636
+ //client on server-side:
8637
+ if(WebsocketImplementation.isNodeContext && typeof(socketIO)!=="undefined"){
8638
+ // // Node client :
8639
+ // connectToServer=function (serverURL, port, isSecure, timeout=10000){
8640
+ // return socketIO.connect(serverURL + ":" + port,{timeout: timeout});
8641
+ // }
8642
+ clientSocket=socketIO.connect(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
8643
+ }
8644
+ }
8645
+
8646
+ // NODE CLIENT INSTANCE :
8647
+ const nodeClientInstance={
8648
+ clientSocket:clientSocket,
8649
+
8650
+ receptionEntryPoints:[],
8651
+
8652
+ receive:(channelName, doOnIncomingMessage, clientsRoomsTag=null)=>{
8653
+
8654
+ const receptionEntryPoint={
8655
+ channelName:channelName,
8656
+ clientsRoomsTag:clientsRoomsTag,
8657
+ execute:(clientSocketParam)=>{
8658
+
8659
+ const doOnMessage=(message)=>{
8660
+
8661
+ // dataWrapped=JSON.parse(dataWrapped);
8662
+ // dataWrapped=getAt(dataWrapped,0);// We get the root element
8663
+
8664
+ const dataWrapped=(WebsocketImplementation.useFlatStrings?JSON.parse(message):message);
8665
+
8666
+
8667
+ // Channel information is stored in exchanged data :
8668
+ if(dataWrapped.channelName && dataWrapped.channelName!==channelName) return;
8669
+
8670
+ let clientSocket=nodeClientInstance.clientSocket;
8671
+
8672
+ // Room information is stored in client socket object :
8673
+ if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
8674
+
8675
+ doOnIncomingMessage(dataWrapped.data, clientSocket);
8676
+
8677
+ };
8678
+
8679
+ if(!WebsocketImplementation.useSocketIOImplementation) nodeClientInstance.clientSocket.addEventListener("message", doOnMessage);
8680
+ else nodeClientInstance.clientSocket.on(channelName, doOnMessage);
8681
+
8682
+
8683
+ }
8684
+ };
8685
+
8686
+
8687
+ nodeClientInstance.receptionEntryPoints.push(receptionEntryPoint);
8688
+
8689
+ return nodeClientInstance;
8690
+ },
8691
+
8692
+
8693
+ send:(channelName, data, clientsRoomsTag=null)=>{
8694
+
8695
+ let clientSocket=nodeClientInstance.clientSocket;
8696
+
8697
+
8698
+ // // DBG
8699
+ // console.log("(NODE CLIENT) TRYING TO SEND : clientSocket.readyState:",clientSocket.readyState);
8700
+
8701
+ // TODO : FIXME : Use one single interface !
8702
+ if(!WebsocketImplementation.useSocketIOImplementation) if(clientSocket.readyState!==WebSocket.OPEN) return;
8703
+ else if(clientSocket.connected) return;
8704
+
8705
+
8706
+ // Room information is stored in client socket object :
8707
+ if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
8708
+
8709
+ // Channel information is stored in exchanged data :
8710
+ let dataWrapped={channelName:channelName, data:data};
8711
+
8712
+
8713
+ // // DBG
8714
+ // console.log("(NODE CLIENT) SENDING DATA ! dataWrapped:",dataWrapped);
8715
+
8716
+
8717
+ dataWrapped=JSON.stringify(dataWrapped);
8718
+
8719
+
8720
+ // // DBG
8721
+ // console.log("(NODE CLIENT) SENDING DATA ! channelName:«"+channelName+"» ; clientsRoomsTag:«"+clientsRoomsTag+"»");
8722
+ // console.log("(NODE CLIENT) SENDING DATA ! dataWrapped:",dataWrapped);
8723
+
8724
+ // TODO : FIXME : Use one single interface !
8725
+ if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.send(dataWrapped);
8726
+ else clientSocket.emit(channelName,dataWrapped);
8727
+
8728
+ return nodeClientInstance;
8729
+ },
8730
+
8731
+
8732
+ join:(clientRoomTag)=>{
8733
+ // Join room client part protocol :
8734
+ const message={type:"joinRoom",clientRoomTag:clientRoomTag};
8735
+
8736
+ nodeClientInstance.send("protocol",message);
8737
+
8738
+ },
8739
+
8740
+
8741
+ onConnectionToServer:(doOnConnection)=>{
8742
+
8743
+ const doAllOnConnection=()=>{
8744
+
8745
+ // To avoid triggering this event several times, depending on the implementation :
8746
+ if(nodeClientInstance.hasConnectEventFired) return;
8747
+ nodeClientInstance.hasConnectEventFired=true;
8748
+
8749
+ let clientSocket=nodeClientInstance.clientSocket;
8750
+
8751
+ doOnConnection(nodeClientInstance, clientSocket);
8752
+
8753
+ // We execute the listeners entry points:
8754
+ foreach(nodeClientInstance.receptionEntryPoints,(receptionEntryPoint)=>{
8755
+ receptionEntryPoint.execute(clientSocket);
8756
+ });
8757
+
8758
+ };
8759
+
8760
+ if(!WebsocketImplementation.useSocketIOImplementation) nodeClientInstance.clientSocket.addEventListener("open",doAllOnConnection);
8761
+ else nodeClientInstance.clientSocket.on("connect",doAllOnConnection);
8762
+
8763
+ },
8764
+
8765
+
8766
+ };
8767
+
8768
+
8769
+ return nodeClientInstance;
8770
+ },
8771
+
8772
+
8773
+
8774
+ // BROWSER CLIENT
8775
+
8776
+ /*private*/connectToServerFromBrowser:(serverURL, port, isSecure, timeout)=>{
8777
+
8778
+ // NEW : ws :
8779
+ if(typeof(WebSocket)==="undefined"){
8780
+ // TRACE
8781
+ lognow("ERROR : CLIENT : Could not find websocket client lib, aborting client connection.");
8782
+ return null;
8783
+ }
8784
+
8785
+ // TODO : FIXME : Use one single interface !
8786
+ // BROWSER CLIENT MODE ONLY :
8787
+ let clientSocket;
8788
+ if(!WebsocketImplementation.useSocketIOImplementation){
8789
+ clientSocket=new WebSocket(serverURL+":"+port,["ws","wss"]);
8790
+ }else if(typeof(io)!=="undefined"){
8791
+ // NOW : socket.io :
8792
+ // // Browser client :
8793
+ // connectToServer=function (server, port, isSecure, timeout=10000){
8794
+ // return io.connect(serverURL + ":" + port,{timeout: timeout});
8795
+ //// ALTERNATIVE : return io(serverURL + ":" + port,{timeout: timeout});
8796
+ // };
8797
+ // }else{
8798
+ // // TRACE
8799
+ // console.log("ERROR : Could not initialize socket !");
8800
+ clientSocket=io.connect(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
8801
+ }
8802
+
8803
+
8804
+ // BROWSER CLIENT INSTANCE :
8805
+ const browserInstance={
8806
+ clientSocket:clientSocket,
8807
+
8808
+ receive:(channelName, doOnIncomingMessage, clientsRoomsTag=null)=>{
8809
+
8810
+ // DBG
8811
+ lognow("!!! SETTING UP RECEIVE for :",channelName);
8812
+
8813
+ const doOnMessage=(message)=>{
8814
+
8815
+ // // DBG
8816
+ // lognow("!!! RECEIVED : message",message);
8817
+
8818
+ // OLD : let dataWrapped=JSON.parse(event.data);
8819
+ // const dataWrapped=JSON.parse(message);
8820
+ // dataWrapped=getAt(dataWrapped,0);// We get the root element
8821
+
8822
+ const dataWrapped=(WebsocketImplementation.useFlatStrings?JSON.parse(message):message);
8823
+
8824
+
8825
+ // Channel information is stored in exchanged data :
8826
+ if(dataWrapped.channelName && dataWrapped.channelName!==channelName) return;
8827
+
8828
+ let clientSocket=browserInstance.clientSocket;
8829
+
8830
+ // Room information is stored in client socket object :
8831
+ if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
8832
+
8833
+ doOnIncomingMessage(dataWrapped.data, clientSocket);
8834
+
8835
+ };
8836
+
8837
+
8838
+ if(!WebsocketImplementation.useSocketIOImplementation) browserInstance.clientSocket.addEventListener("message", doOnMessage);
8839
+ else browserInstance.clientSocket.on(channelName, doOnMessage);
8840
+
8841
+
8842
+ return browserInstance;
8843
+ },
8844
+
8845
+
8846
+ send:(channelName, data, clientsRoomsTag=null)=>{
8847
+
8848
+ let clientSocket=browserInstance.clientSocket;
8849
+
8850
+
8851
+ // // DBG
8852
+ // console.log("(BROWSER) TRYING TO SEND : clientSocket.readyState:",clientSocket.readyState);
8853
+
8854
+ // TODO : FIXME : Use one single interface !
8855
+ if(!WebsocketImplementation.useSocketIOImplementation) if(clientSocket.readyState!==WebSocket.OPEN) return;
8856
+ else if(clientSocket.connected) return;
8857
+
8858
+
8859
+ // Room information is stored in client socket object :
8860
+ if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
8861
+
8862
+ // Channel information is stored in exchanged data :
8863
+ let dataWrapped={channelName:channelName, data:data};
8864
+
8865
+
8866
+ // // DBG
8867
+ // console.log("(BROWSER) SENDING... : dataWrapped :",dataWrapped);
8868
+
8869
+
8870
+ dataWrapped=JSON.stringify(dataWrapped);
8871
+
8872
+
8873
+ // TODO : FIXME : Use one single interface !
8874
+ if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.send(dataWrapped);
8875
+ else clientSocket.emit(channelName,dataWrapped);
8876
+
8877
+
8878
+ return browserInstance;
8879
+ },
8880
+
8881
+
8882
+ join:(clientRoomTag)=>{
8883
+ // Join room client part protocol :
8884
+ const message={type:"joinRoom",clientRoomTag:clientRoomTag};
8885
+ browserInstance.send("protocol",message);
8886
+ },
8887
+
8888
+
8889
+ onConnectionToServer:(doOnConnection)=>{
8890
+
8891
+ // To avoid triggering this event several times, depending on the implementation :
8892
+ if(browserInstance.hasConnectEventFired) return;
8893
+ browserInstance.hasConnectEventFired=true;
8894
+
8895
+ if(!WebsocketImplementation.useSocketIOImplementation) browserInstance.clientSocket.addEventListener("open",doOnConnection);
8896
+ else browserInstance.clientSocket.on("connect",doOnConnection);
8897
+
8898
+
8899
+ },
8900
+
8901
+
8902
+ };
8903
+
8904
+
8905
+ return browserInstance;
8906
+ },
8907
+
8908
+ };
8909
+
8910
+
8911
+
8912
+ launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /*OPTIONAL*/sslOptions=null){
8913
+
8914
+ const EXCLUDED_FILENAMES_PARTS=[".saltkey."];
8915
+
8916
+
8917
+ if(typeof(https)==="undefined"){
8918
+ // TRACE
8919
+ console.log("«https» SERVER library not called yet, calling it now.");
8920
+ https=require("https");
8921
+ }
8922
+ if(typeof(http)==="undefined"){
8923
+ // TRACE
8924
+ console.log("«http» SERVER library not called yet, calling it now.");
8925
+ http=require("http");
8926
+ }
8927
+
8928
+
8929
+ const handler=function(request, response){
8930
+
8931
+ const url=request.url;
8932
+
8933
+ let isURLInExclusionZone=!!foreach(EXCLUDED_FILENAMES_PARTS,(excludedStr)=>{
8934
+ if(contains(url,excludedStr)){
8935
+ return true;
8936
+ }
8937
+ });
8938
+
8939
+ if(isURLInExclusionZone){
8940
+ // TRACE
8941
+ console.log("ERROR 403 forbidden access error :");
8942
+ console.log(error);
8943
+
8944
+ response.writeHead(403);
8945
+ response.end("Sorry, cannot access resource : error: "+error.code+" ..\n");
8946
+ response.end();
8947
+ return;
8948
+ }
8949
+
8950
+
8951
+ const urlFile="." + url;
8952
+
8953
+ let filePath=urlFile.indexOf("?")!==-1?urlFile.split("?")[0]:urlFile;
8954
+ if(filePath == "./") filePath="./index.html";
8955
+
8956
+ let extname=path.extname(filePath);
8957
+ let contentType="text/html";
8958
+
8959
+
8960
+ fs.readFile(filePath, function(error, fileContent){
8961
+ if(error){
8962
+ if(error.code == "ENOENT"){
8963
+ // TRACE
8964
+ console.log("ERROR 404 file not found :"+filePath);
8965
+
8966
+ fs.readFile("./404.html", function(error, fileContent){
8967
+ response.writeHead(200, { "Content-Type": contentType });
8968
+ response.end(fileContent, "utf-8");
8969
+ });
8970
+
8971
+ }else {
8972
+
8973
+ // TRACE
8974
+ console.log("ERROR 500 server error :");
8975
+ console.log(error);
8976
+
8977
+ response.writeHead(500);
8978
+ response.end("Sorry, check with the site admin for error: "+error.code+" ..\n");
8979
+ response.end();
8980
+
8981
+ }
8982
+ }else {
8983
+
8984
+ // TRACE
8985
+ console.log("INFO 200 OK :"+filePath);
8986
+
8987
+ response.writeHead(200, { "Content-Type": contentType });
8988
+ response.end(fileContent, "utf-8");
8989
+
8990
+ // res.writeHead(200);
8991
+ // res.end("hello world\n");
8992
+ // res.sendFile(__dirname + "/public/index.html");
8993
+
8994
+ }
8995
+ });
8996
+
8997
+ };
8998
+
8999
+
9000
+ let listenableServer;
9001
+ if(sslOptions){
9002
+ let httpsServer=https.createServer(sslOptions, handler).listen(port);
9003
+ // TRACE
9004
+ console.log("INFO : SERVER : HTTPS Server launched and listening on port " + port + "!");
9005
+ listenableServer=httpsServer;
9006
+ }else{
9007
+ let httpServer=http.createServer(handler).listen(port);
9008
+ // TRACE
9009
+ console.log("INFO : SERVER : HTTP Server launched and listening on port " + port + "!");
9010
+ listenableServer=httpServer;
9011
+ }
9012
+
9013
+
9014
+ const server=WebsocketImplementation.getStatic(true).getServer(listenableServer);
9015
+
9016
+ // When a client connects, we execute the callback :
9017
+ // CAUTION : MUST BE CALLED ONLY ONCE !
9018
+ server.onConnectionToClient((serverParam, clientSocketParam)=>{
9019
+ if(doOnConnect) doOnConnect(serverParam, clientSocketParam);
9020
+ });
9021
+
9022
+
9023
+ server.onFinalize((serverParam)=>{
9024
+ if(doOnFinalizeServer) doOnFinalizeServer(serverParam);
9025
+ });
9026
+
9027
+
9028
+ // TRACE
9029
+ console.log("INFO : SERVER : Generic Nodejs server launched and listening on port:" + port + "!");
9030
+
9031
+
9032
+ return server;
9033
+ }
9034
+
9035
+
9036
+ initNodeServer=function(doOnClientConnection=null, doOnFinalizeServer=null, /*OPTIONAL*/portParam, /*OPTIONAL*/certPathParam, /*OPTIONAL*/keyPathParam){
9037
+
9038
+ // TRACE
9039
+ console.log("Server launched.");
9040
+ console.log("Usage : node <server.js> conf {port:[port], sslCertPath:[ssl certificate path | unsecure ], sslKeyPath:[ssl key path], serverConfig:[JSON server configuration]}");
9041
+ console.log("Or (to generate password hash) : node <server.js> hash <clientId@repositoryName> <clearTextSecretString>");
9042
+ // EXAMPLE : node orita-srv.js hash orita.global@saltkey 1234567890
9043
+ console.log("Server launched.");
9044
+
9045
+ // We read the command-line arguments if needed :
9046
+
9047
+ let argCLPort;
9048
+ let argCLCertPath;
9049
+ let argCLKeyPath;
9050
+ let serverConfig={};
9051
+ let isForceUnsecure;
9052
+
9053
+ let isHashAsked=false;
9054
+ let clearTextParam=null;
9055
+ let persisterId=null;
9056
+
9057
+
9058
+ process.argv.forEach(function (val, i){
9059
+ if(!val) return;
9060
+ // 0 corresponds to «node / nodejs»
9061
+ if(i<=1) return; // 1 corresponds to « <server.js> »
9062
+ else if(i==2){
9063
+ if(val==="hash") isHashAsked=true;
9064
+ }else if(i==3){
9065
+ if(!isHashAsked){
9066
+ try{
9067
+ const jsonConf=JSON.parse(val);
9068
+ argCLPort=jsonConf.port;
9069
+ argCLCertPath=jsonConf.sslCertPath;
9070
+ argCLKeyPath=jsonConf.sslKeyPath;
9071
+ serverConfig=nonull({},jsonConf.serverConfig);
9072
+ }catch(err1){
9073
+ lognow("ERROR : Cannot parse argument JSON string «"+val+"».",err1);
9074
+ }
9075
+ } else persisterId=val;
9076
+ }else if(i==4){
9077
+ if(isHashAsked) clearTextParam=val;
9078
+ }
9079
+ });
9080
+ isForceUnsecure=(argCLCertPath==="unsecure");
9081
+
9082
+
9083
+ const aotraNodeServer={config:serverConfig};
9084
+ aotraNodeServer.serverManager={ start:()=>{/*DEFAULT START FUNCTION, WILL BE OVERRIDEN LATER*/}};
9085
+
9086
+ if(isHashAsked){
9087
+ // We isntanciate a temporary persister just to read the saltkey file:
9088
+ const persister=getPersister("./");
9089
+ let persisterIdSplits=persisterId.split("@");
9090
+ if(empty(persisterIdSplits) || persisterIdSplits.length!=2){
9091
+ // TRACE
9092
+ console.log("ERROR : No persister repository IDs provided correctly. Cannot read saltkey. Aborting hash generation.");
9093
+ return aotraNodeServer;
9094
+ }
9095
+ const persisterClientId=persisterIdSplits[0];
9096
+ const persisterRepositoryName=persisterIdSplits[1];
9097
+ let globalSaltkeyObject=persister.readTreeObjectFromFile(persisterClientId, persisterRepositoryName);
9098
+ if(!globalSaltkeyObject || !globalSaltkeyObject.saltkey){
9099
+ // TRACE
9100
+ console.log("WARN : No saltkey found. Generating one now.");
9101
+ globalSaltkeyObject={saltkey:getUUID(), hashes:[]};
9102
+ persister.saveDataToFileForClient(persisterClientId,persisterRepositoryName,globalSaltkeyObject,false,()=>{
9103
+ // TRACE
9104
+ console.log("INFO : Saltkey generated and saved successfully.");
9105
+ });
9106
+ }
9107
+ const globalSaltkey=globalSaltkeyObject.saltkey;
9108
+
9109
+ let firstHash=getHashedString(clearTextParam);
9110
+
9111
+ let generatedHash=getHashedString( firstHash + globalSaltkey, true);// (we use the heavy treatment thing.)
9112
+ globalSaltkeyObject.hashes.push(generatedHash);
9113
+
9114
+ // We update the repository :
9115
+ persister.saveDataToFileForClient(persisterClientId,persisterRepositoryName,globalSaltkeyObject,false,()=>{
9116
+ // TRACE
9117
+ console.log("INFO : Hash added to repository and saved successfully.");
9118
+ });
9119
+
9120
+ // OUTPUT
9121
+ console.log("Here is your key : share it with your clients but DO NOT LEAK IT !\n********************\n"+clearTextParam+"\n********************\n");
9122
+
9123
+ return aotraNodeServer;
9124
+ }
9125
+
9126
+
9127
+ const DEFAULT_PORT=nonull(argCLPort,25000);
9128
+ const DEFAULT_CERT_PATH=nonull(argCLCertPath,"cert.pem");
9129
+ const DEFAULT_KEY_PATH=nonull(argCLKeyPath,"key.key");
9130
+
9131
+
9132
+ let port=portParam ? portParam : DEFAULT_PORT;
9133
+ let certPath=null;
9134
+ let keyPath=null;
9135
+
9136
+ if(!isForceUnsecure){
9137
+ certPath=certPathParam?certPathParam:DEFAULT_CERT_PATH;
9138
+ keyPath=keyPathParam?keyPathParam:DEFAULT_KEY_PATH;
9139
+ }
9140
+
9141
+
9142
+ // UNUSEFUL :
9143
+ //aotraNodeServer.serverManager.microClientsSockets=[];
9144
+ // UNUSEFUL :
9145
+ //aotraNodeServer.serverManager.mainClientsSockets=[];
9146
+ aotraNodeServer.serverManager.start=function(){
9147
+
9148
+ // Eventual encryption options :
9149
+ let sslOptions=null;
9150
+ if(!isForceUnsecure){
9151
+ if(!fs){
9152
+ // TRACE
9153
+ lognow("ERROR : «fs» node subsystem not present, cannot access files. Aborting SSL configuration of server.");
9154
+ }else{
9155
+ try{
9156
+ sslOptions={
9157
+ cert: fs.readFileSync(certPath),
9158
+ key: fs.readFileSync(keyPath),
9159
+ };
9160
+ }catch(exception){
9161
+ // TRACE
9162
+ lognow("ERROR : Could not open SSL files certPath:«"+certPath+"» or keyPath:«"+keyPath+"». Aborting SSL configuration of server.");
9163
+ }
9164
+ }
9165
+ }
9166
+
9167
+ aotraNodeServer.server=launchNodeHTTPServer(port, doOnClientConnection, doOnFinalizeServer, sslOptions);
9168
+
9169
+
9170
+ return aotraNodeServer;
9171
+ };
9172
+
9173
+ return aotraNodeServer;
9174
+ }
9175
+
9176
+ // ========================= FUSRODA SERVER : =========================
9177
+
9178
+ //
9179
+ // DOES NOT WORK : USE Java FusrodaServer instead :
9180
+ //
9181
+ ///*FUSRODA server stands from FSRD SERVER, for Fucking Simple Remote Desktop SERVER*/
9182
+ //createFusrodaServer=function(certPathParam=null,keyPathParam=null,portParam=6080){
9183
+ //
9184
+ // // https://www.npmjs.com/package/screenshot-desktop
9185
+ // // https://github.com/octalmage/robotjs
9186
+ // // npm install --save screenshot-desktop
9187
+ // //
9188
+ // // sudo apt-get install libxtst-dev libx11-dev
9189
+ // // npm install --save robotjs
9190
+ //
9191
+ // // http://getrobot.net/docs/usage.html
9192
+ // //
9193
+ // // apt-get install build-essential python libxt-dev libxtst-dev libxinerama-dev -y
9194
+ //
9195
+ // const screenshot=require("screenshot-desktop");
9196
+ //
9197
+ // const REFRESH_SCREENSHOTS_MILLIS=500;
9198
+ //
9199
+ //
9200
+ // const server=initNodeServer(
9201
+ // // On client connection :
9202
+ //// (serverParam, clientSocketParam)=>{},
9203
+ // null,
9204
+ // // On server finalization :
9205
+ // (serverParam)=>{
9206
+ //
9207
+ // serverParam.receive("protocol_fusroda", (message, clientSocket)=> {
9208
+ // serverParam.addToRoom(clientSocket,"clients");
9209
+ // });
9210
+ //
9211
+ //
9212
+ // serverParam.sendScreenshotsRoutine=setInterval(()=>{
9213
+ // if(serverParam.isScreenshotStarted) return;
9214
+ //
9215
+ // serverParam.isScreenshotStarted=true;
9216
+ //
9217
+ // screenshot().then((img) => {
9218
+ //
9219
+ // const data={image:img,messageType:"imageData"};
9220
+ // serverParam.send("message", data, "clients");
9221
+ //
9222
+ // serverParam.isScreenshotStarted=false;
9223
+ //
9224
+ // }).catch((error) => {
9225
+ // // TRACE
9226
+ // lognow("ERROR : Error during screenshot :",error);
9227
+ // });
9228
+ //
9229
+ // },REFRESH_SCREENSHOTS_MILLIS);
9230
+ //
9231
+ //
9232
+ // },portParam,certPathParam,keyPathParam);
9233
+ //
9234
+ //
9235
+ //// const doOnConnect=(serverParam, clientSocketParam)=>{
9236
+ //// };
9237
+ //// const doOnFinalizeServer=(serverParam)=>{
9238
+ //// /*DO NOTHING*/
9239
+ //// };
9240
+ //// const server={};
9241
+ //// server.start=(port=6080)=>{
9242
+ //// server.httpServer=launchNodeHTTPServer(port, doOnConnect, doOnFinalizeServer, sslOptions);
9243
+ //// };
9244
+ //
9245
+ // return server;
9246
+ //}
9247
+
9248
+
9249
+ // ========================= UTILITY SERVERSIDE METHODS : =========================
9250
+
9251
+ class ListManager{
9252
+ constructor(config){
9253
+ this.config=config;
9254
+ this.maxItemsNumber=nonull(this.config.max,999);
9255
+ this.simultaneousItemsNumber=nonull(this.config.simultaneous,1);
9256
+ this.sessionDurationSeconds=nonull(this.config.duration,null);
9257
+ this.mode=nonull(this.config.mode,"startAtConnexion");
9258
+
9259
+ this.itemsInfos={};
9260
+ this.time=null;
9261
+ this.started=false;
9262
+ }
9263
+
9264
+ addItem(id,item){
9265
+ if(id==null){
9266
+ // TRACE
9267
+ lognow("ERROR : Cannot add item with no id.");
9268
+ return;
9269
+ }
9270
+ if(!item){
9271
+ // TRACE
9272
+ lognow("ERROR : Cannot add null item.");
9273
+ return;
9274
+ }
9275
+ const numberOfItemsCurrently=countKeys(this.itemsInfos);
9276
+ if(this.maxItemsNumber<=numberOfItemsCurrently){
9277
+ // TRACE
9278
+ lognow("ERROR : Cannot add item with id «"+id+"», list already full.");
9279
+ return;
9280
+ }
9281
+ if(numberOfItemsCurrently==0 && this.mode==="startAtConnexion"){
9282
+ this.startSession();
9283
+ }
9284
+ this.itemsInfos[id]={
9285
+ item:item,
9286
+ time:getNow(),
9287
+ };
9288
+ }
9289
+
9290
+ startSession(){
9291
+ this.time==getNow();
9292
+ this.started=true;
9293
+ }
9294
+
9295
+ stopSession(){
9296
+ this.started=false;
9297
+ }
9298
+
9299
+ isSessionActive(){
9300
+ if(!this.sessionDurationSeconds) return true;
9301
+ if(!this.started) return false;
9302
+ return !hasDelayPassed(this.time, this.sessionDurationSeconds*1000);
9303
+ }
9304
+
9305
+ isClientActive(clientId){
9306
+ if(!clientsListManager.isSessionActive()) return false;
9307
+ const clientPosition=this.getItemPosition(clientId);
9308
+ const result=(clientPosition<=this.maxItemsNumber);
9309
+ return result;
9310
+ }
9311
+
9312
+ removeItem(id){
9313
+ if(id==null){
9314
+ // TRACE
9315
+ lognow("ERROR : Cannot remove item, no id specified.");
9316
+ return;
9317
+ }
9318
+ if(!this.itemsInfos[id]){
9319
+ // TRACE
9320
+ lognow("ERROR : Cannot remove item, item not found for id «"+id+"».");
9321
+ return;
9322
+ }
9323
+ delete this.itemsInfos[id];
9324
+ }
9325
+
9326
+ // Goes from 1 to <length>:
9327
+ getItemPosition(id){
9328
+ if(id==null){
9329
+ // TRACE
9330
+ lognow("ERROR : Cannot calculate item position, no id specified.");
9331
+ return;
9332
+ }
9333
+ if(!this.itemsInfos[id]){
9334
+ // TRACE
9335
+ lognow("ERROR : Cannot calculate item position, item not found for id «"+id+"».");
9336
+ return;
9337
+ }
9338
+ let result=0;
9339
+ foreach(this.itemsInfos,(itemInfo,key)=>{
9340
+ result++;
9341
+ if(id==key) return "break";
9342
+ },null,(itemInfo1,itemInfo2)=>item1.time<item2.time);
9343
+ return result;
9344
+ }
9345
+
9346
+
9347
+ }
9348
+
9349
+ function getListManager(config){
9350
+ return new ListManager(config);
9351
+ }
9352
+
9353
+
9354
+
9355
+
9356
+
9357
+
9358
+
9359
+
9360
+ /*utils COMMONS library associated with aotra version : «1.0.0.000 (12/07/2022-23:38:27)»*/
8058
9361
  /*-----------------------------------------------------------------------------*/
8059
9362
 
8060
9363
 
aotrautils/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aotrautils",
3
- "version": "0.0.55",
3
+ "version": "0.0.56",
4
4
  "main": "aotrautils.build.js",
5
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)",