1import { BasePainter, getHPainter, httpRequest, clone, d3_select } from 'jsroot';
3import { source_dir, executeMethod, GO4 } from './core.mjs';
5class ParameterEditor extends BasePainter {
7 constructor(dom, par) {
10 this.changes = ["dummy", "init"]; // TODO: put to common "base class" of condition and parameter editor
15 /** @summary add identifier of changed element to list, make warning sign visible */
17 // first avoid duplicate keys:
18 if (this.changes.indexOf(key) >= 0) return;
19 this.changes.push(key);
20 console.log("Mark changed :%s", key);
21 this.selectDom().select(".buttonChangedParameter").style("display", null);// show warning sign
24 /** @summary clear changes flag */
27 this.selectDom().select(".buttonChangedParameter").style("display", "none"); // hide warning sign
30 /** @summary scan changed value list and return optionstring to be send to server */
31 evaluateChanges(optionstring) {
32 let dom = this.selectDom(),
33 len = this.changes.length;
34 for (let index = 0; index < len; index++) {
35 //let cursor=changes.pop();
36 let key = this.changes[index];
37 console.log("Evaluate change key:%s", key);
38 // here mapping of key to editor field:
39 // key will be name of letiable which is class name of input field:
40 let val = dom.select("." + key.toString()).property("value"),
41 arraysplit = key.split("_"), opt = "";
42 if (arraysplit.length > 1) {
43 // found array with index after separator, reformat it:
45 if (arraysplit.length > 3) {
47 let ix = arraysplit[arraysplit.length - 3], //
48 iy = arraysplit[arraysplit.length - 2], //
49 iz = arraysplit[arraysplit.length - 1]; //
50 opt += `[${ix}][${iy}][${iz}]`;
51 } else if (arraysplit.length > 2) {
53 let ix = arraysplit[arraysplit.length - 2], //
54 iy = arraysplit[arraysplit.length - 1]; //
55 opt += `[${ix}][${iy}]`;
59 let ix = arraysplit[arraysplit.length - 1]; //
66 optionstring += `&${opt}=${val}`;
68 console.log("Resulting option string:%s", optionstring);
72 /** @summary fill comments for parameter */
74 if (this.xreq || !this.getItemName())
75 return Promise.resolve(this); // avoid double requests
77 let tr_nodes = this.selectDom().select(".par_values tbody").selectAll("tr").nodes();
81 return httpRequest(this.getItemName() + '/h.json?more', 'object').then(res => {
83 tr_nodes.forEach(raw_tr => {
84 let tr = d3_select(raw_tr),
85 name = tr.select("td").text(),
86 title = null, arrayinfo = null, typeinfo = null;
87 for (let i in res._childs) {
88 let n = res._childs[i]._name;
89 let arsplit = name.split("["); // remove array information at the end, if any
90 if (arsplit[0] == n) {
91 //if ((name==n) || (name.indexOf(n) == 0)) {
92 title = res._childs[i]._title;
93 arrayinfo = res._childs[i]._arraydim;
94 typeinfo = res._childs[i]._typename;
95 //console.log("found title="+title+", arrayinfo="+arrayinfo);
100 tr.select("td.par_comment").text(title).style('white-space', 'nowrap'); // comments from class member declaration
101 if (typeinfo !== null) {
102 tr.select("td.par_class").text(typeinfo).style('white-space', 'nowrap'); // member type
104 let par_table = d3_select(raw_tr.parentNode.parentNode);
106 if (par_table.classed("par_arraytable")) {
108 // if we are inside array table, indicate that we are an array
109 par_table.select('td.par_comment').text("Array").style('white-space', 'nowrap');
111 // put type information of array to subtable header
112 par_table.selectAll('td.par_class').text(arrayinfo ? `${typeinfo} [${arrayinfo}]` : typeinfo).style('white-space', 'nowrap');
118 return this; // return painter
120 }).finally(() => { this.xreq = false; });
123 /** @summary fill parameter values in the editor */
127 dom = this.selectDom();
129 dom.selectAll(".par_values tbody").html("");
130 let found_title = false;
131 for (let key in par) {
132 if (typeof par[key] == 'function') continue;
133 if (key == 'fTitle') { found_title = true; continue; }
134 if (!found_title) continue;
135 let value = (par[key] != null ? (par[key] instanceof Array ? par[key] : par[key].toString()) : "null");
137 if (value instanceof Array) {
138 // here put array table with clickable header:
139 // (thanks to Jonathan at http://mediaformations.com/accordion-tables-with-jquery/ for this idea!)
140 let arraytableclass = key.toString() + "_array";
141 let isTooBig = false;
142 dom.select(".par_values > tbody").append("tr").html(`<td style='width:100%; padding:0px' colspan='4' > <table class='${arraytableclass} par_arraytable'><thead><tr><td class='par_key'> <bf>[+]</bf>${key.toString()}</td><td class='par_class'></td><td class='par_value' >Click to expand</td><td class='par_comment'></td></tr></thead><tbody></tbody></table></td>`);
143 for (let i = 0; i < value.length; i++) {
144 if (value[i] instanceof Array) {
145 let subvalue = value[i];
146 for (let j = 0; j < subvalue.length; j++) {
147 if (subvalue[j] instanceof Array) {
148 let subsubvalue = subvalue[j];
149 // here suppress display of 3d arrays if too
151 if (subsubvalue.length * subvalue.length * value.length > 1000) {
156 for (let k = 0; k < subsubvalue.length; k++) {
158 classname = key.toString() + `_${i}_${j}_${k}`;
159 dom.select("." + arraytableclass + " tbody").append("tr")
160 .html(`<td class='par_key'>${key.toString()}[${i}][${j}][${k}]</td>
161 <td class='par_class'></td>
162 <td class='par_value'><input type='text' size='10' value='${subsubvalue[k]}' class='${classname}'/></td>
163 <td class='par_comment'></td>`);
170 classname = key.toString() + `_${i}_${j}`;
171 dom.select("." + arraytableclass + " tbody").append("tr")
172 .html(`<td class='par_key'>${key.toString()}[${i}][${j}]</td>
173 <td class='par_class'></td>
174 <td class='par_value'><input type='text' size='10' value='${subvalue[j]}' class='${classname}'/></td>
175 <td class='par_comment'></td>`);
182 classname = key.toString() + "_" + i;
183 dom.select("." + arraytableclass + " tbody").append("tr")
184 .html(`<td class='par_key'>${key.toString()}[${i}]</td>
185 <td class='par_class'></td>
186 <td class='par_value'><input type='text' size='10' value='${value[i]}' class='${classname}'/></td>
187 <td class='par_comment'></td>`);
192 dom.select("." + arraytableclass + " tbody")
194 `<td class='par_key'>${key.toString()}</td>
195 <td colspan='3'> Sorry, Array dimension [${value.length}][${subvalue.length}][${subsubvalue.length}] too big to display!</td>`);
198 dom.select("table." + arraytableclass + " thead tr").on("click",
200 let prnt = d3_select(this.parentNode);
201 while (!prnt.empty() && !prnt.classed('par_arraytable'))
202 prnt = d3_select(prnt.node().parentNode);
204 let disp = prnt.select('tbody').style('display');
205 prnt.select('tbody').style('display', disp == 'none' ? null : 'none');
207 // $(this).parents('table.par_arraytable').children('tbody').toggle();
209 let txt = prnt.select('td').text();
210 if (txt.indexOf("[+]") >= 0)
211 txt = txt.replace("[+]", "[-]");
213 txt = txt.replace("[-]", "[+]");
214 prnt.select('td').text(txt);
216 txt = prnt.select('td.par_value').text();
217 if (txt.indexOf("expand") != -1)
218 txt = txt.replace("expand", "shrink");
220 txt = txt.replace("shrink", "expand");
221 prnt.select('td.par_value').text(txt);
224 dom.select("table." + arraytableclass).select('tbody').style('display', 'none');
227 classname = key.toString();
228 dom.select(".par_values > tbody").append("tr")
229 .html(`<td class='par_key'>${key.toString()}</td>
230 <td class='par_class'></td>
231 <td class='par_value'><input type='text' size='10' value='${value}' class='${classname}'/></td>
232 <td class='par_comment'></td>`);
236 // here set callbacks; referred classname must be evaluated dynamically in function!:
237 dom.select(" .par_values tbody").selectAll("input").on("change", function() { editor.markChanged(d3_select(this).attr('class')); });
238 dom.select(".par_values tbody").selectAll("td").classed("par_membertable_style", true);
239 dom.selectAll(".par_values > thead th").classed("par_memberheader_style", true);
240 dom.selectAll(".par_arraytable thead td").classed("par_arrayheader_style", true);
245 /** @summary fill basic editor fields */
247 let dom = this.selectDom();
249 dom.select(".par_name").text(this.par.fName);
250 dom.select(".par_type").text(this.par._typename);
252 dom.select(".buttonGetParameter")
253 .style('background-image', `url(${source_dir}icons/right.png)`)
255 console.log("update item = " + this.getItemName());
256 getHPainter()?.display(this.getItemName());
259 dom.select(".buttonSetParameter")
260 .style('background-image', `url(${source_dir}icons/left.png)`)
262 let options = this.evaluateChanges(""); // do not need to use name here
263 console.log("set parameter " + this.getItemName() + ", options=" + options);
264 executeMethod(this, "UpdateFromUrl", options).then(() => {
265 console.log("set parameter done.");
268 console.log("Set parameter FAILED.", err);
272 dom.select(".buttonChangedParameter")
273 .style('background-image', `url(${source_dir}icons/info1.png)`);
275 this.fillMemberTable();
278 /** @summary redraw parameter - when object updated from analysis */
280 console.log('redraw parameter!!!');
281 if (obj._typename != this.par._typename) return false;
282 this.par = clone(obj);
283 this.redraw(); // no need to redraw complete pad
287 /** @summary set item name - used by hpainter */
288 setItemName(name, opt, hpainter) {
289 super.setItemName(name, opt, hpainter);
293 /** @summary readraw editor */
295 console.log("ParemeterEditor Redraw...");
296 this.fillMemberTable();
301/** @summary entry function to draw parameter editor */
302GO4.drawParameter = function(dom, par /*, option */) {
303 let editor = new ParameterEditor(dom, par),
304 sel = editor.selectDom(),
305 h = sel.node().clientHeight,
306 w = sel.node().clientWidth;
308 if ((h < 10) && (w > 10)) sel.style("height", Math.round(w * 0.4)+"px");
310 return httpRequest(`${source_dir}html5/pareditor.html`, 'text').then(code => {
312 editor.setTopPainter();
314 return editor.fillComments();
318export { ParameterEditor };