aotrautils 0.0.1808 → 0.0.1812

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 COMMONS library associated with aotra version : «1_29072022-2359 (01/03/2026-01:46:57)»*/
3
+ /*utils COMMONS library associated with aotra version : «1_29072022-2359 (14/03/2026-13:12:00)»*/
4
4
  /*-----------------------------------------------------------------------------*/
5
5
 
6
6
 
@@ -1631,7 +1631,11 @@ window.PeriodicalExecuter=class PeriodicalExecuter{
1631
1631
  //================================================================
1632
1632
 
1633
1633
  // Node dependencies :
1634
- if(typeof(require)!="undefined" && typeof(sjcl)=="undefined" ) sjcl = require("sjcl");
1634
+ if(typeof(require)!=="undefined" && typeof(sjcl)==="undefined"){
1635
+ window.sjcl=require("sjcl");
1636
+ }
1637
+ // For debug (WORKAROUND):
1638
+ sjcl=window.sjcl;
1635
1639
 
1636
1640
 
1637
1641
  // NOT AOTESTABLE !
@@ -5072,7 +5076,7 @@ AOTRAUTILS_LIB_IS_LOADED=true;
5072
5076
 
5073
5077
 
5074
5078
 
5075
- /*utils CLIENT library associated with aotra version : «1_29072022-2359 (01/03/2026-01:46:57)»*/
5079
+ /*utils CLIENT library associated with aotra version : «1_29072022-2359 (14/03/2026-13:12:00)»*/
5076
5080
  /*-----------------------------------------------------------------------------*/
5077
5081
  /* ## Utility global methods in a browser (htmljs) client environment.
5078
5082
  *
@@ -5148,7 +5152,7 @@ if(typeof monitorProgression === "undefined"){
5148
5152
  //}
5149
5153
 
5150
5154
  //...so we replace it with another function that is supposed to do the same thing :
5151
- function getURLParameter(nameParam){
5155
+ window.getURLParameter=(nameParam)=>{
5152
5156
  var name=nameParam;
5153
5157
  if (name=new RegExp("[?&]" + encodeURIComponent(name) + "=([^&]*)").exec(location.search))
5154
5158
  return decodeURIComponent(name[1]);
@@ -5188,244 +5192,6 @@ window.getLastPathConcreteSegment=function(urlPath){
5188
5192
 
5189
5193
 
5190
5194
 
5191
-
5192
-
5193
- // A little method to help i18ning aotra user interface :
5194
- const I18N_REPOSITORY_VARIABLE_NAME="I18N_KEYS";
5195
- function i18n(languagesAndStrings, langParam=null){
5196
-
5197
- // ******************* CONFIG *******************
5198
- const PREFERED_LANGUAGE="en";
5199
-
5200
- const NO_TRANSLATION_LABEL="NO TRANSLATION FOUND";
5201
-
5202
- const ADD_SYSTEMATICALLY_TO_REPOSITORY=true;
5203
- const MAX_CHARACTERS_FOR_TRANSLATION_KEY=64; // Really if you have more than 64 characters in commons, then it really should be the same string !
5204
-
5205
- // ***************** END CONFIG *****************
5206
-
5207
-
5208
- if(!languagesAndStrings){
5209
- // TRACE
5210
- console.log("ERROR : No parameter for i18n provided!",languagesAndStrings);
5211
- return NO_TRANSLATION_LABEL;
5212
- }
5213
-
5214
- let lang=null;
5215
- if(langParam){
5216
- lang=langParam;
5217
- }else{
5218
- // Parameters priority :
5219
- if(window){
5220
- if(window.getURLParameter){
5221
- lang=nonull(window.getURLParameter("language"), window.getURLParameter("lang"));
5222
- }
5223
- if(!lang && (window.language || window.lang)){
5224
- lang=nonull(window.language, window.lang);
5225
- }
5226
- }else if(global){ // (NodeJS compatibility)
5227
- if(global.getURLParameter){
5228
- lang=nonull(global.getURLParameter("language"), global.getURLParameter("lang"));
5229
- }
5230
- if(!lang && (global.language || global.lang)){
5231
- lang=nonull(global.language, global.lang);
5232
- }
5233
- }
5234
- lang=nonull(lang, PREFERED_LANGUAGE);
5235
- }
5236
-
5237
- if(contains(["capitalize","append"],lang)){
5238
- // TRACE
5239
- console.log("WARN : Language parameter has for value a reserved attribute keyword, using default language «"+PREFERED_LANGUAGE+"»");
5240
- lang=PREFERED_LANGUAGE;
5241
- }
5242
-
5243
-
5244
- let result=null;
5245
-
5246
-
5247
- if(!isString(languagesAndStrings)){ // Case direct i18n translation, with no translation keys :
5248
-
5249
- result=languagesAndStrings[lang];
5250
-
5251
- if (!result){
5252
- // TRACE
5253
- console.log("ERROR : No label found for language «"+lang+"»!",languagesAndStrings);
5254
- }
5255
-
5256
- if (!result){
5257
- for(key in languagesAndStrings){
5258
- if (!languagesAndStrings.hasOwnProperty(key))
5259
- continue;
5260
- // We take the first that we find, in the other languages:
5261
- result=languagesAndStrings[key];
5262
- break;
5263
- }
5264
- }
5265
-
5266
- if (!result) return NO_TRANSLATION_LABEL;
5267
-
5268
-
5269
- // We add this «rogue» usage of i18n to the whole repository at all useful ends :
5270
- if(ADD_SYSTEMATICALLY_TO_REPOSITORY){
5271
- // let declarable={};
5272
- let maxLength=Math.min(result.length, MAX_CHARACTERS_FOR_TRANSLATION_KEY);
5273
- let key=toConvention("camel", result.substring(0,maxLength).toLowerCase() );
5274
- // let calculatedKey=radicalKey+"_"+getUUID("short");
5275
- // declarable[calculatedKey]=languagesAndStrings;
5276
- i18n.declareSingleEntry(key, languagesAndStrings);
5277
- }
5278
-
5279
- // Eventual post-treatments :
5280
- if(languagesAndStrings.capitalize) result=capitalize(result);
5281
- if(languagesAndStrings.append) result+=languagesAndStrings.append;
5282
-
5283
- return result;
5284
- }
5285
- // else // Case we use translation keys : (here, languagesAndStrings parameter holds just the wished translation key)
5286
-
5287
-
5288
- let repository=(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME];
5289
- if(!repository){
5290
- // TRACE
5291
- console.log("ERROR : No repository variable found for repository «"+I18N_REPOSITORY_VARIABLE_NAME+"»!");
5292
- return NO_TRANSLATION_LABEL;
5293
- }
5294
-
5295
- // OLD (ie. splitted format :)
5296
- // let repositoryForLang=repository[lang];
5297
- // if (!repositoryForLang){
5298
- // // TRACE
5299
- // console.log("ERROR : No translation found for language «"+lang+"»!",languagesAndStrings);
5300
- // return NO_TRANSLATION_LABEL;
5301
- // }
5302
- // result=repositoryForLang[languagesAndStrings]; // Here, languagesAndStrings is just a bundle key.
5303
- // if (!result) return NO_TRANSLATION_LABEL+" (for key «"+languagesAndStrings+"»)";
5304
-
5305
- let languagesAndStringsForTranslationKey=repository[languagesAndStrings];
5306
- if (!languagesAndStringsForTranslationKey){
5307
- // SILENT ERROR :
5308
- // // TRACE
5309
- // console.log("ERROR : No translation found for key «"+languagesAndStrings+"» for language «"+lang+"»!");
5310
- // NO, OLD : return NO_TRANSLATION_LABEL;
5311
- return languagesAndStrings;
5312
- }
5313
- result=i18n(languagesAndStringsForTranslationKey, lang); // Since we use the same format !
5314
-
5315
- return result;
5316
- };
5317
-
5318
-
5319
- i18n.declareSingleEntry=function(key, languagesAndStringsEntry){
5320
- let newRepository;
5321
- let foundRepository=(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME];
5322
- if(!foundRepository){
5323
- // TRACE
5324
- console.log("ERROR : No repository variable found for repository «"+I18N_REPOSITORY_VARIABLE_NAME+"»!");
5325
- return;
5326
- }
5327
-
5328
- let foundEntryForKey=foundRepository[key];
5329
- if(!foundEntryForKey){
5330
- foundRepository[key]=languagesAndStringsEntry;
5331
- // }else{
5332
- // // TRACE
5333
- // console.log("WARN : Duplicate entry for translation key «"+key+"», keeping only the first declared :",foundEntryForKey);
5334
- }
5335
-
5336
- };
5337
-
5338
-
5339
- //USAGE :
5340
- //Example :
5341
- //i18n.declare({
5342
- // "commencer":{"fr":"Commencer","en":"Start"}
5343
- //});
5344
- i18n.declare=function(repositoryInfos){
5345
- let newRepository;
5346
- let foundRepository=(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME];
5347
- if(foundRepository){
5348
- // // TRACE
5349
- // console.log("WARN : A repository has already been declared, merging.",foundRepository);
5350
- newRepository=merge(foundRepository, repositoryInfos);
5351
- }else{
5352
- newRepository=repositoryInfos;
5353
- }
5354
- (window?window:global)[I18N_REPOSITORY_VARIABLE_NAME]=newRepository;
5355
- };
5356
-
5357
- // USAGE :
5358
- // Example :
5359
- //i18n.declareSplitted({
5360
- // "fr":{
5361
- // "commencer":"Commencer",
5362
- // },
5363
- // "en":{
5364
- // "commencer":"Start",
5365
- // },
5366
- //});
5367
- i18n.declareSplitted=function(repositoryInfos){
5368
- let nonSplittedFormat={};
5369
-
5370
- foreach(repositoryInfos,(allKeys, lang)=>{
5371
- foreach(allKeys,(text, key)=>{
5372
-
5373
- let foundKeyInfos=nonSplittedFormat[key];
5374
- if(!foundKeyInfos){
5375
- nonSplittedFormat[key]={};
5376
- foundKeyInfos=nonSplittedFormat[key];
5377
- }
5378
-
5379
- let foundTextForLanguage=foundKeyInfos[lang];
5380
- if(foundTextForLanguage){
5381
- // TRACE
5382
- console.log("WARN : Duplicate entry for translation key «"+key+"» for language «"+lang+"», keeping only the first declared :",foundTextForLanguage);
5383
- }else{
5384
- foundKeyInfos[lang]=text;
5385
- }
5386
-
5387
- });
5388
- });
5389
-
5390
- // Then we use the standard declaration method :
5391
- i18n.declare(nonSplittedFormat);
5392
- };
5393
-
5394
- // For generating languages files helping purposes :
5395
- i18n.displayAll=function(displaySingleEntriesCalls=false){
5396
- let foundRepository=(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME];
5397
- if(!foundRepository){
5398
- // TRACE
5399
- console.log("ERROR : No repository found under the variable name «"+I18N_REPOSITORY_VARIABLE_NAME+"»");
5400
- }else{
5401
- console.log("TRACE : Variable under the name of «"+I18N_REPOSITORY_VARIABLE_NAME+"»:", foundRepository);
5402
- console.log("\n\n");
5403
-
5404
- let message="";
5405
- foreach(foundRepository, (entry, entryKey)=>{
5406
- let normalizedEntry={};
5407
- if(entry.capitalize) normalizedEntry.capitalize=entry.capitalize;
5408
- if(entry.append) normalizedEntry.append=entry.append;
5409
- foreach(entry,(e,k)=>{
5410
- normalizedEntry[k]=e;
5411
- }
5412
- ,(i,key)=>{ return (!contains(["append","capitalize"],key)); }
5413
- ,(i1,i2)=>{ return (i1.key==i2.key?0:(i1.key=="fr"?-1:1));/*(Sorry, French always comes first !)*/}
5414
- );
5415
- let entryStr=stringifyObject(normalizedEntry);
5416
- message+="\""+entryKey+"\":"+entryStr+",\n";
5417
- if(displaySingleEntriesCalls) message+="// i18n("+entryStr+");\n";
5418
- });
5419
-
5420
- console.log("\n"+message);
5421
-
5422
- }
5423
- }
5424
-
5425
-
5426
-
5427
-
5428
-
5429
5195
  // -------------------------------------------------------------------------------------------
