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.
- aotrautils/aotrautils.build.js +265 -1549
- aotrautils/package.json +1 -1
aotrautils/aotrautils.build.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
/*utils COMMONS library associated with aotra version : «1_29072022-2359 (
|
|
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)
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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
|
|
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
|
-
//
|
|
15753
|
-
|
|
15754
|
-
|
|
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
|
-
|
|
15432
|
+
// ******************* CONFIG *******************
|
|
15433
|
+
const PREFERED_LANGUAGE="en";
|
|
15759
15434
|
|
|
15435
|
+
const NO_TRANSLATION_LABEL="NO TRANSLATION FOUND";
|
|
15760
15436
|
|
|
15761
|
-
|
|
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
|
-
|
|
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
|
-
|
|
15849
|
-
return
|
|
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
|
-
|
|
15877
|
-
|
|
15878
|
-
|
|
15879
|
-
|
|
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
|
-
|
|
15471
|
+
|
|
15472
|
+
if(contains(["capitalize","append"],lang)){
|
|
15882
15473
|
// TRACE
|
|
15883
|
-
console.log("WARN :
|
|
15884
|
-
|
|
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
|
-
|
|
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(
|
|
15902
|
-
|
|
15903
|
-
result
|
|
15904
|
-
}
|
|
15482
|
+
if(!isString(languagesAndStrings)){ // Case direct i18n translation, with no translation keys :
|
|
15483
|
+
|
|
15484
|
+
result=languagesAndStrings[lang];
|
|
15905
15485
|
|
|
15906
|
-
|
|
15907
|
-
|
|
15908
|
-
|
|
15909
|
-
|
|
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
|
-
|
|
15916
|
-
|
|
15917
|
-
|
|
15918
|
-
|
|
15919
|
-
|
|
15920
|
-
|
|
15921
|
-
|
|
15922
|
-
|
|
15923
|
-
|
|
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
|
-
|
|
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
|
-
|
|
15963
|
-
|
|
15964
|
-
|
|
15965
|
-
|
|
15966
|
-
|
|
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
|
-
|
|
15974
|
-
|
|
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
|
-
|
|
15978
|
-
|
|
15979
|
-
|
|
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("
|
|
16361
|
-
|
|
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
|
-
|
|
16673
|
-
|
|
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
|
-
|
|
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
|
-
|
|
16685
|
-
|
|
16686
|
-
|
|
16687
|
-
|
|
16688
|
-
|
|
16689
|
-
|
|
16690
|
-
|
|
16691
|
-
|
|
16692
|
-
|
|
16693
|
-
|
|
16694
|
-
|
|
16695
|
-
|
|
16696
|
-
|
|
16697
|
-
|
|
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
|
-
|
|
16723
|
-
|
|
16724
|
-
|
|
16725
|
-
|
|
16726
|
-
|
|
16727
|
-
|
|
16728
|
-
|
|
16729
|
-
|
|
16730
|
-
|
|
16731
|
-
|
|
16732
|
-
|
|
16733
|
-
|
|
16734
|
-
|
|
16735
|
-
|
|
16736
|
-
|
|
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
|
-
|
|
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
|
-
|
|
16782
|
-
|
|
16783
|
-
|
|
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
|
-
|
|
16788
|
-
let
|
|
16789
|
-
|
|
16790
|
-
|
|
16791
|
-
|
|
16792
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
16907
|
-
|
|
16908
|
-
|
|
16909
|
-
|
|
16910
|
-
|
|
16911
|
-
|
|
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
|
-
|
|
16918
|
-
|
|
16919
|
-
|
|
16920
|
-
|
|
16921
|
-
|
|
16922
|
-
|
|
16923
|
-
|
|
16924
|
-
|
|
16925
|
-
|
|
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
|
-
|
|
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
|
-
|
|
16967
|
-
|
|
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.
|