From bf91858cd5838cfb6f1e04c36a58619f840014e8 Mon Sep 17 00:00:00 2001 From: Sergey Linev <S.Linev@gsi.de> Date: Fri, 20 Feb 2015 14:37:18 +0100 Subject: [PATCH] jsroot: implement raw Text output from THttpServer 1. One could create now 'Text' item on the server side, which just disaplyed in the browser. 2. For text display MathJax can be used - one should specify 'mathjax' property for Text item. 3. Provide example with Text in httpcontrol.C macro 4. Support 'autozoom' draw option 5. Support _same_ identifier in items list 6. Remove default-ui class from droppable, hide nasty effects in StreamerInfo and Text display Signed-off-by: Bertrand Bellenot <bertrand.bellenot@cern.ch> --- etc/http/scripts/JSRootCore.js | 65 ++++++++------ etc/http/scripts/JSRootInterface.js | 35 ++++---- etc/http/scripts/JSRootPainter.js | 126 ++++++++++++++++++++++++---- etc/http/style/JSRootPainter.css | 9 +- tutorials/http/httpcontrol.C | 25 +++--- 5 files changed, 189 insertions(+), 71 deletions(-) diff --git a/etc/http/scripts/JSRootCore.js b/etc/http/scripts/JSRootCore.js index f67eeb4e637..d0f01e031ca 100644 --- a/etc/http/scripts/JSRootCore.js +++ b/etc/http/scripts/JSRootCore.js @@ -14,7 +14,7 @@ JSROOT = {}; - JSROOT.version = "3.3 dev 18/02/2015"; + JSROOT.version = "3.3 dev 20/02/2015"; JSROOT.source_dir = ""; @@ -153,6 +153,35 @@ return dflt; } + JSROOT.ParseAsArray = function(val) { + // parse string value as array. + // It could be just simple string: "value" + // or array with or without string quotes: [element], ['eleme1',elem2] + + var res = []; + + if (typeof val != 'string') return res; + + val = val.trim(); + if (val=="") return res; + + // return as array with single element + if ((val.length<2) || (val[0]!='[') || (val[val.length-1]!=']')) { + res.push(val); return res; + } + + // try to parse ourself + var arr = val.substr(1, val.length-2).split(","); // remove brackets + + for (var i in arr) { + var sub = arr[i].trim(); + if ((sub.length>1) && (sub[0]==sub[sub.length-1]) && ((sub[0]=='"') || (sub[0]=="'"))) + sub = sub.substr(1, sub.length-2); + res.push(sub); + } + return res; + } + JSROOT.GetUrlOptionAsArray = function(opt, url) { // special handling of URL options to produce array // if normal option is specified ...?opt=abc, than array with single element will be created @@ -169,24 +198,7 @@ if (separ>0) opt = opt.substr(separ+1); else opt = ""; var val = this.GetUrlOption(part, url, null); - if (val==null) continue; - val = val.trim(); - if (val=="") continue; - - // return as array with single element - if ((val[0]!='[') && (val[val.length-1]!=']')) { - res.push(val); continue; - } - - // try to parse ourself - var arr = val.substr(1, val.length-2).split(","); // remove brackets - - for (var i in arr) { - var sub = arr[i].trim(); - if ((sub.length>1) && (sub[0]==sub[sub.length-1]) && ((sub[0]=='"') || (sub[0]=="'"))) - sub = sub.substr(1, sub.length-2); - res.push(sub); - } + res = res.concat(JSROOT.ParseAsArray(val)); } return res; } @@ -208,10 +220,12 @@ if (func==null) return; - if (typeof func=='function') return func(arg1,arg2); + if (typeof func == 'string') func = JSROOT.findFunction(func); + + if (typeof func == 'function') return func(arg1,arg2); - if (typeof func=='obj' && typeof func.obj == 'object' && - typeof func.fun == 'string' && typeof func.obj[func.func] == 'function') return func.obj[func.func](arg1, arg2); + if (typeof func == 'object' && typeof func.obj == 'object' && + typeof func.func == 'string' && typeof func.obj[func.func] == 'function') return func.obj[func.func](arg1, arg2); } JSROOT.NewHttpRequest = function(url, kind, user_call_back) { @@ -349,9 +363,7 @@ if (debugout) document.getElementById(debugout).innerHTML = ""; - if (typeof callback == 'string') callback = JSROOT.findFunction(callback); - - if (typeof callback == 'function') callback(); + JSROOT.CallBack(callback); } if ((urllist==null) || (urllist.length==0)) @@ -475,6 +487,9 @@ ";$$$scripts/helvetiker_bold.typeface.js" + ";$$$scripts/JSRoot3DPainter.js"; + if (kind.indexOf("mathjax;")>=0) + allfiles += ";https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"; + if (kind.indexOf("simple;")>=0) allfiles += ';$$$scripts/JSRootInterface.js' + ';$$$style/JSRootInterface.css'; diff --git a/etc/http/scripts/JSRootInterface.js b/etc/http/scripts/JSRootInterface.js index 63bfdb8a0ae..f041f5434d8 100644 --- a/etc/http/scripts/JSRootInterface.js +++ b/etc/http/scripts/JSRootInterface.js @@ -31,7 +31,6 @@ function setGuiLayout(value) { } } - function ReadFile() { var navigator_version = navigator.appVersion; if (typeof ActiveXObject == "function") { // Windows @@ -155,8 +154,9 @@ function BuildSimpleGUI() { var monitor = JSROOT.GetUrlOption("monitoring"); - var layout = JSROOT.GetUrlOption("layout"); - if (layout=="") layout = null; + var ilayout = JSROOT.GetUrlOption("layout"); + if (ilayout=="") ilayout = null; + var layout = ilayout; hpainter = new JSROOT.HierarchyPainter('root', nobrowser ? null : 'browser'); @@ -183,14 +183,14 @@ function BuildSimpleGUI() { hpainter.SetDisplay(layout, drawDivId); - hpainter.EnableMonitoring(monitor!=null); + hpainter.SetMonitoring(monitor); var h0 = null; if (online) { if (!nobrowser) $("#monitoring") - .prop('checked', monitor!=null) + .prop('checked', hpainter.IsMonitoring()) .click(function() { hpainter.EnableMonitoring(this.checked); if (this.checked) hpainter.updateAll(); @@ -212,23 +212,24 @@ function BuildSimpleGUI() { function AfterOnlineOpened() { // check if server enables monitoring - if ('_monitoring' in hpainter.h) { - var v = parseInt(hpainter.h._monitoring); - if ((v == NaN) || (hpainter.h._monitoring == 'false')) { - hpainter.EnableMonitoring(false); - } else { - hpainter.EnableMonitoring(true); - hpainter.MonitoringInterval(v); - } + if (('_monitoring' in hpainter.h) && (monitor==null)) { + hpainter.SetMonitoring(hpainter.h._monitoring); if (!nobrowser) $("#monitoring").prop('checked', hpainter.IsMonitoring()); } - if ('_loadfile' in hpainter.h) - filesarr.push(hpainter.h._loadfile); + if (('_layout' in hpainter.h) && (ilayout==null)) { + setGuiLayout(hpainter.h._layout); + hpainter.SetDisplay(hpainter.h._layout, drawDivId); + } + + if ('_loadfile' in hpainter.h) { + filesarr = filesarr.concat(JSROOT.ParseAsArray(hpainter.h._loadfile)); + } if ('_drawitem' in hpainter.h) { - itemsarr.push(hpainter.h._drawitem); - optionsarr.push('_drawopt' in hpainter.h ? hpainter.h._drawopt : ""); + itemsarr = itemsarr.concat(JSROOT.ParseAsArray(hpainter.h._drawitem)); + if ('_drawopt' in hpainter.h) + optionsarr = optionsarr.concat(JSROOT.ParseAsArray(hpainter.h._drawopt)); } OpenAllFiles(); diff --git a/etc/http/scripts/JSRootPainter.js b/etc/http/scripts/JSRootPainter.js index 780251e7527..cb60093e0b4 100644 --- a/etc/http/scripts/JSRootPainter.js +++ b/etc/http/scripts/JSRootPainter.js @@ -3074,7 +3074,7 @@ Lego: 0, Surf: 0, Off: 0, Tri: 0, Proj: 0, AxisPos: 0, Spec: 0, Pie: 0, List: 0, Zscale: 0, FrontBox: 1, BackBox: 1, System: JSROOT.Painter.Coord.kCARTESIAN, - AutoColor : 0, NoStat : 0, + AutoColor : 0, NoStat : 0, AutoZoom : false, HighRes: 0, Zero: 0, Logx: 0, Logy: 0, Logz: 0, Gridx: 0, Gridy: 0 }; // check for graphical cuts @@ -3089,6 +3089,11 @@ option.Hist = 1; chopt = chopt.replace('AUTOCOL', ''); } + if (chopt.indexOf('AUTOZOOM') != -1) { + option.AutoZoom = 1; + option.Hist = 1; + chopt = chopt.replace('AUTOZOOM', ''); + } if (chopt.indexOf('NOSTAT') != -1) { option.NoStat = 1; chopt = chopt.replace('NOSTAT', ''); @@ -5272,6 +5277,8 @@ painter.AddInteractive(); + if (painter.options.AutoZoom) painter.AutoZoom(); + return painter; } @@ -5957,6 +5964,8 @@ this.AddInteractive(); + if (this.options.AutoZoom) this.AutoZoom(); + this['done2d'] = true; // indicate that 2d drawing was once done } @@ -6723,8 +6732,64 @@ return painter; } - // =========================================================== + // ================= painer of raw text ======================================== + + JSROOT.RawTextPainter = function(txt) { + JSROOT.TBasePainter.call(this); + this.txt = txt; + return this; + } + + JSROOT.RawTextPainter.prototype = Object.create( JSROOT.TBasePainter.prototype ); + + JSROOT.RawTextPainter.prototype.RedrawObject = function(obj) { + this.txt = obj; + this.Draw(); + return true; + } + + JSROOT.RawTextPainter.prototype.Draw = function() { + var frame = $("#" + this.divid); + + var txt = this.txt.value; + if (txt==null) txt = "<undefined>"; + var mathjax = 'mathjax' in this.txt; + + if (!mathjax && !('as_is' in this.txt)) { + var arr = []; + while (txt.length > 0) { + var pos = txt.indexOf("\\n"); + if (pos<0) break; + arr.push(txt.substr(0,pos)); + txt = txt.substr(pos+2); + } + arr.push(txt); txt = ""; + for (var i in arr) + txt += "<pre>" + arr[i] + "</pre>"; + } + + frame.html("<div style='overflow:hidden'>" + txt + "</div>"); + + // (re) set painter to first child element + this.SetDivId(this.divid); + + if (mathjax) + JSROOT.AssertPrerequisites('mathjax', function() { + if (typeof MathJax == 'object') { + MathJax.Hub.Queue(["Typeset", MathJax.Hub, frame.get()]); + } + }); + } + + JSROOT.Painter.drawRawText = function(divid, txt, opt) { + var painter = new JSROOT.RawTextPainter(txt); + painter.SetDivId(divid); + painter.Draw(); + return painter; + } + + // ========== performs tree drawing on server ================== JSROOT.TTreePlayer = function(itemname) { JSROOT.TBasePainter.call(this); @@ -7184,6 +7249,10 @@ cando.ctxt = true; cando.execute = true; cando.img1 = "img_execute"; + } else if (kind=="Text") { + cando.ctxt = true; + cando.display = true; + cando.img1 = "img_text"; } else if (kind.match(/^ROOT.TH1/)) { cando.img1 = "img_histo1d"; cando.scan = false; @@ -7230,6 +7299,11 @@ cando.img1 = "img_histo1d"; cando.scan = false; cando.display = true; + } else + if (JSROOT.canDraw('kind:' + kind)) { + cando.img1 = "img_leaf"; + cando.scan = false; + cando.display = true; } if ((cando.img1.length==0) && ('_online' in node)) cando.img1 = "img_globe"; @@ -7703,7 +7777,7 @@ if (dropname==null) return false; return h.dropitem(dropname, frame.attr("id")); } - }); + }).removeClass('ui-state-default'); } } @@ -7788,6 +7862,11 @@ dropitems[i][j] = dropitems[i][j].substr(0,pos) + items[i].substr(pos); } } + + // also check if subsequent items has _same_, than use name from first item + var pos = items[i].indexOf("_same_"); + if ((pos>0) && !this.Find(items[i]) && (i>0)) + items[i] = items[i].substr(0,pos) + items[0].substr(pos); } // Than create empty frames for each item @@ -7942,17 +8021,22 @@ JSROOT.HierarchyPainter.prototype.GetOnlineItem = function(item, itemname, callback) { // method used to request object from the http server - var url = itemname, h_get = false; + var url = itemname, h_get = false, req = 'root.json.gz?compact=3'; if (item != null) { var top = item; while ((top!=null) && (!('_online' in top))) top = top._parent; url = this.itemFullName(item, top); - h_get = ('_doing_expand' in item); + if ('_doing_expand' in item) { + h_get = true; + req = 'h.json?compact=3'; + } else + if (item._kind.indexOf("ROOT.")!=0) + req = 'get.json?compact=3'; } if (url.length > 0) url += "/"; - url += h_get ? 'h.json?compact=3' : 'root.json.gz?compact=3'; + url += req; var itemreq = JSROOT.NewHttpRequest(url, 'object', function(obj) { @@ -8082,18 +8166,21 @@ this.RefreshHtml(); } + JSROOT.HierarchyPainter.prototype.SetMonitoring = function(val) { + this['_monitoring_on'] = false; + this['_monitoring_interval'] = 3000; + + if ((val!=null) && (val!='0')) { + this['_monitoring_on'] = true; + this['_monitoring_interval'] = parseInt(val); + if ((this['_monitoring_interval'] == NaN) || (this['_monitoring_interval']<100)) + this['_monitoring_interval'] = 3000; + } + } + JSROOT.HierarchyPainter.prototype.MonitoringInterval = function(val) { // returns interval - if (val!=null) this['_monitoring_interval'] = val; - var monitor = this['_monitoring_interval']; - if (monitor == null) { - monitor = JSROOT.GetUrlOption("monitoring"); - if ((monitor == "") || (monitor==null)) monitor = 3000; - else monitor = parseInt(monitor); - if ((monitor == NaN) || (monitor<=0)) monitor = 3000; - this['_monitoring_interval'] = monitor; - } - return monitor; + return ('_monitoring_interval' in this) ? this['_monitoring_interval'] : 3000; } JSROOT.HierarchyPainter.prototype.EnableMonitoring = function(on) { @@ -8729,6 +8816,7 @@ JSROOT.addDrawFunc(/^RooCurve/, JSROOT.Painter.drawGraph,";L;P"); JSROOT.addDrawFunc("TMultiGraph", JSROOT.Painter.drawMultiGraph); JSROOT.addDrawFunc("TStreamerInfoList", JSROOT.Painter.drawStreamerInfo); + JSROOT.addDrawFunc("kind:Text", JSROOT.Painter.drawRawText); JSROOT.getDrawFunc = function(classname, drawopt) { if (typeof classname != 'string') return null; @@ -8796,9 +8884,11 @@ * Draw object in specified HTML element with given draw options */ JSROOT.draw = function(divid, obj, opt) { - if ((typeof obj != 'object') || (!('_typename' in obj))) return null; + if (typeof obj != 'object') return null; - var draw_func = JSROOT.getDrawFunc(obj['_typename'], opt); + var draw_func = null; + if ('_typename' in obj) draw_func = JSROOT.getDrawFunc(obj['_typename'], opt); + else if ('_kind' in obj) draw_func = JSROOT.getDrawFunc('kind:' + obj['_kind'], opt); if (draw_func==null) return null; diff --git a/etc/http/style/JSRootPainter.css b/etc/http/style/JSRootPainter.css index 8523ecbb44d..ce51d84dddc 100644 --- a/etc/http/style/JSRootPainter.css +++ b/etc/http/style/JSRootPainter.css @@ -1,5 +1,5 @@ /* Interface stylesheet for Javascript ROOT Web Page. */ - + /*--------------------------------------------------| | dTree 2.05 | www.destroydrop.com/javascript/tree/ | |---------------------------------------------------| @@ -223,6 +223,13 @@ background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABmJLR0T///////8JWPfcAAAACXBIWXMAAABIAAAASABGyWs+AAAACXZwQWcAAAAQAAAAEABcxq3DAAAA2klEQVRIx61VURbDIAgTX+9ljg4n2z5sNouj1ml+LE9rQkSU5PA6kTZBToTznj5aqKqq+py4lFJKScnMzCwlAAB6IbnNuyXycd1g3oHrf32CmR9mZqpVOdDHs2DmI+c+AiJixu1RAN9xFUcdWCjVIr8xCX8Jubc8Ao9CJF8nRFgNJBxZSCEkjmrIxxSS0yIAoBU4OkpfU8sCPEbEvqaOXcR31zWORbYJ8EI8rsK+DWm7gMVb8F/GK7eg6818jNjJZjMn0agY7x6oxqL5sWbIbhLHoQN78PQ5F3kDgX8u9tphBfoAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTUtMDItMDZUMTA6Mjc6MzErMDE6MDChLu/mAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE0LTExLTEyVDA4OjM5OjIwKzAxOjAwIGvf8wAAAABJRU5ErkJggg=='); } +.img_text { + display: inline-block; + height: 18px; + width: 18px; + background-image: url('data:image/gif;base64,R0lGODlhEgASALMAAP/////MzP+Zmf9mZv8zM/8AAMzM/8zMzJmZ/5mZmWZm/2ZmZjMz/zMzMwAA/////yH5BAUUAA8ALAAAAAASABIAAARo8MlJq73SKGSwdSDjUQoIjhNYOujDnGAnFXRBZKoBIpMw1ICHaaigBAq/AUK1CVEIhcfPNFlRbAEBEvWr0VDYQLYgkCQWh8XiAfgRymPyoTFRa2uPO009maP8ZmsjAHxnBygLDQ1zihEAOw=='); +} + /*--------------------------------------------------| | tooltip style | diff --git a/tutorials/http/httpcontrol.C b/tutorials/http/httpcontrol.C index 4513b0d3d43..36943105f81 100644 --- a/tutorials/http/httpcontrol.C +++ b/tutorials/http/httpcontrol.C @@ -16,7 +16,7 @@ void httpcontrol() // After macro started, open in browser with url // http://localhost:8080 // -// Histogram hpxpy will be automatically displayed and +// Histograms will be automatically displayed and // monitoring with interval 2000 ms started // create histograms @@ -38,9 +38,10 @@ void httpcontrol() serv->Register("/", hpxpy); // enable monitoring and - // specify item to draw when page is opened + // specify items to draw when page is opened serv->SetItemField("/","_monitoring","2000"); - serv->SetItemField("/","_drawitem","hpxpy"); + serv->SetItemField("/","_layout","grid2x2"); + serv->SetItemField("/","_drawitem","[hpxpy,hpx,Debug]"); serv->SetItemField("/","_drawopt","col"); // register simple start/stop commands @@ -52,14 +53,15 @@ void httpcontrol() serv->Hide("/Stop"); // register commands, invoking object methods - // one could set command properties directly - serv->RegisterCommand("/ResetHPX","/hpx/->Reset()"); - serv->SetIcon("/ResetHPX", "/rootsys/icons/ed_delete.png"); - serv->SetItemField("/ResetHPX","_fastcmd", "true"); + serv->RegisterCommand("/ResetHPX","/hpx/->Reset()", "button;/rootsys/icons/ed_delete.png"); + serv->RegisterCommand("/ResetHPXPY","/hpxpy/->Reset()", "button;/rootsys/icons/bld_delete.png"); - serv->RegisterCommand("/ResetHPXPY","/hpxpy/->Reset()"); - serv->SetIcon("/ResetHPXPY", "/rootsys/icons/bld_delete.png"); - serv->SetItemField("/ResetHPXPY", "_fastcmd", "true"); + + // create debug text element, use MathJax - works only on Firefox + serv->CreateItem("/Debug","debug output"); + serv->SetItemField("/Debug", "_kind", "Text"); + serv->SetItemField("/Debug", "value","\\(\\displaystyle{x+1\\over y-1}\\)"); + serv->SetItemField("/Debug", "mathjax", "true"); // Fill histograms randomly TRandom3 random; @@ -79,6 +81,9 @@ void httpcontrol() if ((cnt % kUPDATE==0) || !bFillHist) { // IMPORTANT: one should regularly call ProcessEvents // to let http server process requests + + serv->SetItemField("/Debug", "value", Form("\\(\\displaystyle{x+1\\over y-1}\\) Loop:%d", cnt/kUPDATE)); + if (gSystem->ProcessEvents()) break; } } -- GitLab