js-gei 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Evaluator/ExpresionLogicService.ts +669 -0
- package/Evaluator/Parser/AddLeadZerosParse.ts +70 -0
- package/Evaluator/Parser/AppearParse.ts +67 -0
- package/Evaluator/Parser/BaseParse.ts +93 -0
- package/Evaluator/Parser/CalcDateParse.ts +73 -0
- package/Evaluator/Parser/CheckParse.ts +99 -0
- package/Evaluator/Parser/CommaParser.ts +17 -0
- package/Evaluator/Parser/ConcatParse.ts +34 -0
- package/Evaluator/Parser/CountParse.ts +26 -0
- package/Evaluator/Parser/EmptyParse.ts +41 -0
- package/Evaluator/Parser/ExistParse.ts +69 -0
- package/Evaluator/Parser/IfParse.ts +388 -0
- package/Evaluator/Parser/IfStategment.ts +345 -0
- package/Evaluator/Parser/InDetailParse.ts +109 -0
- package/Evaluator/Parser/InListParse.ts +57 -0
- package/Evaluator/Parser/LiteralValueParse.ts +36 -0
- package/Evaluator/Parser/LogicalOperatorParse.ts +16 -0
- package/Evaluator/Parser/OnlyExpressionParse.ts +137 -0
- package/Evaluator/Parser/OperatorParse.ts +17 -0
- package/Evaluator/Parser/StartsParse.ts +36 -0
- package/Evaluator/Parser/SubstrParse.ts +110 -0
- package/Evaluator/Parser/SumParse.ts +15 -0
- package/Evaluator/Parser/ToNullParser.ts +0 -0
- package/Evaluator/Parser/TrimParse.ts +30 -0
- package/Evaluator/Parser/UpperLoverParse.ts +41 -0
- package/Evaluator/Parser/ValueFromObjParse.ts +37 -0
- package/Evaluator/Parser/ValueParse.ts +61 -0
- package/HelperSessionStorage.ts +13 -0
- package/Interfaces/SchemaStruct.ts +28 -0
- package/Library/jsonrpc-2.0.js +869 -0
- package/Structure/InvoiceObj.ts +691 -0
- package/Structure/RecordArray.ts +145 -0
- package/Structure/RecordSingle.ts +1076 -0
- package/Structure/TableLevel.ts +248 -0
- package/dist/Evaluator/ExpresionLogicService.js +505 -0
- package/dist/Evaluator/Parser/AddLeadZerosParse.js +55 -0
- package/dist/Evaluator/Parser/AppearParse.js +48 -0
- package/dist/Evaluator/Parser/BaseParse.js +89 -0
- package/dist/Evaluator/Parser/CalcDateParse.js +54 -0
- package/dist/Evaluator/Parser/CheckParse.js +80 -0
- package/dist/Evaluator/Parser/CommaParser.js +13 -0
- package/dist/Evaluator/Parser/ConcatParse.js +29 -0
- package/dist/Evaluator/Parser/CountParse.js +23 -0
- package/dist/Evaluator/Parser/EmptyParse.js +35 -0
- package/dist/Evaluator/Parser/ExistParse.js +57 -0
- package/dist/Evaluator/Parser/IfParse.js +296 -0
- package/dist/Evaluator/Parser/IfStategment.js +287 -0
- package/dist/Evaluator/Parser/InDetailParse.js +79 -0
- package/dist/Evaluator/Parser/InListParse.js +40 -0
- package/dist/Evaluator/Parser/LiteralValueParse.js +26 -0
- package/dist/Evaluator/Parser/LogicalOperatorParse.js +13 -0
- package/dist/Evaluator/Parser/OnlyExpressionParse.js +106 -0
- package/dist/Evaluator/Parser/OperatorParse.js +13 -0
- package/dist/Evaluator/Parser/StartsParse.js +30 -0
- package/dist/Evaluator/Parser/SubstrParse.js +83 -0
- package/dist/Evaluator/Parser/SumParse.js +13 -0
- package/dist/Evaluator/Parser/ToNullParser.js +1 -0
- package/dist/Evaluator/Parser/TrimParse.js +25 -0
- package/dist/Evaluator/Parser/UpperLoverParse.js +32 -0
- package/dist/Evaluator/Parser/ValueFromObjParse.js +29 -0
- package/dist/Evaluator/Parser/ValueParse.js +59 -0
- package/dist/HelperSessionStorage.js +12 -0
- package/dist/Interfaces/SchemaStruct.js +2 -0
- package/dist/Structure/InvoiceObj.js +610 -0
- package/dist/Structure/RecordArray.js +102 -0
- package/dist/Structure/RecordSingle.js +950 -0
- package/dist/Structure/TableLevel.js +162 -0
- package/dist/index.js +19 -0
- package/index.ts +20 -0
- package/package.json +20 -0
- package/tsconfig.json +103 -0
|
@@ -0,0 +1,869 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
var RpcClient = initRpcClient();
|
|
3
|
+
|
|
4
|
+
function initRpcClient() {
|
|
5
|
+
let dt = new Date();
|
|
6
|
+
let _id = dt.getMilliseconds() + (1000 * dt.getSeconds()) + (60000 * dt.getMinutes()) + (3600000 * dt.getHours());
|
|
7
|
+
let _ctxid = 0;
|
|
8
|
+
let _preempted = null;
|
|
9
|
+
let _pendingAjaxCount = 0;
|
|
10
|
+
|
|
11
|
+
let _currentPath = window.location.pathname.trim().replace(/(^[\/]*|[\/]*$)/g, "");
|
|
12
|
+
let _currentPathTopLevel = '(unavailable)';
|
|
13
|
+
try {
|
|
14
|
+
_currentPathTopLevel = window.top.location.pathname.trim().replace(/(^[\/]*|[\/]*$)/g, "");
|
|
15
|
+
} catch (e) {
|
|
16
|
+
// Ignore sandbox error.
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function schedulePreemptedQuery(id, ticket, processor, progressor, context, queue) {
|
|
20
|
+
let started = true;
|
|
21
|
+
if (!_preempted) {
|
|
22
|
+
_preempted = [];
|
|
23
|
+
started = false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
_preempted.push({ id: id, ticket: ticket, processor: processor, progressor: progressor, context: context, queue: queue });
|
|
27
|
+
|
|
28
|
+
let queries = 0;
|
|
29
|
+
|
|
30
|
+
function query() {
|
|
31
|
+
// Check if there is anything to query, first.
|
|
32
|
+
if (_preempted && _preempted.length === 0) {
|
|
33
|
+
_preempted = null;
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
queries++;
|
|
38
|
+
|
|
39
|
+
// Configure the RPC context to automatically reschedule the query afterwards.
|
|
40
|
+
let ctx = RpcClient.context().onFinally(function () {
|
|
41
|
+
if (queries <= 2) {
|
|
42
|
+
setTimeout(query, 2000);
|
|
43
|
+
} else if (queries <= 4) {
|
|
44
|
+
setTimeout(query, 3000);
|
|
45
|
+
} else {
|
|
46
|
+
setTimeout(query, 5000);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Gather the tickets for all known preempted requests and issue a combined query.
|
|
51
|
+
let tickets = [];
|
|
52
|
+
for (let i = 0; i < _preempted.length; i++) {
|
|
53
|
+
let ticket = _preempted[i].ticket;
|
|
54
|
+
tickets.push(ticket);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
RpcClient.call('$.QueryPreemptedResponses', { 'tickets': tickets }).done(function (e) {
|
|
58
|
+
if (e.error) {
|
|
59
|
+
// Something went wrong. Prevent future queries by removing current items
|
|
60
|
+
// and manually invoke the callbacks with appropriate error.
|
|
61
|
+
let items = _preempted.splice(0, tickets.length);
|
|
62
|
+
for (let i = 0; i < items.length; i++) {
|
|
63
|
+
let item = items[i];
|
|
64
|
+
item.processor.process(e);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Leave each item's context exactly once!
|
|
68
|
+
for (let i = 0; i < items.length; i++) {
|
|
69
|
+
let item = items[i];
|
|
70
|
+
if (item.context.preempted) {
|
|
71
|
+
item.context.preempted = null;
|
|
72
|
+
item.context.__leave();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return false; // Handled.
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Process each returned status report - there is one for each ticket that was queried.
|
|
80
|
+
for (let ticket in e.result) {
|
|
81
|
+
let report = e.result[ticket];
|
|
82
|
+
|
|
83
|
+
// Find the pending request that corresponds to current report.
|
|
84
|
+
let idx = -1;
|
|
85
|
+
for (let j = 0; j < _preempted.length; j++) {
|
|
86
|
+
let p = _preempted[j];
|
|
87
|
+
if (p.ticket === ticket) {
|
|
88
|
+
idx = j;
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (idx === -1) {
|
|
94
|
+
// Request not found. Shouldn't happen.
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function handleTicketStatusReport(report, ticket, idx) {
|
|
99
|
+
|
|
100
|
+
// IMPORTANT: Preserve the order of callback execution for each queue by gathering the
|
|
101
|
+
// ticket responses first and storing them temporarily inside the corresponding
|
|
102
|
+
// source queue. Execute them only after they've all been retrieved!
|
|
103
|
+
function gather(item, rpcresult) {
|
|
104
|
+
if (!item.queue.ready) {
|
|
105
|
+
item.queue.ready = [];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
item.queue.ready.push({
|
|
109
|
+
ticket: ticket, handler: function () {
|
|
110
|
+
try {
|
|
111
|
+
item.processor.process(rpcresult);
|
|
112
|
+
} finally {
|
|
113
|
+
// Clean up the original context.
|
|
114
|
+
let idxt = item.context.preempted.indexOf(ticket);
|
|
115
|
+
item.context.preempted.splice(idxt, 1);
|
|
116
|
+
|
|
117
|
+
if (item.context.preempted.length === 0) {
|
|
118
|
+
item.context.preempted = null;
|
|
119
|
+
item.context.__leave();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Invoke deferred callbacks for item's queue when it's ready.
|
|
126
|
+
// IMPORTANT: Do it in the same order as the tickets were originally queried, NOT the one in which
|
|
127
|
+
// their statuses were gathered!
|
|
128
|
+
if (item.queue.ready.length === item.queue.preempted) {
|
|
129
|
+
for (let i = 0; i < tickets.length; i++) {
|
|
130
|
+
let reqt = tickets[i];
|
|
131
|
+
for (let j = 0; j < item.queue.ready.length; j++) {
|
|
132
|
+
let rsp = item.queue.ready[j];
|
|
133
|
+
if (rsp.ticket !== reqt) {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
rsp.handler();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
item.queue.ready = [];
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (report.status === 'error') {
|
|
146
|
+
// The response could not be found.
|
|
147
|
+
let item = _preempted.splice(idx, 1)[0];
|
|
148
|
+
|
|
149
|
+
gather(item, { id: item.id, error: { code: -1, message: report.reason } });
|
|
150
|
+
}
|
|
151
|
+
else if (report.status === 'ok') {
|
|
152
|
+
// The response is ready to be retrieved.
|
|
153
|
+
let r = _preempted.splice(idx, 1)[0];
|
|
154
|
+
|
|
155
|
+
let retrieve = function (r) {
|
|
156
|
+
RpcClient.call('$.RetrievePreemptedResponse', { 'ticket': r.ticket })
|
|
157
|
+
.done(function(realResponse) {
|
|
158
|
+
let response = realResponse;
|
|
159
|
+
if (response && response.result && response.result.code && response.result.code >= -32700 && response.result.code <= -32000) {
|
|
160
|
+
// This is likely a preempted error, so report it as such.
|
|
161
|
+
response = { id: response.id, error: response.result };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
gather(r, response);
|
|
165
|
+
})
|
|
166
|
+
.post(ctx);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
retrieve(r);
|
|
170
|
+
} else {
|
|
171
|
+
// Trigger the progress callback WITHOUT removing the item.
|
|
172
|
+
// It will be queried again in the next round.
|
|
173
|
+
let item = _preempted[idx];
|
|
174
|
+
if (item.progressor) {
|
|
175
|
+
item.progressor.progress(report);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
handleTicketStatusReport(report, ticket, idx);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return false; // Handled.
|
|
184
|
+
}).post(ctx);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (!started) {
|
|
188
|
+
setTimeout(query, 2000);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
let _dispatcher = function () {
|
|
193
|
+
let _queue = [];
|
|
194
|
+
let _last = null;
|
|
195
|
+
let _delay = 700; // This is the window in ms, during which rapid requests are batched.
|
|
196
|
+
|
|
197
|
+
function postQueue(config) {
|
|
198
|
+
let queue = _queue;
|
|
199
|
+
_queue = [];
|
|
200
|
+
|
|
201
|
+
if (queue.length === 0) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
queue.preempted = 0;
|
|
206
|
+
|
|
207
|
+
// Combine all batches into one request.
|
|
208
|
+
let batch;
|
|
209
|
+
if (queue.length === 1) {
|
|
210
|
+
batch = queue[0].batch;
|
|
211
|
+
} else {
|
|
212
|
+
batch = [];
|
|
213
|
+
for (let i = 0; i < queue.length; i++) {
|
|
214
|
+
let qb = queue[i].batch;
|
|
215
|
+
for (let j = 0; j < qb.length; j++) {
|
|
216
|
+
batch.push(qb[j]);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
let request = [];
|
|
222
|
+
if (batch.length === 1) {
|
|
223
|
+
request = batch[0].request;
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
// Send multiple requests as sync batch so server can take advantage of caching.
|
|
227
|
+
request.push({ method: '$.BeginSynchronizedBatch', params: [] });
|
|
228
|
+
for (let i = 0; i < batch.length; i++) {
|
|
229
|
+
let r = batch[i].request;
|
|
230
|
+
if (i === 0 && r.method === request[0].method) {
|
|
231
|
+
// Don't duplicate the sync batch header - use the explicitly provided one.
|
|
232
|
+
request[0] = r;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
request.push(batch[i].request);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Prepare a joined fail wrapper for all queued batches.
|
|
241
|
+
let fail = function(rpc, error) {
|
|
242
|
+
for (let i = 0; i < queue.length; i++) {
|
|
243
|
+
queue[i].fail(rpc, error);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
let handler = function (data, xhr) {
|
|
248
|
+
try {
|
|
249
|
+
if (!data) {
|
|
250
|
+
fail(true, "Server returned an empty response. This is likely a server bug.");
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (data.ajaxCallFailed) {
|
|
255
|
+
if (data.error.status === 'timeout') {
|
|
256
|
+
fail(true, 'JSON-RPC call failed. Timeout.');
|
|
257
|
+
} else if (data.error.status === 'error' && !data.error.details) {
|
|
258
|
+
fail(true, 'JSON-RPC call failed. No connection to server.');
|
|
259
|
+
} else {
|
|
260
|
+
fail(true, "JSON-RPC call failed. Status: '" + data.error.status + "'" +
|
|
261
|
+
(data.error.details ? (", Details: " + JSON.stringify(data.error.details, null, 2)) : ''));
|
|
262
|
+
}
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
let responses = (typeof data === 'object' && data instanceof Array) ? data : [data];
|
|
267
|
+
if (responses.length === 0) {
|
|
268
|
+
fail(true, "Server returned an empty list of responses.");
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Look for known special "broadcast"-type responses that come in singles.
|
|
273
|
+
if (responses.length === 1) {
|
|
274
|
+
let response = responses[0];
|
|
275
|
+
if (typeof (response.id) !== 'undefined' && response.id.indexOf && response.id.indexOf('e2-status-') === 0) {
|
|
276
|
+
// Distribute this response to all queued requests' callbacks.
|
|
277
|
+
responses = [];
|
|
278
|
+
for (let b = 0; b < queue.length; b++) {
|
|
279
|
+
let qb = queue[b];
|
|
280
|
+
let batch = qb.batch;
|
|
281
|
+
for (let m = 0; m < batch.length; m++) {
|
|
282
|
+
let copy = JSON.parse(JSON.stringify(response));
|
|
283
|
+
copy.id = batch[m].request.id;
|
|
284
|
+
|
|
285
|
+
responses.push(copy);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
for (let l = 0; l < responses.length; l++) {
|
|
292
|
+
let response = responses[l];
|
|
293
|
+
|
|
294
|
+
if (typeof (response.result) === 'undefined' && !response.error) {
|
|
295
|
+
// TODO: This shouldn't go through fail - it should go through callbacks, otherwise they will hang!
|
|
296
|
+
fail(true, "Server returned invalid JSON-RPC response: " + JSON.stringify(response, null, 2));
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Find the request to which this response belongs.
|
|
301
|
+
let callbacks = null;
|
|
302
|
+
let progressCallbacks = null;
|
|
303
|
+
let context = null;
|
|
304
|
+
|
|
305
|
+
if (typeof (response.id) !== 'undefined') {
|
|
306
|
+
for (let b = 0; b < queue.length; b++) {
|
|
307
|
+
let qb = queue[b];
|
|
308
|
+
let batch = qb.batch;
|
|
309
|
+
for (let m = 0; m < batch.length; m++) {
|
|
310
|
+
let item = batch[m];
|
|
311
|
+
if (!item.request.id || (item.request.id !== response.id)) {
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
callbacks = item.callbacks;
|
|
316
|
+
progressCallbacks = item.progressCallbacks;
|
|
317
|
+
context = qb.context;
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (callbacks) {
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (!context) {
|
|
328
|
+
// The response is not mappable to any request. Provide a default.
|
|
329
|
+
context = RpcClient.context();
|
|
330
|
+
callbacks = [function (m) {
|
|
331
|
+
if (window.MessagesUI) {
|
|
332
|
+
window.MessagesUI.show({ message: m, json: true });
|
|
333
|
+
} else {
|
|
334
|
+
console.log(m);
|
|
335
|
+
}
|
|
336
|
+
return true;
|
|
337
|
+
}];
|
|
338
|
+
|
|
339
|
+
progressCallbacks = [];
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Prepare a processor instance.
|
|
343
|
+
let processor = {
|
|
344
|
+
context: context,
|
|
345
|
+
callbacks: callbacks,
|
|
346
|
+
process: function(response, xhr) {
|
|
347
|
+
// Invoke the context previewer.
|
|
348
|
+
let doDefaultProcessing = this.context.__preview(response);
|
|
349
|
+
if (!doDefaultProcessing) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Invoke the callbacks.
|
|
354
|
+
if (this.callbacks) {
|
|
355
|
+
for (let n = 0; n < this.callbacks.length; n++) {
|
|
356
|
+
let callback = this.callbacks[n];
|
|
357
|
+
if (!callback(response, xhr)) {
|
|
358
|
+
doDefaultProcessing = false;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// If none of the handlers actually handled the request, invoke our default processing.
|
|
364
|
+
if (doDefaultProcessing) {
|
|
365
|
+
if (response.error) {
|
|
366
|
+
if (!this.context.__defaultApiFail(response.error)) {
|
|
367
|
+
fail(false, response.error);
|
|
368
|
+
}
|
|
369
|
+
} else if (!this.context.__defaultApiSucceed(response.result)) {
|
|
370
|
+
config.log(response.result);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
// Response was preempted. Schedule processor for background waiting, unless explicitly forbidden by config.
|
|
377
|
+
if (response.error && response.error.code === -32099 && config.autoQueryPreempted) {
|
|
378
|
+
let ticket = response.error.data;
|
|
379
|
+
|
|
380
|
+
if (!context.preempted) {
|
|
381
|
+
context.preempted = [];
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
context.preempted.push(ticket);
|
|
385
|
+
queue.preempted++;
|
|
386
|
+
|
|
387
|
+
// Prepare a progress handler.
|
|
388
|
+
let progressor = {
|
|
389
|
+
callbacks: progressCallbacks,
|
|
390
|
+
progress: function(report) {
|
|
391
|
+
if (!this.callbacks) {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
for (let c = 0; c < this.callbacks.length; c++) {
|
|
396
|
+
let callback = this.callbacks[c];
|
|
397
|
+
if (callback) {
|
|
398
|
+
callback(report);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
schedulePreemptedQuery(response.id, ticket, processor, progressor, context, queue);
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Process the current response.
|
|
409
|
+
processor.process(response, xhr);
|
|
410
|
+
}
|
|
411
|
+
} finally {
|
|
412
|
+
for (let i = 0; i < queue.length; i++) {
|
|
413
|
+
// Only leave the context at this point if none of its requests were preempted!
|
|
414
|
+
let ctx = queue[i].context;
|
|
415
|
+
if (!ctx.preempted) {
|
|
416
|
+
ctx.__leave();
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
let requestConfig = {
|
|
423
|
+
timeout: 0, // config.timeout,
|
|
424
|
+
responseType: 'blob',
|
|
425
|
+
withCredentials: true
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Apply custom headers.
|
|
429
|
+
let headers = config.headers;
|
|
430
|
+
if (!headers) {
|
|
431
|
+
headers = {};
|
|
432
|
+
} else {
|
|
433
|
+
headers = JSON.parse(JSON.stringify(headers));
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Configure preemptive response support.
|
|
437
|
+
let preempt = headers["JsonRpc-Preempt-After"];
|
|
438
|
+
if (!preempt) {
|
|
439
|
+
headers["JsonRpc-Preempt-After"] = config.jsonRpcPreemptAfter;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
headers["JsonRpc-Path-TopLevel"] = _currentPathTopLevel;
|
|
443
|
+
headers["JsonRpc-Path"] = _currentPath;
|
|
444
|
+
|
|
445
|
+
requestConfig["headers"] = headers;
|
|
446
|
+
|
|
447
|
+
_pendingAjaxCount++;
|
|
448
|
+
|
|
449
|
+
axios.post(config.url, request, requestConfig)
|
|
450
|
+
.then(async function (response) {
|
|
451
|
+
_pendingAjaxCount--;
|
|
452
|
+
|
|
453
|
+
let length = response.headers["Content-Length"] ?? response.headers["content-length"] ?? response.headers["D3Pro-Uncompressed-Content-Length"] ?? response.headers["d3pro-uncompressed-content-length"] ?? 0;
|
|
454
|
+
const blob = new Blob([response.data], { type: 'application/json' });
|
|
455
|
+
|
|
456
|
+
//console.log(config);
|
|
457
|
+
let downloadToFile = length && config.largeResponseDownloadLimitMb && config.largeResponseDownloadLimitMb > 0 && length > config.largeResponseDownloadLimitMb * 1024 * 1024;
|
|
458
|
+
//console.log("Length: " + length);
|
|
459
|
+
//console.log("Download to file: " + (downloadToFile ? "YES" : "NO"));
|
|
460
|
+
|
|
461
|
+
if (downloadToFile) {
|
|
462
|
+
let fileName = `LargeJsonRpcResponse (${new Date().toISOString().replace('T', ' ').replaceAll(':', '-',)}).json`;
|
|
463
|
+
console.log("Response too large to load. It will be downloaded to file instead: " + fileName);
|
|
464
|
+
const a = document.createElement("a");
|
|
465
|
+
const url = URL.createObjectURL(blob);
|
|
466
|
+
a.href = url;
|
|
467
|
+
a.download = fileName;
|
|
468
|
+
a.click();
|
|
469
|
+
URL.revokeObjectURL(url);
|
|
470
|
+
|
|
471
|
+
handler({ error: { status: "TOO_LARGE_REDIRECTED_TO_FILE", details: "Response was too large to load. It was saved to a file instead: '" + fileName + "'." } }, response.request);
|
|
472
|
+
} else {
|
|
473
|
+
let json = JSON.parse(await blob.text());
|
|
474
|
+
|
|
475
|
+
handler(json, response.request);
|
|
476
|
+
}
|
|
477
|
+
})
|
|
478
|
+
.catch(function (error) {
|
|
479
|
+
console.log(error);
|
|
480
|
+
|
|
481
|
+
_pendingAjaxCount--;
|
|
482
|
+
handler({ error: { status: error.code, details: error.message }, ajaxCallFailed: true });
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
return {
|
|
487
|
+
dispatch: function(batch, context, fail, customConfig) {
|
|
488
|
+
let last = _last;
|
|
489
|
+
_last = new Date();
|
|
490
|
+
|
|
491
|
+
if (_queue.length === 0) {
|
|
492
|
+
_queue.created = _last;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
_queue.push({ batch, context, fail });
|
|
496
|
+
|
|
497
|
+
let config = customConfig ?? RpcClient.config;
|
|
498
|
+
|
|
499
|
+
// Make sure that preemption requests aren't batched. It would be a protocol violation.
|
|
500
|
+
if (batch.length === 1) {
|
|
501
|
+
let method = batch[0].request.method;
|
|
502
|
+
if (method[0] === '$' && method.indexOf('Preempted') > -1) {
|
|
503
|
+
// If a preemption request comes in a batch, we pop it from the queue and send it separately after the batch.
|
|
504
|
+
if (_queue.length > 1) {
|
|
505
|
+
let temp = _queue.pop();
|
|
506
|
+
postQueue(config);
|
|
507
|
+
|
|
508
|
+
_queue = [];
|
|
509
|
+
_queue.push(temp);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// Solo preemption requests are sent straight away.
|
|
513
|
+
postQueue(config);
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (!config.batch) {
|
|
519
|
+
postQueue(config);
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (last != null) {
|
|
524
|
+
let diff = _last - last;
|
|
525
|
+
let span = _last - _queue.created;
|
|
526
|
+
|
|
527
|
+
let delayExistingBatch = span < 3000 && (_pendingAjaxCount > 3 || diff < _delay);
|
|
528
|
+
if (delayExistingBatch) {
|
|
529
|
+
// Reschedule pending post.
|
|
530
|
+
if (_queue.timeoutHandle) {
|
|
531
|
+
clearTimeout(_queue.timeoutHandle);
|
|
532
|
+
_queue.timeoutHandle = null;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
if (_pendingAjaxCount === 0) {
|
|
538
|
+
// There are no pending AJAX posts, so post immediately.
|
|
539
|
+
_queue.timeoutHandle = setTimeout(function () {
|
|
540
|
+
postQueue(config);
|
|
541
|
+
}, 1);
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// There are existing pending posts, so schedule a delayed one.
|
|
546
|
+
_queue.timeoutHandle = setTimeout(function () {
|
|
547
|
+
postQueue(config);
|
|
548
|
+
}, _delay + 50);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}();
|
|
552
|
+
|
|
553
|
+
function preparePacket() {
|
|
554
|
+
let method = arguments[0];
|
|
555
|
+
let args = [];
|
|
556
|
+
|
|
557
|
+
const isPlainObject = value => value && [undefined, Object].includes(value.constructor);
|
|
558
|
+
|
|
559
|
+
if (arguments.length === 2 && (isPlainObject(arguments[1]) || Array.isArray(arguments[1]))) {
|
|
560
|
+
args = arguments[1];
|
|
561
|
+
} else {
|
|
562
|
+
for (let i = 1; i < arguments.length; i++) {
|
|
563
|
+
args.push(arguments[i]);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
return { method: method, params: args };
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
function main(packet, config) {
|
|
571
|
+
let batch = [];
|
|
572
|
+
batch.push({ request: packet, callbacks: [], progressCallbacks: [] });
|
|
573
|
+
|
|
574
|
+
let context = null;
|
|
575
|
+
|
|
576
|
+
let fail = function(rpc, e) {
|
|
577
|
+
// NOTE: Context.__rpcFail should only be called for ajax failures.
|
|
578
|
+
if (context && rpc === true && context.__rpcFail(e)) {
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
if (config && config.unhandledError) {
|
|
583
|
+
config.unhandledError(e);
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
console.log(e);
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
return {
|
|
591
|
+
call: function () {
|
|
592
|
+
let p = preparePacket.apply(null, arguments);
|
|
593
|
+
_id++;
|
|
594
|
+
p.id = _id;
|
|
595
|
+
batch.push({ request: p, callbacks: [], progressCallbacks: [] });
|
|
596
|
+
|
|
597
|
+
return this;
|
|
598
|
+
},
|
|
599
|
+
notify: function () {
|
|
600
|
+
let p = preparePacket.apply(null, arguments);
|
|
601
|
+
batch.push({ request: p, callbacks: [], progressCallbacks: [] });
|
|
602
|
+
|
|
603
|
+
return this;
|
|
604
|
+
},
|
|
605
|
+
using: function (id) {
|
|
606
|
+
let p = batch[batch.length - 1].request;
|
|
607
|
+
p.id = id;
|
|
608
|
+
|
|
609
|
+
return this;
|
|
610
|
+
},
|
|
611
|
+
done: function (c) {
|
|
612
|
+
let item = batch.length > 0 ? batch[batch.length - 1] : null;
|
|
613
|
+
if (typeof (c) == 'function') {
|
|
614
|
+
item.callbacks.push(c);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
return this;
|
|
618
|
+
},
|
|
619
|
+
progress: function (c) {
|
|
620
|
+
let item = batch.length > 0 ? batch[batch.length - 1] : null;
|
|
621
|
+
if (typeof (c) == 'function') {
|
|
622
|
+
item.progressCallbacks.push(c);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
return this;
|
|
626
|
+
},
|
|
627
|
+
post: function (ctx, customConfig) {
|
|
628
|
+
context = ctx;
|
|
629
|
+
|
|
630
|
+
if (!context) {
|
|
631
|
+
context = RpcClient.context();
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
context.__enter();
|
|
635
|
+
|
|
636
|
+
let config = customConfig ?? RpcClient.config;
|
|
637
|
+
if (!config) {
|
|
638
|
+
try {
|
|
639
|
+
fail(true, "RpcClient was invoked without a valid config object!");
|
|
640
|
+
} finally {
|
|
641
|
+
context.__leave();
|
|
642
|
+
}
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// Is this request supposed to go as JSONP?
|
|
647
|
+
if (config.jsonp) {
|
|
648
|
+
let request = [];
|
|
649
|
+
if (batch.length === 1) {
|
|
650
|
+
request = batch[0].request;
|
|
651
|
+
}
|
|
652
|
+
else {
|
|
653
|
+
for (let i = 0; i < batch.length; i++) {
|
|
654
|
+
request.push(batch[i].request);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
let headers = config.headers;
|
|
659
|
+
if (!headers) {
|
|
660
|
+
headers = {};
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
let jsonp = "__jsonpCallback_" + _id;
|
|
664
|
+
let url = config.url + "?callback=" + jsonp + "&jsonrpc=" + JSON.stringify(request);
|
|
665
|
+
|
|
666
|
+
for (let header in headers) {
|
|
667
|
+
if (headers.hasOwnProperty(header)) {
|
|
668
|
+
url = url + "&" + header + "=" + config.headers[header];
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
let script = document.createElement("script");
|
|
673
|
+
script.setAttribute("src", url);
|
|
674
|
+
|
|
675
|
+
window[jsonp] = function (data) {
|
|
676
|
+
window[jsonp] = null;
|
|
677
|
+
|
|
678
|
+
document.getElementsByTagName("body")[0].removeChild(script);
|
|
679
|
+
script = null;
|
|
680
|
+
|
|
681
|
+
handler(data);
|
|
682
|
+
};
|
|
683
|
+
|
|
684
|
+
// This will trigger the call to JSONP server.
|
|
685
|
+
document.getElementsByTagName("body")[0].appendChild(script);
|
|
686
|
+
|
|
687
|
+
context.__leave();
|
|
688
|
+
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// No, dispatch it through AJAX.
|
|
693
|
+
_dispatcher.dispatch(batch, context, fail, config);
|
|
694
|
+
}
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
let configTemplate = {
|
|
699
|
+
url: "json.rpc",
|
|
700
|
+
//timeout: 20000,
|
|
701
|
+
batch: true,
|
|
702
|
+
jsonRpcPreemptAfter: 7,
|
|
703
|
+
autoQueryPreempted: true,
|
|
704
|
+
log: function (obj) { console.log(obj); },
|
|
705
|
+
unhandledError: function (e) { console.log(e); }
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
let baseUrl = null;
|
|
709
|
+
let headElements = document.getElementsByTagName("head");
|
|
710
|
+
if (headElements && headElements.length) {
|
|
711
|
+
let baseElements = headElements[0].getElementsByTagName("base");
|
|
712
|
+
if (baseElements && baseElements.length) {
|
|
713
|
+
baseUrl = baseElements[0].getAttribute("href");
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
function urlCombine(parts) {
|
|
718
|
+
return parts.map(function (part) { return part.trim().replace(/(^[\/]*|[\/]*$)/g, ""); }).join("/");
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
let path = window.top.location.pathname.trim().replace(/(^[\/]*|[\/]*$)/g, "");
|
|
722
|
+
if (!path || path === '') {
|
|
723
|
+
if (baseUrl) {
|
|
724
|
+
configTemplate.url = urlCombine([baseUrl, configTemplate.url]);
|
|
725
|
+
}
|
|
726
|
+
} else {
|
|
727
|
+
if (baseUrl) {
|
|
728
|
+
configTemplate.url = urlCombine([baseUrl, path, configTemplate.url]);
|
|
729
|
+
} else {
|
|
730
|
+
configTemplate.url = urlCombine([path, configTemplate.url]);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
return {
|
|
735
|
+
config: configTemplate,
|
|
736
|
+
call: function() {
|
|
737
|
+
let packet = preparePacket.apply(null, arguments);
|
|
738
|
+
_id++;
|
|
739
|
+
packet.id = _id;
|
|
740
|
+
return main(packet, this.config);
|
|
741
|
+
},
|
|
742
|
+
notify: function () {
|
|
743
|
+
let packet = preparePacket.apply(null, arguments);
|
|
744
|
+
return main(packet, this.config);
|
|
745
|
+
},
|
|
746
|
+
context: function () {
|
|
747
|
+
_ctxid++;
|
|
748
|
+
|
|
749
|
+
let ctx = {
|
|
750
|
+
id: _ctxid,
|
|
751
|
+
count: 0,
|
|
752
|
+
rpcErrorHandlers: [],
|
|
753
|
+
apiErrorHandlers: [],
|
|
754
|
+
apiSuccessHandlers: [],
|
|
755
|
+
responsePreviewers: [],
|
|
756
|
+
finallyHandlers: []
|
|
757
|
+
};
|
|
758
|
+
|
|
759
|
+
return {
|
|
760
|
+
// These are meant for user code.
|
|
761
|
+
onRpcError: function (c) {
|
|
762
|
+
if (typeof (c) == 'function') {
|
|
763
|
+
ctx.rpcErrorHandlers.push(c);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
return this;
|
|
767
|
+
},
|
|
768
|
+
onDefaultApiError: function (c) {
|
|
769
|
+
if (typeof (c) == 'function') {
|
|
770
|
+
ctx.apiErrorHandlers.push(c);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
return this;
|
|
774
|
+
},
|
|
775
|
+
onDefaultApiSuccess: function (c) {
|
|
776
|
+
if (typeof (c) == 'function') {
|
|
777
|
+
ctx.apiSuccessHandlers.push(c);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
return this;
|
|
781
|
+
},
|
|
782
|
+
onFinally: function (c) {
|
|
783
|
+
if (typeof (c) == 'function') {
|
|
784
|
+
ctx.finallyHandlers.push(c);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
return this;
|
|
788
|
+
},
|
|
789
|
+
onPreviewResponse: function (c) {
|
|
790
|
+
if (typeof (c) == 'function') {
|
|
791
|
+
ctx.responsePreviewers.push(c);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
return this;
|
|
795
|
+
},
|
|
796
|
+
|
|
797
|
+
// These are called internally.
|
|
798
|
+
__enter: function () {
|
|
799
|
+
ctx.count++;
|
|
800
|
+
},
|
|
801
|
+
__preview: function () {
|
|
802
|
+
// IMPORTANT: If this function returns true, it means it DIDN'T handle the request and other handlers should proceed.
|
|
803
|
+
// This is the reverse of the other function below, which return true when they DO handle it!
|
|
804
|
+
if (ctx.responsePreviewers.length === 0) {
|
|
805
|
+
return true; // Not handled.
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
let handled = false;
|
|
809
|
+
|
|
810
|
+
for (let i = 0; i < ctx.responsePreviewers.length; i++) {
|
|
811
|
+
let callback = ctx.responsePreviewers[i];
|
|
812
|
+
if (!callback.apply(null, arguments)) {
|
|
813
|
+
handled = true;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
return !handled;
|
|
818
|
+
},
|
|
819
|
+
__rpcFail: function () {
|
|
820
|
+
if (ctx.rpcErrorHandlers.length === 0) {
|
|
821
|
+
return false;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
for (let i = 0; i < ctx.rpcErrorHandlers.length; i++) {
|
|
825
|
+
let callback = ctx.rpcErrorHandlers[i];
|
|
826
|
+
callback.apply(null, arguments);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
return true;
|
|
830
|
+
},
|
|
831
|
+
__defaultApiFail: function () {
|
|
832
|
+
if (ctx.apiErrorHandlers.length === 0) {
|
|
833
|
+
return false;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
for (let i = 0; i < ctx.apiErrorHandlers.length; i++) {
|
|
837
|
+
let callback = ctx.apiErrorHandlers[i];
|
|
838
|
+
callback.apply(null, arguments);
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
return true;
|
|
842
|
+
},
|
|
843
|
+
__defaultApiSucceed: function () {
|
|
844
|
+
if (ctx.apiSuccessHandlers.length === 0) {
|
|
845
|
+
return false;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
for (let i = 0; i < ctx.apiSuccessHandlers.length; i++) {
|
|
849
|
+
let callback = ctx.apiSuccessHandlers[i];
|
|
850
|
+
callback.apply(null, arguments);
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
return true;
|
|
854
|
+
},
|
|
855
|
+
__leave: function () {
|
|
856
|
+
ctx.count--;
|
|
857
|
+
if (ctx.count > 0) {
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
for (let i = 0; i < ctx.finallyHandlers.length; i++) {
|
|
862
|
+
let callback = ctx.finallyHandlers[i];
|
|
863
|
+
callback.apply(null, arguments);
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
};
|
|
869
|
+
}
|