aotrautils 0.0.53 → 0.0.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aotrautils/aotrautils.build.js +2204 -881
- aotrautils/package.json +1 -1
aotrautils/aotrautils.build.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
/*utils CLIENT library associated with aotra version : «1.0.0.000 (
|
|
3
|
+
/*utils CLIENT library associated with aotra version : «1.0.0.000 (12/07/2022-23:38:27)»*/
|
|
4
4
|
/*-----------------------------------------------------------------------------*/
|
|
5
5
|
/* ## Utility global methods in a browser (htmljs) client environment.
|
|
6
6
|
*
|
|
@@ -55,571 +55,298 @@ if(typeof monitorProgression === "undefined"){
|
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
58
61
|
// NOT AOTESTABLE !
|
|
59
|
-
//TODO : develop :
|
|
60
|
-
//getHashedString=monitorProgression(100000,
|
|
61
|
-
window.getHashedString=function(str,/*OPTIONAL*/isHeavyTreatment){
|
|
62
|
-
|
|
63
|
-
//CAUTION : YOU MUST *NOT* CHANGE THIS VALUE, UNLESS THE HASH HEAVY TREATMENT VALUE IN YOUR AOTRA SERVER-SIDE CODEIS THE SAME !
|
|
64
|
-
const HEAVY_TREATMENT_LOOP_COUNT=100000;
|
|
65
|
-
|
|
66
|
-
if(isHeavyTreatment){
|
|
67
62
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
63
|
+
//Works, but doesn't compile under PhoneGap...:
|
|
64
|
+
//function getURLParameter(nameParam){
|
|
65
|
+
//var name=encodeURIComponent(nameParam);
|
|
66
|
+
//return decodeURIComponent(
|
|
67
|
+
//(
|
|
68
|
+
//new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)' )
|
|
69
|
+
//.exec(window.location.search)
|
|
70
|
+
//|| [,""]
|
|
71
|
+
//)[1]
|
|
72
|
+
//.replace(/\+/g, '%20')
|
|
73
|
+
//)||null;
|
|
74
|
+
//}
|
|
76
75
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// DEPENDENCY : SJCL JS libray (Stanford Javascript Crypto Library)
|
|
85
|
-
if(typeof(sjcl)!="undefined"){
|
|
86
|
-
let result=sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(str));
|
|
87
|
-
|
|
88
|
-
// TODO : develop :
|
|
89
|
-
// hash.progressionMonitor.progress(1);
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return result;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// if not found : MD5 hash (sucks) :
|
|
96
|
-
//TRACE
|
|
97
|
-
log("WARN : Could not find SHA hashing, using MD5. This sucks, and this very likely will prevent your aotra client to be able to write data to aotra server.");
|
|
98
|
-
|
|
99
|
-
return hex_md5(str);
|
|
100
|
-
}
|
|
76
|
+
//...so we replace it with another function that is supposed to do the same
|
|
77
|
+
//thing :
|
|
78
|
+
function getURLParameter(nameParam){
|
|
79
|
+
var name=nameParam;
|
|
80
|
+
if (name=new RegExp("[?&]" + encodeURIComponent(name) + "=([^&]*)").exec(location.search))
|
|
81
|
+
return decodeURIComponent(name[1]);
|
|
82
|
+
return null;
|
|
101
83
|
}
|
|
102
|
-
//);
|
|
103
84
|
|
|
104
|
-
function encrypt(strToEncrypt,key,/*OPTIONAL*/mode){
|
|
105
|
-
if(!mode || typeof(sjcl)=="undefined") mode="xor";
|
|
106
|
-
mode=mode.trim();
|
|
107
85
|
|
|
108
|
-
if(contains(mode,"aes") && contains(mode,"xor")){
|
|
109
86
|
|
|
110
|
-
// XOR :
|
|
111
|
-
var xorStr=btoa(encodeXORNoPattern(strToEncrypt,key));
|
|
112
|
-
|
|
113
|
-
var aesStr=
|
|
114
|
-
// AES
|
|
115
|
-
sjcl.encrypt(key,xorStr);
|
|
116
87
|
|
|
117
|
-
|
|
118
|
-
|
|
88
|
+
// A little method to help i18ning aotra user interface :
|
|
89
|
+
const I18N_REPOSITORY_VARIABLE_NAME="I18N_KEYS";
|
|
90
|
+
function i18n(languagesAndStrings, langParam=null){
|
|
119
91
|
|
|
120
|
-
//
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
function decrypt(strToDecrypt,key,/*OPTIONAL*/mode){
|
|
128
|
-
if(!mode || typeof(sjcl)=="undefined") mode="xor";
|
|
129
|
-
mode=mode.trim();
|
|
92
|
+
// ******************* CONFIG *******************
|
|
93
|
+
const PREFERED_LANGUAGE="fr"; // My Quebecer wife (love on her for ever and beyond) forced me to set this...(I'm French.)
|
|
94
|
+
|
|
95
|
+
const NO_TRANSLATION_LABEL="NO TRANSLATION FOUND";
|
|
96
|
+
|
|
97
|
+
const ADD_SYSTEMATICALLY_TO_REPOSITORY=true;
|
|
98
|
+
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 !
|
|
130
99
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
// AES
|
|
134
|
-
var aesStr=sjcl.decrypt(key,strToDecrypt);
|
|
135
|
-
|
|
136
|
-
var xorStr=
|
|
137
|
-
// XOR :
|
|
138
|
-
encodeXORNoPattern(atob(aesStr),key);
|
|
100
|
+
// ***************** END CONFIG *****************
|
|
139
101
|
|
|
140
|
-
|
|
141
|
-
|
|
102
|
+
|
|
103
|
+
if(!languagesAndStrings){
|
|
104
|
+
// TRACE
|
|
105
|
+
console.log("ERROR : No parameter for i18n provided!",languagesAndStrings);
|
|
106
|
+
return NO_TRANSLATION_LABEL;
|
|
142
107
|
}
|
|
143
108
|
|
|
144
|
-
|
|
145
|
-
if(
|
|
146
|
-
|
|
147
|
-
|
|
109
|
+
let lang=null;
|
|
110
|
+
if(langParam){
|
|
111
|
+
lang=langParam;
|
|
112
|
+
}else{
|
|
113
|
+
// Parameters priority :
|
|
114
|
+
if(window){
|
|
115
|
+
if(window.getURLParameter){
|
|
116
|
+
lang=nonull(window.getURLParameter("language"), window.getURLParameter("lang"));
|
|
117
|
+
}
|
|
118
|
+
if(!lang && (window.language || window.lang)){
|
|
119
|
+
lang=nonull(window.language, window.lang);
|
|
120
|
+
}
|
|
121
|
+
}else if(global){ // (NodeJS compatibility)
|
|
122
|
+
if(global.getURLParameter){
|
|
123
|
+
lang=nonull(global.getURLParameter("language"), global.getURLParameter("lang"));
|
|
124
|
+
}
|
|
125
|
+
if(!lang && (global.language || global.lang)){
|
|
126
|
+
lang=nonull(global.language, global.lang);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
lang=nonull(lang, PREFERED_LANGUAGE);
|
|
130
|
+
}
|
|
148
131
|
|
|
132
|
+
if(contains(["capitalize","append"],lang)){
|
|
133
|
+
// TRACE
|
|
134
|
+
console.log("WARN : Language parameter has for value a reserved attribute keyword, using default language «"+PREFERED_LANGUAGE+"»");
|
|
135
|
+
lang=PREFERED_LANGUAGE;
|
|
136
|
+
}
|
|
149
137
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
let keyLength=keyChunk.length;
|
|
156
|
-
let previousChars="";
|
|
157
|
-
let cnt=0;
|
|
158
|
-
for(let i=0;i<strToEncode.length;i++){
|
|
159
|
-
let char=strToEncode.charAt(i);
|
|
160
|
-
let keyByte=keyChunk.charAt(cnt);
|
|
138
|
+
|
|
139
|
+
let result=null;
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
if(!isString(languagesAndStrings)){ // Case direct i18n translation, with no translation keys :
|
|
161
143
|
|
|
162
|
-
result
|
|
144
|
+
result=languagesAndStrings[lang];
|
|
145
|
+
|
|
146
|
+
if (!result){
|
|
147
|
+
// TRACE
|
|
148
|
+
console.log("ERROR : No label found for language «"+lang+"»!",languagesAndStrings);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (!result){
|
|
152
|
+
for(key in languagesAndStrings){
|
|
153
|
+
if (!languagesAndStrings.hasOwnProperty(key))
|
|
154
|
+
continue;
|
|
155
|
+
// We take the first that we find, in the other languages:
|
|
156
|
+
result=languagesAndStrings[key];
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (!result) return NO_TRANSLATION_LABEL;
|
|
163
162
|
|
|
164
|
-
if(cnt%keyLength===(keyLength-1)){
|
|
165
|
-
cnt=0;
|
|
166
|
-
// NO : TOO LONG TREATMENT !: We hash with all the previous blocks too :
|
|
167
|
-
// We hash with a character of the previous chunk (they add up at each new chunk):
|
|
168
|
-
keyChunk=getHashedString(previousChars+keyChunk);
|
|
169
163
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
164
|
+
// We add this «rogue» usage of i18n to the whole repository at all useful ends :
|
|
165
|
+
if(ADD_SYSTEMATICALLY_TO_REPOSITORY){
|
|
166
|
+
// let declarable={};
|
|
167
|
+
let maxLength=Math.min(result.length, MAX_CHARACTERS_FOR_TRANSLATION_KEY);
|
|
168
|
+
let key=toConvention("camel", result.substring(0,maxLength).toLowerCase() );
|
|
169
|
+
// let calculatedKey=radicalKey+"_"+getUUID("short");
|
|
170
|
+
// declarable[calculatedKey]=languagesAndStrings;
|
|
171
|
+
i18n.declareSingleEntry(key, languagesAndStrings);
|
|
173
172
|
}
|
|
173
|
+
|
|
174
|
+
// Eventual post-treatments :
|
|
175
|
+
if(languagesAndStrings.capitalize) result=capitalize(result);
|
|
176
|
+
if(languagesAndStrings.append) result+=languagesAndStrings.append;
|
|
177
|
+
|
|
178
|
+
return result;
|
|
174
179
|
}
|
|
175
|
-
|
|
176
|
-
}
|
|
180
|
+
// else // Case we use translation keys : (here, languagesAndStrings parameter holds just the wished translation key)
|
|
177
181
|
|
|
178
|
-
function generateRandomString(length,/*NULLABLE*/mode){
|
|
179
182
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
+
let repository=(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME];
|
|
184
|
+
if(!repository){
|
|
185
|
+
// TRACE
|
|
186
|
+
console.log("ERROR : No repository variable found for repository «"+I18N_REPOSITORY_VARIABLE_NAME+"»!");
|
|
187
|
+
return NO_TRANSLATION_LABEL;
|
|
188
|
+
}
|
|
183
189
|
|
|
190
|
+
// OLD (ie. splitted format :)
|
|
191
|
+
// let repositoryForLang=repository[lang];
|
|
192
|
+
// if (!repositoryForLang){
|
|
193
|
+
// // TRACE
|
|
194
|
+
// console.log("ERROR : No translation found for language «"+lang+"»!",languagesAndStrings);
|
|
195
|
+
// return NO_TRANSLATION_LABEL;
|
|
196
|
+
// }
|
|
197
|
+
// result=repositoryForLang[languagesAndStrings]; // Here, languagesAndStrings is just a bundle key.
|
|
198
|
+
// if (!result) return NO_TRANSLATION_LABEL+" (for key «"+languagesAndStrings+"»)";
|
|
184
199
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
200
|
+
let languagesAndStringsForTranslationKey=repository[languagesAndStrings];
|
|
201
|
+
if (!languagesAndStringsForTranslationKey){
|
|
202
|
+
// SILENT ERROR :
|
|
203
|
+
// // TRACE
|
|
204
|
+
// console.log("ERROR : No translation found for key «"+languagesAndStrings+"» for language «"+lang+"»!");
|
|
205
|
+
// NO, OLD : return NO_TRANSLATION_LABEL;
|
|
206
|
+
return languagesAndStrings;
|
|
191
207
|
}
|
|
208
|
+
result=i18n(languagesAndStringsForTranslationKey, lang); // Since we use the same format !
|
|
192
209
|
|
|
193
|
-
|
|
210
|
+
return result;
|
|
211
|
+
};
|
|
194
212
|
|
|
195
|
-
if(mode==="simple"){
|
|
196
|
-
|
|
197
|
-
for(var i=0;i<length;i++){
|
|
198
|
-
var value=Math.getRandomInArray(ALLOWED_CHARS);
|
|
199
|
-
result+=value;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
return result;
|
|
203
|
-
}else if(mode==="human"){
|
|
204
|
-
|
|
205
|
-
const markovGraph={
|
|
206
|
-
// For simplicity sakes, we ignore the consonants and vowels doubling possibilities:
|
|
207
|
-
consonants:{// They always accept vowels at their following
|
|
208
|
-
// Indicate ^ to specify wich of the other group letter to exclude as direct follower of the given letter :
|
|
209
|
-
"b":["r","l"],
|
|
210
|
-
"c":["h","t","k","l","r"],
|
|
211
|
-
"d":["r"],
|
|
212
|
-
"f":["r","l"],
|
|
213
|
-
"g":["r","l"],
|
|
214
|
-
"h":[],
|
|
215
|
-
"j":["r"],
|
|
216
|
-
"k":["r","l","t","h"],
|
|
217
|
-
"l":["t"],
|
|
218
|
-
"m":["b","p"],
|
|
219
|
-
"n":["t","d"],
|
|
220
|
-
"p":["h","r","s","t","l"],
|
|
221
|
-
"q":["r","l"],
|
|
222
|
-
"r":["t","c","d","k","q","h"],
|
|
223
|
-
"s":["t","c","d","k","q","h"],
|
|
224
|
-
"t":["r","h"],
|
|
225
|
-
"v":["r","l"],
|
|
226
|
-
"w":["r"],
|
|
227
|
-
"x":["p"],
|
|
228
|
-
"z":["r","h"],
|
|
229
|
-
},
|
|
230
|
-
vowels:{// They always accept consonants at their following
|
|
231
|
-
"a":["i","u","y"],
|
|
232
|
-
"e":["i","u","y"],
|
|
233
|
-
"i":["e","o"],
|
|
234
|
-
"o":["i","u"],
|
|
235
|
-
"u":["i"],
|
|
236
|
-
"y":[],
|
|
237
|
-
},
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
const LETTERS_TYPES=Object.keys(markovGraph);
|
|
241
|
-
|
|
242
|
-
const CURR_LETTER_MODE=Math.getRandomInArray(LETTERS_TYPES);
|
|
243
|
-
const POSSIBLE_LETTERS=Object.keys(markovGraph[CURR_LETTER_MODE]);
|
|
244
|
-
const CURR_LETTER=Math.getRandomInArray(POSSIBLE_LETTERS);
|
|
245
|
-
|
|
246
|
-
var automataState={
|
|
247
|
-
currentLetterMode:CURR_LETTER_MODE,
|
|
248
|
-
possibleLetters:POSSIBLE_LETTERS,
|
|
249
|
-
currentLetter:CURR_LETTER,
|
|
250
|
-
|
|
251
|
-
calculateLetter:function(){
|
|
252
|
-
|
|
253
|
-
this.currentLetter=Math.getRandomInArray(this.possibleLetters);
|
|
254
|
-
|
|
255
|
-
var sameLetters=Object.keys( markovGraph[this.currentLetterMode] );
|
|
256
|
-
if(!contains(sameLetters,this.currentLetter)){
|
|
257
|
-
// If we picked from the same group : we don't change of group to pick
|
|
258
|
-
// If we picked from the other group : we do change of group to pick
|
|
259
|
-
|
|
260
|
-
var otherLetterMode=foreach(LETTERS_TYPES,(item)=>{
|
|
261
|
-
if(item!==this.currentLetterMode) return item;
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
this.currentLetterMode=otherLetterMode;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
},
|
|
268
|
-
|
|
269
|
-
updatePossibleLettersForLetter:function(){
|
|
270
|
-
|
|
271
|
-
var nextPossibleLetters=[];
|
|
272
|
-
var nextNotPossibleLetters=[];
|
|
273
|
-
|
|
274
|
-
// We store those of the other group marked «^»
|
|
275
|
-
foreach(markovGraph[this.currentLetterMode][this.currentLetter],(letter)=>{
|
|
276
|
-
if(letter.charAt(0)==="^"){
|
|
277
|
-
nextNotPossibleLetters.push(letter.charAt(1));
|
|
278
|
-
}else{
|
|
279
|
-
nextPossibleLetters.push(letter);
|
|
280
|
-
}
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
var otherLetterMode=foreach(LETTERS_TYPES,(item)=>{
|
|
284
|
-
if(item!==this.currentLetterMode) return item;
|
|
285
|
-
});
|
|
286
|
-
var otherLetters=Object.keys(markovGraph[otherLetterMode]);
|
|
287
|
-
|
|
288
|
-
// We exclude those marked «^»
|
|
289
|
-
foreach( otherLetters ,(letter)=>{
|
|
290
|
-
if(!contains(nextNotPossibleLetters,letter)){
|
|
291
|
-
nextPossibleLetters.push(letter);
|
|
292
|
-
}
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
this.possibleLetters=nextPossibleLetters;
|
|
296
|
-
}
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
var i=0;
|
|
300
|
-
do{
|
|
301
213
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
return result;
|
|
214
|
+
i18n.declareSingleEntry=function(key, languagesAndStringsEntry){
|
|
215
|
+
let newRepository;
|
|
216
|
+
let foundRepository=(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME];
|
|
217
|
+
if(!foundRepository){
|
|
218
|
+
// TRACE
|
|
219
|
+
console.log("ERROR : No repository variable found for repository «"+I18N_REPOSITORY_VARIABLE_NAME+"»!");
|
|
220
|
+
return;
|
|
311
221
|
}
|
|
312
222
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
223
|
+
let foundEntryForKey=foundRepository[key];
|
|
224
|
+
if(!foundEntryForKey){
|
|
225
|
+
foundRepository[key]=languagesAndStringsEntry;
|
|
226
|
+
// }else{
|
|
227
|
+
// // TRACE
|
|
228
|
+
// console.log("WARN : Duplicate entry for translation key «"+key+"», keeping only the first declared :",foundEntryForKey);
|
|
316
229
|
}
|
|
317
230
|
|
|
318
|
-
|
|
319
|
-
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
//USAGE :
|
|
235
|
+
//Example :
|
|
236
|
+
//i18n.declare({
|
|
237
|
+
// "commencer":{"fr":"Commencer","en":"Start"}
|
|
238
|
+
//});
|
|
239
|
+
i18n.declare=function(repositoryInfos){
|
|
240
|
+
let newRepository;
|
|
241
|
+
let foundRepository=(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME];
|
|
242
|
+
if(foundRepository){
|
|
243
|
+
// // TRACE
|
|
244
|
+
// console.log("WARN : A repository has already been declared, merging.",foundRepository);
|
|
245
|
+
newRepository=merge(foundRepository, repositoryInfos);
|
|
246
|
+
}else{
|
|
247
|
+
newRepository=repositoryInfos;
|
|
320
248
|
}
|
|
249
|
+
(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME]=newRepository;
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// USAGE :
|
|
253
|
+
// Example :
|
|
254
|
+
//i18n.declareSplitted({
|
|
255
|
+
// "fr":{
|
|
256
|
+
// "commencer":"Commencer",
|
|
257
|
+
// },
|
|
258
|
+
// "en":{
|
|
259
|
+
// "commencer":"Start",
|
|
260
|
+
// },
|
|
261
|
+
//});
|
|
262
|
+
i18n.declareSplitted=function(repositoryInfos){
|
|
263
|
+
let nonSplittedFormat={};
|
|
321
264
|
|
|
322
|
-
|
|
323
|
-
|
|
265
|
+
foreach(repositoryInfos,(allKeys, lang)=>{
|
|
266
|
+
foreach(allKeys,(text, key)=>{
|
|
267
|
+
|
|
268
|
+
let foundKeyInfos=nonSplittedFormat[key];
|
|
269
|
+
if(!foundKeyInfos){
|
|
270
|
+
nonSplittedFormat[key]={};
|
|
271
|
+
foundKeyInfos=nonSplittedFormat[key];
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
let foundTextForLanguage=foundKeyInfos[lang];
|
|
275
|
+
if(foundTextForLanguage){
|
|
276
|
+
// TRACE
|
|
277
|
+
console.log("WARN : Duplicate entry for translation key «"+key+"» for language «"+lang+"», keeping only the first declared :",foundTextForLanguage);
|
|
278
|
+
}else{
|
|
279
|
+
foundKeyInfos[lang]=text;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
});
|
|
283
|
+
});
|
|
324
284
|
|
|
285
|
+
// Then we use the standard declaration method :
|
|
286
|
+
i18n.declare(nonSplittedFormat);
|
|
287
|
+
};
|
|
325
288
|
|
|
289
|
+
// For generating languages files helping purposes :
|
|
290
|
+
i18n.displayAll=function(displaySingleEntriesCalls=false){
|
|
291
|
+
let foundRepository=(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME];
|
|
292
|
+
if(!foundRepository){
|
|
293
|
+
// TRACE
|
|
294
|
+
console.log("ERROR : No repository found under the variable name «"+I18N_REPOSITORY_VARIABLE_NAME+"»");
|
|
295
|
+
}else{
|
|
296
|
+
console.log("TRACE : Variable under the name of «"+I18N_REPOSITORY_VARIABLE_NAME+"»:", foundRepository);
|
|
297
|
+
console.log("\n\n");
|
|
326
298
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
299
|
+
let message="";
|
|
300
|
+
foreach(foundRepository, (entry, entryKey)=>{
|
|
301
|
+
let normalizedEntry={};
|
|
302
|
+
if(entry.capitalize) normalizedEntry.capitalize=entry.capitalize;
|
|
303
|
+
if(entry.append) normalizedEntry.append=entry.append;
|
|
304
|
+
foreach(entry,(e,k)=>{
|
|
305
|
+
normalizedEntry[k]=e;
|
|
306
|
+
}
|
|
307
|
+
,(i,key)=>{ return (!contains(["append","capitalize"],key)); }
|
|
308
|
+
,(i1,i2)=>{ return (i1.key==i2.key?0:(i1.key=="fr"?-1:1));/*(Sorry, French always comes first !)*/}
|
|
309
|
+
);
|
|
310
|
+
let entryStr=stringifyObject(normalizedEntry);
|
|
311
|
+
message+="\""+entryKey+"\":"+entryStr+",\n";
|
|
312
|
+
if(displaySingleEntriesCalls) message+="// i18n("+entryStr+");\n";
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
console.log("\n"+message);
|
|
316
|
+
|
|
317
|
+
}
|
|
330
318
|
}
|
|
331
319
|
|
|
332
320
|
|
|
333
321
|
|
|
334
|
-
// NOT AOTESTABLE !
|
|
335
322
|
|
|
336
|
-
//Works, but doesn't compile under PhoneGap...:
|
|
337
|
-
//function getURLParameter(nameParam){
|
|
338
|
-
//var name=encodeURIComponent(nameParam);
|
|
339
|
-
//return decodeURIComponent(
|
|
340
|
-
//(
|
|
341
|
-
//new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)' )
|
|
342
|
-
//.exec(window.location.search)
|
|
343
|
-
//|| [,""]
|
|
344
|
-
//)[1]
|
|
345
|
-
//.replace(/\+/g, '%20')
|
|
346
|
-
//)||null;
|
|
347
|
-
//}
|
|
348
323
|
|
|
349
|
-
|
|
350
|
-
//thing :
|
|
351
|
-
function getURLParameter(nameParam){
|
|
352
|
-
var name=nameParam;
|
|
353
|
-
if (name=new RegExp("[?&]" + encodeURIComponent(name) + "=([^&]*)").exec(location.search))
|
|
354
|
-
return decodeURIComponent(name[1]);
|
|
355
|
-
return null;
|
|
356
|
-
}
|
|
324
|
+
// -------------------------------------------------------------------------------------------
|
|
357
325
|
|
|
326
|
+
// Mouse management :
|
|
358
327
|
|
|
328
|
+
// Code from (www.adomas.org/javascript-mouse-wheel)
|
|
329
|
+
function initMouseWheel(handle,/* OPTIONAL */filterFunction){
|
|
359
330
|
|
|
331
|
+
var wheel=function(event){
|
|
360
332
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
function i18n(languagesAndStrings, langParam=null){
|
|
364
|
-
|
|
365
|
-
// ******************* CONFIG *******************
|
|
366
|
-
const PREFERED_LANGUAGE="fr"; // My Quebecer wife (love on her for ever and beyond) forced me to set this...(I'm French.)
|
|
367
|
-
|
|
368
|
-
const NO_TRANSLATION_LABEL="NO TRANSLATION FOUND";
|
|
369
|
-
|
|
370
|
-
const ADD_SYSTEMATICALLY_TO_REPOSITORY=true;
|
|
371
|
-
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 !
|
|
333
|
+
if (filterFunction && !filterFunction(event.target))
|
|
334
|
+
return;
|
|
372
335
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
if(window){
|
|
388
|
-
if(window.getURLParameter){
|
|
389
|
-
lang=nonull(window.getURLParameter("language"), window.getURLParameter("lang"));
|
|
390
|
-
}
|
|
391
|
-
if(!lang && (window.language || window.lang)){
|
|
392
|
-
lang=nonull(window.language, window.lang);
|
|
393
|
-
}
|
|
394
|
-
}else if(global){ // (NodeJS compatibility)
|
|
395
|
-
if(global.getURLParameter){
|
|
396
|
-
lang=nonull(global.getURLParameter("language"), global.getURLParameter("lang"));
|
|
397
|
-
}
|
|
398
|
-
if(!lang && (global.language || global.lang)){
|
|
399
|
-
lang=nonull(global.language, global.lang);
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
lang=nonull(lang, PREFERED_LANGUAGE);
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
if(contains(["capitalize","append"],lang)){
|
|
406
|
-
// TRACE
|
|
407
|
-
console.log("WARN : Language parameter has for value a reserved attribute keyword, using default language «"+PREFERED_LANGUAGE+"»");
|
|
408
|
-
lang=PREFERED_LANGUAGE;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
let result=null;
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
if(!isString(languagesAndStrings)){ // Case direct i18n translation, with no translation keys :
|
|
416
|
-
|
|
417
|
-
result=languagesAndStrings[lang];
|
|
418
|
-
|
|
419
|
-
if (!result){
|
|
420
|
-
// TRACE
|
|
421
|
-
console.log("ERROR : No label found for language «"+lang+"»!",languagesAndStrings);
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
if (!result){
|
|
425
|
-
for(key in languagesAndStrings){
|
|
426
|
-
if (!languagesAndStrings.hasOwnProperty(key))
|
|
427
|
-
continue;
|
|
428
|
-
// We take the first that we find, in the other languages:
|
|
429
|
-
result=languagesAndStrings[key];
|
|
430
|
-
break;
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
if (!result) return NO_TRANSLATION_LABEL;
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
// We add this «rogue» usage of i18n to the whole repository at all useful ends :
|
|
438
|
-
if(ADD_SYSTEMATICALLY_TO_REPOSITORY){
|
|
439
|
-
// let declarable={};
|
|
440
|
-
let maxLength=Math.min(result.length, MAX_CHARACTERS_FOR_TRANSLATION_KEY);
|
|
441
|
-
let key=toConvention("camel", result.substring(0,maxLength).toLowerCase() );
|
|
442
|
-
// let calculatedKey=radicalKey+"_"+getUUID("short");
|
|
443
|
-
// declarable[calculatedKey]=languagesAndStrings;
|
|
444
|
-
i18n.declareSingleEntry(key, languagesAndStrings);
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// Eventual post-treatments :
|
|
448
|
-
if(languagesAndStrings.capitalize) result=capitalize(result);
|
|
449
|
-
if(languagesAndStrings.append) result+=languagesAndStrings.append;
|
|
450
|
-
|
|
451
|
-
return result;
|
|
452
|
-
}
|
|
453
|
-
// else // Case we use translation keys : (here, languagesAndStrings parameter holds just the wished translation key)
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
let repository=(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME];
|
|
457
|
-
if(!repository){
|
|
458
|
-
// TRACE
|
|
459
|
-
console.log("ERROR : No repository variable found for repository «"+I18N_REPOSITORY_VARIABLE_NAME+"»!");
|
|
460
|
-
return NO_TRANSLATION_LABEL;
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
// OLD (ie. splitted format :)
|
|
464
|
-
// let repositoryForLang=repository[lang];
|
|
465
|
-
// if (!repositoryForLang){
|
|
466
|
-
// // TRACE
|
|
467
|
-
// console.log("ERROR : No translation found for language «"+lang+"»!",languagesAndStrings);
|
|
468
|
-
// return NO_TRANSLATION_LABEL;
|
|
469
|
-
// }
|
|
470
|
-
// result=repositoryForLang[languagesAndStrings]; // Here, languagesAndStrings is just a bundle key.
|
|
471
|
-
// if (!result) return NO_TRANSLATION_LABEL+" (for key «"+languagesAndStrings+"»)";
|
|
472
|
-
|
|
473
|
-
let languagesAndStringsForTranslationKey=repository[languagesAndStrings];
|
|
474
|
-
if (!languagesAndStringsForTranslationKey){
|
|
475
|
-
// SILENT ERROR :
|
|
476
|
-
// // TRACE
|
|
477
|
-
// console.log("ERROR : No translation found for key «"+languagesAndStrings+"» for language «"+lang+"»!");
|
|
478
|
-
// NO, OLD : return NO_TRANSLATION_LABEL;
|
|
479
|
-
return languagesAndStrings;
|
|
480
|
-
}
|
|
481
|
-
result=i18n(languagesAndStringsForTranslationKey, lang); // Since we use the same format !
|
|
482
|
-
|
|
483
|
-
return result;
|
|
484
|
-
};
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
i18n.declareSingleEntry=function(key, languagesAndStringsEntry){
|
|
488
|
-
let newRepository;
|
|
489
|
-
let foundRepository=(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME];
|
|
490
|
-
if(!foundRepository){
|
|
491
|
-
// TRACE
|
|
492
|
-
console.log("ERROR : No repository variable found for repository «"+I18N_REPOSITORY_VARIABLE_NAME+"»!");
|
|
493
|
-
return;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
let foundEntryForKey=foundRepository[key];
|
|
497
|
-
if(!foundEntryForKey){
|
|
498
|
-
foundRepository[key]=languagesAndStringsEntry;
|
|
499
|
-
// }else{
|
|
500
|
-
// // TRACE
|
|
501
|
-
// console.log("WARN : Duplicate entry for translation key «"+key+"», keeping only the first declared :",foundEntryForKey);
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
};
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
//USAGE :
|
|
508
|
-
//Example :
|
|
509
|
-
//i18n.declare({
|
|
510
|
-
// "commencer":{"fr":"Commencer","en":"Start"}
|
|
511
|
-
//});
|
|
512
|
-
i18n.declare=function(repositoryInfos){
|
|
513
|
-
let newRepository;
|
|
514
|
-
let foundRepository=(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME];
|
|
515
|
-
if(foundRepository){
|
|
516
|
-
// // TRACE
|
|
517
|
-
// console.log("WARN : A repository has already been declared, merging.",foundRepository);
|
|
518
|
-
newRepository=merge(foundRepository, repositoryInfos);
|
|
519
|
-
}else{
|
|
520
|
-
newRepository=repositoryInfos;
|
|
521
|
-
}
|
|
522
|
-
(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME]=newRepository;
|
|
523
|
-
};
|
|
524
|
-
|
|
525
|
-
// USAGE :
|
|
526
|
-
// Example :
|
|
527
|
-
//i18n.declareSplitted({
|
|
528
|
-
// "fr":{
|
|
529
|
-
// "commencer":"Commencer",
|
|
530
|
-
// },
|
|
531
|
-
// "en":{
|
|
532
|
-
// "commencer":"Start",
|
|
533
|
-
// },
|
|
534
|
-
//});
|
|
535
|
-
i18n.declareSplitted=function(repositoryInfos){
|
|
536
|
-
let nonSplittedFormat={};
|
|
537
|
-
|
|
538
|
-
foreach(repositoryInfos,(allKeys, lang)=>{
|
|
539
|
-
foreach(allKeys,(text, key)=>{
|
|
540
|
-
|
|
541
|
-
let foundKeyInfos=nonSplittedFormat[key];
|
|
542
|
-
if(!foundKeyInfos){
|
|
543
|
-
nonSplittedFormat[key]={};
|
|
544
|
-
foundKeyInfos=nonSplittedFormat[key];
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
let foundTextForLanguage=foundKeyInfos[lang];
|
|
548
|
-
if(foundTextForLanguage){
|
|
549
|
-
// TRACE
|
|
550
|
-
console.log("WARN : Duplicate entry for translation key «"+key+"» for language «"+lang+"», keeping only the first declared :",foundTextForLanguage);
|
|
551
|
-
}else{
|
|
552
|
-
foundKeyInfos[lang]=text;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
});
|
|
556
|
-
});
|
|
557
|
-
|
|
558
|
-
// Then we use the standard declaration method :
|
|
559
|
-
i18n.declare(nonSplittedFormat);
|
|
560
|
-
};
|
|
561
|
-
|
|
562
|
-
// For generating languages files helping purposes :
|
|
563
|
-
i18n.displayAll=function(displaySingleEntriesCalls=false){
|
|
564
|
-
let foundRepository=(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME];
|
|
565
|
-
if(!foundRepository){
|
|
566
|
-
// TRACE
|
|
567
|
-
console.log("ERROR : No repository found under the variable name «"+I18N_REPOSITORY_VARIABLE_NAME+"»");
|
|
568
|
-
}else{
|
|
569
|
-
console.log("TRACE : Variable under the name of «"+I18N_REPOSITORY_VARIABLE_NAME+"»:", foundRepository);
|
|
570
|
-
console.log("\n\n");
|
|
571
|
-
|
|
572
|
-
let message="";
|
|
573
|
-
foreach(foundRepository, (entry, entryKey)=>{
|
|
574
|
-
let normalizedEntry={};
|
|
575
|
-
if(entry.capitalize) normalizedEntry.capitalize=entry.capitalize;
|
|
576
|
-
if(entry.append) normalizedEntry.append=entry.append;
|
|
577
|
-
foreach(entry,(e,k)=>{
|
|
578
|
-
normalizedEntry[k]=e;
|
|
579
|
-
}
|
|
580
|
-
,(i,key)=>{ return (!contains(["append","capitalize"],key)); }
|
|
581
|
-
,(i1,i2)=>{ return (i1.key==i2.key?0:(i1.key=="fr"?-1:1));/*(Sorry, French always comes first !)*/}
|
|
582
|
-
);
|
|
583
|
-
let entryStr=stringifyObject(normalizedEntry);
|
|
584
|
-
message+="\""+entryKey+"\":"+entryStr+",\n";
|
|
585
|
-
if(displaySingleEntriesCalls) message+="// i18n("+entryStr+");\n";
|
|
586
|
-
});
|
|
587
|
-
|
|
588
|
-
console.log("\n"+message);
|
|
589
|
-
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
// -------------------------------------------------------------------------------------------
|
|
598
|
-
|
|
599
|
-
// Mouse management :
|
|
600
|
-
|
|
601
|
-
// Code from (www.adomas.org/javascript-mouse-wheel)
|
|
602
|
-
function initMouseWheel(handle,/* OPTIONAL */filterFunction){
|
|
603
|
-
|
|
604
|
-
var wheel=function(event){
|
|
605
|
-
|
|
606
|
-
if (filterFunction && !filterFunction(event.target))
|
|
607
|
-
return;
|
|
608
|
-
|
|
609
|
-
var delta=0;
|
|
610
|
-
if (!event)
|
|
611
|
-
event=window.event;
|
|
612
|
-
if (event.wheelDelta){
|
|
613
|
-
delta=event.wheelDelta / 120;
|
|
614
|
-
} else if (event.detail){
|
|
615
|
-
delta=-event.detail / 3;
|
|
616
|
-
}
|
|
617
|
-
if (delta)
|
|
618
|
-
handle(delta);
|
|
619
|
-
if (event.preventDefault)
|
|
620
|
-
event.preventDefault();
|
|
621
|
-
event.returnValue=false;
|
|
622
|
-
};
|
|
336
|
+
var delta=0;
|
|
337
|
+
if (!event)
|
|
338
|
+
event=window.event;
|
|
339
|
+
if (event.wheelDelta){
|
|
340
|
+
delta=event.wheelDelta / 120;
|
|
341
|
+
} else if (event.detail){
|
|
342
|
+
delta=-event.detail / 3;
|
|
343
|
+
}
|
|
344
|
+
if (delta)
|
|
345
|
+
handle(delta);
|
|
346
|
+
if (event.preventDefault)
|
|
347
|
+
event.preventDefault();
|
|
348
|
+
event.returnValue=false;
|
|
349
|
+
};
|
|
623
350
|
|
|
624
351
|
/* Initialization code. */
|
|
625
352
|
if (window.addEventListener){
|
|
@@ -6782,6 +6509,9 @@ initClient=function(isNodeContext=true, useSocketIOImplementation=false, doOnSer
|
|
|
6782
6509
|
aotraClient.client={};
|
|
6783
6510
|
aotraClient.client.start=function(){
|
|
6784
6511
|
|
|
6512
|
+
// DBG
|
|
6513
|
+
lognow("INFO : Setting up Websocket implementation server :...");
|
|
6514
|
+
|
|
6785
6515
|
|
|
6786
6516
|
let socketToServer=WebsocketImplementation.getStatic(isNodeContext,useSocketIOImplementation).connectToServer(url, port, isSecure, timeout);
|
|
6787
6517
|
if(!socketToServer){
|
|
@@ -7121,7 +6851,7 @@ createFusrodaClient=function(doOnClientReady, doOnDataReception, urlParam=null,
|
|
|
7121
6851
|
|
|
7122
6852
|
|
|
7123
6853
|
|
|
7124
|
-
/*utils GEOMETRY library associated with aotra version : «1.0.0.000 (
|
|
6854
|
+
/*utils GEOMETRY library associated with aotra version : «1.0.0.000 (12/07/2022-23:38:27)»*/
|
|
7125
6855
|
/*-----------------------------------------------------------------------------*/
|
|
7126
6856
|
|
|
7127
6857
|
|
|
@@ -7955,368 +7685,1670 @@ Math.compareDistances=function(p1, p2, distance, comparison){
|
|
|
7955
7685
|
return false;
|
|
7956
7686
|
};
|
|
7957
7687
|
|
|
7958
|
-
Math.getSquaredDistance=function(p1, p2){
|
|
7959
|
-
return Math.pow(Math.abs(p2.x-p1.x), 2) + Math.pow(Math.abs(p2.y-p1.y), 2) + ((p1.z!=null && p2.z!=null)?Math.pow(Math.abs(p2.z-p1.z), 2):0);
|
|
7960
|
-
};
|
|
7688
|
+
Math.getSquaredDistance=function(p1, p2){
|
|
7689
|
+
return Math.pow(Math.abs(p2.x-p1.x), 2) + Math.pow(Math.abs(p2.y-p1.y), 2) + ((p1.z!=null && p2.z!=null)?Math.pow(Math.abs(p2.z-p1.z), 2):0);
|
|
7690
|
+
};
|
|
7691
|
+
|
|
7692
|
+
Math.getDistance=function(p1, p2){
|
|
7693
|
+
return Math.sqrt(Math.getSquaredDistance(p1,p2));
|
|
7694
|
+
};
|
|
7695
|
+
|
|
7696
|
+
|
|
7697
|
+
|
|
7698
|
+
|
|
7699
|
+
Math.getBarycenter=function(coordsArray){
|
|
7700
|
+
|
|
7701
|
+
let coordsX=[];
|
|
7702
|
+
let coordsY=[];
|
|
7703
|
+
foreach(coordsArray,(coords)=>{
|
|
7704
|
+
coordsX.push(coords.x);
|
|
7705
|
+
coordsY.push(coords.y);
|
|
7706
|
+
});
|
|
7707
|
+
|
|
7708
|
+
// Barycenters calculation :
|
|
7709
|
+
let bx=Math.floor(Math.averageInArray(coordsX));
|
|
7710
|
+
let by=Math.floor(Math.averageInArray(coordsY));
|
|
7711
|
+
|
|
7712
|
+
|
|
7713
|
+
return {x:bx,y:by};
|
|
7714
|
+
}
|
|
7715
|
+
|
|
7716
|
+
|
|
7717
|
+
Math.getBarycentersFromZonesWithColors=function(zonesWithColors, withColors=true){
|
|
7718
|
+
let barycenters=[];
|
|
7719
|
+
|
|
7720
|
+
foreach(zonesWithColors,(singleContigousZonePixelsCoords)=>{
|
|
7721
|
+
let barycenter=Math.getBarycenter(singleContigousZonePixelsCoords);
|
|
7722
|
+
barycenters.push(barycenter);
|
|
7723
|
+
|
|
7724
|
+
let zoneLength=singleContigousZonePixelsCoords.length;
|
|
7725
|
+
|
|
7726
|
+
if(withColors){
|
|
7727
|
+
// barycenter color :
|
|
7728
|
+
let avgR=0;
|
|
7729
|
+
let avgG=0;
|
|
7730
|
+
let avgB=0;
|
|
7731
|
+
foreach(singleContigousZonePixelsCoords,(coords)=>{
|
|
7732
|
+
let color=coords.color;
|
|
7733
|
+
avgR+=color.r;
|
|
7734
|
+
avgG+=color.g;
|
|
7735
|
+
avgB+=color.b;
|
|
7736
|
+
});
|
|
7737
|
+
barycenter.color={r:avgR/zoneLength, g:avgG/zoneLength, b:avgB/zoneLength};
|
|
7738
|
+
}else{
|
|
7739
|
+
barycenter.color={r:128, g:128, b:128};
|
|
7740
|
+
}
|
|
7741
|
+
|
|
7742
|
+
});
|
|
7743
|
+
|
|
7744
|
+
return barycenters;
|
|
7745
|
+
}
|
|
7746
|
+
|
|
7747
|
+
|
|
7748
|
+
Math.getBidimensional8Direction=(currentPosition,destination,invertYAxis=false)=>{
|
|
7749
|
+
|
|
7750
|
+
let x=currentPosition.x;
|
|
7751
|
+
let y=currentPosition.y;
|
|
7752
|
+
|
|
7753
|
+
let deltaX=(destination.x-x);
|
|
7754
|
+
let deltaY=(invertYAxis?-1:1)*(destination.y-y);
|
|
7755
|
+
|
|
7756
|
+
if(deltaX===0 && deltaY===0) return null;
|
|
7757
|
+
|
|
7758
|
+
let directionXAxis=(deltaX<0?"W":"E");
|
|
7759
|
+
let directionYAxis=(deltaY<0?"S":"N");
|
|
7760
|
+
if(deltaX===0) return directionYAxis;
|
|
7761
|
+
if(deltaY===0) return directionXAxis;
|
|
7762
|
+
|
|
7763
|
+
let tanRatio=(deltaY/deltaX);
|
|
7764
|
+
|
|
7765
|
+
if(0<deltaX && 0<deltaY){ // 0 -> PI/2 quarter :
|
|
7766
|
+
if(tanRatio<=Math.TAN_RATIOS_DEGREES[22.5]) return "E";
|
|
7767
|
+
if(Math.TAN_RATIOS_DEGREES[22.5]<=tanRatio && tanRatio<Math.TAN_RATIOS_DEGREES[67.5]) return "NE";
|
|
7768
|
+
if(Math.TAN_RATIOS_DEGREES[67.5]<=tanRatio) return "N";
|
|
7769
|
+
}else if(deltaX<0 && 0<deltaY){ // PI/2 -> PI quarter :
|
|
7770
|
+
if(tanRatio<=Math.TAN_RATIOS_DEGREES[112.5]) return "N";
|
|
7771
|
+
if(Math.TAN_RATIOS_DEGREES[112.5]<=tanRatio && tanRatio<Math.TAN_RATIOS_DEGREES[157.5]) return "NW";
|
|
7772
|
+
if(Math.TAN_RATIOS_DEGREES[157.5]<=tanRatio) return "W";
|
|
7773
|
+
}else if(deltaX<0 && deltaY<0){ // PI -> (3/2)*PI quarter :
|
|
7774
|
+
if(tanRatio<=Math.TAN_RATIOS_DEGREES[202.5]) return "W";
|
|
7775
|
+
if(Math.TAN_RATIOS_DEGREES[202.5]<=tanRatio && tanRatio<Math.TAN_RATIOS_DEGREES[247.5]) return "SW";
|
|
7776
|
+
if(Math.TAN_RATIOS_DEGREES[247.5]<=tanRatio) return "S";
|
|
7777
|
+
}else{ // (3/2)*PI -> 2*PI quarter :
|
|
7778
|
+
if(tanRatio<=Math.TAN_RATIOS_DEGREES[292.5]) return "S";
|
|
7779
|
+
if(Math.TAN_RATIOS_DEGREES[292.5]<=tanRatio && tanRatio<Math.TAN_RATIOS_DEGREES[337.5]) return "SE";
|
|
7780
|
+
if(Math.TAN_RATIOS_DEGREES[337.5]<=tanRatio) return "E";
|
|
7781
|
+
}
|
|
7782
|
+
return null;
|
|
7783
|
+
};
|
|
7784
|
+
|
|
7785
|
+
|
|
7786
|
+
|
|
7787
|
+
/*public static*/window.getRandomPositionInZone=function(positionZone, xOffset=0, yOffset=0, zOffset=0, avoidOverlap=null, size=null, randomizeAngles=false){
|
|
7788
|
+
|
|
7789
|
+
let w=nonull(positionZone.w,0);
|
|
7790
|
+
let h=nonull(positionZone.h,0);
|
|
7791
|
+
let d=nonull(positionZone.d,0);
|
|
7792
|
+
|
|
7793
|
+
let demiW=w*.5;
|
|
7794
|
+
let demiH=h*.5;
|
|
7795
|
+
let demiD=d*.5;
|
|
7796
|
+
|
|
7797
|
+
let xOrZero=nonull(positionZone.x,0);
|
|
7798
|
+
let yOrZero=nonull(positionZone.y,0);
|
|
7799
|
+
let zOrZero=nonull(positionZone.z,0);
|
|
7800
|
+
|
|
7801
|
+
// If respectively width or height is 0, then there is no need to use a randomization !
|
|
7802
|
+
let x= ( w<=0 ? xOrZero : (xOrZero + Math.getRandomInt(Math.floor(demiW), -Math.ceil(demiW)))) + xOffset;
|
|
7803
|
+
let y= ( h<=0 ? yOrZero : (yOrZero + Math.getRandomInt(Math.floor(demiH), -Math.ceil(demiH)))) + yOffset;
|
|
7804
|
+
let z= ( d<=0 ? zOrZero : (zOrZero + Math.getRandomInt(Math.floor(demiD), -Math.ceil(demiD)))) + zOffset;
|
|
7805
|
+
|
|
7806
|
+
if(avoidOverlap && size){
|
|
7807
|
+
|
|
7808
|
+
let itemW=nonull(size.w,0);
|
|
7809
|
+
let itemH=nonull(size.h,0);
|
|
7810
|
+
let itemD=nonull(size.d,0);
|
|
7811
|
+
|
|
7812
|
+
if(xOrZero/*includes case==0*/ && 0<itemW && contains(avoidOverlap,"x")) x=Math.floor(x/itemW)*itemW;
|
|
7813
|
+
if(yOrZero/*includes case==0*/ && 0<itemH && contains(avoidOverlap,"y")) y=Math.floor(y/itemH)*itemH;
|
|
7814
|
+
if(zOrZero/*includes case==0*/ && 0<itemD && contains(avoidOverlap,"z")) z=Math.floor(z/itemD)*itemD;
|
|
7815
|
+
|
|
7816
|
+
}
|
|
7817
|
+
|
|
7818
|
+
let b=positionZone.b;
|
|
7819
|
+
let g=positionZone.g;
|
|
7820
|
+
let a=positionZone.a;
|
|
7821
|
+
if(randomizeAngles){
|
|
7822
|
+
b=Math.random()*Math.PI*2;
|
|
7823
|
+
g=Math.random()*Math.PI*2;
|
|
7824
|
+
a=Math.random()*Math.PI*2;
|
|
7825
|
+
}
|
|
7826
|
+
|
|
7827
|
+
return {x:x, y:y, z:z, b:b, g:g, a:a, parallax:positionZone.parallax};
|
|
7828
|
+
}
|
|
7829
|
+
|
|
7830
|
+
|
|
7831
|
+
// CAUTION : ONLY HANDLES «center-positionned» ZONES !
|
|
7832
|
+
/*public static*/window.isZoneCollidingWithZone=function(zone1, zone2){
|
|
7833
|
+
|
|
7834
|
+
let demiWidth1=zone1.w*.5;
|
|
7835
|
+
let demiWidth2=zone2.w*.5;
|
|
7836
|
+
|
|
7837
|
+
let xMin1=zone1.x-demiWidth1;
|
|
7838
|
+
let xMax1=zone1.x+demiWidth1;
|
|
7839
|
+
let xMin2=zone2.x-demiWidth2;
|
|
7840
|
+
let xMax2=zone2.x+demiWidth2;
|
|
7841
|
+
|
|
7842
|
+
//OLD : let widthsOverlap=((xMin2<xMin1 && xMin1<xMax2) || (xMin2<xMax1 && xMax1<xMax2)
|
|
7843
|
+
// || (xMin1<xMin2 && xMin2<xMax1) || (xMin1<xMax2 && xMax2<xMax1));
|
|
7844
|
+
|
|
7845
|
+
let widthsOverlap=(xMin1<xMax2 && xMin2<xMax1);
|
|
7846
|
+
|
|
7847
|
+
|
|
7848
|
+
let demiHeight1=zone1.h*.5;
|
|
7849
|
+
let demiHeight2=zone2.h*.5;
|
|
7850
|
+
|
|
7851
|
+
let yMin1=zone1.y-demiHeight1;
|
|
7852
|
+
let yMax1=zone1.y+demiHeight1;
|
|
7853
|
+
let yMin2=zone2.y-demiHeight2;
|
|
7854
|
+
let yMax2=zone2.y+demiHeight2;
|
|
7855
|
+
|
|
7856
|
+
//OLD : let heightsOverlap=((yMin1<yMin2 && yMin2<yMax1) || (yMin1<yMax2 && yMax2<yMax1)
|
|
7857
|
+
// || (yMin2<yMin1 && yMin1<yMax2) || (yMin2<yMax1 && yMax1<yMax2));
|
|
7858
|
+
|
|
7859
|
+
let heightsOverlap=(yMin1<yMax2 && yMin2<yMax1);
|
|
7860
|
+
|
|
7861
|
+
return widthsOverlap && heightsOverlap;
|
|
7862
|
+
}
|
|
7863
|
+
|
|
7864
|
+
|
|
7865
|
+
|
|
7866
|
+
// CAUTION : ONLY HANDLES «center-positionned» ZONES !
|
|
7867
|
+
/*public static*/window.getIntersectionZone=function(zone1, zone2){
|
|
7868
|
+
if(!isZoneCollidingWithZone(zone1, zone2)) return null;
|
|
7869
|
+
|
|
7870
|
+
let edges=[];
|
|
7871
|
+
|
|
7872
|
+
|
|
7873
|
+
// Horizontal axis :
|
|
7874
|
+
let hasXAxisWholeZone1=false;
|
|
7875
|
+
let hasXAxisWholeZone2=false;
|
|
7876
|
+
|
|
7877
|
+
let demiWidth1=zone1.w*.5;
|
|
7878
|
+
let demiWidth2=zone2.w*.5;
|
|
7879
|
+
let xMin1=zone1.x-demiWidth1;
|
|
7880
|
+
let xMax1=zone1.x+demiWidth1;
|
|
7881
|
+
let xMin2=zone2.x-demiWidth2;
|
|
7882
|
+
let xMax2=zone2.x+demiWidth2;
|
|
7883
|
+
|
|
7884
|
+
let deltaXMins=xMin2-xMin1;
|
|
7885
|
+
let deltaXMaxs=xMax2-xMax1;
|
|
7886
|
+
|
|
7887
|
+
let deltaXMinsSign=(deltaXMins<0?-1:1);
|
|
7888
|
+
let deltaXMaxsSign=(deltaXMaxs<0?-1:1);
|
|
7889
|
+
let intersectionXMin=0;
|
|
7890
|
+
let intersectionXMax=0;
|
|
7891
|
+
if(deltaXMinsSign==deltaXMaxsSign){ // case «parallelogram»
|
|
7892
|
+
if(deltaXMinsSign<0){ // case /=/ parallelogram
|
|
7893
|
+
intersectionXMin=xMin1;
|
|
7894
|
+
intersectionXMax=xMax2;
|
|
7895
|
+
// We calculate edges :
|
|
7896
|
+
edges.push("left");
|
|
7897
|
+
}else{ // case \=\ parallelogram
|
|
7898
|
+
intersectionXMin=xMin2;
|
|
7899
|
+
intersectionXMax=xMax1;
|
|
7900
|
+
// We calculate edges :
|
|
7901
|
+
edges.push("right");
|
|
7902
|
+
}
|
|
7903
|
+
}else{ // case «trapeze»
|
|
7904
|
+
if(deltaXMinsSign<0){ // case /=\ trapeze
|
|
7905
|
+
intersectionXMin=xMin1; // Basically zone1 x !
|
|
7906
|
+
intersectionXMax=xMax1;
|
|
7907
|
+
// We calculate edges :
|
|
7908
|
+
hasXAxisWholeZone1=true;
|
|
7909
|
+
}else{ // case \=/ trapeze
|
|
7910
|
+
intersectionXMin=xMin2; // Basically zone2 x !
|
|
7911
|
+
intersectionXMax=xMax2;
|
|
7912
|
+
// We calculate edges :
|
|
7913
|
+
hasXAxisWholeZone2=true;
|
|
7914
|
+
}
|
|
7915
|
+
}
|
|
7916
|
+
|
|
7917
|
+
let w=intersectionXMax-intersectionXMin;
|
|
7918
|
+
let x=(intersectionXMax+intersectionXMin)*.5; // (The average)
|
|
7919
|
+
|
|
7920
|
+
|
|
7921
|
+
// Vertical axis :
|
|
7922
|
+
|
|
7923
|
+
let hasYAxisWholeZone1=false;
|
|
7924
|
+
let hasYAxisWholeZone2=false;
|
|
7925
|
+
|
|
7926
|
+
let demiHeight1=zone1.h*.5;
|
|
7927
|
+
let demiHeight2=zone2.h*.5;
|
|
7928
|
+
|
|
7929
|
+
let yMin1=zone1.y-demiHeight1;
|
|
7930
|
+
let yMax1=zone1.y+demiHeight1;
|
|
7931
|
+
let yMin2=zone2.y-demiHeight2;
|
|
7932
|
+
let yMax2=zone2.y+demiHeight2;
|
|
7933
|
+
|
|
7934
|
+
|
|
7935
|
+
let deltaYMins=yMin2-yMin1;
|
|
7936
|
+
let deltaYMaxs=yMax2-yMax1;
|
|
7937
|
+
|
|
7938
|
+
let deltaYMinsSign=(deltaYMins<0?-1:1);
|
|
7939
|
+
let deltaYMaxsSign=(deltaYMaxs<0?-1:1);
|
|
7940
|
+
let intersectionYMin=0;
|
|
7941
|
+
let intersectionYMax=0;
|
|
7942
|
+
if(deltaYMinsSign==deltaYMaxsSign){ // case «parallelogram»
|
|
7943
|
+
if(deltaYMinsSign<0){ // case /=/ parallelogram
|
|
7944
|
+
intersectionYMin=yMin1;
|
|
7945
|
+
intersectionYMax=yMax2;
|
|
7946
|
+
// We calculate edges :
|
|
7947
|
+
edges.push("bottom"); // Here the Y axis is NOT inverted !
|
|
7948
|
+
}else{ // case \=\ parallelogram
|
|
7949
|
+
intersectionYMin=yMin2;
|
|
7950
|
+
intersectionYMax=yMax1;
|
|
7951
|
+
// We calculate edges :
|
|
7952
|
+
edges.push("top"); // Here the Y axis is NOT inverted !
|
|
7953
|
+
}
|
|
7954
|
+
}else{ // case «trapeze»
|
|
7955
|
+
if(deltaYMinsSign<0){ // case /=\ trapeze
|
|
7956
|
+
intersectionYMin=yMin1; // Basically zone2 y !
|
|
7957
|
+
intersectionYMax=yMax1;
|
|
7958
|
+
// We calculate edges :
|
|
7959
|
+
hasYAxisWholeZone1=true;
|
|
7960
|
+
}else{ // case \=/ trapeze
|
|
7961
|
+
intersectionYMin=yMin2; // Basically zone2 y !
|
|
7962
|
+
intersectionYMax=yMax2;
|
|
7963
|
+
// We calculate edges :
|
|
7964
|
+
hasYAxisWholeZone2=true;
|
|
7965
|
+
}
|
|
7966
|
+
}
|
|
7967
|
+
|
|
7968
|
+
let h=intersectionYMax-intersectionYMin;
|
|
7969
|
+
let y=(intersectionYMax+intersectionYMin)*.5; // (The average)
|
|
7970
|
+
|
|
7971
|
+
// We calculate edges :
|
|
7972
|
+
if(hasXAxisWholeZone1 && hasYAxisWholeZone1) edges.push("wholeZone1");
|
|
7973
|
+
if(hasXAxisWholeZone2 && hasYAxisWholeZone2) edges.push("wholeZone2");
|
|
7974
|
+
|
|
7975
|
+
return {x:x,y:y,w:w,h:h,edges:edges};
|
|
7976
|
+
}
|
|
7977
|
+
|
|
7978
|
+
/*public static*/window.areZonesIdentical=function(zone1, zone2){
|
|
7979
|
+
return (zone1==zone2 || (zone1.x==zone2.x || zone1.y==zone2.y || zone1.w==zone2.w || zone1.h==zone2.h));
|
|
7980
|
+
}
|
|
7981
|
+
|
|
7982
|
+
|
|
7983
|
+
|
|
7984
|
+
// 3D :
|
|
7985
|
+
|
|
7986
|
+
|
|
7987
|
+
/*
|
|
7988
|
+
// (needs the jsgl library file !)
|
|
7989
|
+
// UNUSED
|
|
7990
|
+
function screenToSpherePt(mouseX, mouseY) {
|
|
7991
|
+
var p = { x: camera_mat.e12, y: camera_mat.e13, z: camera_mat.e14 + 1 };
|
|
7992
|
+
// camera dir
|
|
7993
|
+
var r = { x: camera_mat.e0, y: camera_mat.e1, z: camera_mat.e2 };
|
|
7994
|
+
var up = { x: camera_mat.e4, y: camera_mat.e5, z: camera_mat.e6 };
|
|
7995
|
+
var right = { x: camera_mat.e8, y: camera_mat.e9, z: camera_mat.e10 };
|
|
7996
|
+
var tan_half = Math.tan(horizontal_fov_radians / 2);
|
|
7997
|
+
r = jsgl.vectorAdd(r, jsgl.vectorScale(right, mouseX * tan_half));
|
|
7998
|
+
r = jsgl.vectorAdd(r, jsgl.vectorScale(up, mouseY * tan_half));
|
|
7999
|
+
r = jsgl.vectorNormalize(r);
|
|
8000
|
+
|
|
8001
|
+
return rayVsUnitSphereClosestPoint(p, r);
|
|
8002
|
+
}
|
|
8003
|
+
*/
|
|
8004
|
+
|
|
8005
|
+
/*
|
|
8006
|
+
// (needs the jsgl library file !)
|
|
8007
|
+
// UNUSED
|
|
8008
|
+
// Return the first exterior hit or closest point between the unit
|
|
8009
|
+
// sphere and the ray starting at p and going in the r direction.
|
|
8010
|
+
function rayVsUnitSphereClosestPoint(p, r) {
|
|
8011
|
+
var p_len2 = jsgl.dotProduct(p, p);
|
|
8012
|
+
if (p_len2 < 1) {
|
|
8013
|
+
// Ray is inside sphere, no exterior hit.
|
|
8014
|
+
return null;
|
|
8015
|
+
}
|
|
8016
|
+
|
|
8017
|
+
var along_ray = -jsgl.dotProduct(p, r);
|
|
8018
|
+
if (along_ray < 0) {
|
|
8019
|
+
// Behind ray start-point.
|
|
8020
|
+
return null;
|
|
8021
|
+
}
|
|
8022
|
+
|
|
8023
|
+
var perp = jsgl.vectorAdd(p, jsgl.vectorScale(r, along_ray));
|
|
8024
|
+
var perp_len2 = jsgl.dotProduct(perp, perp);
|
|
8025
|
+
if (perp_len2 >= 0.999999) {
|
|
8026
|
+
// Return the closest point.
|
|
8027
|
+
return jsgl.vectorNormalize(perp);
|
|
8028
|
+
}
|
|
8029
|
+
|
|
8030
|
+
// Compute intersection.
|
|
8031
|
+
var e = Math.sqrt(1 - jsgl.dotProduct(perp, perp));
|
|
8032
|
+
var hit = jsgl.vectorAdd(p, jsgl.vectorScale(r, (along_ray - e)));
|
|
8033
|
+
return jsgl.vectorNormalize(hit);
|
|
8034
|
+
}
|
|
8035
|
+
*/
|
|
8036
|
+
|
|
8037
|
+
|
|
8038
|
+
|
|
8039
|
+
|
|
8040
|
+
|
|
8041
|
+
|
|
8042
|
+
|
|
8043
|
+
|
|
8044
|
+
|
|
8045
|
+
|
|
8046
|
+
|
|
8047
|
+
|
|
8048
|
+
|
|
8049
|
+
|
|
8050
|
+
|
|
8051
|
+
|
|
8052
|
+
|
|
8053
|
+
|
|
8054
|
+
|
|
8055
|
+
|
|
8056
|
+
|
|
8057
|
+
// MUST REMAIN AT THE END OF THIS LIBRARY FILE !
|
|
8058
|
+
|
|
8059
|
+
AOTRAUTILS_GEOMETRY_LIB_IS_LOADED=true;
|
|
8060
|
+
/*utils SERVER library associated with aotra version : «1.0.0.000 (12/07/2022-23:38:27)»*/
|
|
8061
|
+
/*-----------------------------------------------------------------------------*/
|
|
8062
|
+
|
|
8063
|
+
|
|
8064
|
+
/* ## Utility global methods in a javascript, console (nodejs) server, or vanilla javascript with no browser environment.
|
|
8065
|
+
*
|
|
8066
|
+
* This set of methods gathers utility generic-purpose methods usable in any JS project.
|
|
8067
|
+
* Several authors of snippets published freely on the Internet contributed to this library.
|
|
8068
|
+
* Feel free to use/modify-enhance/publish them under the terms of its license.
|
|
8069
|
+
*
|
|
8070
|
+
* # Library name : «aotrautils»
|
|
8071
|
+
* # Library license : HGPL(Help Burma) (see aotra README information for details : https://alqemia.com/aotra.js )
|
|
8072
|
+
* # Author name : Jérémie Ratomposon (massively helped by his native country free education system)
|
|
8073
|
+
* # Author email : info@alqemia.com
|
|
8074
|
+
* # Organization name : Alqemia
|
|
8075
|
+
* # Organization email : admin@alqemia.com
|
|
8076
|
+
* # Organization website : https://alqemia.com
|
|
8077
|
+
*
|
|
8078
|
+
*
|
|
8079
|
+
*/
|
|
8080
|
+
|
|
8081
|
+
|
|
8082
|
+
|
|
8083
|
+
// COMPATIBILITY browser javascript / nodejs environment :
|
|
8084
|
+
|
|
8085
|
+
if(typeof(window)==="undefined") window=global;
|
|
8086
|
+
|
|
8087
|
+
|
|
8088
|
+
|
|
8089
|
+
|
|
8090
|
+
// =================================================================================
|
|
8091
|
+
// NODEJS UTILS
|
|
8092
|
+
|
|
8093
|
+
const FILE_ENCODING="utf8";
|
|
8094
|
+
|
|
8095
|
+
|
|
8096
|
+
// Nodejs filesystem utils :
|
|
8097
|
+
if(typeof(fs)==="undefined"){
|
|
8098
|
+
// TRACE
|
|
8099
|
+
console.log("WARN : Could not find the nodejs dependency «fs», aborting persister setup.");
|
|
8100
|
+
window.getPersister=()=>{ return null; };
|
|
8101
|
+
|
|
8102
|
+
}else{
|
|
8103
|
+
|
|
8104
|
+
|
|
8105
|
+
window.getPersister=function(dataDirPath,suffix=""){
|
|
8106
|
+
|
|
8107
|
+
let self={
|
|
8108
|
+
|
|
8109
|
+
dataDirPath:dataDirPath,
|
|
8110
|
+
suffix:suffix,
|
|
8111
|
+
|
|
8112
|
+
//FILE_NAME_PATTERN:"data.clientId.repositoryName.json",
|
|
8113
|
+
/*private*/getPath:function(clientId,repositoryName){
|
|
8114
|
+
// let path=self.FILE_NAME_PATTERN.replace(new RegExp("@clientId@","g"),clientId);
|
|
8115
|
+
let path=`${self.dataDirPath}`
|
|
8116
|
+
+ (blank(self.suffix)?"":(self.suffix+"."))
|
|
8117
|
+
+`${clientId}.${repositoryName}.json`;
|
|
8118
|
+
return path;
|
|
8119
|
+
},
|
|
8120
|
+
|
|
8121
|
+
readTreeObjectFromFile:function(clientId, repositoryName, CLASSNAME_ATTR_NAME=DEFAULT_CLASSNAME_ATTR_NAME){
|
|
8122
|
+
|
|
8123
|
+
let path=self.getPath(clientId,repositoryName);
|
|
8124
|
+
|
|
8125
|
+
let resultFlat=null;
|
|
8126
|
+
|
|
8127
|
+
try{
|
|
8128
|
+
resultFlat=fs.readFileSync(path, FILE_ENCODING);
|
|
8129
|
+
|
|
8130
|
+
}catch(error){
|
|
8131
|
+
// TRACE
|
|
8132
|
+
console.log("ERROR : Could not read file «"+path+"».");
|
|
8133
|
+
|
|
8134
|
+
return null;
|
|
8135
|
+
}
|
|
8136
|
+
|
|
8137
|
+
|
|
8138
|
+
let resultData={};
|
|
8139
|
+
if(!empty(resultFlat)) resultData=JSON.parse(resultFlat);
|
|
8140
|
+
|
|
8141
|
+
|
|
8142
|
+
if(!empty(resultData) && isFlatMap(resultData)){
|
|
8143
|
+
|
|
8144
|
+
resultData=getAsTreeStructure(resultData,true
|
|
8145
|
+
// We have to keep the type information, here too ! (in the sub-objects)
|
|
8146
|
+
,false);
|
|
8147
|
+
|
|
8148
|
+
}
|
|
8149
|
+
|
|
8150
|
+
return resultData;
|
|
8151
|
+
},
|
|
8152
|
+
|
|
8153
|
+
|
|
8154
|
+
saveDataToFileForClient:function(clientId,repositoryName,dataFlatForClient,forceKeepUnflatten=false,doOnSuccess=null){
|
|
8155
|
+
|
|
8156
|
+
|
|
8157
|
+
|
|
8158
|
+
if(!empty(dataFlatForClient) && !isFlatMap(dataFlatForClient) && !forceKeepUnflatten){
|
|
8159
|
+
dataFlatForClient=getAsFlatStructure(dataFlatForClient,true);
|
|
8160
|
+
}
|
|
8161
|
+
|
|
8162
|
+
// reserved characters : -/\^$*+?.()|[]{}
|
|
8163
|
+
// CANNOT USE stringifyObject(...) function because we are in a common, lower-level library !
|
|
8164
|
+
let dataFlatStr=JSON.stringify(dataFlatForClient)
|
|
8165
|
+
// We «aerate» the produced JSON :
|
|
8166
|
+
.replace(/":[\w]*\{/gim,"\":{\n").replace(/,"/gim,",\n\"")
|
|
8167
|
+
// ...except for inline, escaped JSON string representations :
|
|
8168
|
+
.replace(/\\\":[\w]*\{\n/gim,"\\\":{");
|
|
8169
|
+
// NO : .replace(/}/gim,"}\n");
|
|
8170
|
+
|
|
8171
|
+
|
|
8172
|
+
let path=self.getPath(clientId,repositoryName);
|
|
8173
|
+
|
|
8174
|
+
fs.writeFile(path, dataFlatStr, FILE_ENCODING, (error) => {
|
|
8175
|
+
if(error){
|
|
8176
|
+
// TRACE
|
|
8177
|
+
console.log("ERROR : Could not write file «"+path+"»:",error);
|
|
8178
|
+
throw error;
|
|
8179
|
+
}
|
|
8180
|
+
if(doOnSuccess) doOnSuccess(dataFlatForClient);
|
|
8181
|
+
});
|
|
8182
|
+
}
|
|
8183
|
+
|
|
8184
|
+
};
|
|
8185
|
+
|
|
8186
|
+
|
|
8187
|
+
return self;
|
|
8188
|
+
};
|
|
8189
|
+
|
|
8190
|
+
}
|
|
8191
|
+
|
|
8192
|
+
|
|
8193
|
+
|
|
8194
|
+
// Nodejs server launching helper functions :
|
|
8195
|
+
//Networking management :
|
|
8196
|
+
//- WEBSOCKETS AND NODEJS :
|
|
8197
|
+
|
|
8198
|
+
// -Server :
|
|
8199
|
+
|
|
8200
|
+
// UNUSED : (BUT KEEP CODE!!)
|
|
8201
|
+
//getServerParams=function(portParam,isSecureParam,certPathParam,keyPathParam){
|
|
8202
|
+
//
|
|
8203
|
+
// // Node dependencies :
|
|
8204
|
+
// // https=require("https");
|
|
8205
|
+
// // fs=require("fs");
|
|
8206
|
+
//
|
|
8207
|
+
// if(!https){
|
|
8208
|
+
// // TRACE
|
|
8209
|
+
// console.log("WARN : Could not find the nodejs dependency «https», aborting SSL setup.");
|
|
8210
|
+
// return null;
|
|
8211
|
+
// }
|
|
8212
|
+
// if(!fs){
|
|
8213
|
+
// // TRACE
|
|
8214
|
+
// console.log("WARN : Could not find the nodejs dependency «fs», aborting SSL setup.");
|
|
8215
|
+
// return null;
|
|
8216
|
+
// }
|
|
8217
|
+
//
|
|
8218
|
+
// var result={};
|
|
8219
|
+
//
|
|
8220
|
+
// // We read the command-line arguments if needed :
|
|
8221
|
+
// var argCLPort;
|
|
8222
|
+
// var argCLCertPath;
|
|
8223
|
+
// var argCLKeyPath;
|
|
8224
|
+
// process.argv.forEach(function (val, i){
|
|
8225
|
+
// if(i<=1) return;
|
|
8226
|
+
// else if(i==2) argCLPort=val;
|
|
8227
|
+
// else if(i==3) argCLCertPath=val;
|
|
8228
|
+
// else if(i==4) argCLKeyPath=val;
|
|
8229
|
+
// });
|
|
8230
|
+
//
|
|
8231
|
+
// // Console, command-line arguments OVERRIDE parameters values :
|
|
8232
|
+
//
|
|
8233
|
+
// result.port=nonull(argCLPort,portParam);
|
|
8234
|
+
// result.certPath=null;
|
|
8235
|
+
// result.keyPath=null;
|
|
8236
|
+
// result.isSecure=isSecureParam;
|
|
8237
|
+
//
|
|
8238
|
+
// if(isSecureParam){
|
|
8239
|
+
// result.certPath=nonull(argCLCertPath,certPathParam);
|
|
8240
|
+
// result.keyPath=nonull(argCLKeyPath,keyPathParam);
|
|
8241
|
+
// }
|
|
8242
|
+
//
|
|
8243
|
+
// // Eventual encryption options :
|
|
8244
|
+
// result.sslOptions=null;
|
|
8245
|
+
// if(isSecureParam){
|
|
8246
|
+
// result.sslOptions={
|
|
8247
|
+
// cert: fs.readFileSync(certPath),
|
|
8248
|
+
// key: fs.readFileSync(keyPath),
|
|
8249
|
+
// };
|
|
8250
|
+
// }
|
|
8251
|
+
//
|
|
8252
|
+
// return result;
|
|
8253
|
+
//}
|
|
8254
|
+
|
|
8255
|
+
|
|
8256
|
+
// NODE ONLY SERVER / CLIENTS :
|
|
8257
|
+
WebsocketImplementation={
|
|
8258
|
+
|
|
8259
|
+
isNodeContext:true,
|
|
8260
|
+
useSocketIOImplementation:false,
|
|
8261
|
+
useFlatStrings:false,
|
|
8262
|
+
|
|
8263
|
+
// COMMON METHODS
|
|
8264
|
+
/*private static*/isInRoom(clientSocket, clientsRoomsTag){
|
|
8265
|
+
return !clientsRoomsTag || empty(clientsRoomsTag) || contains(clientsRoomsTag, clientSocket.clientRoomTag);
|
|
8266
|
+
},
|
|
8267
|
+
|
|
8268
|
+
|
|
8269
|
+
//
|
|
8270
|
+
// NODE SERVER
|
|
8271
|
+
//
|
|
8272
|
+
getStatic:(isNodeContext=true, useSocketIOImplementation=false)=>{
|
|
8273
|
+
|
|
8274
|
+
WebsocketImplementation.isNodeContext=isNodeContext;
|
|
8275
|
+
|
|
8276
|
+
// cf. https://socket.io/docs/v4/client-socket-instance/
|
|
8277
|
+
WebsocketImplementation.useSocketIOImplementation=useSocketIOImplementation;
|
|
8278
|
+
|
|
8279
|
+
// OLD : socket.io :
|
|
8280
|
+
// https://stackoverflow.com/questions/31156884/how-to-use-https-on-node-js-using-express-socket-io
|
|
8281
|
+
// https://stackoverflow.com/questions/6599470/node-js-socket-io-with-ssl
|
|
8282
|
+
// https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener
|
|
8283
|
+
|
|
8284
|
+
// if(typeof(socketIO)==="undefined"){
|
|
8285
|
+
// // TRACE
|
|
8286
|
+
// console.log("«socket.io» SERVER library not called yet, calling it now.");
|
|
8287
|
+
// socketIO=require("socket.io");
|
|
8288
|
+
// return socketIO;
|
|
8289
|
+
// }
|
|
8290
|
+
// if(typeof(socketIO)==="undefined"){
|
|
8291
|
+
// // TRACE
|
|
8292
|
+
// console.log("ERROR : «socket.io» SERVER library not found. Cannot launch nodejs server. Aborting.");
|
|
8293
|
+
// return null;
|
|
8294
|
+
// }
|
|
8295
|
+
|
|
8296
|
+
// *********************************************************************************
|
|
8297
|
+
|
|
8298
|
+
// NEW : ws :
|
|
8299
|
+
// https://github.com/websockets/ws#installing
|
|
8300
|
+
// https://github.com/websockets/ws/blob/master/doc/ws.md#event-message
|
|
8301
|
+
// ON BROWSER SIDE : Native Websockets :
|
|
8302
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
|
|
8303
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
|
|
8304
|
+
if(typeof(WebSocket)==="undefined"){
|
|
8305
|
+
WebSocket=require("ws");
|
|
8306
|
+
// TRACE
|
|
8307
|
+
console.log("«ws» SERVER library not called yet, calling it now.");
|
|
8308
|
+
return WebsocketImplementation;
|
|
8309
|
+
}
|
|
8310
|
+
if(typeof(WebSocket)==="undefined"){
|
|
8311
|
+
// TRACE
|
|
8312
|
+
console.log("ERROR : «ws» SERVER library not found. Cannot launch nodejs server. Aborting.");
|
|
8313
|
+
return null;
|
|
8314
|
+
}
|
|
8315
|
+
|
|
8316
|
+
return WebsocketImplementation;
|
|
8317
|
+
},
|
|
8318
|
+
|
|
8319
|
+
|
|
8320
|
+
getServer:(listenableServer)=>{
|
|
8321
|
+
|
|
8322
|
+
if(!WebsocketImplementation.isNodeContext){
|
|
8323
|
+
// TRACE
|
|
8324
|
+
throw new Error("ERROR : SERVER : Server launch is not supported in a non-nodejs context for any implementation.");
|
|
8325
|
+
}
|
|
8326
|
+
|
|
8327
|
+
// TODO : FIXME : Use one single interface !
|
|
8328
|
+
// NODE SERVER MODE ONLY :
|
|
8329
|
+
let serverSocket;
|
|
8330
|
+
if(!WebsocketImplementation.useSocketIOImplementation){
|
|
8331
|
+
serverSocket=new WebSocket.Server({ "server":listenableServer });
|
|
8332
|
+
}else{
|
|
8333
|
+
// NOW : socket.io :
|
|
8334
|
+
// Loading socket.io
|
|
8335
|
+
serverSocket=socketIO.listen(listenableServer);
|
|
8336
|
+
|
|
8337
|
+
// Setting up the disctonnect event :
|
|
8338
|
+
serverSocket.on("endConnection",()=>{
|
|
8339
|
+
serverSocket.disconnect();
|
|
8340
|
+
});
|
|
8341
|
+
|
|
8342
|
+
}
|
|
8343
|
+
|
|
8344
|
+
// NODE SERVER INSTANCE :
|
|
8345
|
+
const nodeServerInstance={
|
|
8346
|
+
|
|
8347
|
+
onClientLostListeners:[],
|
|
8348
|
+
|
|
8349
|
+
clientTimeoutMillis:20000,
|
|
8350
|
+
// clientsSockets:[],
|
|
8351
|
+
serverSocket:serverSocket,
|
|
8352
|
+
receptionEntryPoints:[],
|
|
8353
|
+
|
|
8354
|
+
receive:(channelName, doOnIncomingMessage, clientsRoomsTag=null)=>{
|
|
8355
|
+
|
|
8356
|
+
const receptionEntryPoint={
|
|
8357
|
+
channelName:channelName,
|
|
8358
|
+
clientsRoomsTag:clientsRoomsTag,
|
|
8359
|
+
execute:(clientSocketParam)=>{
|
|
8360
|
+
|
|
8361
|
+
|
|
8362
|
+
// With «ws» library we have no choive than register message events inside a «connection» event !
|
|
8363
|
+
// nodeServerInstance.onConnectionToClient((serverParam, clientSocketParam)=>{
|
|
8364
|
+
|
|
8365
|
+
// TODO : Find a way to remove this !
|
|
8366
|
+
const doOnMessage=(message)=>{
|
|
8367
|
+
|
|
8368
|
+
// dataWrapped=JSON.parse(dataWrapped);
|
|
8369
|
+
// dataWrapped=getAt(dataWrapped,0);// We get the root element
|
|
8370
|
+
const dataWrapped=(WebsocketImplementation.useFlatStrings?JSON.parse(message):message);
|
|
8371
|
+
|
|
8372
|
+
|
|
8373
|
+
// Channel information is stored in exchanged data :
|
|
8374
|
+
if(dataWrapped.channelName!==receptionEntryPoint.channelName) return;
|
|
8375
|
+
|
|
8376
|
+
|
|
8377
|
+
const clientSocket=clientSocketParam;
|
|
8378
|
+
|
|
8379
|
+
// TODO : FIXME : Use one single interface !
|
|
8380
|
+
// Room information is stored in client socket object :
|
|
8381
|
+
if(!WebsocketImplementation.isInRoom(clientSocket, receptionEntryPoint.clientsRoomsTag)) return;
|
|
8382
|
+
|
|
8383
|
+
doOnIncomingMessage(dataWrapped.data, clientSocket);
|
|
8384
|
+
|
|
8385
|
+
};
|
|
8386
|
+
|
|
8387
|
+
|
|
8388
|
+
if(!WebsocketImplementation.useSocketIOImplementation) clientSocketParam.addEventListener("message", doOnMessage);
|
|
8389
|
+
else clientSocketParam.on(channelName, doOnMessage);
|
|
8390
|
+
|
|
8391
|
+
|
|
8392
|
+
|
|
8393
|
+
|
|
8394
|
+
},
|
|
8395
|
+
};
|
|
8396
|
+
|
|
8397
|
+
|
|
8398
|
+
nodeServerInstance.receptionEntryPoints.push(receptionEntryPoint);
|
|
8399
|
+
|
|
8400
|
+
|
|
8401
|
+
// DBG
|
|
8402
|
+
console.log("ADD RECEPTION ENTRY POINT channelName:«"+channelName+"»! nodeServerInstance.receptionEntryPoints.length:",nodeServerInstance.receptionEntryPoints.length);
|
|
8403
|
+
|
|
8404
|
+
|
|
8405
|
+
return nodeServerInstance;
|
|
8406
|
+
},
|
|
8407
|
+
|
|
8408
|
+
|
|
8409
|
+
send:(channelName, data, clientsRoomsTag=null, clientSocketParam=null)=>{
|
|
8410
|
+
|
|
8411
|
+
|
|
8412
|
+
if(!clientSocketParam){
|
|
8413
|
+
|
|
8414
|
+
// TODO : FIXME : Use one single interface !
|
|
8415
|
+
let serverClients;
|
|
8416
|
+
if(!WebsocketImplementation.useSocketIOImplementation) serverClients=nodeServerInstance.serverSocket.clients;
|
|
8417
|
+
else serverClients=nodeServerInstance.serverSocket.sockets.clients();
|
|
8418
|
+
|
|
8419
|
+
serverClients.forEach((clientSocket)=>{
|
|
8420
|
+
|
|
8421
|
+
|
|
8422
|
+
// TODO : FIXME : Use one single interface !
|
|
8423
|
+
if(!WebsocketImplementation.useSocketIOImplementation) if(clientSocket.readyState!==WebSocket.OPEN) return;
|
|
8424
|
+
else if(clientSocket.connected) return;
|
|
8425
|
+
|
|
8426
|
+
|
|
8427
|
+
|
|
8428
|
+
// Room information is stored in client socket object :
|
|
8429
|
+
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
8430
|
+
|
|
8431
|
+
// Channel information is stored in exchanged data :
|
|
8432
|
+
let dataWrapped={channelName:channelName, data:data};
|
|
8433
|
+
|
|
8434
|
+
|
|
8435
|
+
dataWrapped=JSON.stringify(dataWrapped);
|
|
8436
|
+
|
|
8437
|
+
// TODO : FIXME : Use one single interface !
|
|
8438
|
+
if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.send(dataWrapped);
|
|
8439
|
+
else clientSocket.emit(channelName,dataWrapped);
|
|
8440
|
+
|
|
8441
|
+
});
|
|
8442
|
+
|
|
8443
|
+
}else{
|
|
8444
|
+
|
|
8445
|
+
// TODO : FIXME : Use one single interface !
|
|
8446
|
+
let clientSocket=clientSocketParam;
|
|
8447
|
+
if(!WebsocketImplementation.useSocketIOImplementation) if(clientSocket.readyState!==WebSocket.OPEN) return;
|
|
8448
|
+
else if(clientSocket.connected) return;
|
|
7961
8449
|
|
|
7962
|
-
Math.getDistance=function(p1, p2){
|
|
7963
|
-
return Math.sqrt(Math.getSquaredDistance(p1,p2));
|
|
7964
|
-
};
|
|
7965
8450
|
|
|
8451
|
+
// Room information is stored in client socket object :
|
|
8452
|
+
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
7966
8453
|
|
|
8454
|
+
// Channel information is stored in exchanged data :
|
|
8455
|
+
let dataWrapped={channelName:channelName, data:data};
|
|
8456
|
+
|
|
8457
|
+
dataWrapped=JSON.stringify(dataWrapped);
|
|
8458
|
+
|
|
8459
|
+
// TODO : FIXME : Use one single interface !
|
|
8460
|
+
if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.send(dataWrapped);
|
|
8461
|
+
else clientSocket.emit(channelName,dataWrapped);
|
|
8462
|
+
|
|
8463
|
+
|
|
8464
|
+
}
|
|
8465
|
+
|
|
8466
|
+
|
|
8467
|
+
return nodeServerInstance;
|
|
8468
|
+
},
|
|
8469
|
+
|
|
8470
|
+
|
|
8471
|
+
onConnectionToClient:(doOnConnection)=>{
|
|
8472
|
+
// «connection» is the only event fired by the serverSocket :
|
|
8473
|
+
nodeServerInstance.serverSocket.on("connection", (clientSocket)=>{
|
|
8474
|
+
|
|
8475
|
+
// DBG
|
|
8476
|
+
console.log("SERVER : ON CONNECTION !");
|
|
8477
|
+
|
|
8478
|
+
|
|
8479
|
+
// if(contains(nodeServerInstance.clientsSockets, clientSocket)) return;
|
|
8480
|
+
// nodeServerInstance.clientsSockets.push(clientSocket);
|
|
7967
8481
|
|
|
7968
8482
|
|
|
7969
|
-
|
|
7970
|
-
|
|
7971
|
-
let coordsX=[];
|
|
7972
|
-
let coordsY=[];
|
|
7973
|
-
foreach(coordsArray,(coords)=>{
|
|
7974
|
-
coordsX.push(coords.x);
|
|
7975
|
-
coordsY.push(coords.y);
|
|
7976
|
-
});
|
|
8483
|
+
// DBG
|
|
8484
|
+
console.log("nodeServerInstance.receptionEntryPoints.length:",nodeServerInstance.receptionEntryPoints.length);
|
|
7977
8485
|
|
|
7978
|
-
// Barycenters calculation :
|
|
7979
|
-
let bx=Math.floor(Math.averageInArray(coordsX));
|
|
7980
|
-
let by=Math.floor(Math.averageInArray(coordsY));
|
|
7981
8486
|
|
|
7982
|
-
|
|
7983
|
-
|
|
7984
|
-
|
|
8487
|
+
|
|
8488
|
+
// We execute the events registration listeners entry points:
|
|
8489
|
+
foreach(nodeServerInstance.receptionEntryPoints,(receptionEntryPoint)=>{
|
|
8490
|
+
|
|
8491
|
+
// DBG
|
|
8492
|
+
console.log("ENTRY POINT !");
|
|
8493
|
+
|
|
8494
|
+
receptionEntryPoint.execute(clientSocket);
|
|
8495
|
+
});
|
|
7985
8496
|
|
|
7986
8497
|
|
|
7987
|
-
|
|
7988
|
-
|
|
7989
|
-
|
|
7990
|
-
|
|
7991
|
-
|
|
7992
|
-
|
|
7993
|
-
|
|
7994
|
-
|
|
7995
|
-
|
|
7996
|
-
|
|
7997
|
-
|
|
7998
|
-
|
|
7999
|
-
|
|
8000
|
-
|
|
8001
|
-
|
|
8002
|
-
|
|
8003
|
-
|
|
8004
|
-
|
|
8005
|
-
|
|
8498
|
+
doOnConnection(nodeServerInstance, clientSocket);
|
|
8499
|
+
|
|
8500
|
+
|
|
8501
|
+
|
|
8502
|
+
|
|
8503
|
+
// To make the server aware of the clients connections states :
|
|
8504
|
+
clientSocket.stateCheckInterval=setInterval(()=>{
|
|
8505
|
+
|
|
8506
|
+
|
|
8507
|
+
if (clientSocket.isAlive===false){
|
|
8508
|
+
clearInterval(clientSocket.stateCheckInterval);
|
|
8509
|
+
|
|
8510
|
+
// TODO : FIXME : DUPLICATED CODE :
|
|
8511
|
+
// TODO : FIXME : Use one single interface !
|
|
8512
|
+
if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.terminate();
|
|
8513
|
+
else clientSocket.emit("endConnection");
|
|
8514
|
+
|
|
8515
|
+
// TODO : FIXME : DUPLICATED CODE :
|
|
8516
|
+
if(!empty(nodeServerInstance.onClientLostListeners))
|
|
8517
|
+
foreach(nodeServerInstance.onClientLostListeners,l=>{l.execute(clientSocket);});
|
|
8518
|
+
|
|
8519
|
+
return;
|
|
8520
|
+
}
|
|
8521
|
+
|
|
8522
|
+
clientSocket.isAlive=false;
|
|
8523
|
+
try{
|
|
8524
|
+
clientSocket.ping();
|
|
8525
|
+
}catch(error){
|
|
8526
|
+
lognow("ERROR : A problem occurred when tried to ping client socket : ",error);
|
|
8527
|
+
// We effectively end the client connection to this server :
|
|
8528
|
+
|
|
8529
|
+
// TODO : FIXME : DUPLICATED CODE :
|
|
8530
|
+
// TODO : FIXME : Use one single interface !
|
|
8531
|
+
if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.terminate();
|
|
8532
|
+
else clientSocket.emit("endConnection");
|
|
8533
|
+
|
|
8534
|
+
// TODO : FIXME : DUPLICATED CODE :
|
|
8535
|
+
if(!empty(nodeServerInstance.onClientLostListeners))
|
|
8536
|
+
foreach(nodeServerInstance.onClientLostListeners,l=>{l.execute(clientSocket);});
|
|
8537
|
+
|
|
8538
|
+
return;
|
|
8539
|
+
}
|
|
8540
|
+
|
|
8541
|
+
|
|
8542
|
+
}, nodeServerInstance.clientTimeoutMillis);
|
|
8543
|
+
clientSocket.on("pong",()=>{
|
|
8544
|
+
clientSocket.isAlive=true;
|
|
8545
|
+
});
|
|
8546
|
+
|
|
8547
|
+
|
|
8548
|
+
|
|
8549
|
+
});
|
|
8550
|
+
|
|
8551
|
+
|
|
8552
|
+
return nodeServerInstance;
|
|
8553
|
+
},
|
|
8554
|
+
|
|
8555
|
+
onFinalize:(doOnFinalizeServer)=>{
|
|
8556
|
+
|
|
8557
|
+
doOnFinalizeServer(nodeServerInstance);
|
|
8558
|
+
|
|
8559
|
+
// TRACE
|
|
8560
|
+
console.log("INFO : SERVER : Node server setup complete.");
|
|
8561
|
+
|
|
8562
|
+
return nodeServerInstance;
|
|
8563
|
+
},
|
|
8564
|
+
|
|
8565
|
+
addToRoom:(clientSocket,clientRoomTag)=>{
|
|
8566
|
+
clientSocket.clientRoomTag=clientRoomTag;
|
|
8567
|
+
},
|
|
8568
|
+
|
|
8569
|
+
|
|
8570
|
+
|
|
8571
|
+
};
|
|
8572
|
+
|
|
8573
|
+
// Join room server part protocol :
|
|
8574
|
+
nodeServerInstance.receive("protocol",(message, clientSocket)=>{
|
|
8575
|
+
|
|
8576
|
+
if(message.type!=="joinRoom" || !clientSocket) return;
|
|
8577
|
+
|
|
8578
|
+
nodeServerInstance.addToRoom(clientSocket, message.clientRoomTag);
|
|
8579
|
+
|
|
8006
8580
|
});
|
|
8007
|
-
|
|
8008
|
-
|
|
8009
|
-
|
|
8010
|
-
|
|
8581
|
+
|
|
8582
|
+
|
|
8583
|
+
|
|
8584
|
+
// To make the server aware of the clients connections states :
|
|
8585
|
+
nodeServerInstance.serverSocket.on("close", function close() {
|
|
8586
|
+
|
|
8587
|
+
// TODO : FIXME : Use one single interface !
|
|
8588
|
+
if(!WebsocketImplementation.useSocketIOImplementation) serverClients=nodeServerInstance.serverSocket.clients;
|
|
8589
|
+
else serverClients=nodeServerInstance.serverSocket.sockets.clients();
|
|
8590
|
+
|
|
8591
|
+
serverClients.forEach((clientSocket)=>{
|
|
8592
|
+
clearInterval(clientSocket.stateCheckInterval);
|
|
8593
|
+
});
|
|
8594
|
+
});
|
|
8595
|
+
|
|
8596
|
+
|
|
8597
|
+
|
|
8598
|
+
return nodeServerInstance;
|
|
8599
|
+
},
|
|
8011
8600
|
|
|
8012
|
-
|
|
8601
|
+
|
|
8602
|
+
// NODE / BROWSER CLIENT CONNECTS TO SERVER MAIN ENTRYPOINT:
|
|
8603
|
+
connectToServer:(serverURL, port, isSecure=false, timeout)=>{
|
|
8013
8604
|
|
|
8014
|
-
|
|
8015
|
-
|
|
8605
|
+
// TRACE
|
|
8606
|
+
lognow("INFO : Using websocket implementation : "+(WebsocketImplementation.isNodeContext?"node (server-side)":"browser (client-side)"));
|
|
8607
|
+
|
|
8608
|
+
if(WebsocketImplementation.isNodeContext)
|
|
8609
|
+
return WebsocketImplementation.connectToServerFromNode(serverURL, port, isSecure, timeout);
|
|
8610
|
+
else
|
|
8611
|
+
return WebsocketImplementation.connectToServerFromBrowser(serverURL, port, isSecure, timeout);
|
|
8612
|
+
},
|
|
8613
|
+
|
|
8614
|
+
|
|
8615
|
+
|
|
8616
|
+
//
|
|
8617
|
+
// NODE CLIENT
|
|
8618
|
+
//
|
|
8619
|
+
/*private*/connectToServerFromNode:(serverURL, port, isSecure, timeout)=>{
|
|
8620
|
+
|
|
8621
|
+
|
|
8622
|
+
|
|
8623
|
+
// NEW : ws :
|
|
8624
|
+
if(typeof(WebSocket)==="undefined"){
|
|
8625
|
+
// TRACE
|
|
8626
|
+
lognow("ERROR : CLIENT : Could not find websocket client lib, aborting client connection.");
|
|
8627
|
+
return null;
|
|
8628
|
+
}
|
|
8629
|
+
|
|
8630
|
+
// NODE CLIENT MODE ONLY :
|
|
8631
|
+
let clientSocket;
|
|
8632
|
+
if(!WebsocketImplementation.useSocketIOImplementation){
|
|
8633
|
+
clientSocket=new WebSocket(serverURL+":"+port,/*WORKAROUND:*/{ rejectUnauthorized:false, secure: isSecure });
|
|
8634
|
+
}else{
|
|
8635
|
+
// NOW : socket.io :
|
|
8636
|
+
//client on server-side:
|
|
8637
|
+
if(WebsocketImplementation.isNodeContext && typeof(socketIO)!=="undefined"){
|
|
8638
|
+
// // Node client :
|
|
8639
|
+
// connectToServer=function (serverURL, port, isSecure, timeout=10000){
|
|
8640
|
+
// return socketIO.connect(serverURL + ":" + port,{timeout: timeout});
|
|
8641
|
+
// }
|
|
8642
|
+
clientSocket=socketIO.connect(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
|
|
8643
|
+
}
|
|
8644
|
+
}
|
|
8016
8645
|
|
|
8646
|
+
// NODE CLIENT INSTANCE :
|
|
8647
|
+
const nodeClientInstance={
|
|
8648
|
+
clientSocket:clientSocket,
|
|
8649
|
+
|
|
8650
|
+
receptionEntryPoints:[],
|
|
8651
|
+
|
|
8652
|
+
receive:(channelName, doOnIncomingMessage, clientsRoomsTag=null)=>{
|
|
8653
|
+
|
|
8654
|
+
const receptionEntryPoint={
|
|
8655
|
+
channelName:channelName,
|
|
8656
|
+
clientsRoomsTag:clientsRoomsTag,
|
|
8657
|
+
execute:(clientSocketParam)=>{
|
|
8658
|
+
|
|
8659
|
+
const doOnMessage=(message)=>{
|
|
8660
|
+
|
|
8661
|
+
// dataWrapped=JSON.parse(dataWrapped);
|
|
8662
|
+
// dataWrapped=getAt(dataWrapped,0);// We get the root element
|
|
8663
|
+
|
|
8664
|
+
const dataWrapped=(WebsocketImplementation.useFlatStrings?JSON.parse(message):message);
|
|
8665
|
+
|
|
8666
|
+
|
|
8667
|
+
// Channel information is stored in exchanged data :
|
|
8668
|
+
if(dataWrapped.channelName && dataWrapped.channelName!==channelName) return;
|
|
8669
|
+
|
|
8670
|
+
let clientSocket=nodeClientInstance.clientSocket;
|
|
8671
|
+
|
|
8672
|
+
// Room information is stored in client socket object :
|
|
8673
|
+
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
8674
|
+
|
|
8675
|
+
doOnIncomingMessage(dataWrapped.data, clientSocket);
|
|
8676
|
+
|
|
8677
|
+
};
|
|
8678
|
+
|
|
8679
|
+
if(!WebsocketImplementation.useSocketIOImplementation) nodeClientInstance.clientSocket.addEventListener("message", doOnMessage);
|
|
8680
|
+
else nodeClientInstance.clientSocket.on(channelName, doOnMessage);
|
|
8017
8681
|
|
|
8018
|
-
|
|
8682
|
+
|
|
8683
|
+
}
|
|
8684
|
+
};
|
|
8685
|
+
|
|
8686
|
+
|
|
8687
|
+
nodeClientInstance.receptionEntryPoints.push(receptionEntryPoint);
|
|
8688
|
+
|
|
8689
|
+
return nodeClientInstance;
|
|
8690
|
+
},
|
|
8691
|
+
|
|
8692
|
+
|
|
8693
|
+
send:(channelName, data, clientsRoomsTag=null)=>{
|
|
8694
|
+
|
|
8695
|
+
let clientSocket=nodeClientInstance.clientSocket;
|
|
8696
|
+
|
|
8697
|
+
|
|
8698
|
+
// // DBG
|
|
8699
|
+
// console.log("(NODE CLIENT) TRYING TO SEND : clientSocket.readyState:",clientSocket.readyState);
|
|
8019
8700
|
|
|
8020
|
-
|
|
8021
|
-
|
|
8022
|
-
|
|
8023
|
-
let deltaX=(destination.x-x);
|
|
8024
|
-
let deltaY=(invertYAxis?-1:1)*(destination.y-y);
|
|
8701
|
+
// TODO : FIXME : Use one single interface !
|
|
8702
|
+
if(!WebsocketImplementation.useSocketIOImplementation) if(clientSocket.readyState!==WebSocket.OPEN) return;
|
|
8703
|
+
else if(clientSocket.connected) return;
|
|
8025
8704
|
|
|
8026
|
-
if(deltaX===0 && deltaY===0) return null;
|
|
8027
8705
|
|
|
8028
|
-
|
|
8029
|
-
|
|
8030
|
-
|
|
8031
|
-
|
|
8032
|
-
|
|
8033
|
-
let tanRatio=(deltaY/deltaX);
|
|
8034
|
-
|
|
8035
|
-
if(0<deltaX && 0<deltaY){ // 0 -> PI/2 quarter :
|
|
8036
|
-
if(tanRatio<=Math.TAN_RATIOS_DEGREES[22.5]) return "E";
|
|
8037
|
-
if(Math.TAN_RATIOS_DEGREES[22.5]<=tanRatio && tanRatio<Math.TAN_RATIOS_DEGREES[67.5]) return "NE";
|
|
8038
|
-
if(Math.TAN_RATIOS_DEGREES[67.5]<=tanRatio) return "N";
|
|
8039
|
-
}else if(deltaX<0 && 0<deltaY){ // PI/2 -> PI quarter :
|
|
8040
|
-
if(tanRatio<=Math.TAN_RATIOS_DEGREES[112.5]) return "N";
|
|
8041
|
-
if(Math.TAN_RATIOS_DEGREES[112.5]<=tanRatio && tanRatio<Math.TAN_RATIOS_DEGREES[157.5]) return "NW";
|
|
8042
|
-
if(Math.TAN_RATIOS_DEGREES[157.5]<=tanRatio) return "W";
|
|
8043
|
-
}else if(deltaX<0 && deltaY<0){ // PI -> (3/2)*PI quarter :
|
|
8044
|
-
if(tanRatio<=Math.TAN_RATIOS_DEGREES[202.5]) return "W";
|
|
8045
|
-
if(Math.TAN_RATIOS_DEGREES[202.5]<=tanRatio && tanRatio<Math.TAN_RATIOS_DEGREES[247.5]) return "SW";
|
|
8046
|
-
if(Math.TAN_RATIOS_DEGREES[247.5]<=tanRatio) return "S";
|
|
8047
|
-
}else{ // (3/2)*PI -> 2*PI quarter :
|
|
8048
|
-
if(tanRatio<=Math.TAN_RATIOS_DEGREES[292.5]) return "S";
|
|
8049
|
-
if(Math.TAN_RATIOS_DEGREES[292.5]<=tanRatio && tanRatio<Math.TAN_RATIOS_DEGREES[337.5]) return "SE";
|
|
8050
|
-
if(Math.TAN_RATIOS_DEGREES[337.5]<=tanRatio) return "E";
|
|
8051
|
-
}
|
|
8052
|
-
return null;
|
|
8053
|
-
};
|
|
8706
|
+
// Room information is stored in client socket object :
|
|
8707
|
+
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
8708
|
+
|
|
8709
|
+
// Channel information is stored in exchanged data :
|
|
8710
|
+
let dataWrapped={channelName:channelName, data:data};
|
|
8054
8711
|
|
|
8055
8712
|
|
|
8713
|
+
// // DBG
|
|
8714
|
+
// console.log("(NODE CLIENT) SENDING DATA ! dataWrapped:",dataWrapped);
|
|
8056
8715
|
|
|
8057
|
-
/*public static*/window.getRandomPositionInZone=function(positionZone, xOffset=0, yOffset=0, zOffset=0, avoidOverlap=null, size=null, randomizeAngles=false){
|
|
8058
|
-
|
|
8059
|
-
let w=nonull(positionZone.w,0);
|
|
8060
|
-
let h=nonull(positionZone.h,0);
|
|
8061
|
-
let d=nonull(positionZone.d,0);
|
|
8062
8716
|
|
|
8063
|
-
|
|
8064
|
-
let demiH=h*.5;
|
|
8065
|
-
let demiD=d*.5;
|
|
8717
|
+
dataWrapped=JSON.stringify(dataWrapped);
|
|
8066
8718
|
|
|
8067
|
-
let xOrZero=nonull(positionZone.x,0);
|
|
8068
|
-
let yOrZero=nonull(positionZone.y,0);
|
|
8069
|
-
let zOrZero=nonull(positionZone.z,0);
|
|
8070
8719
|
|
|
8071
|
-
|
|
8072
|
-
|
|
8073
|
-
|
|
8074
|
-
let z= ( d<=0 ? zOrZero : (zOrZero + Math.getRandomInt(Math.floor(demiD), -Math.ceil(demiD)))) + zOffset;
|
|
8720
|
+
// // DBG
|
|
8721
|
+
// console.log("(NODE CLIENT) SENDING DATA ! channelName:«"+channelName+"» ; clientsRoomsTag:«"+clientsRoomsTag+"»");
|
|
8722
|
+
// console.log("(NODE CLIENT) SENDING DATA ! dataWrapped:",dataWrapped);
|
|
8075
8723
|
|
|
8076
|
-
|
|
8077
|
-
|
|
8078
|
-
|
|
8079
|
-
|
|
8080
|
-
|
|
8081
|
-
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
|
|
8085
|
-
|
|
8086
|
-
|
|
8087
|
-
|
|
8088
|
-
|
|
8089
|
-
|
|
8090
|
-
|
|
8091
|
-
|
|
8092
|
-
|
|
8093
|
-
|
|
8094
|
-
|
|
8095
|
-
|
|
8724
|
+
// TODO : FIXME : Use one single interface !
|
|
8725
|
+
if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.send(dataWrapped);
|
|
8726
|
+
else clientSocket.emit(channelName,dataWrapped);
|
|
8727
|
+
|
|
8728
|
+
return nodeClientInstance;
|
|
8729
|
+
},
|
|
8730
|
+
|
|
8731
|
+
|
|
8732
|
+
join:(clientRoomTag)=>{
|
|
8733
|
+
// Join room client part protocol :
|
|
8734
|
+
const message={type:"joinRoom",clientRoomTag:clientRoomTag};
|
|
8735
|
+
|
|
8736
|
+
nodeClientInstance.send("protocol",message);
|
|
8737
|
+
|
|
8738
|
+
},
|
|
8739
|
+
|
|
8740
|
+
|
|
8741
|
+
onConnectionToServer:(doOnConnection)=>{
|
|
8742
|
+
|
|
8743
|
+
const doAllOnConnection=()=>{
|
|
8744
|
+
|
|
8745
|
+
// To avoid triggering this event several times, depending on the implementation :
|
|
8746
|
+
if(nodeClientInstance.hasConnectEventFired) return;
|
|
8747
|
+
nodeClientInstance.hasConnectEventFired=true;
|
|
8748
|
+
|
|
8749
|
+
let clientSocket=nodeClientInstance.clientSocket;
|
|
8750
|
+
|
|
8751
|
+
doOnConnection(nodeClientInstance, clientSocket);
|
|
8752
|
+
|
|
8753
|
+
// We execute the listeners entry points:
|
|
8754
|
+
foreach(nodeClientInstance.receptionEntryPoints,(receptionEntryPoint)=>{
|
|
8755
|
+
receptionEntryPoint.execute(clientSocket);
|
|
8756
|
+
});
|
|
8757
|
+
|
|
8758
|
+
};
|
|
8096
8759
|
|
|
8097
|
-
|
|
8098
|
-
|
|
8760
|
+
if(!WebsocketImplementation.useSocketIOImplementation) nodeClientInstance.clientSocket.addEventListener("open",doAllOnConnection);
|
|
8761
|
+
else nodeClientInstance.clientSocket.on("connect",doAllOnConnection);
|
|
8762
|
+
|
|
8763
|
+
},
|
|
8764
|
+
|
|
8765
|
+
|
|
8766
|
+
};
|
|
8767
|
+
|
|
8768
|
+
|
|
8769
|
+
return nodeClientInstance;
|
|
8770
|
+
},
|
|
8771
|
+
|
|
8099
8772
|
|
|
8773
|
+
|
|
8774
|
+
// BROWSER CLIENT
|
|
8775
|
+
|
|
8776
|
+
/*private*/connectToServerFromBrowser:(serverURL, port, isSecure, timeout)=>{
|
|
8777
|
+
|
|
8778
|
+
// NEW : ws :
|
|
8779
|
+
if(typeof(WebSocket)==="undefined"){
|
|
8780
|
+
// TRACE
|
|
8781
|
+
lognow("ERROR : CLIENT : Could not find websocket client lib, aborting client connection.");
|
|
8782
|
+
return null;
|
|
8783
|
+
}
|
|
8784
|
+
|
|
8785
|
+
// TODO : FIXME : Use one single interface !
|
|
8786
|
+
// BROWSER CLIENT MODE ONLY :
|
|
8787
|
+
let clientSocket;
|
|
8788
|
+
if(!WebsocketImplementation.useSocketIOImplementation){
|
|
8789
|
+
clientSocket=new WebSocket(serverURL+":"+port,["ws","wss"]);
|
|
8790
|
+
}else if(typeof(io)!=="undefined"){
|
|
8791
|
+
// NOW : socket.io :
|
|
8792
|
+
// // Browser client :
|
|
8793
|
+
// connectToServer=function (server, port, isSecure, timeout=10000){
|
|
8794
|
+
// return io.connect(serverURL + ":" + port,{timeout: timeout});
|
|
8795
|
+
//// ALTERNATIVE : return io(serverURL + ":" + port,{timeout: timeout});
|
|
8796
|
+
// };
|
|
8797
|
+
// }else{
|
|
8798
|
+
// // TRACE
|
|
8799
|
+
// console.log("ERROR : Could not initialize socket !");
|
|
8800
|
+
clientSocket=io.connect(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
|
|
8801
|
+
}
|
|
8802
|
+
|
|
8803
|
+
|
|
8804
|
+
// BROWSER CLIENT INSTANCE :
|
|
8805
|
+
const browserInstance={
|
|
8806
|
+
clientSocket:clientSocket,
|
|
8807
|
+
|
|
8808
|
+
receive:(channelName, doOnIncomingMessage, clientsRoomsTag=null)=>{
|
|
8100
8809
|
|
|
8101
|
-
//
|
|
8102
|
-
|
|
8810
|
+
// DBG
|
|
8811
|
+
lognow("!!! SETTING UP RECEIVE for :",channelName);
|
|
8812
|
+
|
|
8813
|
+
const doOnMessage=(message)=>{
|
|
8814
|
+
|
|
8815
|
+
// // DBG
|
|
8816
|
+
// lognow("!!! RECEIVED : message",message);
|
|
8817
|
+
|
|
8818
|
+
// OLD : let dataWrapped=JSON.parse(event.data);
|
|
8819
|
+
// const dataWrapped=JSON.parse(message);
|
|
8820
|
+
// dataWrapped=getAt(dataWrapped,0);// We get the root element
|
|
8103
8821
|
|
|
8104
|
-
|
|
8105
|
-
|
|
8822
|
+
const dataWrapped=(WebsocketImplementation.useFlatStrings?JSON.parse(message):message);
|
|
8823
|
+
|
|
8824
|
+
|
|
8825
|
+
// Channel information is stored in exchanged data :
|
|
8826
|
+
if(dataWrapped.channelName && dataWrapped.channelName!==channelName) return;
|
|
8827
|
+
|
|
8828
|
+
let clientSocket=browserInstance.clientSocket;
|
|
8829
|
+
|
|
8830
|
+
// Room information is stored in client socket object :
|
|
8831
|
+
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
8832
|
+
|
|
8833
|
+
doOnIncomingMessage(dataWrapped.data, clientSocket);
|
|
8834
|
+
|
|
8835
|
+
};
|
|
8836
|
+
|
|
8837
|
+
|
|
8838
|
+
if(!WebsocketImplementation.useSocketIOImplementation) browserInstance.clientSocket.addEventListener("message", doOnMessage);
|
|
8839
|
+
else browserInstance.clientSocket.on(channelName, doOnMessage);
|
|
8106
8840
|
|
|
8107
|
-
|
|
8108
|
-
|
|
8109
|
-
|
|
8110
|
-
|
|
8841
|
+
|
|
8842
|
+
return browserInstance;
|
|
8843
|
+
},
|
|
8844
|
+
|
|
8845
|
+
|
|
8846
|
+
send:(channelName, data, clientsRoomsTag=null)=>{
|
|
8847
|
+
|
|
8848
|
+
let clientSocket=browserInstance.clientSocket;
|
|
8849
|
+
|
|
8850
|
+
|
|
8851
|
+
// // DBG
|
|
8852
|
+
// console.log("(BROWSER) TRYING TO SEND : clientSocket.readyState:",clientSocket.readyState);
|
|
8111
8853
|
|
|
8112
|
-
//
|
|
8113
|
-
|
|
8854
|
+
// TODO : FIXME : Use one single interface !
|
|
8855
|
+
if(!WebsocketImplementation.useSocketIOImplementation) if(clientSocket.readyState!==WebSocket.OPEN) return;
|
|
8856
|
+
else if(clientSocket.connected) return;
|
|
8114
8857
|
|
|
8115
|
-
|
|
8858
|
+
|
|
8859
|
+
// Room information is stored in client socket object :
|
|
8860
|
+
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
8116
8861
|
|
|
8862
|
+
// Channel information is stored in exchanged data :
|
|
8863
|
+
let dataWrapped={channelName:channelName, data:data};
|
|
8864
|
+
|
|
8865
|
+
|
|
8866
|
+
// // DBG
|
|
8867
|
+
// console.log("(BROWSER) SENDING... : dataWrapped :",dataWrapped);
|
|
8117
8868
|
|
|
8118
|
-
|
|
8119
|
-
|
|
8869
|
+
|
|
8870
|
+
dataWrapped=JSON.stringify(dataWrapped);
|
|
8120
8871
|
|
|
8121
|
-
|
|
8122
|
-
|
|
8123
|
-
|
|
8124
|
-
|
|
8872
|
+
|
|
8873
|
+
// TODO : FIXME : Use one single interface !
|
|
8874
|
+
if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.send(dataWrapped);
|
|
8875
|
+
else clientSocket.emit(channelName,dataWrapped);
|
|
8125
8876
|
|
|
8126
|
-
|
|
8127
|
-
|
|
8877
|
+
|
|
8878
|
+
return browserInstance;
|
|
8879
|
+
},
|
|
8880
|
+
|
|
8881
|
+
|
|
8882
|
+
join:(clientRoomTag)=>{
|
|
8883
|
+
// Join room client part protocol :
|
|
8884
|
+
const message={type:"joinRoom",clientRoomTag:clientRoomTag};
|
|
8885
|
+
browserInstance.send("protocol",message);
|
|
8886
|
+
},
|
|
8887
|
+
|
|
8888
|
+
|
|
8889
|
+
onConnectionToServer:(doOnConnection)=>{
|
|
8890
|
+
|
|
8891
|
+
// To avoid triggering this event several times, depending on the implementation :
|
|
8892
|
+
if(browserInstance.hasConnectEventFired) return;
|
|
8893
|
+
browserInstance.hasConnectEventFired=true;
|
|
8894
|
+
|
|
8895
|
+
if(!WebsocketImplementation.useSocketIOImplementation) browserInstance.clientSocket.addEventListener("open",doOnConnection);
|
|
8896
|
+
else browserInstance.clientSocket.on("connect",doOnConnection);
|
|
8897
|
+
|
|
8898
|
+
|
|
8899
|
+
},
|
|
8900
|
+
|
|
8901
|
+
|
|
8902
|
+
};
|
|
8903
|
+
|
|
8904
|
+
|
|
8905
|
+
return browserInstance;
|
|
8906
|
+
},
|
|
8128
8907
|
|
|
8129
|
-
|
|
8130
|
-
|
|
8131
|
-
return widthsOverlap && heightsOverlap;
|
|
8132
|
-
}
|
|
8908
|
+
};
|
|
8133
8909
|
|
|
8134
8910
|
|
|
8135
8911
|
|
|
8136
|
-
|
|
8137
|
-
/*public static*/window.getIntersectionZone=function(zone1, zone2){
|
|
8138
|
-
if(!isZoneCollidingWithZone(zone1, zone2)) return null;
|
|
8912
|
+
launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /*OPTIONAL*/sslOptions=null){
|
|
8139
8913
|
|
|
8140
|
-
|
|
8914
|
+
const EXCLUDED_FILENAMES_PARTS=[".saltkey."];
|
|
8141
8915
|
|
|
8142
8916
|
|
|
8143
|
-
|
|
8144
|
-
|
|
8145
|
-
|
|
8917
|
+
if(typeof(https)==="undefined"){
|
|
8918
|
+
// TRACE
|
|
8919
|
+
console.log("«https» SERVER library not called yet, calling it now.");
|
|
8920
|
+
https=require("https");
|
|
8921
|
+
}
|
|
8922
|
+
if(typeof(http)==="undefined"){
|
|
8923
|
+
// TRACE
|
|
8924
|
+
console.log("«http» SERVER library not called yet, calling it now.");
|
|
8925
|
+
http=require("http");
|
|
8926
|
+
}
|
|
8146
8927
|
|
|
8147
|
-
let demiWidth1=zone1.w*.5;
|
|
8148
|
-
let demiWidth2=zone2.w*.5;
|
|
8149
|
-
let xMin1=zone1.x-demiWidth1;
|
|
8150
|
-
let xMax1=zone1.x+demiWidth1;
|
|
8151
|
-
let xMin2=zone2.x-demiWidth2;
|
|
8152
|
-
let xMax2=zone2.x+demiWidth2;
|
|
8153
|
-
|
|
8154
|
-
let deltaXMins=xMin2-xMin1;
|
|
8155
|
-
let deltaXMaxs=xMax2-xMax1;
|
|
8156
8928
|
|
|
8157
|
-
|
|
8158
|
-
|
|
8159
|
-
|
|
8160
|
-
let intersectionXMax=0;
|
|
8161
|
-
if(deltaXMinsSign==deltaXMaxsSign){ // case «parallelogram»
|
|
8162
|
-
if(deltaXMinsSign<0){ // case /=/ parallelogram
|
|
8163
|
-
intersectionXMin=xMin1;
|
|
8164
|
-
intersectionXMax=xMax2;
|
|
8165
|
-
// We calculate edges :
|
|
8166
|
-
edges.push("left");
|
|
8167
|
-
}else{ // case \=\ parallelogram
|
|
8168
|
-
intersectionXMin=xMin2;
|
|
8169
|
-
intersectionXMax=xMax1;
|
|
8170
|
-
// We calculate edges :
|
|
8171
|
-
edges.push("right");
|
|
8172
|
-
}
|
|
8173
|
-
}else{ // case «trapeze»
|
|
8174
|
-
if(deltaXMinsSign<0){ // case /=\ trapeze
|
|
8175
|
-
intersectionXMin=xMin1; // Basically zone1 x !
|
|
8176
|
-
intersectionXMax=xMax1;
|
|
8177
|
-
// We calculate edges :
|
|
8178
|
-
hasXAxisWholeZone1=true;
|
|
8179
|
-
}else{ // case \=/ trapeze
|
|
8180
|
-
intersectionXMin=xMin2; // Basically zone2 x !
|
|
8181
|
-
intersectionXMax=xMax2;
|
|
8182
|
-
// We calculate edges :
|
|
8183
|
-
hasXAxisWholeZone2=true;
|
|
8184
|
-
}
|
|
8185
|
-
}
|
|
8186
|
-
|
|
8187
|
-
let w=intersectionXMax-intersectionXMin;
|
|
8188
|
-
let x=(intersectionXMax+intersectionXMin)*.5; // (The average)
|
|
8189
|
-
|
|
8190
|
-
|
|
8191
|
-
// Vertical axis :
|
|
8192
|
-
|
|
8193
|
-
let hasYAxisWholeZone1=false;
|
|
8194
|
-
let hasYAxisWholeZone2=false;
|
|
8195
|
-
|
|
8196
|
-
let demiHeight1=zone1.h*.5;
|
|
8197
|
-
let demiHeight2=zone2.h*.5;
|
|
8929
|
+
const handler=function(request, response){
|
|
8930
|
+
|
|
8931
|
+
const url=request.url;
|
|
8198
8932
|
|
|
8199
|
-
|
|
8200
|
-
|
|
8201
|
-
|
|
8202
|
-
|
|
8933
|
+
let isURLInExclusionZone=!!foreach(EXCLUDED_FILENAMES_PARTS,(excludedStr)=>{
|
|
8934
|
+
if(contains(url,excludedStr)){
|
|
8935
|
+
return true;
|
|
8936
|
+
}
|
|
8937
|
+
});
|
|
8938
|
+
|
|
8939
|
+
if(isURLInExclusionZone){
|
|
8940
|
+
// TRACE
|
|
8941
|
+
console.log("ERROR 403 forbidden access error :");
|
|
8942
|
+
console.log(error);
|
|
8943
|
+
|
|
8944
|
+
response.writeHead(403);
|
|
8945
|
+
response.end("Sorry, cannot access resource : error: "+error.code+" ..\n");
|
|
8946
|
+
response.end();
|
|
8947
|
+
return;
|
|
8948
|
+
}
|
|
8949
|
+
|
|
8950
|
+
|
|
8951
|
+
const urlFile="." + url;
|
|
8952
|
+
|
|
8953
|
+
let filePath=urlFile.indexOf("?")!==-1?urlFile.split("?")[0]:urlFile;
|
|
8954
|
+
if(filePath == "./") filePath="./index.html";
|
|
8955
|
+
|
|
8956
|
+
let extname=path.extname(filePath);
|
|
8957
|
+
let contentType="text/html";
|
|
8958
|
+
|
|
8959
|
+
|
|
8960
|
+
fs.readFile(filePath, function(error, fileContent){
|
|
8961
|
+
if(error){
|
|
8962
|
+
if(error.code == "ENOENT"){
|
|
8963
|
+
// TRACE
|
|
8964
|
+
console.log("ERROR 404 file not found :"+filePath);
|
|
8965
|
+
|
|
8966
|
+
fs.readFile("./404.html", function(error, fileContent){
|
|
8967
|
+
response.writeHead(200, { "Content-Type": contentType });
|
|
8968
|
+
response.end(fileContent, "utf-8");
|
|
8969
|
+
});
|
|
8970
|
+
|
|
8971
|
+
}else {
|
|
8972
|
+
|
|
8973
|
+
// TRACE
|
|
8974
|
+
console.log("ERROR 500 server error :");
|
|
8975
|
+
console.log(error);
|
|
8976
|
+
|
|
8977
|
+
response.writeHead(500);
|
|
8978
|
+
response.end("Sorry, check with the site admin for error: "+error.code+" ..\n");
|
|
8979
|
+
response.end();
|
|
8980
|
+
|
|
8981
|
+
}
|
|
8982
|
+
}else {
|
|
8983
|
+
|
|
8984
|
+
// TRACE
|
|
8985
|
+
console.log("INFO 200 OK :"+filePath);
|
|
8986
|
+
|
|
8987
|
+
response.writeHead(200, { "Content-Type": contentType });
|
|
8988
|
+
response.end(fileContent, "utf-8");
|
|
8989
|
+
|
|
8990
|
+
// res.writeHead(200);
|
|
8991
|
+
// res.end("hello world\n");
|
|
8992
|
+
// res.sendFile(__dirname + "/public/index.html");
|
|
8993
|
+
|
|
8994
|
+
}
|
|
8995
|
+
});
|
|
8996
|
+
|
|
8997
|
+
};
|
|
8203
8998
|
|
|
8204
|
-
|
|
8205
|
-
let deltaYMins=yMin2-yMin1;
|
|
8206
|
-
let deltaYMaxs=yMax2-yMax1;
|
|
8207
8999
|
|
|
8208
|
-
let
|
|
8209
|
-
|
|
8210
|
-
|
|
8211
|
-
|
|
8212
|
-
|
|
8213
|
-
|
|
8214
|
-
|
|
8215
|
-
|
|
8216
|
-
|
|
8217
|
-
|
|
8218
|
-
|
|
8219
|
-
intersectionYMin=yMin2;
|
|
8220
|
-
intersectionYMax=yMax1;
|
|
8221
|
-
// We calculate edges :
|
|
8222
|
-
edges.push("top"); // Here the Y axis is NOT inverted !
|
|
8223
|
-
}
|
|
8224
|
-
}else{ // case «trapeze»
|
|
8225
|
-
if(deltaYMinsSign<0){ // case /=\ trapeze
|
|
8226
|
-
intersectionYMin=yMin1; // Basically zone2 y !
|
|
8227
|
-
intersectionYMax=yMax1;
|
|
8228
|
-
// We calculate edges :
|
|
8229
|
-
hasYAxisWholeZone1=true;
|
|
8230
|
-
}else{ // case \=/ trapeze
|
|
8231
|
-
intersectionYMin=yMin2; // Basically zone2 y !
|
|
8232
|
-
intersectionYMax=yMax2;
|
|
8233
|
-
// We calculate edges :
|
|
8234
|
-
hasYAxisWholeZone2=true;
|
|
8235
|
-
}
|
|
9000
|
+
let listenableServer;
|
|
9001
|
+
if(sslOptions){
|
|
9002
|
+
let httpsServer=https.createServer(sslOptions, handler).listen(port);
|
|
9003
|
+
// TRACE
|
|
9004
|
+
console.log("INFO : SERVER : HTTPS Server launched and listening on port " + port + "!");
|
|
9005
|
+
listenableServer=httpsServer;
|
|
9006
|
+
}else{
|
|
9007
|
+
let httpServer=http.createServer(handler).listen(port);
|
|
9008
|
+
// TRACE
|
|
9009
|
+
console.log("INFO : SERVER : HTTP Server launched and listening on port " + port + "!");
|
|
9010
|
+
listenableServer=httpServer;
|
|
8236
9011
|
}
|
|
8237
9012
|
|
|
8238
|
-
let h=intersectionYMax-intersectionYMin;
|
|
8239
|
-
let y=(intersectionYMax+intersectionYMin)*.5; // (The average)
|
|
8240
9013
|
|
|
8241
|
-
|
|
8242
|
-
if(hasXAxisWholeZone1 && hasYAxisWholeZone1) edges.push("wholeZone1");
|
|
8243
|
-
if(hasXAxisWholeZone2 && hasYAxisWholeZone2) edges.push("wholeZone2");
|
|
9014
|
+
const server=WebsocketImplementation.getStatic(true).getServer(listenableServer);
|
|
8244
9015
|
|
|
8245
|
-
|
|
8246
|
-
|
|
9016
|
+
// When a client connects, we execute the callback :
|
|
9017
|
+
// CAUTION : MUST BE CALLED ONLY ONCE !
|
|
9018
|
+
server.onConnectionToClient((serverParam, clientSocketParam)=>{
|
|
9019
|
+
if(doOnConnect) doOnConnect(serverParam, clientSocketParam);
|
|
9020
|
+
});
|
|
8247
9021
|
|
|
8248
|
-
/*public static*/window.areZonesIdentical=function(zone1, zone2){
|
|
8249
|
-
return (zone1==zone2 || (zone1.x==zone2.x || zone1.y==zone2.y || zone1.w==zone2.w || zone1.h==zone2.h));
|
|
8250
|
-
}
|
|
8251
9022
|
|
|
9023
|
+
server.onFinalize((serverParam)=>{
|
|
9024
|
+
if(doOnFinalizeServer) doOnFinalizeServer(serverParam);
|
|
9025
|
+
});
|
|
9026
|
+
|
|
8252
9027
|
|
|
9028
|
+
// TRACE
|
|
9029
|
+
console.log("INFO : SERVER : Generic Nodejs server launched and listening on port:" + port + "!");
|
|
8253
9030
|
|
|
8254
|
-
// 3D :
|
|
8255
9031
|
|
|
9032
|
+
return server;
|
|
9033
|
+
}
|
|
8256
9034
|
|
|
8257
|
-
/*
|
|
8258
|
-
// (needs the jsgl library file !)
|
|
8259
|
-
// UNUSED
|
|
8260
|
-
function screenToSpherePt(mouseX, mouseY) {
|
|
8261
|
-
var p = { x: camera_mat.e12, y: camera_mat.e13, z: camera_mat.e14 + 1 };
|
|
8262
|
-
// camera dir
|
|
8263
|
-
var r = { x: camera_mat.e0, y: camera_mat.e1, z: camera_mat.e2 };
|
|
8264
|
-
var up = { x: camera_mat.e4, y: camera_mat.e5, z: camera_mat.e6 };
|
|
8265
|
-
var right = { x: camera_mat.e8, y: camera_mat.e9, z: camera_mat.e10 };
|
|
8266
|
-
var tan_half = Math.tan(horizontal_fov_radians / 2);
|
|
8267
|
-
r = jsgl.vectorAdd(r, jsgl.vectorScale(right, mouseX * tan_half));
|
|
8268
|
-
r = jsgl.vectorAdd(r, jsgl.vectorScale(up, mouseY * tan_half));
|
|
8269
|
-
r = jsgl.vectorNormalize(r);
|
|
8270
9035
|
|
|
8271
|
-
|
|
8272
|
-
}
|
|
8273
|
-
*/
|
|
9036
|
+
initNodeServer=function(doOnClientConnection=null, doOnFinalizeServer=null, /*OPTIONAL*/portParam, /*OPTIONAL*/certPathParam, /*OPTIONAL*/keyPathParam){
|
|
8274
9037
|
|
|
8275
|
-
|
|
8276
|
-
|
|
8277
|
-
|
|
8278
|
-
|
|
8279
|
-
//
|
|
8280
|
-
|
|
8281
|
-
|
|
8282
|
-
|
|
8283
|
-
|
|
8284
|
-
|
|
8285
|
-
|
|
9038
|
+
// TRACE
|
|
9039
|
+
console.log("Server launched.");
|
|
9040
|
+
console.log("Usage : node <server.js> conf {port:[port], sslCertPath:[ssl certificate path | unsecure ], sslKeyPath:[ssl key path], serverConfig:[JSON server configuration]}");
|
|
9041
|
+
console.log("Or (to generate password hash) : node <server.js> hash <clientId@repositoryName> <clearTextSecretString>");
|
|
9042
|
+
// EXAMPLE : node orita-srv.js hash orita.global@saltkey 1234567890
|
|
9043
|
+
console.log("Server launched.");
|
|
9044
|
+
|
|
9045
|
+
// We read the command-line arguments if needed :
|
|
9046
|
+
|
|
9047
|
+
let argCLPort;
|
|
9048
|
+
let argCLCertPath;
|
|
9049
|
+
let argCLKeyPath;
|
|
9050
|
+
let serverConfig={};
|
|
9051
|
+
let isForceUnsecure;
|
|
9052
|
+
|
|
9053
|
+
let isHashAsked=false;
|
|
9054
|
+
let clearTextParam=null;
|
|
9055
|
+
let persisterId=null;
|
|
9056
|
+
|
|
9057
|
+
|
|
9058
|
+
process.argv.forEach(function (val, i){
|
|
9059
|
+
if(!val) return;
|
|
9060
|
+
// 0 corresponds to «node / nodejs»
|
|
9061
|
+
if(i<=1) return; // 1 corresponds to « <server.js> »
|
|
9062
|
+
else if(i==2){
|
|
9063
|
+
if(val==="hash") isHashAsked=true;
|
|
9064
|
+
}else if(i==3){
|
|
9065
|
+
if(!isHashAsked){
|
|
9066
|
+
try{
|
|
9067
|
+
const jsonConf=JSON.parse(val);
|
|
9068
|
+
argCLPort=jsonConf.port;
|
|
9069
|
+
argCLCertPath=jsonConf.sslCertPath;
|
|
9070
|
+
argCLKeyPath=jsonConf.sslKeyPath;
|
|
9071
|
+
serverConfig=nonull({},jsonConf.serverConfig);
|
|
9072
|
+
}catch(err1){
|
|
9073
|
+
lognow("ERROR : Cannot parse argument JSON string «"+val+"».",err1);
|
|
9074
|
+
}
|
|
9075
|
+
} else persisterId=val;
|
|
9076
|
+
}else if(i==4){
|
|
9077
|
+
if(isHashAsked) clearTextParam=val;
|
|
9078
|
+
}
|
|
9079
|
+
});
|
|
9080
|
+
isForceUnsecure=(argCLCertPath==="unsecure");
|
|
8286
9081
|
|
|
8287
|
-
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
return null;
|
|
8291
|
-
}
|
|
9082
|
+
|
|
9083
|
+
const aotraNodeServer={config:serverConfig};
|
|
9084
|
+
aotraNodeServer.serverManager={ start:()=>{/*DEFAULT START FUNCTION, WILL BE OVERRIDEN LATER*/}};
|
|
8292
9085
|
|
|
8293
|
-
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
9086
|
+
if(isHashAsked){
|
|
9087
|
+
// We isntanciate a temporary persister just to read the saltkey file:
|
|
9088
|
+
const persister=getPersister("./");
|
|
9089
|
+
let persisterIdSplits=persisterId.split("@");
|
|
9090
|
+
if(empty(persisterIdSplits) || persisterIdSplits.length!=2){
|
|
9091
|
+
// TRACE
|
|
9092
|
+
console.log("ERROR : No persister repository IDs provided correctly. Cannot read saltkey. Aborting hash generation.");
|
|
9093
|
+
return aotraNodeServer;
|
|
9094
|
+
}
|
|
9095
|
+
const persisterClientId=persisterIdSplits[0];
|
|
9096
|
+
const persisterRepositoryName=persisterIdSplits[1];
|
|
9097
|
+
let globalSaltkeyObject=persister.readTreeObjectFromFile(persisterClientId, persisterRepositoryName);
|
|
9098
|
+
if(!globalSaltkeyObject || !globalSaltkeyObject.saltkey){
|
|
9099
|
+
// TRACE
|
|
9100
|
+
console.log("WARN : No saltkey found. Generating one now.");
|
|
9101
|
+
globalSaltkeyObject={saltkey:getUUID(), hashes:[]};
|
|
9102
|
+
persister.saveDataToFileForClient(persisterClientId,persisterRepositoryName,globalSaltkeyObject,false,()=>{
|
|
9103
|
+
// TRACE
|
|
9104
|
+
console.log("INFO : Saltkey generated and saved successfully.");
|
|
9105
|
+
});
|
|
9106
|
+
}
|
|
9107
|
+
const globalSaltkey=globalSaltkeyObject.saltkey;
|
|
9108
|
+
|
|
9109
|
+
let firstHash=getHashedString(clearTextParam);
|
|
9110
|
+
|
|
9111
|
+
let generatedHash=getHashedString( firstHash + globalSaltkey, true);// (we use the heavy treatment thing.)
|
|
9112
|
+
globalSaltkeyObject.hashes.push(generatedHash);
|
|
9113
|
+
|
|
9114
|
+
// We update the repository :
|
|
9115
|
+
persister.saveDataToFileForClient(persisterClientId,persisterRepositoryName,globalSaltkeyObject,false,()=>{
|
|
9116
|
+
// TRACE
|
|
9117
|
+
console.log("INFO : Hash added to repository and saved successfully.");
|
|
9118
|
+
});
|
|
9119
|
+
|
|
9120
|
+
// OUTPUT
|
|
9121
|
+
console.log("Here is your key : share it with your clients but DO NOT LEAK IT !\n********************\n"+clearTextParam+"\n********************\n");
|
|
9122
|
+
|
|
9123
|
+
return aotraNodeServer;
|
|
8298
9124
|
}
|
|
8299
9125
|
|
|
8300
|
-
|
|
8301
|
-
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
}
|
|
8305
|
-
*/
|
|
9126
|
+
|
|
9127
|
+
const DEFAULT_PORT=nonull(argCLPort,25000);
|
|
9128
|
+
const DEFAULT_CERT_PATH=nonull(argCLCertPath,"cert.pem");
|
|
9129
|
+
const DEFAULT_KEY_PATH=nonull(argCLKeyPath,"key.key");
|
|
8306
9130
|
|
|
9131
|
+
|
|
9132
|
+
let port=portParam ? portParam : DEFAULT_PORT;
|
|
9133
|
+
let certPath=null;
|
|
9134
|
+
let keyPath=null;
|
|
8307
9135
|
|
|
9136
|
+
if(!isForceUnsecure){
|
|
9137
|
+
certPath=certPathParam?certPathParam:DEFAULT_CERT_PATH;
|
|
9138
|
+
keyPath=keyPathParam?keyPathParam:DEFAULT_KEY_PATH;
|
|
9139
|
+
}
|
|
8308
9140
|
|
|
9141
|
+
|
|
9142
|
+
// UNUSEFUL :
|
|
9143
|
+
//aotraNodeServer.serverManager.microClientsSockets=[];
|
|
9144
|
+
// UNUSEFUL :
|
|
9145
|
+
//aotraNodeServer.serverManager.mainClientsSockets=[];
|
|
9146
|
+
aotraNodeServer.serverManager.start=function(){
|
|
8309
9147
|
|
|
9148
|
+
// Eventual encryption options :
|
|
9149
|
+
let sslOptions=null;
|
|
9150
|
+
if(!isForceUnsecure){
|
|
9151
|
+
if(!fs){
|
|
9152
|
+
// TRACE
|
|
9153
|
+
lognow("ERROR : «fs» node subsystem not present, cannot access files. Aborting SSL configuration of server.");
|
|
9154
|
+
}else{
|
|
9155
|
+
try{
|
|
9156
|
+
sslOptions={
|
|
9157
|
+
cert: fs.readFileSync(certPath),
|
|
9158
|
+
key: fs.readFileSync(keyPath),
|
|
9159
|
+
};
|
|
9160
|
+
}catch(exception){
|
|
9161
|
+
// TRACE
|
|
9162
|
+
lognow("ERROR : Could not open SSL files certPath:«"+certPath+"» or keyPath:«"+keyPath+"». Aborting SSL configuration of server.");
|
|
9163
|
+
}
|
|
9164
|
+
}
|
|
9165
|
+
}
|
|
9166
|
+
|
|
9167
|
+
aotraNodeServer.server=launchNodeHTTPServer(port, doOnClientConnection, doOnFinalizeServer, sslOptions);
|
|
9168
|
+
|
|
8310
9169
|
|
|
9170
|
+
return aotraNodeServer;
|
|
9171
|
+
};
|
|
8311
9172
|
|
|
9173
|
+
return aotraNodeServer;
|
|
9174
|
+
}
|
|
8312
9175
|
|
|
9176
|
+
// ========================= FUSRODA SERVER : =========================
|
|
8313
9177
|
|
|
9178
|
+
//
|
|
9179
|
+
// DOES NOT WORK : USE Java FusrodaServer instead :
|
|
9180
|
+
//
|
|
9181
|
+
///*FUSRODA server stands from FSRD SERVER, for Fucking Simple Remote Desktop SERVER*/
|
|
9182
|
+
//createFusrodaServer=function(certPathParam=null,keyPathParam=null,portParam=6080){
|
|
9183
|
+
//
|
|
9184
|
+
// // https://www.npmjs.com/package/screenshot-desktop
|
|
9185
|
+
// // https://github.com/octalmage/robotjs
|
|
9186
|
+
// // npm install --save screenshot-desktop
|
|
9187
|
+
// //
|
|
9188
|
+
// // sudo apt-get install libxtst-dev libx11-dev
|
|
9189
|
+
// // npm install --save robotjs
|
|
9190
|
+
//
|
|
9191
|
+
// // http://getrobot.net/docs/usage.html
|
|
9192
|
+
// //
|
|
9193
|
+
// // apt-get install build-essential python libxt-dev libxtst-dev libxinerama-dev -y
|
|
9194
|
+
//
|
|
9195
|
+
// const screenshot=require("screenshot-desktop");
|
|
9196
|
+
//
|
|
9197
|
+
// const REFRESH_SCREENSHOTS_MILLIS=500;
|
|
9198
|
+
//
|
|
9199
|
+
//
|
|
9200
|
+
// const server=initNodeServer(
|
|
9201
|
+
// // On client connection :
|
|
9202
|
+
//// (serverParam, clientSocketParam)=>{},
|
|
9203
|
+
// null,
|
|
9204
|
+
// // On server finalization :
|
|
9205
|
+
// (serverParam)=>{
|
|
9206
|
+
//
|
|
9207
|
+
// serverParam.receive("protocol_fusroda", (message, clientSocket)=> {
|
|
9208
|
+
// serverParam.addToRoom(clientSocket,"clients");
|
|
9209
|
+
// });
|
|
9210
|
+
//
|
|
9211
|
+
//
|
|
9212
|
+
// serverParam.sendScreenshotsRoutine=setInterval(()=>{
|
|
9213
|
+
// if(serverParam.isScreenshotStarted) return;
|
|
9214
|
+
//
|
|
9215
|
+
// serverParam.isScreenshotStarted=true;
|
|
9216
|
+
//
|
|
9217
|
+
// screenshot().then((img) => {
|
|
9218
|
+
//
|
|
9219
|
+
// const data={image:img,messageType:"imageData"};
|
|
9220
|
+
// serverParam.send("message", data, "clients");
|
|
9221
|
+
//
|
|
9222
|
+
// serverParam.isScreenshotStarted=false;
|
|
9223
|
+
//
|
|
9224
|
+
// }).catch((error) => {
|
|
9225
|
+
// // TRACE
|
|
9226
|
+
// lognow("ERROR : Error during screenshot :",error);
|
|
9227
|
+
// });
|
|
9228
|
+
//
|
|
9229
|
+
// },REFRESH_SCREENSHOTS_MILLIS);
|
|
9230
|
+
//
|
|
9231
|
+
//
|
|
9232
|
+
// },portParam,certPathParam,keyPathParam);
|
|
9233
|
+
//
|
|
9234
|
+
//
|
|
9235
|
+
//// const doOnConnect=(serverParam, clientSocketParam)=>{
|
|
9236
|
+
//// };
|
|
9237
|
+
//// const doOnFinalizeServer=(serverParam)=>{
|
|
9238
|
+
//// /*DO NOTHING*/
|
|
9239
|
+
//// };
|
|
9240
|
+
//// const server={};
|
|
9241
|
+
//// server.start=(port=6080)=>{
|
|
9242
|
+
//// server.httpServer=launchNodeHTTPServer(port, doOnConnect, doOnFinalizeServer, sslOptions);
|
|
9243
|
+
//// };
|
|
9244
|
+
//
|
|
9245
|
+
// return server;
|
|
9246
|
+
//}
|
|
8314
9247
|
|
|
8315
9248
|
|
|
9249
|
+
// ========================= UTILITY SERVERSIDE METHODS : =========================
|
|
8316
9250
|
|
|
9251
|
+
class ListManager{
|
|
9252
|
+
constructor(config){
|
|
9253
|
+
this.config=config;
|
|
9254
|
+
this.maxItemsNumber=nonull(this.config.max,999);
|
|
9255
|
+
this.simultaneousItemsNumber=nonull(this.config.simultaneous,1);
|
|
9256
|
+
this.sessionDurationSeconds=nonull(this.config.duration,null);
|
|
9257
|
+
this.mode=nonull(this.config.mode,"startAtConnexion");
|
|
9258
|
+
|
|
9259
|
+
this.itemsInfos={};
|
|
9260
|
+
this.time=null;
|
|
9261
|
+
this.started=false;
|
|
9262
|
+
}
|
|
9263
|
+
|
|
9264
|
+
addItem(id,item){
|
|
9265
|
+
if(id==null){
|
|
9266
|
+
// TRACE
|
|
9267
|
+
lognow("ERROR : Cannot add item with no id.");
|
|
9268
|
+
return;
|
|
9269
|
+
}
|
|
9270
|
+
if(!item){
|
|
9271
|
+
// TRACE
|
|
9272
|
+
lognow("ERROR : Cannot add null item.");
|
|
9273
|
+
return;
|
|
9274
|
+
}
|
|
9275
|
+
const numberOfItemsCurrently=countKeys(this.itemsInfos);
|
|
9276
|
+
if(this.maxItemsNumber<=numberOfItemsCurrently){
|
|
9277
|
+
// TRACE
|
|
9278
|
+
lognow("ERROR : Cannot add item with id «"+id+"», list already full.");
|
|
9279
|
+
return;
|
|
9280
|
+
}
|
|
9281
|
+
if(numberOfItemsCurrently==0 && this.mode==="startAtConnexion"){
|
|
9282
|
+
this.startSession();
|
|
9283
|
+
}
|
|
9284
|
+
this.itemsInfos[id]={
|
|
9285
|
+
item:item,
|
|
9286
|
+
time:getNow(),
|
|
9287
|
+
};
|
|
9288
|
+
}
|
|
9289
|
+
|
|
9290
|
+
startSession(){
|
|
9291
|
+
this.time==getNow();
|
|
9292
|
+
this.started=true;
|
|
9293
|
+
}
|
|
9294
|
+
|
|
9295
|
+
stopSession(){
|
|
9296
|
+
this.started=false;
|
|
9297
|
+
}
|
|
9298
|
+
|
|
9299
|
+
isSessionActive(){
|
|
9300
|
+
if(!this.sessionDurationSeconds) return true;
|
|
9301
|
+
if(!this.started) return false;
|
|
9302
|
+
return !hasDelayPassed(this.time, this.sessionDurationSeconds*1000);
|
|
9303
|
+
}
|
|
9304
|
+
|
|
9305
|
+
isClientActive(clientId){
|
|
9306
|
+
if(!clientsListManager.isSessionActive()) return false;
|
|
9307
|
+
const clientPosition=this.getItemPosition(clientId);
|
|
9308
|
+
const result=(clientPosition<=this.maxItemsNumber);
|
|
9309
|
+
return result;
|
|
9310
|
+
}
|
|
9311
|
+
|
|
9312
|
+
removeItem(id){
|
|
9313
|
+
if(id==null){
|
|
9314
|
+
// TRACE
|
|
9315
|
+
lognow("ERROR : Cannot remove item, no id specified.");
|
|
9316
|
+
return;
|
|
9317
|
+
}
|
|
9318
|
+
if(!this.itemsInfos[id]){
|
|
9319
|
+
// TRACE
|
|
9320
|
+
lognow("ERROR : Cannot remove item, item not found for id «"+id+"».");
|
|
9321
|
+
return;
|
|
9322
|
+
}
|
|
9323
|
+
delete this.itemsInfos[id];
|
|
9324
|
+
}
|
|
8317
9325
|
|
|
9326
|
+
// Goes from 1 to <length>:
|
|
9327
|
+
getItemPosition(id){
|
|
9328
|
+
if(id==null){
|
|
9329
|
+
// TRACE
|
|
9330
|
+
lognow("ERROR : Cannot calculate item position, no id specified.");
|
|
9331
|
+
return;
|
|
9332
|
+
}
|
|
9333
|
+
if(!this.itemsInfos[id]){
|
|
9334
|
+
// TRACE
|
|
9335
|
+
lognow("ERROR : Cannot calculate item position, item not found for id «"+id+"».");
|
|
9336
|
+
return;
|
|
9337
|
+
}
|
|
9338
|
+
let result=0;
|
|
9339
|
+
foreach(this.itemsInfos,(itemInfo,key)=>{
|
|
9340
|
+
result++;
|
|
9341
|
+
if(id==key) return "break";
|
|
9342
|
+
},null,(itemInfo1,itemInfo2)=>item1.time<item2.time);
|
|
9343
|
+
return result;
|
|
9344
|
+
}
|
|
8318
9345
|
|
|
9346
|
+
|
|
9347
|
+
}
|
|
8319
9348
|
|
|
9349
|
+
function getListManager(config){
|
|
9350
|
+
return new ListManager(config);
|
|
9351
|
+
}
|
|
8320
9352
|
|
|
8321
9353
|
|
|
8322
9354
|
|
|
@@ -8324,10 +9356,8 @@ function rayVsUnitSphereClosestPoint(p, r) {
|
|
|
8324
9356
|
|
|
8325
9357
|
|
|
8326
9358
|
|
|
8327
|
-
// MUST REMAIN AT THE END OF THIS LIBRARY FILE !
|
|
8328
9359
|
|
|
8329
|
-
|
|
8330
|
-
/*utils COMMONS library associated with aotra version : «1.0.0.000 (11/07/2022-23:49:31)»*/
|
|
9360
|
+
/*utils COMMONS library associated with aotra version : «1.0.0.000 (12/07/2022-23:38:27)»*/
|
|
8331
9361
|
/*-----------------------------------------------------------------------------*/
|
|
8332
9362
|
|
|
8333
9363
|
|
|
@@ -9668,105 +10698,398 @@ aotest.run=function(testName=null,scenarioName=null){
|
|
|
9668
10698
|
|
|
9669
10699
|
var buildHRReport=function(resultObj){
|
|
9670
10700
|
|
|
9671
|
-
let str="";
|
|
9672
|
-
|
|
9673
|
-
let resultsFailed=getCumulatedInt(resultObj,"childrenFailed");
|
|
9674
|
-
let resultsRunned=getCumulatedInt(resultObj,"childrenRunned");
|
|
9675
|
-
|
|
10701
|
+
let str="";
|
|
10702
|
+
|
|
10703
|
+
let resultsFailed=getCumulatedInt(resultObj,"childrenFailed");
|
|
10704
|
+
let resultsRunned=getCumulatedInt(resultObj,"childrenRunned");
|
|
10705
|
+
|
|
10706
|
+
|
|
10707
|
+
// TRACE
|
|
10708
|
+
str+="@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n";
|
|
10709
|
+
str+=" AOTEST REPORT \n";
|
|
10710
|
+
str+="REPORT: "+resultsFailed + "/" + resultsRunned + " scenarii failed.\n";
|
|
10711
|
+
str+="REPORT: "+(resultsRunned - resultsFailed) + "/" + resultsRunned + " scenarii succeeded.\n";
|
|
10712
|
+
str+="REPORT: "+resultsRunned + " scenarii runned.\n";
|
|
10713
|
+
str+="@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n";
|
|
10714
|
+
|
|
10715
|
+
if(0<resultsRunned){
|
|
10716
|
+
if(!resultObj.global.isFailed){
|
|
10717
|
+
// TRACE
|
|
10718
|
+
str+="LOG: RESULT :>>>>>> All tests SUCCEEDED ! :D\n";
|
|
10719
|
+
} else {
|
|
10720
|
+
// TRACE
|
|
10721
|
+
str+="LOG: RESULT :>>>>>> Some tests FAILED... :(\n";
|
|
10722
|
+
}
|
|
10723
|
+
} else {
|
|
10724
|
+
// TRACE
|
|
10725
|
+
str+="LOG: RESULT :>>>>>> No tests runned... :|\n";
|
|
10726
|
+
}
|
|
10727
|
+
|
|
10728
|
+
return str;
|
|
10729
|
+
};
|
|
10730
|
+
|
|
10731
|
+
|
|
10732
|
+
var getCumulatedInt=function(resultObj, attrName){
|
|
10733
|
+
|
|
10734
|
+
let result=0;
|
|
10735
|
+
|
|
10736
|
+
result+=resultObj.global[attrName];
|
|
10737
|
+
|
|
10738
|
+
foreach(resultObj.children,(child)=>{
|
|
10739
|
+
result+=getCumulatedInt(child, attrName);
|
|
10740
|
+
});
|
|
10741
|
+
|
|
10742
|
+
return result;
|
|
10743
|
+
};
|
|
10744
|
+
|
|
10745
|
+
|
|
10746
|
+
|
|
10747
|
+
|
|
10748
|
+
// TRACE
|
|
10749
|
+
console.log( buildHRReport(allTestsResultObj));
|
|
10750
|
+
|
|
10751
|
+
|
|
10752
|
+
// DBG
|
|
10753
|
+
console.log("TOTAL RESULT :",allTestsResultObj);
|
|
10754
|
+
|
|
10755
|
+
|
|
10756
|
+
aotest.isRunning=false;
|
|
10757
|
+
|
|
10758
|
+
return allTestsResultObj;
|
|
10759
|
+
};
|
|
10760
|
+
|
|
10761
|
+
|
|
10762
|
+
|
|
10763
|
+
aotest.profile=function(rootObject,methodName,visited=[]){
|
|
10764
|
+
|
|
10765
|
+
|
|
10766
|
+
//if(!rootObject || !isObject(rootObject) || contains(visited,rootObject)) return;
|
|
10767
|
+
if( !rootObject || isPrimitive(rootObject) || isFunction(rootObject)
|
|
10768
|
+
|| contains(visited,rootObject)) return;
|
|
10769
|
+
|
|
10770
|
+
const THRESHOLD_MILLIS=10;
|
|
10771
|
+
|
|
10772
|
+
visited.push(rootObject);
|
|
10773
|
+
|
|
10774
|
+
let method=rootObject[methodName];
|
|
10775
|
+
if(method && isFunction(method)){
|
|
10776
|
+
rootObject[methodName]=function(){
|
|
10777
|
+
|
|
10778
|
+
let time=getNow();
|
|
10779
|
+
|
|
10780
|
+
method.apply(rootObject, arguments);
|
|
10781
|
+
|
|
10782
|
+
let deltaMillis=(getNow()-time);
|
|
10783
|
+
|
|
10784
|
+
// TRACE
|
|
10785
|
+
if(THRESHOLD_MILLIS<=deltaMillis) lognow("Duration ["+deltaMillis+"ms] of «"+methodName+"» on object:"+getClassName(rootObject),rootObject);
|
|
10786
|
+
};
|
|
10787
|
+
}
|
|
10788
|
+
|
|
10789
|
+
foreach(rootObject,(attr,attrName)=>{
|
|
10790
|
+
|
|
10791
|
+
// DBG
|
|
10792
|
+
if(attrName==="currentContainer") lognow("currentContainer:",attr);
|
|
10793
|
+
|
|
10794
|
+
aotest.profile(attr,methodName,visited);
|
|
10795
|
+
|
|
10796
|
+
});
|
|
10797
|
+
|
|
10798
|
+
|
|
10799
|
+
};
|
|
10800
|
+
|
|
10801
|
+
|
|
10802
|
+
|
|
10803
|
+
|
|
10804
|
+
|
|
10805
|
+
//================================================================
|
|
10806
|
+
//================= Cryptography utility methods =================
|
|
10807
|
+
//================================================================
|
|
10808
|
+
|
|
10809
|
+
|
|
10810
|
+
// NOT AOTESTABLE !
|
|
10811
|
+
//TODO : develop :
|
|
10812
|
+
//getHashedString=monitorProgression(100000,
|
|
10813
|
+
window.getHashedString=function(str,/*OPTIONAL*/isHeavyTreatment){
|
|
10814
|
+
|
|
10815
|
+
//CAUTION : YOU MUST *NOT* CHANGE THIS VALUE, UNLESS THE HASH HEAVY TREATMENT VALUE IN YOUR AOTRA SERVER-SIDE CODEIS THE SAME !
|
|
10816
|
+
const HEAVY_TREATMENT_LOOP_COUNT=100000;
|
|
10817
|
+
|
|
10818
|
+
if(isHeavyTreatment){
|
|
10819
|
+
|
|
10820
|
+
// If needed, we make sure the hashing will take a long time, so that
|
|
10821
|
+
// if the private salt key is leaked, it will be fairly difficult to crack
|
|
10822
|
+
// password write protection and content encryption :
|
|
10823
|
+
|
|
10824
|
+
let result=str;
|
|
10825
|
+
for(var i=0;i<HEAVY_TREATMENT_LOOP_COUNT;i++){
|
|
10826
|
+
result=getHashedString(result,false);
|
|
10827
|
+
}
|
|
10828
|
+
|
|
10829
|
+
|
|
10830
|
+
return result;
|
|
10831
|
+
|
|
10832
|
+
}else{
|
|
10833
|
+
|
|
10834
|
+
|
|
10835
|
+
// SHA-256 hash (sucks less) :
|
|
10836
|
+
// DEPENDENCY : SJCL JS libray (Stanford Javascript Crypto Library)
|
|
10837
|
+
if(typeof(sjcl)!="undefined"){
|
|
10838
|
+
let result=sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(str));
|
|
10839
|
+
|
|
10840
|
+
// TODO : develop :
|
|
10841
|
+
// hash.progressionMonitor.progress(1);
|
|
10842
|
+
|
|
10843
|
+
|
|
10844
|
+
return result;
|
|
10845
|
+
}
|
|
10846
|
+
|
|
10847
|
+
// if not found : MD5 hash (sucks) :
|
|
10848
|
+
//TRACE
|
|
10849
|
+
log("WARN : Could not find SHA hashing, using MD5. This sucks, and this very likely will prevent your aotra client to be able to write data to aotra server.");
|
|
10850
|
+
|
|
10851
|
+
return hex_md5(str);
|
|
10852
|
+
}
|
|
10853
|
+
}
|
|
10854
|
+
//);
|
|
10855
|
+
|
|
10856
|
+
function encrypt(strToEncrypt,key,/*OPTIONAL*/mode){
|
|
10857
|
+
if(!mode || typeof(sjcl)=="undefined") mode="xor";
|
|
10858
|
+
mode=mode.trim();
|
|
10859
|
+
|
|
10860
|
+
if(contains(mode,"aes") && contains(mode,"xor")){
|
|
10861
|
+
|
|
10862
|
+
// XOR :
|
|
10863
|
+
var xorStr=btoa(encodeXORNoPattern(strToEncrypt,key));
|
|
10864
|
+
|
|
10865
|
+
var aesStr=
|
|
10866
|
+
// AES
|
|
10867
|
+
sjcl.encrypt(key,xorStr);
|
|
10868
|
+
|
|
10869
|
+
return aesStr;
|
|
10870
|
+
}
|
|
10871
|
+
|
|
10872
|
+
// DEPENDENCY : SJCL JS libray (Stanford Javascript Crypto Library)
|
|
10873
|
+
if(mode==="aes")
|
|
10874
|
+
return sjcl.encrypt(key,strToEncrypt);
|
|
10875
|
+
return btoa(encodeXORNoPattern(strToEncrypt,key));
|
|
10876
|
+
|
|
10877
|
+
}
|
|
10878
|
+
|
|
10879
|
+
function decrypt(strToDecrypt,key,/*OPTIONAL*/mode){
|
|
10880
|
+
if(!mode || typeof(sjcl)=="undefined") mode="xor";
|
|
10881
|
+
mode=mode.trim();
|
|
10882
|
+
|
|
10883
|
+
if(contains(mode,"aes") && contains(mode,"xor")){
|
|
9676
10884
|
|
|
9677
|
-
//
|
|
9678
|
-
|
|
9679
|
-
str+=" AOTEST REPORT \n";
|
|
9680
|
-
str+="REPORT: "+resultsFailed + "/" + resultsRunned + " scenarii failed.\n";
|
|
9681
|
-
str+="REPORT: "+(resultsRunned - resultsFailed) + "/" + resultsRunned + " scenarii succeeded.\n";
|
|
9682
|
-
str+="REPORT: "+resultsRunned + " scenarii runned.\n";
|
|
9683
|
-
str+="@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n";
|
|
10885
|
+
// AES
|
|
10886
|
+
var aesStr=sjcl.decrypt(key,strToDecrypt);
|
|
9684
10887
|
|
|
9685
|
-
|
|
9686
|
-
|
|
9687
|
-
|
|
9688
|
-
|
|
9689
|
-
|
|
9690
|
-
// TRACE
|
|
9691
|
-
str+="LOG: RESULT :>>>>>> Some tests FAILED... :(\n";
|
|
9692
|
-
}
|
|
9693
|
-
} else {
|
|
9694
|
-
// TRACE
|
|
9695
|
-
str+="LOG: RESULT :>>>>>> No tests runned... :|\n";
|
|
9696
|
-
}
|
|
10888
|
+
var xorStr=
|
|
10889
|
+
// XOR :
|
|
10890
|
+
encodeXORNoPattern(atob(aesStr),key);
|
|
10891
|
+
|
|
10892
|
+
return xorStr;
|
|
9697
10893
|
|
|
9698
|
-
|
|
9699
|
-
};
|
|
10894
|
+
}
|
|
9700
10895
|
|
|
10896
|
+
// DEPENDENCY : SJCL JS libray (Stanford Javascript Crypto Library)
|
|
10897
|
+
if(mode==="aes")
|
|
10898
|
+
return sjcl.decrypt(key,strToDecrypt);
|
|
10899
|
+
return encodeXORNoPattern(atob(strToDecrypt),key);
|
|
10900
|
+
|
|
10901
|
+
|
|
10902
|
+
}
|
|
9701
10903
|
|
|
9702
|
-
|
|
9703
|
-
|
|
9704
|
-
|
|
10904
|
+
function encodeXORNoPattern(strToEncode,key){
|
|
10905
|
+
let result="";
|
|
10906
|
+
let keyChunk=key;
|
|
10907
|
+
let keyLength=keyChunk.length;
|
|
10908
|
+
let previousChars="";
|
|
10909
|
+
let cnt=0;
|
|
10910
|
+
for(let i=0;i<strToEncode.length;i++){
|
|
10911
|
+
let char=strToEncode.charAt(i);
|
|
10912
|
+
let keyByte=keyChunk.charAt(cnt);
|
|
9705
10913
|
|
|
9706
|
-
result+=
|
|
10914
|
+
result+=String.fromCharCode(char.charCodeAt(0)^keyByte.charCodeAt(0));
|
|
9707
10915
|
|
|
9708
|
-
|
|
9709
|
-
|
|
9710
|
-
|
|
10916
|
+
if(cnt%keyLength===(keyLength-1)){
|
|
10917
|
+
cnt=0;
|
|
10918
|
+
// NO : TOO LONG TREATMENT !: We hash with all the previous blocks too :
|
|
10919
|
+
// We hash with a character of the previous chunk (they add up at each new chunk):
|
|
10920
|
+
keyChunk=getHashedString(previousChars+keyChunk);
|
|
9711
10921
|
|
|
9712
|
-
|
|
9713
|
-
|
|
10922
|
+
previousChars+=keyChunk.charAt(keyChunk.length-1);
|
|
10923
|
+
}else{
|
|
10924
|
+
cnt++;
|
|
10925
|
+
}
|
|
10926
|
+
}
|
|
10927
|
+
return result;
|
|
10928
|
+
}
|
|
9714
10929
|
|
|
10930
|
+
function generateRandomString(length,/*NULLABLE*/mode){
|
|
9715
10931
|
|
|
10932
|
+
// This list must be very conservative, because we want to be able to pass it through GET URLs !
|
|
10933
|
+
// OLD (not enough conservative ?) : const ALLOWED_CHARS="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0912346789_-£¢¤¬²³¼½¾";
|
|
10934
|
+
const ALLOWED_CHARS="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0912346789!-:;";
|
|
9716
10935
|
|
|
9717
10936
|
|
|
9718
|
-
|
|
9719
|
-
|
|
9720
|
-
|
|
10937
|
+
if(typeof(length)==="string"){
|
|
10938
|
+
if(contains(length,"->")){
|
|
10939
|
+
length=Math.getRandomInRange(length);
|
|
10940
|
+
}else{
|
|
10941
|
+
length=64;
|
|
10942
|
+
}
|
|
10943
|
+
}
|
|
9721
10944
|
|
|
9722
|
-
|
|
9723
|
-
console.log("TOTAL RESULT :",allTestsResultObj);
|
|
10945
|
+
var result="";
|
|
9724
10946
|
|
|
9725
|
-
|
|
9726
|
-
|
|
9727
|
-
|
|
9728
|
-
|
|
9729
|
-
|
|
10947
|
+
if(mode==="simple"){
|
|
10948
|
+
|
|
10949
|
+
for(var i=0;i<length;i++){
|
|
10950
|
+
var value=Math.getRandomInArray(ALLOWED_CHARS);
|
|
10951
|
+
result+=value;
|
|
10952
|
+
}
|
|
10953
|
+
|
|
10954
|
+
return result;
|
|
10955
|
+
}else if(mode==="human"){
|
|
10956
|
+
|
|
10957
|
+
const markovGraph={
|
|
10958
|
+
// For simplicity sakes, we ignore the consonants and vowels doubling possibilities:
|
|
10959
|
+
consonants:{// They always accept vowels at their following
|
|
10960
|
+
// Indicate ^ to specify wich of the other group letter to exclude as direct follower of the given letter :
|
|
10961
|
+
"b":["r","l"],
|
|
10962
|
+
"c":["h","t","k","l","r"],
|
|
10963
|
+
"d":["r"],
|
|
10964
|
+
"f":["r","l"],
|
|
10965
|
+
"g":["r","l"],
|
|
10966
|
+
"h":[],
|
|
10967
|
+
"j":["r"],
|
|
10968
|
+
"k":["r","l","t","h"],
|
|
10969
|
+
"l":["t"],
|
|
10970
|
+
"m":["b","p"],
|
|
10971
|
+
"n":["t","d"],
|
|
10972
|
+
"p":["h","r","s","t","l"],
|
|
10973
|
+
"q":["r","l"],
|
|
10974
|
+
"r":["t","c","d","k","q","h"],
|
|
10975
|
+
"s":["t","c","d","k","q","h"],
|
|
10976
|
+
"t":["r","h"],
|
|
10977
|
+
"v":["r","l"],
|
|
10978
|
+
"w":["r"],
|
|
10979
|
+
"x":["p"],
|
|
10980
|
+
"z":["r","h"],
|
|
10981
|
+
},
|
|
10982
|
+
vowels:{// They always accept consonants at their following
|
|
10983
|
+
"a":["i","u","y"],
|
|
10984
|
+
"e":["i","u","y"],
|
|
10985
|
+
"i":["e","o"],
|
|
10986
|
+
"o":["i","u"],
|
|
10987
|
+
"u":["i"],
|
|
10988
|
+
"y":[],
|
|
10989
|
+
},
|
|
10990
|
+
};
|
|
10991
|
+
|
|
10992
|
+
const LETTERS_TYPES=Object.keys(markovGraph);
|
|
9730
10993
|
|
|
10994
|
+
const CURR_LETTER_MODE=Math.getRandomInArray(LETTERS_TYPES);
|
|
10995
|
+
const POSSIBLE_LETTERS=Object.keys(markovGraph[CURR_LETTER_MODE]);
|
|
10996
|
+
const CURR_LETTER=Math.getRandomInArray(POSSIBLE_LETTERS);
|
|
10997
|
+
|
|
10998
|
+
var automataState={
|
|
10999
|
+
currentLetterMode:CURR_LETTER_MODE,
|
|
11000
|
+
possibleLetters:POSSIBLE_LETTERS,
|
|
11001
|
+
currentLetter:CURR_LETTER,
|
|
11002
|
+
|
|
11003
|
+
calculateLetter:function(){
|
|
11004
|
+
|
|
11005
|
+
this.currentLetter=Math.getRandomInArray(this.possibleLetters);
|
|
11006
|
+
|
|
11007
|
+
var sameLetters=Object.keys( markovGraph[this.currentLetterMode] );
|
|
11008
|
+
if(!contains(sameLetters,this.currentLetter)){
|
|
11009
|
+
// If we picked from the same group : we don't change of group to pick
|
|
11010
|
+
// If we picked from the other group : we do change of group to pick
|
|
11011
|
+
|
|
11012
|
+
var otherLetterMode=foreach(LETTERS_TYPES,(item)=>{
|
|
11013
|
+
if(item!==this.currentLetterMode) return item;
|
|
11014
|
+
});
|
|
11015
|
+
|
|
11016
|
+
this.currentLetterMode=otherLetterMode;
|
|
11017
|
+
}
|
|
9731
11018
|
|
|
11019
|
+
},
|
|
11020
|
+
|
|
11021
|
+
updatePossibleLettersForLetter:function(){
|
|
11022
|
+
|
|
11023
|
+
var nextPossibleLetters=[];
|
|
11024
|
+
var nextNotPossibleLetters=[];
|
|
9732
11025
|
|
|
9733
|
-
|
|
9734
|
-
|
|
9735
|
-
|
|
9736
|
-
|
|
9737
|
-
|
|
9738
|
-
|
|
11026
|
+
// We store those of the other group marked «^»
|
|
11027
|
+
foreach(markovGraph[this.currentLetterMode][this.currentLetter],(letter)=>{
|
|
11028
|
+
if(letter.charAt(0)==="^"){
|
|
11029
|
+
nextNotPossibleLetters.push(letter.charAt(1));
|
|
11030
|
+
}else{
|
|
11031
|
+
nextPossibleLetters.push(letter);
|
|
11032
|
+
}
|
|
11033
|
+
});
|
|
11034
|
+
|
|
11035
|
+
var otherLetterMode=foreach(LETTERS_TYPES,(item)=>{
|
|
11036
|
+
if(item!==this.currentLetterMode) return item;
|
|
11037
|
+
});
|
|
11038
|
+
var otherLetters=Object.keys(markovGraph[otherLetterMode]);
|
|
11039
|
+
|
|
11040
|
+
// We exclude those marked «^»
|
|
11041
|
+
foreach( otherLetters ,(letter)=>{
|
|
11042
|
+
if(!contains(nextNotPossibleLetters,letter)){
|
|
11043
|
+
nextPossibleLetters.push(letter);
|
|
11044
|
+
}
|
|
11045
|
+
});
|
|
9739
11046
|
|
|
9740
|
-
|
|
11047
|
+
this.possibleLetters=nextPossibleLetters;
|
|
11048
|
+
}
|
|
11049
|
+
};
|
|
11050
|
+
|
|
11051
|
+
var i=0;
|
|
11052
|
+
do{
|
|
9741
11053
|
|
|
9742
|
-
|
|
9743
|
-
|
|
9744
|
-
let method=rootObject[methodName];
|
|
9745
|
-
if(method && isFunction(method)){
|
|
9746
|
-
rootObject[methodName]=function(){
|
|
9747
|
-
|
|
9748
|
-
let time=getNow();
|
|
9749
|
-
|
|
9750
|
-
method.apply(rootObject, arguments);
|
|
11054
|
+
result+=automataState.currentLetter;
|
|
9751
11055
|
|
|
9752
|
-
|
|
11056
|
+
automataState.updatePossibleLettersForLetter();
|
|
11057
|
+
automataState.calculateLetter();
|
|
9753
11058
|
|
|
9754
|
-
|
|
9755
|
-
|
|
9756
|
-
|
|
11059
|
+
i++;
|
|
11060
|
+
}while(i<length);
|
|
11061
|
+
|
|
11062
|
+
return result;
|
|
9757
11063
|
}
|
|
9758
11064
|
|
|
9759
|
-
|
|
9760
|
-
|
|
9761
|
-
|
|
9762
|
-
|
|
9763
|
-
|
|
9764
|
-
aotest.profile(attr,methodName,visited);
|
|
9765
|
-
|
|
9766
|
-
});
|
|
11065
|
+
for(var i=0;i<length;i++){
|
|
11066
|
+
var value=Math.getRandomInt(255,0);
|
|
11067
|
+
result+=String.fromCharCode(value);
|
|
11068
|
+
}
|
|
9767
11069
|
|
|
11070
|
+
if(mode==="base64"){
|
|
11071
|
+
return btoa(result);
|
|
11072
|
+
}
|
|
9768
11073
|
|
|
9769
|
-
|
|
11074
|
+
return result;
|
|
11075
|
+
}
|
|
11076
|
+
|
|
11077
|
+
|
|
11078
|
+
|
|
11079
|
+
// NOT AOTESTABLE !
|
|
11080
|
+
function getUniqueIdWithDate(){
|
|
11081
|
+
return getHashedString(getNow() + "");
|
|
11082
|
+
}
|
|
11083
|
+
|
|
11084
|
+
|
|
11085
|
+
|
|
11086
|
+
|
|
11087
|
+
|
|
11088
|
+
|
|
11089
|
+
//================================================================
|
|
11090
|
+
//================= Miscelanneous utility methods =================
|
|
11091
|
+
//================================================================
|
|
11092
|
+
|
|
9770
11093
|
|
|
9771
11094
|
|
|
9772
11095
|
|