3JSROOT.define([], () => {
7 if (typeof GO4 != "object") {
8 let e1 = new Error("pareditor.js requires GO4 to be already loaded");
9 e1.source = "pareditor.js";
13 // ===========================================================================================
15 class ParameterEditor extends JSROOT.BasePainter {
17 constructor(dom, par) {
20 this.changes = ["dummy", "init"]; // TODO: put to common "base class" of condition and parameter editor
25 /** @summary add identifier of changed element to list, make warning sign visible */
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
34 /** @summary clear changes flag */
37 this.selectDom().select(".buttonChangedParameter").style("display", "none"); // hide warning sign
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:
55 if (arraysplit.length > 3) {
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) {
63 let ix = arraysplit[arraysplit.length - 2], //
64 iy = arraysplit[arraysplit.length - 1]; //
65 opt += `[${ix}][${iy}]`;
69 let ix = arraysplit[arraysplit.length - 1]; //
76 optionstring += `&${opt}=${val}`;
78 console.log("Resulting option string:%s", optionstring);
82 /** @summary fill comments for parameter */
84 if (this.xreq || !this.getItemName())
85 return Promise.resolve(this); // avoid double requests
87 let tr_nodes = this.selectDom().select(".par_values tbody").selectAll("tr").nodes();
91 return JSROOT.httpRequest(this.getItemName() + "/h.json?more", 'object').then(res => {
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);
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
114 let par_table = d3.select(raw_tr.parentNode.parentNode);
116 if (par_table.classed("par_arraytable")) {
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');
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');
128 return this; // return painter
130 }).finally(() => { this.xreq = false; });
133 /** @summary fill parameter values in the editor */
137 dom = this.selectDom();
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");
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
161 if (subsubvalue.length * subvalue.length * value.length > 1000) {
166 for (let k = 0; k < subsubvalue.length; k++) {
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>`);
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>`);
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>`);
202 dom.select("." + arraytableclass + " tbody")
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>`);
208 dom.select("table." + arraytableclass + " thead tr").on("click",
210 let prnt = d3.select(this.parentNode);
211 while (!prnt.empty() && !prnt.classed('par_arraytable')) prnt = d3.select(prnt.node().parentNode);
213 let disp = prnt.select('tbody').style('display');
214 prnt.select('tbody').style('display', disp == 'none' ? null : 'none');
216 // $(this).parents('table.par_arraytable').children('tbody').toggle();
218 let txt = prnt.select('td').text();
219 if (txt.indexOf("[+]") >= 0)
220 txt = txt.replace("[+]", "[-]");
222 txt = txt.replace("[-]", "[+]");
223 prnt.select('td').text(txt);
225 txt = prnt.select('td.par_value').text();
226 if (txt.indexOf("expand") != -1)
227 txt = txt.replace("expand", "shrink");
229 txt = txt.replace("shrink", "expand");
230 prnt.select('td.par_value').text(txt);
233 dom.select("table." + arraytableclass).select('tbody').style('display', 'none');
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>`);
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);
254 /** @summary fill basic efitor fields */
256 let dom = this.selectDom();
258 dom.select(".par_name").text(this.par.fName);
259 dom.select(".par_type").text(this.par._typename);
261 dom.select(".buttonGetParameter")
262 .style('background-image', "url(" + GO4.source_dir + "icons/right.png)")
264 console.log("update item = " + this.getItemName());
266 JSROOT.hpainter.display(this.getItemName());
268 console.log("hpainter not found");
271 dom.select(".buttonSetParameter")
272 .style('background-image', "url(" + GO4.source_dir + "icons/left.png)")
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.");
280 console.log("Set parameter FAILED.", err);
284 dom.select(".buttonChangedParameter")
285 .style('background-image', "url(" + GO4.source_dir + "icons/info1.png)");
287 this.fillMemberTable();
290 /** @summary redraw parameter - when object updated from analysis */
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
299 /** @summary set item name - used by hpainter */
300 setItemName(name, opt, hpainter) {
301 super.setItemName(name, opt, hpainter);
305 /** @summary readraw editor */
307 console.log("ParemeterEditor Redraw...");
308 this.fillMemberTable();
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;
320 if ((h < 10) && (w > 10)) sel.style("height", Math.round(w * 0.4)+"px");
322 return JSROOT.httpRequest(GO4.source_dir + "html/pareditor.htm", "text").then(code => {
324 editor.setTopPainter();
326 return editor.fillComments();