• R/O
  • SSH

fcf-framework-core: Commit

Main functions and classes of the FCF framework


Commit MetaInfo

Révisionf3713d135d07eea78fc9a110d102fc13e72cb7a0 (tree)
l'heure2023-05-01 04:00:14
Auteurvmarkin
Commitervmarkin

Message de Log

version 2.0.2

Change Summary

Modification

diff -r f396c07135fc -r f3713d135d07 .fcfbuild
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.fcfbuild Sun Apr 30 22:00:14 2023 +0300
@@ -0,0 +1,10 @@
1+{
2+ skip: [
3+ "^NDetails/resolver.js",
4+ "^NDetails/inlineExecution.js",
5+ "^NDetails/load.js",
6+ "^NProcess/NProcess.js",
7+ "^fcf_origin.js",
8+ "^serverConfig.js",
9+ ]
10+}
diff -r f396c07135fc -r f3713d135d07 NDetails/inlineExecution.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/NDetails/inlineExecution.js Sun Apr 30 22:00:14 2023 +0300
@@ -0,0 +1,1205 @@
1+(function() {
2+ let STATE_END = 0;
3+ const S_WAIT = STATE_END++;
4+ const S_BACK = STATE_END++;
5+ const S_NUMBER_START = STATE_END++;
6+ const S_NUMBER_READ = STATE_END++;
7+ const S_STRING = STATE_END++;
8+ const S_ESTRING = STATE_END++;
9+ const S_KEY_START = STATE_END++;
10+ const S_KEY_READ = STATE_END++;
11+ const S_OBJECT_START = STATE_END++;
12+ const S_OBJECT_SIMPLE_READ = STATE_END++;
13+ const S_OBJECT_RCLOSE = STATE_END++;
14+ const S_OBJECT_CCLOSE = STATE_END++;
15+ const S_OBJECT_NEXT = STATE_END++;
16+ const S_ARRAY_START = STATE_END++;
17+ const S_ARRAY_NEXT = STATE_END++;
18+ const S_DOBJECT_START = STATE_END++;
19+ const S_DOBJECT_NEXT = STATE_END++;
20+ const S_DOBJECT_KEY = STATE_END++;
21+ const S_COMMAND_READ = STATE_END++;
22+ const S_UCOMMAND_READ = STATE_END++;
23+ const S_BLOCK_OPEN = STATE_END++;
24+ const S_BLOCK_CLOSE = STATE_END++;
25+ const S_BLOCK_SELECT = STATE_END++;
26+ const S_BLOCK_SELECTA = STATE_END++;
27+
28+ const KEYWORDS = {
29+ "new": 1,
30+ "typeof": 1,
31+ "NaN": 1,
32+ "Infinity": 1,
33+ "undefined": 1,
34+ "false": 1,
35+ "true": 1,
36+ };
37+
38+ const UCOMMANDS = {
39+ "-": "!-",
40+ "!": "!",
41+ "~": "~",
42+ };
43+
44+ const COMMANDS = {
45+ "*": { "": "*", "*": {"": "**"} },
46+ "/": { "": "/" },
47+ "%": { "": "%" },
48+ "+": { "": "+" },
49+ "-": { "": "-" },
50+ "^": { "": "^" },
51+ "|": { "": "|", "|": {"": "||"} },
52+ "&": { "": "&", "&": {"": "&&"} },
53+ "<": { "": "<", "=": {"": "<="}, "<": {"": "<<"} },
54+ ">": { "": ">", "=": {"": ">="}, ">": {"": ">>", ">": {"": ">>>"}} },
55+ "!": { "=": {"": "!=", "=": {"": "!=="}} },
56+ "=": { "=": {"": "==", "=": {"": "==="}} },
57+ "i": { "n": {"": "in", "s": {"t": {"a": {"n": {"c": {"e": {"o": {"f": {"": "instanceof"}}}}}}}}} },
58+ };
59+
60+ const WEIGHTS = {
61+ "**": 14,
62+ "*": 13,
63+ "/": 13,
64+ "%": 13,
65+ "+": 12,
66+ "-": 12,
67+ "<<": 11,
68+ ">>": 11,
69+ ">>>": 11,
70+ "<": 10,
71+ "<=": 10,
72+ ">": 10,
73+ ">=": 10,
74+ "instanceof": 9,
75+ "in": 9,
76+ "==": 9,
77+ "!=": 9,
78+ "===": 9,
79+ "!==": 9,
80+ "&": 8,
81+ "^": 7,
82+ "|": 6,
83+ "&&": 5,
84+ "||": 4,
85+ };
86+
87+ const ARGS = {
88+ "?": [3, 3],
89+ "!-": [1, 1],
90+ "!": [1, 1],
91+ "~": [1, 1],
92+ "b": [0, 1],
93+ "bi": [1, 1],
94+ "v": [1, 1],
95+ "i": [1, 1],
96+ "n": [1, 1],
97+ "t": [1, 1],
98+ "cmd": [1, 1],
99+ "c": [0, Infinity],
100+ "o": [1, Infinity],
101+ "d": [0, Infinity],
102+ "instanceof": [2, 2],
103+ "in": [2, 2],
104+ "di": [2, 2],
105+ "a": [0, Infinity],
106+ "k": [1, 1],
107+ "**": [2, 2],
108+ "*": [2, 2],
109+ "/": [2, 2],
110+ "%": [2, 2],
111+ "+": [2, 2],
112+ "-": [2, 2],
113+ "<<": [2, 2],
114+ ">>": [2, 2],
115+ ">>>": [2, 2],
116+ "<": [2, 2],
117+ "<=": [2, 2],
118+ ">": [2, 2],
119+ ">=": [2, 2],
120+ "==": [2, 2],
121+ "!=": [2, 2],
122+ "===": [2, 2],
123+ "!==": [2, 2],
124+ "&": [2, 2],
125+ "^": [2, 2],
126+ "|": [2, 2],
127+ "&&": [2, 2],
128+ "||": [2, 2],
129+ };
130+
131+ const CHAR0 = '0'.charCodeAt(0);
132+ const CHAR9 = '9'.charCodeAt(0);
133+ const CHARa = 'a'.charCodeAt(0);
134+ const CHARz = 'z'.charCodeAt(0);
135+ const CHARA = 'A'.charCodeAt(0);
136+ const CHARZ = 'Z'.charCodeAt(0);
137+ const CHAR$ = '$'.charCodeAt(0);
138+ const CHAR_ = '_'.charCodeAt(0);
139+
140+
141+ module.exports = fcf.NDetails.inlineExecution = new (class {
142+ execute(a_command, a_args) {
143+ let instructions = typeof a_command == "object" ? a_command : this.parse(a_command);
144+ let info = {
145+ env: fcf.getConfiguration()._tokenizeEnvironment,
146+ args: a_args,
147+ command: a_command
148+ };
149+ return this._execute(info, instructions);
150+ }
151+ _resolve(a_path, a_env) {
152+ for (let p of a_path) {
153+ if (a_env === null && typeof a_env !== "object") {
154+ return {c: false};
155+ }
156+ a_env = a_env[p];
157+ }
158+ return {r: a_env, c: true };
159+ }
160+ _execute(a_info, a_op) {
161+ switch (a_op.t) {
162+ case "b":
163+ return this._execute(a_info, a_op.a[0]);
164+ case "bi":
165+ return this._execute(a_info, a_op.a[0]);
166+ case "v":
167+ return a_op.a[0];
168+ case "cmd":
169+ return this.execute(a_op.a[0], a_info.args);
170+ case "n":
171+ case "o":
172+ {
173+ let newComplete = false;
174+ let isNew = a_op.t == "n";
175+ let items = a_op.t == "n" ? a_op.a[0].a : a_op.a;
176+ let sources = [a_info.args, a_info.env];
177+ let lstObject;
178+ let object;
179+ let found;
180+ let call = items.find((a_item)=>{ return a_item.t == "c" });
181+ if (!call && isNew){
182+ items = fcf.clone(items);
183+ items.push({t:"c", a:[]});
184+ call = true;
185+ }
186+ let calls;
187+ let callsAll;
188+ let directCall;
189+ let lstKey;
190+ let envInfo;
191+
192+ found = items[0].t != "i" ? 0 : -1;
193+ if (call) {
194+ calls = fcf.getConfiguration()._tokenizeFunctions;
195+ callsAll = [];
196+ }
197+ if (found == -1) {
198+ if (call) {
199+ for(let i = 0; i < items.length; ++i) {
200+ lstKey = this._execute(a_info, items[i]);
201+ if (items[i+1].t == "i"){
202+ if (calls.items["*"])
203+ callsAll.push(calls.items["*"].functions)
204+ calls = typeof calls.items[lstKey] == "object" ? calls.items[lstKey] : undefined;
205+ } else {
206+ if (calls.functions[lstKey] || calls.functions["*"]){
207+ found = i;
208+ }
209+ break;
210+ }
211+ if (!calls)
212+ break;
213+ }
214+ }
215+ if (found == -1){
216+ let fullFound = -1;
217+ let shortFound = -1;
218+ lstObject = undefined;
219+ let isEnv = false;
220+ for(let source of sources) {
221+ envInfo = !call && isEnv ? fcf.getConfiguration()._tokenizeEnvironmentInfo : undefined;
222+ isEnv = true;
223+ object = source;
224+ for (let i = 0; i < items.length && items[i].t == "i"; ++i) {
225+ if ((typeof object !== "object" && typeof object !== "string") || object === null) {
226+ shortFound = -1;
227+ fullFound = -1;
228+ envInfo = undefined;
229+ break;
230+ }
231+ lstKey = this._execute(a_info, items[i]);
232+ lstObject = object;
233+ let exists = typeof object == "object" && lstKey in object ? true :
234+ typeof object == "string" && (lstKey == "length" || !isNaN(lstKey)) ? true :
235+ false;
236+ if (envInfo) {
237+ if (i == 0) {
238+ envInfo = envInfo.parts[lstKey];
239+ } else if (envInfo.parts[lstKey]){
240+ envInfo = envInfo.parts[lstKey];
241+ }
242+ }
243+ if (exists) {
244+ fullFound = i;
245+ } else {
246+ fullFound = -1;
247+ }
248+ object = object[lstKey];
249+ shortFound = i;
250+ }
251+ if (shortFound != -1 && found == -1) {
252+ found = shortFound;
253+ }
254+ if (fullFound != -1){
255+ found = fullFound;
256+ break;
257+ }
258+ }
259+ }
260+ } else {
261+ object = this._execute(a_info, items[0]);
262+ }
263+ if (found == -1) {
264+ throw new fcf.Exception("ACCESS_FAILED_FIELD_TOKENIZE", {command: a_info.command});
265+ }
266+ if (!call && envInfo && envInfo.access === false) {
267+ throw new fcf.Exception("ACCESS_FAILED_FIELD_TOKENIZE", {command: a_info.command});
268+ }
269+ ++found;
270+ for (let i = found; i < items.length; ++i) {
271+ if (items[i].t == "c") {
272+ let args = [];
273+ for(let arg of items[i].a) {
274+ args.push(this._execute(a_info, arg));
275+ }
276+ let callInfo;
277+ let callInfoAll;
278+ let globalObject = typeof global === "object" ? global : window;
279+ let directCall = false;
280+
281+ if (calls) {
282+ if (calls.functions[lstKey]){
283+ callInfo = calls.functions[lstKey][calls.functions[lstKey].length-1];
284+ directCall = true;
285+ } else if (calls.functions["*"]){
286+ callInfo = calls.functions["*"][calls.functions["*"].length-1];
287+ directCall = true;
288+ }
289+ }
290+
291+ if (!callInfo) {
292+ for(let c of callsAll) {
293+ if (typeof c[lstKey] == "object") {
294+ for(let ci of c[lstKey]) {
295+ if (ci.class){
296+ if (ci.class == "String") {
297+ if (typeof lstObject == "string") {
298+ callInfo = ci;
299+ }
300+ } else {
301+ let classItem = fcf.resolve(globalObject, ci.class);
302+ if (lstObject instanceof classItem){
303+ callInfo = ci;
304+ }
305+ }
306+ } else {
307+ callInfo = ci;
308+ }
309+ }
310+ }
311+ if (c["*"]) {
312+ for(let ci of c["*"]) {
313+ if (ci.class){
314+ if (ci.class == "String") {
315+ if (typeof lstObject == "string") {
316+ callInfoAll = ci;
317+ }
318+ } else {
319+ let classItem = fcf.resolve(globalObject, ci.class);
320+ if (lstObject instanceof classItem){
321+ callInfoAll = ci;
322+ }
323+ }
324+ } else {
325+ callInfoAll = ci;
326+ }
327+ }
328+ }
329+ }
330+ }
331+ if (!callInfo && callInfoAll){
332+ callInfo = callInfoAll;
333+ }
334+ if (!callInfo){
335+ throw new fcf.Exception("ACCESS_FAILED_FIELD_TOKENIZE", {command: a_info.command});
336+ }
337+ if (isNew && !newComplete){
338+ lstObject = object;
339+ if (directCall && callInfo.object) {
340+ object = (callInfo.object[1] == "" ? globalObject : fcf.resolve(globalObject, callInfo.object[1]))[lstKey];
341+ }
342+ object = new object(...args);
343+ newComplete = true;
344+ } else {
345+ let lst = object;
346+ if (directCall && callInfo.object) {
347+ lstObject = callInfo.object[1] == "" ? globalObject : fcf.resolve(globalObject, callInfo.object[1]);
348+ object = lstObject[lstKey];
349+ }
350+ if (object === undefined || object === null){
351+ throw new fcf.Exception("ACCESS_FAILED_FIELD_TOKENIZE", {command: a_info.command});
352+ }
353+ object = object.apply(lstObject, args);
354+ lstObject = lst;
355+ }
356+ } else if (items[i].t == "i") {
357+ if (!call) {
358+ if ((typeof object != "object" && typeof object != "string") || object === null) {
359+ throw new fcf.Exception("ACCESS_FAILED_FIELD_TOKENIZE", {command: a_info.command});
360+ }
361+ }
362+ lstKey = this._execute(a_info, items[i]);
363+ if (calls) {
364+ if (calls.items["*"]){
365+ callsAll.push(calls.items["*"].functions)
366+ }
367+ calls = typeof calls.items[lstKey] == "object" ? calls.items[lstKey] : undefined;
368+ }
369+ lstObject = object;
370+ if (object !== null) {
371+ object = object[lstKey];
372+ }
373+ } else {
374+ lstObject = object;
375+ object = this._execute(a_info, items[i]);
376+ }
377+ }
378+ if (isNew && !newComplete){
379+ object = new object();
380+ }
381+ return object;
382+ }
383+ case "d":
384+ {
385+ let object = {};
386+ for(let di of a_op.a) {
387+ object[this._execute(a_info, di.a[0])] = this._execute(a_info, di.a[1]);
388+ }
389+ return object;
390+ }
391+ break;
392+ case "a":
393+ {
394+ let arr = [];
395+ for(let itm of a_op.a) {
396+ arr.push(this._execute(a_info, itm));
397+ }
398+ return arr;
399+ }
400+ break;
401+ case "i":
402+ return typeof a_op.a[0] != "object" ? a_op.a[0] : this._execute(a_info, a_op.a[0]);
403+ case "instanceof":
404+ return this._execute(a_info, a_op.a[0]) instanceof this._execute(a_info, a_op.a[1]);
405+ case "in":
406+ return this._execute(a_info, a_op.a[0]) in this._execute(a_info, a_op.a[1]);
407+ case "t":
408+ return typeof this._execute(a_info, a_op.a[0]);
409+ case "?":
410+ return this._execute(a_info, a_op.a[0]) ? this._execute(a_info, a_op.a[1]) : this._execute(a_info, a_op.a[2]);
411+ case "!-":
412+ return -this._execute(a_info, a_op.a[0]);
413+ case "!":
414+ return !this._execute(a_info, a_op.a[0]);
415+ case "~":
416+ return ~this._execute(a_info, a_op.a[0]);
417+ case "**":
418+ return this._execute(a_info, a_op.a[0]) ** this._execute(a_info, a_op.a[1]);
419+ case "*":
420+ return this._execute(a_info, a_op.a[0]) * this._execute(a_info, a_op.a[1]);
421+ case "/":
422+ return this._execute(a_info, a_op.a[0]) / this._execute(a_info, a_op.a[1]);
423+ case "%":
424+ return this._execute(a_info, a_op.a[0]) % this._execute(a_info, a_op.a[1]);
425+ case "+":
426+ return this._execute(a_info, a_op.a[0]) + this._execute(a_info, a_op.a[1]);
427+ case "-":
428+ return this._execute(a_info, a_op.a[0]) - this._execute(a_info, a_op.a[1]);
429+ case "<<":
430+ return this._execute(a_info, a_op.a[0]) << this._execute(a_info, a_op.a[1]);
431+ case ">>":
432+ return this._execute(a_info, a_op.a[0]) >> this._execute(a_info, a_op.a[1]);
433+ case ">>>":
434+ return this._execute(a_info, a_op.a[0]) >>> this._execute(a_info, a_op.a[1]);
435+ case "<":
436+ return this._execute(a_info, a_op.a[0]) < this._execute(a_info, a_op.a[1]);
437+ case "<=":
438+ return this._execute(a_info, a_op.a[0]) <= this._execute(a_info, a_op.a[1]);
439+ case ">":
440+ return this._execute(a_info, a_op.a[0]) > this._execute(a_info, a_op.a[1]);
441+ case ">=":
442+ return this._execute(a_info, a_op.a[0]) >= this._execute(a_info, a_op.a[1]);
443+ case "==":
444+ return this._execute(a_info, a_op.a[0]) == this._execute(a_info, a_op.a[1]);
445+ case "!=":
446+ return this._execute(a_info, a_op.a[0]) != this._execute(a_info, a_op.a[1]);
447+ case "===":
448+ return this._execute(a_info, a_op.a[0]) === this._execute(a_info, a_op.a[1]);
449+ case "!==":
450+ return this._execute(a_info, a_op.a[0]) !== this._execute(a_info, a_op.a[1]);
451+ case "&":
452+ return this._execute(a_info, a_op.a[0]) & this._execute(a_info, a_op.a[1]);
453+ case "^":
454+ return this._execute(a_info, a_op.a[0]) ^ this._execute(a_info, a_op.a[1]);
455+ case "|":
456+ return this._execute(a_info, a_op.a[0]) | this._execute(a_info, a_op.a[1]);
457+ case "&&":
458+ return this._execute(a_info, a_op.a[0]) && this._execute(a_info, a_op.a[1]);
459+ case "||":
460+ return this._execute(a_info, a_op.a[0]) || this._execute(a_info, a_op.a[1]);
461+ default:
462+ throw new fcf.Exception("INVALID_COMMAND_TOKENIZE_INTERPETER", {command: a_info.command});
463+ break;
464+ }
465+ }
466+
467+ _popStack(a_command, a_stack) {
468+ if (a_stack.length == 1 ||
469+ !ARGS[a_stack[a_stack.length-1].t] ||
470+ a_stack[a_stack.length-1].a.length < ARGS[a_stack[a_stack.length-1].t][0]) {
471+ if (!a_stack[a_stack.length-1].r) {
472+ throw new fcf.Exception("INVALID_COMMAND_TOKENIZE_INTERPETER", {command: a_command});
473+ }
474+ }
475+ a_stack.pop();
476+ if (a_stack[a_stack.length-1].u) {
477+ this._popStack(a_command, a_stack);
478+ }
479+ }
480+
481+ _pushArg(a_command, a_item, a_arg) {
482+ let mc = ARGS[a_item.t][1];
483+ if (a_item.a.length >= mc){
484+ throw new fcf.Exception("INVALID_COMMAND_TOKENIZE_INTERPETER", {command: a_command});
485+ }
486+ a_item.a.push(a_arg);
487+ }
488+
489+ _isVarChar(a_char, a_isFirst) {
490+ const cn = a_char !== undefined ? a_char.charCodeAt(0) : undefined;
491+ return cn > 181 ||
492+ (!a_isFirst && cn >= CHAR0 && cn <= CHAR9) ||
493+ (cn >= CHARa && cn <= CHARz) ||
494+ (cn >= CHARA && cn <= CHARZ) ||
495+ cn === CHAR$ || cn === CHAR_;
496+ }
497+
498+ parse(a_command){
499+ a_command = fcf.str(a_command);
500+ let state = S_WAIT;
501+ let stack = [{t: "b", b: true, a: []}];
502+ const cn0 = "0".charCodeAt(0);
503+ const cn9 = "9".charCodeAt(0);
504+ const cnd = ".".charCodeAt(0);
505+ const cnq = "'".charCodeAt(0);
506+ const cndq = "\"".charCodeAt(0);
507+ const lengthWithZero = a_command.length+1;
508+ let isBreak;
509+ let lstState = -1;
510+ let switches = [];
511+ let nswitches = [];
512+ for(let i = 0; i < lengthWithZero; ++i) {
513+ do {
514+ let c = a_command[i];
515+ let cn = a_command.charCodeAt(i);
516+ isBreak = true;
517+ if (state != S_WAIT && lstState == S_WAIT) {
518+ if (switches.length){
519+ if (!switches[switches.length-1][state]){
520+ throw new fcf.Exception("INVALID_COMMAND_TOKENIZE_INTERPETER", {command: a_command});
521+ } else {
522+ --switches[switches.length-1][state];
523+ }
524+ }
525+ if (nswitches.length){
526+ for(let j = 0; j < nswitches.length; ++j) {
527+ if (nswitches[j][state] && stack[stack.length-1].t == nswitches[j][state]){
528+ throw new fcf.Exception("INVALID_COMMAND_TOKENIZE_INTERPETER", {command: a_command});
529+ }
530+ }
531+ nswitches = [];
532+ }
533+ }
534+ lstState = state;
535+ switch (state) {
536+ case S_WAIT:
537+ if (cn >= 0 && cn <= 32) {
538+ continue;
539+ } else if (c == "(") {
540+ state = S_BLOCK_OPEN;
541+ isBreak = false;
542+ continue;
543+ } else if (c == ")") {
544+ for(let i = stack.length - 1; i >= 0; --i) {
545+ if (stack[i].t == "c") {
546+ state = S_OBJECT_CCLOSE;
547+ break;
548+ } else if (stack[i].t == "b") {
549+ state = S_BLOCK_CLOSE;
550+ break;
551+ }
552+ }
553+ isBreak = false;
554+ continue;
555+ } else if (c == ",") {
556+ let found;
557+ for(let i = stack.length - 1; i >= 0; --i) {
558+ if (stack[i].t == "a") {
559+ while(stack[stack.length - 1].t != "a") {
560+ this._popStack(a_command, stack);
561+ }
562+ found = true;
563+ state = S_ARRAY_NEXT;
564+ isBreak = false;
565+ break;
566+ } else if (stack[i].t == "d") {
567+ while(stack[stack.length - 1].t != "d") {
568+ this._popStack(a_command, stack);
569+ }
570+ found = true;
571+ state = S_DOBJECT_NEXT;
572+ isBreak = false;
573+ break;
574+ } else if (stack[i].t == "c") {
575+ while(stack[stack.length - 1].t != "c") {
576+ this._popStack(a_command, stack);
577+ }
578+ let ns = {};
579+ ns[S_COMMAND_READ] = "c";
580+ nswitches.push(ns);
581+ found = true;
582+ break;
583+ }
584+ }
585+ if (!found) {
586+ throw new fcf.Exception("INVALID_COMMAND_TOKENIZE_INTERPETER", {command: a_command});
587+ }
588+ continue;
589+ } else if (c == "?") {
590+ state = S_BLOCK_SELECT;
591+ isBreak = false;
592+ continue;
593+ } else if (c == "{") {
594+ state = S_DOBJECT_START;
595+ continue;
596+ } else if (c == "}") {
597+ state = S_DOBJECT_NEXT;
598+ isBreak = false;
599+ continue;
600+ } else if (c == "[") {
601+ state = S_ARRAY_START;
602+ isBreak = false;
603+ continue;
604+ } else if (c == "]") {
605+ let found = false;
606+ for(let i = stack.length - 1; i >= 1; --i) {
607+ if (stack[i].t == "i" || stack[i].t == "a"){
608+ found = stack[i].t;
609+ break;
610+ }
611+ }
612+ if (found == "i") {
613+ state = S_OBJECT_RCLOSE;
614+ isBreak = false;
615+ } else if (found == "a") {
616+ state = S_ARRAY_NEXT;
617+ isBreak = false;
618+ } else {
619+ throw new fcf.Exception("INVALID_COMMAND_TOKENIZE_INTERPETER", {command: a_command});
620+ }
621+ continue;
622+ } else if (c == ":") {
623+ for(let i = stack.length - 1; i >= 0; --i) {
624+ if (i == 0) {
625+ throw new fcf.Exception("INVALID_COMMAND_TOKENIZE_INTERPETER", {command: a_command});
626+ }
627+ if (stack[i].t == "?") {
628+ state = S_BLOCK_SELECTA;
629+ isBreak = false;
630+ break;
631+ } else if (stack[i].t == "d"){
632+ state = S_DOBJECT_NEXT;
633+ isBreak = false;
634+ break;
635+ }
636+ }
637+ continue;
638+ } else if (cn == cnq || cn == cndq) {
639+ state = S_STRING;
640+ isBreak = false;
641+ continue;
642+ } else if (c == "`") {
643+ state = S_ESTRING;
644+ isBreak = false;
645+ continue;
646+ } else if (cn >= cn0 && cn <= cn9) {
647+ if (stack[stack.length - 1].a.length + 1 > ARGS[stack[stack.length - 1].t][1]) {
648+ throw new fcf.Exception("INVALID_COMMAND_TOKENIZE_INTERPETER", {command: a_command});
649+ }
650+ state = S_NUMBER_START;
651+ isBreak = false;
652+ continue;
653+ } else if (a_command[i] == "i" && a_command[i+1] == "n" && (!this._isVarChar(a_command[i+2]) || (a_command[i+2] == "s" && a_command[i+3] == "t" && a_command[i+4] == "a" && a_command[i+5] == "n" && a_command[i+6] == "c" && a_command[i+7] == "e" && a_command[i+8] == "o" && a_command[i+9] == "f" && !this._isVarChar(a_command[i+10])))) {
654+ state = S_COMMAND_READ;
655+ isBreak = false;
656+ } else if (this._isVarChar(c, true)) {
657+ if (stack[stack.length - 1].t == "di" && !stack[stack.length - 1].a.length) {
658+ state = S_KEY_START;
659+ isBreak = false;
660+ } else {
661+ state = S_OBJECT_START;
662+ isBreak = false;
663+ }
664+ } else if (isNaN(cn)) {
665+ break;
666+ } else if (stack[stack.length - 1].a.length != 0) {
667+ if (stack[stack.length - 1].c && stack[stack.length - 1].a.length != 2 && UCOMMANDS[c]) {
668+ state = S_UCOMMAND_READ;
669+ isBreak = false;
670+ continue;
671+ } else if (stack[stack.length - 1].t == "di" && stack[stack.length - 1].a.length == 1 && UCOMMANDS[c]) {
672+ state = S_UCOMMAND_READ;
673+ isBreak = false;
674+ continue;
675+ } else if (stack[stack.length - 1].t == "?" && UCOMMANDS[c]) {
676+ state = S_UCOMMAND_READ;
677+ isBreak = false;
678+ continue;
679+ }
680+ if (stack[stack.length - 1].t == "c" && UCOMMANDS[c]) {
681+ state = S_UCOMMAND_READ;
682+ isBreak = false;
683+ continue;
684+ }
685+ if (stack[stack.length - 1].t == "a" && UCOMMANDS[c]) {
686+ state = S_UCOMMAND_READ;
687+ isBreak = false;
688+ continue;
689+ }
690+ state = S_COMMAND_READ;
691+ isBreak = false;
692+ continue;
693+ } else if (c in UCOMMANDS) {
694+ state = S_UCOMMAND_READ;
695+ isBreak = false;
696+ continue;
697+ } else {
698+ throw new fcf.Exception("INVALID_COMMAND_TOKENIZE_INTERPETER", {command: a_command});
699+ }
700+ break;
701+ case S_BLOCK_SELECT:
702+ {
703+ let fill = false;
704+ let item = stack[stack.length - 1];
705+ while(!item.b) {
706+ this._popStack(a_command, stack);
707+ item = stack[stack.length - 1];
708+ }
709+ let v = {t: "?", b: true, a: []};
710+ this._pushArg(a_command, v, item.a[item.a.length-1]);
711+ item.a[item.a.length-1] = v;
712+ stack.push(v);
713+ state = S_WAIT;
714+ }
715+ break;
716+ case S_BLOCK_SELECTA:
717+ {
718+ let fill = false;
719+ let item = stack[stack.length - 1];
720+ while(item.t != "?" || (item.t == "?" && item.a.length == 3)) {
721+ fill = true;
722+ this._popStack(a_command, stack);
723+ item = stack[stack.length - 1];
724+ }
725+ state = S_WAIT;
726+ }
727+ break;
728+ case S_BLOCK_OPEN:
729+ {
730+ let v = {t: "b", b: true, a: []};
731+ let o = {t: "o", a: [v]};
732+ this._pushArg(a_command, stack[stack.length - 1], o);
733+ stack.push(o);
734+ stack.push(v);
735+ state = S_WAIT;
736+ }
737+ break;
738+ case S_BLOCK_CLOSE:
739+ if (!stack[stack.length-1].a.length) {
740+ throw new fcf.Exception("INVALID_COMMAND_TOKENIZE_INTERPETER", {command: a_command});
741+ }
742+ while(stack[stack.length-1].t != "b") {
743+ this._popStack(a_command, stack);
744+ }
745+ this._popStack(a_command, stack);
746+ state = S_OBJECT_NEXT;
747+ break;
748+ case S_BACK:
749+ state = S_WAIT;
750+ isBreak = false;
751+ continue;
752+ break;
753+ case S_UCOMMAND_READ:
754+ {
755+ let v = {t: UCOMMANDS[c], u: true, a: []};
756+ this._pushArg(a_command, stack[stack.length - 1], v);
757+ if (c == "-" && a_command[i - 1] == "-") {
758+ throw new fcf.Exception("INVALID_COMMAND_TOKENIZE_INTERPETER", {command: a_command});
759+ }
760+
761+ stack.push(v);
762+ state = S_WAIT;
763+ }
764+ break;
765+ case S_COMMAND_READ:
766+ let lastCommand;
767+ let lastIndex = i;
768+ let commands = COMMANDS;
769+ let j = i;
770+ while(commands) {
771+ if (!commands[a_command[j]]) {
772+ break;
773+ }
774+ if (commands[a_command[j]][""]){
775+ lastCommand = commands[a_command[j]][""];
776+ lastIndex = j;
777+ }
778+ commands = commands[a_command[j]];
779+ ++j;
780+ }
781+ i = lastIndex;
782+ if (!lastCommand) {
783+ throw new fcf.Exception("INVALID_COMMAND_TOKENIZE_INTERPETER", {command: a_command});
784+ }
785+ let newItm = {t: lastCommand, c: true, a: []};
786+ let weight = WEIGHTS[lastCommand];
787+ let stackItem = stack[stack.length - 1];
788+ while(true) {
789+ if (stack[stack.length-1].c && weight <= WEIGHTS[stack[stack.length-1].t]){
790+ this._popStack(a_command, stack);
791+ stackItem = stack[stack.length-1];
792+ } else {
793+ break;
794+ }
795+ }
796+ let curItm = stackItem.a[stackItem.a.length-1];
797+ this._pushArg(a_command, newItm, curItm);
798+ stackItem.a[stackItem.a.length-1] = newItm;
799+ stack.push(newItm);
800+ state = S_WAIT;
801+ break;
802+ case S_NUMBER_START:
803+ {
804+ let v = {t: "v", a: [""]};
805+ this._pushArg(a_command, stack[stack.length - 1], v);
806+ stack.push(v);
807+ state = S_NUMBER_READ;
808+ isBreak = false;
809+ break;
810+ }
811+ case S_NUMBER_READ:
812+ if ((cn >= cn0 && cn <= cn9) || (stack[stack.length - 1].a[0].length && cn == cnd && !stack[stack.length - 1].dot)) {
813+ if (cn == cnd) {
814+ stack[stack.length - 1].dot = true;
815+ }
816+ stack[stack.length - 1].a[0] += c;
817+ } else {
818+ delete stack[stack.length - 1].dot;
819+ stack[stack.length - 1].a[0] = parseFloat(stack[stack.length - 1].a[0]);
820+ this._popStack(a_command, stack);
821+ state = S_BACK;
822+ isBreak = false;
823+ }
824+ break;
825+ case S_KEY_START:
826+ {
827+ let v = {t: "v", a: [""]};
828+ v.a[0] += c;
829+ stack[stack.length - 1].a.push(v);
830+ stack.push(v);
831+ state = S_KEY_READ;
832+ }
833+ break;
834+ case S_KEY_READ:
835+ if (this._isVarChar(c)){
836+ stack[stack.length - 1].a[0] += c;
837+ } else {
838+ this._popStack(a_command, stack);
839+ state = S_WAIT;
840+ isBreak = false;
841+ }
842+ break;
843+ case S_ARRAY_START:
844+ {
845+ let v = {t: "a", e: false, a: []};
846+ let o = {t: "o", a: [v]};
847+ this._pushArg(a_command, stack[stack.length - 1], o);
848+ stack.push(o);
849+ stack.push(v);
850+ state = S_WAIT;
851+ }
852+ break;
853+ case S_ARRAY_NEXT:
854+ {
855+ if (c == "]") {
856+ for(let i = stack.length-1; i >= 0; --i ) {
857+ if (stack[i]) {
858+ if (stack[i].t == "a") {
859+ stack[i].e = true;
860+ break;
861+ }
862+ this._popStack(a_command, stack);
863+ }
864+ }
865+ this._popStack(a_command, stack);
866+ state = S_OBJECT_NEXT;
867+ } else if (c == ",") {
868+ let ns = {};
869+ ns[S_COMMAND_READ] = "a";
870+ nswitches.push(ns);
871+ for(let i = stack.length-1; i >= 0; --i ) {
872+ if (stack[i]) {
873+ if (stack[i].t == "a") {
874+ break;
875+ }
876+ this._popStack(a_command, stack);
877+ }
878+ }
879+ state = S_WAIT;
880+ }
881+ }
882+ break;
883+ case S_DOBJECT_START:
884+ {
885+ let v = {t: "d", e: false, a: [{t:"di", a:[]}]};
886+ this._pushArg(a_command, stack[stack.length - 1], v);
887+ stack.push(v);
888+ stack.push(v.a[0]);
889+ let s = {};
890+ s[S_NUMBER_START] = 1;
891+ s[S_STRING] = 1;
892+ s[S_KEY_START] = 1;
893+ s[S_DOBJECT_NEXT] = Infinity;
894+ switches.push(s);
895+ state = S_WAIT;
896+ isBreak = false;
897+ }
898+ break;
899+ case S_DOBJECT_NEXT:
900+ {
901+ let item;
902+ for(let i = stack.length-1; i >= 0; --i ) {
903+ if (stack[i].t == "d") {
904+ item = stack[i];
905+ break;
906+ }
907+ }
908+ if (c == "}") {
909+ if (item.a[item.a.length - 1].a.length == 0){
910+ item.a[item.a.length - 1].r = true;
911+ item.a.pop();
912+ }
913+ for(let i = stack.length-1; i >= 0; --i ) {
914+ if (stack[i]) {
915+ if (stack[i].t == "d") {
916+ break;
917+ }
918+ this._popStack(a_command, stack);
919+ }
920+ }
921+ this._popStack(a_command, stack);
922+ if (!item){
923+ throw new fcf.Exception("INVALID_COMMAND_TOKENIZE_INTERPETER", {command: a_command});
924+ }
925+ item.e = true;
926+ state = S_WAIT;
927+ switches.pop();
928+ } else if (c == ",") {
929+ let v = {t:"di", a:[]};
930+ for(let i = stack.length-1; i >= 0; --i ) {
931+ if (stack[i]) {
932+ if (stack[i].t == "d") {
933+ break;
934+ }
935+ this._popStack(a_command, stack);
936+ }
937+ }
938+ stack.push(v);
939+ this._pushArg(a_command, item, v);
940+ let s = {};
941+ s[S_NUMBER_START] = 1;
942+ s[S_STRING] = 1;
943+ s[S_KEY_START] = 1;
944+ s[S_DOBJECT_NEXT] = Infinity;
945+ switches.push(s);
946+ state = S_WAIT;
947+ isBreak = true;
948+ } else if (c == ":") {
949+ if (item.a[item.a.length - 1].a.length != 1){
950+ throw new fcf.Exception("INVALID_COMMAND_TOKENIZE_INTERPETER", {command: a_command});
951+ }
952+ state = S_WAIT;
953+ switches.pop();
954+ }
955+ }
956+ break;
957+ case S_OBJECT_START:
958+ {
959+ let v = {t: "o", a: []};
960+ this._pushArg(a_command, stack[stack.length - 1], v);
961+ stack.push(v);
962+ state = S_OBJECT_SIMPLE_READ;
963+ isBreak = false;
964+ break;
965+ }
966+ break;
967+ case S_OBJECT_SIMPLE_READ:
968+ {
969+ let item = stack[stack.length - 1];
970+ let name = "";
971+ for(; i < a_command.length && this._isVarChar(a_command[i]); ++i) {
972+ name += a_command[i];
973+ }
974+ this._pushArg(a_command, item, { t: "i", a: [name] });
975+ state = S_OBJECT_NEXT;
976+ isBreak = false;
977+ break;
978+ }
979+ break;
980+ case S_OBJECT_NEXT:
981+ {
982+ let item = stack[stack.length - 1];
983+ for(; i < a_command.length && a_command.charCodeAt(i) <= 32 ; ++i);
984+ if (item.a.length == 1 && item.a[0].t == "i" && item.a[0].a[0] in KEYWORDS) {
985+ let keyword = item.a[0].a[0];
986+ if (keyword == "typeof"){
987+ item.t = "t";
988+ item.u = true;
989+ item.a = [];
990+ } else if (keyword == "new") {
991+ item.t = "n";
992+ item.u = true;
993+ item.a = [];
994+ } else {
995+ item.t = "v";
996+ item.a= [(
997+ keyword === "NaN" ? NaN :
998+ keyword === "Infinity" ? Infinity :
999+ keyword === "undefined" ? undefined :
1000+ keyword === "false" ? false :
1001+ true
1002+ )];
1003+ this._popStack(a_command, stack);
1004+ }
1005+ state = S_WAIT;
1006+ isBreak = false;
1007+ } else if (a_command[i] == ".") {
1008+ ++i;
1009+ for(; i < a_command.length && a_command.charCodeAt(i) <= 32 ; ++i);
1010+ if (!this._isVarChar(a_command[i])) {
1011+ throw new fcf.Exception("INVALID_COMMAND_TOKENIZE_INTERPETER", {command: a_command});
1012+ }
1013+ state = S_OBJECT_SIMPLE_READ;
1014+ isBreak = false;
1015+ } else if (a_command[i] == "[") {
1016+ let arg = {t: "i", b: true, e: false, a: []};
1017+ this._pushArg(a_command, item, arg);
1018+ stack.push(arg);
1019+ state = S_WAIT;
1020+ } else if (a_command[i] == "(") {
1021+ let arg = {t: "c", b: true, e: false, a: []};
1022+ this._pushArg(a_command, item, arg);
1023+ stack.push(arg);
1024+ state = S_WAIT;
1025+ } else {
1026+ this._popStack(a_command, stack);
1027+ state = S_WAIT;
1028+ isBreak = false;
1029+ }
1030+ }
1031+ break;
1032+ case S_OBJECT_RCLOSE:
1033+ {
1034+ while (stack[stack.length - 1].t != "i") {
1035+ this._popStack(a_command, stack);
1036+ }
1037+ this._popStack(a_command, stack);
1038+ state = S_OBJECT_NEXT;
1039+ }
1040+ break;
1041+ case S_OBJECT_CCLOSE:
1042+ {
1043+ let item = stack[stack.length - 1];
1044+ item.e = true;
1045+ while (item.t != "c") {
1046+ this._popStack(a_command, stack);
1047+ item = stack[stack.length - 1];
1048+ }
1049+ this._popStack(a_command, stack);
1050+ state = S_OBJECT_NEXT;
1051+ }
1052+ break;
1053+ case S_ESTRING:
1054+ case S_STRING:
1055+ {
1056+ let v = {t: "v", a: [""]};
1057+ let rootv = v;
1058+ let c = a_command[i];
1059+ let eq = c;
1060+ let sc = 0;
1061+ ++i;
1062+ c = a_command[i];
1063+ const SPEC_CHARS = {
1064+ "n": "\n",
1065+ "b": "\b",
1066+ "r": "\r",
1067+ "n": "\n",
1068+ "\"": "\"",
1069+ "'": "'",
1070+ "f": "\f",
1071+ "t": "\t",
1072+ };
1073+ function specChar(i) {
1074+ let c = a_command[i];
1075+ if (c in SPEC_CHARS) {
1076+ v.a[0] += SPEC_CHARS[c];
1077+ } else if (c.charCodeAt(0) >= CHAR0 && c.charCodeAt(0) <= "7".charCodeAt(0)) {
1078+ let ss = "";
1079+ let sn = -1;
1080+ for(let j = 0; j < 4 && (i+j) < a_command.length; ++j) {
1081+ let sss = ss;
1082+ let c = a_command[i+j];
1083+ let cn = c.charCodeAt(0);
1084+ if (cn < CHAR0 || cn > "7".charCodeAt(0)) {
1085+ break;
1086+ }
1087+ sss += c;
1088+ let ssn = parseInt(sss, 8);
1089+ if (ssn < 0 || ssn > 255) {
1090+ break;
1091+ }
1092+ ss = sss;
1093+ sn = ssn;
1094+ }
1095+ if (sn != -1){
1096+ v.a[0] += String.fromCharCode(sn)
1097+ }
1098+ if (ss.length) {
1099+ i += ss.length - 1;
1100+ }
1101+ } else if (c == "u" || c == "x") {
1102+ let l = 0;
1103+ let maxLen = c == "x" ? 2 : 4;
1104+ for(let c = a_command.charCodeAt((i+1) + l);
1105+ l <= maxLen &&
1106+ ((i+1) + l) < a_command.length &&
1107+ (
1108+ (c >= CHAR0 && c <= CHAR9) ||
1109+ (c >= CHARa && c <= 'f'.charCodeAt(0)) ||
1110+ (c >= CHARA && c <= 'F'.charCodeAt(0))
1111+ )
1112+ ;
1113+ ++l);
1114+ if (l) { --l };
1115+ let n = parseInt(a_command.substr(i+1, l), 16);
1116+ if (!isNaN(n)) {
1117+ v.a[0] += String.fromCharCode(n)
1118+ }
1119+ i += l;
1120+ } else {
1121+ v.a[0] += c;
1122+ }
1123+ return i;
1124+ }
1125+ while (i < a_command.length && !(c == eq && sc % 2 == 0)) {
1126+ if (c == "\\") {
1127+ if (sc % 2 == 1) {
1128+ v.a[0] += c;
1129+ }
1130+ ++sc;
1131+ } else if (sc % 2 == 1) {
1132+ i = specChar(i);
1133+ sc = 0;
1134+ } else if (c == "$" && a_command[i+1] == "{" && (sc % 2 == 0) && state == S_ESTRING) {
1135+ let isc = 0;
1136+ v.t = "+";
1137+ let container = {
1138+ t: "+",
1139+ a: [
1140+ {
1141+ t: "cmd",
1142+ a: [""],
1143+ },
1144+ {
1145+ t: "v",
1146+ a: [""],
1147+ },
1148+ ],
1149+ };
1150+ v.a[0] = {
1151+ t: "v",
1152+ a: [ v.a[0] ]
1153+ };
1154+ v.a[1] = container;
1155+ v = container.a[0];
1156+ for(i += 2; i < a_command.length; ++i) {
1157+ c = a_command[i];
1158+ if (c == "\\") {
1159+ if (isc % 2 == 1) {
1160+ v.a[0] += c;
1161+ }
1162+ ++isc;
1163+ } else if (c == "}" && (isc % 2) == 0) {
1164+ break;
1165+ } else if ((isc % 2) == 1) {
1166+ i = specChar(i);
1167+ isc = 0;
1168+ } else {
1169+ v.a[0] += c;
1170+ }
1171+ }
1172+ v = container.a[1];
1173+ sc = 0;
1174+ } else {
1175+ sc = 0;
1176+ v.a[0] += c;
1177+ }
1178+ ++i;
1179+ c = a_command[i];
1180+ }
1181+ if (i == a_command.length) {
1182+ throw new fcf.Exception("INVALID_COMMAND_TOKENIZE_INTERPETER", {command: a_command});
1183+ }
1184+ let o = {t: "o", a: [rootv]};
1185+ this._pushArg(a_command, stack[stack.length - 1], o);
1186+ stack.push(o);
1187+ state = S_OBJECT_NEXT;
1188+ }
1189+ break;
1190+ }
1191+ } while(!isBreak);
1192+ }
1193+
1194+ if (!stack.length) {
1195+ throw new fcf.Exception("INVALID_COMMAND_TOKENIZE_INTERPETER", {command: a_command});
1196+ }
1197+ for(let i = 1; i < stack.length; ++i) {
1198+ if (stack[i].a.length < ARGS[stack[i].t][0] || stack[i].e === false) {
1199+ throw new fcf.Exception("INVALID_COMMAND_TOKENIZE_INTERPETER", {command: a_command});
1200+ }
1201+ }
1202+ return stack[0];
1203+ }
1204+ })();
1205+})();
diff -r f396c07135fc -r f3713d135d07 NDetails/load.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/NDetails/load.js Sun Apr 30 22:00:14 2023 +0300
@@ -0,0 +1,73 @@
1+const libHttp = require('node:http');
2+const libHttps = require('node:https');
3+
4+module.exports = (a_url, a_options)=>{
5+ a_options = a_options || {};
6+ return fcf.actions()
7+ .then((a_res, a_act)=>{
8+ let ri = new fcf.RouteInfo(a_url, "server");
9+ let foundContentType = false;
10+ if (typeof a_options.header === "object") {
11+ for(let k in a_options.header) {
12+ if (k.toLowerCase() == "content-type"){
13+ foundContentType = true;
14+ break;
15+ }
16+ }
17+ }
18+ let header = {};
19+ let body = a_options.body;
20+ if (body !== undefined && body !== null) {
21+ if (typeof body !== "string") {
22+ header["Content-Type"] = "application/json";
23+ body = JSON.stringify(body);
24+ } else {
25+ header["Content-Type"] = "text/plain";
26+ }
27+ }
28+ header["Content-Length"] = typeof body === "string" ? Buffer.byteLength(body) : 0;
29+ let options = {
30+ hostname: ri.server,
31+ port: ri.port ? ri.port : (ri.protocol == "https" ? 443 : 80),
32+ path: ri.uri + (ri.urlArgsStr ? "?" + ri.urlArgsStr : ""),
33+ method: a_options.method ? a_options.method.toUpperCase() : "GET",
34+ headers: fcf.append(
35+ {},
36+ a_options.header,
37+ header
38+ )
39+ };
40+ let libQuery = ri.protocol == "https" ? libHttps : libHttp;
41+ let request = libQuery.request(options, (a_res) => {
42+ let data = "";
43+ a_res.setEncoding("utf8");
44+ a_res.on("data", (a_part)=>{
45+ data += a_part;
46+ });
47+ a_res.on("end", ()=>{
48+ if (a_res.statusCode == 200) {
49+ a_act.complete(data);
50+ } else {
51+ let error = data;
52+ try {
53+ error = JSON.parse(error);
54+ } catch(e) {
55+ }
56+ if (typeof error !== "object" || !error._templateMessage) {
57+ error = new fcf.Exception("HTTP_REQUEST_ERROR", {code: a_res.statusCode});
58+ } else {
59+ error = new fcf.Exception(error);
60+ }
61+ a_act.error(error);
62+ }
63+ });
64+ });
65+ request.on("error", (a_error)=>{
66+ a_act.error(a_error);
67+ });
68+ if (typeof body == "string") {
69+ request.write(body);
70+ }
71+ request.end();
72+ })
73+};
diff -r f396c07135fc -r f3713d135d07 NDetails/logger.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/NDetails/logger.js Sun Apr 30 22:00:14 2023 +0300
@@ -0,0 +1,161 @@
1+let libFS = require("fs");
2+let libPath = require("path");
3+let libUtil = require("util");
4+let _directories = {};
5+let _garbageСollectorInfo = {};
6+
7+async function garbageСollector(a_preffix) {
8+ let gci;
9+ if (a_preffix){
10+ gci = {};
11+ gci[a_preffix] = _garbageСollectorInfo[a_preffix];
12+ } else {
13+ gci = _garbageСollectorInfo;
14+ }
15+ for(let prefix in gci) {
16+ try {
17+ let realPrefix = fcf.getPath(prefix);
18+ let directory = fcf.getPath(fcf.getDirectory(prefix));
19+ let files = await libUtil.promisify(libFS.readdir)(directory);
20+ for(let file of files) {
21+ let path = libPath.join(directory, file);
22+ if (path.indexOf(path) !== 0)
23+ continue;
24+ let suffix = path.substr(realPrefix.length);
25+ if (suffix.search(/\d\d\d\d-\d\d-\d\d.log$/) != 0){
26+ continue;
27+ }
28+ let date = fcf.parseDate(suffix, "Y-m-d.log");
29+ let now = new Date();
30+ if (Math.floor((now.getTime() - date.getTime()) / (24*60*60*1000)) <= gci[prefix]) {
31+ continue;
32+ }
33+ try {
34+ await libUtil.promisify(libFS.unlink)(path);
35+ } catch(e){
36+ fcf.log.err("FCF", `Failed to delete log file ${path}: ${e.message}`);
37+ }
38+ }
39+ } catch(e) {
40+ }
41+ }
42+}
43+
44+function startTimer() {
45+ let tsEnd = fcf.parseDate(fcf.formatDate(new Date(), "Y-m-d"), "Y-m-d").getTime() + 24*60*60*1000 + 5*60*1000;
46+ setTimeout(()=>{
47+ garbageСollector();
48+ startTimer()
49+ }, tsEnd - Date.now());
50+}
51+
52+setTimeout(()=>{
53+ startTimer();
54+}, 1);
55+
56+
57+function _cutFile(a_file, a_size) {
58+ if (!a_size)
59+ return;
60+ a_size *= 1000000;
61+ let source;
62+ let destination;
63+ let tmpFilePath;
64+ return fcf.actions()
65+ .then(async ()=>{
66+ let stat;
67+ try {
68+ stat = await libUtil.promisify(libFS.stat)(a_file);
69+ } catch(e){
70+ return;
71+ }
72+ if (stat.size < a_size)
73+ return;
74+ let partSize = 100000;
75+ let offset = stat.size - (a_size - 500000);
76+ let woffset = 0;
77+ tmpFilePath = a_file + fcf.id();
78+ source = await libUtil.promisify(libFS.open)(a_file, "r");
79+ destination = await libUtil.promisify(libFS.open)(tmpFilePath, "w");
80+ let buffer = Buffer.alloc(partSize);
81+ while(offset < stat.size) {
82+ buffer.offset = 0;
83+ let rc = await libUtil.promisify(libFS.read)(source, {buffer: buffer, offset: 0, length: (stat.size - offset) > partSize ? partSize : stat.size - offset, position: offset});
84+ rc = typeof rc == "object" ? rc.bytesRead : rc;
85+ if (!rc)
86+ break;
87+ buffer.offset = 0;
88+ await libUtil.promisify(libFS.write)(destination, buffer, 0, rc, woffset);
89+ woffset += rc;
90+ offset += rc;
91+ }
92+ stat = libFS.statSync(a_file);
93+ while (offset < stat.size){
94+ buffer.offset = 0;
95+ let rc = libFS.readSync(source, buffer, 0, (stat.size - offset) > partSize ? partSize : stat.size - offset, offset);
96+ rc = typeof rc == "object" ? rc.bytesRead : rc;
97+ if (!rc)
98+ break;
99+ buffer.offset = 0;
100+ libFS.writeSync(destination, buffer, 0, rc, woffset);
101+ woffset += rc;
102+ offset += rc;
103+ }
104+ libFS.renameSync(tmpFilePath, a_file);
105+ })
106+ .finally(async ()=>{
107+ if (source){
108+ try {
109+ await libUtil.promisify(libFS.close)(source);
110+ } catch(e){
111+ }
112+ }
113+ if (destination){
114+ try {
115+ await libUtil.promisify(libFS.close)(destination);
116+ } catch(e){
117+ }
118+ }
119+ if (tmpFilePath) {
120+ try {
121+ await libUtil.promisify(libFS.unlink)(tmpFilePath);
122+ } catch(e){
123+ }
124+ }
125+ })
126+ .catch((e)=>{
127+ });
128+}
129+
130+module.exports = {
131+ fileHandler: (a_info) => {
132+ let logFile = a_info.logger.getConfiguration().logFile;
133+ if (logFile) {
134+ if (_garbageСollectorInfo[logFile] !== a_info.logger.getConfiguration().logLifeTime) {
135+ _garbageСollectorInfo[logFile] = a_info.logger.getConfiguration().logLifeTime;
136+ garbageСollector(logFile);
137+ }
138+ let file = fcf.getPath(logFile + fcf.formatDate(new Date(), "Y-m-d") + ".log");
139+ let directory = fcf.getPath(fcf.getDirectory(file));
140+ if (!_directories[directory]) {
141+ _directories[directory] = 1;
142+ let exist = libFS.existsSync(directory);
143+ if (exist)
144+ return;
145+ libFS.mkdirSync(directory, { recursive: true });
146+ }
147+
148+ let firstCheckFile = !("_outputSumSize" in a_info.logger);
149+ if (!"_outputSumSize" in a_info.logger) {
150+ a_info.logger._outputSumSize = 0;
151+ }
152+ a_info.logger._outputSumSize += Buffer.byteLength(a_info.output, 'utf8') + 2;
153+
154+ if (firstCheckFile || a_info.logger._outputSumSize > 100000) {
155+ a_info.logger._outputSumSize = 0;
156+ _cutFile(file, !("logMaxFileSize" in a_info.logger.getConfiguration()) ? 1 : parseInt(a_info.logger.getConfiguration().logMaxFileSize));
157+ }
158+ return libFS.appendFileSync(file, a_info.output + "\r\n", "utf8");
159+ }
160+ }
161+}
diff -r f396c07135fc -r f3713d135d07 NDetails/resolver.js
--- a/NDetails/resolver.js Thu Dec 08 08:45:01 2022 +0300
+++ b/NDetails/resolver.js Sun Apr 30 22:00:14 2023 +0300
@@ -13,24 +13,32 @@
1313 for(let dir of directories){
1414 if (typeof dir !== "string")
1515 continue;
16- if (paths.indexOf(dir) == -1)
16+ dir = fcf.getPath(dir);
17+ if (paths.indexOf(dir) == -1){
1718 paths.unshift(dir);
18- needUpdate = true;
19+ needUpdate = true;
20+ }
1921 }
2022 if (needUpdate) {
2123 process.env.NODE_PATH = paths.join(":");
24+ libModule.Module._initPaths();
2225 }
23- libModule.Module._initPaths();
2426 _directories = {};
2527 }
2628
2729 resolveModule(a_module){
2830 a_module = a_module.split("\\")[0].split("/")[0];
2931 if (!(a_module in _directories)) {
30- try {
31- _directories[a_module] = libPath.dirname(require.resolve(`${a_module}/package.json`));
32- } catch(e){
33- return;
32+ if (a_module == "..") {
33+ throw new fcf.Exception("GET_PATH_ERROR_INVALID_MODULE_NAME", { path: a_module});
34+ } else if (a_module == ".") {
35+ _directories[a_module] = process.cwd();
36+ } else {
37+ try {
38+ _directories[a_module] = libPath.dirname(require.resolve(`${a_module}/package.json`));
39+ } catch(e){
40+ throw new fcf.Exception("GET_PATH_ERROR_INVALID_MODULE_NAME", { path: a_module});
41+ }
3442 }
3543 }
3644 return _directories[a_module];
diff -r f396c07135fc -r f3713d135d07 NDetails/state.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/NDetails/state.js Sun Apr 30 22:00:14 2023 +0300
@@ -0,0 +1,42 @@
1+const libAsyncHooks = require("async_hooks");
2+const _stateStorage = new Map();
3+const _asyncHooks = libAsyncHooks.createHook({
4+ init: (a_id, a_type, a_triggerId)=>{
5+ let state = _stateStorage.get(a_triggerId);
6+ let stateInfo;
7+ if (state){
8+ _stateStorage.set(a_id, [a_triggerId, state[1]])
9+ }
10+ },
11+ before: function(a_id){
12+ let state = _stateStorage.get(a_id);
13+ if (state){
14+ let pstate = _stateStorage.get(state[0]);
15+ if (pstate) {
16+ state[1] = pstate[1];
17+ }
18+ }
19+ },
20+ destroy: (a_id)=>{
21+ if (_stateStorage.has(a_id))
22+ _stateStorage.delete(a_id);
23+ }
24+}).enable();
25+module.exports = {
26+ getState: function() {
27+ let state = _stateStorage.get(libAsyncHooks.executionAsyncId());
28+ if (!state) {
29+ state = {state: {}};
30+ this.setState(state);
31+ }
32+ return state[1];
33+ },
34+ setState: function(a_state) {
35+ let pid = -1;
36+ let state = _stateStorage.get(libAsyncHooks.executionAsyncId());
37+ if (state) {
38+ pid = state[0];
39+ }
40+ _stateStorage.set(libAsyncHooks.executionAsyncId(), [pid, a_state]);
41+ }
42+};
diff -r f396c07135fc -r f3713d135d07 NProcess/NProcess.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/NProcess/NProcess.js Sun Apr 30 22:00:14 2023 +0300
@@ -0,0 +1,96 @@
1+fcf.module({
2+ name: "fcf-framework-core:NProcess/NProcess.js" ,
3+ module:() => {
4+ let Namesapce = fcf.prepare(fcf, "NProcess");
5+
6+ function indexOfSpace(a_str, a_startPos) {
7+ if (a_startPos === -1)
8+ return -1;
9+ if (!a_startPos || isNaN(a_startPos))
10+ a_startPos = 0;
11+ for(var i = a_startPos; i < a_str.length; ++i) {
12+ var code = a_str.charCodeAt(i);
13+ if (code >= 0 && code <= 32)
14+ return i;
15+ }
16+ return -1;
17+ }
18+
19+
20+ Namesapce.commandToString = (a_command, a_arguments) => {
21+ a_command = Array.isArray(a_command) ? a_command :
22+ !fcf.empty(a_command) ? [a_command] :
23+ undefined;
24+ a_arguments = Array.isArray(a_arguments) ? a_arguments :
25+ !fcf.empty(a_arguments) ? [a_arguments] :
26+ undefined;
27+ return a_command.concat(a_arguments)
28+ .map((a_val)=>{
29+ a_val = fcf.str(a_val);
30+ return indexOfSpace(a_val) != -1 || a_val.indexOf("\"") != -1 || a_val.indexOf("'") != -1
31+ ? `"${fcf.escapeQuotes(a_val)}"`
32+ : a_val;
33+ })
34+ .join(" ");
35+ }
36+
37+ Namesapce.commandFromString = (a_command) => {
38+ let command = "";
39+ let args = [];
40+ let sc = 0;
41+ let isq = false;
42+ let isdq = false;
43+ let arg = "";
44+ let isargs = false;
45+
46+ for (let i = 0; i < a_command.length; ++i) {
47+ let c = a_command[i];
48+ let cn = a_command.charCodeAt(i);
49+
50+ if (sc % 2 == 0 && c == "'") {
51+ if (!isq) {
52+ isq = true;
53+ } else {
54+ isq = false;
55+ }
56+ } else if (sc % 2 == 0 && c == "\"") {
57+ if (!isdq) {
58+ isdq = true;
59+ } else {
60+ isdq = false;
61+ }
62+ } else if (cn >= 0 && cn <= 32 && arg.length) {
63+ if (isargs) {
64+ args.push(arg);
65+ } else {
66+ command = arg;
67+ }
68+
69+ arg = "";
70+ isargs = true;
71+ } else if (c == "\\") {
72+ ++sc;
73+ if (sc % 2 == 0) arg += c;
74+ } else {
75+ arg += c;
76+ sc = 0;
77+ }
78+ }
79+
80+ if (arg.length) {
81+ if (!command) {
82+ command = arg;
83+ } else {
84+ args.push(arg);
85+ }
86+ }
87+
88+ return {
89+ command: command,
90+ args: args
91+ };
92+ };
93+
94+ return Namesapce;
95+ }
96+});
diff -r f396c07135fc -r f3713d135d07 fcf.js
--- a/fcf.js Thu Dec 08 08:45:01 2022 +0300
+++ b/fcf.js Sun Apr 30 22:00:14 2023 +0300
@@ -1,11 +1,8 @@
1-//NEED REMOVE
2-function _fcfGlobals() { return this; }
3-
41 (function() {
52 var fcf = typeof global !== 'undefined' && global.fcf ? global.fcf :
63 typeof window !== 'undefined' && window.fcf ? window.fcf :
74 {};
8- fcf.NDetails = fcf.NDetails || {};
5+ fcf.NDetails = fcf.NDetails || {};
96 fcf.namespaces = fcf.namespaces || {};
107
118 if (typeof module !== 'undefined')
@@ -31,11 +28,16 @@
3128
3229
3330
34- let libResolver;
35- let libPath;
36- if (_isServer){
37- libResolver = require("fcf-framework-core/NDetails/resolver.js");
38- libPath = require("path");
31+ let libResolver, libPath, libFS, libUtil, libState, libLogger;
32+ if (_isServer) {
33+ libResolver = require("./NDetails/resolver.js");
34+ libPath = require("path");
35+ libFS = require("fs");
36+ libUtil = require("util")
37+ libInlineInterpreter = require("./NDetails/inlineExecution.js");
38+ libLoad = require("./NDetails/load.js");
39+ libState = require("./NDetails/state.js");
40+ libLogger = require("./NDetails/logger.js");
3941 }
4042
4143
@@ -51,57 +53,58 @@
5153 /// @details NaN, undefined and null values are represented as an empty string
5254 /// @param mixed a_data - source data
5355 /// @result string
54- fcf.str = (a_data) => {
55- return a_data === undefined ? "" :
56- a_data === null ? "" :
57- a_data === NaN ? "" :
58- typeof a_data == "object" ? (
59- a_data instanceof fcf.Exception ? a_data.toString() :
60- a_data instanceof Error ? a_data.toString() :
61- a_data.sqlMessage && a_data.sqlState && a_data.code ? a_data.sqlMessage :
62- JSON.stringify(a_data, undefined, 2)
63- ) :
64- a_data !== "string" ? a_data.toString() :
65- a_data;
56+ fcf.str = (a_data, a_fullMode) => {
57+ return typeof a_data == "string" ? a_data :
58+ a_data === undefined ? "" :
59+ a_data === null ? "" :
60+ typeof a_data === "number" && isNaN(a_data) ? "" :
61+ typeof a_data == "object" ? (
62+ a_data instanceof fcf.Exception ? fcf.errorToString(a_data, a_fullMode) :
63+ a_data instanceof Error ? fcf.errorToString(a_data, a_fullMode) :
64+ a_data.sqlMessage && a_data.sqlState && a_data.code ? a_data.sqlMessage :
65+ JSON.stringify(a_data, undefined, 2)
66+ ) :
67+ a_data.toString();
6668 }
6769
6870
6971
70- /// @fn boolean fcf.isSpace(string a_char)
71- /// @brief Checks if a character is whitespace
72- /// @param string a_char - Character to test
73- /// @result boolean - Returns true if a_char is whitespace
74- fcf.isSpace = (a_char) => {
75- let cn = a_char.charCodeAt(0);
76- return (cn >= 0 && cn <= 32) || cn == 160;
77- }
78-
79-
80-
81- /// @fn boolean fcf.isSpaceCode(number a_code)
82- /// @brief Checks if a character code is whitespace
83- /// @param number a_code - Character code to test
84- /// @result boolean - Returns true if a_code is whitespace
85- fcf.isSpaceCode = (a_code) => {
86- return (a_code >= 0 && a_code <= 32) || a_code == 160;
87- }
88-
89-
90-
91- /// @fn string fcf.escapeQuotes(string a_str)
72+ /// @fn string fcf.escapeQuotes(string a_str, string|[string] a_quote = undefined)
9273 /// @brief Escapes single and double quotes with \
9374 /// @param string a_str - Source string
75+ /// @param string|[string] a_quote = undefined - If the parameter is specified and contains the value
76+ /// of the escaped character or an array of escaped characters,
77+ /// then only the specified character and the \ character are escaped.
9478 /// @result string - String with escaped characters
95- fcf.escapeQuotes = (a_str) => {
79+ fcf.escapeQuotes = (a_str, a_quote) => {
9680 let result = "";
97- a_str = fcf.str(a_str);
98- for (let i = 0; i < a_str.length; ++i) {
99- let c = a_str[i];
100- switch (c) {
101- case "\\": result += "\\\\"; break;
102- case "\"": result += "\\\""; break;
103- case "'": result += "\\'"; break;
104- default: result += c; break;
81+ if (Array.isArray(a_quote)) {
82+ for (let i = 0; i < a_str.length; ++i) {
83+ let c = a_str[i];
84+ if (c === "\\"){
85+ result += "\\\\";
86+ } else if (a_quote.indexOf(c) != -1){
87+ result += "\\";
88+ result += c;
89+ } else {
90+ result += c;
91+ }
92+ }
93+ } else {
94+ for (let i = 0; i < a_str.length; ++i) {
95+ let c = a_str[i];
96+ if (c === "\\"){
97+ result += "\\\\";
98+ } else if (a_quote && c === a_quote){
99+ result += "\\";
100+ result += a_quote;
101+ } else if (!a_quote && c === "\""){
102+ result += "\\\"";
103+ } else if (!a_quote && c === "'"){
104+ result += "\\'";
105+ } else {
106+ result += c;
107+ }
105108 }
106109 }
107110 return result;
@@ -148,7 +151,7 @@
148151 /// @brief Performs replacement of all searched substrings in a string
149152 /// @param string a_str - Source string
150153 /// @param string a_search - Search substring
151- /// @param string a_str - replacement
154+ /// @param string a_replacement - replacement
152155 /// @result string - New string
153156 fcf.replaceAll = (a_str, a_search, a_replacement) => {
154157 a_str = fcf.str(a_str);
@@ -160,7 +163,7 @@
160163
161164
162165 /// @fn string fcf.decodeHtml(string a_str)
163- /// @brief Performs decoding of an HTML string
166+ /// @brief Performs decoding of special characters in an HTML string
164167 /// @param string a_str - Source string
165168 /// @result string - Decoding result string
166169 fcf.decodeHtml = (a_str) => {
@@ -255,12 +258,7 @@
255258 /// @result string - Encoded string
256259 fcf.encodeHtml = (a_str) => {
257260 let result = "";
258- a_str = a_str === undefined || a_str === null ? "" : a_str;
259- if (typeof a_str === 'object'){
260- a_str = JSON.stringify(a_str);
261- } else if (typeof a_str !== 'string'){
262- a_str = fcf.str(a_str);
263- }
261+ a_str = fcf.str(a_str);
264262 for(let i = 0; i < a_str.length; ++i) {
265263 let c = a_str[i];
266264 switch(c){
@@ -282,45 +280,17 @@
282280 /// @param string a_str - Source string
283281 /// @result string - String with tags removed
284282 fcf.stripTags = (a_str) => {
285- if (typeof a_str != "string")
286- return a_str;
287- let reg = new RegExp("(<[^>]*>)", "g");
288- return a_str.replace(reg, "");
283+ return fcf.str(a_str).replace(_regStripTags, "");
289284 }
290-
291-
292-
293- /// @fn array fcf.splitSpace(string a_str)
294- /// @brief Splits a string into substrings into a whitespace-separated array
295- /// @param string a_str - Source string
296- /// @result array - Array of substrings
297- fcf.splitSpace = (a_str) => {
298- let result = [];
299- let empty = true;
300- let beg = 0;
301- for (let i = 0; i < a_str.length; ++i) {
302- let code = a_str.charCodeAt(i);
303- if (!empty && fcf.isSpaceCode(code)){
304- result.push(a_str.substr(beg, i - beg));
305- empty = true;
306- } else {
307- if (empty)
308- beg = i;
309- empty = false;
310- }
311- }
312- if (!empty)
313- result.push(a_str.substr(beg, a_str.length - beg));
314- return result;
315- }
316-
317-
318-
319- /// @fn string fcf.ltrim(string a_str, a_arr = [false])
285+ const _regStripTags = new RegExp("(<[^>]*>)", "g");
286+
287+
288+
289+ /// @fn string fcf.ltrim(string a_str, string|false|[string|false] a_arr = [false])
320290 /// @brief Removes the given characters from the beginning of a string
321291 /// @param string a_str - Source string
322- /// @param string a_arr = [false]- Array of characters for which deletion will be performed
323- /// If the array element is false, characters with code <= 32 || code == 160 are removed
292+ /// @param string|false|[string|false] a_arr = [false]- Array of characters for which deletion will be performed or single string delimiter
293+ /// If the array element is false, characters with code <= 32 are removed
324294 /// @result string - New string
325295 fcf.ltrim = (a_str, a_arr) => {
326296 a_str = fcf.str(a_str);
@@ -333,8 +303,8 @@
333303 /// @fn string fcf.ltrim(string a_str, a_arr = [false])
334304 /// @brief Removes the given characters from the end of a string
335305 /// @param string a_str - Source string
336- /// @param string a_arr = [false]- Array of characters for which deletion will be performed
337- /// If the array element is false, characters with code <= 32 || code == 160 are removed
306+ /// @param string|false|[string|false] a_arr = [false]- Array of characters for which deletion will be performed or single string delimiter
307+ /// If the array element is false, characters with code <= 32 are removed
338308 /// @result string - New string
339309 fcf.rtrim = (a_str, a_arr) => {
340310 a_str = fcf.str(a_str);
@@ -347,8 +317,8 @@
347317 /// @fn string fcf.ltrim(string a_str, a_arr = [false])
348318 /// @brief Removes the given characters from the beginning and end of a string
349319 /// @param string a_str - Source string
350- /// @param string a_arr = [false]- Массив символов для которых будет быполнятся удаление
351- /// If the array element is false, characters with code <= 32 || code == 160 are removed
320+ /// @param string|false|[string|false] a_arr = [false]- Array of characters for which deletion will be performed or single string delimiter
321+ /// If the array element is false, characters with code <= 32 are removed
352322 /// @result string - New string
353323 fcf.trim = (a_str, a_arr) => {
354324 a_str = fcf.str(a_str);
@@ -371,7 +341,7 @@
371341 for(let i = 0; i < a_arr.length; ++i){
372342 if (!a_arr[i]) {
373343 let cn = a_str.charCodeAt(pos);
374- if (fcf.isSpaceCode(cn)){
344+ if (cn >= 0 && cn <= 32){
375345 found = true;
376346 break;
377347 }
@@ -396,7 +366,7 @@
396366 for(let i = 0; i < a_arr.length; ++i){
397367 if (!a_arr[i]) {
398368 let cn = a_str.charCodeAt(pos);
399- if (fcf.isSpaceCode(cn)){
369+ if (cn >= 0 && cn <= 32){
400370 found = true;
401371 break;
402372 }
@@ -413,73 +383,58 @@
413383
414384
415385
416- /// @fn string fcf.padEnd(string a_str, number a_len, string a_fill = " ")
417- /// @brief Pads a string from the end to the given length
418- /// @param string a_str - Source string
419- /// @param number a_len - The length to which you want to pad the original string
420- /// @param string a_fill = " " - The string which will be filled with empty space
421- /// @result string - Result string
422- fcf.padEnd = (a_str, a_len, a_fill) => {
423- if (typeof a_fill !== "string" || a_fill.length == 0)
424- a_fill = " ";
425-
426- let result = a_str;
427- if (result.length >= a_len)
428- return result;
429-
430- let с = Math.floor((a_len - result.length) / a_fill.length);
431- for(let i = 0; i < с; ++i)
432- result += a_fill;
433-
434- let d = (a_len - result.length) % a_fill.length;
435- if (d)
436- result += a_fill.substr(0, d);
437-
438- return result;
439- }
440-
441-
442-
443- /// @fn string fcf.padEnd(string a_str, number a_len, string a_fill = " ")
444- /// @brief Pads a string from the beginning to the given length
386+ /// @fn string fcf.pad(string a_str, number a_len, string a_fill = " ", string a_align = "left")
387+ /// @brief Pads a string to a given length
445388 /// @param string a_str - Source string
446389 /// @param number a_len - The length to which you want to pad the original string
447390 /// @param string a_fill = " " - The string which will be filled with empty space
391+ /// @param string a_align = "left" - String alignment a_str
392+ /// - "l"|"left" - Alignment is done to the left
393+ /// - "r"|"right" - Alignment is done to the right
394+ /// - "c"|"center" - Alignment is done in the center
448395 /// @result string - Result string
449- fcf.padStart = (a_str, a_len, a_fill) => {
450- if (typeof a_fill !== "string" || a_fill.length == 0)
396+ fcf.pad = (a_str, a_len, a_fill, a_align) => {
397+ if (isNaN(a_len))
398+ return a_str;
399+ let fillLen = a_len - a_str.length;
400+ if (fillLen <= 0)
401+ return a_str;
402+ if (!a_fill)
451403 a_fill = " ";
452-
453- let result = a_str;
454- if (result.length >= a_len)
455- return result;
456-
457- let prefix = "";
458- let с = Math.floor((a_len - result.length) / a_fill.length);
459- for(let i = 0; i < с; ++i)
460- prefix += a_fill;
461-
462- let d = (a_len - (result.length + prefix.length)) % a_fill.length;
463- if (d)
464- prefix += a_fill.substr(0, d);
465-
466- return prefix + result;
404+ if (typeof a_align !== "string")
405+ a_align = "l";
406+ let leftLen = a_align[0] == "r" ? fillLen :
407+ a_align[0] == "c" ? Math.floor(fillLen / 2) :
408+ 0;
409+ let rightLen = a_align[0] == "r" ? 0 :
410+ a_align[0] == "c" ? Math.floor(fillLen / 2) + (fillLen % 2) :
411+ fillLen;
412+ let result = "";
413+ for (let i = 0; i < leftLen; ++i) {
414+ result += a_fill[i%a_fill.length];
415+ }
416+ result += a_str;
417+ for (let i = 0; i < rightLen; ++i) {
418+ result += a_fill[i%a_fill.length];
419+ }
420+ return result;
467421 }
468422
469423
470424
471- /// @fn string fcf.id(number a_size = 32, boolean a_unsafeFirstChar = false)
425+ /// @fn string fcf.id(number a_size = 32, boolean a_safeFirstChar = true)
472426 /// @brief Creates a string from random characters in hex format
473427 /// @param number a_size default = 32 - Generated string size
474- /// @param boolean a_unsafeFirstChar default = false - If true, then the first character takes
475- /// values from 0-f. If false, then the first character takes values from a-f.
428+ /// @param boolean a_safeFirstChar default = true - If false, then the first character takes
429+ /// values from 0-f. If true, then the first character takes values from a-f.
476430 /// @result string - String with random hex characters
477- fcf.id = (a_size, a_unsafeFirstChar) => {
431+ fcf.id = (a_size, a_safeFirstChar) => {
478432 a_size = a_size || 32;
433+ a_safeFirstChar = a_safeFirstChar === undefined ? true : a_safeFirstChar;
479434 let result = "";
480435 for(let i = 0; i < a_size; ++i) {
481- result += i || !a_unsafeFirstChar ? (Math.floor(Math.random()*16)).toString(16)
482- : "abcdef"[(Math.floor(Math.random()*6))];
436+ result += i || !a_safeFirstChar ? (Math.floor(Math.random()*16)).toString(16)
437+ : "abcdef"[(Math.floor(Math.random()*6))];
483438 }
484439 return result;
485440 }
@@ -490,70 +445,28 @@
490445 /// @brief Creates a UUID string (v4)
491446 /// @result string - UUID string
492447 fcf.uuid = () => {
493- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
494- let r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
495- return v.toString(16);
496- });
448+ let res = "";
449+ for(let i = 0; i < 36; ++i) {
450+ if (i == 8 || i == 13 || i == 18 || i == 23) {
451+ res += "-";
452+ } else if (i == 14) {
453+ res += "4";
454+ } else if (i == 19) {
455+ res += ((Math.random() * 16 | 0) & 0x3 | 0x8).toString(16);
456+ } else {
457+ res += (Math.random() * 16 | 0).toString(16);
458+ }
459+ }
460+ return res;
497461 }
498462
499463
500464
501- /// @fn string fcf.base64Encode(string a_input)
502- /// @brief Encodes a string in base64 format
503- /// @param string a_input - Source string
504- /// @result string - Result base64 string
505- fcf.base64Encode = function (a_input) {
506- function utf8Encode (a_string) {
507- a_string = a_string.replace(/\r\n/g,"\n");
508- let utftext = "";
509- for (let n = 0; n < a_string.length; n++) {
510- let c = a_string.charCodeAt(n);
511- if (c < 128) {
512- utftext += String.fromCharCode(c);
513- } else if ((c > 127) && (c < 2048)) {
514- utftext += String.fromCharCode((c >> 6) | 192);
515- utftext += String.fromCharCode((c & 63) | 128);
516- } else {
517- utftext += String.fromCharCode((c >> 12) | 224);
518- utftext += String.fromCharCode(((c >> 6) & 63) | 128);
519- utftext += String.fromCharCode((c & 63) | 128);
520- }
521- }
522- return utftext;
523- }
524-
525- let output = "";
526- let chr1, chr2, chr3, enc1, enc2, enc3, enc4;
527- let i = 0;
528- a_input = utf8Encode(a_input);
529- while (i < a_input.length) {
530- chr1 = a_input.charCodeAt(i++);
531- chr2 = a_input.charCodeAt(i++);
532- chr3 = a_input.charCodeAt(i++);
533- enc1 = chr1 >> 2;
534- enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
535- enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
536- enc4 = chr3 & 63;
537- if (isNaN(chr2)) {
538- enc3 = enc4 = 64;
539- } else if (isNaN(chr3)) {
540- enc4 = 64;
541- }
542- output = output +
543- _keyBase64.charAt(enc1) + _keyBase64.charAt(enc2) +
544- _keyBase64.charAt(enc3) + _keyBase64.charAt(enc4);
545- }
546-
547- return output;
548- }
549-
550-
551-
552- /// @fn string fcf.base64Encode(string a_input)
553- /// @brief Decodes a string from base64 format
554- /// @param string a_input - Source base64 string
465+ /// @fn string fcf.decodeBase64(string a_base64String)
466+ /// @brief Decodes a string from base64 format
467+ /// @param string a_base64String - Source base64 string
555468 /// @result string - Result string
556- fcf.base64Decode = function (a_input) {
469+ fcf.decodeBase64 = function (a_input) {
557470 function utf8Decode (utftext) {
558471 let string = "";
559472 let i = 0;
@@ -583,7 +496,7 @@
583496 let chr1, chr2, chr3;
584497 let enc1, enc2, enc3, enc4;
585498 let i = 0;
586- a_input = a_input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
499+ a_input = fcf.str(a_input).replace(/[^A-Za-z0-9\+\/\=]/g, "");
587500 while (i < a_input.length) {
588501 enc1 = _keyBase64.indexOf(a_input.charAt(i++));
589502 enc2 = _keyBase64.indexOf(a_input.charAt(i++));
@@ -604,6 +517,57 @@
604517 return output;
605518 }
606519
520+
521+
522+ /// @fn string fcf.encodeBase64(string a_input)
523+ /// @brief Encodes a string in base64 format
524+ /// @param string a_input - Source string
525+ /// @result string - Result base64 string
526+ fcf.encodeBase64 = function (a_input) {
527+ function utf8Encode (a_string) {
528+ a_string = a_string.replace(/\r\n/g,"\n");
529+ let utftext = "";
530+ for (let n = 0; n < a_string.length; n++) {
531+ let c = a_string.charCodeAt(n);
532+ if (c < 128) {
533+ utftext += String.fromCharCode(c);
534+ } else if ((c > 127) && (c < 2048)) {
535+ utftext += String.fromCharCode((c >> 6) | 192);
536+ utftext += String.fromCharCode((c & 63) | 128);
537+ } else {
538+ utftext += String.fromCharCode((c >> 12) | 224);
539+ utftext += String.fromCharCode(((c >> 6) & 63) | 128);
540+ utftext += String.fromCharCode((c & 63) | 128);
541+ }
542+ }
543+ return utftext;
544+ }
545+
546+ let output = "";
547+ let chr1, chr2, chr3, enc1, enc2, enc3, enc4;
548+ let i = 0;
549+ a_input = utf8Encode(fcf.str(a_input));
550+ while (i < a_input.length) {
551+ chr1 = a_input.charCodeAt(i++);
552+ chr2 = a_input.charCodeAt(i++);
553+ chr3 = a_input.charCodeAt(i++);
554+ enc1 = chr1 >> 2;
555+ enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
556+ enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
557+ enc4 = chr3 & 63;
558+ if (isNaN(chr2)) {
559+ enc3 = enc4 = 64;
560+ } else if (isNaN(chr3)) {
561+ enc4 = 64;
562+ }
563+ output = output +
564+ _keyBase64.charAt(enc1) + _keyBase64.charAt(enc2) +
565+ _keyBase64.charAt(enc3) + _keyBase64.charAt(enc4);
566+ }
567+
568+ return output;
569+ }
570+
607571 const _keyBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
608572
609573
@@ -625,28 +589,33 @@
625589
626590
627591 /// @fn boolean fcf.isIterable(mixed a_value)
628- /// @brief Checks if an argument is iterable
592+ /// @brief Checks if an argument is iterable (but not a string)
629593 /// @param mixed a_value Checked value
630594 /// @result boolean - Returns true if the argument iterable
631595 fcf.isIterable = (a_value) => {
632- if (a_value === null)
633- return false;
634- return typeof a_value[Symbol.iterator] === 'function';
596+ return typeof a_value === "object" && a_value !== null
597+ ? typeof a_value[Symbol.iterator] === 'function'
598+ : false;
635599 }
636600
637601
638602
639603 /// @fn boolean fcf.isNumbered(mixed a_value)
640- /// @brief Checks if an argument is numbered
604+ /// @brief Checks if an argument is numbered (but not a string)
641605 /// @param mixed a_value Checked value
642606 /// @result boolean - Returns true if the argument numbered
643607 fcf.isNumbered = (a_value) => {
644- if (typeof a_value === "string"){
608+ if (typeof a_value !== "object" || a_value === null)
645609 return false;
610+ if (typeof a_value[Symbol.iterator] !== 'function' || typeof a_value.length !== "number")
611+ return false;
612+ if (a_value.length > 0) {
613+ return 0 in a_value;
646614 } else {
647- if (a_value === null)
615+ for(let v of a_value) {
648616 return false;
649- return typeof a_value[Symbol.iterator] === 'function' && typeof a_value.length === "number";
617+ }
618+ return true;
650619 }
651620 }
652621
@@ -654,165 +623,157 @@
654623
655624 /// @var integer fcf.UNDEFINED = 0
656625 /// @brief Nature type of variable. Undefined value
657- fcf.UNDEFINED = 0;
626+ Object.defineProperty(fcf,
627+ "UNDEFINED",
628+ { value: 0, writable: false });
658629
659630 /// @var integer fcf.NULL = 1
660631 /// @brief Nature type of variable. Null value
661- fcf.NULL = 1;
662-
663- /// @var integer fcf.STRING = 2
664- /// @brief Nature type of variable. String value
665- fcf.STRING = 2;
632+ Object.defineProperty(fcf,
633+ "NULL",
634+ { value: 1, writable: false });
635+
636+ /// @var integer fcf.NAN = 2
637+ /// @brief Nature type of variable. NaN value
638+ Object.defineProperty(fcf,
639+ "NAN",
640+ { value: 2, writable: false });
666641
667642 /// @var integer fcf.BOOLEAN = 3
668643 /// @brief Nature type of variable. Boolean value
669- fcf.BOOLEAN = 3;
644+ Object.defineProperty(fcf,
645+ "BOOLEAN",
646+ { value: 3, writable: false });
670647
671648 /// @var integer fcf.NUMBER = 4
672649 /// @brief Nature type of variable. Number value
673- fcf.NUMBER = 4;
674-
675- /// @var integer fcf.OBJECT = 5
676- /// @brief Nature type of variable. Object value (Excluding null)
677- fcf.OBJECT = 5;
678-
679- /// @var integer fcf.ARRAY = 6
650+ Object.defineProperty(fcf,
651+ "NUMBER",
652+ { value: 4, writable: false });
653+
654+ /// @var integer fcf.STRING = 5
655+ /// @brief Nature type of variable. String value
656+ Object.defineProperty(fcf,
657+ "STRING",
658+ { value: 5, writable: false });
659+
660+ /// @var integer fcf.DATE = 6
661+ /// @brief Nature type of variable. Date value
662+ Object.defineProperty(fcf,
663+ "DATE",
664+ { value: 6, writable: false });
665+
666+ /// @var integer fcf.OBJECT = 7
667+ /// @brief Nature type of variable. Object value (Excluding null and date)
668+ Object.defineProperty(fcf,
669+ "OBJECT",
670+ { value: 7, writable: false });
671+
672+ /// @var integer fcf.ARRAY = 8
680673 /// @brief Nature type of variable. Array value
681- fcf.ARRAY = 6;
682-
683- /// @var integer fcf.ITERABLE = 7
674+ Object.defineProperty(fcf,
675+ "ARRAY",
676+ { value: 8, writable: false });
677+
678+ /// @var integer fcf.ITERABLE = 9
684679 /// @brief Nature type of variable. Iterable object
685- fcf.ITERABLE = 7;
686-
687- /// @var integer fcf.NUMBERED = 8
680+ Object.defineProperty(fcf,
681+ "ITERABLE",
682+ { value: 9, writable: false });
683+
684+ /// @var integer fcf.NUMBERED = 10
688685 /// @brief Nature type of variable. Numbered object (Excluding string)
689- fcf.NUMBERED = 8;
690-
691-
692-
693- /// @fn boolean fcf.isNature(a_value, string|integer|[string|integer] a_nature)
686+ Object.defineProperty(fcf,
687+ "NUMBERED",
688+ { value: 10, writable: false });
689+
690+
691+
692+ /// @fn boolean fcf.isNature(mixed a_value, string|fcf.UNDEFINED..fcf.NUMBERED|[string|fcf.UNDEFINED..fcf.NUMBERED] a_nature, boolean a_softMode = false)
694693 /// @brief Checks if a value matches the nature type
695694 /// @param mixed a_value - Checked value
696695 /// @param string|integer|[string|integer] a_nature - The nature type or an array of nature types.
697696 /// nature can take an integer value or a string value:
698697 /// - fcf.UNDEFINED=0 | "undefined" - Undefined value
699698 /// - fcf.NULL=1 | "null" - Null value
700- /// - fcf.STRING=2 | "string" - String value
699+ /// - fcf.NAN=2 | "nan" - NaN value
701700 /// - fcf.BOOLEAN=3 | "boolean" - Boolean value
702701 /// - fcf.NUMBER=4 | "number" - Number value
703- /// - fcf.OBJECT=5 | "object" - Object value
704- /// - fcf.ARRAY=6 | "array" - Array value
705- /// - fcf.ITERABLE=7 | "iterable" - Iterable object
706- /// - fcf.NUMBERED=8 | "numbered" - Numbered object (Excluding string)
702+ /// - fcf.STRING=5 | "string" - String value
703+ /// - fcf.DATE=6 | "date" - Date value
704+ /// - fcf.OBJECT=7 | "object" - Object value (Excluding null and date)
705+ /// - fcf.ARRAY=8 | "array" - Array value
706+ /// - fcf.ITERABLE=9 | "iterable" - Iterable object (Excluding string)
707+ /// - fcf.NUMBERED=10 | "numbered" - Numbered object (Excluding string)
708+ /// @param boolean a_softMode = false - If it is true when checking a string containing a number or a date
709+ /// for compliance with the fcf.NUMBER or fcf.DATE types, the function will return true
707710 /// @result boolean - Returns true if there is a match with type nature
708- fcf.isNature = (a_value, a_nature) => {
709- if (!Array.isArray(a_nature)) {
710- if (typeof a_nature === "string") {
711- switch(a_nature) {
712- case "iterable":
713- return fcf.isIterable(a_value);
714- case "numbered":
715- return fcf.isNumbered(a_value);
716- case "array":
717- return Array.isArray(a_value);
718- case "object":
719- return typeof a_value == "object" && a_value !== null;
720- case "null":
721- return a_value === null;
722- default:
723- return typeof a_value == a_nature;
724- }
725- } else {
726- switch(a_nature) {
727- case fcf.ITERABLE:
728- return fcf.isIterable(a_value);
729- case fcf.NUMBERED:
730- return fcf.isNumbered(a_value);
731- case fcf.ARRAY:
732- return Array.isArray(a_value);
733- case fcf.OBJECT:
734- return typeof a_value == "object" && a_value !== null;
735- case fcf.NULL:
736- return a_value === null;
737- case fcf.UNDEFINED:
738- return a_value === undefined;
739- case fcf.BOOLEAN:
740- return typeof a_value === "boolean";
741- case fcf.NUMBER:
742- return typeof a_value === "number";
743- case fcf.STRING:
744- return typeof a_value === "string";
745- }
746- }
747- } else {
748- for(let nature of a_nature) {
749- if (typeof nature === "string") {
750- switch(nature) {
751- case "iterable":
752- if (fcf.isIterable(a_value))
753- return true;
754- break;
755- case "numbered":
756- if (fcf.isNumbered(a_value))
757- return true;
758- break;
759- case "array":
760- if (Array.isArray(a_value))
761- return true;
762- break;
763- case "object":
764- if (typeof a_value == "object" && a_value !== null)
765- return true;
766- break;
767- case "null":
768- if (a_value === null)
769- return true;
770- break;
771- default:
772- if (typeof a_value == nature)
773- return true;
774- break;
775- }
776- } else {
777- switch(nature) {
778- case fcf.ITERABLE:
779- if (fcf.isIterable(a_value))
780- return true;
781- break;
782- case fcf.NUMBERED:
783- if (fcf.isNumbered(a_value))
784- return true;
785- break;
786- case fcf.ARRAY:
787- if (Array.isArray(a_value))
788- return true;
789- break;
790- case fcf.OBJECT:
791- if (typeof a_value == "object" && a_value !== null)
792- return true;
793- break;
794- case fcf.NULL:
795- if (a_value === null)
796- return true;
797- break;
798- case fcf.UNDEFINED:
799- if (a_value === undefined)
800- return true;
801- break;
802- case fcf.BOOLEAN:
803- if (typeof a_value === "boolean")
804- return true;
805- break;
806- case fcf.NUMBER:
807- if (typeof a_value === "number")
808- return true;
809- break;
810- case fcf.STRING:
811- if (typeof a_value === "string")
812- return true;
813- break;
814- }
815- }
711+ fcf.isNature = (a_value, a_nature, a_softMode) => {
712+ let l = Array.isArray(a_nature) ? a_nature.length : 1;
713+ for(let i = 0; i < l; ++i) {
714+ let nature = Array.isArray(a_nature) ? a_nature[i] : a_nature;
715+ if (typeof nature == "string") {
716+ nature = nature == "numbered" ? fcf.NUMBERED :
717+ nature == "iterable" ? fcf.ITERABLE :
718+ nature == "array" ? fcf.ARRAY :
719+ nature == "object" ? fcf.OBJECT :
720+ nature == "date" ? fcf.DATE :
721+ nature == "string" ? fcf.STRING :
722+ nature == "number" ? fcf.NUMBER :
723+ nature == "boolean" ? fcf.BOOLEAN :
724+ nature == "nan" ? fcf.NAN :
725+ nature == "null" ? fcf.NULL :
726+ fcf.UNDEFINED;
727+ }
728+ switch(nature) {
729+ case fcf.NUMBERED:
730+ if (fcf.isNumbered(a_value))
731+ return true;
732+ break;
733+ case fcf.ITERABLE:
734+ if (fcf.isIterable(a_value))
735+ return true;
736+ break;
737+ case fcf.ARRAY:
738+ if (Array.isArray(a_value))
739+ return true;
740+ break;
741+ case fcf.OBJECT:
742+ if (typeof a_value == "object" && a_value !== null && !(a_value instanceof Date))
743+ return true;
744+ break;
745+ case fcf.DATE:
746+ if (a_value instanceof Date)
747+ return true;
748+ if (a_softMode && typeof a_value == "string" && !isNaN(new Date(a_value).getTime()))
749+ return true;
750+ break;
751+ case fcf.STRING:
752+ if (typeof a_value === "string")
753+ return true;
754+ break;
755+ case fcf.NUMBER:
756+ if (typeof a_value === "number" && !isNaN(a_value))
757+ return true;
758+ if (a_softMode && !isNaN(a_value) && !isNaN(parseFloat(a_value)) )
759+ return true;
760+ break;
761+ case fcf.BOOLEAN:
762+ if (typeof a_value === "boolean")
763+ return true;
764+ break;
765+ case fcf.NAN:
766+ if (typeof a_value === "number" && isNaN(a_value))
767+ return true;
768+ break;
769+ case fcf.NULL:
770+ if (a_value === null)
771+ return true;
772+ break;
773+ case fcf.UNDEFINED:
774+ if (a_value === undefined)
775+ return true;
776+ break;
816777 }
817778 }
818779 return false;
@@ -820,54 +781,317 @@
820781
821782
822783
823- // @fn boolean fcf.isContains(mixed a_object, mixed a_key)
824- // @brief Safely checks if the key is contained in the object.
825- // @param mixed a_object - Checked object
826- // @param mixed a_key - Checked key
827- // @result boolean - Returns true if the key is contained in the object
828- fcf.isContains = (a_object, a_key) => {
829- return typeof a_object === "object" && a_object !== null && a_key in a_object;
784+ /// @fn boolean fcf.empty(mixed a_object)
785+ /// @brief Checks if the object is empty.
786+ /// The following are considered empty: empty arrays (all empty enumerated objects),
787+ /// fieldless objects, empty strings, and the following values: new Date(NaN), NaN , null, undefined.
788+ /// @param mixed a_object Checked object
789+ /// @result boolean Returns true if the object is empty
790+ fcf.empty = (a_object) => {
791+ if (a_object === undefined || a_object === null) {
792+ return true;
793+ } else if (typeof a_object === "number") {
794+ return isNaN(a_object);
795+ } else if (typeof a_object === "string") {
796+ return a_object === "";
797+ } else if (a_object instanceof Error) {
798+ return false;
799+ } else if (a_object instanceof Date) {
800+ return isNaN(a_object.getTime());
801+ } else if (fcf.isIterable(a_object)) {
802+ for(let v of a_object)
803+ return false;
804+ return true;
805+ } else if (typeof a_object === "object") {
806+ for(var k in a_object)
807+ return false;
808+ return true;
809+ }
810+ return false;
830811 }
831812
832813
833814
834- // @fn boolean fcf.isContains(mixed a_object, mixed a_key, string|integer|[string|integer] a_nature)
835- // @brief Safely checks if an object contains a value of type nature by a given key
836- // @param mixed a_object - Checked object
837- // @param mixed a_key - Checked key
838- /// @param string|integer|[string|integer] a_nature - The nature type or an array of nature types.
839- /// nature can take an integer value or a string value:
840- /// - fcf.UNDEFINED=0 | "undefined" - Undefined value
841- /// - fcf.NULL=1 | "null" - Null value
842- /// - fcf.STRING=2 | "string" - String value
843- /// - fcf.BOOLEAN=3 | "boolean" - Boolean value
844- /// - fcf.NUMBER=4 | "number" - Number value
845- /// - fcf.OBJECT=5 | "object" - Object value
846- /// - fcf.ARRAY=6 | "array" - Array value
847- /// - fcf.ITERABLE=7 | "iterable" - Iterable object
848- /// - fcf.NUMBERED=8 | "numbered" - Numbered object (Excluding string)
849- // @result boolean - Returns true if the key is contained in the object
850- fcf.isContainsNature = (a_object, a_key, a_nature) => {
815+ /// @fn boolean fcf.has(object a_object, mixed a_key)
816+ /// @brief Checks if an object contains an element with a given key. Also performed for Map and Set objects
817+ /// @param mixed a_object - Checked object
818+ /// @param mixed a_key - Checked key
819+ /// @result boolean - Returns true if the object contains the given key
820+ fcf.has = (a_object, a_key) => {
851821 if (typeof a_object !== "object" || a_object === null) {
852822 return false;
853823 }
854- return fcf.isNature(a_object[a_key], a_nature);
824+ if (fcf.isIterable(a_object) && typeof a_object.has == "function") {
825+ return a_object.has(a_key);
826+ }
827+ if (fcf.isNumbered(a_object)) {
828+ a_key = parseInt(a_key);
829+ return a_key >= 0 && a_key < a_object.length;
830+ }
831+ return a_key in a_object;
855832 }
856833
857834
858835
836+ /// @fn mixed fcf.get(object a_object, mixed a_key)
837+ /// @brief Get an element stored in an object by key. Also performed for Map and Set objects
838+ /// @param object a_object - Source object
839+ /// @param mixed a_key - Source key
840+ /// @result mixed - Returns the element stored in the object,
841+ /// if the element is not found in the object, undefined is returned
842+ fcf.get = (a_object, a_key) => {
843+ if (typeof a_object !== "object" || a_object === null) {
844+ return;
845+ }
846+ if (a_object instanceof Set) {
847+ return a_object.has(a_key) ? a_key : undefined;
848+ }
849+ if (a_object instanceof Map) {
850+ return a_object.get(a_key);
851+ }
852+ return a_object[a_key];
853+ }
854+
855+
856+
857+ /// @fn bool fcf.equal(mixed a_left, mixed a_right, boolean a_strict = false)
858+ /// @brief Compares two values for equality
859+ /// The objects being compared can be simple types, arrays, objects, Date, Map and Set
860+ /// When comparing two NaN, the function will return true
861+ /// @param mixed a_left - First comparison value
862+ /// @param mixed a_right - Second comparison value
863+ /// @param boolean a_strict = false - If true, then strict comparison is used for comparison ===
864+ /// @result boolean - Returns true if two values are equal
865+ fcf.equal = (a_left, a_right, a_strict) => {
866+ if (!_equalObject(a_left, a_right, a_strict))
867+ return false;
868+ if (typeof a_left == "object")
869+ return _equalObject(a_right, a_left, a_strict);
870+ return true;
871+ }
872+ function _equalObject(a_left, a_right, a_strict) {
873+ if (Array.isArray(a_left)) {
874+ if (!Array.isArray(a_right))
875+ return false;
876+ if (a_left.length != a_right.length)
877+ return false;
878+ for (let i = 0; i < a_left.length; ++i) {
879+ if (!fcf.equal(a_left[i], a_right[i], a_strict))
880+ return false;
881+ }
882+ } else if (a_left instanceof Date || a_right instanceof Date) {
883+ if (!(a_left instanceof Date) || !(a_right instanceof Date)){
884+ return false;
885+ } else {
886+ if (isNaN(a_left.getTime()) && isNaN(a_right.getTime()))
887+ return true;
888+ return a_left.getTime() == a_right.getTime();
889+ }
890+ } else if (typeof a_left === "object" && a_left !== null ){
891+ if (typeof a_right !== "object" || a_right == null)
892+ return false;
893+ if (Array.isArray(a_right))
894+ return false;
895+ if (a_strict && a_left.constructor != a_right.constructor) {
896+ return false;
897+ }
898+ let fastResult;
899+ fastResult = fcf.each(a_left, (a_key, a_value)=>{
900+ if (a_value !== undefined && !fcf.has(a_right, a_key))
901+ return false;
902+ if (!_equalObject(a_value, fcf.get(a_right, a_key), a_strict))
903+ return false;
904+ }).result();
905+ if (fastResult !== undefined)
906+ return fastResult;
907+ fastResult = fcf.each(a_right, (a_key, a_value)=>{
908+ if (a_value !== undefined && !fcf.has(a_left, a_key)){
909+ return false;
910+ }
911+ }).result();
912+ if (fastResult !== undefined)
913+ return false;
914+ } else {
915+ if (typeof a_left == "number" && isNaN(a_left) && typeof a_right == "number" && isNaN(a_right)){
916+ return true;
917+ }
918+ if (a_strict){
919+ if (a_left !== a_right) {
920+ return false;
921+ }
922+ } else {
923+ if (a_left != a_right) {
924+ return false;
925+ }
926+ }
927+ }
928+ return true;
929+ }
930+
931+
932+
933+ /// @fn integer fcf.compare(mixed a_left, mixed a_right, boolean a_strict = false)
934+ /// @brief Compares two values
935+ /// The objects being compared can be simple types, arrays, objects, Date, Map and Set
936+ /// When comparing two NaN, the function will return 0
937+ /// @param mixed a_left - First comparison value
938+ /// @param mixed a_right - Second comparison value
939+ /// @param boolean a_strict = false - If it is true, then when comparing for equality, strict equality is used ===, if it is false, comparison == is used
940+ /// @result integer - Returns 0 if two values are equal;
941+ /// Returns 1 if a_left > a_right;
942+ /// Returns -1 if a_left < a_right;
943+ fcf.compare = (a_left, a_right, a_strict) => {
944+ let c = _compareObject(a_left, a_right, a_strict);
945+ if (c)
946+ return c;
947+ c = _compareObject(a_right, a_left, a_strict);
948+ return c == 0 ? 0 :
949+ c < 0 ? 1 :
950+ -1;
951+ }
952+ function _compareObject(a_left, a_right, a_strict) {
953+ if (Array.isArray(a_left)) {
954+ if (!Array.isArray(a_right)) {
955+ return 1;
956+ }
957+ if (a_left.length != a_right.length) {
958+ return a_left.length < a_right.length ? -1 : 1;
959+ }
960+ for (let i = 0; i < a_left.length; ++i) {
961+ let c = fcf.compare(a_left[i], a_right[i], a_strict);
962+ if (c) {
963+ return c;
964+ }
965+ }
966+ return 0;
967+ } else if (typeof a_left === "object" && a_left !== null && !(a_left instanceof Date)) {
968+ if (typeof a_right !== "object" || a_right == null) {
969+ return 1;
970+ }
971+ if (Array.isArray(a_right)) {
972+ return -1;
973+ }
974+ if (a_strict){
975+ if (a_left.constructor > a_right.constructor) {
976+ return 1;
977+ } else if (a_left.constructor < a_right.constructor) {
978+ return -1;
979+ }
980+ }
981+ let fastResult;
982+ fastResult = fcf.each(a_left, (a_key, a_value)=>{
983+ if (a_value !== undefined && !fcf.has(a_right, a_key)) {
984+ return 1;
985+ }
986+ let c = fcf.compare(a_value, fcf.get(a_right, a_key), a_strict);
987+ if (c)
988+ return c;
989+ }).result();
990+
991+ if (fastResult)
992+ return fastResult;
993+
994+ fastResult = fcf.each(a_right, (a_key, a_value)=>{
995+ if (a_value !== undefined && !fcf.has(a_left, a_key))
996+ return -1;
997+ }).result();
998+
999+ if (fastResult)
1000+ return -1;
1001+
1002+ return 0;
1003+ } else {
1004+ if (!a_strict) {
1005+ if (a_left === undefined || a_left === null || a_left === 0 || a_left === false) {
1006+ if (a_right === undefined || a_right === null || a_right === 0 || a_right === false){
1007+ return 0;
1008+ } else if (typeof a_right == "string") {
1009+ let tr = fcf.trim(a_right);
1010+ if (tr == "" || tr == "0")
1011+ return 0;
1012+ }
1013+ }
1014+ if (a_right === undefined || a_right === null || a_right === 0 || a_right === false) {
1015+ if (a_left === undefined || a_left === null || a_left === 0 || a_left === false){
1016+ return 0;
1017+ } else if (typeof a_left == "string") {
1018+ let tr = fcf.trim(a_left);
1019+ if (tr == "" || tr == "0")
1020+ return 0;
1021+ }
1022+ }
1023+ }
1024+ let t1 = a_left instanceof Date ? "date" : typeof a_left;
1025+ let t2 = a_right instanceof Date ? "date" : typeof a_right;
1026+ if (!a_strict && (typeof a_left == "number" || typeof a_right == "number")) {
1027+ if (typeof a_left != "number") {
1028+ a_left = parseFloat(a_left);
1029+ t1 = "number";
1030+ }
1031+ if (isNaN(a_left)) {
1032+ t1 = "nan";
1033+ };
1034+ if (typeof a_right != "number") {
1035+ a_right = parseFloat(a_left);
1036+ t2 = "number";
1037+ }
1038+ if (isNaN(a_right)) {
1039+ t2 = "nan";
1040+ };
1041+ }
1042+ let tc1 = _compareWeights[t1];
1043+ let tc2 = _compareWeights[t2];
1044+ if (t1 == "nan" && t2 == "nan") {
1045+ return 0;
1046+ } else if (!a_strict && a_left == a_right) {
1047+ return 0;
1048+ } else if (tc1 == tc2){
1049+ if (a_left instanceof Date){
1050+ a_left = a_left.getTime();
1051+ a_right = a_right.getTime();
1052+ if (isNaN(a_left) && isNaN(a_right))
1053+ return 0;
1054+ }
1055+ if (a_left == a_right)
1056+ return 0;
1057+ if (a_left < a_right)
1058+ return -1;
1059+ return 1;
1060+ } else if (tc1 > tc2){
1061+ return 1
1062+ } else {
1063+ return -1;
1064+ }
1065+ }
1066+ }
1067+ const _compareWeights = {
1068+ "undefined": 0,
1069+ "null": 1,
1070+ "nan": 2,
1071+ "boolean": 3,
1072+ "number": 4,
1073+ "string": 5,
1074+ "date": 6,
1075+ "object": 7,
1076+ "array": 8,
1077+ };
1078+
1079+
1080+
8591081 /// @fn number fcf.count(object a_object, function a_cb = undefined)
8601082 /// @brief Safely determines the number of elements in an object or a string
861- /// @param object a_object - Checked object
1083+ /// @param object a_object - Source object
8621084 /// @param function a_cb = undefined - If a functor is given, then the child
8631085 /// element is taken into account if the function returns true
8641086 /// - Function signature: boolean a_cb(mixed a_key, mixed a_value)
865- /// @result number - Number of counted elements in an object or a string
1087+ /// @result integer - Number of counted elements in an object or a string
8661088 fcf.count = (a_object, a_cb) => {
8671089 if (!a_cb) {
8681090 if (typeof a_object == "object" && a_object !== null) {
8691091 if (fcf.isNumbered(a_object)) {
8701092 return a_object.length;
1093+ } else if (a_object instanceof Map || a_object instanceof Set) {
1094+ return a_object.size;
8711095 } else {
8721096 let count = 0;
8731097 for(let k in a_object)
@@ -892,219 +1116,23 @@
8921116
8931117
8941118
895- /// @fn fcf.getApproximateMSize(a_value)
896- /// @brief A very approximate estimate of the memory occupied by an object in bytes.
897- /// @details The error can reach up to 10 times.
898- /// Used to protect algorithms from memory overflows
899- /// @param mixed a_value - Checked value
900- /// @result number - The amount of memory used by the object
901- fcf.getApproximateMSize = (a_value) => {
902- if (typeof a_value == "string"){
903- return 30 * a_value.length;
904- } else if (Array.isArray(a_value)) {
905- let s = 30;
906- for(let i = 0; i < a_value.length; ++i) {
907- s += fcf.getApproximateMSize(a_value[i]);
908- }
909- return s;
910- } else if (typeof a_value == "object" && a_value !== null) {
911- let s = 30;
912- for(let k in a_value){
913- s += fcf.getApproximateMSize(k);
914- s += fcf.getApproximateMSize(a_value[k]);
915- }
916- return s;
917- } else {
918- return 30;
919- }
920- }
921-
922-
923-
924- /// @fn object fcf.append(object|array a_dstObject, object|array a_srcObject1, object|array a_srcObject2, ...)
925- /// @fn object fcf.append(boolean a_recursionCopy, object a_dstObject, object a_srcObject1, object a_srcObject2, ...)
926- /// @brief Copies properties from a_srcObjectN objects to the receiving object
927- /// @param boolean a_recursionCopy - If the parameter is not used or is equal to false,
928- /// then only the fields are copied into the a_dstObject element from the a_srcObjectN objects.
929- /// If the parameter is used and equals true, then nested nested elements are copied recursively,
930- /// i.e. the object is supplemented with new objects.
931- /// @param object|array a_dstObject - Receiving object
932- /// @param object|array a_srcObject - Source objects
933- /// @result object|array - Results a_dstObject
934- fcf.append = (...args) => {
935- let startArg = typeof args[0] === "boolean" ? 1 : 0;
936- let req = typeof args[0] === "boolean" && args[0];
937-
938- if (Array.isArray(args[startArg])) {
939- for(let j = startArg + 1; j < args.length; ++j) {
940- if (!args[j])
941- continue;
942- if (req) {
943- for(let i = 0; i < args[j].length; ++i) {
944- let itm = args[j][i] === null ? null :
945- Array.isArray(args[j][i]) ? fcf.append(true, [], args[j][i]) :
946- args[j][i] instanceof Date ? new Date(args[j][i]) :
947- typeof args[j][i] === "object" ? fcf.append(true, Object.create(Object.getPrototypeOf(args[j][i])), args[j][i]) :
948- args[j][i];
949- args[startArg].push(itm);
950- }
951- } else {
952- for(let i = 0; i < args[j].length; ++i)
953- args[startArg].push(args[j][i]);
954- }
955- }
956- } else if (typeof args[startArg] === "object" && args[startArg] !== null) {
957- for(let j = startArg + 1; j < args.length; ++j) {
958- if (!args[j])
959- continue;
960- let prop = Object.getOwnPropertyNames(args[j]);
961- for(let pkey in prop) {
962- let key = prop[pkey];
963- if (req && typeof args[j][key] === "object" && args[j][key] !== null){
964- if (!args[startArg][key])
965- args[startArg][key] = Array.isArray(args[j][key]) ? [] :
966- args[j][key] instanceof Date ? new Date(args[j][key]) :
967- Object.create(Object.getPrototypeOf(args[j][key]));
968- fcf.append(true, args[startArg][key], args[j][key]);
969- } else {
970- args[startArg][key] = args[j][key];
971- }
972- }
973- }
974- }
975- return args[startArg];
976- }
977-
978-
979-
980- /// @fn mixed fcf.clone(mixed a_value)
981- /// @brief Creates a copy of an object
982- /// @param mixed a_value - Source value
983- /// @result mixed - Returns a clone of a_value
984- fcf.clone = (a_object) => {
985- return a_object === null ? null :
986- Array.isArray(a_object) ? fcf.append(true, [], a_object) :
987- a_object instanceof Date ? new Date(a_object) :
988- typeof a_object == "object" ? fcf.append(true, Object.create(Object.getPrototypeOf(a_object)), a_object) :
989- a_object;
990- }
991-
992-
993-
994- /// @fn bool fcf.equal(mixed a_left, mixed a_right)
995- /// @brief Compares two values for equality
996- /// @param mixed a_left - First comparison value
997- /// @param mixed a_right - Second comparison value
998- /// @result boolean - Returns true if two values are equal
999- fcf.equal = (a_left, a_right) => {
1000- if (!_equalObject(a_left, a_right))
1001- return false;
1002- return _equalObject(a_right, a_left);
1003- }
1004- function _equalObject(a_left, a_right) {
1005- if (Array.isArray(a_left)) {
1006- if (!Array.isArray(a_right))
1007- return false;
1008- if (a_left.length != a_right.length)
1009- return false;
1010- for (let i = 0; i < a_left.length; ++i) {
1011- if (!fcf.equal(a_left[i], a_right[i]))
1012- return false;
1013- }
1014- } else if (typeof a_left === "object" && a_left !== null ){
1015- if (typeof a_right !== "object" || a_right == null)
1016- return false;
1017- for (let key in a_left) {
1018- if (a_left[key] !== undefined && !(key in a_right))
1019- return false;
1020- _equalObject(a_left[key], a_right[key]);
1021- }
1022- for (let key in a_right) {
1023- if (a_right[key] !== undefined && !(key in a_left))
1024- return false;
1025- }
1026- } else {
1027- if (a_left != a_right) {
1028- return false;
1029- }
1030- }
1031- return true;
1032- }
1033-
1034-
1035-
1036- /// @fn mixed fcf.each(mixed a_obj, function a_cb(mixed a_key, mixed a_value) )
1119+ /// @fn fcf.Actions->mixed fcf.each(mixed a_obj, function a_cb(mixed a_key, mixed a_value) )
10371120 /// @brief Iterates over the elements of the argument a_obj
10381121 /// @details The function is used for unified enumeration of
1039- /// objects, enumerated objects and strings.
1040- /// @details If the fcf.sort() function was applied to the a_obj object,
1041- /// then the iteration will be performed in sorted order.
1122+ /// objects, enumerated objects (but not strings).
10421123 /// @details Iteration is performed until the last element or until the a_cb
10431124 /// functor returns a result other than undefined, Promise->!undefined or fcf.Actions->!undefined
10441125 /// @param mixed a_obj - Iterable object
10451126 /// @param function a_cb(mixed a_key, mixed a_value) - Functor, can be asynchronous.
10461127 /// - Example:
10471128 /// await fcf.each([1,2,3], async (a_key, a_value)=>{ ... }));
1048- /// @result mixed - Returns a value of a last result.
1129+ /// @result mixed - Returns a fcf.Actions object with value of a last result.
10491130 fcf.each = (a_obj, a_cb) => {
1050- let isAsync = a_cb.toString().substr(0, 5) == "async" && fcf.isSpaceCode(a_cb.toString().charCodeAt(5));
1051- let asyncResult = isAsync ? fcf.actions() : undefined;
1131+ let asyncResult;
10521132 let result = undefined;
1053- if (a_obj && typeof a_obj == "object" && a_obj !== null && Array.isArray(a_obj.___fcf___order)){
1054- for(let i = 0; i < a_obj.___fcf___order.length; ++i) {
1055- if (isAsync){
1056- result = a_cb(a_obj.___fcf___order[i], a_obj[a_obj.___fcf___order[i]]).catch((e)=>{ asyncResult.error(e) });
1057- } else {
1058- result = a_cb(a_obj.___fcf___order[i], a_obj[a_obj.___fcf___order[i]]);
1059- }
1060- if (result instanceof Promise || result instanceof fcf.Actions) {
1061- let index = i + 1;
1062- let currentResult = result;
1063- let actions = fcf.actions();
1064- let act = undefined;
1065- result = asyncResult ? asyncResult : fcf.actions();
1066- result.then((a_res, a_act) => { act = a_act; });
1067- function doAction(){
1068- actions.then(()=>{
1069- if (index < a_obj.___fcf___order.length){
1070- return a_cb(a_obj.___fcf___order[index], a_obj[a_obj.___fcf___order[index]]);
1071- }
1072- })
1073- .then((a_res)=>{
1074- ++index;
1075- if (a_res === undefined && index < a_obj.___fcf___order.length) {
1076- doAction();
1077- } else {
1078- act.complete(a_res);
1079- }
1080- })
1081- .catch((e)=>{
1082- act.error(e);
1083- });
1084- }
1085- currentResult.then((a_res)=>{
1086- if (a_res === undefined) {
1087- doAction();
1088- } else {
1089- act.complete(a_res);
1090- }
1091- })
1092- .catch((e)=>{
1093- act.complete(e);
1094- })
1095-
1096- break;
1097- } if (result !== undefined) {
1098- break;
1099- }
1100- }
1101- } else if (fcf.isNumbered(a_obj)) {
1133+ if (fcf.isNumbered(a_obj) && (a_obj && typeof a_obj == "object" && typeof a_obj.forEach != "function")) {
11021134 for(let i = 0; i < a_obj.length; ++i) {
1103- if (isAsync){
1104- result = a_cb(i, a_obj[i]).catch((e)=>{ asyncResult.error(e) });
1105- } else {
1106- result = a_cb(i, a_obj[i]);
1107- }
1135+ result = a_cb(i, a_obj[i]);
11081136 if (result instanceof Promise || result instanceof fcf.Actions) {
11091137 let index = i + 1;
11101138 let currentResult = result;
@@ -1137,9 +1165,6 @@
11371165 }
11381166 })
11391167 .catch((e)=>{
1140- act.complete(e);
1141- })
1142- .catch((e)=>{
11431168 act.error(e);
11441169 });
11451170 break;
@@ -1151,20 +1176,35 @@
11511176 let asyncEnable = false;
11521177 let asyncKeys = [];
11531178 let index = 0;
1154- for(let k in a_obj) {
1155- if (asyncEnable) {
1156- asyncKeys.push(k);
1157- } else {
1158- if (isAsync){
1159- result = a_cb(k, a_obj[k]).catch((e)=>{ asyncResult.error(e) });
1179+ if (typeof a_obj.forEach === "function") {
1180+ let breakLoop = false;
1181+ a_obj.forEach((a_value, a_key)=>{
1182+ if (breakLoop) {
1183+ return;
1184+ }
1185+ if (asyncEnable) {
1186+ asyncKeys.push([a_key, a_value]);
1187+ } else {
1188+ result = a_cb(a_key, a_value);
1189+ if (result instanceof Promise || result instanceof fcf.Actions) {
1190+ asyncEnable = true;
1191+ } else if (result !== undefined) {
1192+ breakLoop = true;
1193+ return true;
1194+ }
1195+ }
1196+ });
1197+ } else {
1198+ for(let k in a_obj) {
1199+ if (asyncEnable) {
1200+ asyncKeys.push([k, a_obj[k]]);
11601201 } else {
11611202 result = a_cb(k, a_obj[k]);
1162- }
1163-
1164- if (result instanceof Promise || result instanceof fcf.Actions) {
1165- asyncEnable = true;
1166- } else if (result !== undefined) {
1167- break;
1203+ if (result instanceof Promise || result instanceof fcf.Actions) {
1204+ asyncEnable = true;
1205+ } else if (result !== undefined) {
1206+ break;
1207+ }
11681208 }
11691209 }
11701210 }
@@ -1178,7 +1218,7 @@
11781218 function doAction(){
11791219 actions.then(() => {
11801220 if (index < asyncKeys.length)
1181- return a_cb(asyncKeys[index], a_obj[asyncKeys[index]]);
1221+ return a_cb(asyncKeys[index][0], asyncKeys[index][1]);
11821222 })
11831223 .then((a_res) => {
11841224 ++index;
@@ -1203,2018 +1243,206 @@
12031243 .catch((e)=>{
12041244 act.error(e);
12051245 });
1206-
1207- }
1208- } else if (typeof a_obj === "string") {
1209- for(let i = 0; i < a_obj.length; ++i) {
1210- result = a_cb(i, a_obj[i]);
1211- if (result !== undefined)
1212- break;
1213- }
1214- }
1215- return result;
1216- }
1217-
1218-
1219- fcf.styleToString = (a_name, a_value) => {
1220- let result = "";
1221- if (typeof a_name == "object" && a_name !== null){
1222- for(let key in a_name) {
1223- result += fcf.styleToString(key, a_name[key]);
1224- }
1225- } else {
1226- if (a_value === undefined || a_value === null || a_value === "")
1227- return result;
1228- if (["left", "top", "bottom", "right", "width", "min-width", "max-width", "height", "min-height", "max-height"].indexOf(a_name) != -1) {
1229- if (fcf.str(a_value).search(/([^\.0-9])/) == -1)
1230- a_value = a_value + "px";
1231- }
1232- result = a_name + ": " + a_value + "; ";
1233- }
1234- return result;
1235- }
1236-
1237-
1238-
1239- //////////////////////////////////////////////////////////////////////////////
1240- // FUNCTIONS AND CLASSES OF BASIC FUNCTIONALITY
1241- //////////////////////////////////////////////////////////////////////////////
1242-
1243-
1244-
1245- fcf.getParamCount = (a_function) => {
1246- if ("length" in a_function)
1247- return a_function.length;
1248- let str = a_function.toString();
1249- let pos = str.indexOf("(");
1250- let startPos = pos;
1251- let end = str.indexOf(")");
1252- if (pos == -1 || end == -1 || pos >= end)
1253- return 0;
1254- let counter = 0;
1255- while(true){
1256- pos = str.indexOf(",", pos+1);
1257- if (pos == -1 || pos >= end)
1258- break
1259- if (!counter)
1260- counter = 2;
1261- else
1262- ++counter;
1263- }
1264- if (counter == 0){
1265- pos = startPos+1;
1266- while(pos < end){
1267- let c = str.charCodeAt(pos);
1268- if (c > 32)
1269- return 1;
1270- ++pos
1271- }
1272- }
1273- return counter;
1274- }
1275-
1276-
1277-
1278- /// @fn fcf.Actions fcf.actions(a_options = {deferred: false, noexcept: false})
1279- /// @fn fcf.Actions fcf.actions(a_cb, a_options = {deferred: false, noexcept: false})
1280- /// @fn fcf.Actions fcf.actions(Promise a_promise, a_options = {deferred: false, noexcept: false})
1281- /// @fn fcf.Actions fcf.actions(fcf.Actions a_actions, a_options = {deferred: false, noexcept: false})
1282- /// @brief Creates a new fcf.Actions object
1283- /// @param function a_cb - Deferred action callback. The function can be asynchronous.
1284- /// - Has the following sigratures:
1285- /// - mixed () - Callback without completion confirmation.
1286- /// The callback is considered executed when the function has completed its execution.
1287- /// If the function returns a Promise (which is asynchronous) or an fcf.Actions object,
1288- /// then the operation completes when the Promise or fcf.Actions is executed.
1289- /// - mixed (fcf.Actions.Act a_act) - Callback with completion confirmation.
1290- /// The callback is considered executed when the fcf.Actions.Act.compete(mixed a_result)
1291- /// or fcf.Actions.Act.error(Error a_error) method is called on the a_act object
1292- /// @param object a_object = {deferred: false, noexcept: false}. Optional additional options
1293- /// - boolean deferred = false - If the flag is true, then the added callbacks will not be executed
1294- /// until the fcf.Actions.startup() method is called.
1295- /// - boolean noexcept = false - If the flag is true, then if the callback ends with an error,
1296- /// the queue execution is not stopped and the handlers passed to catch are not called.
1297- /// - mixed errorResult = undefined - The result returned by the result() method in case of an error
1298- /// - boolean quiet = false - If true, then raw error messages are not printed to the console.
1299- /// @result fcf.Actions - Returns a new object
1300- fcf.actions = (a_cb, a_options) => {
1301- return new fcf.Actions(a_cb, a_options);
1246+ }
1247+ }
1248+
1249+ return (result instanceof fcf.Actions ? result : fcf.actions().result(result)).options({quiet: true}).exception();
13021250 }
13031251
13041252
13051253
1306- /// @class fcf.Actions
1307- /// @brief The analogue of the Promise class with extended functionality.
1308- /// @details a) Has the ability to get the current result:
1309- /// SERVER SIDE EXAMPLE : let mod = fcf.require("module").result().
1310- /// b) Has additional each & asyncEach methods.
1311- /// c) Task processing callbacks can have an additional action-act
1312- /// argument to complete the operation.
1313- fcf.Actions = class {
1314-
1315- /// @method constructor(a_options = {deferred: false, noexcept: false, errorResult: undefined})
1316- /// @method constructor(a_cb, a_options = {deferred: false, noexcept: false})
1317- /// @method constructor(Promise a_promise, a_options = {deferred: false, noexcept: false})
1318- /// @method constructor(fcf.Actions a_actions, a_options = {deferred: false, noexcept: false})
1319- /// @param function a_cb - Action callback. The function can be asynchronous.
1320- /// - Has the following sigratures:
1321- /// - mixed () - Callback without completion confirmation.
1322- /// The callback is considered executed when the function has completed its execution.
1323- /// If the function returns a Promise (which is asynchronous) or an fcf.Actions object,
1324- /// then the operation completes when the Promise or fcf.Actions is executed.
1325- /// - mixed (fcf.Actions.Act a_act) - Callback with completion confirmation.
1326- /// The callback is considered executed when the fcf.Actions.Act.compete(mixed a_result)
1327- /// or fcf.Actions.Act.error(Error a_error) method is called on the a_act object
1328- /// @param object a_object = {deferred: false, noexcept: false}. Optional additional options
1329- /// - boolean deferred = false - If the flag is true, then the added callbacks will not be executed
1330- /// until the fcf.Actions.startup() method is called.
1331- /// - boolean noexcept = false - If the flag is true, then if the callback ends with an error,
1332- /// the queue execution is not stopped and the handlers passed to catch are not called.
1333- /// - mixed errorResult = undefined - The result returned by the result() method in case of an error
1334- /// - boolean quiet = false - If true, then raw error messages are not printed to the console.
1335- /// @example
1336- /// let actions = new fcf.Actions(async ()=>{
1337- /// ...
1338- /// return "result_value";
1339- /// });
1340- /// @example
1341- /// let actions = new fcf.Actions((a_act)=>{
1342- /// ...
1343- /// a_act.complete("result_value");
1344- /// });
1345- /// @example
1346- /// let actions = new fcf.Actions(async (a_act)=>{
1347- /// console.log("constructor call")
1348- /// ...
1349- /// a_act.complete("result_value");
1350- /// }, { deferred: true });
1351- /// ...
1352- /// actions.then(async (a_lastResult)=>{
1353- /// console.log("callcack call: " + a_lastResult)
1354- /// ...
1355- /// })
1356- /// ...
1357- /// console.log("startup call")
1358- /// await actions.startup();
1359- /// console.log("end")
1360- ///
1361- /// // stdout: startup call
1362- /// // stdout: constructor call
1363- /// // stdout: callcack call: result_value
1364- /// // stdout: end
1365- constructor(a_cb, a_options) {
1366- if (typeof a_cb == "object"){
1367- a_options = a_cb;
1368- a_cb = undefined;
1369- }
1370- this._flags = (a_options && !!a_options.deferred ? ACTIONS_FLAGS_DEFERRED : 0) |
1371- (a_options && !!a_options.noexcept ? ACTIONS_FLAGS_NOEXCEPT : 0) |
1372- (a_options && !!a_options.quiet ? ACTIONS_FLAGS_QUIET : 0);
1373-
1374- this._stack = [];
1375- if (a_options && a_options.context)
1376- fcf.setContext(a_options.context);
1377- this._state = fcf.getState();
1378- this._errorcbs = [];
1379- if (a_options && a_options.errorResult)
1380- this._errorResult = a_options.errorResult;
1381- if (typeof a_cb == "function") {
1382- this.then(a_cb, undefined, true);
1383- }
1384- }
1385-
1386-
1387-
1388- /// @method fcf.Actions then(function a_cb, function a_cberror = undefined)
1389- /// @brief Adds a callback to the execution queue.
1390- /// @details When the a_cb function is called, this is set to fcf.Actions object
1391- /// @param function a_cb - Action callback. The function can be asynchronous.
1392- /// - Has the following sigratures:
1393- /// - mixed a_cb(mixed a_lastResult) - Callback without completion confirmation.
1394- /// The callback is considered executed when the function has completed its execution.
1395- /// If the function returns a Promise (which is asynchronous) or an fcf.Actions object,
1396- /// then the operation completes when the Promise or fcf.Actions is executed.
1397- /// - mixed a_cb(mixed a_lastResult, fcf.Actions.Act a_act) - Callback with completion confirmation.
1398- /// The callback is considered executed when the fcf.Actions.Act.compete(mixed a_result)
1399- /// or fcf.Actions.Act.error(Error a_error) method is called on the a_act object
1400- /// @param function a_cberror - Error handler
1401- /// - Has the following sigrature:
1402- /// - a_cberror(Error a_error)
1403- /// @result fcf.Actions - Self object
1404- /// @example
1405- /// await (new fcf.Actions())
1406- /// .then(async (a_lastResult)=>{
1407- /// let result;
1408- /// ...
1409- /// return result;
1410- /// });
1411- /// @example
1412- /// await (new fcf.Actions())
1413- /// .then((a_lastResult, a_act)=>{
1414- /// let error;
1415- /// let result;
1416- /// ...
1417- /// if (error)
1418- /// a_act.error(error);
1419- /// else
1420- /// a_act.compete(result);
1421- /// });
1422- then(a_cb, a_cberror, _a_skipResult) {
1423- if (typeof Promise !== "undefined" && a_cb instanceof Promise){
1424- let self = this;
1425- a_cb.catch((e)=>{
1426- self.error(e);
1427- });
1428- this.then(()=>{
1429- return a_cb;
1430- })
1431- } else if (a_cb instanceof fcf.Actions){
1432- this.then(()=>{
1433- return a_cb;
1434- })
1435- } else {
1436- if (a_cb) {
1437- this._stack.push({cb: a_cb, args: undefined, autoComplete: fcf.getParamCount(a_cb) < (_a_skipResult ? 1 : 2), skipResult: _a_skipResult });
1438- this._execute();
1439- }
1440- }
1441-
1442- if (a_cberror)
1443- this.catch(a_cberror);
1444- return this;
1445- }
1446-
1447-
1448-
1449- /// @method fcf.Actions each(object|array|function a_obj, function a_cb)
1450- /// @brief Executes a_cb one by one for each element from a_obj
1451- /// @details When the a_cb function is called, this is set to fcf.Actions object
1452- /// @param object|array|function a_obj - An object for whose elements the a_cb callback will be called one by one
1453- /// a_obj can be a function, in which case its result is the iterated object.
1454- /// The function call is made just before the callbacks are executed.
1455- /// @param function a_cb - Action callback for each a_obj element. The function can be asynchronous.
1456- /// - Has the following sigratures:
1457- /// - mixed a_cb(mixed a_key, mixed a_value, mixed a_lastResult) -
1458- /// Callback without completion confirmation.
1459- /// The callback is considered executed when the function has completed its execution.
1460- /// If the function returns a Promise (which is asynchronous) or an fcf.Actions object,
1461- /// then the operation completes when the Promise or fcf.Actions is executed.
1462- /// - mixed a_cb(mixed a_key, mixed a_value, mixed a_lastResult, fcf.Actions.Act a_act) -
1463- /// Callback with completion confirmation.
1464- /// The callback is considered executed when the fcf.Actions.Act.compete(mixed a_result)
1465- /// or fcf.Actions.Act.error(Error a_error) method is called on the a_act object
1466- /// @result fcf.Actions - Self object
1467- /// @example
1468- /// await (new fcf.Actions())
1469- /// .each(["one", "two", "three"], async (a_key, a_value, a_lastResult, a_act)=>{
1470- /// console.log(a_key, " : ", a_value)
1471- /// a_act.complete(a_lastResult);
1472- /// })
1473- /// .then(()=>{
1474- /// console.log("end")
1475- /// })
1476- ///
1477- /// // stdout: 0 : one
1478- /// // stdout: 1 : two
1479- /// // stdout: 2 : three
1480- /// // stdout: end
1481- /// @example
1482- /// let array;
1483- /// await (new fcf.Actions())
1484- /// .then(()=>{
1485- /// array = ["one", "two", "three"];
1486- /// })
1487- /// .each(()=>{ return array; }, async (a_key, a_value, a_lastResult, a_act)=>{
1488- /// console.log(a_key, " : ", a_value)
1489- /// a_act.complete(a_lastResult);
1490- /// })
1491- /// .then(()=>{
1492- /// console.log("end")
1493- /// })
1494- ///
1495- /// // stdout: 0 : one
1496- /// // stdout: 1 : two
1497- /// // stdout: 2 : three
1498- /// // stdout: end
1499- each(a_obj, a_cb) {
1500- if (typeof a_obj != "function"){
1501- if (typeof a_obj == "object" && a_obj !== null) {
1502- if (fcf.isNumbered(a_obj)) {
1503- for(let k = 0; k < a_obj.length; ++k)
1504- this._stack.push({cb: a_cb, args: [k, a_obj[k]], autoComplete: fcf.getParamCount(a_cb) < 4});
1254+ /// @fn object fcf.append(object|array a_dstObject, object|array a_srcObject1, object|array a_srcObject2, ...)
1255+ /// @fn object fcf.append(boolean a_recursionCopy, object a_dstObject, object a_srcObject1, object a_srcObject2, ...)
1256+ /// @brief Copies properties from a_srcObjectN objects to the receiving object
1257+ /// @param boolean a_recursionCopy - If the parameter is not used or is equal to false,
1258+ /// then only the fields are copied into the a_dstObject element from the a_srcObjectN objects.
1259+ /// If the parameter is used and equals true, then nested nested elements are copied recursively,
1260+ /// i.e. the object is supplemented with new objects.
1261+ /// @param object|array a_dstObject - Receiving object
1262+ /// @param object|array a_srcObject - Source objects
1263+ /// @result object|array - Results a_dstObject
1264+ fcf.append = (...args) => {
1265+ let startArg = typeof args[0] === "boolean" ? 1 : 0;
1266+ let req = typeof args[0] === "boolean" && args[0];
1267+
1268+ if (Array.isArray(args[startArg])) {
1269+ for(let j = startArg + 1; j < args.length; ++j) {
1270+ if (req) {
1271+ if (Array.isArray(args[j])) {
1272+ for(let i = 0; i < args[j].length; ++i) {
1273+ let itm = args[j][i] === null ? null :
1274+ args[j][i] instanceof Date ? new Date(args[j][i]) :
1275+ Array.isArray(args[j][i]) ? fcf.append(true, [], args[j][i]) :
1276+ typeof args[j][i] === "object" ? fcf.append(true, new args[j][i].__proto__.constructor(), args[j][i]) :
1277+ args[j][i];
1278+ args[startArg].push(itm);
1279+ }
15051280 } else {
1506- for(let k in a_obj)
1507- this._stack.push({cb: a_cb, args: [k, a_obj[k]], autoComplete: fcf.getParamCount(a_cb) < 4});
1281+ fcf.each(args[j], (a_key, a_value)=>{
1282+ let itm = a_value === null ? null :
1283+ a_value instanceof Date ? new Date(a_value) :
1284+ Array.isArray(a_value) ? fcf.append(true, [], a_value) :
1285+ typeof a_value === "object" ? fcf.append(true, new a_value.__proto__.constructor(), a_value) :
1286+ a_value;
1287+ args[startArg].push(itm);
1288+ });
1289+ }
1290+ } else {
1291+ if (Array.isArray(args[j])) {
1292+ for(let i = 0; i < args[j].length; ++i){
1293+ args[startArg].push(args[j][i]);
1294+ }
1295+ } else {
1296+ fcf.each(args[j], (a_key, a_value)=>{
1297+ args[startArg].push(a_value);
1298+ });
15081299 }
15091300 }
1510- this._execute();
1511- } else {
1512- this.then((a_res)=>{
1513- a_obj = a_obj();
1514- if (fcf.isNumbered(a_obj)) {
1515- for(let i = a_obj.length-1; i >= 0; --i)
1516- this._stack.unshift({cb: a_cb, args: [i, a_obj[i]], autoComplete: fcf.getParamCount(a_cb) < 4});
1517- } else if (typeof a_obj == "object" && a_obj !== null) {
1518- let keys = [];
1519- for(let k in a_obj)
1520- keys.push(k);
1521- for(let i = keys.length-1; i >= 0; --i)
1522- this._stack.unshift({cb: a_cb, args: [keys[i], a_obj[keys[i]]], autoComplete: fcf.getParamCount(a_cb) < 4});
1301+ }
1302+ } else if (args[startArg] instanceof Set) {
1303+ for(let j = startArg + 1; j < args.length; ++j) {
1304+ if (typeof args[j] !== "object" || args[j] === null)
1305+ continue;
1306+ if (args[j] instanceof Set) {
1307+ for(let key of args[j]) {
1308+ args[startArg].add(key);
15231309 }
1524- this._execute();
1525- return a_res;
1526- })
1527- }
1528- return this;
1529- }
1530-
1531-
1532-
1533- /// @method fcf.Actions asyncEach(object|array|function a_obj, function a_cb)
1534- /// @brief Executes multiple a_cb at the same time for each element from a_obj
1535- /// @details When the a_cb function is called, this is set to fcf.Actions object
1536- /// @param object|array|function a_obj - An object for whose elements the a_cb callback will be called one by one
1537- /// a_obj can be a function, in which case its result is the iterated object.
1538- /// The function call is made just before the callbacks are executed.
1539- /// @param function a_cb - Action callback for each a_obj element. The function can be asynchronous.
1540- /// - Has the following sigratures:
1541- /// - mixed a_cb(mixed a_key, mixed a_value, mixed a_lastResult) -
1542- /// Callback without completion confirmation.
1543- /// The callback is considered executed when the function has completed its execution.
1544- /// If the function returns a Promise (which is asynchronous) or an fcf.Actions object,
1545- /// then the operation completes when the Promise or fcf.Actions is executed.
1546- /// - mixed a_cb(mixed a_key, mixed a_value, mixed a_lastResult, fcf.Actions.Act a_act) -
1547- /// Callback with completion confirmation.
1548- /// The callback is considered executed when the fcf.Actions.Act.compete(mixed a_result)
1549- /// or fcf.Actions.Act.error(Error a_error) method is called on the a_act object
1550- /// @result fcf.Actions - Self object
1551- /// @example
1552- /// await (new fcf.Actions())
1553- /// .asyncEach(["one", "two", "three"], async (a_key, a_value, a_lastResult, a_act)=>{
1554- /// ...
1555- /// setTimeout(()=>{
1556- /// console.log(a_key, " : ", a_value);
1557- /// a_act.complete(a_lastResult);
1558- /// }, 100 - a_key);
1559- /// })
1560- /// .then(()=>{
1561- /// console.log("end")
1562- /// })
1563- ///
1564- /// // stdout: 2 : three
1565- /// // stdout: 1 : two
1566- /// // stdout: 0 : one
1567- /// // stdout: end
1568- asyncEach(a_obj, a_cb) {
1569- let self = this;
1570-
1571- this.then((a_value, a_act) => {
1572- a_obj = typeof a_obj == "function" ? a_obj() : a_obj;
1573- let autoComplete = fcf.getParamCount(a_cb) < 4;
1574- let size = fcf.count(a_obj);
1575- let counter = 0;
1576- let error = false;
1577-
1578- if (size == 0) {
1579- a_act.complete();
1580- return;
1581- }
1582-
1583- let brk = false;
1584- fcf.each(a_obj, (k)=>{
1585- if (brk)
1586- return false;
1587- let act = {
1588- _complete: false,
1589- complete: function(a_value){
1590- if (_isServer)
1591- fcf.setState(self._state);
1592- if (this._complete || error || self._error)
1593- return;
1594-
1595- ++counter;
1596-
1597- self._result = a_value;
1598-
1599- if (counter == size){
1600- this._complete = true;
1601- a_act.complete(a_value);
1602- }
1603- },
1604- error: function(a_error){
1605- if (_isServer)
1606- fcf.setState(self._state);
1607- if (self._flags & ACTIONS_FLAGS_NOEXCEPT){
1608- this.complete();
1609- return;
1610- }
1611-
1612- if (this._complete || error || self._error)
1613- return;
1614-
1615- self._result = undefined;
1616- this._complete = true;
1617- error = true;
1618- a_act.error(a_error);
1619- },
1620- };
1621-
1622- let args = [k, a_obj[k], self._result];
1623- if (!autoComplete)
1624- args.push(act);
1625-
1626- let forceExit = false;
1627- let res;
1628- try {
1629- let asyncPrefix = a_cb.toString().substr(0, 5);
1630- if (asyncPrefix == "async" && a_cb.toString().charCodeAt(5) <= 32){
1631- res = a_cb.apply(self, args).catch((e)=>{
1632- forceExit = true;
1633- brk = true;
1634- act.error(e)
1635- });
1636- } else {
1637- res = a_cb.apply(self, args)
1638- }
1639- } catch(e) {
1640- brk = true;
1641- act.error(e);
1642- return;
1643- }
1644-
1645- if (forceExit)
1646- return;
1647-
1648- if (autoComplete && ((typeof Promise !== "undefined" && res instanceof Promise) || res instanceof fcf.Actions)){
1649- res
1650- .then((a_value)=>{
1651- act.complete(a_value);
1652- })
1653- .catch((a_error)=>{
1654- act.error(a_error);
1655- });
1656- } else if (autoComplete){
1657- act.complete(res);
1658- }
1659- });
1660- });
1661- return this;
1662- }
1663-
1664-
1665-
1666- /// @method fcf.Actions catch(function a_cb)
1667- /// @brief Adds an error handler callback.
1668- /// @details If the callback returns or throws an error object,
1669- /// then it replaces the current fcf.Actions error
1670- /// @param function a_cb - Error handler callback
1671- /// - Has the following sigrature:
1672- /// undefined|Error a_cb(Error a_error)
1673- /// @result fcf.Actions - Self object
1674- /// @example
1675- /// (new fcf.Actions())
1676- /// .then(()=>{
1677- /// throw new Error("Some error");
1678- /// })
1679- /// .catch((a_error)=>{
1680- /// console.error(a_error.message);
1681- /// });
1682- ///
1683- /// // stderr: Some error
1684- catch(a_cb) {
1685- if (!this._stack)
1686- return;
1687- this._flags |= ACTIONS_FLAGS_CATCH;
1688- if (a_cb && this._error) {
1689- try {
1690- let e = a_cb.call(this, this._error);
1691- if (e instanceof Error) {
1692- this._error = e;
1693- }
1694- } catch(e) {
1695- this._error = e;
1310+ } else {
1311+ fcf.each(args[j], (a_key, a_value) => {
1312+ args[startArg].add(a_value);
1313+ });
16961314 }
1697- } else if (a_cb) {
1698- this._errorcbs.push(a_cb);
1699- }
1700- return this;
1701- }
1702-
1703-
1704-
1705- /// @method fcf.Actions finally(function a_cb)
1706- /// @brief Adds a callback to the run queue that is also called will be called on error
1707- /// @details When the a_cb function is called, this is set to fcf.Actions object
1708- /// When the handler is called when an error occurs,
1709- /// exceptions thrown from the handler are not processed and go out
1710- /// @param function a_cb - Action callback. The function can be asynchronous.
1711- /// - Has the following sigratures:
1712- /// - mixed a_cb(mixed a_lastResult) - Callback without completion confirmation.
1713- /// The callback is considered executed when the function has completed its execution.
1714- /// If the function returns a Promise (which is asynchronous) or an fcf.Actions object,
1715- /// then the operation completes when the Promise or fcf.Actions is executed.
1716- /// - mixed a_cb(mixed a_lastResult, fcf.Actions.Act a_act) - Callback with completion confirmation.
1717- /// The callback is considered executed when the fcf.Actions.Act.compete(mixed a_result)
1718- /// or fcf.Actions.Act.error(Error a_error) method is called on the a_act object
1719- /// @result fcf.Actions - Self object
1720- /// @example
1721- /// (new fcf.Actions())
1722- /// .then(()=>{
1723- /// ...
1724- /// })
1725- /// .finally(function (a_lastResult){
1726- /// if (this.error()){
1727- /// ...
1728- /// } else {
1729- /// ...
1730- /// }
1731- /// });
1732- finally(a_cb) {
1733- if (a_cb && this._error) {
1734- try {
1735- a_cb.call(this, undefined, {complete: ()=>{}, error: ()=>{}});
1736- } catch (e){
1737- this._error = e;
1738- }
1739- } else {
1740- this._stack.push({cb: a_cb, args: undefined, finally: true, autoComplete: fcf.getParamCount(a_cb) < 2 });
1741- this._execute();
1742- }
1743- return this;
1744- }
1745-
1746-
1747-
1748- /// @method fcf.Actions startup()
1749- /// @brief Starts execution of scheduled tasks if the fcf.Actions
1750- /// object was created with the deferred flag set to true
1751- /// @result fcf.Actions - Self object
1752- startup() {
1753- this._flags &= ~ACTIONS_FLAGS_DEFERRED;
1754- this._execute();
1755- return this;
1756- }
1757-
1758-
1759-
1760- /// @method Error error()
1761- /// @brief Returns the current error set in the object
1762- /// @result Error - Error object
1763-
1764- /// @method fcf.Actions error(Error a_error)
1765- /// @brief Sets an error for the fcf.Actions object
1766- /// @param Error a_error - Installable error
1767- /// @result fcf.Actions - Self object
1768- error(a_error) {
1769- if (!arguments.length) {
1770- return this._error;
1771- }
1772- if (this._error){
1773- if (a_error) {
1774- this._error = a_error;
1775- }
1776- return this;
1777- }
1778- this._error = a_error ? a_error : new Error("Unknown error");
1779- this._callErrors();
1780- return this;
1781- }
1782-
1783-
1784-
1785- /// @method mixed result()
1786- /// @brief Returns the current result set in the object
1787- /// @result mixed - Current result
1788-
1789- /// @method fcf.Actions result(mixed a_result)
1790- /// @brief Sets an result for the fcf.Actions object
1791- /// @param mixed a_result - New result value
1792- /// @result fcf.Actions - Self object
1793- result(a_value) {
1794- if (!arguments.length){
1795- return !this._error ? this._result : this._errorResult;
1796- }
1797- this._result = a_value;
1798- return this;
1799- }
1800-
1801-
1802-
1803- /// @method mixed exception()
1804- /// @brief Throws an exception if the object of actions is in a state of error
1805- /// @result fcf.Actions - Self object
1806- exception(){
1807- if (this._error)
1808- throw this._error;
1809- return this;
1810- }
1811-
1812-
1813-
1814- /// @method object options()
1815- /// @brief Returns the options of the fcf.Actions object
1816- /// @result object - fcf.Actions object options:
1817- /// - boolean deferred - If the flag is true, then the added callbacks will not be executed
1818- /// until the fcf.Actions.startup() method is called.
1819- /// - boolean noexcept - If the flag is true, then if the callback ends with an error,
1820- /// the queue execution is not stopped and the handlers passed to catch are not called.
1821- /// - mixed errorResult - The result returned by the result() method in case of an error
1822- /// - boolean quiet - If true, then raw error messages are not printed to the console.
1823-
1824- /// @method fcf.Actions options(object a_options)
1825- /// @brief Sets the options of the fcf.Actions object
1826- /// @param object a_options - An options object that can store the following options:
1827- /// - boolean deferred - If the flag is true, then the added callbacks will not be executed
1828- /// until the fcf.Actions.startup() method is called.
1829- /// - boolean noexcept - If the flag is true, then if the callback ends with an error,
1830- /// the queue execution is not stopped and the handlers passed to catch are not called.
1831- /// - mixed errorResult - The result returned by the result() method in case of an error
1832- /// - boolean quiet - If true, then raw error messages are not printed to the console.
1833- /// @result fcf.Actions - Self object
1834- options(a_options) {
1835- if (!arguments.length){
1836- return {
1837- noexcept: this._flags & ACTIONS_FLAGS_NOEXCEPT ? true : false,
1838- quiet: this._flags & ACTIONS_FLAGS_QUIET ? true : false,
1839- deferred: this._flags & ACTIONS_FLAGS_DEFERRED ? true : false,
1840- errorResult: this._errorResult
1841- };
1842- } else {
1843- if ("noexcept" in a_options){
1844- let b = a_options.noexcept ? ACTIONS_FLAGS_NOEXCEPT : 0;
1845- if (b) this._flags |= b;
1846- else this._flags &= ~b;
1847- }
1848- if ("quiet" in a_options){
1849- let b = a_options.quiet ? ACTIONS_FLAGS_QUIET : 0;
1850- if (b) this._flags |= b;
1851- else this._flags &= ~b;
1852- }
1853- if ("deferred" in a_options) {
1854- let b = a_options.deferred ? ACTIONS_FLAGS_DEFERRED : 0;
1855- if (b) this._flags |= b;
1856- else this._flags &= ~b;
1857- }
1858- if ("errorResult" in a_options) {
1859- this._errorResult = a_options.errorResult;
1860- }
1861- return this;
1862- }
1863- }
1864-
1865-
1866-
1867- /// @method Promise promise()
1868- /// @brief Adds an empty Promise object to the run queue and returns it
1869- /// @result Promise - A new Promise object
1870- promise() {
1871- return new Promise((a_resolve, a_reject)=>{
1872- this
1873- .then((a_res)=>{
1874- a_resolve(a_res);
1875- })
1876- .catch((a_error)=>{
1877- a_reject(a_error);
1878- })
1879- })
1880- }
1881-
1882- _callErrors() {
1883- for (let i = 0; i < this._errorcbs.length; ++i){
1884- try {
1885- let e = this._errorcbs[i].call(this, this._error);
1886- if (e instanceof Error) {
1887- this._error = e;
1315+ }
1316+ } else if (args[startArg] instanceof Map) {
1317+ for(let j = startArg + 1; j < args.length; ++j) {
1318+ if (typeof args[j] !== "object" || args[j] === null)
1319+ continue;
1320+ if (req) {
1321+ if (args[j] instanceof Map) {
1322+ for(let pair of args[j]) {
1323+ args[startArg].set(pair[0], cloneValue(pair[1]));
1324+ }
1325+ } else {
1326+ fcf.each(args[j], (a_key, a_value) => {
1327+ args[startArg].set(a_key, cloneValue(a_value));
1328+ });
18881329 }
1889- } catch(e) {
1890- this._error = e;
1891- }
1892- }
1893- for (let i = 0; i < this._stack.length; ++i){
1894- if (this._stack[i].finally){
1895- try {
1896- this._stack[i].cb.call(this, undefined, {complete: ()=>{}, error: ()=>{}});
1897- } catch(e) {
1898- this._error = e;
1330+ } else {
1331+ if (args[j] instanceof Map) {
1332+ for(let pair of args[j]) {
1333+ args[startArg].set(pair[0], pair[1]);
1334+ }
1335+ } else {
1336+ fcf.each(args[j], (a_key, a_value) => {
1337+ args[startArg].set(a_key, a_value);
1338+ });
18991339 }
19001340 }
19011341 }
1902- if (!(this._flags & ACTIONS_FLAGS_QUIET)) {
1903- if (!(this._flags & ACTIONS_FLAGS_CATCH)){
1904- setTimeout(()=>{
1905- if (!(this._flags & ACTIONS_FLAGS_CATCH)){
1906- fcf.log.err("FCF", "Unhandled error in fcf.Actions (to handle catch method or \"quiet\" flag).", this._error)
1342+ } else if (typeof args[startArg] === "object" && args[startArg] !== null) {
1343+ for(let j = startArg + 1; j < args.length; ++j) {
1344+ if (typeof args[j] !== "object" || args[j] === null)
1345+ continue;
1346+ if (req) {
1347+ if (!Array.isArray(args[j]) && !(args[j] instanceof Map) && !(args[j] instanceof Set)) {
1348+ for(let key of Object.getOwnPropertyNames(args[j])) {
1349+ args[startArg][key] = cloneValue(args[j][key]);
19071350 }
1908- }, 0);
1909- }
1910- }
1911- }
1912-
1913- _execute() {
1914- let self = this;
1915- if (_isServer)
1916- fcf.setState(self._state);
1917- if (this._flags & ACTIONS_FLAGS_RUN || !this._stack || this._stack.length == 0 || this._error)
1918- return;
1919- if (this._flags & ACTIONS_FLAGS_DEFERRED)
1920- return;
1921- this._flags |= ACTIONS_FLAGS_RUN;
1922- let cbi = this._stack.shift();
1923- let act = {
1924- _end: false,
1925- complete: function(a_value) {
1926- if (_isServer)
1927- fcf.setState(self._state);
1928- if (this._end || self._error)
1929- return;
1930- this._end = true;
1931- self._flags &= ~ACTIONS_FLAGS_RUN;
1932- if ((typeof Promise !== "undefined" && a_value instanceof Promise) || a_value instanceof fcf.Actions){
1933- a_value
1934- .then((a_value)=>{
1935- if (!cbi.finally)
1936- self._result = a_value;
1937- self._execute();
1938- })
1939- .catch((a_error)=>{
1940- this._end = false;
1941- this.error(a_error);
1942- })
19431351 } else {
1944- if (!cbi.finally)
1945- self._result = a_value;
1946- self._execute();
1947- }
1948- },
1949- error: function(a_error) {
1950- if (_isServer)
1951- fcf.setState(self._state);
1952- if (self._flags & ACTIONS_FLAGS_NOEXCEPT){
1953- this.complete();
1954- return;
1352+ fcf.each(args[j], (a_key, a_value) => {
1353+ args[startArg][a_key] = cloneValue(a_value);
1354+ });
19551355 }
1956- if (this._end || self._error)
1957- return;
1958- this._end = true;
1959- self._flags &= ~ACTIONS_FLAGS_RUN;
1960- self._result = undefined;
1961- self._error = a_error ? a_error : new Error("Unknown error");
1962- self._callErrors();
1963- },
1964- };
1965- let args = [];
1966- if (cbi.args) {
1967- args.push(cbi.args[0]);
1968- args.push(cbi.args[1]);
1969- }
1970- if (!cbi.skipResult) {
1971- args.push(this._result);
1972- }
1973- if (!cbi.autoComplete) {
1974- args.push(act);
1975- }
1976-
1977- let res = undefined;
1978- let forceExit = false;
1356+ } else {
1357+ if (!Array.isArray(args[j]) && !(args[j] instanceof Map) && !(args[j] instanceof Set)) {
1358+ for(let key of Object.getOwnPropertyNames(args[j])) {
1359+ args[startArg][key] = args[j][key];
1360+ }
1361+ } else {
1362+ fcf.each(args[j], (a_key, a_value)=>{
1363+ args[startArg][a_key] = a_value;
1364+ });
1365+ }
1366+ }
1367+ }
1368+ }
1369+ return args[startArg];
1370+ }
1371+
1372+ const cloneValue = (a_source) => {
1373+ if (a_source === null) {
1374+ return null;
1375+ } else if (a_source instanceof Date) {
1376+ return new Date(a_source);
1377+ } else if (Array.isArray(a_source)) {
1378+ return fcf.append(true, [], a_source);
1379+ } else if (typeof a_source === "object") {
1380+ let base;
19791381 try {
1980- let asyncPrefix = cbi.cb.toString().substr(0, 5);
1981- if (asyncPrefix == "async" && cbi.cb.toString().charCodeAt(5) <= 32){
1982- res = cbi.cb.apply(this, args).catch((e)=>{
1983- forceExit = true;
1984- act.error(e);
1985- });
1986- } else {
1987- res = cbi.cb.apply(this, args);
1988- }
1382+ base = new a_source.__proto__.constructor();
19891383 } catch(e) {
1990- act.error(e);
1991- return;
1992- }
1993-
1994- if (forceExit)
1995- return;
1996-
1997- if ((typeof Promise !== "undefined" && res instanceof Promise) || res instanceof fcf.Actions) {
1998- if (cbi.autoComplete){
1999- res
2000- .then((a_value)=>{
2001- act.complete(a_value);
2002- })
2003- .catch((a_error)=>{
2004- act.error(a_error);
2005- });
2006- } else {
2007- res
2008- .catch((a_error)=>{
2009- act.error(a_error);
2010- });
2011- }
2012- } else if (cbi.autoComplete){
2013- act.complete(res);
2014- }
1384+ base = {};
1385+ }
1386+ return fcf.append(true, base, a_source);
1387+ } else {
1388+ return a_source;
20151389 }
20161390 }
20171391
2018- const ACTIONS_FLAGS_DEFERRED = 1;
2019- const ACTIONS_FLAGS_NOEXCEPT = 2;
2020- const ACTIONS_FLAGS_RUN = 4;
2021- const ACTIONS_FLAGS_QUIET = 8;
2022- const ACTIONS_FLAGS_CATCH = 16;
2023-
2024-
2025-
2026- /// @fn fcf.module(object a_options)
2027- /// @brief Declares the FCF module available on the server side and on the browser side
2028- /// @details JS files act as module dependencies. If the dependency file does not use the fcf.module() declaration, then the module data is:
2029- /// - On the NODEJS server side: Data exported to the module.exports variable
2030- /// - On the browser side: A global variable that is declared in the configuration for a module
2031- /// @param object a_options - Module options
2032- /// - string name - Path (name) of the module in FCF notation
2033- /// - array dependencies - Array with module dependency paths in FCF notation
2034- /// - array lazy - An array of module dependencies that are loaded after the module body is executed.
2035- /// These modules do not fall into the module arguments and
2036- /// these dependencies are accessed through global variables.
2037- /// Used for cyclic module dependencies.
2038- /// - function module - A module function that should return module data.
2039- /// The function arguments are the data of the modules declared in the dependencies property.
2040- /// - Function signature: mixed module(<DEPENDENCY1, DEPENDENCY2, ...>)
2041- /// @example
2042- /// ========================================================================
2043- /// Browser side example (Using native JS files on the browser side)
2044- ///
2045- /// ------------------------------------------------------------------------
2046- /// File /modules/package/helloWorld.js:
2047- ///
2048- /// window.someGlobalVariable = window.someGlobalVariable || {};
2049- /// window.someGlobalVariable.helloWorld = function(){
2050- /// document.write("<h3>Hello world</h3>")
2051- /// }
2052- ///
2053- /// ------------------------------------------------------------------------
2054- /// File /modules/package/page.js:
2055- ///
2056- /// fcf.module({
2057- /// name: "package:page.js",
2058- /// dependencies: ["package:helloWorld.js"],
2059- /// module: (helloWorld)=>{
2060- /// return function() {
2061- /// document.write("<h1>Title</h1>");
2062- /// helloWorld();
2063- /// };
2064- /// }
2065- /// });
2066- ///
2067- /// ------------------------------------------------------------------------
2068- /// File /index.html
2069- ///
2070- /// <html>
2071- /// <head>
2072- /// <script src="/node_modules/fcf-framework-core/fcf.js"></script>
2073- /// <script>
2074- /// // Adding data about the new "package" package to the configuration
2075- /// fcf.configuration.append({
2076- /// webModules: {
2077- ///
2078- /// // Information about the "package" package
2079- /// "package": {
2080- ///
2081- /// // URL to package files
2082- /// path: "/modules/package",
2083- ///
2084- /// // Mapping the module and the global variable as a result of module execution
2085- /// results: {
2086- /// "helloWorld.js": "someGlobalVariable.helloWorld",
2087- /// },
2088- ///
2089- /// }
2090- /// }
2091- /// });
2092- /// </script>
2093- /// </head>
2094- /// <body>
2095- /// <script>
2096- /// fcf.require("package:page.js")
2097- /// .then(([page])=>{
2098- /// page();
2099- /// });
2100- /// </script>
2101- /// </body>
2102- /// </html>
2103- ///
2104- /// ------------------------------------------------------------------------
2105- /// Result in browser:
2106- ///
2107- /// Title
2108- ///
2109- /// Hello world
2110- ///
2111- fcf.module = (a_options) => {
2112- let moduleName = _getModulePath(a_options.name);
2113- let rootCall = false;
2114-
2115- if (!_modules[moduleName]){
2116- _modules[moduleName] = {
2117- state: "wait",
2118- result: undefined,
2119- error: undefined,
2120- actions: fcf.actions(),
2121- act: undefined,
2122- };
2123- rootCall = true;
2124- } else if (_modules[moduleName].state != "wait") {
2125- _modules[moduleName].dependencies = Array.isArray(a_options.dependencies) ? a_options.dependencies : [];
2126- _modules[moduleName].lazy = Array.isArray(a_options.lazy) ? a_options.lazy : [];
2127- return;
2128- }
2129-
2130- _modules[moduleName].dependencies = Array.isArray(a_options.dependencies) ? a_options.dependencies : [];
2131- _modules[moduleName].lazy = Array.isArray(a_options.lazy) ? a_options.lazy : [];
2132-
2133- let moduleInfo = _modules[moduleName];
2134- moduleInfo.state = "processing";
2135-
2136- fcf.each(a_options.dependencies, (a_key, a_value)=>{
2137- a_options.dependencies[a_key] = fcf.ltrim(a_value, ":");
2138- });
2139-
2140- let actions = rootCall ? _modules[moduleName].actions
2141- : fcf.actions();
2142-
2143- return actions
2144- .asyncEach(a_options.dependencies, (k, mod)=>{
2145- return _loadModule(mod, _quietModuleError);
2146- })
2147- .then(()=>{
2148- let dependencies = [];
2149- fcf.each(a_options.dependencies, (k, mod)=>{
2150- dependencies.push(_modules[_getModulePath(mod)].result);
2151- })
2152- moduleInfo.result = a_options.module.apply(undefined, dependencies);
2153- if (_isServer) {
2154- require.cache[moduleName].exports = moduleInfo.result;
2155- }
2156- moduleInfo.state = "ok";
2157- fcf.actions()
2158- .asyncEach(a_options.lazy, (k, mod)=>{
2159- return _loadModule(mod, _quietModuleError);
2160- })
2161- if (moduleInfo.act){
2162- moduleInfo.act.complete();
2163- }
2164- })
2165- .catch((a_error)=>{
2166- moduleInfo.state = "error";
2167- moduleInfo.error = a_error;
2168- if (moduleInfo.act)
2169- moduleInfo.act.error(a_error);
2170- if (_isServer)
2171- throw a_error;
2172- })
2173- }
2174-
2175-
2176-
2177- fcf.require = function (a_modules) {
2178- let quietError = false;
2179- let args = [];
2180- for(let arg of arguments){
2181- if (typeof arg == "object" && arg !== null && !Array.isArray(arg)) {
2182- quietError = "quiet" in arg && _isServer ? arg.quiet : false;
2183- } else if (typeof arg === "string") {
2184- args.push(arg);
2185- }
2186- }
2187- let modules = args.length > 1 ? args :
2188- Array.isArray(a_modules) ? a_modules :
2189- typeof a_modules == "string" ? [a_modules] :
2190- [];
2191- _quietModuleError = quietError;
2192- return fcf.actions({errorResult: []})
2193- .asyncEach(modules, (k, mod) => {
2194- return _loadModule(mod, quietError);
2195- })
2196- .then(()=>{
2197- let result = [];
2198- fcf.each(modules, (k, mod) => {
2199- result.push(_modules[_getModulePath(mod)].result);
2200- });
2201- return result;
2202- })
2203- .finally(()=>{
2204- _quietModuleError = false;
2205- })
2206- }
2207-
2208- function _loadModule(a_module, a_quietError) {
2209- let path = _getModulePath(a_module);
2210- let state = _modules[path] ? _modules[path].state
2211- : undefined;
2212- if (state == "ok") {
2213- return fcf.actions()
2214- .then(()=> {
2215- return _modules[path].result;
2216- });
2217- } else if (state == "error") {
2218- return fcf.actions()
2219- .then((a_res, a_act)=> {
2220- a_act.error(_modules[path].error);
2221- });
2222- } else if (state == "wait" || state == "processing") {
2223- return fcf.actions()
2224- .then((a_res, a_act)=> {
2225- _modules[path].actions
2226- .then(()=>{
2227- a_act.complete(_modules[path].result);
2228- })
2229- .catch((a_error)=>{
2230- a_act.error(a_error);
2231- })
2232- });
1392+
1393+ /// @fn mixed fcf.clone(mixed a_value)
1394+ /// @brief Creates a copy of an object
1395+ /// @param mixed a_value - Source value
1396+ /// @result mixed - Returns a clone of a_value
1397+ fcf.clone = (a_object) => {
1398+ if (a_object === null) {
1399+ return null;
1400+ } else if (Array.isArray(a_object)) {
1401+ return fcf.append(true, [], a_object);
1402+ } else if (a_object instanceof Date) {
1403+ return new Date(a_object);
1404+ } else if (typeof a_object == "object") {
1405+ let base;
1406+ try {
1407+ base = new a_object.__proto__.constructor();
1408+ } catch(e){
1409+ base = {};
1410+ }
1411+ return fcf.append(true, base, a_object);
22331412 } else {
2234- _modules[path] = {
2235- state: "wait",
2236- result: undefined,
2237- error: undefined,
2238- actions: fcf.actions(),
2239- act: undefined,
2240- };
2241-
2242- return fcf.actions()
2243- .then((a_res, a_rootAct)=>{
2244- _modules[path].actions
2245- .then((a_res, a_act)=>{
2246- _modules[path].act = a_act;
2247- if (_isServer) {
2248- let result;
2249- try {
2250- result = require(path);
2251- }catch(e) {
2252- _modules[path].act.error(e);
2253- return;
2254- }
2255- _modules[path].result = result;
2256- if (_modules[path].state == "wait"){
2257- _modules[path].act.complete();
2258- }
2259- } else {
2260- let script = document.createElement('script');
2261- let moduleArr = a_module.split(":");
2262- let moduleName = moduleArr[0];
2263- let filePath = moduleArr[1];
2264- let moduleInfo = fcf.configuration.webModules[moduleName];
2265- if (moduleInfo){
2266- if (typeof moduleInfo !== "object") {
2267- moduleInfo = {path: moduleInfo};
2268- }
2269- if (!filePath){
2270- filePath = moduleInfo.main;
2271- }
2272- }
2273- script.onerror = ()=>{
2274- _modules[path].act.error(new Error("Failed to receive module file '" + path + "'"));
2275- }
2276- script.onload = () => {
2277- if (moduleInfo){
2278- if (moduleInfo.results &&
2279- typeof moduleInfo.results == "object" &&
2280- filePath in moduleInfo.results){
2281- _modules[path].result = fcf.resolve(window, moduleInfo.results[filePath]);
2282- }
2283- }
2284- if (_modules[path].state == "wait"){
2285- _modules[path].act.complete();
2286- }
2287- }
2288- let loadPath = (path[0] != "/" && path.indexOf(":") == -1) ? ":" + path : path;
2289- let src = fcf.getPath(loadPath);
2290- if (moduleInfo && moduleInfo.version) {
2291- src += moduleInfo.version.indexOf("?") == -1 ? "?version=" + encodeURIComponent(fcf.str(moduleInfo.version))
2292- : "&version=" + encodeURIComponent(fcf.str(moduleInfo.version));
2293- }
2294- script.src = src;
2295- document.head.appendChild(script);
2296- }
2297- })
2298- .then(()=>{
2299- a_rootAct.complete(_modules[path].result);
2300- })
2301- .catch((a_error) => {
2302- if (!a_quietError) {
2303- fcf.log.err("FCF", `Failed load module ${a_module}`, a_error);
2304- }
2305- a_rootAct.error(a_error);
2306- })
2307- });
1413+ return a_object;
23081414 }
23091415 }
23101416
2311- function _getModulePath(a_module){
2312- return _isServer &&
2313- a_module.indexOf(":") == -1 &&
2314- a_module.indexOf(".") == -1 &&
2315- a_module.indexOf("/") == -1 &&
2316- a_module.indexOf("\\") == -1 ? a_module // this is nodejs module
2317- : fcf.getPath(a_module);
2318- }
2319-
2320- let _modules = {};
2321- let _quietModuleError = false;
2322-
2323-
2324- // CACHING CLASSES
2325-
2326-
2327-
2328- fcf.prepare = (a_root, a_objectPath) => {
2329- a_objectPath = fcf.trim(a_objectPath, "/");
2330- if (fcf.empty(a_objectPath))
2331- return a_root;
2332-
2333- let items;
2334- if (typeof a_objectPath == "string") {
2335- items = a_objectPath.split("/").join(".").split(".");
2336- } else if (Array.isArray(a_objectPath)) {
2337- items = a_objectPath;
2338- } else {
2339- return a_root;
2340- }
2341-
2342- let obj = a_root;
2343- for(var i = 0; i < items.length; ++i){
2344- if (!(items[i] in obj))
2345- obj[items[i]] = {};
2346- obj = obj[items[i]];
2347- }
2348- return obj;
2349- }
2350-
2351- fcf.empty = (a_object) => {
2352- if (a_object instanceof Error){
2353- return false;
2354- } else if (a_object === undefined || a_object === null){
2355- return true;
2356- } else if (typeof a_object === "string") {
2357- return a_object == "";
2358- } else if (Array.isArray(a_object)) {
2359- return a_object.length === 0;
2360- } else if (!_isServer && a_object instanceof NodeList) {
2361- return a_object.length === 0;
2362- } else if (typeof a_object === "object") {
2363- for(var k in a_object)
2364- return false;
2365- return true;
2366- } else if (typeof a_object === "number") {
2367- return isNaN(a_object);
2368- }
2369- return false;
2370- }
2371-
2372-
2373- /// @class fcf.Cache
2374- /// @brief A simple class for caching results
2375- /// @tests
2376- /// Updating an item in a cache with 1MB (a_mcapacity) (2912 items) takes approximately ~2 us
2377- /// (keys and values: 0 - 1000000 as string).
2378- /// Updating an item in a cache with 0.1MB (a_mcapacity) (284 items) takes approximately ~0.4 us
2379- /// (keys and values: 0 - 1000000 as string).
2380- /// OS: LINUX
2381- /// Node version: v18.11.0:
2382- /// CPU: Intel(R) Core(TM) i5-2400 CPU @ 3.10GHz
2383- fcf.SimpleCache = class {
2384-
2385- constructor(a_mcapacity, a_capacity) {
2386- this._mcapacity = !isNaN(a_mcapacity) && parseInt(a_mcapacity) > 0 ? a_mcapacity : 1000000;
2387- this._capacity = !isNaN(a_capacity) && parseInt(a_capacity) > 0 ? a_capacity : Infinity;
2388- this._cachem = new Map();
2389- this._msize = 0;
2390- }
2391-
2392- has(a_key){
2393- a_key = a_key.toString();
2394- return this._cachem.has(a_key);
2395- }
2396-
2397- get(a_key){
2398- a_key = a_key.toString();
2399- let rec = this._cachem.get(a_key);
2400- return rec ? rec.v : undefined;
2401- }
2402-
2403- set(a_key, a_value) {
2404- a_key = a_key.toString();
2405- let rec = this._cachem.get(a_key);
2406- if (rec) {
2407- this._msize -= rec.s;
2408- this._cachem.delete(a_key);
2409- }
2410- let s = fcf.getApproximateMSize(a_key) + fcf.getApproximateMSize(a_value);
2411- this._cachem.set(a_key, { v: a_value, s: s });
2412- this._msize += s;
2413- this._clear();
2414- }
2415-
2416- getSize(){
2417- return this._cachem.size;
2418- }
2419-
2420- getMSize(){
2421- return this._msize;
2422- }
2423-
2424- getCapacity() {
2425- return this._capacity;
2426- }
2427-
2428- setCapacity(a_capacity) {
2429- this._capacity = a_capacity;
2430- this._clear();
2431- }
2432-
2433- getMCapacity() {
2434- return this._mcapacity;
2435- }
2436-
2437- setMCapacity(a_mcapacity) {
2438- this._mcapacity = a_mcapacity;
2439- this._clear();
2440- }
2441-
2442- _clear() {
2443- while(this._msize > this._mcapacity || this._cachem.size > this._capacity) {
2444- let rkey = this._cachem.entries().next().value[0];
2445- let rec = this._cachem.get(rkey);
2446- this._msize -= rec.s;
2447- this._cachem.delete(rkey);
2448- }
2449- }
2450-
2451- };
2452-
2453-
2454- //
2455- // TOKENIZE & JS EXECUTION FUNCTIONS
2456- //
2457-
2458- //----------------------------------------------------------------------------
2459-
2460-
2461-
2462-
2463-
2464-
2465-
2466-
2467-
2468-
2469- fcf.getDirectory = (a_path) => {
2470- if (a_path.indexOf("/") != -1) {
2471- let arr = a_path.split("/");
2472- arr.pop();
2473- a_path = arr.join("/");
2474- } else if (a_path.indexOf("\\") != -1){
2475- arr = a_path.split("\\");
2476- arr.pop();
2477- a_path = arr.join("\\");
2478- } else {
2479- a_path = "";
2480- }
2481- return a_path;
2482- }
2483-
2484- fcf.getExtension = (a_path) => {
2485- if (!a_path)
2486- return "";
2487- a_path = a_path.split("?")[0];
2488- var arr = a_path.split(".");
2489- return arr.length > 1 ? arr[arr.length-1] : "";
2490- }
2491-
2492- fcf.getShortFileName = (a_path) => {
2493- if (!a_path)
2494- return "";
2495- var offset = -1;
2496- for (var p = 0; p < a_path.length; ++p) {
2497- var c = a_path.charAt(p);
2498- if (c == ":" || c == "/" || c == "\\")
2499- offset = p;
2500- }
2501-
2502- let arr = a_path.substr(offset == -1 ? 0 : offset+1).split(".");
2503- if (arr.length > 1)
2504- arr.pop();
2505- return arr.join(".");
2506- }
2507-
2508- fcf.getFileName = (a_path) => {
2509- a_path = fcf.rtrim(a_path, "/");
2510- if (!a_path)
2511- return "";
2512- var offset = 0;
2513- for (var p = 0; p < a_path.length; ++p) {
2514- var c = a_path.charAt(p);
2515- if (c == ":" || c == "/" || c == "\\")
2516- offset = p;
2517- }
2518-
2519- if (offset)
2520- offset += 1;
2521-
2522- return a_path.substr(offset);
2523- }
2524-
2525- fcf.resolvePath = function(a_uri, a_aliases){
2526- if (a_uri[0] != "@")
2527- return a_uri;
2528- let pos = a_uri.indexOf("+");
2529- let prefix = pos != -1 ? a_uri.substring(1, pos) : a_uri.substring(1);
2530- let suffix = pos != -1 ? a_uri.substring(pos) : "";
2531- if (typeof a_aliases == "object" && prefix in a_aliases){
2532- a_uri = a_aliases[prefix] + suffix;
2533- } else if (fcf.application && prefix in fcf.application.getConfiguration().aliases) {
2534- a_uri = fcf.application.getConfiguration().aliases[prefix] + suffix;
2535- }
2536- return a_uri;
2537- }
2538-
2539- //fcf.getPath = function(a_modURI, a_innerServerPath);
2540- fcf.getPath = function(a_uri, a_aliases, a_innerServerPath){
2541- a_uri = fcf.str(a_uri);
2542-
2543- if (typeof a_aliases !== "object") {
2544- a_innerServerPath = a_aliases;
2545- a_aliases = undefined;
2546- }
2547-
2548- if (a_innerServerPath === undefined) {
2549- a_innerServerPath = _isServer;
2550- }
2551-
2552- if (a_uri.charAt(0) == "@"){
2553- let pos;
2554- if (a_innerServerPath){
2555- let plusPos = a_uri.indexOf("+");
2556- if (plusPos == -1)
2557- plusPos = Number.MAX_VALUE;
2558- let hooksExtPos = a_uri.indexOf(".hooks.js");
2559- if (hooksExtPos == -1)
2560- hooksExtPos = Number.MAX_VALUE;
2561- let wrapperExtPos = a_uri.indexOf(".wrapper.js");
2562- if (wrapperExtPos == -1)
2563- wrapperExtPos = Number.MAX_VALUE;
2564- let slashPos = a_uri.indexOf("/");
2565- if (slashPos == -1)
2566- slashPos = Number.MAX_VALUE;
2567- let bslashPos = a_uri.indexOf("\\");
2568- if (bslashPos == -1)
2569- bslashPos = Number.MAX_VALUE;
2570- pos = Math.min(plusPos, hooksExtPos, wrapperExtPos, slashPos, bslashPos);
2571- if (pos == Number.MAX_VALUE)
2572- pos = undefined;
2573- } else {
2574- let plusPos = a_uri.indexOf("+");
2575- if (plusPos == -1)
2576- plusPos = Number.MAX_VALUE;
2577- let hooksExtPos = a_uri.indexOf(".hooks.js");
2578- if (hooksExtPos == -1)
2579- hooksExtPos = Number.MAX_VALUE;
2580- let wrapperExtPos = a_uri.indexOf(".wrapper.js");
2581- if (wrapperExtPos == -1)
2582- wrapperExtPos = Number.MAX_VALUE;
2583- let slashPos = a_uri.indexOf("/");
2584- if (slashPos == -1)
2585- slashPos = Number.MAX_VALUE;
2586- let bslashPos = a_uri.indexOf("\\");
2587- if (bslashPos == -1)
2588- bslashPos = Number.MAX_VALUE;
2589- let argsPos = a_uri.indexOf("?");
2590- if (argsPos == -1)
2591- argsPos = Number.MAX_VALUE;
2592- let hashPos = a_uri.indexOf("#");
2593- if (hashPos == -1)
2594- hashPos = Number.MAX_VALUE;
2595- pos = Math.min(plusPos, hooksExtPos, wrapperExtPos, slashPos, bslashPos, argsPos, hashPos);
2596- if (pos == Number.MAX_VALUE)
2597- pos = undefined;
2598- }
2599- let prefix = a_uri.substring(1, pos);
2600- let suffix = pos !== undefined ? a_uri.substring(pos) : "";
2601- if (typeof a_aliases == "object" && prefix in a_aliases){
2602- a_uri = a_aliases[prefix] + suffix;
2603- if (!a_innerServerPath && a_aliases[prefix] == -1){
2604- a_uri = "/" + fcf.ltrim(a_uri, "/");
2605- }
2606- } else if (fcf.application && prefix in fcf.application.getConfiguration().aliases) {
2607- a_uri = fcf.application.getConfiguration().aliases[prefix] + suffix;
2608- if (!a_innerServerPath && fcf.application.getConfiguration().aliases[prefix].indexOf(":") == -1){
2609- a_uri = "/" + fcf.ltrim(a_uri, "/");
2610- }
2611- } else {
2612- throw new fcf.Exception("ERROR_GET_PATH", {path: a_uri});
2613- }
2614- }
2615-
2616- if (a_innerServerPath) {
2617- let winDSPos = a_uri.indexOf(":\\");
2618- if (a_uri[0] == "/" || (winDSPos != -1 && winDSPos < 2))
2619- return a_uri;
2620- } else {
2621- if (a_uri.indexOf("://") != -1)
2622- return a_uri;
2623- }
2624-
2625- let modulePos = a_uri.indexOf(":");
2626- let moduleName = modulePos != -1 ? a_uri.substring(0, modulePos) : "";
2627- let relativePath = modulePos != -1 ? a_uri.substring(modulePos + 1) : a_uri;
2628- if (moduleName.indexOf("/") != -1 || moduleName.indexOf("\\") != -1){
2629- modulePos = -1;
2630- relativePath = a_uri;
2631- moduleName = "";
2632- }
2633-
2634- if (!a_innerServerPath) {
2635- if (moduleName == "" &&
2636- relativePath.indexOf("/") == -1 &&
2637- relativePath.indexOf(".") == -1) {
2638- moduleName = relativePath;
2639- let modInfo = fcf.configuration.webModules[moduleName];
2640- if (modInfo || typeof modInfo != "object"){
2641- modInfo = {path: modInfo};
2642- }
2643- relativePath = modInfo.main ? modInfo.main : "index.js";
2644- }
2645- }
2646-
2647- if (moduleName != "") {
2648- if (a_innerServerPath){
2649- let modulePath = libResolver.resolveModule(moduleName);
2650- if (!modulePath){
2651- throw new fcf.Exception("ERROR_GET_PATH_INVALID_MODULE_NAME", { path: a_uri});
2652- }
2653- a_uri = libPath.join(modulePath, fcf.ltrim(relativePath, ["/", "\\", ":"]));
2654- } else {
2655- let prefix = "";
2656- let modInfo = fcf.configuration.webModules[moduleName];
2657- if (typeof modInfo == "string"){
2658- modInfo = {path: modInfo};
2659- }
2660- if (modInfo){
2661- prefix += fcf.rtrim(modInfo.path, "/");
2662- }
2663- if (prefix){
2664- prefix += "/";
2665- }
2666- a_uri = prefix + fcf.ltrim(relativePath, ["/", "\\", ":"]);
2667- }
2668- } else {
2669- if (a_innerServerPath){
2670- a_uri = process.cwd() + "/" + fcf.ltrim(a_uri, ["/", "\\", ":"]);
2671- } else {
2672- if (modulePos == 0) {
2673- a_uri = "/" + fcf.ltrim(a_uri, ["/", "\\", ":"]);
2674- }
2675- }
2676- }
2677-
2678- return a_uri;
2679- }
2680-
2681-
2682-
2683-
2684- //----------------------------------------------------------------------------
2685-
2686- fcf.translate = (a_str, a_language) => {
2687- try {
2688- if (typeof a_str == "string") {
2689- return fcf._translate(a_str, a_language);
2690- } else if (typeof a_str == "object") {
2691- a_str = fcf.clone(a_str);
2692- fcf.each(a_str, (k,v) => {
2693- a_str[k] = fcf.translate(a_str[k], a_language);
2694- });
2695- return a_str;
2696- } else {
2697- return a_str;
2698- }
2699- } catch (e) {
2700- return "";
2701- }
2702- }
2703-
2704- fcf._translate = (a_str, a_language) => {
2705- var result = "";
2706- var buff = "";
2707- var state = 0
2708- var start = undefined;
2709- a_str = fcf.str(a_str);
2710- for (var i = 0; i < a_str.length; ++i) {
2711- var c = a_str.charAt(i);
2712- if (state == 0){
2713- if (c == "!") { start = i; state = 1;}
2714- else { result += c; }
2715- } else if (state == 1) {
2716- if (c == "{") { state = 2; buff = ""; }
2717- else { result += "!" + c; state = 0; }
2718- } else if (state == 2) {
2719- if (c == "}") {
2720- if (a_str.charAt(i+1) == "!"){
2721- state = 0;
2722- if (buff[0] == "{" && buff[buff.length-1] == "}")
2723- buff = buff.substr(1, buff.length-2);
2724- result += fcf.t(buff, a_language);
2725- ++i;
2726- } else {
2727- buff += c;
2728- }
1417+
1418+
1419+ /// @fn [string|object] fcf.parseObjectAddress(string a_path, boolean a_exMode = false)
1420+ /// @brief Converts a string with the address of an object to an array with information about the elements
1421+ /// @details The address format can take the following form:
1422+ /// - Dot separated fields: "field.subfield.subsubfield"
1423+ /// - Fields enclosed in brackets: "field[\"subfield\"][\"subsubfield\"]"
1424+ /// - The address containing the array is enclosed in double quotes: "field[[\"subarray\"]][0]"
1425+ /// @param string a_path - Object path
1426+ /// @param boolean a_exMode = false - If it is true, then the function returns an array of objects with information
1427+ /// about the elements, each element contains the part field - the name of the field and
1428+ /// the array field which is true if an array is given
1429+ /// @result [string|object] - Returns an array with fields.
1430+ /// - If the a_exMode option is true, then each element contains an object with fields:
1431+ /// - string part - field name
1432+ /// - boolean array - is true if the element is an array
1433+ /// - If the a_exMode argument is false then the array elements are strings with field names
1434+ fcf.parseObjectAddress = (a_path, a_exMode) => {
1435+ if (Array.isArray(a_path)){
1436+ let result = [];
1437+ for(let part of a_path) {
1438+ if (a_exMode){
1439+ result.push(typeof part == "object" ? part : {part: part, array: false});
27291440 } else {
2730- buff += c;
1441+ result.push(typeof part == "object" ? part.part : part);
27311442 }
27321443 }
2733- }
2734- return result;
2735- }
2736-
2737- fcf.tokenize = (a_ctxtptr, a_obj, a_escape) => {
2738- if (!fcf.getContext())
2739- fcf.setContext(new fcf.Context());
2740-
2741- if (a_ctxtptr instanceof fcf.StringPointer){
2742- return fcf._tokenizePtrEx(ctxtptr, a_obj, a_escape).data
2743- } else if (typeof a_ctxtptr == "object"){
2744- a_ctxtptr = fcf.clone(a_ctxtptr);
2745- fcf.each(a_ctxtptr, (k, v) => {
2746- a_ctxtptr[k] = fcf.tokenize(v, a_obj, a_escape);
2747- })
2748- return a_ctxtptr;
2749- } else if (typeof a_ctxtptr == "string") {
2750- if (a_ctxtptr.indexOf("{{") == -1)
2751- return a_ctxtptr;
2752- var ctxtptr = new fcf.StringPointer(a_ctxtptr);
2753- return fcf._tokenizePtrEx(ctxtptr, a_obj, a_escape).data;
2754- } else {
2755- return a_ctxtptr;
2756- }
2757- }
2758-
2759- fcf.tokenizeObject = (a_object, a_args, a_escape) => {
2760- var result = Array.isArray(a_object) ? [] : Object.create(Object.getPrototypeOf(a_object));
2761-
2762- if (fcf.isArg(a_object) && typeof a_object.level === "number" && a_object.level > 1)
2763- return fcf.clone(a_object);
2764-
2765- if (Array.isArray(a_object)){
2766- for(var i = 0; i < a_object.length; ++i){
2767- var v = a_object[i];
2768- if (typeof v === "object" && v !== null) {
2769- result[i] = fcf.tokenizeObject(v, a_args, a_escape);
2770- } else if (typeof v === "string") {
2771- result[i] = fcf.tokenize(v, a_args, a_escape);
2772- } else {
2773- result[i] = v;
2774- }
2775- }
2776- } else {
2777- for(var k in a_object) {
2778- var v = a_object[k];
2779- if (typeof v === "object" && v !== null) {
2780- if (typeof k == "string")
2781- k = fcf.tokenize(k, a_args, a_escape);
2782- result[k] = fcf.tokenizeObject(v, a_args, a_escape);
2783- } else if (typeof v === "string") {
2784- if (typeof k == "string")
2785- k = fcf.tokenize(k, a_args, a_escape);
2786- result[k] = fcf.tokenize(v, a_args, a_escape);
2787- } else {
2788- if (typeof k == "string")
2789- k = fcf.tokenize(k, a_args, a_escape);
2790- result[k] = v;
2791- }
2792- }
2793- }
2794-
2795- return result;
2796- }
2797-
2798- fcf.pattern = (a_str, a_object, a_refInfo) => {
2799- if (!a_object)
2800- a_object = {};
2801- try {
2802- if (typeof a_str == "string") {
2803- return fcf._pattern(a_str, a_object, a_refInfo);
2804- } else if (typeof a_str == "object") {
2805- a_str = fcf.clone(a_str);
2806- fcf.each(a_str, (k,v) => {
2807- a_str[k] = fcf.pattern(a_str[k], a_object, a_refInfo);
2808- });
2809- return a_str;
2810- } else {
2811- return a_str;
2812- }
2813- } catch (e) {
2814- return "";
2815- }
2816- }
2817-
2818- fcf._pattern = (a_str, a_object, a_refInfo) => {
2819- a_refInfo = a_refInfo ? a_refInfo : {};
2820- if (!a_refInfo.references)
2821- a_refInfo.references = {};
2822-
2823- var result = "";
2824- var buff = "";
2825- var state = 0
2826- var rmode = 0;
2827- var start = undefined;
2828- a_str = fcf.str(a_str);
2829- for (var i = 0; i < a_str.length; ++i) {
2830- var c = a_str.charAt(i);
2831- if (state == 0){
2832- if (c == "$") { start = i; state = 1; rmode = 1;}
2833- else if (c == "@") { start = i; state = 1; rmode = 2;}
2834- else if (c == "&") { start = i; state = 1; rmode = 3;}
2835- else if (c == "!") { start = i; state = 1; rmode = 4;}
2836- else { result += c; }
2837- } else if (state == 1) {
2838- if (c == "{") { state = 2; buff = ""; }
2839- else { result += (rmode==1 ? "$" :
2840- rmode==2 ? "@" :
2841- rmode==3 ? "&" :
2842- "!"
2843- ) + c; state = 0; }
2844- } else if (state == 2) {
2845- if (c == "}") {
2846- if (rmode == 1 && a_str.charAt(i+1) == "$"){
2847- state = 0;
2848- if (buff[0] == "{"){
2849- result += "${";
2850- result += buff;
2851- result += "}$";
2852- } if (start == 0 && i == a_str.length-2){
2853- return fcf.resolve(a_object, buff);
2854- } else {
2855- result += fcf.str(fcf.resolve(a_object, buff));
2856- }
2857- ++i;
2858- } else if (rmode == 2 && a_str.charAt(i+1) == "@"){
2859- state = 0;
2860- if (buff[0] == "{"){
2861- result += "@{";
2862- result += buff;
2863- result += "}@";
2864- } else if (start == 0 && i == a_str.length-2){
2865- return fcf.safeEvalResult(buff, a_object);
2866- } else {
2867- result += fcf.str(fcf.safeEvalResult(buff, a_object));
2868- }
2869- ++i;
2870- } else if (rmode == 3 && a_str.charAt(i+1) == "&"){
2871- state = 0;
2872- var refPathArr = fcf.parseObjectAddress(buff);
2873- var refPathArrFirst = refPathArr[0];
2874- refPathArr.shift();
2875- var fullRef = a_refInfo.references[refPathArrFirst];
2876- for(var j = 0; j < refPathArr.length; ++j)
2877- fullRef += '["' + refPathArr[j] + '"]';
2878- var ref = fcf.argRef(a_refInfo.id, fullRef);
2879- if (start == 0 && i == a_str.length-2){
2880- return ref;
2881- } else {
2882- result += fcf.str(ref);
2883- }
2884- ++i;
2885- } else if (rmode == 4 && a_str.charAt(i+1) == "!"){
2886- state = 0;
2887- if (buff[0] == "{" && buff[buff.length-1] == "}")
2888- buff = buff.substr(1, buff.length-2);
2889- result += fcf.t(buff);
2890- ++i;
2891- } else {
2892- buff += c;
2893- }
2894- }
2895- else {
2896- buff += c;
2897- }
2898- }
2899- }
2900- return result;
2901- }
2902-
2903-
2904- fcf.tokenizeEx = (a_ctxtptr, a_obj, a_escape) => {
2905- var ctxtptr = typeof a_ctxtptr === "object" ? a_ctxtptr
2906- : new fcf.StringPointer(a_ctxtptr);
2907- return fcf._tokenizePtrEx(ctxtptr, a_obj, a_escape);
2908- }
2909-
2910- fcf._tokenizePtrEx = (a_ctxtptr, a_obj, a_escape) => {
2911- var result = {
2912- data: "",
2913- noComplete: false,
2914- replace: false,
2915- };
2916-
2917- let clFindSubstitution = (a_str, a_startPos) => {
2918- var result = {
2919- beg: undefined,
2920- end: undefined,
2921- type: undefined,
2922- }
2923- var emptyResult = {
2924- beg: undefined,
2925- end: undefined,
2926- type: undefined,
2927- }
2928- var escCnt = 0;
2929- var state = 0; /*
2930- 0 find block
2931- 1 find first {
2932- 2 find second {
2933- 3 find first }
2934- 4 find second }
2935- 5 find close
2936- */
2937- for (var i = a_startPos; i < a_str.length; ++i) {
2938- var c = a_str[i];
2939- if ((!a_escape || (escCnt % 2) == 0) && state == 0) {
2940- if (c == "@" || c == "#" || c == "$" || c == "!") {
2941- result.type = c;
2942- result.beg = i;
2943- state = 1;
2944- }
2945- } else if (state == 1) {
2946- if (c == "{") { state = 2; }
2947- else { state = 0; --i; }
2948- } else if (state == 2) {
2949- if (c == "{") state = 3;
2950- else state = 0;
2951- } else if (state == 3) {
2952- if (c == "}") {
2953- state = 4;
2954- }
2955- } else if (state == 4) {
2956- if (c == "}") {
2957- state = 5;
2958- } else {
2959- state = 3;
2960- }
2961- } else if (state == 5) {
2962- if (c == result.type) {
2963- result.end = i+1;
2964- return result;
2965- } else if (c == "}") {
2966- state = 5;
2967- } else {
2968- state = 3;
2969- }
2970- }
2971-
2972- if (c == "\\")
2973- ++escCnt;
2974- else
2975- escCnt = 0;
2976- }
2977-
2978- return emptyResult;
2979- }
2980-
2981- let clResolveSimple = (a_str, a_blockPosition) => {
2982- var key = a_str.substr(a_blockPosition.beg + 3, a_blockPosition.end - a_blockPosition.beg - 6);
2983- return fcf.resolveEx(a_obj, key);
2984- }
2985-
2986- let clResolveData = (a_str, a_blockPosition) => {
2987- var key = a_str.substr(a_blockPosition.beg + 3, a_blockPosition.end - a_blockPosition.beg - 6);
2988- return fcf.resolveEx(a_obj, key);
2989- }
2990-
2991- let clResolveCode = (a_str, a_blockPosition) => {
2992- var code = a_str.substr(a_blockPosition.beg + 3, a_blockPosition.end - a_blockPosition.beg - 6);
2993- var result;
2994- try {
2995- result = fcf.safeEvalResult('(' + code + ')', a_obj);
2996- } catch(e) {
2997- return {object: undefined, key: undefined};
2998- }
2999-
3000- if (typeof result === "number" && isNaN(result))
3001- return {object: undefined, key: undefined};
3002-
3003- return {object: {result: result}, key: "result"};
3004- }
3005-
3006- var pos = a_ctxtptr.pos;
3007- var str = a_ctxtptr.fullStr();
3008- while (pos !== undefined) {
3009- var blockPosition = clFindSubstitution(str, pos);
3010- var item = str.substr(
3011- pos,
3012- blockPosition.beg !== undefined ? blockPosition.beg - pos : str.length - pos);
3013- if (item !== "")
3014- result.data += item;
3015-
3016- if (blockPosition.beg !== undefined) {
3017- if (blockPosition.type == "$") {
3018- var retSubstitution = clResolveSimple(str, blockPosition);
3019- if (retSubstitution.object) {
3020- result.data = fcf.str(result.data) + fcf.str(retSubstitution.object[retSubstitution.key]);
3021- result.replace = true;
3022- } else {
3023- result.data += str.substr(blockPosition.beg, blockPosition.end - blockPosition.beg);
3024- result.noComplete = true;
3025- }
3026- } else if (blockPosition.type == "#") {
3027- var retSubstitution = clResolveData(str, blockPosition);
3028- if (retSubstitution.object) {
3029- result.data = (blockPosition.beg == 0 && blockPosition.end == str.length)
3030- ? retSubstitution.object[retSubstitution.key]
3031- : fcf.str(result.data) + retSubstitution.object[retSubstitution.key];
3032- result.replace = true;
3033- } else {
3034- result.data += str.substr(blockPosition.beg, blockPosition.end - blockPosition.beg);
3035- result.noComplete = true;
3036- }
3037- } else if (blockPosition.type == "@") {
3038- var retSubstitution = clResolveCode(str, blockPosition);
3039- if (retSubstitution.object) {
3040- result.data = (blockPosition.beg == 0 && blockPosition.end == str.length)
3041- ? retSubstitution.object[retSubstitution.key]
3042- : fcf.str(result.data) + retSubstitution.object[retSubstitution.key];
3043- result.replace = true;
3044- } else {
3045- result.data += str.substr(blockPosition.beg, blockPosition.end - blockPosition.beg);
3046- result.noComplete = true;
3047- }
3048- } else if (blockPosition.type == "!") {
3049- result.data += fcf.tokenize(fcf.t(str.substr(blockPosition.beg+3, blockPosition.end - blockPosition.beg-6)), a_obj, a_escape);
3050- result.replace = true;
3051- }
3052- }
3053-
3054- pos = blockPosition.end;
3055- }
3056-
3057- return result;
3058- }
3059-
3060-
3061- fcf.normalizeObjectAddress = (a_path) => {
3062- var arr = Array.isArray(a_path) ? a_path : fcf.parseObjectAddress(a_path, true);
3063- var result = "";
3064- for(var i = 0; i < arr.length; ++i) {
3065- let key;
3066- let isa;
3067- if (typeof arr[i] == "object") {
3068- key = arr[i].part;
3069- isa = arr[i].array;
3070- } else {
3071- key = arr[i];
3072- isa = false;
3073- }
3074- key = fcf.replaceAll(key, "\\", "\\\\");
3075- key = fcf.replaceAll(key, "\"", "\\\"");
3076- if (!isa) {
3077- result += "[\"" + key + "\"]";
3078- } else {
3079- result += "[[\"" + key + "\"]]";
3080- }
3081- }
3082- return result;
3083- }
3084-
3085- class fcfStringPointer {
3086- constructor(a_string){
3087- this._str = a_string;
3088- this.pos = 0;
3089- this.posend = this._str.length;
3090- }
3091-
3092- clone() {
3093- return fcf.append({}, this);
3094- }
3095-
3096- fullStr() {
3097- return this._str;
3098- }
3099-
3100- str(){
3101- return this._str.substr(this.pos, this.posend - this.pos);
3102- }
3103-
3104- indexOf(a_ctx, a_startPos){
3105- if (a_startPos === -1)
3106- return -1;
3107-
3108- var startPos = a_startPos !== undefined ? a_startPos : this.pos;
3109- var result = -1;
3110-
3111- if (typeof a_ctx === "string") {
3112- result = this._str.indexOf(a_ctx, startPos);
3113- } else {
3114- for(var i = startPos; i < this.posend; ++i) {
3115- var char = this._str.charAt(i);
3116- for(var j = 0; j < a_ctx.length; ++j) {
3117- if (a_ctx[j] == char) {
3118- if (result === -1 || i < result) {
3119- result = i;
3120- break;
3121- }
3122- }
3123- }
3124- }
3125-
3126- }
3127- return result >= this.posend ? -1 : result;
3128- }
3129-
3130- indexOfSpace(a_startPos){
3131- if (a_startPos === -1)
3132- return -1;
3133- var startPos = a_startPos !== undefined ? a_startPos : this.pos;
3134- for(var i = startPos; i < this.posend; ++i){
3135- var code = this._str.charCodeAt(i);
3136- if (code <= 32 && code > 0)
3137- return i;
3138- }
3139- return -1;
3140- }
3141-
3142- end (){
3143- return this.pos >= this.posend;
3144- }
3145-
3146- ltrim (a_arr) {
3147- var startPos = this.pos;
3148- if (a_arr === undefined) {
3149- while(!this.end()){
3150- var code = this._str.charCodeAt(this.pos);
3151- if (code <= 32 && code > 0)
3152- break;
3153- ++this.pos;
3154- }
3155- } else {
3156- var br = false;
3157- a_arr = Array.isArray(a_arr) ? a_arr : [a_arr];
3158- while(!this.end()) {
3159- var char = this._str.charAt(this.pos);
3160- for(var i = 0; i < a_arr.length; ++i){
3161- if (char == a_arr[i]){
3162- br = true;
3163- }
3164- }
3165- if (br)
3166- break;
3167- ++this.pos;
3168- }
3169- }
3170- return this._str.substr(startPos, this.pos - startPos);
3171- }
3172- }
3173-
3174-
3175-
3176- fcf.StringPointer = fcfStringPointer;
3177-
3178- fcf.NDetails.OABuffer = class {
3179- constructor(){
3180- this.m = {};
3181- this.a = [];
3182- this.c = 100;
3183- }
3184-
3185- get(a_key){
3186- return this.m[a_key];
3187- }
3188-
3189- set(a_key, a_value){
3190- if (a_key in this.m){
3191- this.m[a_key] = a_value;
3192- } else {
3193- if (this.a.length >= this.c) {
3194- delete this.m[this.a[this.a.length-1]];
3195- this.a.pop();
3196- }
3197- this.a.unshift(a_key);
3198- this.m[a_key] = a_value;
3199- }
3200- }
3201- }
3202- fcf.NDetails.oab = new fcf.NDetails.OABuffer();
3203- fcf.NDetails.oabe = new fcf.NDetails.OABuffer();
3204-
3205- fcf.parseObjectAddress = (a_path, a_exMode) => {
3206- if (Array.isArray(a_path))
3207- return fcf.clone(a_path);
3208- let oab = a_exMode ? fcf.NDetails.oabe : fcf.NDetails.oab;
3209- let result = oab.get(a_path);
3210- if (result === undefined) {
3211- result = fcf.NDetails.parseObjectAddress(a_path, a_exMode);
3212- oab.set(a_path, result);
3213- }
3214- return fcf.clone(result);
3215- }
3216-
3217- fcf.NDetails.parseObjectAddress = (a_path, a_exMode) => {
1444+ return result;
1445+ }
32181446 // 0 - simple
32191447 // 1 - in [] || [[]]
32201448 // 2 - in [""] || [[""]]
@@ -3452,29 +1680,111 @@
34521680 break;
34531681 }
34541682 }
3455- if (!result.length)
3456- result.push("");
1683+ if (!result.length) {
1684+ if (a_exMode) {
1685+ result.push({part: "", array: false});
1686+ } else {
1687+ result.push("");
1688+ }
1689+ }
34571690 return result;
34581691 }
34591692
34601693
1694+
1695+ /// @fn string fcf.normalizeObjectAddress(string|[string] a_path)
1696+ /// @brief Normalizes the address of an object
1697+ /// @param string|[string] a_path - Address of the object.
1698+ /// @result string Normalized object address
1699+ fcf.normalizeObjectAddress = (a_path) => {
1700+ var arr = fcf.parseObjectAddress(a_path, true);
1701+ var result = "";
1702+ for(var i = 0; i < arr.length; ++i) {
1703+ let key;
1704+ let isa;
1705+ if (typeof arr[i] == "object") {
1706+ key = arr[i].part;
1707+ isa = arr[i].array;
1708+ } else {
1709+ key = arr[i];
1710+ isa = false;
1711+ }
1712+ key = fcf.escapeQuotes(key, "\"");
1713+ if (!isa) {
1714+ result += "[\"" + key + "\"]";
1715+ } else {
1716+ result += "[[\"" + key + "\"]]";
1717+ }
1718+ }
1719+ return result;
1720+ }
1721+
1722+
1723+
1724+ /// @fn mixed fcf.resolve(object a_obj, string|[string] a_path, boolean a_quiet = true)
1725+ /// @brief Returns a subobject at the given path
1726+ /// @param object a_obj - Root object
1727+ /// @param string|[string] a_obj - Path to returned subobject
1728+ /// @param a_quiet = true - If equality is false, then arriving at an element is no exception,
1729+ /// If the equality is false, then the arrival to the element is not disappeared is thrown with the code ACCESS_FAILED_FIELD_TOKENIZE,
1730+ /// @result mixed - subobject data
1731+ /// @example
1732+ /// JS:
1733+ /// var root = { object: { subobject: { value: "123" } } } };
1734+ /// var value = fcf.resolve(root, "object.subobject.value");
1735+ /// console.log(value);
1736+ /// Console output:
1737+ /// 123
1738+ fcf.resolve = (a_obj, a_path, a_quiet) => {
1739+ if (typeof a_obj !== "object") {
1740+ return;
1741+ }
1742+ let path = fcf.parseObjectAddress(a_path);
1743+ for(let i = 0; i < path.length; ++i) {
1744+ a_obj = a_obj[path[i]];
1745+ if (a_obj === undefined) {
1746+ if (a_quiet !== undefined && !a_quiet && i != path.length - 1) {
1747+ throw new fcf.Exception("ACCESS_FAILED_FIELD_TOKENIZE", {command: a_path});
1748+ }
1749+ return;
1750+ }
1751+ }
1752+ return a_obj;
1753+ }
1754+
1755+
1756+
1757+ /// @fn object fcf.resolveEx(object a_obj, string|[string|object] a_path, boolean a_createObj = false)
1758+ /// @brief Returns a subobject information at the given path
1759+ /// @param object a_obj - Root object
1760+ /// @param string|[string] a_obj - Path to returned subobject
1761+ /// @param boolean a_createObj = false - If true, then if there are no nested objects, they are created.
1762+ /// @result object - a subobject information
1763+ /// - object object - Object containing subobject
1764+ /// - string key - Subobject name
1765+ /// @example
1766+ /// JS:
1767+ /// var root = { object: { subobject: { value: "123" } } } };
1768+ /// var ref = fcf.resolveEx(root, "object.subobject.value");
1769+ /// console.log(ref)
1770+ /// Console output:
1771+ /// {
1772+ /// object: { value: "123" },
1773+ /// key: "value"
1774+ /// }
34611775 fcf.resolveEx = (a_obj, a_path, a_createObj) => {
3462- if (typeof a_obj !== "object"){
3463- return {
3464- key: undefined,
3465- object: undefined,
3466- };
3467- }
3468-
3469- let pathArr = fcf.parseObjectAddress(a_path, true);
34701776 let result = {
34711777 key: undefined,
34721778 object: undefined,
34731779 };
1780+ if (typeof a_obj !== "object"){
1781+ return result;
1782+ }
1783+ let pathArr = fcf.parseObjectAddress(a_path, true);
34741784 let cur = a_obj;
34751785 for(var i = 0; i < pathArr.length-1; ++i) {
34761786 let key = pathArr[i].part;
3477- if (typeof cur[key] !== "object" || cur[key] === null)
1787+ if (cur[key] === undefined || cur[key] === null) {
34781788 if (a_createObj) {
34791789 if (pathArr[i].array){
34801790 cur[key] = [];
@@ -3484,6 +1794,7 @@
34841794 } else {
34851795 return result;
34861796 }
1797+ }
34871798 cur = cur[key];
34881799 }
34891800 result.key = pathArr[pathArr.length-1].part;
@@ -3492,829 +1803,2058 @@
34921803 return result;
34931804 }
34941805
3495- fcf.resolve = (a_obj, a_path) => {
3496- if (typeof a_obj !== "object")
3497- return;
3498-
3499- let path = fcf.parseObjectAddress(a_path);
3500- for(let i = 0; i < path.length; ++i){
3501- a_obj = a_obj[path[i]];
3502- if (a_obj === undefined)
3503- return;
3504- }
3505- return a_obj;
3506- }
3507-
3508-
3509-
3510- fcf.NDetails.messages = {};
3511- fcf.addException = function(a_messageName, a_messageText) {
3512- fcf.NDetails.messages[a_messageName] = a_messageText;
1806+
1807+
1808+ /// @fn object fcf.prepare(object a_object, string|[string|object] a_objectPath)
1809+ /// @brief Creates an object at the path specified in the argument if the object does not exist.
1810+ /// And returns the specified object.
1811+ /// @param object a_object - The object in which the elements are created
1812+ /// @param string|[string|object] a_objectPath - Object path
1813+ /// @result object - Object at path a_objectPath from object a_object
1814+ fcf.prepare = (a_root, a_objectPath) => {
1815+ a_objectPath = typeof a_objectPath == "string" ? fcf.parseObjectAddress(a_objectPath, true) :
1816+ Array.isArray(a_objectPath) ? a_objectPath :
1817+ false;
1818+ if (!a_objectPath)
1819+ return a_root;
1820+ for (let part of a_objectPath) {
1821+ if (typeof part == "object") {
1822+ if (!(part.part in a_root))
1823+ a_root[part.part] = part.array ? [] : {};
1824+ a_root = a_root[part.part];
1825+ } else {
1826+ if (!(part in a_root))
1827+ a_root[part] = {};
1828+ a_root = a_root[part];
1829+ }
1830+ }
1831+ return a_root;
35131832 }
35141833
3515- fcf.Exception = class Exception extends Error{
3516- constructor(a_nameOrMessageOrException, a_args, a_subException) {
3517- super(a_nameOrMessageOrException);
3518- let exceptionName;
3519- let template;
3520- let stackTxt;
3521- let stackTxtStartLevel = 1;
3522-
3523- if (Array.isArray(a_args)) {
3524- for(var i = 0; i < a_args.length; ++i)
3525- this[i+1] = a_args[i];
3526- } else if (typeof a_args == "object"){
3527- for(let key in a_args)
3528- this[key] = a_args[key];
3529- }
3530-
3531- if (typeof a_nameOrMessageOrException == "string"){
3532- template = fcf.NDetails.messages[a_nameOrMessageOrException];
3533- if (!template) {
3534- exceptionName = "ERROR";
3535- template = fcf.NDetails.messages[exceptionName];
3536- this.error = a_nameOrMessageOrException;
3537- } else {
3538- exceptionName = a_nameOrMessageOrException;
3539- }
3540- } else if (a_nameOrMessageOrException instanceof Error) {
3541- exceptionName = "ERROR";
3542- stackTxt = a_nameOrMessageOrException.stack || a_nameOrMessageOrException.stacktrace;
3543- stackTxtStartLevel = 0;
3544- template = fcf.NDetails.messages[exceptionName];
3545- this.error = a_nameOrMessageOrException.toString();
3546- } else if (typeof a_nameOrMessageOrException == "object"){
3547- template = fcf.NDetails.messages[a_nameOrMessageOrException.name];
3548- if (!template){
3549- if (a_nameOrMessageOrException._templateMessage){
3550- for(let key in a_nameOrMessageOrException)
3551- this[key] = a_nameOrMessageOrException[key];
3552- exceptionName = a_nameOrMessageOrException.name ? a_nameOrMessageOrException.name : "ERROR";
3553- template = a_nameOrMessageOrException._templateMessage;
3554- } if (a_nameOrMessageOrException.message && (a_nameOrMessageOrException.stack || a_nameOrMessageOrException.stacktrace)){
3555- exceptionName = "ERROR";
3556- stackTxt = a_nameOrMessageOrException.stack || a_nameOrMessageOrException.stacktrace;
3557- stackTxtStartLevel = 0;
3558- template = fcf.NDetails.messages[exceptionName];
3559- this.error = a_nameOrMessageOrException.message;
3560- } else {
3561- for(let key in a_nameOrMessageOrException)
3562- this[key] = a_nameOrMessageOrException[key];
3563- exceptionName = a_nameOrMessageOrException.name ? a_nameOrMessageOrException.name : "ERROR";
3564- template = fcf.NDetails.messages["ERROR"];
3565- this.error = a_nameOrMessageOrException.error ? a_nameOrMessageOrException.error :
3566- a_nameOrMessageOrException.message ? a_nameOrMessageOrException.message :
3567- "Unknown error";
3568- }
1834+
1835+
1836+ /// @fn string fcf.getDirectory(string a_path)
1837+ /// @brief Returns the path of a directory
1838+ /// @param string a_path - Source path
1839+ /// @result string
1840+ fcf.getDirectory = (a_path) => {
1841+ let i = a_path.length - 1;
1842+ for(; i >= 0; --i){
1843+ let c = a_path[i];
1844+ if (c == "\\" || c == "/"){
1845+ break;
1846+ }
1847+ }
1848+ return i == -1 ? "" : a_path.substring(0, i);
1849+ }
1850+
1851+
1852+
1853+ /// @fn string fcf.getExtension(string a_path)
1854+ /// @brief Returns an extension for a file path
1855+ /// @param string a_path - Source path
1856+ /// @result string
1857+ fcf.getExtension = (a_path) => {
1858+ if (typeof a_path !== "string")
1859+ return "";
1860+ a_path = a_path.split("?")[0].split("#")[0];
1861+ var arr = a_path.split(".");
1862+ return arr.length > 1 ? arr[arr.length-1] : "";
1863+ }
1864+
1865+
1866+
1867+ /// @fn string fcf.getExtension(string a_path)
1868+ /// @brief Returns a filename without a path extension
1869+ /// @param string a_path - Source path
1870+ /// @result string
1871+ fcf.getShortFileName = (a_path) => {
1872+ if (!a_path)
1873+ return "";
1874+ var offset = -1;
1875+ for (var p = 0; p < a_path.length; ++p) {
1876+ var c = a_path.charAt(p);
1877+ if (c == ":" || c == "/" || c == "\\")
1878+ offset = p;
1879+ }
1880+ let arr = a_path.substr(offset == -1 ? 0 : offset+1).split(".");
1881+ if (arr.length > 1)
1882+ arr.pop();
1883+ return arr.join(".");
1884+ }
1885+
1886+
1887+
1888+ /// @fn string fcf.getExtension(string a_path)
1889+ /// @brief Returns a filename
1890+ /// @param string a_path - Source path
1891+ /// @result string
1892+ fcf.getFileName = (a_path) => {
1893+ a_path = fcf.rtrim(a_path, "/");
1894+ if (!a_path)
1895+ return "";
1896+ var offset = 0;
1897+ for (var p = 0; p < a_path.length; ++p) {
1898+ var c = a_path.charAt(p);
1899+ if (c == ":" || c == "/" || c == "\\")
1900+ offset = p;
1901+ }
1902+
1903+ if (offset)
1904+ offset += 1;
1905+
1906+ return a_path.substr(offset);
1907+ }
1908+
1909+
1910+
1911+ /// @fn string fcf.resolvePath(string a_uri, object a_aliases = undefined)
1912+ /// @brief Converts a URI alias string to a real path in FCF notation.
1913+ /// @details The data from the aliases configuration and the a_aliases argument is used as aliases
1914+ /// @param string a_uri - Transformable string
1915+ /// @param object a_aliases - Aliases object. The alias is the key and the value is the path.
1916+ /// @result string - Result string
1917+ fcf.resolvePath = function(a_uri, a_aliases) {
1918+ if (a_uri[0] != "@")
1919+ return a_uri;
1920+ let pos;
1921+ pos = Math.min( (pos = a_uri.indexOf("+")) != -1 ? pos : Infinity,
1922+ (pos = a_uri.indexOf("?")) != -1 ? pos : Infinity,
1923+ (pos = a_uri.indexOf("#")) != -1 ? pos : Infinity,
1924+ (pos = a_uri.indexOf("/")) != -1 ? pos : Infinity,
1925+ (pos = a_uri.indexOf("\\")) != -1 ? pos : Infinity,
1926+ (pos = a_uri.indexOf(".wrapper.js")) != -1 ? pos : Infinity,
1927+ (pos = a_uri.indexOf(".wrapper.min.js")) != -1 ? pos : Infinity);
1928+ let prefix = pos != Infinity ? a_uri.substring(1, pos) : a_uri.substring(1);
1929+ let suffix = pos != Infinity ? a_uri.substring(pos) : "";
1930+ if (typeof a_aliases == "object" && prefix in a_aliases){
1931+ a_uri = a_aliases[prefix] + suffix;
1932+ } else if (prefix in fcf.getConfiguration().aliases) {
1933+ a_uri = fcf.getConfiguration().aliases[prefix] + suffix;
1934+ } else {
1935+ throw new fcf.Exception("RESOLVE_PATH_ERROR", {path: a_uri});
1936+ }
1937+ return a_uri;
1938+ }
1939+
1940+
1941+
1942+ /// @fn string fcf.normalizePath(string a_path, boolean a_isServerPath = fcf.isServer())
1943+ /// @brief Normalizes the resource path (FS/HTTP/HTTPS). Removes paths ./ and performs an offset .. inside the package's relative path
1944+ /// @param string a_path - Path
1945+ /// @param boolean a_isServerPath = fcf.isServer() - If it is true, then the path contains the FS address,
1946+ /// If it is false, the path contains the WEB (HTTP/HTTPS) address
1947+ /// @result string - Normalized path
1948+ fcf.normalizePath = (a_path, a_isServerPath) => {
1949+ let prefixPosEnd = 0;
1950+ let pos;
1951+ a_isServerPath = a_isServerPath !== undefined ? a_isServerPath : _isServer;
1952+ if (a_path[0] == "/") {
1953+ prefixPosEnd = 1;
1954+ } else if ((pos = a_path.indexOf(":")) != -1) {
1955+ if ((a_isServerPath || a_isServerPath === "*") && a_path[pos+1] == "\\" && pos == 1) {
1956+ prefixPosEnd = pos + 2;
1957+ } else if ((!a_isServerPath || a_isServerPath === "*") && (a_path.indexOf("http://") == 0 || a_path.indexOf("https://") == 0)) {
1958+ a_isServerPath = false;
1959+ prefixPosEnd = a_path.indexOf("/", a_path.indexOf("://") + 3);
1960+ if (prefixPosEnd == -1) {
1961+ prefixPosEnd = a_path.length;
35691962 } else {
3570- for(let key in a_nameOrMessageOrException)
3571- this[key] = a_nameOrMessageOrException[key];
3572- exceptionName = a_nameOrMessageOrException.name;
3573- }
3574- }
3575-
3576- this.name = exceptionName;
3577- this._templateMessage = template;
3578- this.subException = a_subException;
3579- if (!Array.isArray(this.stackArr)) {
3580- if (!stackTxt)
3581- stackTxt = typeof console === "object" && console.trace === "function" ? console.trace() : (new Error()).stack
3582- if (stackTxt === undefined) {
3583- try {
3584- throw new Error();
3585- } catch(e) {
3586- stackTxt = e.stack;
3587- }
1963+ prefixPosEnd += 1;
35881964 }
3589- this.stackArr = fcf.Exception.parseStack(stackTxt, stackTxtStartLevel);
3590- }
3591- this.message = this.toString();
3592- }
3593-
3594- toString(a_buildSubException, a_enableDefaultLanguage) {
3595- let message = "";
3596- let lang = a_enableDefaultLanguage && _isServer && fcf.application && fcf.application.getSystemVariable("fcf", "defaultLanguage") ? fcf.application.getSystemVariable("fcf", "defaultLanguage") : undefined;
3597- message = fcf.t(this._templateMessage, lang);
3598- message = fcf.tokenize(message, this);
3599-
3600- if ((a_buildSubException === undefined || a_buildSubException) && this.subException) {
3601- var subText = "\n" + this.subException.toString();
3602- if (this.subException.stack)
3603- subText += "\nSub stack: " + this.subException.stack;
3604- fcf.replaceAll(subText, "\n", "\n ");
3605- message += subText;
3606- }
3607-
3608- this._enableDefaultLanguage = a_enableDefaultLanguage;
3609- return message;
3610- }
1965+ } else {
1966+ let subitem = a_path.substring(0, pos);
1967+ if (subitem.indexOf("/") == -1 && subitem.indexOf("\\") == -1) {
1968+ prefixPosEnd = pos + 1;
1969+ }
1970+ }
1971+ }
1972+ let prefix = a_path.substring(0, prefixPosEnd);
1973+ let body = a_path.substring(prefixPosEnd);
1974+ let suffix = "";
1975+ if (!a_isServerPath){
1976+ if ((pos = body.indexOf("?")) != -1 || (pos = body.indexOf("#")) != -1){
1977+ suffix = body.substring(pos);
1978+ body = body.substring(0, pos);
1979+ }
1980+ }
1981+ body = fcf.replaceAll(body, "\\", "/");
1982+ let bodyArr = body.split("/");
1983+ let resultBodyArr = [];
1984+ for(let itm of bodyArr) {
1985+ if (itm == "." || itm == ""){
1986+ continue;
1987+ } else if (itm == "..") {
1988+ resultBodyArr.pop();
1989+ } else {
1990+ resultBodyArr.push(itm);
1991+ }
1992+ }
1993+ return prefix + resultBodyArr.join("/") + suffix;
36111994 }
36121995
3613- fcf.Exception.stackToString = function(a_stackArr){
1996+
1997+
1998+ /// @fn string fcf.normalizeSubpath(string a_path)
1999+ /// @brief Normalizes the relative subpath. Removes paths. and performs an offset .. inside the relative path
2000+ /// @param string a_path - Path
2001+ /// @result string - Normalized path
2002+ fcf.normalizeSubpath = (a_path)=>{
2003+ let body = fcf.replaceAll(a_path, "\\", "/");
2004+ let bodyArr = body.split("/");
2005+ let resultBodyArr = [];
2006+ for(let itm of bodyArr) {
2007+ if (itm == "." || itm == ""){
2008+ continue;
2009+ } else if (itm == "..") {
2010+ resultBodyArr.pop();
2011+ } else {
2012+ resultBodyArr.push(itm);
2013+ }
2014+ }
2015+ return resultBodyArr.join("/");
2016+ }
2017+
2018+
2019+
2020+ /// @fn {module, subpath} fcf.getPathInfo(string a_path, a_isServerPath = fcf.isServer(), boolean a_quiet = false)
2021+ /// @brief Returns the module and the subpath by the path
2022+ /// @param string a_path - source path
2023+ /// @param a_isServerPath = fcf.isServer() - If true then a_path contains the path on the server, otherwise a_path is the network address
2024+ /// @param boolean a_quiet = false - If the parameter is equal to false if the path does not contain
2025+ /// the application catalog or the path to the module is excluded by
2026+ /// fcf.Exception with the GET_PATH_INFO_ERROR code.
2027+ /// If the argument is equal to true, then an empty object returns.
2028+ /// @result {module, subpath} - Returns an object containing the name of the module and the subpath in this module.
2029+ fcf.getPathInfo = (a_path, a_isServerPath, a_quiet) => {
2030+ a_isServerPath = a_isServerPath === undefined ? fcf.isServer() : a_isServerPath;
2031+ let path = fcf.resolvePath(a_path);
2032+ path = fcf.normalizePath(path, a_isServerPath);
2033+ if ((!a_isServerPath || a_isServerPath === "*") && (path.indexOf("http://") != -1 || path.indexOf("https://") != -1)) {
2034+ if (!_isServer) {
2035+ let pos = window.location.href.indexOf("://");
2036+ if (pos != -1 && (pos = window.location.href.indexOf("/", pos+3)) != -1 && path.indexOf(window.location.href.substring(0, pos)) != -1){
2037+ path = path.substring(pos);
2038+ } else {
2039+ return {};
2040+ }
2041+ } else {
2042+ return {};
2043+ }
2044+ }
2045+ let pos = path.indexOf(":");
2046+ let mod = pos != -1 ? path.substring(0, pos) : "";
2047+ if (mod.indexOf("/") != -1 || mod.indexOf("\\") != -1) {
2048+ mod = undefined;
2049+ pos = -1;
2050+ }
2051+ if (pos != -1) {
2052+ return {module: mod, subpath: path.substring(pos+1)};
2053+ }
2054+ let rootPath = fcf.getPath(path);
2055+ if (!a_isServerPath || a_isServerPath === "*") {
2056+ let sources = fcf.getConfiguration().sources;
2057+ for(let modName in sources) {
2058+ if (!sources[modName].webPackagePath)
2059+ continue;
2060+ if ((pos = rootPath.indexOf(sources[modName].webPackagePath)) == 0) {
2061+ return {module: modName, subpath: rootPath.substring(sources[modName].webPackagePath.length+1)};
2062+ }
2063+ }
2064+ }
2065+ let modDirs;
2066+ if (_isServer) {
2067+ modDirs = process.env.NODE_PATH.split(":").filter((v)=>{ return v != "" && v != "." });
2068+ } else {
2069+ modDirs = [fcf.getConfiguration().webModuleDirectory];
2070+ }
2071+ for(let modDir of modDirs) {
2072+ if (_isServer && (a_isServerPath || a_isServerPath === "*") && process.cwd() == modDir) {
2073+ continue;
2074+ }
2075+ if ((pos = rootPath.indexOf(modDir)) == 0){
2076+ mod = rootPath.substring(modDir.length+1).split("/")[0].split("\\")[0];
2077+ if (_isServer && (a_isServerPath || a_isServerPath === "*") && (process.cwd() == modDir+"/"+mod || process.cwd() == modDir+"\\"+mod)) {
2078+ mod = undefined;
2079+ pos = -1;
2080+ } else if (!mod) {
2081+ mod = undefined;
2082+ pos = -1;
2083+ } else {
2084+ return {module: mod, subpath: rootPath.substring(modDir.length + 1 + mod.length + 1)};
2085+ }
2086+ }
2087+ }
2088+ if (_isServer && (a_isServerPath || a_isServerPath === "*") && path.indexOf(process.cwd()) == 0) {
2089+ return {module: "", subpath: path.substr(process.cwd().length + 1)};
2090+ }
2091+ if (!a_isServerPath || a_isServerPath === "*" || (path[0] != "/" && path.indexOf(":\\") != 1)) {
2092+ return {module: "", subpath: fcf.ltrim(path, "/")};
2093+ }
2094+ if (!a_quiet) {
2095+ throw new fcf.Exception("GET_PATH_INFO_ERROR", {path: a_path})
2096+ }
2097+ return {};
2098+ }
2099+
2100+
2101+
2102+ ///@fn string fcf.getPath(string a_uri, string a_aliases, a_innerServerPath = fcf.isServer());
2103+ ///@fn string fcf.getPath(string a_uri, string a_innerServerPath = fcf.isServer());
2104+ ///@details Translates a relative URI in FCF notation into a real resource path
2105+ ///@param string a_uri - Relative URI in FCF
2106+ ///@param object a_aliases - Additional aliases for converting URIs
2107+ ///@param object a_innerServerPath = fcf.isServer()- If it is true, then the path is converted to a file path
2108+ /// on the server side, if it is false, the result will be a URI on the browser side
2109+ ///@result string - Converted URI
2110+ fcf.getPath = (a_uri, a_aliases, a_innerServerPath) => {
2111+ if (typeof a_aliases !== "object") {
2112+ a_innerServerPath = a_aliases;
2113+ a_aliases = undefined;
2114+ }
2115+ a_innerServerPath = a_innerServerPath !== undefined ? a_innerServerPath : _isServer;
2116+ a_uri = fcf.str(a_uri);
2117+ a_uri = fcf.resolvePath(a_uri, a_aliases);
2118+ a_uri = fcf.normalizePath(a_uri, a_innerServerPath);
2119+
2120+ if (a_innerServerPath) {
2121+ let winDSPos = a_uri.indexOf(":\\");
2122+ if (a_uri[0] == "/" || (winDSPos != -1 && winDSPos < 2))
2123+ return a_uri;
2124+ } else {
2125+ if (a_uri.indexOf("://") != -1)
2126+ return a_uri;
2127+ }
2128+
2129+ let modulePos = a_uri.indexOf(":");
2130+ let moduleName = modulePos != -1 ? a_uri.substring(0, modulePos) : "";
2131+ let relativePath = modulePos != -1 ? a_uri.substring(modulePos + 1) : a_uri;
2132+ if (moduleName.indexOf("/") != -1 || moduleName.indexOf("\\") != -1) {
2133+ modulePos = -1;
2134+ relativePath = a_uri;
2135+ moduleName = "";
2136+ }
2137+
2138+ const configuration = fcf.getConfiguration();
2139+
2140+ if (moduleName != "") {
2141+ if (a_innerServerPath){
2142+ let modulePath = libResolver.resolveModule(moduleName);
2143+ a_uri = libPath.join(modulePath, relativePath);
2144+ } else {
2145+ let prefix = "";
2146+ let srcInfo = configuration.sources[moduleName];
2147+ if (typeof srcInfo == "string"){
2148+ srcInfo = {webPackagePath: srcInfo};
2149+ }
2150+ if (srcInfo && srcInfo.webPackagePath){
2151+ prefix += srcInfo.webPackagePath;
2152+ } else {
2153+ prefix += configuration.webModuleDirectory + "/" + moduleName;
2154+ }
2155+ if (prefix && relativePath && relativePath[0] != "/" && relativePath[0] != "\\"){
2156+ prefix += "/";
2157+ }
2158+ a_uri = prefix + relativePath;
2159+ }
2160+ } else {
2161+ if (a_innerServerPath){
2162+ a_uri = libPath.join(process.cwd(), relativePath);
2163+ } else {
2164+ a_uri = "/" + fcf.ltrim(relativePath, ["/", "\\", ":"]);
2165+ }
2166+ }
2167+
2168+ return a_uri;
2169+ }
2170+
2171+
2172+
2173+ // @fn string fcf.styleToString(string a_name, a_mixed a_value)
2174+ // @fn string fcf.styleToString(object a_descriptions)
2175+ // @brief Converts the a_name and a_value parameters to a string of the format `a_name : a_value`.
2176+ // For dimension values, automatically adds the ending "px";
2177+ // @param string a_name - Style Name.
2178+ // @param string a_value - Style value.
2179+ // For dimension values ("left", "top", "bottom", "right",
2180+ // "width", "min-width", "max-width", "height", "min-height",
2181+ // "max-height"), automatically adds the ending "px";
2182+ // @param {a_name: a_value} a_descriptions - Object with property description
2183+ // @result Returns a string of the format `a_name : a_value`.
2184+ fcf.styleToString = (a_name, a_value) => {
36142185 let result = "";
3615- for(let i = 0; i < a_stackArr.length; ++i){
3616- result += a_stackArr[i].file + ": " +
3617- a_stackArr[i].function + " [" +
3618- a_stackArr[i].line + ":" + a_stackArr[i].column + "]" +
3619- "\n";
3620- }
3621- return result;
3622- }
3623-
3624-
3625- fcf.Exception.is = function(a_exception, a_name) {
3626- return typeof a_exception === "object" && a_exception !== null && a_exception.name && a_exception.name == a_name;
3627- }
3628-
3629- fcf.Exception.parseStack = function(a_stackTxt, a_startLevel) {
3630- if (a_startLevel === undefined)
3631- a_startLevel = 0;
3632- var result = [];
3633- if (!a_stackTxt)
3634- return result;
3635- var stackArr = a_stackTxt.split("\n");
3636- for(var i = a_startLevel + 1; i < stackArr.length; ++i) {
3637- var level = {};
3638- var endPosFunc = stackArr[i].indexOf("(");
3639- var posInfoArr = stackArr[i].substr(endPosFunc+1, stackArr[i].length - endPosFunc - 2).split(":");
3640- level.function = stackArr[i].substr(7, endPosFunc-8);
3641- let fileArr = [];
3642- for(let j = 0; j < posInfoArr.length - 2; ++j)
3643- fileArr.push(posInfoArr[j]);
3644- level.file = fileArr.join(":");
3645- if (level.file.indexOf("at ") != -1)
3646- level.file = level.file.split("at ")[1];
3647- level.line = posInfoArr[posInfoArr.length - 2];
3648- level.column = posInfoArr[posInfoArr.length - 1];
3649- result.push(level);
2186+ if (typeof a_name == "object" && a_name !== null){
2187+ let first = true;
2188+ for(let key in a_name) {
2189+ if (first) {
2190+ first = false;
2191+ } else {
2192+ result += "; ";
2193+ }
2194+ result += fcf.styleToString(key, a_name[key]);
2195+ }
2196+ } else {
2197+ if (a_value === undefined || a_value === null || a_value === "")
2198+ return result;
2199+ if (["left", "top", "bottom", "right", "width", "min-width", "max-width", "height", "min-height", "max-height"].indexOf(a_name) != -1) {
2200+ if (!isNaN(a_value)) {
2201+ a_value = a_value + "px";
2202+ }
2203+ }
2204+ result = a_name + ": " + a_value;
36502205 }
36512206 return result;
36522207 }
36532208
36542209
36552210
3656- fcf.addException("ERROR_ACCESS", "Access denied to ${{resource}}$");
3657- fcf.addException("ERROR_404", "Page not found ${{address}}$");
3658- fcf.addException("OPERATION_NOT_SUPPORTED", "Operation not supported");
3659- fcf.addException("ERROR_TEST_OPEN_DIRECTORY", "Unable to open the '${{directory}}$' directory for reading");
3660- fcf.addException("ERROR_TEST_EXCEPTION", "The test throw an exception");
3661- fcf.addException("ERROR_TEST_FAILED_EQUAL", "The value '${{1}}$' is not equal to '${{2}}$'");
3662- fcf.addException("ERROR_TEST_FAILED_NOT_EQUAL", "The value '${{1}}$' must not be equal to '${{2}}$'");
3663- fcf.addException("ERROR_TEST_FAILED_EQUAL_OBJECT", "The value '${{1}}$' is not equal to '${{2}}$ ' in the '${{3}}$' object");
3664- fcf.addException("ERROR_TEST_FAILED_EQUAL_OBJECT_ITEM_NOT_FOUND", "The '${{element}}$' element is missing from the '${{object}}$' object");
3665- fcf.addException("ERROR_TEST_FAILED_EQUAL_OBJECT_ARG2_NOT_OBJECT", "Argument 2 is not an object in the object '${{1}}$'");
3666- fcf.addException("ERROR_TEST_FAILED_EQUAL_OBJECT_ARG2_NOT_ARRAY", "Argument 2 is not an array in the object '${{1}}$'");
3667- fcf.addException("ERROR_TEST_FAILED_EQUAL_OBJECT_LENGTH_ARRAY", "The array size of the first array (${{1}}$) is not equal to the size of the second (${{2}}$) in the '${{3}}$' object");
3668-
3669- fcf.addException("ERROR_EXECUTE_PROCESS", "Process execution failed \"@{{command}}@\": @{{error}}@\n STDERR: @{{stderr}}@");
3670-
3671- fcf.addException("ERROR_THEME_NOT_FOUND", "Failed to load @{{theme}}@ theme");
3672- fcf.addException("FAILD_FORM_INPUT", "The form was filled out incorrectly");
3673-
3674- fcf.addException("ERROR", "${{error}}$");
3675- fcf.addException("ERROR_GET_PATH", "Failed resolve path '${{path}}$'");
3676- fcf.addException("ERROR_GET_PATH_INVALID_MODULE_NAME", "Failed resolve path '${{path}}$'. Invalid module name");
3677- fcf.addException("ERROR_FILE_NOT_FOUND", "File ${{file}}$ not found");
3678- fcf.addException("ERROR_FORM_INPUT", "Incorrectly filled out form"); // Исключение должно содержать массив errors
3679- // new fcf.Exception("ERROR_FORM_INPUT", {errors: errors})
3680- fcf.addException("ERROR_READ_FILE", "Failed to read the file ${{1}}$ (${{2}}$)");
3681- fcf.addException("ERROR_WRITE_FILE", "Failed to write the file ${{file}}$ (${{error}}$)");
3682- fcf.addException("ERROR_COPY_FILE", "Failed to copy '${{source}}$' to '${{destination}}$' : (${{error}}$)");
3683- fcf.addException("ERROR_DELETE_FILE", "Failed to delete ${{file}}$ file: (${{error}}$)");
3684- fcf.addException("ERROR_READ_NOT_FILE", "Failed to read the file ${{1}}$ (The specified path is not a file)");
3685- fcf.addException("ERROR_READ_FORMAT_FILE", "Invalid file format ${{file}}$");
3686- fcf.addException("ERROR_INCORRECT_FORMAT_HANDLER", "Invalid format of the request handler ${{file}}$");
3687- fcf.addException("ERROR_REQUEST_PARAMETER_NOT_SET", "The request parameter ${{1}}$ is not set");
3688- fcf.addException("ERROR_REQUEST_PARAMETER_NOT_VALID", "Invalid request parameter format ${{1}}$");
3689- fcf.addException("ERROR_ASYNC_ACTIONS_CLOSED", "Adding a method to a completed execution queue");
3690- fcf.addException("ERROR_EVAL_TEMPL", "Execution error in the template ${{1}}$[${{2}}$:${{3}}$]");
3691- fcf.addException("ERROR_TEMPLATE_NOT_FOUND", "Template '@{{template}}@' not found");
3692- fcf.addException("ERROR_TEMPLATE_MULTIPLE_CONTROL_IN_ATTR", "Diferent types of control structures are used togethe with #{{}}# in th same atrrtibute in the '@{{template}}@' template");
3693- fcf.addException("ERROR_EVAL_SCRIPT", "Execution error in the file ${{1}}$[${{2}}$]");
3694- fcf.addException("ERROR_INCORRECT_TEMPL_REQUEST_WRAPPER", "Wrapper request to an unavailable template ${{template}}$");
3695- fcf.addException("ERROR_NOSET_GET_ARG", "The required GET request parameter '${{arg}}$' was omitted");
3696- fcf.addException("ERROR_INCORRECT_DATA_TYPE", "Invalid data type '${{type}}$'");
3697- fcf.addException("ERROR_INVALID_FORMAT_TYPE", "The value \"@{{value}}@\" does not match the type \"@{{type}}@\".");
3698-
3699- fcf.addException("ERROR_HTTP_REQUEST_ERROR", "HTTP request failed ${{1}}$");
3700- fcf.addException("ERROR_HTTP_REQUEST_OBJECT_FORMAT", "The server returned a response not in JSON format (${{data}}$)");
3701-
3702- fcf.addException("ERROR_HTTP_ARG_NOT_SET", "The '${{arg}}$' argument in the request to the server is not set");
3703-
3704- fcf.addException("ERROR_REDNER_LOOP", "Render looping error on '@{{template}}@' template");
3705- fcf.addException("ERROR_NOT_SET_TEMPLATE_IN_VIEW", "Not set template for view [type:@{{type}}@]");
3706- fcf.addException("ERROR_MAX_REQURSION_RENDER", "Exceeded the maximum number of nested templates when rendering a template @{{template}}@");
3707-
3708- fcf.addException("ERROR_TEMPLATE_VIEWS_NOT_SET_DATA_FILED", "The views data field in the ${{template}}$ template is not set");
3709- fcf.addException("ERROR_UNKNOWN_PROJECTION", "An unknown '${{projection}}$' projection is requested");
3710- fcf.addException("ERROR_INVALID_PROJECTION_FORMAT", "Invalid projection format '${{projection}}$'");
3711- fcf.addException("ERROR_UNKNOWN_FIELD_TYPE", "An unknown '${{param}}$' type in '${{projection}}$' projection");
3712- fcf.addException("ERROR_INCORRECT_PROJECTION_PARAM_NOT_SET", "The '${{param}}$' parameter for the '${{projection}}$' projection is not set");
3713- fcf.addException("ERROR_UNKNOWN_PROJECTION_IN_JOIN", "Projection '${{joinProjection}}$' was not found in the join block for projection '${{projection}}$'");
3714- fcf.addException("ERROR_PROJECTION_UNSET_WHERE", "The where block is not set in the '${{projection}}$' projection");
3715- fcf.addException("ERROR_PROJECTION_UNSET_ON", "The on block is not set in the '${{projection}}$' projection");
3716-
3717- fcf.addException("ERROR_PROJECTION_UNSET_ALIAS", "The 'alias' property of field in the '${{projection}}$' projection is not set");
3718- fcf.addException("ERROR_PROJECTION_UNSET_TYPE", "The 'type' property of field in the '${{projection}}$' projection is not set");
3719- fcf.addException("ERROR_SAFE_EVAL_ERROR_COMMAND", "safeEval() does not allow 'while', 'for', '=>', 'function', 'class' constructs");
3720- fcf.addException("ERROR_SAFE_EVAL_ERROR_LENGTH", "the maximum length of safeEval must not exceed ${{length}}$");
3721-
3722- fcf.addException("ACCESS_SELECT_PROJECTION", "Access to the selection of data from the \"${{projection}}$\" projection is prohibited");
3723- fcf.addException("ACCESS_UPDATE_PROJECTION", "Access to the update of data from the \"${{projection}}$\" projection is prohibited");
3724- fcf.addException("ACCESS_INSERT_PROJECTION", "Access to the insert of data from the \"${{projection}}$\" projection is prohibited");
3725- fcf.addException("ACCESS_DELETE_PROJECTION", "Access to the delete of data from the \"${{projection}}$\" projection is prohibited");
3726-
3727- fcf.addException("FCF_LOGIN_NOT_SET_USER", "Is not specified the user");
3728- fcf.addException("FCF_LOGIN_NOT_SET_PASSWORD", "Is not specified, the password");
3729- fcf.addException("FCF_LOGIN_INVALID_USER_OR_PASSWORD", "Invalid username or password");
3730- fcf.addException("FCF_LOGIN_INVALID_USER_BLOCKED", "The user is blocked");
3731- fcf.addException("FCF_LOGIN_INVALID_USER_NOT_CREATED", "The registration procedure was not completed for the user");
3732- fcf.addException("FCF_LOGIN_TIMEOUT", "Please try to authorize again in ${{minutes}}$ minutes ${{seconds}}$ seconds");
3733-
3734-
3735-
3736-
3737-
3738- fcf.NDetails.translations = undefined;
3739-
3740- fcf.t = (a_txt, a_lang) => {
3741-
3742- if (_isServer){
3743- if (!fcf.NTools)
3744- return a_txt;
3745- var translations = fcf.NSystem && fcf.NSystem.cache ? fcf.NSystem.cache.get("fcf", "translations") : {} ;
3746- var context = fcf.getContext();
3747- var variables = fcf.NSystem && fcf.NSystem.cache ? fcf.NSystem.cache.get("fcf", "variables") : false;
3748- var lang = a_lang !== undefined ? a_lang :
3749- context ? context.get("language") :
3750- variables && variables["defaultLanguage"] ? variables["defaultLanguage"] :
3751- "en";
3752- return translations && translations[lang] && translations[lang][a_txt] ? translations[lang][a_txt] : a_txt;
3753- } else {
3754- if (fcf.NDetails.translations === undefined){
3755- fcf.loadObject({
3756- path: "/fcfpackages/fcf/Translations.js",
3757- async: false,
2211+ //////////////////////////////////////////////////////////////////////////////
2212+ // FUNCTIONS AND CLASSES OF BASIC FUNCTIONALITY
2213+ //////////////////////////////////////////////////////////////////////////////
2214+
2215+
2216+
2217+ /// @fn fcf.Actions->mixed fcf.load(string a_urlOrFile, object a_options)
2218+ /// @brief Loads the specified resource by URL.
2219+ /// @param string a_urlOrFile - Resource URL or file path on a server
2220+ /// @param object a_options - Additional options
2221+ /// - string|object|FormData body - Data passed in the body of the request.
2222+ /// The request body can be a FormData object, but only for browser requests.
2223+ /// - string method = "GET" - HTTP request method (GET|POST|...)
2224+ /// - bool async = true - If true, the asynchronous request is executed.
2225+ /// If false, the synchronous request is executed,
2226+ /// but for http requests sent from the server,
2227+ /// this parameter will be ignored (an asynchronous call will always be performed)
2228+ /// - bool|"auto" external = false - By default, the fcf.load function on the server side
2229+ /// loads only local files, and if you need to make an http request from the server side,
2230+ /// you need to set the external parameter to true or "auto".
2231+ /// - If the parameter is false - the request on the server side is performed only for the local file
2232+ /// - If the parameter is true - the request on the server side is performed only for the http resource
2233+ /// - If the parameter is equal to "auto" - a request on the server side is performed
2234+ /// to a local file or http resource, depending on the a_urlOrFile parameter
2235+ /// - object header - HTTP request header options
2236+ /// - string format = "raw" - Response format (raw|json|auto).
2237+ /// - raw - The function returns a string received from the server
2238+ /// - json - The function returns data received in JSON format
2239+ /// - auto - If the data can be read as JSON, then the corresponding data is returned,
2240+ /// otherwise the string received from the server is returned
2241+ fcf.load = (a_url, a_options) => {
2242+ if (typeof a_url === "object") {
2243+ a_options = a_url;
2244+ a_url = a_options.url || a_options.path;
2245+ }
2246+ a_url = fcf.resolvePath(a_url);
2247+ a_options = typeof a_options == "object" ? a_options : {};
2248+ if ( a_options.external === true ||
2249+ !_isServer ||
2250+ (a_options.external === "auto" && a_url.indexOf("http://") == 0 && a_url.indexOf("https://") == 0)
2251+ ) {
2252+ a_url = new fcf.RouteInfo(a_url).url;
2253+ }
2254+ let isServerMode = _isServer && a_options.external === "auto" ? a_url.indexOf("http://") == -1 && a_url.indexOf("https://") == -1 :
2255+ _isServer && a_options.external ? false :
2256+ _isServer;
2257+
2258+ a_options = a_options || {};
2259+ let type = a_options.format;
2260+
2261+ if (!_isServer && (a_url.indexOf("://") == -1 || a_url.indexOf(window.location.href) == 0)) {
2262+ let ext = fcf.getExtension(new fcf.RouteInfo(a_url).uri).toLowerCase();
2263+ if (ext == "js" || ext == "css") {
2264+ let module = fcf.getPathInfo(a_url, isServerMode).module;
2265+ if (module && fcf.getConfiguration().sources[module] && fcf.getConfiguration().sources[module].version){
2266+ a_url = fcf.buildUrl(a_url, {version: fcf.getConfiguration().sources[module].version});
2267+ }
2268+ }
2269+ }
2270+
2271+ return fcf.actions()
2272+ .then((a_res, a_act) => {
2273+ if (_isServer) {
2274+ if (!isServerMode) {
2275+ let options = fcf.clone(a_options);
2276+ options.header = options.header || {};
2277+ let serverName = (new fcf.RouteInfo(a_url)).server;
2278+ let isSendContext = a_url[0] == "@" || a_url[0] == "/" || (serverName && serverName == fcf.getConfiguration().host) ;
2279+ if (isSendContext) {
2280+ let ctxt = fcf.append({}, fcf.getContext());
2281+ delete ctxt.route;
2282+ delete ctxt.args;
2283+ delete ctxt.safeEnv;
2284+ options.header["fcf-context"] = fcf.encodeBase64(JSON.stringify(ctxt));
2285+ }
2286+ libLoad(a_url, options)
2287+ .then((a_data)=>{
2288+ a_act.complete(a_data);
2289+ })
2290+ .catch((a_error)=>{
2291+ a_act.error(a_error);
2292+ })
2293+ } else if (a_options.async === undefined || !!a_options.async) {
2294+ libFS.readFile(fcf.getPath(a_url), 'utf8', (a_error, a_data) => {
2295+ if (a_error) {
2296+ a_act.error(a_error);
2297+ }
2298+ a_act.complete(a_data);
2299+ });
2300+ } else {
2301+ try {
2302+ a_act.complete(libFS.readFileSync(fcf.getPath(a_url), 'utf8'));
2303+ } catch(e){
2304+ a_act.error(e);
2305+ }
2306+ }
2307+ } else {
2308+ let url = fcf.getPath(a_url);
2309+ let method = (a_options.method || "GET").toUpperCase();
2310+ if (method == "GET") {
2311+ url = fcf.buildUrl(url, a_options.data);
2312+ }
2313+ var xmlHttp = new XMLHttpRequest();
2314+ xmlHttp.onreadystatechange = () => {
2315+ if (xmlHttp.readyState == 4) {
2316+ if (xmlHttp.status == 200) {
2317+ a_act.complete(xmlHttp.responseText);
2318+ } else {
2319+ let error = xmlHttp.responseText;
2320+ try {
2321+ error = JSON.parse(error);
2322+ } catch(e) {
2323+ }
2324+ if (typeof error !== "object" || !error._templateMessage) {
2325+ error = new fcf.Exception("HTTP_REQUEST_ERROR", {code: xmlHttp.status});
2326+ } else {
2327+ error = new fcf.Exception(error);
2328+ }
2329+ a_act.error(error);
2330+ }
2331+ }
2332+ };
2333+ xmlHttp.open(method, url, a_options.async !== undefined ? a_options.async : true);
2334+ let serverName = (new fcf.RouteInfo(url)).server;
2335+ let isSendContext = url[0] == "@" || url[0] == "/" || (serverName && serverName == window.location.hostname) ;
2336+ if (isSendContext) {
2337+ let ctxt = fcf.append({}, fcf.getContext());
2338+ delete ctxt.route;
2339+ delete ctxt.args;
2340+ delete ctxt.safeEnv;
2341+ xmlHttp.setRequestHeader("fcf-context", fcf.encodeBase64(JSON.stringify(ctxt)));
2342+ }
2343+ if (typeof a_options.header == "object") {
2344+ for(let k in a_options.header) {
2345+ xmlHttp.setRequestHeader(k, a_options.header[k]);
2346+ }
2347+ }
2348+ if (method !== "GET") {
2349+ let foundContentType = false;
2350+ if (typeof a_options.header === "object") {
2351+ for(let k in a_options.header) {
2352+ if (k.toLowerCase() == "content-type"){
2353+ foundContentType = true;
2354+ break;
2355+ }
2356+ }
2357+ }
2358+ if (a_options.body instanceof FormData) {
2359+ if (!foundContentType)
2360+ xmlHttp.setRequestHeader("Content-Type", "multipart/form-data");
2361+ xmlHttp.send(a_options.body);
2362+ } else if (typeof a_options.body == "string"){
2363+ if (!foundContentType)
2364+ xmlHttp.setRequestHeader("Content-Type", "text/plain");
2365+ xmlHttp.send(a_options.body);
2366+ } else if (a_options.body !== undefined && a_options.body !== null) {
2367+ if (!foundContentType)
2368+ xmlHttp.setRequestHeader("Content-Type", "application/json");
2369+ xmlHttp.send(JSON.stringify(a_options.body));
2370+ } else {
2371+ xmlHttp.send(null);
2372+ }
2373+ } else {
2374+ xmlHttp.send(null);
2375+ }
2376+ }
2377+ })
2378+ .then((a_res) => {
2379+ if (a_options.format === "auto") {
2380+ try {
2381+ a_res = JSON.parse(a_res);
2382+ } catch(e) {
2383+ }
2384+ } else if (a_options.format === "json") {
2385+ a_res = JSON.parse(a_res);
2386+ }
2387+ return a_res;
2388+ })
2389+ .catch((a_error)=>{
2390+ return new fcf.Exception("REQUEST_ERROR", {url: a_url}, a_error);
2391+ })
2392+ }
2393+
2394+
2395+
2396+ fcf.getParamCount = (a_function) => {
2397+ if ("length" in a_function)
2398+ return a_function.length;
2399+ let str = a_function.toString();
2400+ let pos = str.indexOf("(");
2401+ let startPos = pos;
2402+ let end = str.indexOf(")");
2403+ if (pos == -1 || end == -1 || pos >= end)
2404+ return 0;
2405+ let counter = 0;
2406+ while(true){
2407+ pos = str.indexOf(",", pos+1);
2408+ if (pos == -1 || pos >= end)
2409+ break
2410+ if (!counter)
2411+ counter = 2;
2412+ else
2413+ ++counter;
2414+ }
2415+ if (counter == 0){
2416+ pos = startPos+1;
2417+ while(pos < end){
2418+ let c = str.charCodeAt(pos);
2419+ if (c > 32)
2420+ return 1;
2421+ ++pos
2422+ }
2423+ }
2424+ return counter;
2425+ }
2426+
2427+
2428+
2429+ /// @fn fcf.Actions fcf.actions(a_options = {deferred: false, noexcept: false})
2430+ /// @fn fcf.Actions fcf.actions(a_cb, a_options = {deferred: false, noexcept: false})
2431+ /// @fn fcf.Actions fcf.actions(Promise a_promise, a_options = {deferred: false, noexcept: false})
2432+ /// @fn fcf.Actions fcf.actions(fcf.Actions a_actions, a_options = {deferred: false, noexcept: false})
2433+ /// @brief Creates a new fcf.Actions object
2434+ /// @param function a_cb - Deferred action callback. The function can be asynchronous.
2435+ /// - Has the following s:
2436+ /// - mixed () - Callback without completion confirmation.
2437+ /// The callback is considered executed when the function has completed its execution.
2438+ /// If the function returns a Promise (which is asynchronous) or an fcf.Actions object,
2439+ /// then the operation completes when the Promise or fcf.Actions is executed.
2440+ /// - mixed (fcf.Actions.Act a_act) - Callback with completion confirmation.
2441+ /// The callback is considered executed when the fcf.Actions.Act.complete(mixed a_result)
2442+ /// or fcf.Actions.Act.error(Error a_error) method is called on the a_act object
2443+ /// @param object a_object = {deferred: false, noexcept: false}. Optional additional options
2444+ /// - boolean deferred = false - If the flag is true, then the added callbacks will not be executed
2445+ /// until the fcf.Actions.startup() method is called.
2446+ /// - boolean noexcept = false - If the flag is true, then if the callback ends with an error,
2447+ /// the queue execution is not stopped and the handlers passed to catch are not called.
2448+ /// - mixed errorResult = undefined - The result returned by the result() method in case of an error
2449+ /// - boolean quiet = false - If true, then raw error messages are not printed to the console.
2450+ /// @result fcf.Actions - Returns a new object
2451+ fcf.actions = (a_cb, a_options) => {
2452+ return new fcf.Actions(a_cb, a_options);
2453+ }
2454+
2455+
2456+
2457+ /// @class fcf.Actions
2458+ /// @brief The analogue of the Promise class with extended functionality.
2459+ /// @details a) Has the ability to get the current result:
2460+ /// SERVER SIDE EXAMPLE : let mod = fcf.require("module").result().
2461+ /// b) Has additional each & asyncEach methods.
2462+ /// c) Task processing callbacks can have an additional action-act
2463+ /// argument to complete the operation.
2464+ fcf.Actions = class Actions{
2465+
2466+ /// @method constructor(a_options = {deferred: false, noexcept: false, errorResult: undefined})
2467+ /// @method constructor(a_cb, a_options = {deferred: false, noexcept: false})
2468+ /// @param function a_cb - Action callback. The function can be asynchronous.
2469+ /// - Has the following s:
2470+ /// - mixed () - Callback without completion confirmation.
2471+ /// The callback is considered executed when the function has completed its execution.
2472+ /// If the function returns a Promise (which is asynchronous) or an fcf.Actions object,
2473+ /// then the operation completes when the Promise or fcf.Actions is executed.
2474+ /// - mixed (fcf.Actions.Act a_act) - Callback with completion confirmation.
2475+ /// The callback is considered executed when the fcf.Actions.Act.complete(mixed a_result)
2476+ /// or fcf.Actions.Act.error(Error a_error) method is called on the a_act object
2477+ /// @param object a_object = {deferred: false, noexcept: false}. Optional additional options
2478+ /// - boolean deferred = false - If the flag is true, then the added callbacks will not be executed
2479+ /// until the fcf.Actions.startup() method is called.
2480+ /// - boolean noexcept = false - If the flag is true, then if the callback ends with an error,
2481+ /// the queue execution is not stopped and the handlers passed to catch are not called.
2482+ /// - mixed errorResult = undefined - The result returned by the result() method in case of an error
2483+ /// - boolean quiet = false - If true, then raw error messages are not printed to the console.
2484+ /// @example
2485+ /// let actions = new fcf.Actions(async () => {
2486+ /// ...
2487+ /// return "result_value";
2488+ /// });
2489+ /// @example
2490+ /// let actions = new fcf.Actions((a_act)=>{
2491+ /// ...
2492+ /// a_act.complete("result_value");
2493+ /// });
2494+ /// @example
2495+ /// let actions = new fcf.Actions(async (a_act)=>{
2496+ /// console.log("constructor call")
2497+ /// ...
2498+ /// a_act.complete("result_value");
2499+ /// }, { deferred: true });
2500+ /// ...
2501+ /// actions.then(async (a_lastResult)=>{
2502+ /// console.log("callcack call: " + a_lastResult)
2503+ /// ...
2504+ /// })
2505+ /// ...
2506+ /// console.log("startup call")
2507+ /// await actions.startup();
2508+ /// console.log("end")
2509+ ///
2510+ /// // stdout: startup call
2511+ /// // stdout: constructor call
2512+ /// // stdout: callcack call: result_value
2513+ /// // stdout: end
2514+ constructor(a_cb, a_options) {
2515+ if (typeof a_cb == "object"){
2516+ a_options = a_cb;
2517+ a_cb = undefined;
2518+ }
2519+ this._flags = (a_options && !!a_options.deferred ? ACTIONS_FLAGS_DEFERRED : 0) |
2520+ (a_options && !!a_options.noexcept ? ACTIONS_FLAGS_NOEXCEPT : 0) |
2521+ (a_options && !!a_options.quiet ? ACTIONS_FLAGS_QUIET : 0);
2522+ this._stack = [];
2523+ if (a_options && a_options.context)
2524+ fcf.setContext(a_options.context);
2525+ this._state = fcf.getState();
2526+ this._errorcbs = [];
2527+ if (a_options && a_options.errorResult)
2528+ this._errorResult = a_options.errorResult;
2529+ if (typeof a_cb == "function") {
2530+ this.then(a_cb, undefined, true);
2531+ }
2532+ }
2533+
2534+
2535+
2536+ /// @method fcf.Actions then(function a_cb, function a_cberror = undefined)
2537+ /// @brief Adds a callback to the execution queue.
2538+ /// @details When the a_cb function is called, this is set to fcf.Actions object
2539+ /// @param function a_cb - Action callback. The function can be asynchronous.
2540+ /// - Has the following s:
2541+ /// - mixed a_cb(mixed a_lastResult) - Callback without completion confirmation.
2542+ /// The callback is considered executed when the function has completed its execution.
2543+ /// If the function returns a Promise (which is asynchronous) or an fcf.Actions object,
2544+ /// then the operation completes when the Promise or fcf.Actions is executed.
2545+ /// - mixed a_cb(mixed a_lastResult, fcf.Actions.Act a_act) - Callback with completion confirmation.
2546+ /// The callback is considered executed when the fcf.Actions.Act.complete(mixed a_result)
2547+ /// or fcf.Actions.Act.error(Error a_error) method is called on the a_act object
2548+ /// @param function a_cberror - Error handler
2549+ /// - Has the following :
2550+ /// - a_cberror(Error a_error)
2551+ /// @result fcf.Actions - Self object
2552+ /// @example
2553+ /// await (new fcf.Actions())
2554+ /// .then(async (a_lastResult)=>{
2555+ /// let result;
2556+ /// ...
2557+ /// return result;
2558+ /// });
2559+ /// @example
2560+ /// await (new fcf.Actions())
2561+ /// .then((a_lastResult, a_act)=>{
2562+ /// let error;
2563+ /// let result;
2564+ /// ...
2565+ /// if (error)
2566+ /// a_act.error(error);
2567+ /// else
2568+ /// a_act.complete(result);
2569+ /// });
2570+ then(a_cb, a_cberror, _a_skipResult) {
2571+ if (typeof Promise !== "undefined" && a_cb instanceof Promise){
2572+ let self = this;
2573+ a_cb.catch((e)=>{
2574+ self.error(e);
2575+ });
2576+ this.then(()=>{
2577+ return a_cb;
37582578 })
3759- .then((a_data)=>{
3760- fcf.NDetails.translations = a_data;
2579+ } else if (a_cb instanceof fcf.Actions){
2580+ this.then(()=>{
2581+ return a_cb;
37612582 })
3762- }
3763- var lang = a_lang ? a_lang : fcf.getContext().get("language");
3764- return fcf.NDetails.translations && fcf.NDetails.translations[lang] && fcf.NDetails.translations[lang][a_txt] ? fcf.NDetails.translations[lang][a_txt] : a_txt;
3765- }
3766- return a_txt;
3767- }
3768-
3769-
3770- fcf.checkReadOnlyJSInsturction = (a_instruction, a_options) => {
3771- }
3772-
3773-
3774-
3775-
3776- fcf.NDetails.currentContext = undefined;
3777-
3778-
3779- class FCFSafeContext {
3780- get(a_variableName) {
3781- let result = fcf.getContext().get(a_variableName);
3782- if (a_variableName == "safeEnv")
3783- return result;
3784- return typeof result == "object" ? fcf.clone(result) : result;
2583+ } else {
2584+ if (a_cb) {
2585+ this._stack.push({cb: a_cb, args: undefined, autoComplete: fcf.getParamCount(a_cb) < (_a_skipResult ? 1 : 2), skipResult: _a_skipResult });
2586+ this._execute();
2587+ }
2588+ }
2589+
2590+ if (a_cberror)
2591+ this.catch(a_cberror);
2592+ return this;
2593+ }
2594+
2595+
2596+
2597+ /// @method fcf.Actions each(object|array|function a_obj, function a_cb)
2598+ /// @brief Executes a_cb one by one for each element from a_obj
2599+ /// @details When the a_cb function is called, this is set to fcf.Actions object
2600+ /// @param object|array|function a_obj - An object for whose elements the a_cb callback will be called one by one
2601+ /// a_obj can be a function, in which case its result is the iterated object.
2602+ /// The function call is made just before the callbacks are executed.
2603+ /// @param function a_cb - Action callback for each a_obj element. The function can be asynchronous.
2604+ /// - Has the following s:
2605+ /// - mixed a_cb(mixed a_key, mixed a_value, mixed a_lastResult) -
2606+ /// Callback without completion confirmation.
2607+ /// The callback is considered executed when the function has completed its execution.
2608+ /// If the function returns a Promise (which is asynchronous) or an fcf.Actions object,
2609+ /// then the operation completes when the Promise or fcf.Actions is executed.
2610+ /// - mixed a_cb(mixed a_key, mixed a_value, mixed a_lastResult, fcf.Actions.Act a_act) -
2611+ /// Callback with completion confirmation.
2612+ /// The callback is considered executed when the fcf.Actions.Act.complete(mixed a_result)
2613+ /// or fcf.Actions.Act.error(Error a_error) method is called on the a_act object
2614+ /// @result fcf.Actions - Self object
2615+ /// @example
2616+ /// await (new fcf.Actions())
2617+ /// .each(["one", "two", "three"], async (a_key, a_value, a_lastResult, a_act)=>{
2618+ /// console.log(a_key, " : ", a_value)
2619+ /// a_act.complete(a_lastResult);
2620+ /// })
2621+ /// .then(()=>{
2622+ /// console.log("end")
2623+ /// })
2624+ ///
2625+ /// // stdout: 0 : one
2626+ /// // stdout: 1 : two
2627+ /// // stdout: 2 : three
2628+ /// // stdout: end
2629+ /// @example
2630+ /// let array;
2631+ /// await (new fcf.Actions())
2632+ /// .then(()=>{
2633+ /// array = ["one", "two", "three"];
2634+ /// })
2635+ /// .each(()=>{ return array; }, async (a_key, a_value, a_lastResult, a_act)=>{
2636+ /// console.log(a_key, " : ", a_value)
2637+ /// a_act.complete(a_lastResult);
2638+ /// })
2639+ /// .then(()=>{
2640+ /// console.log("end")
2641+ /// })
2642+ ///
2643+ /// // stdout: 0 : one
2644+ /// // stdout: 1 : two
2645+ /// // stdout: 2 : three
2646+ /// // stdout: end
2647+ each(a_obj, a_cb) {
2648+ if (typeof a_obj != "function"){
2649+ if (typeof a_obj == "object" && a_obj !== null) {
2650+ fcf.each(a_obj, (a_key, a_value)=>{
2651+ this._stack.push({cb: a_cb, args: [a_key, a_value], autoComplete: fcf.getParamCount(a_cb) < 4});
2652+ });
2653+ }
2654+ this._execute();
2655+ } else {
2656+ this.then((a_res) => {
2657+ a_obj = a_obj();
2658+ let reverse = [];
2659+ fcf.each(a_obj, (a_key, a_value)=>{
2660+ reverse.unshift([a_key, a_value]);
2661+ });
2662+ for(let pair of reverse) {
2663+ this._stack.unshift({cb: a_cb, args: pair, autoComplete: fcf.getParamCount(a_cb) < 4});
2664+ }
2665+ this._execute();
2666+ return a_res;
2667+ })
2668+ }
2669+ return this;
2670+ }
2671+
2672+
2673+
2674+ /// @method fcf.Actions asyncEach(object|array|function a_obj, function a_cb)
2675+ /// @brief Executes multiple a_cb at the same time for each element from a_obj
2676+ /// @details When the a_cb function is called, this is set to fcf.Actions object
2677+ /// @param object|array|function a_obj - An object for whose elements the a_cb callback will be called one by one
2678+ /// a_obj can be a function, in which case its result is the iterated object.
2679+ /// The function call is made just before the callbacks are executed.
2680+ /// @param function a_cb - Action callback for each a_obj element. The function can be asynchronous.
2681+ /// - Has the following s:
2682+ /// - mixed a_cb(mixed a_key, mixed a_value, mixed a_lastResult) -
2683+ /// Callback without completion confirmation.
2684+ /// The callback is considered executed when the function has completed its execution.
2685+ /// If the function returns a Promise (which is asynchronous) or an fcf.Actions object,
2686+ /// then the operation completes when the Promise or fcf.Actions is executed.
2687+ /// - mixed a_cb(mixed a_key, mixed a_value, mixed a_lastResult, fcf.Actions.Act a_act) -
2688+ /// Callback with completion confirmation.
2689+ /// The callback is considered executed when the fcf.Actions.Act.complete(mixed a_result)
2690+ /// or fcf.Actions.Act.error(Error a_error) method is called on the a_act object
2691+ /// @result fcf.Actions - Self object
2692+ /// @example
2693+ /// await (new fcf.Actions())
2694+ /// .asyncEach(["one", "two", "three"], async (a_key, a_value, a_lastResult, a_act)=>{
2695+ /// ...
2696+ /// setTimeout(()=>{
2697+ /// console.log(a_key, " : ", a_value);
2698+ /// a_act.complete(a_lastResult);
2699+ /// }, 100 - a_key);
2700+ /// })
2701+ /// .then(()=>{
2702+ /// console.log("end")
2703+ /// })
2704+ ///
2705+ /// // stdout: 2 : three
2706+ /// // stdout: 1 : two
2707+ /// // stdout: 0 : one
2708+ /// // stdout: end
2709+ asyncEach(a_obj, a_cb) {
2710+ let self = this;
2711+
2712+ this.then((a_value, a_act) => {
2713+ a_obj = typeof a_obj == "function" ? a_obj() : a_obj;
2714+ let autoComplete = fcf.getParamCount(a_cb) < 4;
2715+ let size = fcf.count(a_obj);
2716+ let counter = 0;
2717+ let error = false;
2718+
2719+ if (size == 0) {
2720+ a_act.complete();
2721+ return;
2722+ }
2723+
2724+ let brk = false;
2725+ fcf.each(a_obj, (k, a_itemValue)=>{
2726+ if (brk)
2727+ return false;
2728+ let act = {
2729+ _complete: false,
2730+ complete: function(a_value){
2731+ if (_isServer)
2732+ fcf.setState(self._state);
2733+ if (this._complete || error || self._error)
2734+ return;
2735+
2736+ ++counter;
2737+
2738+ self._result = a_value;
2739+
2740+ if (counter == size){
2741+ this._complete = true;
2742+ a_act.complete(a_value);
2743+ }
2744+ },
2745+ error: function(a_error){
2746+ if (_isServer)
2747+ fcf.setState(self._state);
2748+ if (self._flags & ACTIONS_FLAGS_NOEXCEPT){
2749+ this.complete();
2750+ return;
2751+ }
2752+
2753+ if (this._complete || error || self._error)
2754+ return;
2755+
2756+ self._result = undefined;
2757+ this._complete = true;
2758+ error = true;
2759+ a_act.error(a_error);
2760+ },
2761+ };
2762+
2763+ let args = [k, a_itemValue, self._result];
2764+ if (!autoComplete)
2765+ args.push(act);
2766+
2767+ let forceExit = false;
2768+ let res;
2769+ try {
2770+ let asyncPrefix = a_cb.toString().substr(0, 5);
2771+ if (asyncPrefix == "async" && a_cb.toString().charCodeAt(5) <= 32){
2772+ res = a_cb.apply(self, args).catch((e)=>{
2773+ forceExit = true;
2774+ brk = true;
2775+ act.error(e)
2776+ });
2777+ } else {
2778+ res = a_cb.apply(self, args)
2779+ }
2780+ } catch(e) {
2781+ brk = true;
2782+ act.error(e);
2783+ return;
2784+ }
2785+
2786+ if (forceExit)
2787+ return;
2788+
2789+ if (autoComplete && ((typeof Promise !== "undefined" && res instanceof Promise) || res instanceof fcf.Actions)) {
2790+ res
2791+ .then((a_value)=>{
2792+ act.complete(a_value);
2793+ })
2794+ .catch((a_error)=>{
2795+ act.error(a_error);
2796+ });
2797+ } else if (autoComplete) {
2798+ act.complete(res);
2799+ }
2800+ });
2801+ });
2802+ return this;
2803+ }
2804+
2805+
2806+
2807+ /// @method fcf.Actions catch(function a_cb)
2808+ /// @brief Adds an error handler callback.
2809+ /// @details If the callback returns or throws an error object,
2810+ /// then it replaces the current fcf.Actions error
2811+ /// When the handler is called when an error occurs,
2812+ /// exceptions thrown from the handler are not processed and go out
2813+ /// @param function a_cb - Error handler callback
2814+ /// - Has the following :
2815+ /// undefined|Error a_cb(Error a_error)
2816+ /// @result fcf.Actions - Self object
2817+ /// @example
2818+ /// (new fcf.Actions())
2819+ /// .then(()=>{
2820+ /// throw new Error("Some error");
2821+ /// })
2822+ /// .catch((a_error)=>{
2823+ /// console.error(a_error.message);
2824+ /// });
2825+ ///
2826+ /// // stderr: Some error
2827+ catch(a_cb) {
2828+ if (!this._stack)
2829+ return;
2830+ this._flags |= ACTIONS_FLAGS_CATCH;
2831+ let cerror;
2832+ if (a_cb) {
2833+ if (this._error) {
2834+ try {
2835+ let e = a_cb.call(this, this._error);
2836+ if (e instanceof Error) {
2837+ this._error = e;
2838+ }
2839+ } catch(e) {
2840+ this._error = e;
2841+ cerror = e;
2842+ }
2843+ }
2844+ this._errorcbs.push(a_cb);
2845+ }
2846+ if (cerror) {
2847+ throw cerror;
2848+ }
2849+ return this;
2850+ }
2851+
2852+
2853+
2854+ /// @method fcf.Actions finally(function a_cb)
2855+ /// @brief Adds a callback to the run queue that is also called will be called on error
2856+ /// @details When the handler is called when an error occurs,
2857+ /// exceptions thrown from the handler are not processed and go out
2858+ /// @param function a_cb - Action callback. The function can be asynchronous.
2859+ /// - Has the following s:
2860+ /// - mixed a_cb(mixed a_lastResult) - Callback without completion confirmation.
2861+ /// The callback is considered executed when the function has completed its execution.
2862+ /// If the function returns a Promise (which is asynchronous) or an fcf.Actions object,
2863+ /// then the operation completes when the Promise or fcf.Actions is executed.
2864+ /// - mixed a_cb(mixed a_lastResult, fcf.Actions.Act a_act) - Callback with completion confirmation.
2865+ /// The callback is considered executed when the fcf.Actions.Act.complete(mixed a_result)
2866+ /// or fcf.Actions.Act.error(Error a_error) method is called on the a_act object
2867+ /// @result fcf.Actions - Self object
2868+ /// @example
2869+ /// (new fcf.Actions())
2870+ /// .then(()=>{
2871+ /// ...
2872+ /// })
2873+ /// .finally(function (a_lastResult){
2874+ /// if (this.error()){
2875+ /// ...
2876+ /// } else {
2877+ /// ...
2878+ /// }
2879+ /// });
2880+ finally(a_cb) {
2881+ if (a_cb) {
2882+ this._stack.push({cb: a_cb, args: undefined, finally: true, autoComplete: fcf.getParamCount(a_cb) < 2 });
2883+ if (this._error) {
2884+ try {
2885+ a_cb.call(this, undefined, {complete: ()=>{}, error: ()=>{}});
2886+ } catch (e) {
2887+ this._error = e;
2888+ }
2889+ } else {
2890+ this._execute();
2891+ }
2892+ }
2893+ return this;
2894+ }
2895+
2896+
2897+
2898+ /// @method fcf.Actions startup()
2899+ /// @brief Starts execution of scheduled tasks if the fcf.Actions
2900+ /// object was created with the deferred flag set to true
2901+ /// @result fcf.Actions - Self object
2902+ startup() {
2903+ this._flags &= ~ACTIONS_FLAGS_DEFERRED;
2904+ this._execute();
2905+ return this;
2906+ }
2907+
2908+
2909+
2910+ /// @method Error error()
2911+ /// @brief Returns the current error set in the object
2912+ /// @result Error - Error object
2913+
2914+ /// @method fcf.Actions error(Error a_error)
2915+ /// @brief Sets an error for the fcf.Actions object
2916+ /// @param Error a_error - Installable error
2917+ /// @result fcf.Actions - Self object
2918+ error(a_error) {
2919+ if (!arguments.length) {
2920+ return this._error;
2921+ }
2922+ if (!this._error) {
2923+ this._error = a_error;
2924+ this._callErrors();
2925+ } else {
2926+ this._error = a_error;
2927+ }
2928+ return this;
2929+ }
2930+
2931+
2932+
2933+ /// @method mixed result()
2934+ /// @brief Returns the current result set in the object
2935+ /// @result mixed - Current result
2936+
2937+ /// @method fcf.Actions result(mixed a_result)
2938+ /// @brief Sets an result for the fcf.Actions object
2939+ /// @param mixed a_result - New result value
2940+ /// @result fcf.Actions - Self object
2941+ result(a_value) {
2942+ if (!arguments.length){
2943+ return !this._error ? this._result : this._errorResult;
2944+ }
2945+ this._result = a_value;
2946+ return this;
2947+ }
2948+
2949+
2950+
2951+ /// @method mixed exception()
2952+ /// @brief Throws an exception if the object of actions is in a state of error
2953+ /// @result fcf.Actions - Self object
2954+ exception() {
2955+ if (this._error) {
2956+ this.options({quiet: true});
2957+ throw this._error;
2958+ }
2959+ return this;
2960+ }
2961+
2962+
2963+
2964+ /// @method object options()
2965+ /// @brief Returns the options of the fcf.Actions object
2966+ /// @result object - fcf.Actions object options:
2967+ /// - boolean deferred - If the flag is true, then the added callbacks will not be executed
2968+ /// until the fcf.Actions.startup() method is called.
2969+ /// - boolean noexcept - If the flag is true, then if the callback ends with an error,
2970+ /// the queue execution is not stopped and the handlers passed to catch are not called.
2971+ /// - mixed errorResult - The result returned by the result() method in case of an error
2972+ /// - boolean quiet - If true, then raw error messages are not printed to the console.
2973+
2974+ /// @method fcf.Actions options(object a_options)
2975+ /// @brief Sets the options of the fcf.Actions object
2976+ /// @param object a_options - An options object that can store the following options:
2977+ /// - boolean deferred - If the flag is true, then the added callbacks will not be executed
2978+ /// until the fcf.Actions.startup() method is called.
2979+ /// - boolean noexcept - If the flag is true, then if the callback ends with an error,
2980+ /// the queue execution is not stopped and the handlers passed to catch are not called.
2981+ /// - mixed errorResult - The result returned by the result() method in case of an error
2982+ /// - boolean quiet - If true, then raw error messages are not printed to the console.
2983+ /// @result fcf.Actions - Self object
2984+ options(a_options) {
2985+ if (!arguments.length){
2986+ return {
2987+ noexcept: this._flags & ACTIONS_FLAGS_NOEXCEPT ? true : false,
2988+ quiet: this._flags & ACTIONS_FLAGS_QUIET ? true : false,
2989+ deferred: this._flags & ACTIONS_FLAGS_DEFERRED ? true : false,
2990+ errorResult: this._errorResult
2991+ };
2992+ } else {
2993+ if ("noexcept" in a_options){
2994+ let b = a_options.noexcept ? ACTIONS_FLAGS_NOEXCEPT : 0;
2995+ if (b) this._flags |= b;
2996+ else this._flags &= ~b;
2997+ }
2998+ if ("quiet" in a_options){
2999+ let b = a_options.quiet ? ACTIONS_FLAGS_QUIET : 0;
3000+ if (b) this._flags |= b;
3001+ else this._flags &= ~b;
3002+ }
3003+ if ("deferred" in a_options) {
3004+ let b = a_options.deferred ? ACTIONS_FLAGS_DEFERRED : 0;
3005+ if (b) this._flags |= b;
3006+ else this._flags &= ~b;
3007+ }
3008+ if ("errorResult" in a_options) {
3009+ this._errorResult = a_options.errorResult;
3010+ }
3011+ return this;
3012+ }
3013+ }
3014+
3015+
3016+
3017+ /// @method Promise promise()
3018+ /// @brief Adds an empty Promise object to the run queue and returns it
3019+ /// @result Promise - A new Promise object
3020+ promise() {
3021+ return new Promise((a_resolve, a_reject)=>{
3022+ this
3023+ .then((a_res)=>{
3024+ a_resolve(a_res);
3025+ })
3026+ .catch((a_error)=>{
3027+ a_reject(a_error);
3028+ })
3029+ })
3030+ }
3031+
3032+ _callErrors() {
3033+ let cerror;
3034+ for (let i = 0; i < this._errorcbs.length; ++i) {
3035+ try {
3036+ let e = this._errorcbs[i].call(this, this._error);
3037+ if (e instanceof Error) {
3038+ this._error = e;
3039+ }
3040+ } catch(e) {
3041+ this._error = e;
3042+ cerror = e;
3043+ }
3044+ }
3045+ for (let i = 0; i < this._stack.length; ++i){
3046+ if (this._stack[i].finally){
3047+ try {
3048+ this._stack[i].cb.call(this, undefined, {complete: ()=>{}, error: ()=>{}});
3049+ } catch(e) {
3050+ this._error = e;
3051+ }
3052+ }
3053+ }
3054+ if (!(this._flags & ACTIONS_FLAGS_QUIET)) {
3055+ if (!(this._flags & ACTIONS_FLAGS_CATCH)){
3056+ setTimeout(()=>{
3057+ if (!(this._flags & ACTIONS_FLAGS_CATCH) && !(this._flags & ACTIONS_FLAGS_QUIET)){
3058+ fcf.log.err("FCF", "Unhandled error in fcf.Actions (to handle catch method or \"quiet\" flag). Error: ", this._error)
3059+ }
3060+ }, 0);
3061+ }
3062+ }
3063+ if (cerror){
3064+ throw cerror;
3065+ }
3066+ }
3067+
3068+ _execute() {
3069+ let self = this;
3070+ if (this._flags & ACTIONS_FLAGS_RUN || !this._stack || this._stack.length == 0 || this._error){
3071+ if (_isServer && !(this._flags & ACTIONS_FLAGS_RUN))
3072+ fcf.setState(self._state);
3073+ return;
3074+ }
3075+ if (_isServer)
3076+ fcf.setState(self._state);
3077+ if (this._flags & ACTIONS_FLAGS_DEFERRED)
3078+ return;
3079+ this._flags |= ACTIONS_FLAGS_RUN;
3080+ let cbi = this._stack.shift();
3081+ let act = {
3082+ _end: false,
3083+ complete: function(a_value) {
3084+ if (_isServer)
3085+ fcf.setState(self._state);
3086+ if (this._end || self._error)
3087+ return;
3088+ this._end = true;
3089+ self._flags &= ~ACTIONS_FLAGS_RUN;
3090+ if ((typeof Promise !== "undefined" && a_value instanceof Promise) || a_value instanceof fcf.Actions){
3091+ a_value
3092+ .then((a_value)=>{
3093+ if (!cbi.finally)
3094+ self._result = a_value;
3095+ self._execute();
3096+ })
3097+ .catch((a_error)=>{
3098+ this._end = false;
3099+ this.error(a_error);
3100+ })
3101+ } else {
3102+ if (!cbi.finally)
3103+ self._result = a_value;
3104+ self._execute();
3105+ }
3106+ },
3107+ error: function(a_error) {
3108+ if (_isServer)
3109+ fcf.setState(self._state);
3110+ if (self._flags & ACTIONS_FLAGS_NOEXCEPT){
3111+ this.complete();
3112+ return;
3113+ }
3114+ if (this._end || self._error)
3115+ return;
3116+ this._end = true;
3117+ self._flags &= ~ACTIONS_FLAGS_RUN;
3118+ self._result = undefined;
3119+ self._error = a_error ? a_error : new Error("Unknown error");
3120+ self._callErrors();
3121+ },
3122+ };
3123+ let args = [];
3124+ if (cbi.args) {
3125+ args.push(cbi.args[0]);
3126+ args.push(cbi.args[1]);
3127+ }
3128+ if (!cbi.skipResult) {
3129+ args.push(this._result);
3130+ }
3131+ if (!cbi.autoComplete) {
3132+ args.push(act);
3133+ }
3134+
3135+ let res = undefined;
3136+ let forceExit = false;
3137+ try {
3138+ let asyncPrefix = cbi.cb.toString().substr(0, 5);
3139+ if (asyncPrefix == "async" && cbi.cb.toString().charCodeAt(5) <= 32){
3140+ res = cbi.cb.apply(this, args).catch((e)=>{
3141+ forceExit = true;
3142+ act.error(e);
3143+ });
3144+ } else {
3145+ res = cbi.cb.apply(this, args);
3146+ }
3147+ } catch(e) {
3148+ act.error(e);
3149+ return;
3150+ }
3151+
3152+ if (forceExit)
3153+ return;
3154+
3155+ if ((typeof Promise !== "undefined" && res instanceof Promise) || res instanceof fcf.Actions) {
3156+ if (cbi.autoComplete){
3157+ res
3158+ .then((a_value)=>{
3159+ act.complete(a_value);
3160+ })
3161+ .catch((a_error)=>{
3162+ act.error(a_error);
3163+ });
3164+ } else {
3165+ res
3166+ .catch((a_error)=>{
3167+ act.error(a_error);
3168+ });
3169+ }
3170+ } else if (cbi.autoComplete){
3171+ act.complete(res);
3172+ }
37853173 }
37863174 }
37873175
3788- fcf.Context = class FCFContext{
3789- constructor(a_obj){
3790-
3791- fcf.append(this, a_obj);
3792- if (!_isServer)
3793- this.route = new fcf.RouteInfo(this.route);
3794-
3795- if (_isServer){
3796- let globalObjects = fcf.append(
3797- [],
3798- Object.getOwnPropertyNames(_fcfGlobals()),
3799- Object.getOwnPropertyNames(global),
3800- ["Object","Function","Array","Number","parseFloat","parseInt","Infinity","NaN",
3801- "undefined","Boolean","String","Symbol","Date","Promise","RegExp","Error","EvalError",
3802- "RangeError","ReferenceError","SyntaxError","TypeError","URIError","globalThis","JSON",
3803- "Math","console","Intl","ArrayBuffer","Uint8Array","Int8Array","Uint16Array","Int16Array",
3804- "Uint32Array","Int32Array","Float32Array","Float64Array","Uint8ClampedArray","BigUint64Array",
3805- "BigInt64Array","DataView","Map","BigInt","Set","WeakMap","WeakSet","Proxy","Reflect",
3806- "decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape","eval",
3807- "isFinite","isNaN","SharedArrayBuffer","Atomics","FinalizationRegistry","WeakRef","WebAssembly",
3808- "global","process","Buffer","URL","URLSearchParams","TextEncoder","TextDecoder","clearInterval",
3809- "clearTimeout","setInterval","setTimeout","queueMicrotask","clearImmediate","setImmediate","fcf",
3810- "parts","endstr","anchorArr","c2","c1","inputArgs","module","require","assert","async_hooks",
3811- "buffer","child_process","cluster","constants","crypto","dgram","diagnostics_channel","dns",
3812- "domain","events","fs","http","http2","https","inspector","os","path","perf_hooks","querystring",
3813- "readline","repl","stream","string_decoder","sys","timers","tls","trace_events","url","vm","wasi",
3814- "worker_threads","_","_error","net","punycode","zlib","util","v8","tty","import","export","exports",
3815- "package","debugger","arguments","py","__filename","__dirname","GLOBAL","root","_fcfGlobals", "global, async, await"]);
3816-
3817- let safeEnv = {};
3818- for(let i = 0; i < globalObjects.length; ++i)
3819- if (globalObjects[i] != "undefined")
3820- safeEnv[globalObjects[i]] = null;
3821- safeEnv.Date = Date;
3822- safeEnv.Math = Math;
3823- safeEnv.decodeURIComponent = decodeURIComponent;
3824- safeEnv.encodeURIComponent = encodeURIComponent;
3825- safeEnv.fcf = fcf.append({}, fcf.NDetails.safeFcf);
3826- this.set("safeEnv", safeEnv);
3827- }
3828-
3829- }
3830-
3831- destroy() {
3832- for(let key in this)
3833- delete this[key];
3834- }
3835-
3836- get(a_variableName){
3837- let value = this[a_variableName];
3838- if (typeof value == "object")
3839- return fcf.clone(value);
3840- else
3841- return value;
3842- }
3843-
3844- set(a_variableName, a_value){
3845- this[a_variableName] = a_value;
3846- fcf.saveContext();
3176+ const ACTIONS_FLAGS_DEFERRED = 1;
3177+ const ACTIONS_FLAGS_NOEXCEPT = 2;
3178+ const ACTIONS_FLAGS_RUN = 4;
3179+ const ACTIONS_FLAGS_QUIET = 8;
3180+ const ACTIONS_FLAGS_CATCH = 16;
3181+
3182+
3183+
3184+ /// @fn fcf.module(object a_options)
3185+ /// @brief Declares the FCF module available on the server side and on the browser side
3186+ /// @param object a_options - Module options
3187+ /// - string name - Path (name) of the module in FCF notation
3188+ /// - array dependencies - Array with module dependency paths in FCF notation
3189+ /// - array lazy - An array of module dependencies that are loaded after the module body is executed.
3190+ /// These modules do not fall into the module arguments and
3191+ /// these dependencies are accessed through global variables.
3192+ /// Used for cyclic module dependencies.
3193+ /// - function module - A module function that should return module data.
3194+ /// The function arguments are the data of the modules declared in the dependencies property.
3195+ /// - Function signature: mixed module(<DEPENDENCY1, DEPENDENCY2, ...>)
3196+ /// - boolean quiet = false - If set to true, then module loading error messages are not performed..
3197+ /// @example
3198+ /// ========================================================================
3199+ /// Browser side example (Using native JS files on the browser side) (without fcf server)
3200+ ///
3201+ /// ------------------------------------------------------------------------
3202+ /// File /modules/package/helloWorld.js:
3203+ ///
3204+ /// window.someGlobalVariable = window.someGlobalVariable || {};
3205+ /// window.someGlobalVariable.helloWorld = function(){
3206+ /// document.write("<h3>Hello world</h3>")
3207+ /// }
3208+ ///
3209+ /// ------------------------------------------------------------------------
3210+ /// File /modules/package/page.js:
3211+ ///
3212+ /// fcf.module({
3213+ /// name: "package:page.js",
3214+ /// dependencies: ["package:helloWorld.js"],
3215+ /// module: (helloWorld)=>{
3216+ /// return function() {
3217+ /// document.write("<h1>Title</h1>");
3218+ /// helloWorld();
3219+ /// };
3220+ /// }
3221+ /// });
3222+ ///
3223+ /// ------------------------------------------------------------------------
3224+ /// File /index.html
3225+ ///
3226+ /// <html>
3227+ /// <head>
3228+ /// <script src="/node_modules/fcf-framework-core/fcf.js"></script>
3229+ /// <script>
3230+ /// // Adding data about the new "package" package to the configuration
3231+ /// fcf.getConfiguration().append({
3232+ /// sources: {
3233+ ///
3234+ /// // Information about "package" package
3235+ /// "package": {
3236+ ///
3237+ /// // URL to package files
3238+ /// webPackagePath: "/modules/package",
3239+ ///
3240+ /// // Mapping the module and the global variable as a result of module execution
3241+ /// files: {
3242+/// "helloWorld.js": { result: "someGlobalVariable.helloWorld" },
3243+ /// },
3244+ ///
3245+ /// }
3246+ /// }
3247+ /// });
3248+ /// </script>
3249+ /// </head>
3250+ /// <body>
3251+ /// <script>
3252+ /// fcf.require("package:page.js")
3253+ /// .then(([page])=>{
3254+ /// page();
3255+ /// });
3256+ /// </script>
3257+ /// </body>
3258+ /// </html>
3259+ ///
3260+ /// ------------------------------------------------------------------------
3261+ /// Result in browser:
3262+ ///
3263+ /// Title
3264+ ///
3265+ /// Hello world
3266+ ///
3267+ fcf.module = (a_options) => {
3268+ let moduleName = _getModulePath(a_options.name);
3269+ let rootCall = false;
3270+
3271+ if (!_modules[moduleName]){
3272+ _modules[moduleName] = {
3273+ state: "wait",
3274+ result: undefined,
3275+ error: undefined,
3276+ actions: fcf.actions(),
3277+ act: undefined,
3278+ dependencies: [],
3279+ lazy: [],
3280+ quiet: a_options.quiet ? 1 : 0,
3281+ };
3282+ rootCall = true;
3283+ } else if (_modules[moduleName].state != "wait") {
3284+ _modules[moduleName].dependencies = Array.isArray(a_options.dependencies) ? a_options.dependencies : [];
3285+ _modules[moduleName].lazy = Array.isArray(a_options.lazy) ? a_options.lazy : [];
3286+ return;
3287+ }
3288+
3289+ _modules[moduleName].dependencies = Array.isArray(a_options.dependencies) ? a_options.dependencies : [];
3290+ _modules[moduleName].lazy = Array.isArray(a_options.lazy) ? a_options.lazy : [];
3291+
3292+ let moduleInfo = _modules[moduleName];
3293+ moduleInfo.state = "processing";
3294+
3295+ fcf.each(a_options.dependencies, (a_key, a_value)=>{
3296+ a_options.dependencies[a_key] = a_value[0] != "/" ? fcf.ltrim(a_value, [":", "/"]) : "a_value";
3297+ });
3298+ fcf.each(a_options.lazy, (a_key, a_value)=>{
3299+ a_options.lazy[a_key] = a_value[0] != "/" ? fcf.ltrim(a_value, [":", "/"]) : "a_value";
3300+ });
3301+
3302+ let actions = rootCall ? _modules[moduleName].actions
3303+ : fcf.actions();
3304+
3305+ return actions
3306+ .asyncEach(a_options.dependencies, (k, mod)=>{
3307+ return _loadModule(mod, _modules[moduleName].quiet);
3308+ })
3309+ .then(()=>{
3310+ let dependencies = [];
3311+ fcf.each(a_options.dependencies, (k, mod)=>{
3312+ dependencies.push(_modules[_getModulePath(mod)].result);
3313+ })
3314+ moduleInfo.result = a_options.module.apply(undefined, dependencies);
3315+ if (_isServer) {
3316+ require.cache[moduleName].exports = moduleInfo.result;
3317+ }
3318+ moduleInfo.state = "ready";
3319+ if (moduleInfo.act){
3320+ moduleInfo.act.complete();
3321+ }
3322+ return fcf.actions()
3323+ .asyncEach(a_options.lazy, (k, mod)=>{
3324+ return _loadModule(mod, _modules[moduleName].quiet);
3325+ })
3326+ .then(()=>{
3327+ moduleInfo.state = "loaded";
3328+ _callOnModuleLoad();
3329+ return _waitLazyModule(moduleName);
3330+ })
3331+ .then(()=>{
3332+ moduleInfo.state = "ok";
3333+ })
3334+ .catch((e)=>{
3335+ _modules[moduleName].quiet = 1;
3336+ });
3337+ })
3338+ .then(()=>{
3339+ return moduleInfo.result;
3340+ })
3341+ .catch((a_error)=>{
3342+ moduleInfo.state = "error";
3343+ moduleInfo.error = a_error;
3344+ if (moduleInfo.act)
3345+ moduleInfo.act.error(a_error);
3346+ _callOnModuleLoad();
3347+ if (_isServer)
3348+ throw a_error;
3349+ })
3350+ }
3351+
3352+
3353+
3354+ /// @fn fcf.Actions->[MOD1, ...] fcf.require(string a_module1, string a_module2, ...);
3355+ /// @fn fcf.Actions->[MOD1, ...] fcf.require(string a_module1, string a_module2, ..., object a_options);
3356+ /// @fn fcf.Actions->[MOD1, ...] fcf.require([string] a_modules);
3357+ /// @fn fcf.Actions->[MOD1, ...] fcf.require([string] a_modules, object a_options);
3358+ /// @brief Loads JS modules
3359+ /// @param string a_moduleN - Module path in FCF notation
3360+ /// @param [string] a_modules - Array with paths to modules in FCF notation
3361+ /// @param object a_options - Extra options
3362+ /// - boolean quiet = false - If the parameter is true, then the output of
3363+ /// the module error message to the console is not displayed
3364+ /// - boolean async = true - If the parameter is true, then the loading of modules on the browser side is performed synchronously.
3365+ /// To get the result synchronously, you need to call the result method of the returned fcf.Actions object
3366+ /// - Example:
3367+ /// - let [mod1, mod2] = fcf.require("module1.js", "module2.js", {async: false}).result();
3368+ fcf.require = function (a_modules) {
3369+ let quietError = false;
3370+ let async = true;
3371+ let modules = [];
3372+ for(let arg of arguments) {
3373+ if (typeof arg == "object" && arg !== null && !Array.isArray(arg)) {
3374+ quietError = "quiet" in arg ? arg.quiet : false;
3375+ async = arg.async === false ? false : true;
3376+ } else if (typeof arg === "string") {
3377+ modules.push(arg);
3378+ } else if (Array.isArray(arg)){
3379+ modules = modules.concat(arg);
3380+ }
3381+ }
3382+ return fcf.actions({errorResult: []})
3383+ .asyncEach(modules, (k, mod) => {
3384+ return _loadModule(mod, quietError, !async);
3385+ })
3386+ .asyncEach(modules, (k, mod) => {
3387+ return _waitLazyModule(_getModulePath(mod));
3388+ })
3389+ .then(()=>{
3390+ let result = [];
3391+ fcf.each(modules, (k, mod) => {
3392+ result.push(_modules[_getModulePath(mod)].result);
3393+ });
3394+ return result;
3395+ });
3396+ }
3397+
3398+ function _loadModule(a_module, a_quietError, a_sync) {
3399+ let path = _getModulePath(a_module);
3400+ let state = _modules[path] ? _modules[path].state
3401+ : undefined;
3402+ if (_modules[path]){
3403+ _modules[path].quiet &= !!a_quietError;
3404+ }
3405+ if (state == "ready" || state == "loaded" || state == "ok") {
3406+ return fcf.actions()
3407+ .then(()=> {
3408+ _modules[path].quiet = 1;
3409+ return _modules[path].result;
3410+ });
3411+ } else if (state == "error") {
3412+ return fcf.actions()
3413+ .then((a_res, a_act)=> {
3414+ if (!_modules[path].quiet) {
3415+ fcf.log.err("FCF", `Failed load module ${a_module}.\n`, _modules[path].error);
3416+ }
3417+ _modules[path].quiet = 1;
3418+ a_act.error(_modules[path].error);
3419+ })
3420+
3421+ } else if ((!a_sync || _isServer) && (state == "wait" || state == "processing")) {
3422+ return fcf.actions()
3423+ .then((a_res, a_act)=> {
3424+ _modules[path].actions
3425+ .then(()=>{
3426+ a_act.complete(_modules[path].result);
3427+ })
3428+ .catch((a_error) => {
3429+ if (!_modules[path].quiet) {
3430+ fcf.log.err("FCF", `Failed load module ${a_module}.\n`, a_error);
3431+ }
3432+ a_act.error(a_error);
3433+ });
3434+ });
3435+ } else {
3436+ if (!_isServer && a_sync) {
3437+ ++_globalSyncModuleLoad;
3438+ }
3439+
3440+ _modules[path] = {
3441+ state: "wait",
3442+ result: undefined,
3443+ error: undefined,
3444+ actions: fcf.actions(),
3445+ act: undefined,
3446+ dependencies: [],
3447+ lazy: [],
3448+ quiet: a_quietError ? 1 : 0
3449+ };
3450+
3451+ return fcf.actions()
3452+ .then((a_res, a_rootAct)=>{
3453+ _modules[path].actions
3454+ .then((a_res, a_act)=>{
3455+ _modules[path].act = a_act;
3456+ let {module: moduleName, subpath: filePath} = fcf.getPathInfo(a_module);
3457+ if (_isServer) {
3458+ let result;
3459+ try {
3460+ result = require(path);
3461+ }catch(e) {
3462+ _modules[path].state = "error";
3463+ _modules[path].error = e;
3464+ _modules[path].act.error(e);
3465+ _callOnModuleLoad();
3466+ return;
3467+ }
3468+ let moduleInfo = fcf.getConfiguration().sources[moduleName];
3469+ let resultPath;
3470+ if (moduleInfo) {
3471+ resultPath = fcf.resolve(moduleInfo, ["serverFiles", filePath, "result"]) ||
3472+ fcf.resolve(moduleInfo, ["files", filePath, "result"]);
3473+ if (resultPath){
3474+ _modules[path].result = fcf.resolve(global, resultPath);
3475+ }
3476+ }
3477+ if (!resultPath) {
3478+ _modules[path].result = result;
3479+ }
3480+ if (_modules[path].state == "wait"){
3481+ _modules[path].state = "ok";
3482+ _modules[path].act.complete();
3483+ _callOnModuleLoad();
3484+ }
3485+ } else {
3486+ let moduleInfo = fcf.getConfiguration().sources[moduleName];
3487+ let loadStateTemplate = fcf.resolve(moduleInfo, ["webFiles", filePath, "loadState"]) ||
3488+ fcf.resolve(moduleInfo, ["files", filePath, "loadState"]);
3489+ let resultTemplate = fcf.resolve(moduleInfo, ["webFiles", filePath, "result"]) ||
3490+ fcf.resolve(moduleInfo, ["files", filePath, "result"]);
3491+ let needLoad = !loadStateTemplate || !fcf.tokenize(loadStateTemplate, {}, {quiet: true});
3492+ (
3493+ needLoad
3494+ ? fcf.load(path, {async: _globalSyncModuleLoad == 0})
3495+ : fcf.actions()
3496+ )
3497+ .then((a_res)=>{
3498+ if (needLoad) {
3499+ const state = _modules[path].state;
3500+ if (state == "ready" || state == "loaded" || state == "ok" || state == "error") {
3501+ return;
3502+ }
3503+ let script = document.createElement('script');
3504+ script.textContent = a_res + `\n//# sourceURL=${path}`;
3505+ document.head.appendChild(script);
3506+ }
3507+ if (moduleInfo && resultTemplate) {
3508+ _modules[path].result = fcf.resolve(window, resultTemplate);
3509+ }
3510+ if (_modules[path].state == "wait"){
3511+ _modules[path].state = "ok";
3512+ _modules[path].act.complete();
3513+ _callOnModuleLoad();
3514+ }
3515+ })
3516+ .catch((e)=>{
3517+ let error = new Error("Failed to receive module file '" + path + "'");
3518+ _modules[path].state = "error";
3519+ _modules[path].error = error;
3520+ _modules[path].act.error(error);
3521+ _callOnModuleLoad();
3522+ });
3523+ }
3524+ })
3525+ .then(()=>{
3526+ a_rootAct.complete(_modules[path].result);
3527+ })
3528+ .finally(()=>{
3529+ if (!_isServer && a_sync) {
3530+ if (_globalSyncModuleLoad)
3531+ --_globalSyncModuleLoad;
3532+ }
3533+ })
3534+ .catch((a_error) => {
3535+ if (!_modules[path].quiet) {
3536+ fcf.log.err("FCF", `Failed load module ${a_module}.\n`, a_error);
3537+ }
3538+ _modules[path].quiet = 1;
3539+ a_rootAct.error(a_error);
3540+ })
3541+ });
38473542 }
38483543 }
38493544
3850-
3851-
3852- fcf.NDetails.evalFunctions = {};
3853-
3854- fcf.NDetails.safeEvalResult_findKeyWord = (a_code, a_word)=>{
3855- let an = "a".charCodeAt(0);
3856- let zn = "z".charCodeAt(0);
3857- let An = "A".charCodeAt(0);
3858- let Zn = "Z".charCodeAt(0);
3859- let zeron = "0".charCodeAt(0);
3860- let ninen = "9".charCodeAt(0);
3861- let _n = "_".charCodeAt(0);
3862- let pos = a_code.indexOf(a_word);
3863- if (pos == -1)
3864- return false;
3865- let bc = a_code[pos-1] ? a_code[pos-1].charCodeAt(0) : 0;
3866- let nc = a_code[pos+a_word.length] ? a_code[pos+a_word.length].charCodeAt(0) : 0;
3867- let valid = (bc>=an && bc<=zn) || (bc>=An && bc<=Zn) || (bc==_n) ||
3868- (nc>=an && nc<=zn) || (nc>=An && nc<=Zn) || (nc>=zeron && nc<=ninen) || (nc==_n);
3869- return !valid;
3545+ let _globalSyncModuleLoad = 0;
3546+
3547+ function _getModulePath(a_module) {
3548+ a_module = fcf.resolvePath(a_module);
3549+ const isOnlyModule = a_module.indexOf("/") == -1 && a_module.indexOf("\\") == -1 && a_module.indexOf(":") == -1 && a_module.indexOf(".") == -1;
3550+ if (isOnlyModule && !_isServer && a_module[a_module.length-1] != ":"){
3551+ a_module += ":";
3552+ }
3553+ let {module: moduleName, subpath: relativePath} = fcf.getPathInfo(a_module);
3554+ if (moduleName === undefined) {
3555+ return fcf.getPath(a_module);
3556+ }
3557+ let configuration = fcf.getConfiguration();
3558+ if (!_isServer && !relativePath){
3559+ relativePath = configuration.sources[moduleName] && configuration.sources[moduleName].webMain
3560+ ? configuration.sources[moduleName].webMain
3561+ : "index.js";
3562+ }
3563+ let configPropFilesName = fcf.isServer() ? "serverFiles" : "webFiles";
3564+ let configPropFilePathName = fcf.isServer() ? "serverFilePath" : "webFilePath";
3565+ let relativeTemplate = fcf.resolve(configuration, ["sources", moduleName, configPropFilesName, relativePath, "filePath"]) ||
3566+ fcf.resolve(configuration, ["sources", moduleName, "files", relativePath, "filePath"]) ||
3567+ fcf.resolve(configuration, ["sources", moduleName, configPropFilePathName, "js"]) ||
3568+ fcf.resolve(configuration, ["sources", moduleName, "filePath", "js"]) ||
3569+ fcf.resolve(configuration, ["sources", moduleName, configPropFilePathName, "*"]) ||
3570+ fcf.resolve(configuration, ["sources", moduleName, "filePath", "*"]);
3571+ if (relativeTemplate) {
3572+ relativePath = fcf.tokenize(relativeTemplate,
3573+ {
3574+ path: relativePath,
3575+ directory: fcf.getDirectory(relativePath),
3576+ shortName: fcf.getShortFileName(relativePath),
3577+ name: fcf.getFileName(relativePath),
3578+ extension: fcf.getExtension(relativePath)
3579+ },
3580+ {
3581+ quiet: true
3582+ });
3583+ return fcf.getPath(moduleName + ":" + relativePath);
3584+ }
3585+ return _isServer && isOnlyModule ? a_module : fcf.getPath(moduleName + ":" + relativePath);
38703586 }
38713587
3872- fcf.safeEvalResult = (a_evalCode, a_evalEnvironment) => {
3873- let context = fcf.getContext();
3874- let safeEnv = context && context.get("safeEnv") ? context.get("safeEnv") : {};
3875- let result = undefined;
3876- let func = fcf.NDetails.evalFunctions[a_evalCode];
3877-
3878- if (!func){
3879- let foundErrorCommand = false;
3880- foundErrorCommand |= fcf.NDetails.safeEvalResult_findKeyWord(a_evalCode, "constructor");
3881- foundErrorCommand |= fcf.NDetails.safeEvalResult_findKeyWord(a_evalCode, "while");
3882- foundErrorCommand |= fcf.NDetails.safeEvalResult_findKeyWord(a_evalCode, "for");
3883- foundErrorCommand |= fcf.NDetails.safeEvalResult_findKeyWord(a_evalCode, "function");
3884- foundErrorCommand |= fcf.NDetails.safeEvalResult_findKeyWord(a_evalCode, "prototype");
3885- foundErrorCommand |= fcf.NDetails.safeEvalResult_findKeyWord(a_evalCode, "__proto__");
3886- foundErrorCommand |= a_evalCode.indexOf("__defineGetter__") != -1;
3887- foundErrorCommand |= a_evalCode.indexOf("__defineSetter__") != -1;
3888- foundErrorCommand |= a_evalCode.indexOf("=>") != -1;
3889- foundErrorCommand |= fcf.NDetails.safeEvalResult_findKeyWord(a_evalCode, "class");
3890- foundErrorCommand |= fcf.NDetails.safeEvalResult_findKeyWord(a_evalCode, "goto");
3891- foundErrorCommand |= fcf.NDetails.safeEvalResult_findKeyWord(a_evalCode, "delete");
3892- foundErrorCommand |= a_evalCode.indexOf("\\") != -1;
3893- foundErrorCommand |= a_evalCode.indexOf("`") != -1;
3894- foundErrorCommand |= a_evalCode.indexOf("++") != -1;
3895- foundErrorCommand |= a_evalCode.indexOf("--") != -1;
3896-
3897- if (!foundErrorCommand) {
3898- let lc = undefined;
3899- let quote = undefined;
3900- for(let i = 0; i < a_evalCode.length; ++i){
3901- let c = a_evalCode[i];
3902- if (c == "'" || c == "\""){
3903- if (quote)
3904- quote = undefined;
3905- else
3906- quote = c;
3907- continue;
3908- }
3909-
3910- if (!quote){
3911- if (a_evalCode[i-1] == "<" && c == "=")
3912- continue;
3913- if (a_evalCode[i-1] == ">" && c == "=")
3914- continue;
3915- if (a_evalCode[i-1] == "!" && c == "=")
3916- continue;
3917- if (a_evalCode[i-1] != "=" && c == "=" && a_evalCode[i+1] != "="){
3918- foundErrorCommand = true;
3919- break;
3920- }
3921- }
3922- }
3923- }
3924-
3925-
3926- if (foundErrorCommand)
3927- throw new fcf.Exception("ERROR_SAFE_EVAL_ERROR_COMMAND");
3928- if (a_evalCode.length > 512)
3929- throw new fcf.Exception("ERROR_SAFE_EVAL_ERROR_LENGTH", {length: 512});
3930-
3931- func = eval("(function fcfsafeEvalResult79323(a_env, a_args){ with(a_env) { with(a_args) { var result = undefined; result = " + a_evalCode + "; return result; } }; })");
3932- fcf.NDetails.evalFunctions[a_evalCode] = func;
3933- }
3934-
3935- result = func(safeEnv, a_evalEnvironment);
3588+ function _waitLazyModule(a_modulePath, _a_act) {
3589+ let actions;
3590+ if (_modules[a_modulePath].state == "ok") {
3591+ if (_a_act)
3592+ _a_act.complete();
3593+ return fcf.actions();
3594+ }
3595+ let deps = _getDepsModule(a_modulePath);
3596+ if (!_a_act) {
3597+ actions = fcf.actions();
3598+ actions.then((a_res, a_act)=>{ _a_act = a_act; });
3599+ }
3600+ let error;
3601+ let full = true;
3602+ for (let dep in deps) {
3603+ if (!_modules[dep]) {
3604+ full = false;
3605+ continue;
3606+ }
3607+ if (_modules[dep].state == "error") {
3608+ error = _modules[dep].error;
3609+ } else if (_modules[dep].state != "loaded" && _modules[dep].state != "ok") {
3610+ full = false;
3611+ }
3612+ }
3613+ if (error) {
3614+ _a_act.error(error);
3615+ } else if (full) {
3616+ _a_act.complete();
3617+ } else {
3618+ _attachOnModuleLoad(()=>{
3619+ _waitLazyModule(a_modulePath, _a_act);
3620+ });
3621+ }
3622+ return actions;
3623+ }
3624+
3625+ function _attachOnModuleLoad(a_cb){
3626+ _moduleCallbacks.push(a_cb);
3627+ }
3628+
3629+ function _callOnModuleLoad(){
3630+ let arr = _moduleCallbacks;
3631+ _moduleCallbacks = [];
3632+ for(let i = 0; i < arr.length; ++i){
3633+ arr[i]();
3634+ }
3635+ }
3636+
3637+ function _getDepsModule(a_modulePath, _a_selfPath, _a_dst){
3638+ if (!_a_selfPath)
3639+ _a_selfPath = a_modulePath;
3640+ if (!_a_dst)
3641+ _a_dst = {};
3642+ if (_modules[a_modulePath]) {
3643+ for(let i = 0; i < _modules[a_modulePath].dependencies.length; ++i){
3644+ let dep = _getModulePath(_modules[a_modulePath].dependencies[i]);
3645+ if (dep in _a_dst || _a_selfPath == dep)
3646+ continue;
3647+ _a_dst[dep] = 1;
3648+ _getDepsModule(dep, _a_selfPath, _a_dst);
3649+ }
3650+ for(let i = 0; i < _modules[a_modulePath].lazy.length; ++i){
3651+ let dep = _getModulePath(_modules[a_modulePath].lazy[i]);
3652+ if (dep in _a_dst || _a_selfPath == dep)
3653+ continue;
3654+ _a_dst[dep] = 1;
3655+ _getDepsModule(dep, _a_selfPath, _a_dst);
3656+ }
3657+ }
3658+ return _a_dst;
3659+ }
3660+
3661+ let _modules = {};
3662+ let _moduleCallbacks = [];
3663+
3664+
3665+
3666+ /// @fn string fcf.stackToString(string|Array|Error a_stack)
3667+ /// @brief Converts stack to string
3668+ /// @param string|Array|Error a_stack - Stack string or Error object or Array parsed by fcf.parseStack
3669+ /// @result string - A string describing the call stack
3670+ fcf.stackToString = (a_stack) => {
3671+ a_stack = fcf.parseStack(a_stack);
3672+ let result = "";
3673+ for(let i = 0; i < a_stack.length; ++i) {
3674+ if (i) {
3675+ result += "\n";
3676+ }
3677+ result += fcf.str(a_stack[i].function) + " (" +
3678+ a_stack[i].file + ":" +
3679+ fcf.str(a_stack[i].line) + ":" + fcf.str(a_stack[i].column) + ")";
3680+ }
39363681 return result;
39373682 }
39383683
3939- class FCFScriptExecutor {
3940- constructor(){
3941- this._funcStorage = {};
3942- this._dataFuncStorage = {};
3943- }
3944-
3945- execute(a_code, a_args, a_file, a_startLine, a_startChar, a_async){
3946- let self = this;
3947- let error = undefined;
3948-
3949- if (a_file)
3950- a_file = fcf.getPath(a_file);
3951-
3952- let result = undefined;
3953-
3954- return this._execute(
3955- a_code,
3956- a_args,
3957- a_file,
3958- a_startLine,
3959- function(a_code, a_args){
3960- let func = undefined;
3961- let code = undefined;
3962- try {
3963- if (self._funcStorage[a_file] && self._funcStorage[a_file][a_startLine] && self._funcStorage[a_file][a_startLine][a_startChar]){
3964- func = self._funcStorage[a_file][a_startLine][a_startChar].func;
3965- code = self._funcStorage[a_file][a_startLine][a_startChar].code;
3966- } else {
3967- code = "(" + (a_async ? "async" : "") + " function fcfinnerevalfunction(a_args){";
3968- for(let key in a_args)
3969- code += `var ${key} = a_args.${key};`;
3970- code += a_code;
3971- code += "})";
3972- func = eval(code);
3973- if (!self._funcStorage[a_file])
3974- self._funcStorage[a_file] = {};
3975- if (!self._funcStorage[a_file][a_startLine])
3976- self._funcStorage[a_file][a_startLine] = {};
3977- self._funcStorage[a_file][a_startLine][a_startChar] = {func: func, code: code};
3978- }
3979- if (a_async){
3980- result = fcf.actions();
3981- result.then(()=>{ return func(a_args); });
3982- } else {
3983- result = func(a_args);
3984- }
3985-
3986- } catch(e) {
3987- error = e;
3988- }
3989- self = undefined;
3990- return {
3991- code: code,
3992- error: error,
3993- result: result,
3994- };
3995- }
3996- );
3997- }
3998-
3999- parse(a_code, a_args, a_file, a_startLine, a_startChar){
4000- let self = this;
4001- let error = undefined;
4002-
4003- if (a_file)
4004- a_file = fcf.getPath(a_file);
4005-
4006- return this._execute(
4007- a_code,
4008- a_args,
4009- a_file,
4010- a_startLine,
4011- function(a_code, a_args){
4012- let func = undefined;
4013- let code = undefined;
4014- let result = undefined;
4015- try {
4016- if (self._funcStorage[a_file] && self._funcStorage[a_file][a_startLine] && self._funcStorage[a_file][a_startLine][a_startChar]){
4017- func = self._funcStorage[a_file][a_startLine][a_startChar].func;
4018- code = self._funcStorage[a_file][a_startLine][a_startChar].code;
4019- } else {
4020- code = "(function fcfinnerevalfunction(a_args){";
4021- for(let key in a_args)
4022- code += "var "+ key + " = a_args." + key + ";";
4023- code += "var result = (" + a_code + "); return result; })";
4024- func = eval(code);
4025- if (!self._funcStorage[a_file])
4026- self._funcStorage[a_file] = {};
4027- if (!self._funcStorage[a_file][a_startLine])
4028- self._funcStorage[a_file][a_startLine] = {};
4029- self._funcStorage[a_file][a_startLine][a_startChar] = {func: func, code: code};
4030- }
4031- result = func(a_args);
4032- } catch(e) {
4033- error = e;
4034- }
4035- self = undefined;
4036- return {
4037- code: code,
4038- error: error,
4039- result: result,
4040- };
4041- }
4042- );
4043- }
4044-
4045- clear(a_fileName){
4046- a_fileName = fcf.getPath(a_fileName);
4047- delete this._funcStorage[a_fileName];
4048- }
4049-
4050- _execute(a_code, a_args, a_file, a_startLine, a_handler){
4051- let self = this;
4052-
4053- let handlerResult = a_handler(a_code, a_args);
4054- if (handlerResult.result instanceof Promise || handlerResult.result instanceof fcf.Actions) {
4055- let rs = handlerResult.result;
4056- handlerResult.result = fcf.actions()
4057- .then((a_res, a_act)=>{
4058- fcf.actions()
4059- .then(()=>{
4060- return rs;
4061- })
4062- .then((a_res)=>{
4063- a_act.complete(a_res);
4064- })
4065- .catch((a_error)=>{
4066- try {
4067- self._error(a_code, a_error, a_file, a_startLine);
4068- } catch (error){
4069- a_act.error(error);
4070- }
4071- })
4072- });
4073- } else {
4074- if (handlerResult.error)
4075- this._error(a_code, handlerResult.error, a_file, a_startLine);
4076- }
4077- return handlerResult.result;
4078- }
4079-
4080- _error(a_code, a_error, a_file, a_startLine){
4081- a_startLine = a_startLine ? a_startLine : 0;
4082- let stack = a_error.stackArr ? a_error.stackArr :
4083- a_error.stack ? fcf.Exception.parseStack(a_error.stack) :
4084- a_error.stacktrace ? fcf.Exception.parseStack(a_error.stacktrace) :
4085- [];
4086- let line = undefined;
4087- for(let i = 0; i < stack.length; ++i){
4088- if(stack[i].file.indexOf("eval at") == 0 || stack[i].file.indexOf("fcfEvalTmpl7652492") == 0 || stack[i].function == "fcfinnerevalfunction"){
4089- line = parseInt(stack[i].line) + a_startLine;
4090- break;
4091- }
4092- }
4093-
4094- if (fcf.isServer() && line === undefined){
4095- let babel;
4096- fcf.require(["fcf:NSystem/babel.js"]).then((a_modules)=>{ babel = a_modules[0] });
4097- try {
4098- babel.transformSync(a_code);
4099- } catch(e){
4100- let match = e.toString().match(/\(([0-9]*)/)
4101- if (match)
4102- line = parseInt(match[1]) + a_startLine;
4103- line = !line && !a_startLine ? 0 :
4104- !line ? a_startLine :
4105- line;
4106- throw new fcf.Exception("ERROR_EVAL_SCRIPT", [a_file, line], a_error);
4107- }
4108- }
4109-
4110- line = !line && !a_startLine ? 0 :
4111- !line ? a_startLine :
4112- line;
4113- throw new fcf.Exception("ERROR_EVAL_SCRIPT", [a_file, line], a_error);
4114- }
4115- };
4116- fcf.scriptExecutor = new FCFScriptExecutor();
4117-
4118-
4119- fcf.getContext = () => {
4120- if (_isServer)
4121- return fcf.NServer && fcf.NServer.Application ? fcf.NServer.Application.getContext() :
4122- fcf.NServer && fcf.NServer.Application ? fcf.NServer.Application.getEmptyContext() :
4123- undefined;
4124- else
4125- return fcf.NDetails.currentContext;
4126- }
4127-
4128- fcf.getSafeContext = () => {
4129- return new FCFSafeContext();
4130- }
4131-
4132- fcf.setContext = (a_context) => {
4133- if (_isServer) {
4134- if (fcf.NServer && fcf.NServer.Application) {
4135- fcf.NServer.Application.setContext(a_context);
4136- }
3684+
3685+
3686+ /// @fn string fcf.errorToString(Error a_error, boolean a_fullMode = false)
3687+ /// @brief Converts stack to string
3688+ /// @param string|Error a_error - Error object
3689+ /// @param boolean a_fullMode = false - If the parameter is true, then the stack information is added to the error message.
3690+ /// @result string - String describing the error
3691+ fcf.errorToString = (a_error, a_fullMode) => {
3692+ if (a_error instanceof fcf.Exception) {
3693+ return a_error.toString(a_fullMode, a_fullMode);
41373694 } else {
4138- fcf.NDetails.currentContext = a_context;
3695+ return a_fullMode ? a_error.toString() + "\nStack" + fcf.replaceAll("\n" + fcf.stackToString(a_error), "\n", "\n ")
3696+ : a_error.toString();
41393697 }
41403698 }
41413699
4142- fcf.getState = () => {
4143- if (_isServer){
4144- return fcf.NServer && fcf.NServer.Application ? fcf.NServer.Application.getState() :undefined;
4145- }
4146- return {context: fcf.getContext()};
4147- }
4148-
4149- fcf.setState = (a_state) => {
4150- if (_isServer){
4151- if (fcf.NServer && fcf.NServer.Application) {
4152- fcf.NServer.Application.setState(a_state)
4153- }
4154- } else {
4155- if (typeof a_state === "object")
4156- fcf.setContext(a_state.context);
4157- }
3700+
3701+
3702+ /// @fn [object] fcf.parseStack(string|Error a_stack)
3703+ /// @brief Performs stack parsing
3704+ /// @param string|Error a_stack - Stack string or Error object
3705+ /// @result [object] - Array with objects about stack calls
3706+ /// - Object properties:
3707+ /// - string file - JS file
3708+ /// - string function - function name
3709+ /// - number line - line number
3710+ /// - number column - column number
3711+ fcf.parseStack = function(a_stack) {
3712+ if (a_stack instanceof Error) {
3713+ a_stack = a_stack.stack;
3714+ } else if (a_stack instanceof fcf.Exception) {
3715+ return a_stack.stackArr;
3716+ } else if (Array.isArray(a_stack)) {
3717+ return a_stack;
3718+ }
3719+ if (typeof a_stack != "string"){
3720+ return [];
3721+ }
3722+ return _parseStack(a_stack.split("\n")).stack;
41583723 }
41593724
41603725
4161- fcf.saveContext = (a_request) => {
4162- let sortCtxt = fcf.append({}, fcf.getContext());
4163- delete sortCtxt.route;
4164- delete sortCtxt.args;
4165- delete sortCtxt.safeEnv;
4166-
4167- if (_isServer){
4168- if (a_request)
4169- a_request.setCookie("___fcf___context",
4170- fcf.base64Encode(JSON.stringify(sortCtxt)),
4171- {
4172- maxAge: fcf.application.getConfiguration().sessionLifetime*3600*24,
4173- path: "/",
4174- sameSite: "Lax",
4175- });
4176- } else {
4177- let d = new Date();
4178- d.setTime(d.getTime() + (365*24*60*60*1000));
4179- var expires = "expires="+ d.toUTCString();
4180- document.cookie = "___fcf___context=" + fcf.base64Encode(JSON.stringify(sortCtxt)) + "; path=/ ; SameSite=Lax;" + expires;
3726+
3727+ /// @fn [{message, fullMessage, stack, stackArr}] fcf.parseError(Error a_error)
3728+ /// @brief Parses the error object
3729+ /// @param Error a_error - Error object
3730+ /// @result [object] - Array with objects about stack calls
3731+ /// - Object properties:
3732+ /// - string message - Simple message of the error
3733+ /// - string fullMessage - Full error message
3734+ /// - string stack - String representation of the stack, the result of fcf.stackToString
3735+ /// - [object] stackArr - Array with objects about stack calls
3736+ /// - Object properties:
3737+ /// - string file - JS file
3738+ /// - string function - function name
3739+ /// - number line - line number
3740+ /// - number column - column number
3741+ fcf.parseError = function(a_error) {
3742+ if (a_error instanceof fcf.Exception) {
3743+ return {
3744+ message: a_error.toString(),
3745+ fullMessage: a_error.toString(false, true),
3746+ stack: fcf.stackToString(a_error.stackArr),
3747+ stackArr: a_error.stackArr,
3748+ }
3749+ } else if (!(a_error instanceof Error)) {
3750+ return {
3751+ message: "",
3752+ fullMessage: "",
3753+ stack: "",
3754+ stackArr: [],
3755+ };
3756+ }
3757+ let arr = a_error.stack.split("\n");
3758+ let stackInfo = _parseStack(arr);
3759+ let fullMessage = "";
3760+ for(let j = 0; j <= stackInfo.preStart; ++j) {
3761+ fullMessage += arr[j];
3762+ fullMessage += "\n";
3763+ }
3764+ if (!fullMessage) {
3765+ fullMessage = a_error.message;
3766+ }
3767+ return {
3768+ message: a_error.message,
3769+ fullMessage: fullMessage,
3770+ stackArr: stackInfo.stack,
3771+ stack: fcf.stackToString(stackInfo.stack)
41813772 }
41823773 }
41833774
4184-
4185- fcf.NDetails._thisLoggerMessage = false;
4186-
4187- var Logger = function(){
4188- this.TST = 0;
4189- this.CRH = 10;
4190- this.ERR = 20;
4191- this.WRN = 30;
4192- thi