5430
5196
 
5431
5197
  // Mouse management :
@@ -5987,10 +5753,6 @@ function escapeHTML(str){
5987
5753
  return result;
5988
5754
  }
5989
5755
 
5990
- function capitalize(str){
5991
- if(nothing(str)) return "";
5992
- return str.replace(/(?:^|\s)\S/m, firstChar => firstChar.toUpperCase()); // We replace only the first character.
5993
- }
5994
5756
 
5995
5757
  function startsWithUpperCase(str){
5996
5758
  if(nothing(str)) return false;
@@ -6240,72 +6002,15 @@ var isURLHttps=aotest(
6240
6002
  }, !PERFORM_TESTS_ON_LIBRARY);
6241
6003
 
6242
6004
 
6243
- function isCharacterToSkip(c){
6244
- return RegExp("\\s").test(c) || contains([",","\'","´","\"",":","-","(",")","[","]","<",">","!","?","%",".","/","=",],c);
6245
- }
6246
-
6247
- function snakize(strParam, separator="_"){
6248
- let result="";
6249
- for(let i=0;i<strParam.length;i++){
6250
- let c=strParam[i];
6251
- if(isCharacterToSkip(c)){
6252
- result+=separator;
6253
- }else{
6254
- result+=c.toLowerCase();
6255
- }
6256
- if(separator===".") result=result.replace(/\\.+/,separator);
6257
- else result=result.replace(RegExp(separator+"+"),separator);
6258
- }
6259
- return result;
6260
- }
6261
-
6262
- function toConvention(type="snake",str){
6263
- if(empty(str)) return str;
6264
- str=str.trim();
6265
- let result="";
6266
- if(type==="camel"){
6267
- for(let i=0;i<str.length;i++){
6268
- let c=str[i];
6269
-
6270
-
6271
- if(isCharacterToSkip(c)){
6272
- let nextChar="";
6273
-
6274
- if(i<str.length-1){// If we are not at the very last character :
6275
- do{
6276
- nextChar=str[i+1];
6277
- i++;
6278
- }while(isCharacterToSkip(nextChar) && i<str.length-1);
6279
- if(isCharacterToSkip(nextChar)) nextChar="";
6280
- }
6281
- result+=nextChar.toUpperCase();
6282
- }else{
6283
-
6284
- result+=c;
6285
- }
6286
-
6287
-
6288
- }
6289
- }else{
6290
- if(type==="bundle"){
6291
- result=snakize(str,".");
6292
- }else{
6293
- result=snakize(str);
6294
- }
6295
- }
6296
-
6297
- return result;
6298
- }
6299
-
6300
- /*DEPRECATED, use new URL("...") instead !*/
6301
- function getFileNameFromURL(url,/* OPTIONAL */forceHTMLFileNameAppending){
6302
- var end=url.length;
6303
- // From most outwards...
6304
- if (url.indexOf("#") != -1)
6305
- end=url.indexOf("#");
6306
- // ...to most inwards :
6307
- if (url.indexOf("?") != -1)
6308
- end=url.indexOf("?");
6005
+ /*DEPRECATED, use new URL("...") instead !*/
6006
+ function getFileNameFromURL(url,/* OPTIONAL */forceHTMLFileNameAppending){
6007
+ var end=url.length;
6008
+ // From most outwards...
6009
+ if (url.indexOf("#") != -1)
6010
+ end=url.indexOf("#");
6011
+ // ...to most inwards :
6012
+ if (url.indexOf("?") != -1)
6013
+ end=url.indexOf("?");
6309
6014
 
6310
6015
  // Everything from last «/» to the end...:
6311
6016
  var result=url.substring(url.lastIndexOf("/") + 1, end).trim();
@@ -13540,7 +13245,7 @@ createOritaMicroClient=function(url, port, isNode=false, staticMicroClientIdPara
13540
13245
 
13541
13246
 
13542
13247
 
13543
- /*utils GEOMETRY library associated with aotra version : «1_29072022-2359 (01/03/2026-01:46:57)»*/
13248
+ /*utils GEOMETRY library associated with aotra version : «1_29072022-2359 (14/03/2026-13:12:00)»*/
13544
13249
  /*-----------------------------------------------------------------------------*/
13545
13250
 
13546
13251
 
@@ -14869,10 +14574,10 @@ function rayVsUnitSphereClosestPoint(p, r) {
14869
14574
  // MUST REMAIN AT THE END OF THIS LIBRARY FILE !
14870
14575
 
14871
14576
  AOTRAUTILS_GEOMETRY_LIB_IS_LOADED=true;
14872
- /*utils 3D library associated with aotra version : «1_29072022-2359 (01/03/2026-01:46:57)»*/
14577
+ /*utils 3D library associated with aotra version : «1_29072022-2359 (14/03/2026-13:12:00)»*/
14873
14578
  /*-----------------------------------------------------------------------------*/
14874
14579
 
14875
- /*utils AI library associated with aotra version : «1_29072022-2359 (01/03/2026-01:46:57)»*/
14580
+ /*utils AI library associated with aotra version : «1_29072022-2359 (14/03/2026-13:12:00)»*/
14876
14581
  /*-----------------------------------------------------------------------------*/
14877
14582
 
14878
14583
 
@@ -15018,7 +14723,7 @@ getOpenAIAPIClient=(modelName, apiURL, agentRole, defaultPrompt)=>{
15018
14723
 
15019
14724
 
15020
14725
 
15021
- /*utils CONSOLE library associated with aotra version : «1_29072022-2359 (01/03/2026-01:46:57)»*/
14726
+ /*utils CONSOLE library associated with aotra version : «1_29072022-2359 (14/03/2026-13:12:00)»*/
15022
14727
  /*-----------------------------------------------------------------------------*/
15023
14728
 
15024
14729
 
@@ -15114,6 +14819,9 @@ class AORTACServerCell{
15114
14819
 
15115
14820
  this.handleCommonListeners();
15116
14821
 
14822
+ // TRACE
14823
+ lognow("AORTAC Server cell started on URL : ",this.selfOrigin);
14824
+
15117
14825
  return this;
15118
14826
  }
15119
14827
 
@@ -15689,7 +15397,7 @@ window.getAORTACClient=function(serverNodeOrigin="ws://127.0.0.1:40000", model,
15689
15397
 
15690
15398
 
15691
15399
 
15692
- /* ## Utility global methods in a javascript, console (nodejs) or vanilla javascript with no browser environment.
15400
+ /* ## Utility methods in a javascript, for internationalzation (i18n) subsystem (server & client)
15693
15401
  *
15694
15402
  * This set of methods gathers utility generic-purpose methods usable in any JS project.
15695
15403
  * Several authors of snippets published freely on the Internet contributed to this library.
@@ -15712,1301 +15420,309 @@ window.getAORTACClient=function(serverNodeOrigin="ws://127.0.0.1:40000", model,
15712
15420
  if(typeof(window)==="undefined") window=global;
15713
15421
 
15714
15422
 
15715
-
15716
-
15717
- // OLD : socket.io :
15718
- // https://stackoverflow.com/questions/31156884/how-to-use-https-on-node-js-using-express-socket-io
15719
- // https://stackoverflow.com/questions/6599470/node-js-socket-io-with-ssl
15720
- // https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener
15721
- // https://socket.io/docs/v4/client-socket-instance/
15722
-
15723
- // NEW : ws :
15724
- // https://github.com/websockets/ws#installing
15725
- // https://github.com/websockets/ws/blob/master/doc/ws.md#event-message
15726
- // ON BROWSER SIDE : Native Websockets :
15727
- // https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
15728
- // https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
15729
-
15730
- // We have to import both implementations, regardless of which one is chosen :
15731
- //Socket=require("socket.io");
15732
- //WebSocket=require("ws");
15733
-
15734
-
15735
-
15736
-
15737
- // =================================================================================
15738
- // NODEJS UTILS
15739
-
15740
- const FILE_ENCODING="utf8";
15741
-
15742
- const ADD_CORS_HEADER=true;
15743
-
15744
-
15745
-
15746
- if(typeof(require)!="undefined" && typeof(fs)!="undefined" )
15747
- window.fs=require("fs");
15423
+ // ==================================================================================================================
15748
15424
 
15749
15425
 
15750
15426
 
15751
15427
 
15752
- // Nodejs filesystem utils :
15753
- if(typeof(fs)==="undefined"){
15754
- // TRACE
15755
- console.log("WARN : Could not find the nodejs dependency «fs», aborting persister setup.");
15756
- getPersister=()=>{ return null; };
15428
+ // A little method to help i18ning aotra user interface :
15429
+ const I18N_REPOSITORY_VARIABLE_NAME="I18N_KEYS";
15430
+ window.i18n=(languagesAndStrings, langParam=null)=>{
15757
15431
 
15758
- }else{
15432
+ // ******************* CONFIG *******************
15433
+ const PREFERED_LANGUAGE="en";
15759
15434
 
15435
+ const NO_TRANSLATION_LABEL="NO TRANSLATION FOUND";
15760
15436
 
15761
- getPersister=function(dataDirPath,prefix=""){
15762
-
15763
- let self={
15764
-
15765
- dataDirPath:dataDirPath,
15766
- prefix:prefix,
15767
-
15768
- //FILE_NAME_PATTERN:"data.clientId.repositoryName.json",
15769
- /*private*/getPath:function(clientId="noclient", repositoryName="norepository"){
15770
- // let path=self.FILE_NAME_PATTERN.replace(new RegExp("@clientId@","g"),clientId);
15771
- let path=`${self.dataDirPath}`
15772
- + (blank(self.prefix)?"":(self.prefix+"."))
15773
- +`${clientId}.${repositoryName}.json`;
15774
- return path;
15775
- },
15776
-
15777
- readTreeObjectFromFile:function(clientId="noclient", repositoryName="norepository", keepClassName=false){
15778
-
15779
- let path=self.getPath(clientId,repositoryName);
15780
- let resultFlat=null;
15781
-
15782
- try{
15437
+ const ADD_SYSTEMATICALLY_TO_REPOSITORY=true;
15438
+ const MAX_CHARACTERS_FOR_TRANSLATION_KEY=64; // Really if you have more than 64 characters in commons, then it really should be the same string !
15783
15439
 
15784
- resultFlat=fs.readFileSync(path, FILE_ENCODING);
15785
-
15786
- }catch(error){
15787
- // TRACE
15788
- console.log("ERROR : Could not read file «"+path+"».");
15789
-
15790
- return null;
15791
- }
15792
-
15793
-
15794
- let resultData={};
15795
-
15796
- // 1)
15797
- if(!empty(resultFlat)) resultData=parseJSON(resultFlat);
15798
-
15799
- // 2)
15800
- if(!empty(resultData) && isFlatMap(resultData)){
15801
- // CAUTION : We have to keep the type information, here too ! (in the sub-objects)
15802
- resultData=getAsTreeStructure(resultData, keepClassName);
15803
- }
15804
-
15805
- return resultData;
15806
- },
15440
+ // ***************** END CONFIG *****************
15807
15441
 
15808
-
15809
- saveDataToFileForClient:function(clientId,repositoryName,dataFlatForClient,forceKeepUnflatten=false,doOnSuccess=null){
15810
-
15811
- if(!empty(dataFlatForClient) && !isFlatMap(dataFlatForClient) && !forceKeepUnflatten){
15812
- dataFlatForClient=getAsFlatStructure(dataFlatForClient);
15813
- }
15814
-
15815
- // reserved characters : -/\^$*+?.()|[]{}
15816
- // CANNOT USE stringifyObject(...) function because we are in a common, lower-level library !
15817
- let dataFlatStr=stringifyObject(dataFlatForClient)
15818
- // We «aerate» the produced JSON :
15819
- .replace(/":[\w]*\{/gim,"\":{\n").replace(/,"/gim,",\n\"")
15820
- // ...except for inline, escaped JSON string representations :
15821
- .replace(/\\\":[\w]*\{\n/gim,"\\\":{");
15822
- // NO : .replace(/}/gim,"}\n");
15823
-
15824
-
15825
- let path=self.getPath(clientId,repositoryName);
15826
- fs.writeFile(path, dataFlatStr, FILE_ENCODING, (error) => {
15827
- if(error){
15828
- // TRACE
15829
- console.log("ERROR : Could not write file «"+path+"»:",error);
15830
- throw error;
15831
- }
15832
- if(doOnSuccess) doOnSuccess(dataFlatForClient);
15833
- });
15834
- }
15835
-
15836
- };
15837
-
15838
-
15839
- return self;
15840
- };
15841
15442
 
15842
- }
15843
-
15844
-
15845
- window.fileExists=(filePath)=>{
15846
- if(typeof(fs)=="undefined"){
15443
+ if(!languagesAndStrings){
15847
15444
  // TRACE
15848
- lognow("ERROR : «fs» node dependency is not available ! Cannot test if file exists.");
15849
- return null;
15445
+ console.log("ERROR : No parameter for i18n provided!",languagesAndStrings);
15446
+ return NO_TRANSLATION_LABEL;
15850
15447
  }
15851
- return fs.existsSync(filePath);
15852
- };
15853
-
15854
-
15855
-
15856
- // Nodejs server launching helper functions :
15857
- //Networking management :
15858
- //- WEBSOCKETS AND NODEJS :
15859
-
15860
- function isConnected(clientSocket){
15861
- if(!WebsocketImplementation.useSocketIOImplementation)
15862
- return (clientSocket.readyState===WebSocket.OPEN)
15863
- return (clientSocket.connected);
15864
- }
15865
-
15866
-
15867
-
15868
- // -Server :
15869
-
15870
- getConsoleServerParams=function(portParam=null, certPathParam=null, keyPathParam=null, argsOffset=0, ignoreConsoleArgs=false){
15871
-
15872
- // Node dependencies :
15873
- // https=require("https");
15874
- // fs=require("fs");
15875
15448
 
15876
- if(typeof(https)==="undefined"){
15877
- // TRACE
15878
- console.log("WARN : Could not find the nodejs dependency «https», aborting SSL setup.");
15879
- return null;
15449
+ let lang=null;
15450
+ if(langParam){
15451
+ lang=langParam;
15452
+ }else{
15453
+ // Parameters priority :
15454
+ if(window){
15455
+ if(window.getURLParameter){
15456
+ lang=nonull(window.getURLParameter("language"), window.getURLParameter("lang"));
15457
+ }
15458
+ if(!lang && (window.language || window.lang)){
15459
+ lang=nonull(window.language, window.lang);
15460
+ }
15461
+ }else if(global){ // (NodeJS compatibility)
15462
+ if(global.getURLParameter){
15463
+ lang=nonull(global.getURLParameter("language"), global.getURLParameter("lang"));
15464
+ }
15465
+ if(!lang && (global.language || global.lang)){
15466
+ lang=nonull(global.language, global.lang);
15467
+ }
15468
+ }
15469
+ lang=nonull(lang, PREFERED_LANGUAGE);
15880
15470
  }
15881
- if(typeof(fs)==="undefined"){
15471
+
15472
+ if(contains(["capitalize","append"],lang)){
15882
15473
  // TRACE
15883
- console.log("WARN : Could not find the nodejs dependency «fs», aborting SSL setup.");
15884
- return null;
15474
+ console.log("WARN : Language parameter has for value a reserved attribute keyword, using default language «"+PREFERED_LANGUAGE+"»");
15475
+ lang=PREFERED_LANGUAGE;
15885
15476
  }
15886
-
15887
- const result={};
15888
15477
 
15889
- // We read the command-line arguments if needed :
15890
- const argCLPort=getConsoleParam(1,argsOffset);
15891
- const argCLCertPath=getConsoleParam(2,argsOffset);
15892
- const argCLKeyPath=getConsoleParam(3,argsOffset);
15893
15478
 
15894
- // Console, command-line arguments OVERRIDE parameters values :
15479
+ let result=null;
15895
15480
 
15896
- result.port=(ignoreConsoleArgs?portParam:nonull(argCLPort,portParam));
15897
- result.certPath=null;
15898
- result.keyPath=null;
15899
- result.isSecure=!!(certPathParam || keyPathParam);
15900
15481
 
15901
- if(result.isSecure){
15902
- result.certPath=(ignoreConsoleArgs?certPathParam:nonull(argCLCertPath,certPathParam));
15903
- result.keyPath=(ignoreConsoleArgs?keyPathParam:nonull(argCLKeyPath,keyPathParam));
15904
- }
15482
+ if(!isString(languagesAndStrings)){ // Case direct i18n translation, with no translation keys :
15483
+
15484
+ result=languagesAndStrings[lang];
15905
15485
 
15906
- // Eventual encryption options :
15907
- result.sslOptions=null;
15908
- if(result.isSecure){
15909
- result.sslOptions={
15910
- cert: fs.readFileSync(result.certPath),
15911
- key: fs.readFileSync(result.keyPath),
15912
- };
15913
- }
15486
+ if (!result){
15487
+ // TRACE
15488
+ console.log("ERROR : No label found for language «"+lang+"»!",languagesAndStrings);
15489
+ }
15914
15490
 
15915
- return result;
15916
- }
15917
-
15918
- getConsoleParam=function(index=0, argsOffset=0){
15919
- let result=null;
15920
- if(!process){
15921
- throw new Error("ERROR : Cannot extract console parameter in this context !");
15922
- }
15923
- process.argv.forEach((val, i)=>{
15924
- if(i<=argsOffset+1) return;
15925
- else if(i==argsOffset+index+1) result=val;
15926
- });
15927
- return result;
15928
- }
15929
-
15930
-
15931
-
15932
- window.getConsoleCLI=(doOnCommands={"makeSandiwch":()=>{}}, promptText="Enter command> ")=>{
15933
-
15934
- const readline = require("node:readline");
15935
-
15936
- const cliInterface = readline.createInterface({
15937
- input: process.stdin,
15938
- output: process.stdout,
15939
- prompt: nonoull(promptText,"Enter command> ")
15940
- });
15491
+ if (!result){
15492
+ for(key in languagesAndStrings){
15493
+ if (!languagesAndStrings.hasOwnProperty(key))
15494
+ continue;
15495
+ // We take the first that we find, in the other languages:
15496
+ result=languagesAndStrings[key];
15497
+ break;
15498
+ }
15499
+ }
15941
15500
 
15942
- cliInterface.prompt();
15943
-
15944
- cliInterface.on("line", (line) => {
15945
- const input = line.trim();
15946
- switch (input) {
15947
- case "quit":
15948
- console.log("Bye!");
15949
- cliInterface.close();
15950
- break;
15951
- case "help":
15952
- console.log("Available commands: quit, help and :", Object.keys(doOnCommands));
15953
- break;
15954
- default:
15955
- if(doOnCommands) doOnCommands[input]();
15956
- break;
15957
- }
15501
+ if (!result) return NO_TRANSLATION_LABEL;
15958
15502
 
15959
- // Show the prompt again :
15960
- cliInterface.prompt();
15961
15503
 
15962
- }).on("close", () => {
15963
- process.exit(0);
15964
- });
15965
-
15966
- return cliInterface
15967
- };
15968
-
15969
-
15504
+ // We add this «rogue» usage of i18n to the whole repository at all useful ends :
15505
+ if(ADD_SYSTEMATICALLY_TO_REPOSITORY){
15506
+ // let declarable={};
15507
+ let maxLength=Math.min(result.length, MAX_CHARACTERS_FOR_TRANSLATION_KEY);
15508
+ let key=toConvention("camel", result.substring(0,maxLength).toLowerCase() );
15509
+ // let calculatedKey=radicalKey+"_"+getUUID("short");
15510
+ // declarable[calculatedKey]=languagesAndStrings;
15511
+ i18n.declareSingleEntry(key, languagesAndStrings);
15512
+ }
15970
15513
 
15514
+ // Eventual post-treatments :
15515
+ if(languagesAndStrings.capitalize) result=capitalize(result);
15516
+ if(languagesAndStrings.append) result+=languagesAndStrings.append;
15971
15517
 
15518
+ return result;
15519
+ }
15520
+ // else // Case we use translation keys : (here, languagesAndStrings parameter holds just the wished translation key)
15972
15521
 
15973
- // NODE ONLY SERVER / CLIENTS :
15974
- WebsocketImplementation={
15522
+
15523
+ let repository=(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME];
15524
+ if(!repository){
15525
+ // TRACE
15526
+ console.log("ERROR : No repository variable found for repository «"+I18N_REPOSITORY_VARIABLE_NAME+"»!");
15527
+ return NO_TRANSLATION_LABEL;
15528
+ }
15529
+
15530
+ // OLD (ie. splitted format :)
15531
+ // let repositoryForLang=repository[lang];
15532
+ // if (!repositoryForLang){
15533
+ // // TRACE
15534
+ // console.log("ERROR : No translation found for language «"+lang+"»!",languagesAndStrings);
15535
+ // return NO_TRANSLATION_LABEL;
15536
+ // }
15537
+ // result=repositoryForLang[languagesAndStrings]; // Here, languagesAndStrings is just a bundle key.
15538
+ // if (!result) return NO_TRANSLATION_LABEL+" (for key «"+languagesAndStrings+"»)";
15539
+
15540
+ let languagesAndStringsForTranslationKey=repository[languagesAndStrings];
15541
+ if (!languagesAndStringsForTranslationKey){
15542
+ // SILENT ERROR :
15543
+ // // TRACE
15544
+ // console.log("ERROR : No translation found for key «"+languagesAndStrings+"» for language «"+lang+"»!");
15545
+ // NO, OLD : return NO_TRANSLATION_LABEL;
15546
+ return languagesAndStrings;
15547
+ }
15548
+ result=i18n(languagesAndStringsForTranslationKey, lang); // Since we use the same format !
15549
+
15550
+ return result;
15551
+ };
15975
15552
 
15976
15553
 
15977
- isNodeContext:true,
15978
- useSocketIOImplementation:false,
15979
- useFlatStrings:false,
15980
-
15981
- // COMMON METHODS
15982
- /*private static*/isInRoom(clientSocket, clientsRoomsTag){
15983
- return (!clientsRoomsTag || empty(clientsRoomsTag) || contains(clientsRoomsTag, clientSocket.clientRoomTag));
15984
- },
15985
-
15986
-
15987
- //
15988
- // CONSOLE NODE SERVER/CLIENT
15989
- //
15990
- getStatic:(isNodeContext=true, useSocketIOImplementation=/*DEBUG*/false)=>{
15991
-
15992
- WebsocketImplementation.isNodeContext=isNodeContext;
15993
- WebsocketImplementation.useSocketIOImplementation=useSocketIOImplementation;
15994
-
15995
- if(!WebsocketImplementation.useSocketIOImplementation){
15996
- // TRACE
15997
- lognow("INFO : (SERVER/CLIENT) Using native WebSocket implementation.");
15998
-
15999
- // https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
16000
- if(isNodeContext){
16001
- if(typeof(WebSocket)==="undefined"){
16002
- // TRACE
16003
- console.log("«ws» SERVER library not called yet, calling it now.");
16004
-
16005
- WebSocket=require("ws");
16006
- if(typeof(WebSocket)==="undefined"){
16007
- // TRACE
16008
- console.log("ERROR : «ws» CONSOLE/BROWSER CLIENT/SERVER library not found. Cannot launch nodejs server. Aborting.");
16009
- }
16010
- }
16011
- }
16012
-
16013
- }else{
16014
- // TRACE
16015
- lognow("INFO : (SERVER/CLIENT) Using socket.io websocket implementation.");
16016
-
16017
- if(isNodeContext){
16018
-
16019
- // NODE SERVER :
16020
- // DBG
16021
- lognow("INFO : Loading NODE SERVER socket.io libraries.")
16022
- if(typeof(Socket)==="undefined"){
16023
- // TRACE
16024
- console.log("«socket.io» NODE SERVER library not called yet, calling it now.");
16025
- Socket=require("socket.io");
16026
- }
16027
- if(typeof(Socket)==="undefined"){
16028
- // TRACE
16029
- console.log("ERROR : «socket.io» NODE CLIENT/SERVER library not found. Cannot launch nodejs server. Aborting.");
16030
- }
16031
-
16032
- // NODE CLIENT :
16033
- // DBG
16034
- lognow("INFO : Loading NODE CLIENT socket.io libraries.")
16035
- if(typeof(io)==="undefined"){
16036
- // TRACE
16037
- console.log("«socket.io-client» NODE CLIENT library not called yet, calling it now.");
16038
- io=require("socket.io-client");
16039
- }
16040
- if(typeof(io)==="undefined"){
16041
- // TRACE
16042
- console.log("ERROR : «socket-client.io» NODE CLIENT library not found. Cannot launch nodejs server. Aborting.");
16043
- }
16044
- }
16045
-
16046
-
16047
- }
16048
-
16049
- // *********************************************************************************
16050
-
16051
- return WebsocketImplementation;
16052
- },
16053
-
16054
-
16055
- /*private*/getMessageDataBothImplementations:(eventOrMessageParam)=>{
16056
-
16057
- const eventOrMessage=(!WebsocketImplementation.useSocketIOImplementation?eventOrMessageParam.data:eventOrMessageParam);
16058
-
16059
- let dataResult=eventOrMessage;
16060
-
16061
- try{
16062
- dataResult=(WebsocketImplementation.useFlatStrings || isString(eventOrMessage)?parseJSON(eventOrMessage):eventOrMessage);
16063
- }catch(error1){
16064
- // TRACE
16065
- lognow(`ERROR : Failed to parse JSON for string «${dataResult}»`,error1);
16066
- dataResult=(isString(eventOrMessage)?eventOrMessage:stringifyObject(eventOrMessage));
16067
- }
16068
-
16069
- return dataResult;
16070
- },
16071
-
16072
-
16073
- getServer:(listenableServer)=>{
16074
-
16075
- if(!WebsocketImplementation.isNodeContext){
16076
- // TRACE
16077
- throw new Error("ERROR : SERVER : Server launch is not supported in a non-nodejs context for any implementation.");
16078
- }
16079
-
16080
-
16081
- // TODO : FIXME : Use one single interface !
16082
- // NODE SERVER MODE ONLY :
16083
- let serverSocket;
16084
- if(!WebsocketImplementation.useSocketIOImplementation){
16085
- serverSocket=new WebSocket.Server({ "server":listenableServer });
16086
- }else{
16087
-
16088
- // NOW : socket.io :
16089
- // Loading socket.io
16090
- // OLD SYNTAX : serverSocket=Socket.listen(listenableServer);
16091
- serverSocket=new Socket.Server(listenableServer);
16092
-
16093
- if(listenableServer.cert || listenableServer.key){
16094
- // TRACE :
16095
- lognow("WARN : CAUTION ! ON TODAY (01/08/2022) socket.io SERVER LIBRARY IS BOGUS AND WON'T WORK (node clients can't connect !!) IF listenableServer IS A https SERVER !!!");
16096
- }
16097
-
16098
- // Setting up the disconnect event for a client :
16099
- serverSocket.on("endConnection",()=>{
16100
- serverSocket.disconnect();
16101
- });
16102
- }
16103
-
16104
- serverSocket.on("error",(error)=>{
16105
- // TRACE
16106
- lognow("ERROR : An error occurred when trying to start the server : ",error);
16107
-
16108
- });
16109
-
16110
- // NODE SERVER INSTANCE :
16111
- const nodeServerInstance=new NodeServerInstance(serverSocket, listenableServer);
16112
-
16113
- // Join room server part protocol :
16114
- nodeServerInstance.receive("protocol",(message, clientSocket)=>{
16115
- nodeServerInstance.addToRoom(clientSocket, message.clientRoomTag);
16116
- },{listenerMessageType:"joinRoom"});
16117
-
16118
- // To make the server aware of the clients connections states :
16119
- nodeServerInstance.serverSocket.on("close", function close() {
16120
-
16121
- // TODO : FIXME : Use one single interface !
16122
- if(!WebsocketImplementation.useSocketIOImplementation) serverClients=nodeServerInstance.serverSocket.clients;
16123
- // OLD : else serverClients=nodeServerInstance.serverSocket.sockets.clients();
16124
- else serverClients=nodeServerInstance.serverSocket.sockets.sockets;
16125
-
16126
- serverClients.forEach((clientSocket)=>{
16127
- clearInterval(clientSocket.stateCheckInterval);
16128
- });
16129
- });
16130
-
16131
- return nodeServerInstance;
16132
- },
16133
-
16134
- // DO NOT USE DIRECTLY, USE INSTEAD initClient(...) (this function uses connectToServer(...)) !
16135
- // NODE / BROWSER CLIENT CONNECTS TO SERVER MAIN ENTRYPOINT:
16136
- connectToServer:(serverURL, port, isSecure=false, timeout)=>{
16137
-
16138
- // TRACE
16139
- lognow("INFO : Using socket library flavor : "+(WebsocketImplementation.isNodeContext?"node (client/server-side)":"browser (client-side only)"));
16140
-
16141
- if(WebsocketImplementation.isNodeContext)
16142
- return WebsocketImplementation.connectToServerFromNode(serverURL, port, isSecure, timeout);
16143
- else
16144
- return WebsocketImplementation.connectToServerFromBrowser(serverURL, port, isSecure, timeout);
16145
- },
16146
-
16147
-
16148
- //
16149
- // NODE CLIENT
16150
- //
16151
- /*private*/connectToServerFromNode:(serverURL, port, isSecure, timeout)=>{
16152
-
16153
- // NODE CLIENT MODE ONLY :
16154
- let clientSocket;
16155
- if(!WebsocketImplementation.useSocketIOImplementation){
16156
-
16157
- // NEW : ws :
16158
- if(typeof(WebSocket)==="undefined"){
16159
- // TRACE
16160
- lognow("ERROR : CLIENT : Could not find websocket client lib, aborting client connection.");
16161
- return null;
16162
- }
16163
-
16164
- clientSocket=new WebSocket(serverURL+":"+port,/*WORKAROUND:*/{
16165
- // CAUTION : SECURITY BREACH :
16166
- // BUT ALSO NECESSARY TO ALLOW SELF-SIGNED CERTIFICATES USAGE WITH THE YESBOT SYSTEM !
16167
- rejectUnauthorized:false, // (THIS IS A KNOWN SECURITY BREACH)
16168
- secure: isSecure
16169
- });
16170
-
16171
- clientSocket.addEventListener("error", error=>{
16172
- // TRACE
16173
- lognow("ERROR : (NODEJS) A WebSocket client error occurred while trying to connect to server:", error.message);
16174
- });
16175
-
16176
- }else{
16177
- // NOW : socket.io :
16178
- //client on server-side:
16179
-
16180
- if(WebsocketImplementation.isNodeContext && typeof(io)!=="undefined"){
16181
-
16182
- // OLD SYNTAX: clientSocket=Socket.connect(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
16183
- // NO : clientSocket=new Socket.Client(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
16184
- clientSocket=io(serverURL + ":" + port,{timeout: timeout, secure: isSecure, autoConnect:true});
16185
- // UNUSEFUL (since we have the autoconnect:true option) : clientSocket.connect();
16186
-
16187
- clientSocket.on("connect_error", error=>{
16188
- // TRACE
16189
- lognow("ERROR : (NODEJS) A SocketIO client error occurred while trying to connect to server:", error.message);
16190
- });
16191
-
16192
- }
16193
- }
16194
-
16195
- // DBG
16196
- lognow("DEBUG : CLIENT : clientSocket created:");
16197
-
16198
- const nodeClientInstance=new ClientInstance(clientSocket);
16199
- return nodeClientInstance;
16200
- },
16201
-
16202
- //
16203
- // BROWSER CLIENT
16204
- //
16205
-
16206
- /*private*/connectToServerFromBrowser:(serverURL, port, isSecure, timeout)=>{
16207
-
16208
- // NEW : ws :
16209
- if(typeof(WebSocket)==="undefined"){
16210
- // TRACE
16211
- lognow("ERROR : CLIENT : Could not find websocket client lib, aborting client connection.");
16212
- return null;
16213
- }
16214
-
16215
- // TODO : FIXME : Use one single interface !
16216
- // BROWSER CLIENT MODE ONLY :
16217
- let clientSocket;
16218
- if(!WebsocketImplementation.useSocketIOImplementation){
16219
- // CAUTION : PARAMETER rejectUnauthorized:false WILL DO NOTHING,
16220
- // BECAUSE THIS IS COMPLETLY HANDLED BY THE BROWSER SECURITY POLICY !
16221
- // SO TO CLEAR THE SSL ERROR :
16222
- // - FIRST GO TO THE HTTPS:// SERVER ADDRESS WITH BROWSER
16223
- // - THEN ADD THE SECURITY EXCEPTION IN THE BROWSER !
16224
- clientSocket=new WebSocket(serverURL+":"+port,["ws","wss"]);
16225
-
16226
- clientSocket.addEventListener("error", error=>{
16227
- // TRACE
16228
- lognow("ERROR : (BROWSER) A WebSocket client error occurred while trying to connect to server:", error.message);
16229
- });
16230
-
16231
- }else if(typeof(io)!=="undefined"){
16232
- // OLD SYNTAX :clientSocket=io.connect(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
16233
- // ALTERNATIVE :
16234
- clientSocket=io(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
16235
-
16236
- clientSocket.on("connect_error", error=>{
16237
- // TRACE
16238
- lognow("ERROR : (BROWSER) A SocketIO client error occurred while trying to connect to server:", error.message);
16239
- });
16240
-
16241
- }
16242
-
16243
- // BROWSER CLIENT INSTANCE :
16244
- const browserClientInstance=new ClientInstance(clientSocket);
16245
- return browserClientInstance;
16246
- },
16247
-
16248
- };
16249
-
16250
-
16251
-
16252
- launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /*OPTIONAL*/sslOptions=null, httpHandlerParam=null, addCORSHeader=ADD_CORS_HEADER){
16253
-
16254
- const EXCLUDED_FILENAMES_PARTS=[".keyHash.",".pem"];
16255
-
16256
-
16257
-
16258
- if(typeof(https)==="undefined"){
16259
- // TRACE
16260
- console.log("«https» SERVER library not called yet, calling it now.");
16261
- https=require("https");
16262
- }
16263
- if(typeof(http)==="undefined"){
16264
- // TRACE
16265
- console.log("«http» SERVER library not called yet, calling it now.");
16266
- http=require("http");
16267
- }
16268
-
16269
-
16270
- const DEFAULT_HANDLER=function(request, response){
16271
-
16272
- const url=request.url;
16273
-
16274
- let isURLInExclusionZone=!!foreach(EXCLUDED_FILENAMES_PARTS,(excludedStr)=>{
16275
- if(contains(url,excludedStr)){
16276
- return true;
16277
- }
16278
- });
16279
-
16280
- if(isURLInExclusionZone){
16281
- // TRACE
16282
- console.log("ERROR 403 forbidden access error :");
16283
- console.log(error);
16284
-
16285
- response.writeHead(403);
16286
- response.end("Sorry, cannot access resource : error: "+error.code+" ..\n");
16287
- response.end();
16288
- return;
16289
- }
16290
-
16291
-
16292
-
16293
- const urlFile="." + url;
16294
-
16295
- let filePath=urlFile.indexOf("?")!==-1?urlFile.split("?")[0]:urlFile;
16296
- if(filePath=="./") filePath="./index.html";
16297
-
16298
- let contentType="text/html";
16299
-
16300
- const headers={ "Content-Type": contentType };
16301
-
16302
- // To remove the CORS error message (cf. https://medium.com/@dtkatz/3-ways-to-fix-the-cors-error-and-how-access-control-allow-origin-works-d97d55946d9)
16303
- if(addCORSHeader) headers["Access-Control-Allow-Origin"]="*";
16304
-
16305
-
16306
- fs.readFile(filePath, function(error, fileContent){
16307
- if(error){
16308
- if(error.code=="ENOENT"){
16309
- // TRACE
16310
- console.log("ERROR 404 file not found :"+filePath);
16311
-
16312
- fs.readFile("./404.html", function(error, fileContent){
16313
- response.writeHead(200, headers);
16314
- response.end(fileContent, "utf-8");
16315
- });
16316
-
16317
- }else {
16318
-
16319
- // TRACE
16320
- console.log("ERROR 500 server error :");
16321
- console.log(error);
16322
-
16323
- response.writeHead(500);
16324
- response.end("Sorry, check with the site admin for error: "+error.code+" ..\n");
16325
- response.end();
16326
-
16327
- }
16328
- }else {
16329
-
16330
- // TRACE
16331
- console.log("INFO 200 OK :"+filePath);
16332
-
16333
-
16334
- response.writeHead(200, headers);
16335
- response.end(fileContent, "utf-8");
16336
-
16337
- // res.writeHead(200);
16338
- // res.end("hello world\n");
16339
- // res.sendFile(__dirname + "/public/index.html");
16340
-
16341
- }
16342
- });
16343
-
16344
- };
16345
-
16346
-
16347
- const handler=nonull(httpHandlerParam, DEFAULT_HANDLER);
16348
-
16349
-
16350
-
16351
- let listenableServer;
16352
- if(sslOptions){
16353
- let httpsServer=https.createServer(sslOptions, handler).listen(port);
16354
- // TRACE
16355
- console.log("INFO : SERVER : HTTPS Server launched and listening on port " + port + "!");
16356
- listenableServer=httpsServer;
16357
- }else{
16358
- let httpServer=http.createServer(handler).listen(port);
15554
+ i18n.declareSingleEntry=function(key, languagesAndStringsEntry){
15555
+ let newRepository;
15556
+ let foundRepository=(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME];
15557
+ if(!foundRepository){
16359
15558
  // TRACE
16360
- console.log("INFO : SERVER : HTTP Server launched and listening on port " + port + "!");
16361
- listenableServer=httpServer;
16362
- }
16363
-
16364
-
16365
- const server=WebsocketImplementation.getStatic(true).getServer(listenableServer);
16366
-
16367
- // When a client connects, we execute the callback :
16368
- // CAUTION : MUST BE CALLED ONLY ONCE !
16369
- server.onConnectionToClient((serverParam, clientSocketParam)=>{
16370
- if(doOnConnect) doOnConnect(serverParam, clientSocketParam);
16371
- });
16372
-
16373
-
16374
- server.onFinalize((serverParam)=>{
16375
-
16376
- // DBG
16377
- lognow("onFinalize() server");
16378
-
16379
- if(doOnFinalizeServer) doOnFinalizeServer(serverParam);
16380
- });
16381
-
16382
-
16383
- // TRACE
16384
- console.log("INFO : SERVER : Generic Nodejs server launched and listening on port:" + port + "!");
16385
-
16386
-
16387
-
16388
-
16389
-
16390
- return server;
16391
- }
16392
-
16393
-
16394
- initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFinalizeServer=null, /*OPTIONAL*/portParam, /*OPTIONAL*/certPathParam, /*OPTIONAL*/keyPathParam){
16395
-
16396
- // TRACE
16397
- console.log("Server launched.");
16398
- console.log("Usage : node <server.js> conf {port:[port], sslCertPath:[ssl certificate path | unsecure ], sslKeyPath:[ssl key path], serverConfig:[JSON server configuration]}");
16399
- console.log("Or (to generate password hash) : node <server.js> hash <clientId@repositoryName> <clearTextSecretString>");
16400
- // EXAMPLE : node orita-srv.js hash orita.global@keyHash 1234567890
16401
- console.log("Server launched.");
16402
-
16403
-
16404
- // We read the command-line arguments if needed :
16405
-
16406
- let argCLPort;
16407
- let argCLCertPath;
16408
- let argCLKeyPath;
16409
-
16410
- let serverConfig={};
16411
- let isForceUnsecure;
16412
-
16413
- let isHashAsked=false;
16414
- let clearTextParam=null;
16415
- let persisterId=null;
16416
-
16417
-
16418
- process.argv.forEach(function (val, i){
16419
- if(!val) return;
16420
- // 0 corresponds to «node / nodejs»
16421
- if(i<=1) return; // 1 corresponds to « <server.js> »
16422
- else if(i==2){
16423
- if(val==="hash") isHashAsked=true;
16424
- }else if(i==3){
16425
- if(!isHashAsked){
16426
- try{
16427
- const jsonConf=parseJSON(val);
16428
- argCLPort=jsonConf.port;
16429
- argCLCertPath=jsonConf.sslCertPath;
16430
- argCLKeyPath=jsonConf.sslKeyPath;
16431
- serverConfig=nonull(jsonConf.serverConfig,{});
16432
- }catch(err1){
16433
- lognow("ERROR : Cannot parse argument JSON string «"+val+"».",err1);
16434
- }
16435
- } else persisterId=val;
16436
- }else if(i==4){
16437
- if(isHashAsked) clearTextParam=val;
16438
- }
16439
- });
16440
- isForceUnsecure=(argCLCertPath==="unsecure");
16441
-
16442
-
16443
-
16444
-
16445
-
16446
- const aotraNodeServer={config:serverConfig};
16447
- aotraNodeServer.serverManager={ start:()=>{/*DEFAULT START FUNCTION, WILL BE OVERRIDEN LATER*/}};
16448
-
16449
- if(isHashAsked){
16450
- // We instanciate a temporary persister just to read the key hash file:
16451
- const persister=getPersister("./");
16452
- let persisterIdSplits=persisterId.split("@");
16453
- if(empty(persisterIdSplits) || persisterIdSplits.length!=2){
16454
- // TRACE
16455
- console.log("ERROR : No persister repository IDs provided correctly. Cannot read key hash. Aborting hash generation.");
16456
- return aotraNodeServer;
16457
- }
16458
- const persisterClientId=persisterIdSplits[0];
16459
- const persisterRepositoryName=persisterIdSplits[1];
16460
- let globalKeyHashObject=persister.readTreeObjectFromFile(persisterClientId, persisterRepositoryName);
16461
- if(!globalKeyHashObject || !globalKeyHashObject.keyHash){
16462
- // TRACE
16463
- console.log("WARN : No key hash found. Generating one now.");
16464
- globalKeyHashObject={keyHash:getUUID(), hashes:[]};
16465
- persister.saveDataToFileForClient(persisterClientId,persisterRepositoryName,globalKeyHashObject,false,()=>{
16466
- // TRACE
16467
- console.log("INFO : Key hash generated and saved successfully.");
16468
- });
16469
- }
16470
- const globalKeyHash=globalKeyHashObject.keyHash;
16471
-
16472
- let firstHash=getHashedString(clearTextParam);
16473
-
16474
- let generatedHash=getHashedString( firstHash + globalKeyHash, "SHA-256", true);// (we use the heavy treatment thing.)
16475
- globalKeyHashObject.hashes.push(generatedHash);
16476
-
16477
- // We update the repository :
16478
- persister.saveDataToFileForClient(persisterClientId,persisterRepositoryName,globalKeyHashObject,false,()=>{
16479
- // TRACE
16480
- console.log("INFO : Hash added to repository and saved successfully.");
16481
- });
16482
-
16483
- // OUTPUT
16484
- console.log("Here is your key : share it with your main clients but DO NOT LEAK IT !\n********************\n"+clearTextParam+"\n********************\n");
16485
-
16486
- return aotraNodeServer;
16487
- }
16488
-
16489
-
16490
- const DEFAULT_PORT=nonull(argCLPort,25000);
16491
- const DEFAULT_CERT_PATH=nonull(argCLCertPath,"cert.pem");
16492
- const DEFAULT_KEY_PATH=nonull(argCLKeyPath,"key.key");
16493
-
16494
-
16495
- let port=portParam ? portParam : DEFAULT_PORT;
16496
- let certPath=null;
16497
- let keyPath=null;
16498
-
16499
- if(!isForceUnsecure){
16500
- certPath=certPathParam?certPathParam:DEFAULT_CERT_PATH;
16501
- keyPath=keyPathParam?keyPathParam:DEFAULT_KEY_PATH;
16502
- }
16503
-
16504
-
16505
- // UNUSEFUL :
16506
- //aotraNodeServer.serverManager.microClientsSockets=[];
16507
- // UNUSEFUL :
16508
- //aotraNodeServer.serverManager.mainClientsSockets=[];
16509
-
16510
- aotraNodeServer.serverManager.start=function(){
16511
-
16512
- // Eventual encryption options :
16513
- let sslOptions=null;
16514
- if(!isForceUnsecure){
16515
- if(typeof(fs)==="undefined"){
16516
- // TRACE
16517
- lognow("ERROR : «fs» node subsystem not present, cannot access files. Aborting SSL configuration of server.");
16518
- }else{
16519
- try{
16520
- sslOptions={
16521
- cert: fs.readFileSync(certPath, {encoding: "utf8"}),
16522
- key: fs.readFileSync(keyPath, {encoding: "utf8"}),
16523
- };
16524
- }catch(exception){
16525
- // TRACE
16526
- lognow("ERROR : Could not open SSL files certPath:«"+certPath+"» or keyPath:«"+keyPath+"». Aborting SSL configuration of server.");
16527
- }
16528
- }
16529
- }
16530
-
16531
- aotraNodeServer.server=launchNodeHTTPServer(port, doOnClientConnection, doOnFinalizeServer, sslOptions);
16532
-
16533
-
16534
- return aotraNodeServer;
16535
- };
16536
-
16537
- return aotraNodeServer;
16538
- }
16539
-
16540
- // ========================= FUSRODA SERVER : =========================
16541
-
16542
- //
16543
- // DOES NOT WORK : USE Java FusrodaServer instead :
16544
- //
16545
- ///*FUSRODA server stands from FSRD SERVER, for Fucking Simple Remote Desktop SERVER*/
16546
- //createFusrodaServer=function(certPathParam=null,keyPathParam=null,portParam=4000){
16547
- //
16548
- // // https://www.npmjs.com/package/screenshot-desktop
16549
- // // https://github.com/octalmage/robotjs
16550
- // // npm install --save screenshot-desktop
16551
- // //
16552
- // // sudo apt-get install libxtst-dev libx11-dev
16553
- // // npm install --save robotjs
16554
- //
16555
- // // http://getrobot.net/docs/usage.html
16556
- // //
16557
- // // apt-get install build-essential python libxt-dev libxtst-dev libxinerama-dev -y
16558
- //
16559
- // const screenshot=require("screenshot-desktop");
16560
- //
16561
- // const REFRESH_SCREENSHOTS_MILLIS=500;
16562
- //
16563
- //
16564
- // const server=initNodeServerInfrastructureWrapper(
16565
- // // On each client connection :
16566
- // // (serverParam, clientSocketParam)=>{},
16567
- // null,
16568
- // // On server finalization :
16569
- // (serverParam)=>{
16570
- //
16571
- // serverParam.receive("protocol_fusroda", (message, clientSocket)=> {
16572
- // serverParam.addToRoom(clientSocket,"clients");
16573
- // });
16574
- //
16575
- //
16576
- // serverParam.sendScreenshotsRoutine=setInterval(()=>{
16577
- // if(serverParam.isScreenshotStarted) return;
16578
- //
16579
- // serverParam.isScreenshotStarted=true;
16580
- //
16581
- // screenshot().then((img) => {
16582
- //
16583
- // const data={image:img,listenerMessageType:"imageData"};
16584
- // serverParam.send("message", data, "clients");
16585
- //
16586
- // serverParam.isScreenshotStarted=false;
16587
- //
16588
- // }).catch((error) => {
16589
- // // TRACE
16590
- // lognow("ERROR : Error during screenshot :",error);
16591
- // });
16592
- //
16593
- // },REFRESH_SCREENSHOTS_MILLIS);
16594
- //
16595
- //
16596
- // },portParam,certPathParam,keyPathParam);
16597
- //
16598
- //
16599
- // // const doOnConnect=(serverParam, clientSocketParam)=>{
16600
- // // };
16601
- // // const doOnFinalizeServer=(serverParam)=>{
16602
- // // /*DO NOTHING*/
16603
- // // };
16604
- // // const server={};
16605
- // // server.start=(port=4000)=>{
16606
- // // server.httpServer=launchNodeHTTPServer(port, doOnConnect, doOnFinalizeServer, sslOptions);
16607
- // // };
16608
- //
16609
- // return server;
16610
- //}
16611
-
16612
-
16613
- // ========================= UTILITY SERVERSIDE METHODS : =========================
16614
-
16615
- class ListManager{
16616
-
16617
- constructor(config){
16618
- this.config=config;
16619
- this.maxItemsNumber=nonull(this.config.max,999);
16620
- this.simultaneousItemsNumber=nonull(this.config.simultaneous,1);
16621
- this.sessionDurationSeconds=nonull(this.config.duration,null);
16622
- this.mode=nonull(this.config.mode,"startAtConnexion");
16623
-
16624
- this.itemsInfos={};
16625
- this.time=null;
16626
- this.started=false;
16627
- }
16628
-
16629
- addItem(id,item){
16630
- if(id==null){
16631
- // TRACE
16632
- lognow("ERROR : Cannot add item with no id.");
16633
- return;
16634
- }
16635
- if(!item){
16636
- // TRACE
16637
- lognow("ERROR : Cannot add null item.");
16638
- return;
16639
- }
16640
- const numberOfItemsCurrently=getArraySize(this.itemsInfos);
16641
-
16642
- // DBG
16643
- lognow(">>>>>>>>>numberOfItemsCurrently:",numberOfItemsCurrently);
16644
- lognow(">>>>>>>>>Object.keys(arrayOfValues):",Object.keys(this.itemsInfos));
16645
-
16646
-
16647
- if(this.maxItemsNumber<=numberOfItemsCurrently){
16648
- // TRACE
16649
- lognow("ERROR : Cannot add item with id «"+id+"», list already full.");
16650
- return;
16651
- }
16652
-
16653
-
16654
- if(numberOfItemsCurrently==0 && this.mode==="startAtConnexion"){
16655
-
16656
- // DBG
16657
- lognow(">>>>>>>>>START SESSION !!!!");
16658
-
16659
- this.startSession();
16660
- }
16661
- this.itemsInfos[id]={
16662
- item:item,
16663
- time:getNow(),
16664
- };
16665
- }
16666
-
16667
- startSession(){
16668
- this.time=getNow();
16669
- this.started=true;
15559
+ console.log("ERROR : No repository variable found for repository «"+I18N_REPOSITORY_VARIABLE_NAME+"»!");
15560
+ return;
16670
15561
  }
16671
15562
 
16672
- stopSession(){
16673
- this.started=false;
15563
+ let foundEntryForKey=foundRepository[key];
15564
+ if(!foundEntryForKey){
15565
+ foundRepository[key]=languagesAndStringsEntry;
15566
+ // }else{
15567
+ // // TRACE
15568
+ // console.log("WARN : Duplicate entry for translation key «"+key+"», keeping only the first declared :",foundEntryForKey);
16674
15569
  }
16675
15570
 
16676
- isSessionActive(){
16677
-
16678
- // DBG
16679
- lognow(" !!! this.sessionDurationSeconds : "+this.sessionDurationSeconds);
16680
- lognow(" !!! this.started : "+this.started);
16681
- lognow(" !!! this.time : "+this.time);
15571
+ };
16682
15572
 
16683
-
16684
- if(!this.sessionDurationSeconds) return true;
16685
- if(!this.started) return false;
16686
- const result=!hasDelayPassed(this.time, this.sessionDurationSeconds*1000);
16687
-
16688
- // DBG
16689
- lognow(" !!! HAS DELAY PASSED : "+result);
16690
-
16691
- return result;
16692
- }
16693
-
16694
- isClientActive(clientId){
16695
-
16696
- // DBG
16697
- lognow(" this.isSessionActive()"+this.isSessionActive());
16698
-
16699
- if(!this.isSessionActive()) return false;
16700
- const clientPosition=this.getItemPosition(clientId);
16701
- //CAUTION : Client position starts at 1 !
16702
- const clientIndex=(clientPosition-1);
16703
- const result=(clientIndex<=this.maxItemsNumber && clientIndex<=this.simultaneousItemsNumber);
16704
-
16705
- return result;
16706
- }
16707
-
16708
- removeItemById(id){
16709
- if(id==null){
16710
- // TRACE
16711
- lognow("ERROR : Cannot remove item, no id specified.");
16712
- return;
16713
- }
16714
- if(!this.itemsInfos[id]){
16715
- // TRACE
16716
- lognow("ERROR : Cannot remove item, item not found for id «"+id+"».");
16717
- return;
16718
- }
16719
- delete this.itemsInfos[id];
15573
+
15574
+ //USAGE :
15575
+ //Example :
15576
+ //i18n.declare({
15577
+ // "commencer":{"fr":"Commencer","en":"Start"}
15578
+ //});
15579
+ i18n.declare=function(repositoryInfos){
15580
+ let newRepository;
15581
+ let foundRepository=(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME];
15582
+ if(foundRepository){
15583
+ // // TRACE
15584
+ // console.log("WARN : A repository has already been declared, merging.",foundRepository);
15585
+ newRepository=merge(foundRepository, repositoryInfos);
15586
+ }else{
15587
+ newRepository=repositoryInfos;
16720
15588
  }
15589
+ (window?window:global)[I18N_REPOSITORY_VARIABLE_NAME]=newRepository;
15590
+ };
15591
+
15592
+ // USAGE :
15593
+ // Example :
15594
+ //i18n.declareSplitted({
15595
+ // "fr":{
15596
+ // "commencer":"Commencer",
15597
+ // },
15598
+ // "en":{
15599
+ // "commencer":"Start",
15600
+ // },
15601
+ //});
15602
+ i18n.declareSplitted=function(repositoryInfos){
15603
+ let nonSplittedFormat={};
16721
15604
 
16722
- removeItem(item){
16723
- if(item==null){
16724
- // TRACE
16725
- lognow("ERROR : Cannot remove item, none specified.");
16726
- return;
16727
- }
16728
-
16729
- let id=null;
16730
- foreach(this.itemsInfos,(itemInfos,key)=>{
16731
- if(itemInfos.item===item
16732
- // DEBUG ONLY :
16733
- || (itemInfos.item.id && item.id && itemInfos.item.id===item.id)
16734
- ){
16735
- id=key;
16736
- return "break";
15605
+ foreach(repositoryInfos,(allKeys, lang)=>{
15606
+ foreach(allKeys,(text, key)=>{
15607
+
15608
+ let foundKeyInfos=nonSplittedFormat[key];
15609
+ if(!foundKeyInfos){
15610
+ nonSplittedFormat[key]={};
15611
+ foundKeyInfos=nonSplittedFormat[key];
15612
+ }
15613
+
15614
+ let foundTextForLanguage=foundKeyInfos[lang];
15615
+ if(foundTextForLanguage){
15616
+ // TRACE
15617
+ console.log("WARN : Duplicate entry for translation key «"+key+"» for language «"+lang+"», keeping only the first declared :",foundTextForLanguage);
15618
+ }else{
15619
+ foundKeyInfos[lang]=text;
16737
15620
  }
16738
15621
 
16739
15622
  });
15623
+ });
15624
+
15625
+ // Then we use the standard declaration method :
15626
+ i18n.declare(nonSplittedFormat);
15627
+ };
15628
+
15629
+ // For generating languages files helping purposes :
15630
+ i18n.displayAll=function(displaySingleEntriesCalls=false){
15631
+ let foundRepository=(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME];
15632
+ if(!foundRepository){
15633
+ // TRACE
15634
+ console.log("ERROR : No repository found under the variable name «"+I18N_REPOSITORY_VARIABLE_NAME+"»");
15635
+ }else{
15636
+ console.log("TRACE : Variable under the name of «"+I18N_REPOSITORY_VARIABLE_NAME+"»:", foundRepository);
15637
+ console.log("\n\n");
15638
+
15639
+ let message="";
15640
+ foreach(foundRepository, (entry, entryKey)=>{
15641
+ let normalizedEntry={};
15642
+ if(entry.capitalize) normalizedEntry.capitalize=entry.capitalize;
15643
+ if(entry.append) normalizedEntry.append=entry.append;
15644
+ foreach(entry,(e,k)=>{
15645
+ normalizedEntry[k]=e;
15646
+ }
15647
+ ,(i,key)=>{ return (!contains(["append","capitalize"],key)); }
15648
+ ,(i1,i2)=>{ return (i1.key==i2.key?0:(i1.key=="fr"?-1:1));/*(Sorry, French always comes first !)*/}
15649
+ );
15650
+ let entryStr=stringifyObject(normalizedEntry);
15651
+ message+="\""+entryKey+"\":"+entryStr+",\n";
15652
+ if(displaySingleEntriesCalls) message+="// i18n("+entryStr+");\n";
15653
+ });
16740
15654
 
16741
- if(!id){
16742
- // TRACE
16743
- lognow("ERROR : Cannot remove item, item not found for id «"+id+"».");
16744
- return;
16745
- }
15655
+ console.log("\n"+message);
16746
15656
 
16747
- this.removeItemById(id);
16748
15657
  }
15658
+ }
16749
15659
 
16750
- // Goes from 1 to <length>:
16751
- getItemPosition(id){
16752
- if(id==null){
16753
- // TRACE
16754
- lognow("ERROR : Cannot calculate item position, no id specified.");
16755
- return null;
16756
- }
16757
- if(!this.itemsInfos[id]){
16758
- // TRACE
16759
- lognow("ERROR : Cannot calculate item position, item not found for id «"+id+"».");
16760
- return null;
16761
- }
16762
- let result=0;
16763
- foreach(this.itemsInfos, (itemInfo, key)=>{
16764
- result++;
16765
- if(id===key) return "break";
16766
- },null,(item1, item2)=>item1.time<item2.time);
16767
- return result;
16768
- }
16769
15660
 
16770
- getItemsNumber(){
16771
- return getArraySize(this.itemsInfos);
16772
- }
16773
-
16774
- };
16775
15661
 
16776
- getListManager=function(config){
16777
- return new ListManager(config);
16778
- };
16779
15662
 
16780
15663
 
16781
- // CAUTION ! TODO : FIXME : We replace in response all single quotes with "`" (egrave) in order to avoid errors !
16782
- // NO : IN A NODE CONTEXT WITH require("...") WILL RESULT IN AN UNDEFINED FUNCTION ERROR !!!
16783
- // function performHTTPRequest(...){...
16784
- // USE THIS INSTEAD :
16785
- performHTTPRequest=function(completeURL, httpMethod="GET", headers={}, requestBodyOrNamedArgs=null, isNodeContext=false, addCORSHeader=ADD_CORS_HEADER){
15664
+ function isCharacterToSkip(c){
15665
+ return RegExp("\\s").test(c) || contains([",","\'","´","\"",":","-","(",")","[","]","<",">","!","?","%",".","/","=",],c);
15666
+ }
16786
15667
 
16787
- // Body
16788
- let requestBodyOrNamedArgsStr=null;
16789
- if(requestBodyOrNamedArgs){
16790
- if(isString(requestBodyOrNamedArgs) && !isObject(requestBodyOrNamedArgs)){
16791
- // If it's already a string, we assume it's a valid JSOn string already :
16792
- requestBodyOrNamedArgsStr=requestBodyOrNamedArgs.replace(/[\r\n]+/gim, ""); // We remove all the breaklines
15668
+ function snakize(strParam, separator="_"){
15669
+ let result="";
15670
+ for(let i=0;i<strParam.length;i++){
15671
+ let c=strParam[i];
15672
+ if(isCharacterToSkip(c)){
15673
+ result+=separator;
16793
15674
  }else{
16794
- try{
16795
- requestBodyOrNamedArgsStr=stringifyObject(requestBodyOrNamedArgs).replace(/[\r\n]+/gim, ""); // We remove all the breaklines
16796
- }catch(parseErr){
16797
- requestBodyOrNamedArgsStr=""+requestBodyOrNamedArgs;
16798
- }
16799
- }
16800
- }
16801
-
16802
- let body=null;
16803
- // Not the same way to send parameters, with POST/PUT http method :
16804
- if(contains(["POST","PUT"],httpMethod) && requestBodyOrNamedArgs){
16805
- body=requestBodyOrNamedArgsStr;
16806
- }
16807
-
16808
- // Headers
16809
- if(contains(["POST","PUT"],httpMethod)){
16810
- headers["Content-Type"]="application/json";
16811
-
16812
- if(requestBodyOrNamedArgsStr && isString(body)){
16813
- headers["Content-Length"]=Buffer.byteLength(requestBodyOrNamedArgsStr);
15675
+ result+=c.toLowerCase();
16814
15676
  }
16815
-
16816
- // To remove the CORS error message (cf. https://medium.com/@dtkatz/3-ways-to-fix-the-cors-error-and-how-access-control-allow-origin-works-d97d55946d9)
16817
- if(addCORSHeader) headers["Access-Control-Allow-Origin"]="*";
16818
-
16819
- } else if(httpMethod==="GET" && requestBodyOrNamedArgs){
16820
- // Not the same way to send parameters in GET http method :
16821
- // DBG
16822
- lognow("unformatted API URL : "+completeURL);
16823
-
16824
- completeURL=appendGetParameters(completeURL, requestBodyOrNamedArgs);
16825
-
16826
- // DBG
16827
- lognow("formatted API URL : "+completeURL);
16828
- }
16829
-
16830
- // CASE BROWSER CONTEXT :
16831
- if(!isNodeContext || typeof(require)=="undefined"){
16832
- // TRACE
16833
- lognow("INFO : We are not running in a browser context (isNodeContext:"+isNodeContext+";typeof(require):"+(typeof(require))+"). Using browser library.");
16834
-
16835
- return new Promise((resolve,reject)=>{
16836
- fetch(completeURL, {
16837
- method: httpMethod,
16838
- headers: headers,
16839
- body: body,
16840
- })
16841
- // STRANGE : DOES NOT WORK !!:
16842
- //.then(response => response.json())
16843
- // STRANGE : DOES WORK :
16844
- .then(response => {
16845
-
16846
- // DBG
16847
- console.log("~~~~~~~~~~~response :",response);
16848
-
16849
- return response.json();
16850
- }).then(data => {
16851
-
16852
- // CAUTION : .request(...) => statusCode, .fetch(...) => status (performHTTPRequest(...) uses fetch(...))
16853
- const status=data.status;
16854
- // if(status!=200 && data.error){
16855
- // // const error=nonull(data.error, data.detail);
16856
- // const error=data.error;
16857
- // // TRACE
16858
- // console.error("Error:", error);
16859
- // reject({error:""+error, httpStatus:status});
16860
- // return;
16861
- // }
16862
-
16863
- // DBG
16864
- console.log("~~~~~~~~~~~data :",data);
16865
-
16866
- data.httpStatus=status;
16867
- resolve(data);
16868
- }).catch(error => {
16869
- // TRACE
16870
- console.error("Error:", error);
16871
- reject({error:`${error}`, httpStatus:null});
16872
- });
16873
- });
16874
- }// else :
16875
-
16876
- // CASE NODEJS CONTEXT :
16877
-
16878
- // TRACE
16879
- lognow("INFO : We are running in a nodejs context (isNodeContext:"+isNodeContext+";typeof(require):"+(typeof(require))+"). Using nodejs library.");
16880
-
16881
-
16882
- const isSecure=(!empty(completeURL) && contains(completeURL.toLowerCase(),"https://"));
16883
- const httpHandler=isSecure?require("https"):require("http");
16884
-
16885
- // Options for the HTTP request
16886
- const options = {
16887
- url: completeURL,
16888
- method: httpMethod,
16889
- };
16890
-
16891
- if(contains(["POST","PUT"],httpMethod)){
16892
- options.json=true;
15677
+ if(separator===".") result=result.replace(/\\.+/,separator);
15678
+ else result=result.replace(RegExp(separator+"+"),separator);
16893
15679
  }
16894
-
16895
- options.headers=headers;
16896
-
16897
- return new Promise((resolve,reject)=>{
16898
-
16899
- // Create the HTTP request
16900
- // DOES NOT WORK : const request = httpHandler.request(options, (response) => {
16901
- // UNLESS YOU SPECIFY in options : hostname, port, path
16902
- const request = httpHandler.request(completeURL, options, (response) => {
16903
-
16904
- let responseDataStr = "";
15680
+ return result;
15681
+ }
16905
15682
 
16906
- // A chunk of data has been received.
16907
- response.on("data", (chunk) => {
16908
- responseDataStr += chunk;
16909
- // // DEBUG ONLY
16910
- // const str = Buffer.from(chunk).toString("utf8");
16911
- // lognow(">>>str:"+str);
16912
- });
16913
-
16914
- // The whole response has been received.
16915
- response.on("end", () => {
15683
+ function toConvention(type="snake",str){
15684
+ if(empty(str)) return str;
15685
+ str=str.trim();
15686
+ let result="";
15687
+ if(type==="camel"){
15688
+ for(let i=0;i<str.length;i++){
15689
+ let c=str[i];
16916
15690
 
16917
- try{
16918
- let responseData;
16919
- try{
16920
- responseData=parseJSON(responseDataStr);
16921
- }catch(parseError1){
16922
- // CAUTION ! TODO : FIXME : We replace in response all single quotes with "`" (egrave) in order to avoid errors !
16923
- // DEBUG
16924
- responseDataStr=responseDataStr.replace(/'/gim,"`");
16925
- try{
16926
- responseData=parseJSON(responseDataStr);
16927
- }catch(parseError2){
16928
- // DBG
16929
- lognow("WARN : Could not JSON parse the response data ! Must deal with the raw string:«"+responseDataStr+"»", parseError2);
16930
- resolve( {response:response, responseDataStr:responseDataStr} );
16931
- return;
16932
- }
16933
- }
16934
-
16935
- // CAUTION : .request(...) => statusCode, .fetch(...) => status (performHTTPRequest(...) uses fetch(...))
16936
- const status=response.statusCode;
16937
- // if(status!=200){
16938
- // const error=nonull(response.statusMessage, "No error message for error code "+status);
16939
- // // TRACE
16940
- // console.error("Error:", error);
16941
- // reject({error:""+error, httpStatus:status});
16942
- // return;
16943
- // }
16944
-
16945
- resolve( {responseData:responseData, response:response, responseDataStr:responseDataStr, httpStatus: status} );
16946
- }catch(parseError3){
16947
- // DBG
16948
- lognow("WARN : Could not JSON parse the response data ! Must deal with the raw string:«"+responseDataStr+"»", parseError3);
16949
- resolve( {response:response, responseDataStr:responseDataStr} );
16950
- return;
15691
+ if(isCharacterToSkip(c)){
15692
+ let nextChar="";
15693
+
15694
+ if(i<str.length-1){// If we are not at the very last character :
15695
+ do{
15696
+ nextChar=str[i+1];
15697
+ i++;
15698
+ }while(isCharacterToSkip(nextChar) && i<str.length-1);
15699
+ if(isCharacterToSkip(nextChar)) nextChar="";
16951
15700
  }
15701
+ result+=nextChar.toUpperCase();
15702
+ }else{
16952
15703
 
16953
- });
16954
- });
16955
-
16956
- // Handle errors
16957
- request.on("error", (error) => {
16958
- reject({error:error, httpStatus:null});
16959
- });
16960
-
16961
- // Not the same way to send parameters, with POST/PUT http method :
16962
- if(contains(["POST","PUT"],httpMethod) && body){
16963
- request.write(body);
15704
+ result+=c;
15705
+ }
15706
+
15707
+
16964
15708
  }
16965
-
16966
- // End the request
16967
- request.end();
16968
-
16969
- });
15709
+ }else{
15710
+ if(type==="bundle"){
15711
+ result=snakize(str,".");
15712
+ }else{
15713
+ result=snakize(str);
15714
+ }
15715
+ }
16970
15716
 
16971
- };
16972
-
16973
-
16974
-
16975
-
16976
- replacePathVariablesNamesWithValuesIfPossible=function(apiURL, namedArgs){
16977
- let result=apiURL;
16978
- foreach(namedArgs,(namedArgValue, namedArgKey)=>{
16979
- result=result.replace("{"+namedArgKey+"}",namedArgValue);
16980
- });
16981
15717
  return result;
16982
- };
16983
-
16984
- appendGetParameters=function(apiURL, namedArgsParam){
16985
- if(nothing(namedArgsParam)) return "";
16986
- try{
16987
-
16988
- const namedArgs=isString(namedArgsParam)?parseJSON(namedArgsParam):namedArgsParam;
16989
-
16990
- let result=apiURL;
16991
-
16992
- const paramCouples=[];
16993
- foreach(namedArgs,(value,key)=>{paramCouples.push(key+"="+value);});
16994
-
16995
- if(!empty(paramCouples)) result+=("?"+paramCouples.join("&"));
16996
-
16997
- return result;
16998
- }catch(parseError){
16999
- // TRACE
17000
- lognow("ERROR : Could not parse string parameters object «"+namedArgsParam+"», aborting GET parameter request string calculation:", parseError);
17001
- return "";
17002
- }
17003
- };
17004
-
17005
-
17006
-
15718
+ }
17007
15719
 
17008
15720
 
17009
15721
 
15722
+ function capitalize(str){
15723
+ if(nothing(str)) return "";
15724
+ return str.replace(/(?:^|\s)\S/m, firstChar => firstChar.toUpperCase()); // We replace only the first character.
15725
+ }
17010
15726
 
17011
15727
 
17012
15728
  /* ## Utility network global methods in a javascript, console (nodejs), or vanilla javascript with no browser environment.