@yuneta/gobj-ui 7.8.1 → 7.9.1
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/dist/gobj-ui.cjs.js +203 -5
- package/dist/gobj-ui.es.js +203 -5
- package/package.json +1 -1
- package/src/c_yui_treedb_topic_with_form.js +163 -0
- package/src/c_yui_treedb_topics.js +102 -12
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -14497,6 +14497,7 @@ var attrs_table$6 = [
|
|
|
14497
14497
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "gobj_remote_yuno", 0, null, "Remote yuno for data fetching"),
|
|
14498
14498
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "treedb_name", 0, null, "Remote TreeDB service name"),
|
|
14499
14499
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "descs", 0, null, "Description of topics"),
|
|
14500
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_remote_paging", 0, false, "Each topic table pulls its rows a PAGE at a time (`nodes` from/limit) instead of loading the topic whole. Safe against a backend that cannot page: it answers the whole list, which the table reads as one page"),
|
|
14500
14501
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "system", 0, false, "Manage system topics (true) or user topics (false)"),
|
|
14501
14502
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "tabs_style", 0, "is-toggle is-fullwidth", "Bulma tab styling"),
|
|
14502
14503
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_cards_landing", 0, false, "Land on a grid of topic cards (list->detail): a card opens its table, with the tabs bar + a back-to-grid button. Off = tabs only (legacy)."),
|
|
@@ -15310,7 +15311,8 @@ function process_treedb_descs$1(gobj) {
|
|
|
15310
15311
|
with_delete_button: !readonly,
|
|
15311
15312
|
treedb_name,
|
|
15312
15313
|
topic_name: key,
|
|
15313
|
-
desc
|
|
15314
|
+
desc,
|
|
15315
|
+
with_remote_paging: (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_remote_paging")
|
|
15314
15316
|
};
|
|
15315
15317
|
let id = `${(0, _yuneta_gobj_js.gobj_name)(gobj)}?${desc.topic_name}`;
|
|
15316
15318
|
let gobj_topic_form = (0, _yuneta_gobj_js.gobj_create_service)(id, "C_YUI_TREEDB_TOPIC_WITH_FORM", kw_topic_form, gobj);
|
|
@@ -15381,6 +15383,8 @@ function register_sub_routes$1(gobj) {
|
|
|
15381
15383
|
function get_nodes$1(gobj, topic_name) {
|
|
15382
15384
|
const treedb_name = gobj.priv.treedb_name;
|
|
15383
15385
|
subscribe_treedb$1(gobj, topic_name);
|
|
15386
|
+
let gobj_topic_form = get_gobj_formtable(gobj, topic_name);
|
|
15387
|
+
if (gobj_topic_form && (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj_topic_form, "with_remote_paging")) return;
|
|
15384
15388
|
treedb_nodes$1(gobj, treedb_name, topic_name, { list_dict: true });
|
|
15385
15389
|
}
|
|
15386
15390
|
/************************************************************
|
|
@@ -15428,7 +15432,7 @@ function get_gobj_formtable(gobj, topic_name) {
|
|
|
15428
15432
|
/************************************************************
|
|
15429
15433
|
* Command to remote service
|
|
15430
15434
|
************************************************************/
|
|
15431
|
-
function treedb_nodes$1(gobj, treedb_name, topic_name, options) {
|
|
15435
|
+
function treedb_nodes$1(gobj, treedb_name, topic_name, options, page) {
|
|
15432
15436
|
let command = "nodes";
|
|
15433
15437
|
let gobj_remote_yuno = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "gobj_remote_yuno");
|
|
15434
15438
|
let kw = {
|
|
@@ -15438,8 +15442,17 @@ function treedb_nodes$1(gobj, treedb_name, topic_name, options) {
|
|
|
15438
15442
|
options: options || {}
|
|
15439
15443
|
};
|
|
15440
15444
|
kw.__md_command__ = { topic_name };
|
|
15445
|
+
if (page) {
|
|
15446
|
+
kw.from = page.from;
|
|
15447
|
+
kw.limit = page.limit;
|
|
15448
|
+
kw.__md_command__.req_id = page.req_id;
|
|
15449
|
+
}
|
|
15441
15450
|
let ret = (0, _yuneta_gobj_js.gobj_command)(gobj_remote_yuno, command, kw, gobj);
|
|
15442
|
-
if (ret)
|
|
15451
|
+
if (ret) {
|
|
15452
|
+
(0, _yuneta_gobj_js.log_error)(ret);
|
|
15453
|
+
return -1;
|
|
15454
|
+
}
|
|
15455
|
+
return 0;
|
|
15443
15456
|
}
|
|
15444
15457
|
/************************************************************
|
|
15445
15458
|
* Command to remote service
|
|
@@ -15673,7 +15686,7 @@ function ac_mt_command_answer$2(gobj, event, kw, src) {
|
|
|
15673
15686
|
yui_shell_show_error(yui_shell_of(gobj), comment, { t: i18next.t });
|
|
15674
15687
|
if (command === "update-node" || command === "create-node") {
|
|
15675
15688
|
let failed_topic = (0, _yuneta_gobj_js.kw_get_str)(gobj, kw_command, "topic_name", "", 0);
|
|
15676
|
-
if (failed_topic)
|
|
15689
|
+
if (failed_topic) (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_REFRESH_TOPIC", { topic_name: failed_topic }, gobj);
|
|
15677
15690
|
}
|
|
15678
15691
|
}
|
|
15679
15692
|
return 0;
|
|
@@ -15689,7 +15702,15 @@ function ac_mt_command_answer$2(gobj, event, kw, src) {
|
|
|
15689
15702
|
let topic_name = (0, _yuneta_gobj_js.kw_get_str)(gobj, kw_command, "topic_name", "", _yuneta_gobj_js.kw_flag_t.KW_REQUIRED);
|
|
15690
15703
|
if (result >= 0) {
|
|
15691
15704
|
let gobj_topic_form = (0, _yuneta_gobj_js.gobj_find_child)(gobj, { __gobj_name__: `${(0, _yuneta_gobj_js.gobj_name)(gobj)}?${topic_name}` });
|
|
15692
|
-
|
|
15705
|
+
let answer = nodes_answer(data);
|
|
15706
|
+
let req_id = (0, _yuneta_gobj_js.kw_get_str)(gobj, kw_command, "req_id", "", 0);
|
|
15707
|
+
if (req_id) (0, _yuneta_gobj_js.gobj_send_event)(gobj_topic_form, "EV_PAGE_LOADED", {
|
|
15708
|
+
req_id,
|
|
15709
|
+
rows: answer.rows,
|
|
15710
|
+
pages: answer.pages,
|
|
15711
|
+
total: answer.total
|
|
15712
|
+
}, gobj);
|
|
15713
|
+
else (0, _yuneta_gobj_js.gobj_send_event)(gobj_topic_form, "EV_LOAD_NODES", answer.rows, gobj);
|
|
15693
15714
|
}
|
|
15694
15715
|
break;
|
|
15695
15716
|
case "create-node":
|
|
@@ -15915,6 +15936,31 @@ function ac_create_record(gobj, event, kw, src) {
|
|
|
15915
15936
|
});
|
|
15916
15937
|
}
|
|
15917
15938
|
/********************************************
|
|
15939
|
+
* A topic table wants a page.
|
|
15940
|
+
*
|
|
15941
|
+
* The transport is ours, so the table asks and we fetch; the answer
|
|
15942
|
+
* finds its way back by the `req_id` we echo. A backend that cannot
|
|
15943
|
+
* page answers the whole list, which the table takes as one page —
|
|
15944
|
+
* which is the truth, and is why the table can ask without knowing
|
|
15945
|
+
* what it is talking to.
|
|
15946
|
+
********************************************/
|
|
15947
|
+
function ac_request_page(gobj, event, kw, src) {
|
|
15948
|
+
let topic_name = kw.topic_name || (0, _yuneta_gobj_js.gobj_read_attr)(src, "topic_name");
|
|
15949
|
+
if (!topic_name || !kw.req_id) {
|
|
15950
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: a page request with no topic or no id`);
|
|
15951
|
+
return -1;
|
|
15952
|
+
}
|
|
15953
|
+
if (treedb_nodes$1(gobj, (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "treedb_name"), topic_name, { list_dict: true }, {
|
|
15954
|
+
from: kw.from,
|
|
15955
|
+
limit: kw.limit,
|
|
15956
|
+
req_id: kw.req_id
|
|
15957
|
+
}) < 0) (0, _yuneta_gobj_js.gobj_send_event)(src, "EV_PAGE_FAILED", {
|
|
15958
|
+
req_id: kw.req_id,
|
|
15959
|
+
error: "cannot reach the backend"
|
|
15960
|
+
}, gobj);
|
|
15961
|
+
return 0;
|
|
15962
|
+
}
|
|
15963
|
+
/********************************************
|
|
15918
15964
|
* Event from formtable: ONE field of one record, edited in place.
|
|
15919
15965
|
*
|
|
15920
15966
|
* Written as a PARTIAL update and, deliberately, with NO `autolink`.
|
|
@@ -15981,6 +16027,11 @@ function ac_delete_record(gobj, event, kw, src) {
|
|
|
15981
16027
|
* }
|
|
15982
16028
|
********************************************/
|
|
15983
16029
|
function ac_refresh_topic(gobj, event, kw, src) {
|
|
16030
|
+
let gobj_topic_form = get_gobj_formtable(gobj, kw.topic_name);
|
|
16031
|
+
if (gobj_topic_form && (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj_topic_form, "with_remote_paging")) {
|
|
16032
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj_topic_form, "EV_REPULL_PAGE", {}, gobj);
|
|
16033
|
+
return 0;
|
|
16034
|
+
}
|
|
15984
16035
|
treedb_nodes$1(gobj, (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "treedb_name"), kw.topic_name, { list_dict: true });
|
|
15985
16036
|
return 0;
|
|
15986
16037
|
}
|
|
@@ -16070,6 +16121,11 @@ function create_gclass$6(gclass_name) {
|
|
|
16070
16121
|
ac_update_field,
|
|
16071
16122
|
null
|
|
16072
16123
|
],
|
|
16124
|
+
[
|
|
16125
|
+
"EV_REQUEST_PAGE",
|
|
16126
|
+
ac_request_page,
|
|
16127
|
+
null
|
|
16128
|
+
],
|
|
16073
16129
|
[
|
|
16074
16130
|
"EV_DELETE_RECORD",
|
|
16075
16131
|
ac_delete_record,
|
|
@@ -16144,6 +16200,7 @@ function create_gclass$6(gclass_name) {
|
|
|
16144
16200
|
["EV_CREATE_RECORD", 0],
|
|
16145
16201
|
["EV_UPDATE_RECORD", 0],
|
|
16146
16202
|
["EV_UPDATE_FIELD", 0],
|
|
16203
|
+
["EV_REQUEST_PAGE", 0],
|
|
16147
16204
|
["EV_DELETE_RECORD", 0],
|
|
16148
16205
|
["EV_RECORD_WRITTEN", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_NO_WARN_SUBS],
|
|
16149
16206
|
["EV_REFRESH_TOPIC", 0],
|
|
@@ -16358,6 +16415,8 @@ var attrs_table$5 = [
|
|
|
16358
16415
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_export_button", 0, true, "Button toolbar EXPORT (download as CSV what the table holds)"),
|
|
16359
16416
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_header_filters", 0, true, "Per-column filter box in the table header, on the columns a text/number match means something (not hooks, fkeys or json)"),
|
|
16360
16417
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_inline_edit", 0, true, "Edit a writable scalar cell in place while in edition mode (the record form keeps the rest)"),
|
|
16418
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_remote_paging", 0, false, "Pull the topic a PAGE at a time from the backend (`nodes` from/limit) instead of loading it whole. Needs a backend that pages"),
|
|
16419
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_INTEGER, "page_size", 0, 200, "Rows per page when with_remote_paging. Generous on purpose: a treedb that fits in one page behaves exactly as it did, paginator hidden and every filter seeing every row"),
|
|
16361
16420
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_in_row_edit_icons", 0, true, "Add a last column with internal EDIT/DELETE icon"),
|
|
16362
16421
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "editable", 0, false, "Edit state"),
|
|
16363
16422
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_checkbox", 0, true, "Auxiliary first column to select rows"),
|
|
@@ -16386,6 +16445,8 @@ var attrs_table$5 = [
|
|
|
16386
16445
|
(0, _yuneta_gobj_js.SDATA_END)()
|
|
16387
16446
|
];
|
|
16388
16447
|
var PRIVATE_DATA$5 = {
|
|
16448
|
+
_pending_pages: null,
|
|
16449
|
+
_page_seq: 0,
|
|
16389
16450
|
$container: null,
|
|
16390
16451
|
treedb_name: "",
|
|
16391
16452
|
topic_name: "",
|
|
@@ -17111,6 +17172,25 @@ function create_tabulator(gobj) {
|
|
|
17111
17172
|
let pkey = desc.pkey || "id";
|
|
17112
17173
|
let selectable = with_checkbox ? "highlight" : with_radio ? 1 : false;
|
|
17113
17174
|
let tabulator_settings = (0, _yuneta_gobj_js.json_deep_copy)((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "tabulator_settings"));
|
|
17175
|
+
if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_remote_paging")) {
|
|
17176
|
+
let page_size = (0, _yuneta_gobj_js.gobj_read_integer_attr)(gobj, "page_size") || 200;
|
|
17177
|
+
Object.assign(tabulator_settings, {
|
|
17178
|
+
pagination: true,
|
|
17179
|
+
paginationMode: "remote",
|
|
17180
|
+
filterMode: "local",
|
|
17181
|
+
paginationSize: page_size,
|
|
17182
|
+
paginationSizeSelector: [
|
|
17183
|
+
50,
|
|
17184
|
+
100,
|
|
17185
|
+
200,
|
|
17186
|
+
500
|
|
17187
|
+
],
|
|
17188
|
+
ajaxURL: "nodes",
|
|
17189
|
+
ajaxRequestFunc: function(url, config, params) {
|
|
17190
|
+
return request_page(gobj, params.page || 1, params.size || page_size);
|
|
17191
|
+
}
|
|
17192
|
+
});
|
|
17193
|
+
}
|
|
17114
17194
|
Object.assign(tabulator_settings, {
|
|
17115
17195
|
index: pkey,
|
|
17116
17196
|
columns,
|
|
@@ -18312,6 +18392,99 @@ function ac_show_schema(gobj, event, kw, src) {
|
|
|
18312
18392
|
return 0;
|
|
18313
18393
|
}
|
|
18314
18394
|
/************************************************************
|
|
18395
|
+
* Ask the host for one page and park the promise Tabulator wants.
|
|
18396
|
+
*
|
|
18397
|
+
* The transport belongs to the HOST, so the request goes up as an event
|
|
18398
|
+
* and the answer comes back down as one; what lives here is only the
|
|
18399
|
+
* promise, keyed by a correlation id the host echoes.
|
|
18400
|
+
*
|
|
18401
|
+
* Rejected right away when there is nobody to ask: a request that can
|
|
18402
|
+
* never be answered would leave the table spinning for ever and leak
|
|
18403
|
+
* one entry per attempt. Same for the watchdog — the link can stay up
|
|
18404
|
+
* and the answer still never land.
|
|
18405
|
+
************************************************************/
|
|
18406
|
+
var PAGE_TIMEOUT_MS = 2e4;
|
|
18407
|
+
function request_page(gobj, page, size) {
|
|
18408
|
+
let priv = gobj.priv;
|
|
18409
|
+
return new Promise(function(resolve, reject) {
|
|
18410
|
+
if (!priv._pending_pages) priv._pending_pages = {};
|
|
18411
|
+
let req_id = `p${++priv._page_seq}`;
|
|
18412
|
+
let timer = window.setTimeout(function() {
|
|
18413
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_PAGE_TIMEOUT", { req_id }, gobj);
|
|
18414
|
+
}, PAGE_TIMEOUT_MS);
|
|
18415
|
+
priv._pending_pages[req_id] = {
|
|
18416
|
+
resolve,
|
|
18417
|
+
reject,
|
|
18418
|
+
timer
|
|
18419
|
+
};
|
|
18420
|
+
(0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_REQUEST_PAGE", {
|
|
18421
|
+
topic_name: (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "topic_name"),
|
|
18422
|
+
req_id,
|
|
18423
|
+
from: (page - 1) * size + 1,
|
|
18424
|
+
limit: size
|
|
18425
|
+
});
|
|
18426
|
+
});
|
|
18427
|
+
}
|
|
18428
|
+
/************************************************************
|
|
18429
|
+
* Settle a parked page request, however it ended.
|
|
18430
|
+
************************************************************/
|
|
18431
|
+
function settle_page(gobj, req_id, value, error) {
|
|
18432
|
+
let priv = gobj.priv;
|
|
18433
|
+
let pend = priv._pending_pages ? priv._pending_pages[req_id] : null;
|
|
18434
|
+
if (!pend) return;
|
|
18435
|
+
delete priv._pending_pages[req_id];
|
|
18436
|
+
if (pend.timer) window.clearTimeout(pend.timer);
|
|
18437
|
+
if (error) {
|
|
18438
|
+
pend.reject(new Error(error));
|
|
18439
|
+
return;
|
|
18440
|
+
}
|
|
18441
|
+
pend.resolve(value);
|
|
18442
|
+
}
|
|
18443
|
+
/************************************************************
|
|
18444
|
+
* Pull the current page again. `replaceData()` re-runs
|
|
18445
|
+
* ajaxRequestFunc for the page the reader is on, so a refresh keeps
|
|
18446
|
+
* their position instead of throwing them back to the first page.
|
|
18447
|
+
************************************************************/
|
|
18448
|
+
function ac_repull_page(gobj, event, kw, src) {
|
|
18449
|
+
let tabulator = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "tabulator");
|
|
18450
|
+
if (!tabulator) return 0;
|
|
18451
|
+
try {
|
|
18452
|
+
tabulator.replaceData();
|
|
18453
|
+
} catch (e) {
|
|
18454
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: cannot re-pull the page: ${e}`);
|
|
18455
|
+
return -1;
|
|
18456
|
+
}
|
|
18457
|
+
return 0;
|
|
18458
|
+
}
|
|
18459
|
+
/************************************************************
|
|
18460
|
+
* The host answered a page.
|
|
18461
|
+
************************************************************/
|
|
18462
|
+
function ac_page_loaded(gobj, event, kw, src) {
|
|
18463
|
+
settle_page(gobj, kw.req_id, {
|
|
18464
|
+
data: (0, _yuneta_gobj_js.is_array)(kw.rows) ? kw.rows : [],
|
|
18465
|
+
last_page: kw.pages || 1,
|
|
18466
|
+
last_row: typeof kw.total === "number" ? kw.total : 0
|
|
18467
|
+
}, null);
|
|
18468
|
+
return 0;
|
|
18469
|
+
}
|
|
18470
|
+
/************************************************************
|
|
18471
|
+
* The host could not answer it.
|
|
18472
|
+
************************************************************/
|
|
18473
|
+
function ac_page_failed(gobj, event, kw, src) {
|
|
18474
|
+
let why = kw && kw.error || "page request failed";
|
|
18475
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: ${why}`);
|
|
18476
|
+
settle_page(gobj, kw.req_id, null, why);
|
|
18477
|
+
return 0;
|
|
18478
|
+
}
|
|
18479
|
+
/************************************************************
|
|
18480
|
+
* Nobody answered in time.
|
|
18481
|
+
************************************************************/
|
|
18482
|
+
function ac_page_timeout(gobj, event, kw, src) {
|
|
18483
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no answer for page request '${kw.req_id}' in ${PAGE_TIMEOUT_MS}ms`);
|
|
18484
|
+
settle_page(gobj, kw.req_id, null, "page request timed out");
|
|
18485
|
+
return 0;
|
|
18486
|
+
}
|
|
18487
|
+
/************************************************************
|
|
18315
18488
|
* Filter the loaded rows by a term matched against every non-internal
|
|
18316
18489
|
* field. `clearFilter()` with no argument drops only THIS filter: the
|
|
18317
18490
|
* per-column header filters are a separate layer and survive, so a
|
|
@@ -18559,6 +18732,26 @@ function create_gclass$5(gclass_name) {
|
|
|
18559
18732
|
ac_cell_edited,
|
|
18560
18733
|
null
|
|
18561
18734
|
],
|
|
18735
|
+
[
|
|
18736
|
+
"EV_REPULL_PAGE",
|
|
18737
|
+
ac_repull_page,
|
|
18738
|
+
null
|
|
18739
|
+
],
|
|
18740
|
+
[
|
|
18741
|
+
"EV_PAGE_LOADED",
|
|
18742
|
+
ac_page_loaded,
|
|
18743
|
+
null
|
|
18744
|
+
],
|
|
18745
|
+
[
|
|
18746
|
+
"EV_PAGE_FAILED",
|
|
18747
|
+
ac_page_failed,
|
|
18748
|
+
null
|
|
18749
|
+
],
|
|
18750
|
+
[
|
|
18751
|
+
"EV_PAGE_TIMEOUT",
|
|
18752
|
+
ac_page_timeout,
|
|
18753
|
+
null
|
|
18754
|
+
],
|
|
18562
18755
|
[
|
|
18563
18756
|
"EV_SEARCH",
|
|
18564
18757
|
ac_search,
|
|
@@ -18610,7 +18803,12 @@ function create_gclass$5(gclass_name) {
|
|
|
18610
18803
|
["EV_REFRESH", 0],
|
|
18611
18804
|
["EV_SHOW_SCHEMA", 0],
|
|
18612
18805
|
["EV_CELL_EDITED", 0],
|
|
18806
|
+
["EV_REPULL_PAGE", 0],
|
|
18807
|
+
["EV_PAGE_LOADED", 0],
|
|
18808
|
+
["EV_PAGE_FAILED", 0],
|
|
18809
|
+
["EV_PAGE_TIMEOUT", 0],
|
|
18613
18810
|
["EV_UPDATE_FIELD", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT],
|
|
18811
|
+
["EV_REQUEST_PAGE", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT],
|
|
18614
18812
|
["EV_SEARCH", 0],
|
|
18615
18813
|
["EV_OPEN_COLUMNS", 0],
|
|
18616
18814
|
["EV_TOGGLE_COLUMN", 0],
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -14492,6 +14492,7 @@ var attrs_table$6 = [
|
|
|
14492
14492
|
SDATA(data_type_t.DTP_POINTER, "gobj_remote_yuno", 0, null, "Remote yuno for data fetching"),
|
|
14493
14493
|
SDATA(data_type_t.DTP_STRING, "treedb_name", 0, null, "Remote TreeDB service name"),
|
|
14494
14494
|
SDATA(data_type_t.DTP_JSON, "descs", 0, null, "Description of topics"),
|
|
14495
|
+
SDATA(data_type_t.DTP_BOOLEAN, "with_remote_paging", 0, false, "Each topic table pulls its rows a PAGE at a time (`nodes` from/limit) instead of loading the topic whole. Safe against a backend that cannot page: it answers the whole list, which the table reads as one page"),
|
|
14495
14496
|
SDATA(data_type_t.DTP_BOOLEAN, "system", 0, false, "Manage system topics (true) or user topics (false)"),
|
|
14496
14497
|
SDATA(data_type_t.DTP_STRING, "tabs_style", 0, "is-toggle is-fullwidth", "Bulma tab styling"),
|
|
14497
14498
|
SDATA(data_type_t.DTP_BOOLEAN, "with_cards_landing", 0, false, "Land on a grid of topic cards (list->detail): a card opens its table, with the tabs bar + a back-to-grid button. Off = tabs only (legacy)."),
|
|
@@ -15305,7 +15306,8 @@ function process_treedb_descs$1(gobj) {
|
|
|
15305
15306
|
with_delete_button: !readonly,
|
|
15306
15307
|
treedb_name,
|
|
15307
15308
|
topic_name: key,
|
|
15308
|
-
desc
|
|
15309
|
+
desc,
|
|
15310
|
+
with_remote_paging: gobj_read_bool_attr(gobj, "with_remote_paging")
|
|
15309
15311
|
};
|
|
15310
15312
|
let id = `${gobj_name(gobj)}?${desc.topic_name}`;
|
|
15311
15313
|
let gobj_topic_form = gobj_create_service(id, "C_YUI_TREEDB_TOPIC_WITH_FORM", kw_topic_form, gobj);
|
|
@@ -15376,6 +15378,8 @@ function register_sub_routes$1(gobj) {
|
|
|
15376
15378
|
function get_nodes$1(gobj, topic_name) {
|
|
15377
15379
|
const treedb_name = gobj.priv.treedb_name;
|
|
15378
15380
|
subscribe_treedb$1(gobj, topic_name);
|
|
15381
|
+
let gobj_topic_form = get_gobj_formtable(gobj, topic_name);
|
|
15382
|
+
if (gobj_topic_form && gobj_read_bool_attr(gobj_topic_form, "with_remote_paging")) return;
|
|
15379
15383
|
treedb_nodes$1(gobj, treedb_name, topic_name, { list_dict: true });
|
|
15380
15384
|
}
|
|
15381
15385
|
/************************************************************
|
|
@@ -15423,7 +15427,7 @@ function get_gobj_formtable(gobj, topic_name) {
|
|
|
15423
15427
|
/************************************************************
|
|
15424
15428
|
* Command to remote service
|
|
15425
15429
|
************************************************************/
|
|
15426
|
-
function treedb_nodes$1(gobj, treedb_name, topic_name, options) {
|
|
15430
|
+
function treedb_nodes$1(gobj, treedb_name, topic_name, options, page) {
|
|
15427
15431
|
let command = "nodes";
|
|
15428
15432
|
let gobj_remote_yuno = gobj_read_pointer_attr(gobj, "gobj_remote_yuno");
|
|
15429
15433
|
let kw = {
|
|
@@ -15433,8 +15437,17 @@ function treedb_nodes$1(gobj, treedb_name, topic_name, options) {
|
|
|
15433
15437
|
options: options || {}
|
|
15434
15438
|
};
|
|
15435
15439
|
kw.__md_command__ = { topic_name };
|
|
15440
|
+
if (page) {
|
|
15441
|
+
kw.from = page.from;
|
|
15442
|
+
kw.limit = page.limit;
|
|
15443
|
+
kw.__md_command__.req_id = page.req_id;
|
|
15444
|
+
}
|
|
15436
15445
|
let ret = gobj_command(gobj_remote_yuno, command, kw, gobj);
|
|
15437
|
-
if (ret)
|
|
15446
|
+
if (ret) {
|
|
15447
|
+
log_error(ret);
|
|
15448
|
+
return -1;
|
|
15449
|
+
}
|
|
15450
|
+
return 0;
|
|
15438
15451
|
}
|
|
15439
15452
|
/************************************************************
|
|
15440
15453
|
* Command to remote service
|
|
@@ -15668,7 +15681,7 @@ function ac_mt_command_answer$2(gobj, event, kw, src) {
|
|
|
15668
15681
|
yui_shell_show_error(yui_shell_of(gobj), comment, { t });
|
|
15669
15682
|
if (command === "update-node" || command === "create-node") {
|
|
15670
15683
|
let failed_topic = kw_get_str(gobj, kw_command, "topic_name", "", 0);
|
|
15671
|
-
if (failed_topic)
|
|
15684
|
+
if (failed_topic) gobj_send_event(gobj, "EV_REFRESH_TOPIC", { topic_name: failed_topic }, gobj);
|
|
15672
15685
|
}
|
|
15673
15686
|
}
|
|
15674
15687
|
return 0;
|
|
@@ -15684,7 +15697,15 @@ function ac_mt_command_answer$2(gobj, event, kw, src) {
|
|
|
15684
15697
|
let topic_name = kw_get_str(gobj, kw_command, "topic_name", "", kw_flag_t.KW_REQUIRED);
|
|
15685
15698
|
if (result >= 0) {
|
|
15686
15699
|
let gobj_topic_form = gobj_find_child(gobj, { __gobj_name__: `${gobj_name(gobj)}?${topic_name}` });
|
|
15687
|
-
|
|
15700
|
+
let answer = nodes_answer(data);
|
|
15701
|
+
let req_id = kw_get_str(gobj, kw_command, "req_id", "", 0);
|
|
15702
|
+
if (req_id) gobj_send_event(gobj_topic_form, "EV_PAGE_LOADED", {
|
|
15703
|
+
req_id,
|
|
15704
|
+
rows: answer.rows,
|
|
15705
|
+
pages: answer.pages,
|
|
15706
|
+
total: answer.total
|
|
15707
|
+
}, gobj);
|
|
15708
|
+
else gobj_send_event(gobj_topic_form, "EV_LOAD_NODES", answer.rows, gobj);
|
|
15688
15709
|
}
|
|
15689
15710
|
break;
|
|
15690
15711
|
case "create-node":
|
|
@@ -15910,6 +15931,31 @@ function ac_create_record(gobj, event, kw, src) {
|
|
|
15910
15931
|
});
|
|
15911
15932
|
}
|
|
15912
15933
|
/********************************************
|
|
15934
|
+
* A topic table wants a page.
|
|
15935
|
+
*
|
|
15936
|
+
* The transport is ours, so the table asks and we fetch; the answer
|
|
15937
|
+
* finds its way back by the `req_id` we echo. A backend that cannot
|
|
15938
|
+
* page answers the whole list, which the table takes as one page —
|
|
15939
|
+
* which is the truth, and is why the table can ask without knowing
|
|
15940
|
+
* what it is talking to.
|
|
15941
|
+
********************************************/
|
|
15942
|
+
function ac_request_page(gobj, event, kw, src) {
|
|
15943
|
+
let topic_name = kw.topic_name || gobj_read_attr(src, "topic_name");
|
|
15944
|
+
if (!topic_name || !kw.req_id) {
|
|
15945
|
+
log_error(`${gobj_short_name(gobj)}: a page request with no topic or no id`);
|
|
15946
|
+
return -1;
|
|
15947
|
+
}
|
|
15948
|
+
if (treedb_nodes$1(gobj, gobj_read_str_attr(gobj, "treedb_name"), topic_name, { list_dict: true }, {
|
|
15949
|
+
from: kw.from,
|
|
15950
|
+
limit: kw.limit,
|
|
15951
|
+
req_id: kw.req_id
|
|
15952
|
+
}) < 0) gobj_send_event(src, "EV_PAGE_FAILED", {
|
|
15953
|
+
req_id: kw.req_id,
|
|
15954
|
+
error: "cannot reach the backend"
|
|
15955
|
+
}, gobj);
|
|
15956
|
+
return 0;
|
|
15957
|
+
}
|
|
15958
|
+
/********************************************
|
|
15913
15959
|
* Event from formtable: ONE field of one record, edited in place.
|
|
15914
15960
|
*
|
|
15915
15961
|
* Written as a PARTIAL update and, deliberately, with NO `autolink`.
|
|
@@ -15976,6 +16022,11 @@ function ac_delete_record(gobj, event, kw, src) {
|
|
|
15976
16022
|
* }
|
|
15977
16023
|
********************************************/
|
|
15978
16024
|
function ac_refresh_topic(gobj, event, kw, src) {
|
|
16025
|
+
let gobj_topic_form = get_gobj_formtable(gobj, kw.topic_name);
|
|
16026
|
+
if (gobj_topic_form && gobj_read_bool_attr(gobj_topic_form, "with_remote_paging")) {
|
|
16027
|
+
gobj_send_event(gobj_topic_form, "EV_REPULL_PAGE", {}, gobj);
|
|
16028
|
+
return 0;
|
|
16029
|
+
}
|
|
15979
16030
|
treedb_nodes$1(gobj, gobj_read_str_attr(gobj, "treedb_name"), kw.topic_name, { list_dict: true });
|
|
15980
16031
|
return 0;
|
|
15981
16032
|
}
|
|
@@ -16065,6 +16116,11 @@ function create_gclass$6(gclass_name) {
|
|
|
16065
16116
|
ac_update_field,
|
|
16066
16117
|
null
|
|
16067
16118
|
],
|
|
16119
|
+
[
|
|
16120
|
+
"EV_REQUEST_PAGE",
|
|
16121
|
+
ac_request_page,
|
|
16122
|
+
null
|
|
16123
|
+
],
|
|
16068
16124
|
[
|
|
16069
16125
|
"EV_DELETE_RECORD",
|
|
16070
16126
|
ac_delete_record,
|
|
@@ -16139,6 +16195,7 @@ function create_gclass$6(gclass_name) {
|
|
|
16139
16195
|
["EV_CREATE_RECORD", 0],
|
|
16140
16196
|
["EV_UPDATE_RECORD", 0],
|
|
16141
16197
|
["EV_UPDATE_FIELD", 0],
|
|
16198
|
+
["EV_REQUEST_PAGE", 0],
|
|
16142
16199
|
["EV_DELETE_RECORD", 0],
|
|
16143
16200
|
["EV_RECORD_WRITTEN", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
|
|
16144
16201
|
["EV_REFRESH_TOPIC", 0],
|
|
@@ -16353,6 +16410,8 @@ var attrs_table$5 = [
|
|
|
16353
16410
|
SDATA(data_type_t.DTP_BOOLEAN, "with_export_button", 0, true, "Button toolbar EXPORT (download as CSV what the table holds)"),
|
|
16354
16411
|
SDATA(data_type_t.DTP_BOOLEAN, "with_header_filters", 0, true, "Per-column filter box in the table header, on the columns a text/number match means something (not hooks, fkeys or json)"),
|
|
16355
16412
|
SDATA(data_type_t.DTP_BOOLEAN, "with_inline_edit", 0, true, "Edit a writable scalar cell in place while in edition mode (the record form keeps the rest)"),
|
|
16413
|
+
SDATA(data_type_t.DTP_BOOLEAN, "with_remote_paging", 0, false, "Pull the topic a PAGE at a time from the backend (`nodes` from/limit) instead of loading it whole. Needs a backend that pages"),
|
|
16414
|
+
SDATA(data_type_t.DTP_INTEGER, "page_size", 0, 200, "Rows per page when with_remote_paging. Generous on purpose: a treedb that fits in one page behaves exactly as it did, paginator hidden and every filter seeing every row"),
|
|
16356
16415
|
SDATA(data_type_t.DTP_BOOLEAN, "with_in_row_edit_icons", 0, true, "Add a last column with internal EDIT/DELETE icon"),
|
|
16357
16416
|
SDATA(data_type_t.DTP_BOOLEAN, "editable", 0, false, "Edit state"),
|
|
16358
16417
|
SDATA(data_type_t.DTP_BOOLEAN, "with_checkbox", 0, true, "Auxiliary first column to select rows"),
|
|
@@ -16381,6 +16440,8 @@ var attrs_table$5 = [
|
|
|
16381
16440
|
SDATA_END()
|
|
16382
16441
|
];
|
|
16383
16442
|
var PRIVATE_DATA$5 = {
|
|
16443
|
+
_pending_pages: null,
|
|
16444
|
+
_page_seq: 0,
|
|
16384
16445
|
$container: null,
|
|
16385
16446
|
treedb_name: "",
|
|
16386
16447
|
topic_name: "",
|
|
@@ -17106,6 +17167,25 @@ function create_tabulator(gobj) {
|
|
|
17106
17167
|
let pkey = desc.pkey || "id";
|
|
17107
17168
|
let selectable = with_checkbox ? "highlight" : with_radio ? 1 : false;
|
|
17108
17169
|
let tabulator_settings = json_deep_copy(gobj_read_attr(gobj, "tabulator_settings"));
|
|
17170
|
+
if (gobj_read_bool_attr(gobj, "with_remote_paging")) {
|
|
17171
|
+
let page_size = gobj_read_integer_attr(gobj, "page_size") || 200;
|
|
17172
|
+
Object.assign(tabulator_settings, {
|
|
17173
|
+
pagination: true,
|
|
17174
|
+
paginationMode: "remote",
|
|
17175
|
+
filterMode: "local",
|
|
17176
|
+
paginationSize: page_size,
|
|
17177
|
+
paginationSizeSelector: [
|
|
17178
|
+
50,
|
|
17179
|
+
100,
|
|
17180
|
+
200,
|
|
17181
|
+
500
|
|
17182
|
+
],
|
|
17183
|
+
ajaxURL: "nodes",
|
|
17184
|
+
ajaxRequestFunc: function(url, config, params) {
|
|
17185
|
+
return request_page(gobj, params.page || 1, params.size || page_size);
|
|
17186
|
+
}
|
|
17187
|
+
});
|
|
17188
|
+
}
|
|
17109
17189
|
Object.assign(tabulator_settings, {
|
|
17110
17190
|
index: pkey,
|
|
17111
17191
|
columns,
|
|
@@ -18307,6 +18387,99 @@ function ac_show_schema(gobj, event, kw, src) {
|
|
|
18307
18387
|
return 0;
|
|
18308
18388
|
}
|
|
18309
18389
|
/************************************************************
|
|
18390
|
+
* Ask the host for one page and park the promise Tabulator wants.
|
|
18391
|
+
*
|
|
18392
|
+
* The transport belongs to the HOST, so the request goes up as an event
|
|
18393
|
+
* and the answer comes back down as one; what lives here is only the
|
|
18394
|
+
* promise, keyed by a correlation id the host echoes.
|
|
18395
|
+
*
|
|
18396
|
+
* Rejected right away when there is nobody to ask: a request that can
|
|
18397
|
+
* never be answered would leave the table spinning for ever and leak
|
|
18398
|
+
* one entry per attempt. Same for the watchdog — the link can stay up
|
|
18399
|
+
* and the answer still never land.
|
|
18400
|
+
************************************************************/
|
|
18401
|
+
var PAGE_TIMEOUT_MS = 2e4;
|
|
18402
|
+
function request_page(gobj, page, size) {
|
|
18403
|
+
let priv = gobj.priv;
|
|
18404
|
+
return new Promise(function(resolve, reject) {
|
|
18405
|
+
if (!priv._pending_pages) priv._pending_pages = {};
|
|
18406
|
+
let req_id = `p${++priv._page_seq}`;
|
|
18407
|
+
let timer = window.setTimeout(function() {
|
|
18408
|
+
gobj_send_event(gobj, "EV_PAGE_TIMEOUT", { req_id }, gobj);
|
|
18409
|
+
}, PAGE_TIMEOUT_MS);
|
|
18410
|
+
priv._pending_pages[req_id] = {
|
|
18411
|
+
resolve,
|
|
18412
|
+
reject,
|
|
18413
|
+
timer
|
|
18414
|
+
};
|
|
18415
|
+
gobj_publish_event(gobj, "EV_REQUEST_PAGE", {
|
|
18416
|
+
topic_name: gobj_read_str_attr(gobj, "topic_name"),
|
|
18417
|
+
req_id,
|
|
18418
|
+
from: (page - 1) * size + 1,
|
|
18419
|
+
limit: size
|
|
18420
|
+
});
|
|
18421
|
+
});
|
|
18422
|
+
}
|
|
18423
|
+
/************************************************************
|
|
18424
|
+
* Settle a parked page request, however it ended.
|
|
18425
|
+
************************************************************/
|
|
18426
|
+
function settle_page(gobj, req_id, value, error) {
|
|
18427
|
+
let priv = gobj.priv;
|
|
18428
|
+
let pend = priv._pending_pages ? priv._pending_pages[req_id] : null;
|
|
18429
|
+
if (!pend) return;
|
|
18430
|
+
delete priv._pending_pages[req_id];
|
|
18431
|
+
if (pend.timer) window.clearTimeout(pend.timer);
|
|
18432
|
+
if (error) {
|
|
18433
|
+
pend.reject(new Error(error));
|
|
18434
|
+
return;
|
|
18435
|
+
}
|
|
18436
|
+
pend.resolve(value);
|
|
18437
|
+
}
|
|
18438
|
+
/************************************************************
|
|
18439
|
+
* Pull the current page again. `replaceData()` re-runs
|
|
18440
|
+
* ajaxRequestFunc for the page the reader is on, so a refresh keeps
|
|
18441
|
+
* their position instead of throwing them back to the first page.
|
|
18442
|
+
************************************************************/
|
|
18443
|
+
function ac_repull_page(gobj, event, kw, src) {
|
|
18444
|
+
let tabulator = gobj_read_attr(gobj, "tabulator");
|
|
18445
|
+
if (!tabulator) return 0;
|
|
18446
|
+
try {
|
|
18447
|
+
tabulator.replaceData();
|
|
18448
|
+
} catch (e) {
|
|
18449
|
+
log_error(`${gobj_short_name(gobj)}: cannot re-pull the page: ${e}`);
|
|
18450
|
+
return -1;
|
|
18451
|
+
}
|
|
18452
|
+
return 0;
|
|
18453
|
+
}
|
|
18454
|
+
/************************************************************
|
|
18455
|
+
* The host answered a page.
|
|
18456
|
+
************************************************************/
|
|
18457
|
+
function ac_page_loaded(gobj, event, kw, src) {
|
|
18458
|
+
settle_page(gobj, kw.req_id, {
|
|
18459
|
+
data: is_array(kw.rows) ? kw.rows : [],
|
|
18460
|
+
last_page: kw.pages || 1,
|
|
18461
|
+
last_row: typeof kw.total === "number" ? kw.total : 0
|
|
18462
|
+
}, null);
|
|
18463
|
+
return 0;
|
|
18464
|
+
}
|
|
18465
|
+
/************************************************************
|
|
18466
|
+
* The host could not answer it.
|
|
18467
|
+
************************************************************/
|
|
18468
|
+
function ac_page_failed(gobj, event, kw, src) {
|
|
18469
|
+
let why = kw && kw.error || "page request failed";
|
|
18470
|
+
log_error(`${gobj_short_name(gobj)}: ${why}`);
|
|
18471
|
+
settle_page(gobj, kw.req_id, null, why);
|
|
18472
|
+
return 0;
|
|
18473
|
+
}
|
|
18474
|
+
/************************************************************
|
|
18475
|
+
* Nobody answered in time.
|
|
18476
|
+
************************************************************/
|
|
18477
|
+
function ac_page_timeout(gobj, event, kw, src) {
|
|
18478
|
+
log_error(`${gobj_short_name(gobj)}: no answer for page request '${kw.req_id}' in ${PAGE_TIMEOUT_MS}ms`);
|
|
18479
|
+
settle_page(gobj, kw.req_id, null, "page request timed out");
|
|
18480
|
+
return 0;
|
|
18481
|
+
}
|
|
18482
|
+
/************************************************************
|
|
18310
18483
|
* Filter the loaded rows by a term matched against every non-internal
|
|
18311
18484
|
* field. `clearFilter()` with no argument drops only THIS filter: the
|
|
18312
18485
|
* per-column header filters are a separate layer and survive, so a
|
|
@@ -18554,6 +18727,26 @@ function create_gclass$5(gclass_name) {
|
|
|
18554
18727
|
ac_cell_edited,
|
|
18555
18728
|
null
|
|
18556
18729
|
],
|
|
18730
|
+
[
|
|
18731
|
+
"EV_REPULL_PAGE",
|
|
18732
|
+
ac_repull_page,
|
|
18733
|
+
null
|
|
18734
|
+
],
|
|
18735
|
+
[
|
|
18736
|
+
"EV_PAGE_LOADED",
|
|
18737
|
+
ac_page_loaded,
|
|
18738
|
+
null
|
|
18739
|
+
],
|
|
18740
|
+
[
|
|
18741
|
+
"EV_PAGE_FAILED",
|
|
18742
|
+
ac_page_failed,
|
|
18743
|
+
null
|
|
18744
|
+
],
|
|
18745
|
+
[
|
|
18746
|
+
"EV_PAGE_TIMEOUT",
|
|
18747
|
+
ac_page_timeout,
|
|
18748
|
+
null
|
|
18749
|
+
],
|
|
18557
18750
|
[
|
|
18558
18751
|
"EV_SEARCH",
|
|
18559
18752
|
ac_search,
|
|
@@ -18605,7 +18798,12 @@ function create_gclass$5(gclass_name) {
|
|
|
18605
18798
|
["EV_REFRESH", 0],
|
|
18606
18799
|
["EV_SHOW_SCHEMA", 0],
|
|
18607
18800
|
["EV_CELL_EDITED", 0],
|
|
18801
|
+
["EV_REPULL_PAGE", 0],
|
|
18802
|
+
["EV_PAGE_LOADED", 0],
|
|
18803
|
+
["EV_PAGE_FAILED", 0],
|
|
18804
|
+
["EV_PAGE_TIMEOUT", 0],
|
|
18608
18805
|
["EV_UPDATE_FIELD", event_flag_t.EVF_OUTPUT_EVENT],
|
|
18806
|
+
["EV_REQUEST_PAGE", event_flag_t.EVF_OUTPUT_EVENT],
|
|
18609
18807
|
["EV_SEARCH", 0],
|
|
18610
18808
|
["EV_OPEN_COLUMNS", 0],
|
|
18611
18809
|
["EV_TOGGLE_COLUMN", 0],
|
package/package.json
CHANGED
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
gclass_find_by_name,
|
|
49
49
|
gobj_write_attr,
|
|
50
50
|
gobj_read_bool_attr,
|
|
51
|
+
gobj_read_integer_attr,
|
|
51
52
|
gobj_read_str_attr,
|
|
52
53
|
gobj_write_bool_attr,
|
|
53
54
|
gobj_publish_event,
|
|
@@ -112,6 +113,8 @@ SDATA(data_type_t.DTP_BOOLEAN, "with_columns_button", 0, true, "Button
|
|
|
112
113
|
SDATA(data_type_t.DTP_BOOLEAN, "with_export_button", 0, true, "Button toolbar EXPORT (download as CSV what the table holds)"),
|
|
113
114
|
SDATA(data_type_t.DTP_BOOLEAN, "with_header_filters", 0, true, "Per-column filter box in the table header, on the columns a text/number match means something (not hooks, fkeys or json)"),
|
|
114
115
|
SDATA(data_type_t.DTP_BOOLEAN, "with_inline_edit", 0, true, "Edit a writable scalar cell in place while in edition mode (the record form keeps the rest)"),
|
|
116
|
+
SDATA(data_type_t.DTP_BOOLEAN, "with_remote_paging", 0, false, "Pull the topic a PAGE at a time from the backend (`nodes` from/limit) instead of loading it whole. Needs a backend that pages"),
|
|
117
|
+
SDATA(data_type_t.DTP_INTEGER, "page_size", 0, 200, "Rows per page when with_remote_paging. Generous on purpose: a treedb that fits in one page behaves exactly as it did, paginator hidden and every filter seeing every row"),
|
|
115
118
|
SDATA(data_type_t.DTP_BOOLEAN, "with_in_row_edit_icons", 0, true, "Add a last column with internal EDIT/DELETE icon"),
|
|
116
119
|
|
|
117
120
|
SDATA(data_type_t.DTP_BOOLEAN, "editable", 0, false, "Edit state"),
|
|
@@ -146,6 +149,8 @@ SDATA_END()
|
|
|
146
149
|
];
|
|
147
150
|
|
|
148
151
|
let PRIVATE_DATA = {
|
|
152
|
+
_pending_pages: null, // req_id -> {resolve, reject, timer}
|
|
153
|
+
_page_seq: 0, // correlation id of a page request
|
|
149
154
|
$container: null,
|
|
150
155
|
treedb_name: "",
|
|
151
156
|
topic_name: "",
|
|
@@ -1095,6 +1100,36 @@ function create_tabulator(gobj)
|
|
|
1095
1100
|
|
|
1096
1101
|
let tabulator_settings = json_deep_copy(gobj_read_attr(gobj, "tabulator_settings"));
|
|
1097
1102
|
|
|
1103
|
+
/* Remote paging: the TABLE pulls, instead of the host pushing the whole
|
|
1104
|
+
* topic down. The page size is generous on purpose — a treedb that fits
|
|
1105
|
+
* in one page behaves exactly as it did before, with the paginator
|
|
1106
|
+
* hidden and every filter seeing every row. Only a topic that does NOT
|
|
1107
|
+
* fit pays for paging, and for that one loading it whole was never an
|
|
1108
|
+
* option anyway.
|
|
1109
|
+
*
|
|
1110
|
+
* `filterMode: "local"` says the plain truth: the header filters and the
|
|
1111
|
+
* search box work on the page that is loaded. Same as the tranger
|
|
1112
|
+
* browser's Rows card, and for the same reason — the alternative is
|
|
1113
|
+
* pushing every filter to the backend and changing what "search" means.
|
|
1114
|
+
*/
|
|
1115
|
+
if(gobj_read_bool_attr(gobj, "with_remote_paging")) {
|
|
1116
|
+
let page_size = gobj_read_integer_attr(gobj, "page_size") || 200;
|
|
1117
|
+
Object.assign(tabulator_settings, {
|
|
1118
|
+
pagination: true,
|
|
1119
|
+
paginationMode: "remote",
|
|
1120
|
+
filterMode: "local",
|
|
1121
|
+
paginationSize: page_size,
|
|
1122
|
+
paginationSizeSelector: [50, 100, 200, 500],
|
|
1123
|
+
ajaxURL: "nodes", /* dummy: only fires the func */
|
|
1124
|
+
ajaxRequestFunc: function(url, config, params) {
|
|
1125
|
+
/* Widget plumbing, not an action: Tabulator wants a PROMISE
|
|
1126
|
+
* back — it is a data source. The action is the event this
|
|
1127
|
+
* parks on. */
|
|
1128
|
+
return request_page(gobj, params.page || 1, params.size || page_size);
|
|
1129
|
+
}
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1098
1133
|
Object.assign(tabulator_settings, {
|
|
1099
1134
|
index: pkey,
|
|
1100
1135
|
columns: columns,
|
|
@@ -2965,6 +3000,125 @@ function ac_show_schema(gobj, event, kw, src)
|
|
|
2965
3000
|
return 0;
|
|
2966
3001
|
}
|
|
2967
3002
|
|
|
3003
|
+
/************************************************************
|
|
3004
|
+
* Ask the host for one page and park the promise Tabulator wants.
|
|
3005
|
+
*
|
|
3006
|
+
* The transport belongs to the HOST, so the request goes up as an event
|
|
3007
|
+
* and the answer comes back down as one; what lives here is only the
|
|
3008
|
+
* promise, keyed by a correlation id the host echoes.
|
|
3009
|
+
*
|
|
3010
|
+
* Rejected right away when there is nobody to ask: a request that can
|
|
3011
|
+
* never be answered would leave the table spinning for ever and leak
|
|
3012
|
+
* one entry per attempt. Same for the watchdog — the link can stay up
|
|
3013
|
+
* and the answer still never land.
|
|
3014
|
+
************************************************************/
|
|
3015
|
+
const PAGE_TIMEOUT_MS = 20000;
|
|
3016
|
+
|
|
3017
|
+
function request_page(gobj, page, size)
|
|
3018
|
+
{
|
|
3019
|
+
let priv = gobj.priv;
|
|
3020
|
+
|
|
3021
|
+
return new Promise(function(resolve, reject) {
|
|
3022
|
+
if(!priv._pending_pages) {
|
|
3023
|
+
priv._pending_pages = {};
|
|
3024
|
+
}
|
|
3025
|
+
let req_id = `p${++priv._page_seq}`;
|
|
3026
|
+
|
|
3027
|
+
let timer = window.setTimeout(function() {
|
|
3028
|
+
gobj_send_event(gobj, "EV_PAGE_TIMEOUT", {req_id: req_id}, gobj);
|
|
3029
|
+
}, PAGE_TIMEOUT_MS);
|
|
3030
|
+
|
|
3031
|
+
priv._pending_pages[req_id] = {resolve: resolve, reject: reject, timer: timer};
|
|
3032
|
+
|
|
3033
|
+
gobj_publish_event(
|
|
3034
|
+
gobj,
|
|
3035
|
+
"EV_REQUEST_PAGE",
|
|
3036
|
+
{
|
|
3037
|
+
topic_name: gobj_read_str_attr(gobj, "topic_name"),
|
|
3038
|
+
req_id: req_id,
|
|
3039
|
+
from: (page - 1) * size + 1, /* `nodes` counts from 1 */
|
|
3040
|
+
limit: size
|
|
3041
|
+
}
|
|
3042
|
+
);
|
|
3043
|
+
});
|
|
3044
|
+
}
|
|
3045
|
+
|
|
3046
|
+
/************************************************************
|
|
3047
|
+
* Settle a parked page request, however it ended.
|
|
3048
|
+
************************************************************/
|
|
3049
|
+
function settle_page(gobj, req_id, value, error)
|
|
3050
|
+
{
|
|
3051
|
+
let priv = gobj.priv;
|
|
3052
|
+
let pend = priv._pending_pages? priv._pending_pages[req_id] : null;
|
|
3053
|
+
if(!pend) {
|
|
3054
|
+
return; /* already settled: a late answer after a timeout */
|
|
3055
|
+
}
|
|
3056
|
+
delete priv._pending_pages[req_id];
|
|
3057
|
+
if(pend.timer) {
|
|
3058
|
+
window.clearTimeout(pend.timer);
|
|
3059
|
+
}
|
|
3060
|
+
if(error) {
|
|
3061
|
+
pend.reject(new Error(error));
|
|
3062
|
+
return;
|
|
3063
|
+
}
|
|
3064
|
+
pend.resolve(value);
|
|
3065
|
+
}
|
|
3066
|
+
|
|
3067
|
+
/************************************************************
|
|
3068
|
+
* Pull the current page again. `replaceData()` re-runs
|
|
3069
|
+
* ajaxRequestFunc for the page the reader is on, so a refresh keeps
|
|
3070
|
+
* their position instead of throwing them back to the first page.
|
|
3071
|
+
************************************************************/
|
|
3072
|
+
function ac_repull_page(gobj, event, kw, src)
|
|
3073
|
+
{
|
|
3074
|
+
let tabulator = gobj_read_attr(gobj, "tabulator");
|
|
3075
|
+
if(!tabulator) {
|
|
3076
|
+
return 0;
|
|
3077
|
+
}
|
|
3078
|
+
try {
|
|
3079
|
+
tabulator.replaceData();
|
|
3080
|
+
} catch(e) {
|
|
3081
|
+
log_error(`${gobj_short_name(gobj)}: cannot re-pull the page: ${e}`);
|
|
3082
|
+
return -1;
|
|
3083
|
+
}
|
|
3084
|
+
return 0;
|
|
3085
|
+
}
|
|
3086
|
+
|
|
3087
|
+
/************************************************************
|
|
3088
|
+
* The host answered a page.
|
|
3089
|
+
************************************************************/
|
|
3090
|
+
function ac_page_loaded(gobj, event, kw, src)
|
|
3091
|
+
{
|
|
3092
|
+
settle_page(gobj, kw.req_id, {
|
|
3093
|
+
data: is_array(kw.rows)? kw.rows : [],
|
|
3094
|
+
last_page: kw.pages || 1,
|
|
3095
|
+
last_row: (typeof kw.total === "number")? kw.total : 0
|
|
3096
|
+
}, null);
|
|
3097
|
+
return 0;
|
|
3098
|
+
}
|
|
3099
|
+
|
|
3100
|
+
/************************************************************
|
|
3101
|
+
* The host could not answer it.
|
|
3102
|
+
************************************************************/
|
|
3103
|
+
function ac_page_failed(gobj, event, kw, src)
|
|
3104
|
+
{
|
|
3105
|
+
let why = (kw && kw.error) || "page request failed";
|
|
3106
|
+
log_error(`${gobj_short_name(gobj)}: ${why}`);
|
|
3107
|
+
settle_page(gobj, kw.req_id, null, why);
|
|
3108
|
+
return 0;
|
|
3109
|
+
}
|
|
3110
|
+
|
|
3111
|
+
/************************************************************
|
|
3112
|
+
* Nobody answered in time.
|
|
3113
|
+
************************************************************/
|
|
3114
|
+
function ac_page_timeout(gobj, event, kw, src)
|
|
3115
|
+
{
|
|
3116
|
+
log_error(`${gobj_short_name(gobj)}: no answer for page request ` +
|
|
3117
|
+
`'${kw.req_id}' in ${PAGE_TIMEOUT_MS}ms`);
|
|
3118
|
+
settle_page(gobj, kw.req_id, null, "page request timed out");
|
|
3119
|
+
return 0;
|
|
3120
|
+
}
|
|
3121
|
+
|
|
2968
3122
|
/************************************************************
|
|
2969
3123
|
* Filter the loaded rows by a term matched against every non-internal
|
|
2970
3124
|
* field. `clearFilter()` with no argument drops only THIS filter: the
|
|
@@ -3187,6 +3341,10 @@ function create_gclass(gclass_name)
|
|
|
3187
3341
|
["EV_REFRESH", ac_refresh, null],
|
|
3188
3342
|
["EV_SHOW_SCHEMA", ac_show_schema, null],
|
|
3189
3343
|
["EV_CELL_EDITED", ac_cell_edited, null],
|
|
3344
|
+
["EV_REPULL_PAGE", ac_repull_page, null],
|
|
3345
|
+
["EV_PAGE_LOADED", ac_page_loaded, null],
|
|
3346
|
+
["EV_PAGE_FAILED", ac_page_failed, null],
|
|
3347
|
+
["EV_PAGE_TIMEOUT", ac_page_timeout, null],
|
|
3190
3348
|
["EV_SEARCH", ac_search, null],
|
|
3191
3349
|
["EV_OPEN_COLUMNS", ac_open_columns, null],
|
|
3192
3350
|
["EV_TOGGLE_COLUMN", ac_toggle_column, null],
|
|
@@ -3220,7 +3378,12 @@ function create_gclass(gclass_name)
|
|
|
3220
3378
|
["EV_REFRESH", 0],
|
|
3221
3379
|
["EV_SHOW_SCHEMA", 0],
|
|
3222
3380
|
["EV_CELL_EDITED", 0],
|
|
3381
|
+
["EV_REPULL_PAGE", 0],
|
|
3382
|
+
["EV_PAGE_LOADED", 0],
|
|
3383
|
+
["EV_PAGE_FAILED", 0],
|
|
3384
|
+
["EV_PAGE_TIMEOUT", 0],
|
|
3223
3385
|
["EV_UPDATE_FIELD", event_flag_t.EVF_OUTPUT_EVENT],
|
|
3386
|
+
["EV_REQUEST_PAGE", event_flag_t.EVF_OUTPUT_EVENT],
|
|
3224
3387
|
["EV_SEARCH", 0],
|
|
3225
3388
|
["EV_OPEN_COLUMNS", 0],
|
|
3226
3389
|
["EV_TOGGLE_COLUMN", 0],
|
|
@@ -75,6 +75,7 @@ SDATA(data_type_t.DTP_POINTER, "subscriber", 0, null, "Subscriber of o
|
|
|
75
75
|
SDATA(data_type_t.DTP_POINTER, "gobj_remote_yuno", 0, null, "Remote yuno for data fetching"),
|
|
76
76
|
SDATA(data_type_t.DTP_STRING, "treedb_name", 0, null, "Remote TreeDB service name"),
|
|
77
77
|
SDATA(data_type_t.DTP_JSON, "descs", 0, null, "Description of topics"),
|
|
78
|
+
SDATA(data_type_t.DTP_BOOLEAN, "with_remote_paging", 0, false, "Each topic table pulls its rows a PAGE at a time (`nodes` from/limit) instead of loading the topic whole. Safe against a backend that cannot page: it answers the whole list, which the table reads as one page"),
|
|
78
79
|
SDATA(data_type_t.DTP_BOOLEAN, "system", 0, false, "Manage system topics (true) or user topics (false)"),
|
|
79
80
|
SDATA(data_type_t.DTP_STRING, "tabs_style", 0, "is-toggle is-fullwidth", "Bulma tab styling"),
|
|
80
81
|
SDATA(data_type_t.DTP_BOOLEAN, "with_cards_landing",0, false, "Land on a grid of topic cards (list->detail): a card opens its table, with the tabs bar + a back-to-grid button. Off = tabs only (legacy)."),
|
|
@@ -1037,7 +1038,8 @@ function process_treedb_descs(gobj)
|
|
|
1037
1038
|
|
|
1038
1039
|
treedb_name: treedb_name,
|
|
1039
1040
|
topic_name: key,
|
|
1040
|
-
desc: desc
|
|
1041
|
+
desc: desc,
|
|
1042
|
+
with_remote_paging: gobj_read_bool_attr(gobj, "with_remote_paging")
|
|
1041
1043
|
};
|
|
1042
1044
|
|
|
1043
1045
|
let id = `${gobj_name(gobj)}?${desc.topic_name}`;
|
|
@@ -1171,8 +1173,15 @@ function get_nodes(gobj, topic_name)
|
|
|
1171
1173
|
subscribe_treedb(gobj, topic_name);
|
|
1172
1174
|
|
|
1173
1175
|
/*
|
|
1174
|
-
* Get data
|
|
1176
|
+
* Get data — unless the table pulls its own pages, in which case it has
|
|
1177
|
+
* already asked (or is about to) and a second whole-topic fetch would be
|
|
1178
|
+
* the very transfer paging exists to avoid.
|
|
1175
1179
|
*/
|
|
1180
|
+
let gobj_topic_form = get_gobj_formtable(gobj, topic_name);
|
|
1181
|
+
if(gobj_topic_form && gobj_read_bool_attr(gobj_topic_form, "with_remote_paging")) {
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1176
1185
|
treedb_nodes(
|
|
1177
1186
|
gobj,
|
|
1178
1187
|
treedb_name,
|
|
@@ -1328,7 +1337,7 @@ function get_gobj_formtable(gobj, topic_name)
|
|
|
1328
1337
|
/************************************************************
|
|
1329
1338
|
* Command to remote service
|
|
1330
1339
|
************************************************************/
|
|
1331
|
-
function treedb_nodes(gobj, treedb_name, topic_name, options)
|
|
1340
|
+
function treedb_nodes(gobj, treedb_name, topic_name, options, page)
|
|
1332
1341
|
{
|
|
1333
1342
|
let command = "nodes";
|
|
1334
1343
|
|
|
@@ -1344,6 +1353,16 @@ function treedb_nodes(gobj, treedb_name, topic_name, options)
|
|
|
1344
1353
|
topic_name: topic_name,
|
|
1345
1354
|
};
|
|
1346
1355
|
|
|
1356
|
+
/* A PAGE, when the table asked for one. `from` is 1-based and a
|
|
1357
|
+
* backend that does not know these two answers the whole list, which
|
|
1358
|
+
* nodes_answer() reads as one page — so an old backend degrades to
|
|
1359
|
+
* exactly what it always did. */
|
|
1360
|
+
if(page) {
|
|
1361
|
+
kw.from = page.from;
|
|
1362
|
+
kw.limit = page.limit;
|
|
1363
|
+
kw.__md_command__.req_id = page.req_id;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1347
1366
|
// TODO review msg_iev_write_key(kw, "__topic_name__", topic_name);
|
|
1348
1367
|
|
|
1349
1368
|
let ret = gobj_command(
|
|
@@ -1354,7 +1373,9 @@ function treedb_nodes(gobj, treedb_name, topic_name, options)
|
|
|
1354
1373
|
);
|
|
1355
1374
|
if(ret) {
|
|
1356
1375
|
log_error(ret);
|
|
1376
|
+
return -1;
|
|
1357
1377
|
}
|
|
1378
|
+
return 0;
|
|
1358
1379
|
}
|
|
1359
1380
|
|
|
1360
1381
|
/************************************************************
|
|
@@ -1749,8 +1770,8 @@ function ac_mt_command_answer(gobj, event, kw, src)
|
|
|
1749
1770
|
if(command === "update-node" || command === "create-node") {
|
|
1750
1771
|
let failed_topic = kw_get_str(gobj, kw_command, "topic_name", "", 0);
|
|
1751
1772
|
if(failed_topic) {
|
|
1752
|
-
|
|
1753
|
-
|
|
1773
|
+
gobj_send_event(gobj, "EV_REFRESH_TOPIC",
|
|
1774
|
+
{topic_name: failed_topic}, gobj);
|
|
1754
1775
|
}
|
|
1755
1776
|
}
|
|
1756
1777
|
}
|
|
@@ -1778,13 +1799,38 @@ function ac_mt_command_answer(gobj, event, kw, src)
|
|
|
1778
1799
|
* answers the plain list whatever is asked of it. Reading
|
|
1779
1800
|
* the shape rather than assuming it is what keeps the table
|
|
1780
1801
|
* from rendering an object's keys as rows the day a backend
|
|
1781
|
-
* upgrades
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1802
|
+
* upgrades — and it is what lets a paging table survive a
|
|
1803
|
+
* backend that cannot page: the whole list arrives as one
|
|
1804
|
+
* page, which is the truth. */
|
|
1805
|
+
let answer = nodes_answer(data);
|
|
1806
|
+
/* The correlation id rides in `__md_command__`, which is the
|
|
1807
|
+
* same place `topic_name` rides — and `kw_command` IS that
|
|
1808
|
+
* object: C_IEVENT_CLI EXTRACTS `__md_command__` from the kw
|
|
1809
|
+
* and pushes it as the command stack's `kw`. So it is read
|
|
1810
|
+
* flat, one level up from where the sent kw put it, and it
|
|
1811
|
+
* never travels to the backend as a parameter it does not
|
|
1812
|
+
* know. */
|
|
1813
|
+
let req_id = kw_get_str(gobj, kw_command, "req_id", "", 0);
|
|
1814
|
+
if(req_id) {
|
|
1815
|
+
gobj_send_event(
|
|
1816
|
+
gobj_topic_form,
|
|
1817
|
+
"EV_PAGE_LOADED",
|
|
1818
|
+
{
|
|
1819
|
+
req_id: req_id,
|
|
1820
|
+
rows: answer.rows,
|
|
1821
|
+
pages: answer.pages,
|
|
1822
|
+
total: answer.total
|
|
1823
|
+
},
|
|
1824
|
+
gobj
|
|
1825
|
+
);
|
|
1826
|
+
} else {
|
|
1827
|
+
gobj_send_event(
|
|
1828
|
+
gobj_topic_form,
|
|
1829
|
+
"EV_LOAD_NODES",
|
|
1830
|
+
answer.rows,
|
|
1831
|
+
gobj
|
|
1832
|
+
);
|
|
1833
|
+
}
|
|
1788
1834
|
}
|
|
1789
1835
|
break;
|
|
1790
1836
|
|
|
@@ -2147,6 +2193,40 @@ function ac_create_record(gobj, event, kw, src)
|
|
|
2147
2193
|
);
|
|
2148
2194
|
}
|
|
2149
2195
|
|
|
2196
|
+
/********************************************
|
|
2197
|
+
* A topic table wants a page.
|
|
2198
|
+
*
|
|
2199
|
+
* The transport is ours, so the table asks and we fetch; the answer
|
|
2200
|
+
* finds its way back by the `req_id` we echo. A backend that cannot
|
|
2201
|
+
* page answers the whole list, which the table takes as one page —
|
|
2202
|
+
* which is the truth, and is why the table can ask without knowing
|
|
2203
|
+
* what it is talking to.
|
|
2204
|
+
********************************************/
|
|
2205
|
+
function ac_request_page(gobj, event, kw, src)
|
|
2206
|
+
{
|
|
2207
|
+
let topic_name = kw.topic_name || gobj_read_attr(src, "topic_name");
|
|
2208
|
+
if(!topic_name || !kw.req_id) {
|
|
2209
|
+
log_error(`${gobj_short_name(gobj)}: a page request with no topic or no id`);
|
|
2210
|
+
return -1;
|
|
2211
|
+
}
|
|
2212
|
+
|
|
2213
|
+
let ret = treedb_nodes(
|
|
2214
|
+
gobj,
|
|
2215
|
+
gobj_read_str_attr(gobj, "treedb_name"),
|
|
2216
|
+
topic_name,
|
|
2217
|
+
{list_dict: true},
|
|
2218
|
+
{from: kw.from, limit: kw.limit, req_id: kw.req_id}
|
|
2219
|
+
);
|
|
2220
|
+
|
|
2221
|
+
if(ret < 0) {
|
|
2222
|
+
/* The command never went out, so no answer will ever come: tell the
|
|
2223
|
+
* table now instead of leaving it spinning until its watchdog. */
|
|
2224
|
+
gobj_send_event(src, "EV_PAGE_FAILED",
|
|
2225
|
+
{req_id: kw.req_id, error: "cannot reach the backend"}, gobj);
|
|
2226
|
+
}
|
|
2227
|
+
return 0;
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2150
2230
|
/********************************************
|
|
2151
2231
|
* Event from formtable: ONE field of one record, edited in place.
|
|
2152
2232
|
*
|
|
@@ -2256,6 +2336,14 @@ function ac_delete_record(gobj, event, kw, src)
|
|
|
2256
2336
|
********************************************/
|
|
2257
2337
|
function ac_refresh_topic(gobj, event, kw, src)
|
|
2258
2338
|
{
|
|
2339
|
+
let gobj_topic_form = get_gobj_formtable(gobj, kw.topic_name);
|
|
2340
|
+
if(gobj_topic_form && gobj_read_bool_attr(gobj_topic_form, "with_remote_paging")) {
|
|
2341
|
+
/* A table that pulls refreshes by pulling: pushing a whole topic
|
|
2342
|
+
* into a paged table would replace its page with everything. */
|
|
2343
|
+
gobj_send_event(gobj_topic_form, "EV_REPULL_PAGE", {}, gobj);
|
|
2344
|
+
return 0;
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2259
2347
|
let options = {
|
|
2260
2348
|
list_dict: true
|
|
2261
2349
|
};
|
|
@@ -2357,6 +2445,7 @@ function create_gclass(gclass_name)
|
|
|
2357
2445
|
["EV_CREATE_RECORD", ac_create_record, null],
|
|
2358
2446
|
["EV_UPDATE_RECORD", ac_update_record, null],
|
|
2359
2447
|
["EV_UPDATE_FIELD", ac_update_field, null],
|
|
2448
|
+
["EV_REQUEST_PAGE", ac_request_page, null],
|
|
2360
2449
|
["EV_DELETE_RECORD", ac_delete_record, null],
|
|
2361
2450
|
["EV_REFRESH_TOPIC", ac_refresh_topic, null],
|
|
2362
2451
|
["EV_OPEN_JSON", ac_open_json, null],
|
|
@@ -2384,6 +2473,7 @@ function create_gclass(gclass_name)
|
|
|
2384
2473
|
["EV_CREATE_RECORD", 0],
|
|
2385
2474
|
["EV_UPDATE_RECORD", 0],
|
|
2386
2475
|
["EV_UPDATE_FIELD", 0],
|
|
2476
|
+
["EV_REQUEST_PAGE", 0],
|
|
2387
2477
|
["EV_DELETE_RECORD", 0],
|
|
2388
2478
|
["EV_RECORD_WRITTEN", event_flag_t.EVF_OUTPUT_EVENT|
|
|
2389
2479
|
event_flag_t.EVF_NO_WARN_SUBS],
|