Main functions and classes of the FCF framework
Révision | f3713d135d07eea78fc9a110d102fc13e72cb7a0 (tree) |
---|---|
l'heure | 2023-05-01 04:00:14 |
Auteur | vmarkin |
Commiter | vmarkin |
version 2.0.2
@@ -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 | +} |
@@ -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 | +})(); |
@@ -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 | +}; |
@@ -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 | +} |
@@ -13,24 +13,32 @@ | ||
13 | 13 | for(let dir of directories){ |
14 | 14 | if (typeof dir !== "string") |
15 | 15 | continue; |
16 | - if (paths.indexOf(dir) == -1) | |
16 | + dir = fcf.getPath(dir); | |
17 | + if (paths.indexOf(dir) == -1){ | |
17 | 18 | paths.unshift(dir); |
18 | - needUpdate = true; | |
19 | + needUpdate = true; | |
20 | + } | |
19 | 21 | } |
20 | 22 | if (needUpdate) { |
21 | 23 | process.env.NODE_PATH = paths.join(":"); |
24 | + libModule.Module._initPaths(); | |
22 | 25 | } |
23 | - libModule.Module._initPaths(); | |
24 | 26 | _directories = {}; |
25 | 27 | } |
26 | 28 | |
27 | 29 | resolveModule(a_module){ |
28 | 30 | a_module = a_module.split("\\")[0].split("/")[0]; |
29 | 31 | 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 | + } | |
34 | 42 | } |
35 | 43 | } |
36 | 44 | return _directories[a_module]; |
@@ -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 | +}; |
@@ -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 | +}); |
@@ -1,11 +1,8 @@ | ||
1 | -//NEED REMOVE | |
2 | -function _fcfGlobals() { return this; } | |
3 | - | |
4 | 1 | (function() { |
5 | 2 | var fcf = typeof global !== 'undefined' && global.fcf ? global.fcf : |
6 | 3 | typeof window !== 'undefined' && window.fcf ? window.fcf : |
7 | 4 | {}; |
8 | - fcf.NDetails = fcf.NDetails || {}; | |
5 | + fcf.NDetails = fcf.NDetails || {}; | |
9 | 6 | fcf.namespaces = fcf.namespaces || {}; |
10 | 7 | |
11 | 8 | if (typeof module !== 'undefined') |
@@ -31,11 +28,16 @@ | ||
31 | 28 | |
32 | 29 | |
33 | 30 | |
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"); | |
39 | 41 | } |
40 | 42 | |
41 | 43 |
@@ -51,57 +53,58 @@ | ||
51 | 53 | /// @details NaN, undefined and null values are represented as an empty string |
52 | 54 | /// @param mixed a_data - source data |
53 | 55 | /// @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(); | |
66 | 68 | } |
67 | 69 | |
68 | 70 | |
69 | 71 | |
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) | |
92 | 73 | /// @brief Escapes single and double quotes with \ |
93 | 74 | /// @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. | |
94 | 78 | /// @result string - String with escaped characters |
95 | - fcf.escapeQuotes = (a_str) => { | |
79 | + fcf.escapeQuotes = (a_str, a_quote) => { | |
96 | 80 | 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 | + } | |
105 | 108 | } |
106 | 109 | } |
107 | 110 | return result; |
@@ -148,7 +151,7 @@ | ||
148 | 151 | /// @brief Performs replacement of all searched substrings in a string |
149 | 152 | /// @param string a_str - Source string |
150 | 153 | /// @param string a_search - Search substring |
151 | - /// @param string a_str - replacement | |
154 | + /// @param string a_replacement - replacement | |
152 | 155 | /// @result string - New string |
153 | 156 | fcf.replaceAll = (a_str, a_search, a_replacement) => { |
154 | 157 | a_str = fcf.str(a_str); |
@@ -160,7 +163,7 @@ | ||
160 | 163 | |
161 | 164 | |
162 | 165 | /// @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 | |
164 | 167 | /// @param string a_str - Source string |
165 | 168 | /// @result string - Decoding result string |
166 | 169 | fcf.decodeHtml = (a_str) => { |
@@ -255,12 +258,7 @@ | ||
255 | 258 | /// @result string - Encoded string |
256 | 259 | fcf.encodeHtml = (a_str) => { |
257 | 260 | 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); | |
264 | 262 | for(let i = 0; i < a_str.length; ++i) { |
265 | 263 | let c = a_str[i]; |
266 | 264 | switch(c){ |
@@ -282,45 +280,17 @@ | ||
282 | 280 | /// @param string a_str - Source string |
283 | 281 | /// @result string - String with tags removed |
284 | 282 | 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, ""); | |
289 | 284 | } |
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]) | |
320 | 290 | /// @brief Removes the given characters from the beginning of a string |
321 | 291 | /// @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 | |
324 | 294 | /// @result string - New string |
325 | 295 | fcf.ltrim = (a_str, a_arr) => { |
326 | 296 | a_str = fcf.str(a_str); |
@@ -333,8 +303,8 @@ | ||
333 | 303 | /// @fn string fcf.ltrim(string a_str, a_arr = [false]) |
334 | 304 | /// @brief Removes the given characters from the end of a string |
335 | 305 | /// @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 | |
338 | 308 | /// @result string - New string |
339 | 309 | fcf.rtrim = (a_str, a_arr) => { |
340 | 310 | a_str = fcf.str(a_str); |
@@ -347,8 +317,8 @@ | ||
347 | 317 | /// @fn string fcf.ltrim(string a_str, a_arr = [false]) |
348 | 318 | /// @brief Removes the given characters from the beginning and end of a string |
349 | 319 | /// @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 | |
352 | 322 | /// @result string - New string |
353 | 323 | fcf.trim = (a_str, a_arr) => { |
354 | 324 | a_str = fcf.str(a_str); |
@@ -371,7 +341,7 @@ | ||
371 | 341 | for(let i = 0; i < a_arr.length; ++i){ |
372 | 342 | if (!a_arr[i]) { |
373 | 343 | let cn = a_str.charCodeAt(pos); |
374 | - if (fcf.isSpaceCode(cn)){ | |
344 | + if (cn >= 0 && cn <= 32){ | |
375 | 345 | found = true; |
376 | 346 | break; |
377 | 347 | } |
@@ -396,7 +366,7 @@ | ||
396 | 366 | for(let i = 0; i < a_arr.length; ++i){ |
397 | 367 | if (!a_arr[i]) { |
398 | 368 | let cn = a_str.charCodeAt(pos); |
399 | - if (fcf.isSpaceCode(cn)){ | |
369 | + if (cn >= 0 && cn <= 32){ | |
400 | 370 | found = true; |
401 | 371 | break; |
402 | 372 | } |
@@ -413,73 +383,58 @@ | ||
413 | 383 | |
414 | 384 | |
415 | 385 | |
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 | |
445 | 388 | /// @param string a_str - Source string |
446 | 389 | /// @param number a_len - The length to which you want to pad the original string |
447 | 390 | /// @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 | |
448 | 395 | /// @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) | |
451 | 403 | 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; | |
467 | 421 | } |
468 | 422 | |
469 | 423 | |
470 | 424 | |
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) | |
472 | 426 | /// @brief Creates a string from random characters in hex format |
473 | 427 | /// @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. | |
476 | 430 | /// @result string - String with random hex characters |
477 | - fcf.id = (a_size, a_unsafeFirstChar) => { | |
431 | + fcf.id = (a_size, a_safeFirstChar) => { | |
478 | 432 | a_size = a_size || 32; |
433 | + a_safeFirstChar = a_safeFirstChar === undefined ? true : a_safeFirstChar; | |
479 | 434 | let result = ""; |
480 | 435 | 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))]; | |
483 | 438 | } |
484 | 439 | return result; |
485 | 440 | } |
@@ -490,70 +445,28 @@ | ||
490 | 445 | /// @brief Creates a UUID string (v4) |
491 | 446 | /// @result string - UUID string |
492 | 447 | 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; | |
497 | 461 | } |
498 | 462 | |
499 | 463 | |
500 | 464 | |
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 | |
555 | 468 | /// @result string - Result string |
556 | - fcf.base64Decode = function (a_input) { | |
469 | + fcf.decodeBase64 = function (a_input) { | |
557 | 470 | function utf8Decode (utftext) { |
558 | 471 | let string = ""; |
559 | 472 | let i = 0; |
@@ -583,7 +496,7 @@ | ||
583 | 496 | let chr1, chr2, chr3; |
584 | 497 | let enc1, enc2, enc3, enc4; |
585 | 498 | 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, ""); | |
587 | 500 | while (i < a_input.length) { |
588 | 501 | enc1 = _keyBase64.indexOf(a_input.charAt(i++)); |
589 | 502 | enc2 = _keyBase64.indexOf(a_input.charAt(i++)); |
@@ -604,6 +517,57 @@ | ||
604 | 517 | return output; |
605 | 518 | } |
606 | 519 | |
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 | + | |
607 | 571 | const _keyBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; |
608 | 572 | |
609 | 573 |
@@ -625,28 +589,33 @@ | ||
625 | 589 | |
626 | 590 | |
627 | 591 | /// @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) | |
629 | 593 | /// @param mixed a_value Checked value |
630 | 594 | /// @result boolean - Returns true if the argument iterable |
631 | 595 | 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; | |
635 | 599 | } |
636 | 600 | |
637 | 601 | |
638 | 602 | |
639 | 603 | /// @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) | |
641 | 605 | /// @param mixed a_value Checked value |
642 | 606 | /// @result boolean - Returns true if the argument numbered |
643 | 607 | fcf.isNumbered = (a_value) => { |
644 | - if (typeof a_value === "string"){ | |
608 | + if (typeof a_value !== "object" || a_value === null) | |
645 | 609 | 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; | |
646 | 614 | } else { |
647 | - if (a_value === null) | |
615 | + for(let v of a_value) { | |
648 | 616 | return false; |
649 | - return typeof a_value[Symbol.iterator] === 'function' && typeof a_value.length === "number"; | |
617 | + } | |
618 | + return true; | |
650 | 619 | } |
651 | 620 | } |
652 | 621 |
@@ -654,165 +623,157 @@ | ||
654 | 623 | |
655 | 624 | /// @var integer fcf.UNDEFINED = 0 |
656 | 625 | /// @brief Nature type of variable. Undefined value |
657 | - fcf.UNDEFINED = 0; | |
626 | + Object.defineProperty(fcf, | |
627 | + "UNDEFINED", | |
628 | + { value: 0, writable: false }); | |
658 | 629 | |
659 | 630 | /// @var integer fcf.NULL = 1 |
660 | 631 | /// @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 }); | |
666 | 641 | |
667 | 642 | /// @var integer fcf.BOOLEAN = 3 |
668 | 643 | /// @brief Nature type of variable. Boolean value |
669 | - fcf.BOOLEAN = 3; | |
644 | + Object.defineProperty(fcf, | |
645 | + "BOOLEAN", | |
646 | + { value: 3, writable: false }); | |
670 | 647 | |
671 | 648 | /// @var integer fcf.NUMBER = 4 |
672 | 649 | /// @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 | |
680 | 673 | /// @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 | |
684 | 679 | /// @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 | |
688 | 685 | /// @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) | |
694 | 693 | /// @brief Checks if a value matches the nature type |
695 | 694 | /// @param mixed a_value - Checked value |
696 | 695 | /// @param string|integer|[string|integer] a_nature - The nature type or an array of nature types. |
697 | 696 | /// nature can take an integer value or a string value: |
698 | 697 | /// - fcf.UNDEFINED=0 | "undefined" - Undefined value |
699 | 698 | /// - fcf.NULL=1 | "null" - Null value |
700 | - /// - fcf.STRING=2 | "string" - String value | |
699 | + /// - fcf.NAN=2 | "nan" - NaN value | |
701 | 700 | /// - fcf.BOOLEAN=3 | "boolean" - Boolean value |
702 | 701 | /// - 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 | |
707 | 710 | /// @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; | |
816 | 777 | } |
817 | 778 | } |
818 | 779 | return false; |
@@ -820,54 +781,317 @@ | ||
820 | 781 | |
821 | 782 | |
822 | 783 | |
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; | |
830 | 811 | } |
831 | 812 | |
832 | 813 | |
833 | 814 | |
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) => { | |
851 | 821 | if (typeof a_object !== "object" || a_object === null) { |
852 | 822 | return false; |
853 | 823 | } |
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; | |
855 | 832 | } |
856 | 833 | |
857 | 834 | |
858 | 835 | |
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 | + | |
859 | 1081 | /// @fn number fcf.count(object a_object, function a_cb = undefined) |
860 | 1082 | /// @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 | |
862 | 1084 | /// @param function a_cb = undefined - If a functor is given, then the child |
863 | 1085 | /// element is taken into account if the function returns true |
864 | 1086 | /// - 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 | |
866 | 1088 | fcf.count = (a_object, a_cb) => { |
867 | 1089 | if (!a_cb) { |
868 | 1090 | if (typeof a_object == "object" && a_object !== null) { |
869 | 1091 | if (fcf.isNumbered(a_object)) { |
870 | 1092 | return a_object.length; |
1093 | + } else if (a_object instanceof Map || a_object instanceof Set) { | |
1094 | + return a_object.size; | |
871 | 1095 | } else { |
872 | 1096 | let count = 0; |
873 | 1097 | for(let k in a_object) |
@@ -892,219 +1116,23 @@ | ||
892 | 1116 | |
893 | 1117 | |
894 | 1118 | |
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) ) | |
1037 | 1120 | /// @brief Iterates over the elements of the argument a_obj |
1038 | 1121 | /// @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). | |
1042 | 1123 | /// @details Iteration is performed until the last element or until the a_cb |
1043 | 1124 | /// functor returns a result other than undefined, Promise->!undefined or fcf.Actions->!undefined |
1044 | 1125 | /// @param mixed a_obj - Iterable object |
1045 | 1126 | /// @param function a_cb(mixed a_key, mixed a_value) - Functor, can be asynchronous. |
1046 | 1127 | /// - Example: |
1047 | 1128 | /// 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. | |
1049 | 1130 | 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; | |
1052 | 1132 | 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")) { | |
1102 | 1134 | 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]); | |
1108 | 1136 | if (result instanceof Promise || result instanceof fcf.Actions) { |
1109 | 1137 | let index = i + 1; |
1110 | 1138 | let currentResult = result; |
@@ -1137,9 +1165,6 @@ | ||
1137 | 1165 | } |
1138 | 1166 | }) |
1139 | 1167 | .catch((e)=>{ |
1140 | - act.complete(e); | |
1141 | - }) | |
1142 | - .catch((e)=>{ | |
1143 | 1168 | act.error(e); |
1144 | 1169 | }); |
1145 | 1170 | break; |
@@ -1151,20 +1176,35 @@ | ||
1151 | 1176 | let asyncEnable = false; |
1152 | 1177 | let asyncKeys = []; |
1153 | 1178 | 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]]); | |
1160 | 1201 | } else { |
1161 | 1202 | 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 | + } | |
1168 | 1208 | } |
1169 | 1209 | } |
1170 | 1210 | } |
@@ -1178,7 +1218,7 @@ | ||
1178 | 1218 | function doAction(){ |
1179 | 1219 | actions.then(() => { |
1180 | 1220 | if (index < asyncKeys.length) |
1181 | - return a_cb(asyncKeys[index], a_obj[asyncKeys[index]]); | |
1221 | + return a_cb(asyncKeys[index][0], asyncKeys[index][1]); | |
1182 | 1222 | }) |
1183 | 1223 | .then((a_res) => { |
1184 | 1224 | ++index; |
@@ -1203,2018 +1243,206 @@ | ||
1203 | 1243 | .catch((e)=>{ |
1204 | 1244 | act.error(e); |
1205 | 1245 | }); |
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(); | |
1302 | 1250 | } |
1303 | 1251 | |
1304 | 1252 | |
1305 | 1253 | |
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 | + } | |
1505 | 1280 | } 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 | + }); | |
1508 | 1299 | } |
1509 | 1300 | } |
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); | |
1523 | 1309 | } |
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 | + }); | |
1696 | 1314 | } |
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 | + }); | |
1888 | 1329 | } |
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 | + }); | |
1899 | 1339 | } |
1900 | 1340 | } |
1901 | 1341 | } |
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]); | |
1907 | 1350 | } |
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 | - }) | |
1943 | 1351 | } 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 | + }); | |
1955 | 1355 | } |
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; | |
1979 | 1381 | 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(); | |
1989 | 1383 | } 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; | |
2015 | 1389 | } |
2016 | 1390 | } |
2017 | 1391 | |
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); | |
2233 | 1412 | } 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; | |
2308 | 1414 | } |
2309 | 1415 | } |
2310 | 1416 | |
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}); | |
2729 | 1440 | } else { |
2730 | - buff += c; | |
1441 | + result.push(typeof part == "object" ? part.part : part); | |
2731 | 1442 | } |
2732 | 1443 | } |
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 | + } | |
3218 | 1446 | // 0 - simple |
3219 | 1447 | // 1 - in [] || [[]] |
3220 | 1448 | // 2 - in [""] || [[""]] |
@@ -3452,29 +1680,111 @@ | ||
3452 | 1680 | break; |
3453 | 1681 | } |
3454 | 1682 | } |
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 | + } | |
3457 | 1690 | return result; |
3458 | 1691 | } |
3459 | 1692 | |
3460 | 1693 | |
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 | + /// } | |
3461 | 1775 | 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); | |
3470 | 1776 | let result = { |
3471 | 1777 | key: undefined, |
3472 | 1778 | object: undefined, |
3473 | 1779 | }; |
1780 | + if (typeof a_obj !== "object"){ | |
1781 | + return result; | |
1782 | + } | |
1783 | + let pathArr = fcf.parseObjectAddress(a_path, true); | |
3474 | 1784 | let cur = a_obj; |
3475 | 1785 | for(var i = 0; i < pathArr.length-1; ++i) { |
3476 | 1786 | let key = pathArr[i].part; |
3477 | - if (typeof cur[key] !== "object" || cur[key] === null) | |
1787 | + if (cur[key] === undefined || cur[key] === null) { | |
3478 | 1788 | if (a_createObj) { |
3479 | 1789 | if (pathArr[i].array){ |
3480 | 1790 | cur[key] = []; |
@@ -3484,6 +1794,7 @@ | ||
3484 | 1794 | } else { |
3485 | 1795 | return result; |
3486 | 1796 | } |
1797 | + } | |
3487 | 1798 | cur = cur[key]; |
3488 | 1799 | } |
3489 | 1800 | result.key = pathArr[pathArr.length-1].part; |
@@ -3492,829 +1803,2058 @@ | ||
3492 | 1803 | return result; |
3493 | 1804 | } |
3494 | 1805 | |
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; | |
3513 | 1832 | } |
3514 | 1833 | |
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; | |
3569 | 1962 | } 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; | |
3588 | 1964 | } |
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; | |
3611 | 1994 | } |
3612 | 1995 | |
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) => { | |
3614 | 2185 | 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; | |
3650 | 2205 | } |
3651 | 2206 | return result; |
3652 | 2207 | } |
3653 | 2208 | |
3654 | 2209 | |
3655 | 2210 | |
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; | |
3758 | 2578 | }) |
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; | |
3761 | 2582 | }) |
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 | + } | |
3785 | 3173 | } |
3786 | 3174 | } |
3787 | 3175 | |
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 | + }); | |
3847 | 3542 | } |
3848 | 3543 | } |
3849 | 3544 | |
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); | |
3870 | 3586 | } |
3871 | 3587 | |
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 | + } | |
3936 | 3681 | return result; |
3937 | 3682 | } |
3938 | 3683 | |
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); | |
4137 | 3694 | } 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(); | |
4139 | 3697 | } |
4140 | 3698 | } |
4141 | 3699 | |
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; | |
4158 | 3723 | } |
4159 | 3724 | |
4160 | 3725 | |
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) | |
4181 | 3772 | } |
4182 | 3773 | } |
4183 | 3774 | |
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 |