3const go4Script = document.currentScript;
5JSROOT.define(["painter"], jsrp => {
9 const GO4 = { version: "6.2.99", id_counter: 1, source_dir: "" };
11 if (go4Script && (typeof go4Script.src == "string")) {
12 const pos = go4Script.src.indexOf("html/go4.js");
14 GO4.source_dir = go4Script.src.substr(0, pos);
15 console.log(`Set GO4.source_dir to ${GO4.source_dir}, ${GO4.version}`);
19 /** @summary Execute method for selected painter object
20 * @return {Promise} when done */
21 GO4.ExecuteMethod = function(painter, method, options) {
23 if (painter.getItemName())
24 prefix = painter.getItemName() + "/"; // suppress / if item name is empty
26 let fullcom = prefix + "exe.json?method=" + method + (options || "&"); // send any arguments otherwise ROOT refuse to process it
28 return JSROOT.httpRequest(fullcom, 'text');
31 // ==================================================================================
33 GO4.DrawAnalysisRatemeter = function(divid, itemname) {
35 function CreateHTML() {
36 let elem = d3.select('#'+divid);
38 if (elem.size() == 0) return null;
39 if (elem.select(".event_rate").size() > 0) return elem;
41 let html = "<div style='padding-top:2px'>";
42 html += "<img class='go4_logo' style='vertical-align:middle;margin-left:5px;margin-right:5px;' src='go4sys/icons/go4logorun4.gif' alt='logo'></img>";
43 html += "<label class='event_source' style='border: 1px solid gray; font-size:large; vertical-align:middle; padding-left:3px; padding-right:3px;'>file.lmd</label> ";
44 html += "<label class='event_rate' style='border: 1px solid gray; font-size:large; vertical-align:middle; background-color: grey'; padding-left:3px; padding-right:3px;>---</label> Ev/s ";
45 html += "<label class='aver_rate' style='border: 1px solid gray; font-size:large; vertical-align:middle; padding-left:3px; padding-right:3px;'>---</label> Ev/s ";
46 html += "<label class='run_time' style='border: 1px solid gray; font-size:large; vertical-align:middle; padding-left:3px; padding-right:3px;'>---</label> s ";
47 html += "<label class='total_events' style='border: 1px solid gray; font-size:large; vertical-align:middle; padding-left:3px; padding-right:3px;'>---</label> Events ";
48 html += "<label class='analysis_time' style='border: 1px solid gray; font-size:large; vertical-align:middle; padding-left:3px; padding-right:3px;'>time</label>";
51 elem.style('overflow','hidden')
52 .style('padding-left','5px')
53 .style('display', 'inline-block')
54 .style('white-space', 'nowrap')
57 // use height of child element
58 let brlayout = JSROOT.hpainter ? JSROOT.hpainter.brlayout : null,
59 sz = elem.node().clientHeight + 12;
62 brlayout.adjustSeparators(null, sz, true);
66 let xreq = false, was_running = null;
68 function UpdateRatemeter() {
71 let elem = CreateHTML();
75 JSROOT.httpRequest(itemname + "/root.json.gz", 'object').then(res => {
76 elem.select(".event_rate").style('background-color', res.fbRunning ? 'lightgreen' : 'red');
77 if (was_running != res.fbRunning)
78 elem.select(".go4_logo").attr("src", res.fbRunning ? 'go4sys/icons/go4logorun4.gif' : 'go4sys/icons/go4logo_t.png');
80 was_running = res.fbRunning;
82 elem.select(".event_source").text(res.fxEventSource == "" ? "<source>" : res.fxEventSource);
83 elem.select(".event_rate").text(res.fdRate.toFixed(1));
84 elem.select(".aver_rate").text((res.fdTime > 0 ? res.fuCurrentCount / res.fdTime : 0).toFixed(1));
85 elem.select(".run_time").text(res.fdTime.toFixed(1));
86 elem.select(".total_events").text(res.fuCurrentCount);
87 elem.select(".analysis_time").text(res.fxDateString == "" ? "<datime>" : res.fxDateString);
89 elem.select(".event_rate").style('background-color','grey');
97 setInterval(UpdateRatemeter, 2000);
101 GO4.MakeMsgListRequest = function(hitem, item) {
102 let arg = "&max=2000";
103 if ('last-id' in item) arg+= "&id="+item['last-id'];
104 return 'exe.json.gz?method=Select' + arg;
107 GO4.AfterMsgListRequest = function(hitem, item, obj) {
111 delete item['last-id'];
114 // ignore all other classes
115 if (obj._typename != 'TList') return;
117 obj._typename = "TGo4MsgList";
119 if (obj.arr.length>0) {
120 let duplicate = (('last-id' in item) && (item['last-id'] == obj.arr[0].fString));
122 item['last-id'] = obj.arr[0].fString;
124 // workaround for snapshot, it sends always same messages many times
125 if (duplicate) obj.arr.length = 1;
127 // add clear function for item
128 if (!('clear' in item))
129 item['clear'] = function() { delete this['last-id']; }
133 class MsgListPainter extends JSROOT.BasePainter {
135 constructor(dom, lst) {
141 // if (!obj._typename != 'TList') return false;
148 if (!this.lst) return;
150 let frame = this.selectDom();
152 let main = frame.select("div");
154 main = frame.append("div")
155 .style('max-width','100%')
156 .style('max-height','100%')
157 .style('overflow','auto');
159 let old = main.selectAll("pre");
160 let newsize = old.size() + this.lst.arr.length - 1;
162 // in the browser keep maximum 2000 entries
164 old.select(function(d,i) { return i < newsize - 2000 ? this : null; }).remove();
166 for (let i = this.lst.arr.length-1; i > 0; i--)
167 main.append("pre").style('margin','3px').html(this.lst.arr[i].fString);
171 GO4.DrawMsgList = function(dom, lst) {
173 let painter = new MsgListPainter(dom, lst);
177 painter.setTopPainter();
178 return Promise.resolve(painter);
181 class AnalysisTerminalPainter extends JSROOT.BasePainter {
182 constructor(frame, hpainter, itemname, url) {
184 this.hpainter = hpainter;
185 this.itemname = itemname;
187 this.draw_ready = true;
188 this.needscroll = false;
192 if (p) this.log_painter = p;
193 if(this.needscroll) {
194 this.clickScroll(true);
195 this.needscroll = false;
197 this.draw_ready = true;
203 if (this.log_painter) {
204 this.log_painter.cleanup();
205 delete this.log_painter;
208 clearInterval(this.interval);
209 delete this.interval;
215 let subid = "anaterm_output_container";
216 // detect if drawing disappear
217 if (d3.select("#" + subid).empty())
218 return this.cleanup();
220 if (!this.draw_ready) return;
222 let msgitem = this.itemname.replace("Control/Terminal", "Status/Log");
224 this.draw_ready = false;
226 if (this.log_painter)
227 this.hpainter.display(msgitem, "update:divid:" + subid).then(() => this.logReady());
229 this.hpainter.display(msgitem, "divid:" + subid).then(p => this.logReady(p));
233 let command = this.itemname.replace("Terminal", "CmdExecute");
234 this.hpainter.executeCommand(command, null, kind). then(() => { this.needscroll = true; });
238 d3.select("#anaterm_output_container").html("");
242 // inner frame created by hpainter has the scrollbars, i.e. first child
243 let nodes = d3.select("#anaterm_output_container").selectAll("pre").nodes();
244 if (nodes) nodes[last ? nodes.length-1 : 0].scrollIntoView();
248 this.setTopPainter();
249 this.interval = setInterval(() => this.processTimer(), 2000);
250 let dom = this.selectDom();
252 dom.select(".go4_clearterm")
253 .on("click", () => this.clickClear())
254 .style('background-image', "url(" + GO4.source_dir + "icons/clear.png)");
256 dom.select(".go4_startterm")
257 .on("click", () => this.clickScroll(false))
258 .style('background-image', "url(" + GO4.source_dir + "icons/shiftup.png)");
260 dom.select(".go4_endterm")
261 .on("click", () => this.clickScroll(true))
262 .style('background-image', "url(" + GO4.source_dir + "icons/shiftdown.png)");
264 dom.select(".go4_printhistos")
265 .on("click", () => this.clickCommand("@PrintHistograms()"))
266 .style('background-image', "url(" + GO4.source_dir + "icons/hislist.png)");
268 dom.select(".go4_printcond")
269 .on("click", () => this.clickCommand("@PrintConditions()"))
270 .style('background-image', "url(" + GO4.source_dir + "icons/condlist.png)");
272 dom.select(".go4_executescript")
273 .style('background-image', "url(" + GO4.source_dir + "icons/macro_t.png)");
275 dom.select(".go4_anaterm_cmd_form").on("submit", event => {
276 event.preventDefault();
277 let command = this.itemname.replace("Terminal", "CmdExecute");
278 let cmdpar = document.getElementById("go4_anaterm_command").value;
279 console.log("submit command - " + cmdpar);
280 this.hpainter.executeCommand(command, null, cmdpar).then(() => { this.needscroll = true; });
286 GO4.drawAnalysisTerminal = function(hpainter, itemname) {
287 let url = hpainter.getOnlineItemUrl(itemname),
288 mdi = hpainter.getDisplay(),
289 frame = mdi ? mdi.findFrame(itemname, true) : null;
291 if (!url || !frame) return null;
293 let elem = d3.select(frame),
294 h = frame.clientHeight,
295 w = frame.clientWidth;
296 if ((h < 10) && (w > 10)) elem.style("height", Math.round(w*0.7)+"px");
298 let player = new AnalysisTerminalPainter(frame, hpainter, itemname, url);
300 return JSROOT.httpRequest(GO4.source_dir + "html/terminal.htm", "text")
303 player.fillDisplay();
308 const op_LineColor = 5,
325 function getOptValue(pic, indx, typ) {
326 if (!pic || !pic.fxOptIndex) return null;
327 let srch = indx + 1 + (typ << 16);
328 for (let k = 0; k < pic.fxOptIndex.length; ++k)
329 if (pic.fxOptIndex[k] == srch)
330 return pic.fxOptValue[k];
334 function getOptDouble(pic, indx, typ) {
335 let ivalue = getOptValue(pic, indx, typ);
336 if (ivalue === null) return null;
337 const buffer = new ArrayBuffer(16),
338 view = new DataView(buffer),
339 big = BigInt(ivalue); // to be sure that BigInt is used
340 view.setUint32(0, Number(big >> 32n));
341 view.setUint32(4, Number(big % (2n ** 32n)));
342 return view.getFloat64(0);
345 function drawSpecialObjects(divid, pic, k) {
346 if (!pic.fxSpecialObjects || (k >= pic.fxSpecialObjects.arr.length))
347 return Promise.resolve(false);
349 return JSROOT.draw(divid, pic.fxSpecialObjects.arr[k], pic.fxSpecialObjects.opt[k]).then(() => drawSpecialObjects(divid, pic, k+1));
352 function drawPictureObjects(divid, pic, k) {
353 if (!divid || !pic.fxNames)
354 return Promise.resolve(false);
356 let arr = pic.fxNames ? pic.fxNames.arr : null;
357 if (!arr || (k >= arr.length))
358 return drawSpecialObjects(divid, pic, 0);
360 let n = pic.fxNames.arr[k], itemname = "", isth2 = false;
362 JSROOT.hpainter.forEachItem(item => {
363 if (item._name == n.fString) {
364 itemname = JSROOT.hpainter.itemFullName(item);
365 if (item._kind && (item._kind.indexOf("ROOT.TH2") == 0)) isth2 = true;
370 console.log('not found object with name', n.fString);
371 return drawPictureObjects(divid, pic, k+1);
374 // console.log('Want to display item', itemname, 'on', divid);
376 let opt = isth2 ? "col" : "",
377 iopt = getOptValue(pic, k, op_Draw);
378 if ((iopt !== null) && pic.fxOptObjects)
379 opt = pic.fxOptObjects.arr[iopt].fString;
380 if (k > 0) opt += " same";
382 return JSROOT.hpainter.display(itemname, opt + "divid:" + divid).then(painter => {
383 if (!painter) return;
384 let need_redraw = false;
386 if (painter.lineatt) {
387 let lcol = getOptValue(pic, k, op_LineColor),
388 lwidth = getOptValue(pic, k, op_LineWidth),
389 lstyle = getOptValue(pic, k, op_LineStyle);
390 if ((lcol !== null) && (lwidth !== null) && (lstyle !== null)) {
391 painter.lineatt.change(painter.getColor(lcol), lwidth, lstyle);
396 if (painter.fillatt) {
397 let fcol = getOptValue(pic, k, op_FillColor),
398 fstyle = getOptValue(pic, k, op_FillStyle);
400 if ((fcol !== null) && (fstyle !== null)) {
401 painter.fillatt.change(fcol, fstyle, painter.getCanvSvg());
406 if (typeof painter.getHisto == 'function' && painter.createHistDrawAttributes && painter.isMainPainter()) {
407 const kNoStats = JSROOT.BIT(9),
408 kNoTitle = JSROOT.BIT(17);
410 let histo = painter.getHisto(),
411 stat = painter.findStat(),
412 sx1 = getOptDouble(pic, PictureIndex, op_HisStatsX1),
413 sy1 = getOptDouble(pic, PictureIndex, op_HisStatsY1),
414 sx2 = getOptDouble(pic, PictureIndex, op_HisStatsX2),
415 sy2 = getOptDouble(pic, PictureIndex, op_HisStatsY2),
416 has_stats_pos = (sx1 !== null) && (sy1 !== null) && (sx2!==null) && (sy2!==null),
417 istitle = getOptValue(pic, PictureIndex, op_HisTitle),
418 isstats = getOptValue(pic, PictureIndex, op_HisStats),
419 statsopt = getOptValue(pic, PictureIndex, op_HisStatsOpt),
420 fitopt = getOptValue(pic, PictureIndex, op_HisStatsFit);
422 if ((istitle !== null) && (!istitle != histo.TestBit(kNoTitle))) {
423 histo.InvertBit(kNoTitle); need_redraw = true;
425 if ((isstats !== null) && (!isstats != histo.TestBit(kNoStats))) {
426 histo.InvertBit(kNoStats); need_redraw = true;
428 if (stat && ((statsopt !== null) || (fitopt !== null) || has_stats_pos)) {
429 if (statsopt !== null) stat.fOptStat = statsopt;
430 if (fitopt !== null) stat.fOptFit = fitopt;
431 let pp = painter.getPadPainter(),
432 statpainter = pp ? pp.findPainterFor(stat) : null;
439 if (statpainter) statpainter.redraw();
443 if (need_redraw) return painter.redraw();
445 }).then(() => drawPictureObjects(divid, pic, k+1));
448 function drawSubPictures(pad_painter, pic, nsub) {
449 let arr = pic && pic.fxSubPictures ? pic.fxSubPictures.arr : null;
450 if (!arr || nsub >= arr.length)
451 return Promise.resolve(pad_painter);
453 let subpic = pic.fxSubPictures.arr[nsub];
455 let subpad_painter = pad_painter.getSubPadPainter(1 + subpic.fiPosY*pic.fiNDivX + subpic.fiPosX);
457 return drawPicture(subpad_painter, subpic).then(() => drawSubPictures(pad_painter, pic, nsub+1));
460 function drawPicture(pad_painter, pic) {
462 return Promise.resolve(false);
464 let need_divide = pic.fiNDivX * pic.fiNDivY > 1;
466 if (need_divide && !pad_painter.divide) {
467 console.log('JSROOT version without TPadPainter.divide');
468 return Promise.resolve(false);
471 let prev_name = pad_painter.selectCurrentPad(pad_painter.this_pad_name);
474 return pad_painter.divide(pic.fiNDivX, pic.fiNDivY).then(() => drawSubPictures(pad_painter, pic, 0)).then(() => {
475 pad_painter.selectCurrentPad(prev_name);
479 let divid = pad_painter.selectDom().attr('id');
481 divid = "go4picture_div_" + GO4.id_counter++;
482 pad_painter.selectDom().attr('id', divid);
483 console.error('Drawing must be done on element with id, force ', divid);
486 return drawPictureObjects(divid, pic, 0).then(() => {
487 pad_painter.selectCurrentPad(prev_name);
492 GO4.drawGo4Picture = function(dom, pic) {
493 if (!JSROOT.hpainter) return null;
495 let painter = new JSROOT.ObjectPainter(dom, pic);
497 return JSROOT.require('gpad').then(() => jsrp.ensureTCanvas(painter, false)).then(() => {
498 let pad_painter = painter.getPadPainter();
500 painter.removeFromPadPrimitives();
502 return drawPicture(pad_painter, pic);
503 }).then(() => painter); // return dummy painter
506 // ==============================================================================
508 let canvsrc = GO4.source_dir + 'html/go4canvas.js;';
510 jsrp.addDrawFunc({ name: "TGo4WinCond", script: canvsrc, func: 'GO4.drawGo4Cond', opt: ";editor" });
511 jsrp.addDrawFunc({ name: "TGo4PolyCond", script: canvsrc, func: 'GO4.drawGo4Cond', opt: ";editor" });
512 jsrp.addDrawFunc({ name: "TGo4ShapedCond", script: canvsrc, func: 'GO4.drawGo4Cond', opt: ";editor" });
513 jsrp.addDrawFunc({ name: "TGo4CondArray", script: canvsrc, func: 'GO4.drawCondArray', opt: ";editor" });
514 jsrp.addDrawFunc({ name: "TGo4Marker", script: canvsrc, func: 'GO4.drawGo4Marker' });
516 jsrp.addDrawFunc({ name: "TGo4AnalysisWebStatus", script: GO4.source_dir + 'html/analysiseditor.js', func: 'GO4.drawGo4AnalysisStatus', opt: "editor" });
518 jsrp.addDrawFunc({ name: "TGo4MsgList", func: GO4.DrawMsgList });
519 jsrp.addDrawFunc({ name: "TGo4Picture", func: GO4.drawGo4Picture, icon: GO4.source_dir + "icons/picture.png" });
521 jsrp.addDrawFunc({ name: "TGo4MbsEvent", noinspect: true });
522 jsrp.addDrawFunc({ name: "TGo4EventElement", noinspect: true });
524 globalThis.GO4 = GO4;