GSI Object Oriented Online Offline (Go4)  GO4-6.2.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
pareditor.js
Go to the documentation of this file.
1 // $Id: pareditor.js 3524 2022-01-27 10:18:58Z linev $
2 
3 JSROOT.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 
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 
35  clearChanges() {
36  this.changes = []; //
37  this.selectDom().select(".buttonChangedParameter").style("display", "none"); // hide warning sign
38  }
39 
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 
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 
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 supress 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 
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 
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 
300  setItemName(name, opt, hpainter) {
301  super.setItemName(name, opt, hpainter);
302  this.fillComments();
303  }
304 
306  redraw() {
307  console.log("ParemeterEditor Redraw...");
308  this.fillMemberTable();
309  this.fillComments();
310  }
311  }
312 
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 });