GSI Object Oriented Online Offline (Go4) GO4-6.4.0
Loading...
Searching...
No Matches
pareditor.mjs
Go to the documentation of this file.
1import { BasePainter, getHPainter, httpRequest, clone, d3_select } from 'jsroot';
2
3import { source_dir, executeMethod, GO4 } from './core.mjs';
4
5class ParameterEditor extends BasePainter {
6
7 constructor(dom, par) {
8 super(dom);
9 this.par = par;
10 this.changes = ["dummy", "init"]; // TODO: put to common "base class" of condition and parameter editor
11 }
12
13 checkResize() { }
14
15 /** @summary add identifier of changed element to list, make warning sign visible */
16 markChanged(key) {
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
22 }
23
24 /** @summary clear changes flag */
25 clearChanges() {
26 this.changes = []; //
27 this.selectDom().select(".buttonChangedParameter").style("display", "none"); // hide warning sign
28 }
29
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:
44 opt = arraysplit[0];
45 if (arraysplit.length > 3) {
46 // 3dim array:
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) {
52 // 2dim array:
53 let ix = arraysplit[arraysplit.length - 2], //
54 iy = arraysplit[arraysplit.length - 1]; //
55 opt += `[${ix}][${iy}]`;
56 } else {
57 // 1dim array:
58 opt = arraysplit[0];
59 let ix = arraysplit[arraysplit.length - 1]; //
60 opt += `[${ix}]`;
61 }
62 } else {
63 opt = key;
64 }
65
66 optionstring += `&${opt}=${val}`;
67 }// for index
68 console.log("Resulting option string:%s", optionstring);
69 return optionstring;
70 }
71
72 /** @summary fill comments for parameter */
73 fillComments() {
74 if (this.xreq || !this.getItemName())
75 return Promise.resolve(this); // avoid double requests
76
77 let tr_nodes = this.selectDom().select(".par_values tbody").selectAll("tr").nodes();
78
79 this.xreq = true;
80
81 return httpRequest(this.getItemName() + '/h.json?more', 'object').then(res => {
82
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);
96 break;
97 }
98 }
99 if (title !== null)
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
103
104 let par_table = d3_select(raw_tr.parentNode.parentNode);
105
106 if (par_table.classed("par_arraytable")) {
107
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');
110
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');
113 }
114 }
115
116 });
117
118 return this; // return painter
119
120 }).finally(() => { this.xreq = false; });
121 }
122
123 /** @summary fill parameter values in the editor */
124 fillMemberTable() {
125 let editor = this,
126 par = this.par,
127 dom = this.selectDom();
128
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");
136 let classname = "";
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
150 // large:
151 if (subsubvalue.length * subvalue.length * value.length > 1000) {
152 isTooBig = true;
153 break;
154 }
155 else {
156 for (let k = 0; k < subsubvalue.length; k++) {
157 // decode 3dim array
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>`);
164
165 } // for k
166 }
167 }
168 else {
169 // decode 2dim array
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>`);
176
177 }
178 } // for j
179 }
180 else {
181 // decode 1dim array
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>`);
188 }
189 } // for i
190 //
191 if (isTooBig) {
192 dom.select("." + arraytableclass + " tbody")
193 .append("tr").html(
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>`);
196 }
197
198 dom.select("table." + arraytableclass + " thead tr").on("click",
199 function() {
200 let prnt = d3_select(this.parentNode);
201 while (!prnt.empty() && !prnt.classed('par_arraytable'))
202 prnt = d3_select(prnt.node().parentNode);
203
204 let disp = prnt.select('tbody').style('display');
205 prnt.select('tbody').style('display', disp == 'none' ? null : 'none');
206
207 // $(this).parents('table.par_arraytable').children('tbody').toggle();
208
209 let txt = prnt.select('td').text();
210 if (txt.indexOf("[+]") >= 0)
211 txt = txt.replace("[+]", "[-]");
212 else
213 txt = txt.replace("[-]", "[+]");
214 prnt.select('td').text(txt);
215
216 txt = prnt.select('td.par_value').text();
217 if (txt.indexOf("expand") != -1)
218 txt = txt.replace("expand", "shrink");
219 else
220 txt = txt.replace("shrink", "expand");
221 prnt.select('td.par_value').text(txt);
222 }
223 );
224 dom.select("table." + arraytableclass).select('tbody').style('display', 'none');
225
226 } else {
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>`);
233 }
234
235 }
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);
241
242 this.clearChanges();
243 }
244
245 /** @summary fill basic editor fields */
246 fillEditor() {
247 let dom = this.selectDom();
248
249 dom.select(".par_name").text(this.par.fName);
250 dom.select(".par_type").text(this.par._typename);
251
252 dom.select(".buttonGetParameter")
253 .style('background-image', `url(${source_dir}icons/right.png)`)
254 .on("click", () => {
255 console.log("update item = " + this.getItemName());
256 getHPainter()?.display(this.getItemName());
257 });
258
259 dom.select(".buttonSetParameter")
260 .style('background-image', `url(${source_dir}icons/left.png)`)
261 .on("click", () => {
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.");
266 this.clearChanges();
267 }).catch(err => {
268 console.log("Set parameter FAILED.", err);
269 });
270 })
271
272 dom.select(".buttonChangedParameter")
273 .style('background-image', `url(${source_dir}icons/info1.png)`);
274
275 this.fillMemberTable();
276 }
277
278 /** @summary redraw parameter - when object updated from analysis */
279 redrawObject(obj) {
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
284 return true;
285 }
286
287 /** @summary set item name - used by hpainter */
288 setItemName(name, opt, hpainter) {
289 super.setItemName(name, opt, hpainter);
290 this.fillComments();
291 }
292
293 /** @summary readraw editor */
294 redraw() {
295 console.log("ParemeterEditor Redraw...");
296 this.fillMemberTable();
297 this.fillComments();
298 }
299}
300
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;
307
308 if ((h < 10) && (w > 10)) sel.style("height", Math.round(w * 0.4)+"px");
309
310 return httpRequest(`${source_dir}html5/pareditor.html`, 'text').then(code => {
311 sel.html(code);
312 editor.setTopPainter();
313 editor.fillEditor();
314 return editor.fillComments();
315 });
316}
317
318export { ParameterEditor };