GSI Object Oriented Online Offline (Go4) GO4-6.4.0
Loading...
Searching...
No Matches
pareditor.js
Go to the documentation of this file.
1// $Id$
2
3JSROOT.define([], () => {
4
5 "use strict";
6
7 if (typeof GO4 != "object") {
8 let e1 = new Error("pareditor.js requires GO4 to be already loaded");
9 e1.source = "pareditor.js";
10 throw e1;
11 }
12
13 // ===========================================================================================
14
15 class ParameterEditor extends JSROOT.BasePainter {
16
17 constructor(dom, par) {
18 super(dom);
19 this.par = par;
20 this.changes = ["dummy", "init"]; // TODO: put to common "base class" of condition and parameter editor
21 }
22
23 checkResize() { }
24
25 /** @summary add identifier of changed element to list, make warning sign visible */
26 markChanged(key) {
27 // first avoid duplicate keys:
28 if (this.changes.indexOf(key) >= 0) return;
29 this.changes.push(key);
30 console.log("Mark changed :%s", key);
31 this.selectDom().select(".buttonChangedParameter").style("display", null);// show warning sign
32 }
33
34 /** @summary clear changes flag */
35 clearChanges() {
36 this.changes = []; //
37 this.selectDom().select(".buttonChangedParameter").style("display", "none"); // hide warning sign
38 }
39
40 /** @summary scan changed value list and return optionstring to be send to server */
41 evaluateChanges(optionstring) {
42 let dom = this.selectDom(),
43 len = this.changes.length;
44 for (let index = 0; index < len; index++) {
45 //let cursor=changes.pop();
46 let key = this.changes[index];
47 console.log("Evaluate change key:%s", key);
48 // here mapping of key to editor field:
49 // key will be name of letiable which is class name of input field:
50 let val = dom.select("." + key.toString()).property("value"),
51 arraysplit = key.split("_"), opt = "";
52 if (arraysplit.length > 1) {
53 // found array with index after separator, reformat it:
54 opt = arraysplit[0];
55 if (arraysplit.length > 3) {
56 // 3dim array:
57 let ix = arraysplit[arraysplit.length - 3], //
58 iy = arraysplit[arraysplit.length - 2], //
59 iz = arraysplit[arraysplit.length - 1]; //
60 opt += `[${ix}][${iy}][${iz}]`;
61 } else if (arraysplit.length > 2) {
62 // 2dim array:
63 let ix = arraysplit[arraysplit.length - 2], //
64 iy = arraysplit[arraysplit.length - 1]; //
65 opt += `[${ix}][${iy}]`;
66 } else {
67 // 1dim array:
68 opt = arraysplit[0];
69 let ix = arraysplit[arraysplit.length - 1]; //
70 opt += `[${ix}]`;
71 }
72 } else {
73 opt = key;
74 }
75
76 optionstring += `&${opt}=${val}`;
77 }// for index
78 console.log("Resulting option string:%s", optionstring);
79 return optionstring;
80 }
81
82 /** @summary fill comments for parameter */
83 fillComments() {
84 if (this.xreq || !this.getItemName())
85 return Promise.resolve(this); // avoid double requests
86
87 let tr_nodes = this.selectDom().select(".par_values tbody").selectAll("tr").nodes();
88
89 this.xreq = true;
90
91 return JSROOT.httpRequest(this.getItemName() + "/h.json?more", 'object').then(res => {
92
93 tr_nodes.forEach(raw_tr => {
94 let tr = d3.select(raw_tr),
95 name = tr.select("td").text(),
96 title = null, arrayinfo = null, typeinfo = null;
97 for (let i in res._childs) {
98 let n = res._childs[i]._name;
99 let arsplit = name.split("["); // remove array information at the end, if any
100 if (arsplit[0] == n) {
101 //if ((name==n) || (name.indexOf(n) == 0)) {
102 title = res._childs[i]._title;
103 arrayinfo = res._childs[i]._arraydim;
104 typeinfo = res._childs[i]._typename;
105 //console.log("found title="+title+", arrayinfo="+arrayinfo);
106 break;
107 }
108 }
109 if (title !== null)
110 tr.select("td.par_comment").text(title).style('white-space', 'nowrap'); // comments from class member declaration
111 if (typeinfo !== null) {
112 tr.select("td.par_class").text(typeinfo).style('white-space', 'nowrap'); // member type
113
114 let par_table = d3.select(raw_tr.parentNode.parentNode);
115
116 if (par_table.classed("par_arraytable")) {
117
118 // if we are inside array table, indicate that we are an array
119 par_table.select('td.par_comment').text("Array").style('white-space', 'nowrap');
120
121 // put type information of array to subtable header
122 par_table.selectAll('td.par_class').text(arrayinfo ? `${typeinfo} [${arrayinfo}]` : typeinfo).style('white-space', 'nowrap');
123 }
124 }
125
126 });
127
128 return this; // return painter
129
130 }).finally(() => { this.xreq = false; });
131 }
132
133 /** @summary fill parameter values in the editor */
134 fillMemberTable() {
135 let editor = this,
136 par = this.par,
137 dom = this.selectDom();
138
139 dom.selectAll(".par_values tbody").html("");
140 let found_title = false;
141 for (let key in par) {
142 if (typeof par[key] == 'function') continue;
143 if (key == 'fTitle') { found_title = true; continue; }
144 if (!found_title) continue;
145 let value = (par[key] != null ? (par[key] instanceof Array ? par[key] : par[key].toString()) : "null");
146 let classname = "";
147 if (value instanceof Array) {
148 // here put array table with clickable header:
149 // (thanks to Jonathan at http://mediaformations.com/accordion-tables-with-jquery/ for this idea!)
150 let arraytableclass = key.toString() + "_array";
151 let isTooBig = false;
152 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>`);
153 for (let i = 0; i < value.length; i++) {
154 if (value[i] instanceof Array) {
155 let subvalue = value[i];
156 for (let j = 0; j < subvalue.length; j++) {
157 if (subvalue[j] instanceof Array) {
158 let subsubvalue = subvalue[j];
159 // here suppress display of 3d arrays if too
160 // large:
161 if (subsubvalue.length * subvalue.length * value.length > 1000) {
162 isTooBig = true;
163 break;
164 }
165 else {
166 for (let k = 0; k < subsubvalue.length; k++) {
167 // decode 3dim array
168 classname = key.toString() + `_${i}_${j}_${k}`;
169 dom.select("." + arraytableclass + " tbody").append("tr")
170 .html(`<td class='par_key'>${key.toString()}[${i}][${j}][${k}]</td>
171 <td class='par_class'></td>
172 <td class='par_value'><input type='text' size='10' value='${subsubvalue[k]}' class='${classname}'/></td>
173 <td class='par_comment'></td>`);
174
175 } // for k
176 }
177 }
178 else {
179 // decode 2dim array
180 classname = key.toString() + `_${i}_${j}`;
181 dom.select("." + arraytableclass + " tbody").append("tr")
182 .html(`<td class='par_key'>${key.toString()}[${i}][${j}]</td>
183 <td class='par_class'></td>
184 <td class='par_value'><input type='text' size='10' value='${subvalue[j]}' class='${classname}'/></td>
185 <td class='par_comment'></td>`);
186
187 }
188 } // for j
189 }
190 else {
191 // decode 1dim array
192 classname = key.toString() + "_" + i;
193 dom.select("." + arraytableclass + " tbody").append("tr")
194 .html(`<td class='par_key'>${key.toString()}[${i}]</td>
195 <td class='par_class'></td>
196 <td class='par_value'><input type='text' size='10' value='${value[i]}' class='${classname}'/></td>
197 <td class='par_comment'></td>`);
198 }
199 } // for i
200 //
201 if (isTooBig) {
202 dom.select("." + arraytableclass + " tbody")
203 .append("tr").html(
204 `<td class='par_key'>${key.toString()}</td>
205 <td colspan='3'> Sorry, Array dimension [${value.length}][${subvalue.length}][${subsubvalue.length}] too big to display!</td>`);
206 }
207
208 dom.select("table." + arraytableclass + " thead tr").on("click",
209 function() {
210 let prnt = d3.select(this.parentNode);
211 while (!prnt.empty() && !prnt.classed('par_arraytable')) prnt = d3.select(prnt.node().parentNode);
212
213 let disp = prnt.select('tbody').style('display');
214 prnt.select('tbody').style('display', disp == 'none' ? null : 'none');
215
216 // $(this).parents('table.par_arraytable').children('tbody').toggle();
217
218 let txt = prnt.select('td').text();
219 if (txt.indexOf("[+]") >= 0)
220 txt = txt.replace("[+]", "[-]");
221 else
222 txt = txt.replace("[-]", "[+]");
223 prnt.select('td').text(txt);
224
225 txt = prnt.select('td.par_value').text();
226 if (txt.indexOf("expand") != -1)
227 txt = txt.replace("expand", "shrink");
228 else
229 txt = txt.replace("shrink", "expand");
230 prnt.select('td.par_value').text(txt);
231 }
232 );
233 dom.select("table." + arraytableclass).select('tbody').style('display', 'none');
234
235 } else {
236 classname = key.toString();
237 dom.select(".par_values > tbody").append("tr")
238 .html(`<td class='par_key'>${key.toString()}</td>
239 <td class='par_class'></td>
240 <td class='par_value'><input type='text' size='10' value='${value}' class='${classname}'/></td>
241 <td class='par_comment'></td>`);
242 }
243
244 }
245 // here set callbacks; referred classname must be evaluated dynamically in function!:
246 dom.select(" .par_values tbody").selectAll("input").on("change", function() { editor.markChanged(d3.select(this).attr('class')); });
247 dom.select(".par_values tbody").selectAll("td").classed("par_membertable_style", true);
248 dom.selectAll(".par_values > thead th").classed("par_memberheader_style", true);
249 dom.selectAll(".par_arraytable thead td").classed("par_arrayheader_style", true);
250
251 this.clearChanges();
252 }
253
254 /** @summary fill basic efitor fields */
255 fillEditor() {
256 let dom = this.selectDom();
257
258 dom.select(".par_name").text(this.par.fName);
259 dom.select(".par_type").text(this.par._typename);
260
261 dom.select(".buttonGetParameter")
262 .style('background-image', "url(" + GO4.source_dir + "icons/right.png)")
263 .on("click", () => {
264 console.log("update item = " + this.getItemName());
265 if (JSROOT.hpainter)
266 JSROOT.hpainter.display(this.getItemName());
267 else
268 console.log("hpainter not found");
269 });
270
271 dom.select(".buttonSetParameter")
272 .style('background-image', "url(" + GO4.source_dir + "icons/left.png)")
273 .on("click", () => {
274 let options = this.evaluateChanges(""); // do not need to use name here
275 console.log("set parameter " + this.getItemName() + ", options=" + options);
276 GO4.ExecuteMethod(this, "UpdateFromUrl", options).then(() => {
277 console.log("set parameter done.");
278 this.clearChanges();
279 }).catch(err => {
280 console.log("Set parameter FAILED.", err);
281 });
282 })
283
284 dom.select(".buttonChangedParameter")
285 .style('background-image', "url(" + GO4.source_dir + "icons/info1.png)");
286
287 this.fillMemberTable();
288 }
289
290 /** @summary redraw parameter - when object updated from analysis */
291 redrawObject(obj) {
292 console.log('redraw parameter!!!');
293 if (obj._typename != this.par._typename) return false;
294 this.par = JSROOT.clone(obj);
295 this.redraw(); // no need to redraw complete pad
296 return true;
297 }
298
299 /** @summary set item name - used by hpainter */
300 setItemName(name, opt, hpainter) {
301 super.setItemName(name, opt, hpainter);
302 this.fillComments();
303 }
304
305 /** @summary readraw editor */
306 redraw() {
307 console.log("ParemeterEditor Redraw...");
308 this.fillMemberTable();
309 this.fillComments();
310 }
311 }
312
313 /** @summary entry function to draw parameter editor */
314 GO4.drawParameter = function(dom, par /*, option */) {
315 let editor = new ParameterEditor(dom, par),
316 sel = editor.selectDom(),
317 h = sel.node().clientHeight,
318 w = sel.node().clientWidth;
319
320 if ((h < 10) && (w > 10)) sel.style("height", Math.round(w * 0.4)+"px");
321
322 return JSROOT.httpRequest(GO4.source_dir + "html/pareditor.htm", "text").then(code => {
323 sel.html(code);
324 editor.setTopPainter();
325 editor.fillEditor();
326 return editor.fillComments();
327 });
328 }
329
330 return GO4;
331
332});