otsdaq_utilities  v2_05_02_indev
JSRootPainter.hierarchy.js
1 
4 (function( factory ) {
5  if ( typeof define === "function" && define.amd ) {
6  define( ['JSRootCore', 'd3', 'JSRootPainter'], factory );
7  } else if (typeof exports === 'object' && typeof module !== 'undefined') {
8  factory(require("./JSRootCore.js"), require("d3"), require("./JSRootPainter.js"));
9  } else {
10  if (typeof d3 != 'object')
11  throw new Error('This extension requires d3.js', 'JSRootPainter.hierarchy.js');
12  if (typeof JSROOT == 'undefined')
13  throw new Error('JSROOT is not defined', 'JSRootPainter.hierarchy.js');
14  if (typeof JSROOT.Painter != 'object')
15  throw new Error('JSROOT.Painter not defined', 'JSRootPainter.hierarchy.js');
16  factory(JSROOT, d3);
17  }
18 } (function(JSROOT, d3) {
19 
20  "use strict";
21 
22  JSROOT.sources.push("hierarchy");
23 
24  // ===========================================================================================
25 
27  function drawList(divid, lst, opt, callback) {
28  if (!lst || !lst.arr) return JSROOT.CallBack(callback);
29 
30  var obj = {
31  divid: divid,
32  lst: lst,
33  opt: opt,
34  indx: -1,
35  callback: callback,
36  draw_next: function() {
37  while (++this.indx < this.lst.arr.length) {
38  var handle = { func: this.draw_bind },
39  item = this.lst.arr[this.indx],
40  opt = (this.lst.opt && this.lst.opt[this.indx]) ? this.lst.opt[this.indx] : this.opt;
41  if (!item) continue;
42  JSROOT.draw(this.divid, item, opt, handle);
43  if (!handle.completed) return;
44  }
45 
46  return JSROOT.CallBack(this.callback);
47  }
48  }
49 
50  obj.draw_bind = obj.draw_next.bind(obj);
51 
52  obj.draw_next();
53  }
54 
55  // ===================== hierarchy scanning functions ==================================
56 
57  function FolderHierarchy(item, obj) {
58 
59  if (!obj || !('fFolders' in obj) || (obj.fFolders===null)) return false;
60 
61  if (obj.fFolders.arr.length===0) { item._more = false; return true; }
62 
63  item._childs = [];
64 
65  for ( var i = 0; i < obj.fFolders.arr.length; ++i) {
66  var chld = obj.fFolders.arr[i];
67  item._childs.push( {
68  _name : chld.fName,
69  _kind : "ROOT." + chld._typename,
70  _obj : chld
71  });
72  }
73  return true;
74  }
75 
76  function TaskHierarchy(item, obj) {
77  // function can be used for different derived classes
78  // we show not only child tasks, but all complex data members
79 
80  if (!obj || !('fTasks' in obj) || (obj.fTasks === null)) return false;
81 
82  ObjectHierarchy(item, obj, { exclude: ['fTasks', 'fName'] } );
83 
84  if ((obj.fTasks.arr.length===0) && (item._childs.length==0)) { item._more = false; return true; }
85 
86  // item._childs = [];
87 
88  for ( var i = 0; i < obj.fTasks.arr.length; ++i) {
89  var chld = obj.fTasks.arr[i];
90  item._childs.push( {
91  _name : chld.fName,
92  _kind : "ROOT." + chld._typename,
93  _obj : chld
94  });
95  }
96  return true;
97  }
98 
99  function ListHierarchy(folder, lst) {
100  if (!JSROOT.IsRootCollection(lst)) return false;
101 
102  if ((lst.arr === undefined) || (lst.arr.length === 0)) {
103  folder._more = false;
104  return true;
105  }
106 
107  var do_context = false, prnt = folder;
108  while (prnt) {
109  if (prnt._do_context) do_context = true;
110  prnt = prnt._parent;
111  }
112 
113  // if list has objects with similar names, create cycle number for them
114  var ismap = (lst._typename == 'TMap'), names = [], cnt = [], cycle = [];
115 
116  for (var i = 0; i < lst.arr.length; ++i) {
117  var obj = ismap ? lst.arr[i].first : lst.arr[i];
118  if (!obj) continue; // for such objects index will be used as name
119  var objname = obj.fName || obj.name;
120  if (!objname) continue;
121  var indx = names.indexOf(objname);
122  if (indx>=0) {
123  cnt[indx]++;
124  } else {
125  cnt[names.length] = cycle[names.length] = 1;
126  names.push(objname);
127  }
128  }
129 
130  folder._childs = [];
131  for ( var i = 0; i < lst.arr.length; ++i) {
132  var obj = ismap ? lst.arr[i].first : lst.arr[i];
133 
134  var item;
135 
136  if (!obj || !obj._typename) {
137  item = {
138  _name: i.toString(),
139  _kind: "ROOT.NULL",
140  _title: "NULL",
141  _value: "null",
142  _obj: null
143  }
144  } else {
145  item = {
146  _name: obj.fName || obj.name,
147  _kind: "ROOT." + obj._typename,
148  _title: (obj.fTitle || "") + " type:" + obj._typename,
149  _obj: obj
150  };
151 
152  switch(obj._typename) {
153  case 'TColor': item._value = JSROOT.Painter.MakeColorRGB(obj); break;
154  case 'TText': item._value = obj.fTitle; break;
155  case 'TLatex': item._value = obj.fTitle; break;
156  case 'TObjString': item._value = obj.fString; break;
157  default: if (lst.opt && lst.opt[i] && lst.opt[i].length) item._value = lst.opt[i];
158  }
159 
160  if (do_context && JSROOT.canDraw(obj._typename)) item._direct_context = true;
161 
162  // if name is integer value, it should match array index
163  if (!item._name || (!isNaN(parseInt(item._name)) && (parseInt(item._name)!==i))
164  || (lst.arr.indexOf(obj)<i)) {
165  item._name = i.toString();
166  } else {
167  // if there are several such names, add cycle number to the item name
168  var indx = names.indexOf(obj.fName);
169  if ((indx>=0) && (cnt[indx]>1)) {
170  item._cycle = cycle[indx]++;
171  item._keyname = item._name;
172  item._name = item._keyname + ";" + item._cycle;
173  }
174  }
175  }
176 
177  folder._childs.push(item);
178  }
179  return true;
180  }
181 
182  function KeysHierarchy(folder, keys, file, dirname) {
183 
184  if (keys === undefined) return false;
185 
186  folder._childs = [];
187 
188  for (var i = 0; i < keys.length; ++i) {
189  var key = keys[i];
190 
191  var item = {
192  _name : key.fName + ";" + key.fCycle,
193  _cycle : key.fCycle,
194  _kind : "ROOT." + key.fClassName,
195  _title : key.fTitle,
196  _keyname : key.fName,
197  _readobj : null,
198  _parent : folder
199  };
200 
201  if (key.fObjlen > 1e5) item._title += ' (size: ' + (key.fObjlen/1e6).toFixed(1) + 'MB)';
202 
203  if ('fRealName' in key)
204  item._realname = key.fRealName + ";" + key.fCycle;
205 
206  if (key.fClassName == 'TDirectory' || key.fClassName == 'TDirectoryFile') {
207  var dir = null;
208  if ((dirname!=null) && (file!=null)) dir = file.GetDir(dirname + key.fName);
209  if (dir == null) {
210  item._more = true;
211  item._expand = function(node, obj) {
212  // one can get expand call from child objects - ignore them
213  return KeysHierarchy(node, obj.fKeys);
214  }
215  } else {
216  // remove cycle number - we have already directory
217  item._name = key.fName;
218  KeysHierarchy(item, dir.fKeys, file, dirname + key.fName + "/");
219  }
220  } else
221  if ((key.fClassName == 'TList') && (key.fName == 'StreamerInfo')) {
222  item._name = 'StreamerInfo';
223  item._kind = "ROOT.TStreamerInfoList";
224  item._title = "List of streamer infos for binary I/O";
225  item._readobj = file.fStreamerInfos;
226  }
227 
228  folder._childs.push(item);
229  }
230 
231  return true;
232  }
233 
234  function ObjectHierarchy(top, obj, args) {
235  if (!top || (obj===null)) return false;
236 
237  top._childs = [];
238 
239  var proto = Object.prototype.toString.apply(obj);
240 
241  if (proto === '[object DataView]') {
242 
243  var item = {
244  _parent: top,
245  _name: 'size',
246  _value: obj.byteLength.toString(),
247  _vclass: 'h_value_num'
248  };
249 
250  top._childs.push(item);
251  var namelen = (obj.byteLength < 10) ? 1 : JSROOT.log10(obj.byteLength);
252 
253  for (var k=0;k<obj.byteLength;++k) {
254  if (k % 16 === 0) {
255  item = {
256  _parent: top,
257  _name: k.toString(),
258  _value: "",
259  _vclass: 'h_value_num'
260  };
261  while (item._name.length < namelen) item._name = "0" + item._name;
262  top._childs.push(item);
263  }
264 
265  var val = obj.getUint8(k).toString(16);
266  while (val.length<2) val = "0"+val;
267  if (item._value.length>0)
268  item._value += (k%4===0) ? " | " : " ";
269 
270  item._value += val;
271  }
272  return true;
273  }
274 
275  // check nosimple property in all parents
276  var nosimple = true, do_context = false, prnt = top;
277  while (prnt) {
278  if (prnt._do_context) do_context = true;
279  if ('_nosimple' in prnt) { nosimple = prnt._nosimple; break; }
280  prnt = prnt._parent;
281  }
282 
283  var isarray = (proto.lastIndexOf('Array]') == proto.length-6) && (proto.indexOf('[object')==0) && !isNaN(obj.length),
284  compress = isarray && (obj.length > JSROOT.gStyle.HierarchyLimit), arrcompress = false;
285 
286  if (isarray && (top._name==="Object") && !top._parent) top._name = "Array";
287 
288  if (compress) {
289  arrcompress = true;
290  for (var k=0;k<obj.length;++k) {
291  var typ = typeof obj[k];
292  if ((typ === 'number') || (typ === 'boolean') || (typ=='string' && (obj[k].length<16))) continue;
293  arrcompress = false; break;
294  }
295  }
296 
297  if (!('_obj' in top))
298  top._obj = obj;
299  else
300  if (top._obj !== obj) alert('object missmatch');
301 
302  if (!top._title) {
303  if (obj._typename)
304  top._title = "ROOT." + obj._typename;
305  else
306  if (isarray) top._title = "Array len: " + obj.length;
307  }
308 
309  if (arrcompress) {
310  for (var k=0;k<obj.length;) {
311 
312  var nextk = Math.min(k+10,obj.length), allsame = true, prevk = k;
313 
314  while (allsame) {
315  allsame = true;
316  for (var d=prevk;d<nextk;++d)
317  if (obj[k]!==obj[d]) allsame = false;
318 
319  if (allsame) {
320  if (nextk===obj.length) break;
321  prevk = nextk;
322  nextk = Math.min(nextk+10,obj.length);
323  } else
324  if (prevk !== k) {
325  // last block with similar
326  nextk = prevk;
327  allsame = true;
328  break;
329  }
330  }
331 
332  var item = { _parent: top, _name: k+".."+(nextk-1), _vclass: 'h_value_num' };
333 
334  if (allsame) {
335  item._value = obj[k].toString();
336  } else {
337  item._value = "";
338  for (var d=k;d<nextk;++d)
339  item._value += ((d===k) ? "[ " : ", ") + obj[d].toString();
340  item._value += " ]";
341  }
342 
343  top._childs.push(item);
344 
345  k = nextk;
346  }
347  return true;
348  }
349 
350  var lastitem, lastkey, lastfield, cnt;
351 
352  for (var key in obj) {
353  if ((key == '_typename') || (key[0]=='$')) continue;
354  var fld = obj[key];
355  if (typeof fld == 'function') continue;
356  if (args && args.exclude && (args.exclude.indexOf(key)>=0)) continue;
357 
358  if (compress && lastitem) {
359  if (lastfield===fld) { ++cnt; lastkey = key; continue; }
360  if (cnt>0) lastitem._name += ".." + lastkey;
361  }
362 
363  var item = { _parent: top, _name: key };
364 
365  if (compress) { lastitem = item; lastkey = key; lastfield = fld; cnt = 0; }
366 
367  if (fld === null) {
368  item._value = item._title = "null";
369  if (!nosimple) top._childs.push(item);
370  continue;
371  }
372 
373  var simple = false;
374 
375  if (typeof fld == 'object') {
376 
377  proto = Object.prototype.toString.apply(fld);
378 
379  if ((proto.lastIndexOf('Array]') == proto.length-6) && (proto.indexOf('[object')==0)) {
380  item._title = "array len=" + fld.length;
381  simple = (proto != '[object Array]');
382  if (fld.length === 0) {
383  item._value = "[ ]";
384  item._more = false; // hpainter will not try to expand again
385  } else {
386  item._value = "[...]";
387  item._more = true;
388  item._expand = ObjectHierarchy;
389  item._obj = fld;
390  }
391  } else
392  if (proto === "[object DataView]") {
393  item._title = 'DataView len=' + fld.byteLength;
394  item._value = "[...]";
395  item._more = true;
396  item._expand = ObjectHierarchy;
397  item._obj = fld;
398  } else
399  if (proto === "[object Date]") {
400  item._more = false;
401  item._title = 'Date';
402  item._value = fld.toString();
403  item._vclass = 'h_value_num';
404  } else {
405 
406  if (fld.$kind || fld._typename)
407  item._kind = item._title = "ROOT." + (fld.$kind || fld._typename);
408 
409  if (fld._typename) {
410  item._title = fld._typename;
411  if (do_context && JSROOT.canDraw(fld._typename)) item._direct_context = true;
412  }
413 
414  // check if object already shown in hierarchy (circular dependency)
415  var curr = top, inparent = false;
416  while (curr && !inparent) {
417  inparent = (curr._obj === fld);
418  curr = curr._parent;
419  }
420 
421  if (inparent) {
422  item._value = "{ prnt }";
423  item._vclass = 'h_value_num';
424  item._more = false;
425  simple = true;
426  } else {
427  item._obj = fld;
428  item._more = false;
429 
430  switch(fld._typename) {
431  case 'TColor': item._value = JSROOT.Painter.MakeColorRGB(fld); break;
432  case 'TText': item._value = fld.fTitle; break;
433  case 'TLatex': item._value = fld.fTitle; break;
434  case 'TObjString': item._value = fld.fString; break;
435  default:
436  if (JSROOT.IsRootCollection(fld) && (typeof fld.arr === "object")) {
437  item._value = fld.arr.length ? "[...]" : "[]";
438  item._title += ", size:" + fld.arr.length;
439  if (fld.arr.length>0) item._more = true;
440  } else {
441  item._more = true;
442  item._value = "{ }";
443  }
444  }
445  }
446  }
447  } else
448  if ((typeof fld === 'number') || (typeof fld === 'boolean')) {
449  simple = true;
450  if (key == 'fBits')
451  item._value = "0x" + fld.toString(16);
452  else
453  item._value = fld.toString();
454  item._vclass = 'h_value_num';
455  } else
456  if (typeof fld === 'string') {
457  simple = true;
458  item._value = '&quot;' + fld.replace(/\&/g, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;') + '&quot;';
459  item._vclass = 'h_value_str';
460  } else
461  if (typeof fld === 'undefined') {
462  simple = true;
463  item._value = "undefined";
464  item._vclass = 'h_value_num';
465  } else {
466  simple = true;
467  alert('miss ' + key + ' ' + typeof fld);
468  }
469 
470  if (!simple || !nosimple)
471  top._childs.push(item);
472  }
473 
474  if (compress && lastitem && (cnt>0)) lastitem._name += ".." + lastkey;
475 
476  return true;
477  }
478 
479  // =================================================================================================
480 
483  function BrowserLayout(id, hpainter, objpainter) {
484  this.gui_div = id;
485  this.hpainter = hpainter; // painter for brwoser area (if any)
486  this.objpainter = objpainter; // painter for object area (if any)
487  this.browser_kind = null; // should be 'float' or 'fix'
488  }
489 
490  BrowserLayout.prototype.main = function() {
491  return d3.select("#" + this.gui_div);
492  }
493 
494  BrowserLayout.prototype.drawing_divid = function() {
495  return this.gui_div + "_drawing";
496  }
497 
498  BrowserLayout.prototype.CheckResize = function() {
499  if (this.hpainter && (typeof this.hpainter.CheckResize == 'function'))
500  this.hpainter.CheckResize();
501  else if (this.objpainter && (typeof this.objpainter.CheckResize == 'function')) {
502  this.objpainter.CheckResize(true);
503  }
504  }
505 
508  BrowserLayout.prototype.Create = function(with_browser) {
509  var main = this.main();
510 
511  main.append("div").attr("id", this.drawing_divid())
512  .classed("jsroot_draw_area", true)
513  .style('position',"absolute").style('left',0).style('top',0).style('bottom',0).style('right',0);
514 
515  if (with_browser) main.append("div").classed("jsroot_browser", true);
516  }
517 
518  BrowserLayout.prototype.CreateBrowserBtns = function() {
519  var br = this.main().select(".jsroot_browser");
520  if (br.empty()) return;
521  var btns = br.append("div").classed("jsroot_browser_btns", true).classed("jsroot", true);
522  btns.style('position',"absolute").style("left","7px").style("top","7px");
523  if (JSROOT.touches) btns.style('opacity','0.2'); // on touch devices should be always visible
524  return btns;
525  }
526 
527  BrowserLayout.prototype.RemoveBrowserBtns = function() {
528  this.main().select(".jsroot_browser").select(".jsroot_browser_btns").remove();
529  }
530 
531  BrowserLayout.prototype.SetBrowserContent = function(guiCode) {
532  var main = d3.select("#" + this.gui_div + " .jsroot_browser");
533  if (main.empty()) return;
534 
535  main.insert('div', ".jsroot_browser_btns").classed('jsroot_browser_area', true)
536  .style('position',"absolute").style('left',0).style('top',0).style('bottom',0).style('width','250px')
537  .style('padding-left','5px')
538  .style('display','flex').style('flex-direction', 'column') /* use the flex model */
539  .html("<p class='jsroot_browser_title'>title</p>" + guiCode);
540  }
541 
542  BrowserLayout.prototype.HasContent = function() {
543  var main = d3.select("#" + this.gui_div + " .jsroot_browser");
544  if (main.empty()) return false;
545  return !main.select(".jsroot_browser_area").empty();
546  }
547 
548  BrowserLayout.prototype.DeleteContent = function() {
549  var main = d3.select("#" + this.gui_div + " .jsroot_browser");
550  if (main.empty()) return;
551 
552  main.selectAll("*").remove();
553  delete this.browser_visible;
554  }
555 
556  BrowserLayout.prototype.HasStatus = function() {
557  var main = d3.select("#"+this.gui_div+" .jsroot_browser");
558  if (main.empty()) return false;
559 
560  var id = this.gui_div + "_status",
561  line = d3.select("#"+id);
562 
563  return !line.empty();
564  }
565 
566  BrowserLayout.prototype.CreateStatusLine = function(height, mode) {
567  if (!this.gui_div) return '';
568  var pthis = this;
569  JSROOT.AssertPrerequisites('jq2d', function() {
570  pthis.CreateStatusLine(height, mode);
571  });
572  return this.gui_div + "_status";
573  }
574 
575  // =========== painter of hierarchical structures =================================
576 
577  JSROOT.hpainter = null; // global pointer
578 
579  // HierarchyPainter
580 
581  function HierarchyPainter(name, frameid, backgr) {
582  JSROOT.TBasePainter.call(this);
583  this.name = name;
584  this.h = null; // hierarchy
585  this.with_icons = true;
586  this.background = backgr;
587  this.files_monitoring = (frameid == null); // by default files monitored when nobrowser option specified
588  this.nobrowser = (frameid === null);
589  if (!this.nobrowser) this.SetDivId(frameid); // this is required to be able cleanup painter
590 
591  // remember only very first instance
592  if (!JSROOT.hpainter)
593  JSROOT.hpainter = this;
594  }
595 
596  HierarchyPainter.prototype = Object.create(JSROOT.TBasePainter.prototype);
597 
598  HierarchyPainter.prototype.Cleanup = function() {
599  // clear drawing and browser
600  this.clear(true);
601 
602  JSROOT.TBasePainter.prototype.Cleanup.call(this);
603 
604  if (JSROOT.hpainter === this)
605  JSROOT.hpainter = null;
606  }
607 
608  HierarchyPainter.prototype.FileHierarchy = function(file) {
609  var painter = this;
610 
611  var folder = {
612  _name : file.fFileName,
613  _title : (file.fTitle ? (file.fTitle + ", path ") : "") + file.fFullURL,
614  _kind : "ROOT.TFile",
615  _file : file,
616  _fullurl : file.fFullURL,
617  _localfile : file.fLocalFile,
618  _had_direct_read : false,
619  // this is central get method, item or itemname can be used
620  _get : function(item, itemname, callback) {
621 
622  var fff = this; // file item
623 
624  if (item && item._readobj)
625  return JSROOT.CallBack(callback, item, item._readobj);
626 
627  if (item!=null) itemname = painter.itemFullName(item, fff);
628 
629  function ReadFileObject(file) {
630  if (!fff._file) fff._file = file;
631 
632  if (file == null) return JSROOT.CallBack(callback, item, null);
633 
634  file.ReadObject(itemname, function(obj) {
635 
636  // if object was read even when item did not exist try to reconstruct new hierarchy
637  if (!item && obj) {
638  // first try to found last read directory
639  var d = painter.Find({name:itemname, top:fff, last_exists:true, check_keys:true });
640  if ((d!=null) && ('last' in d) && (d.last!=fff)) {
641  // reconstruct only subdir hierarchy
642  var dir = file.GetDir(painter.itemFullName(d.last, fff));
643  if (dir) {
644  d.last._name = d.last._keyname;
645  var dirname = painter.itemFullName(d.last, fff);
646  KeysHierarchy(d.last, dir.fKeys, file, dirname + "/");
647  }
648  } else {
649  // reconstruct full file hierarchy
650  KeysHierarchy(fff, file.fKeys, file, "");
651  }
652  item = painter.Find({name:itemname, top: fff});
653  }
654 
655  if (item) {
656  item._readobj = obj;
657  // remove cycle number for objects supporting expand
658  if ('_expand' in item) item._name = item._keyname;
659  }
660 
661  JSROOT.CallBack(callback, item, obj);
662  });
663  }
664 
665  if (fff._file) ReadFileObject(fff._file); else
666  if (fff._localfile) new JSROOT.TLocalFile(fff._localfile, ReadFileObject); else
667  if (fff._fullurl) new JSROOT.TFile(fff._fullurl, ReadFileObject);
668  }
669  };
670 
671  KeysHierarchy(folder, file.fKeys, file, "");
672 
673  return folder;
674  }
675 
682  HierarchyPainter.prototype.ForEach = function(callback, top) {
683 
684  if (!top) top = this.h;
685  if (!top || (typeof callback != 'function')) return;
686  function each_item(item) {
687  callback(item);
688  if ('_childs' in item)
689  for (var n = 0; n < item._childs.length; ++n) {
690  item._childs[n]._parent = item;
691  each_item(item._childs[n]);
692  }
693  }
694 
695  each_item(top);
696  }
697 
708  HierarchyPainter.prototype.Find = function(arg) {
709 
710  function find_in_hierarchy(top, fullname) {
711 
712  if (!fullname || (fullname.length == 0) || !top) return top;
713 
714  var pos = fullname.length;
715 
716  if (!top._parent && (top._kind !== 'TopFolder') && (fullname.indexOf(top._name)===0)) {
717  // it is allowed to provide item name, which includes top-parent like file.root/folder/item
718  // but one could skip top-item name, if there are no other items
719  if (fullname === top._name) return top;
720 
721  var len = top._name.length;
722  if (fullname[len] == "/") {
723  fullname = fullname.substr(len+1);
724  pos = fullname.length;
725  }
726  }
727 
728  function process_child(child, ignore_prnt) {
729  // set parent pointer when searching child
730  if (!ignore_prnt) child._parent = top;
731 
732  if ((pos >= fullname.length-1) || (pos < 0)) return child;
733 
734  return find_in_hierarchy(child, fullname.substr(pos + 1));
735  }
736 
737  while (pos > 0) {
738  // we try to find element with slashes inside - start from full name
739  var localname = (pos >= fullname.length) ? fullname : fullname.substr(0, pos);
740 
741  if (top._childs) {
742  // first try to find direct matched item
743  for (var i = 0; i < top._childs.length; ++i)
744  if (top._childs[i]._name == localname)
745  return process_child(top._childs[i]);
746 
747  // if first child online, check its elements
748  if ((top._kind === 'TopFolder') && (top._childs[0]._online!==undefined))
749  for (var i = 0; i < top._childs[0]._childs.length; ++i)
750  if (top._childs[0]._childs[i]._name == localname)
751  return process_child(top._childs[0]._childs[i], true);
752 
753  // if allowed, try to found item with key
754  if (arg.check_keys) {
755  var newest = null;
756  for (var i = 0; i < top._childs.length; ++i) {
757  if (top._childs[i]._keyname === localname) {
758  if (!newest || (newest._cycle < top._childs[i]._cycle)) newest = top._childs[i];
759  }
760  }
761  if (newest) return process_child(newest);
762  }
763 
764  var allow_index = arg.allow_index;
765  if ((localname[0] === '[') && (localname[localname.length-1] === ']') &&
766  !isNaN(parseInt(localname.substr(1,localname.length-2)))) {
767  allow_index = true;
768  localname = localname.substr(1,localname.length-2);
769  }
770 
771  // when search for the elements it could be allowed to check index
772  if (allow_index) {
773  var indx = parseInt(localname);
774  if (!isNaN(indx) && (indx>=0) && (indx<top._childs.length))
775  return process_child(top._childs[indx]);
776  }
777  }
778 
779  pos = fullname.lastIndexOf("/", pos - 1);
780  }
781 
782  if (arg.force) {
783  // if did not found element with given name we just generate it
784  if (top._childs === undefined) top._childs = [];
785  pos = fullname.indexOf("/");
786  var child = { _name: ((pos < 0) ? fullname : fullname.substr(0, pos)) };
787  top._childs.push(child);
788  return process_child(child);
789  }
790 
791  return (arg.last_exists && top) ? { last: top, rest: fullname } : null;
792  }
793 
794  var top = this.h, itemname = "";
795 
796  if (arg === null) return null; else
797  if (typeof arg == 'string') { itemname = arg; arg = {}; } else
798  if (typeof arg == 'object') { itemname = arg.name; if ('top' in arg) top = arg.top; } else
799  return null;
800 
801  if (itemname === "__top_folder__") return top;
802 
803  if ((typeof itemname == 'string') && (itemname.indexOf("img:")==0)) return null;
804 
805  return find_in_hierarchy(top, itemname);
806  }
807 
808  HierarchyPainter.prototype.itemFullName = function(node, uptoparent, compact) {
809 
810  if (node && node._kind ==='TopFolder') return "__top_folder__";
811 
812  var res = "";
813 
814  while (node) {
815  // online items never includes top-level folder
816  if ((node._online!==undefined) && !uptoparent) return res;
817 
818  if ((node === uptoparent) || (node._kind==='TopFolder')) break;
819  if (compact && !node._parent) break; // in compact form top-parent is not included
820  if (res.length > 0) res = "/" + res;
821  res = node._name + res;
822  node = node._parent;
823  }
824 
825  return res;
826  }
827 
828  HierarchyPainter.prototype.ExecuteCommand = function(itemname, callback) {
829  // execute item marked as 'Command'
830  // If command requires additional arguments, they could be specified as extra arguments
831  // Or they will be requested interactive
832 
833  var hitem = this.Find(itemname),
834  url = this.GetOnlineItemUrl(hitem) + "/cmd.json",
835  pthis = this,
836  d3node = d3.select((typeof callback == 'function') ? undefined : callback);
837 
838  if ('_numargs' in hitem)
839  for (var n = 0; n < hitem._numargs; ++n) {
840  var argname = "arg" + (n+1), argvalue = null;
841  if (n+2<arguments.length) argvalue = arguments[n+2];
842  if (!argvalue && (typeof callback == 'object'))
843  argvalue = prompt("Input argument " + argname + " for command " + hitem._name, "");
844  if (!argvalue) return;
845  url += ((n==0) ? "?" : "&") + argname + "=" + argvalue;
846  }
847 
848  if (!d3node.empty()) {
849  d3node.style('background','yellow');
850  if (hitem && hitem._title) d3node.attr('title', "Executing " + hitem._title);
851  }
852 
853  JSROOT.NewHttpRequest(url, 'text', function(res) {
854  if (typeof callback == 'function') return callback(res);
855  if (d3node.empty()) return;
856  var col = ((res!=null) && (res!='false')) ? 'green' : 'red';
857  if (hitem && hitem._title) d3node.attr('title', hitem._title + " lastres=" + res);
858  d3node.style('background', col);
859  setTimeout(function() { d3node.style('background', ''); }, 2000);
860  if ((col == 'green') && ('_hreload' in hitem)) pthis.reload();
861  if ((col == 'green') && ('_update_item' in hitem)) pthis.updateItems(hitem._update_item.split(";"));
862  }).send();
863  }
864 
865  HierarchyPainter.prototype.RefreshHtml = function(callback) {
866  if (!this.divid) return JSROOT.CallBack(callback);
867  var hpainter = this;
868  JSROOT.AssertPrerequisites('jq2d', function() {
869  hpainter.RefreshHtml(callback);
870  });
871  }
872 
873  HierarchyPainter.prototype.get = function(arg, call_back, options) {
874  // get object item with specified name
875  // depending from provided option, same item can generate different object types
876 
877  if (arg===null) return JSROOT.CallBack(call_back, null, null);
878 
879  var itemname, item, hpainter = this;
880 
881  if (typeof arg === 'string') {
882  itemname = arg;
883  } else if (typeof arg === 'object') {
884  if ((arg._parent!==undefined) && (arg._name!==undefined) && (arg._kind!==undefined)) item = arg; else
885  if (arg.name!==undefined) itemname = arg.name; else
886  if (arg.arg!==undefined) itemname = arg.arg; else
887  if (arg.item!==undefined) item = arg.item;
888  }
889 
890  if ((typeof itemname == 'string') && (itemname.indexOf("img:")==0))
891  return JSROOT.CallBack(call_back, null, {
892  _typename: "TJSImage", // artificial class, can be created by users
893  fName: itemname.substr(4)
894  });
895 
896  if (item) itemname = this.itemFullName(item);
897  else item = this.Find( { name: itemname, allow_index: true, check_keys: true } );
898 
899  // if item not found, try to find nearest parent which could allow us to get inside
900  var d = (item!=null) ? null : this.Find({ name: itemname, last_exists: true, check_keys: true, allow_index: true });
901 
902  // if item not found, try to expand hierarchy central function
903  // implements not process get in central method of hierarchy item (if exists)
904  // if last_parent found, try to expand it
905  if ((d !== null) && ('last' in d) && (d.last !== null)) {
906  var parentname = this.itemFullName(d.last);
907 
908  // this is indication that expand does not give us better path to searched item
909  if ((typeof arg == 'object') && ('rest' in arg))
910  if ((arg.rest == d.rest) || (arg.rest.length <= d.rest.length))
911  return JSROOT.CallBack(call_back);
912 
913  return this.expand(parentname, function(res) {
914  if (!res) JSROOT.CallBack(call_back);
915  var newparentname = hpainter.itemFullName(d.last);
916  if (newparentname.length>0) newparentname+="/";
917  hpainter.get( { name: newparentname + d.rest, rest: d.rest }, call_back, options);
918  }, null, true);
919  }
920 
921  if ((item !== null) && (typeof item._obj == 'object'))
922  return JSROOT.CallBack(call_back, item, item._obj);
923 
924  // normally search _get method in the parent items
925  var curr = item;
926  while (curr) {
927  if (('_get' in curr) && (typeof curr._get == 'function'))
928  return curr._get(item, null, call_back, options);
929  curr = ('_parent' in curr) ? curr._parent : null;
930  }
931 
932  JSROOT.CallBack(call_back, item, null);
933  }
934 
935  HierarchyPainter.prototype.draw = function(divid, obj, drawopt) {
936  // just envelope, one should be able to redefine it for sub-classes
937  return JSROOT.draw(divid, obj, drawopt);
938  }
939 
940  HierarchyPainter.prototype.redraw = function(divid, obj, drawopt) {
941  // just envelope, one should be able to redefine it for sub-classes
942  return JSROOT.redraw(divid, obj, drawopt);
943  }
944 
945  HierarchyPainter.prototype.player = function(itemname, option, call_back) {
946  var item = this.Find(itemname);
947 
948  if (!item || !item._player) return JSROOT.CallBack(call_back, null);
949 
950  var hpainter = this;
951 
952  JSROOT.AssertPrerequisites(item._prereq || '', function() {
953 
954  var player_func = JSROOT.findFunction(item._player);
955  if (!player_func) return JSROOT.CallBack(call_back, null);
956 
957  hpainter.CreateDisplay(function(mdi) {
958  var res = mdi ? player_func(hpainter, itemname, option) : null;
959  JSROOT.CallBack(call_back, res);
960  });
961  });
962  }
963 
964  HierarchyPainter.prototype.canDisplay = function(item, drawopt) {
965  if (!item) return false;
966  if (item._player) return true;
967  if (item._can_draw !== undefined) return item._can_draw;
968  if (drawopt == 'inspect') return true;
969  var handle = JSROOT.getDrawHandle(item._kind, drawopt);
970  return handle && (('func' in handle) || ('draw_field' in handle));
971  }
972 
973  HierarchyPainter.prototype.isItemDisplayed = function(itemname) {
974  var mdi = this.GetDisplay();
975  if (!mdi) return false;
976 
977  return mdi.FindFrame(itemname) !== null;
978  }
979 
980  HierarchyPainter.prototype.display = function(itemname, drawopt, call_back) {
981  var h = this,
982  painter = null,
983  updating = false,
984  item = null,
985  display_itemname = itemname,
986  frame_name = itemname,
987  marker = "::_display_on_frame_::",
988  p = drawopt ? drawopt.indexOf(marker) : -1;
989 
990  if (p>=0) {
991  frame_name = drawopt.substr(p + marker.length);
992  drawopt = drawopt.substr(0, p);
993  }
994 
995  function display_callback(respainter) {
996  if (!updating) JSROOT.progress();
997 
998  if (respainter && (typeof respainter === 'object') && (typeof respainter.SetItemName === 'function')) {
999  respainter.SetItemName(display_itemname, updating ? null : drawopt, h); // mark painter as created from hierarchy
1000  if (item && !item._painter) item._painter = respainter;
1001  }
1002  JSROOT.CallBack(call_back, respainter || painter, display_itemname);
1003  }
1004 
1005  h.CreateDisplay(function(mdi) {
1006 
1007  if (!mdi) return display_callback();
1008 
1009  item = h.Find(display_itemname);
1010 
1011  if (item && ('_player' in item))
1012  return h.player(display_itemname, drawopt, display_callback);
1013 
1014  updating = (typeof(drawopt)=='string') && (drawopt.indexOf("update:")==0);
1015 
1016  if (updating) {
1017  drawopt = drawopt.substr(7);
1018  if (!item || item._doing_update) return display_callback();
1019  item._doing_update = true;
1020  }
1021 
1022  if (item && !h.canDisplay(item, drawopt)) return display_callback();
1023 
1024  var divid = "";
1025  if ((typeof(drawopt)=='string') && (drawopt.indexOf("divid:")>=0)) {
1026  var pos = drawopt.indexOf("divid:");
1027  divid = drawopt.slice(pos+6);
1028  drawopt = drawopt.slice(0, pos);
1029  }
1030 
1031  if (!updating) JSROOT.progress("Loading " + display_itemname);
1032 
1033  h.get(display_itemname, function(resitem, obj) {
1034 
1035  if (!updating) JSROOT.progress();
1036 
1037  if (!item) item = resitem;
1038 
1039  if (updating && item) delete item._doing_update;
1040  if (!obj) return display_callback();
1041 
1042  if (!updating) JSROOT.progress("Drawing " + display_itemname);
1043 
1044  if (divid.length > 0)
1045  return (updating ? JSROOT.redraw : JSROOT.draw)(divid, obj, drawopt, display_callback);
1046 
1047  mdi.ForEachPainter(function(p, frame) {
1048  if (p.GetItemName() != display_itemname) return;
1049  // verify that object was drawn with same option as specified now (if any)
1050  if (!updating && (drawopt!=null) && (p.GetItemDrawOpt()!=drawopt)) return;
1051  mdi.ActivateFrame(frame);
1052 
1053  var handle = null;
1054  if (obj._typename) handle = JSROOT.getDrawHandle("ROOT." + obj._typename);
1055  if (handle && handle.draw_field && obj[handle.draw_field])
1056  obj = obj[handle.draw_field];
1057 
1058  if (p.RedrawObject(obj)) painter = p;
1059  });
1060 
1061  if (painter) return display_callback();
1062 
1063  if (updating) {
1064  JSROOT.console("something went wrong - did not found painter when doing update of " + display_itemname);
1065  return display_callback();
1066  }
1067 
1068  var frame = mdi.FindFrame(frame_name, true);
1069  d3.select(frame).html("");
1070  mdi.ActivateFrame(frame);
1071 
1072  JSROOT.draw(d3.select(frame).attr("id"), obj, drawopt, display_callback);
1073 
1074  if (JSROOT.gStyle.DragAndDrop)
1075  h.enable_dropping(frame, display_itemname);
1076 
1077  }, drawopt);
1078  });
1079  }
1080 
1081  HierarchyPainter.prototype.enable_dragging = function(element, itemname) {
1082  // here is not defined - implemented with jquery
1083  }
1084 
1085  HierarchyPainter.prototype.enable_dropping = function(frame, itemname) {
1086  // here is not defined - implemented with jquery
1087  }
1088 
1089  HierarchyPainter.prototype.dropitem = function(itemname, divid, opt, call_back) {
1090  var h = this;
1091 
1092  if (opt && typeof opt === 'function') { call_back = opt; opt = ""; }
1093  if (opt===undefined) opt = "";
1094 
1095  function drop_callback(drop_painter) {
1096  if (drop_painter && (typeof drop_painter === 'object')) drop_painter.SetItemName(itemname, null, h);
1097  JSROOT.CallBack(call_back);
1098  }
1099 
1100  if (itemname == "$legend")
1101  return JSROOT.AssertPrerequisites("v6;hist", function() {
1102  var res = JSROOT.Painter.produceLegend(divid, opt);
1103  JSROOT.CallBack(drop_callback, res);
1104  });
1105 
1106  h.get(itemname, function(item, obj) {
1107 
1108  if (!obj) return JSROOT.CallBack(call_back);
1109 
1110  var main_painter = JSROOT.GetMainPainter(divid);
1111 
1112  if (main_painter && (typeof main_painter.PerformDrop === 'function'))
1113  return main_painter.PerformDrop(obj, itemname, item, opt, drop_callback);
1114 
1115  if (main_painter && main_painter.accept_drops)
1116  return JSROOT.draw(divid, obj, "same " + opt, drop_callback);
1117 
1118  h.CleanupFrame(divid);
1119  return JSROOT.draw(divid, obj, opt, drop_callback);
1120  });
1121 
1122  return true;
1123  }
1124 
1127  HierarchyPainter.prototype.updateItems = function(items) {
1128 
1129  if (!this.disp || !items) return;
1130 
1131  var draw_items = [], draw_options = [];
1132 
1133  this.disp.ForEachPainter(function(p) {
1134  var itemname = p.GetItemName();
1135  if (!itemname || (draw_items.indexOf(itemname)>=0)) return;
1136  if (typeof items == 'array') {
1137  if (items.indexOf(itemname) < 0) return;
1138  } else {
1139  if (items != itemname) return;
1140  }
1141  draw_items.push(itemname);
1142  draw_options.push("update:" + p.GetItemDrawOpt());
1143  }, true); // only visible panels are considered
1144 
1145  if (draw_items.length > 0)
1146  this.displayAll(draw_items, draw_options);
1147  }
1148 
1149 
1152  HierarchyPainter.prototype.updateAll = function(only_auto_items, only_items) {
1153 
1154  if (!this.disp) return;
1155 
1156  if (only_auto_items === "monitoring") only_auto_items = !this._monitoring_on;
1157 
1158  var allitems = [], options = [], hpainter = this;
1159 
1160  // first collect items
1161  this.disp.ForEachPainter(function(p) {
1162  var itemname = p.GetItemName(),
1163  drawopt = p.GetItemDrawOpt();
1164  if ((typeof itemname != 'string') || (allitems.indexOf(itemname)>=0)) return;
1165 
1166  var item = hpainter.Find(itemname), forced = false;
1167  if (!item || ('_not_monitor' in item) || ('_player' in item)) return;
1168 
1169  if ('_always_monitor' in item) {
1170  forced = true;
1171  } else {
1172  var handle = JSROOT.getDrawHandle(item._kind);
1173  if (handle && ('monitor' in handle)) {
1174  if ((handle.monitor===false) || (handle.monitor=='never')) return;
1175  if (handle.monitor==='always') forced = true;
1176  }
1177  }
1178 
1179  if (forced || !only_auto_items) {
1180  allitems.push(itemname);
1181  options.push("update:" + drawopt);
1182  }
1183  }, true); // only visible panels are considered
1184 
1185  var painter = this;
1186 
1187  // force all files to read again (normally in non-browser mode)
1188  if (this.files_monitoring && !only_auto_items)
1189  this.ForEachRootFile(function(item) {
1190  painter.ForEach(function(fitem) { delete fitem._readobj; }, item);
1191  delete item._file;
1192  });
1193 
1194  if (allitems.length > 0)
1195  this.displayAll(allitems, options);
1196  }
1197 
1198  HierarchyPainter.prototype.displayAll = function(items, options, call_back) {
1199 
1200  if (!items || (items.length == 0)) return JSROOT.CallBack(call_back);
1201 
1202  var h = this;
1203 
1204  if (!options) options = [];
1205  while (options.length < items.length)
1206  options.push("");
1207 
1208  if ((options.length == 1) && (options[0] == "iotest")) {
1209  h.clear();
1210  d3.select("#" + h.disp_frameid).html("<h2>Start I/O test</h2>")
1211 
1212  var tm0 = new Date();
1213  return h.get(items[0], function(item, obj) {
1214  var tm1 = new Date();
1215  d3.select("#" + h.disp_frameid).append("h2").html("Item " + items[0] + " reading time = " + (tm1.getTime() - tm0.getTime()) + "ms");
1216  return JSROOT.CallBack(call_back);
1217  });
1218  }
1219 
1220  var dropitems = new Array(items.length), dropopts = new Array(items.length), images = new Array(items.length);
1221 
1222  // First of all check that items are exists, look for cycle extension and plus sign
1223  for (var i = 0; i < items.length; ++i) {
1224  dropitems[i] = dropopts[i] = null;
1225 
1226  var item = items[i], can_split = true;
1227 
1228  if (item && item.indexOf("img:")==0) { images[i] = true; continue; }
1229 
1230  if (item && (item.length>1) && (item[0]=='\'') && (item[item.length-1]=='\'')) {
1231  items[i] = item.substr(1, item.length-2);
1232  can_split = false;
1233  }
1234 
1235  var elem = h.Find({ name: items[i], check_keys: true });
1236  if (elem) { items[i] = h.itemFullName(elem); continue; }
1237 
1238  if (can_split && (items[i][0]=='[') && (items[i][items[i].length-1]==']')) {
1239  dropitems[i] = JSROOT.ParseAsArray(items[i]);
1240  items[i] = dropitems[i].shift();
1241  } else
1242  if (can_split && (items[i].indexOf("+") > 0)) {
1243  dropitems[i] = items[i].split("+");
1244  items[i] = dropitems[i].shift();
1245  }
1246 
1247  if (dropitems[i] && dropitems[i].length > 0) {
1248  // allow to specify _same_ item in different file
1249  for (var j = 0; j < dropitems[i].length; ++j) {
1250  var pos = dropitems[i][j].indexOf("_same_");
1251  if ((pos>0) && (h.Find(dropitems[i][j])===null))
1252  dropitems[i][j] = dropitems[i][j].substr(0,pos) + items[i].substr(pos);
1253 
1254  elem = h.Find({ name: dropitems[i][j], check_keys: true });
1255  if (elem) dropitems[i][j] = h.itemFullName(elem);
1256  }
1257 
1258  if ((options[i][0] == "[") && (options[i][options[i].length-1] == "]")) {
1259  dropopts[i] = JSROOT.ParseAsArray(options[i]);
1260  options[i] = dropopts[i].shift();
1261  } else
1262  if (options[i].indexOf("+") > 0) {
1263  dropopts[i] = options[i].split("+");
1264  options[i] = dropopts[i].shift();
1265  } else {
1266  dropopts[i] = [];
1267  }
1268 
1269  while (dropopts[i].length < dropitems[i].length) dropopts[i].push("");
1270  }
1271 
1272  // also check if subsequent items has _same_, than use name from first item
1273  var pos = items[i].indexOf("_same_");
1274  if ((pos>0) && !h.Find(items[i]) && (i>0))
1275  items[i] = items[i].substr(0,pos) + items[0].substr(pos);
1276 
1277  elem = h.Find({ name: items[i], check_keys: true });
1278  if (elem) items[i] = h.itemFullName(elem);
1279  }
1280 
1281  // now check that items can be displayed
1282  for (var n = items.length-1; n>=0; --n) {
1283  if (images[n]) continue;
1284  var hitem = h.Find(items[n]);
1285  if (!hitem || h.canDisplay(hitem, options[n])) continue;
1286  // try to expand specified item
1287  h.expand(items[n], null, null, true);
1288  items.splice(n, 1);
1289  options.splice(n, 1);
1290  dropitems.splice(n, 1);
1291  }
1292 
1293  if (items.length == 0) return JSROOT.CallBack(call_back);
1294 
1295  var frame_names = new Array(items.length), items_wait = new Array(items.length);
1296  for (var n=0; n < items.length;++n) {
1297  items_wait[n] = 0;
1298  var fname = items[n], k = 0;
1299  if (items.indexOf(fname) < n) items_wait[n] = true; // if same item specified, one should wait first drawing before start next
1300  var p = options[n].indexOf("frameid:");
1301  if (p>=0) {
1302  fname = options[n].substr(p+8);
1303  options[n] = options[n].substr(0,p);
1304  } else {
1305  while (frame_names.indexOf(fname)>=0)
1306  fname = items[n] + "_" + k++;
1307  }
1308  frame_names[n] = fname;
1309  }
1310 
1311  // now check if several same items present - select only one for the drawing
1312  // if draw option includes 'main', such item will be drawn first
1313  for (var n=0; n<items.length;++n) {
1314  if (items_wait[n] !== 0) continue;
1315  var found_main = n;
1316  for (var k=0; k<items.length;++k)
1317  if ((items[n]===items[k]) && (options[k].indexOf('main')>=0)) found_main = k;
1318  for (var k=0; k<items.length;++k)
1319  if (items[n]===items[k]) items_wait[k] = (found_main != k);
1320  }
1321 
1322  h.CreateDisplay(function(mdi) {
1323  if (!mdi) return JSROOT.CallBack(call_back);
1324 
1325  // Than create empty frames for each item
1326  for (var i = 0; i < items.length; ++i)
1327  if (options[i].indexOf('update:')!==0) {
1328  mdi.CreateFrame(frame_names[i]);
1329  options[i] += "::_display_on_frame_::"+frame_names[i];
1330  }
1331 
1332  function DropNextItem(indx, painter) {
1333  if (painter && dropitems[indx] && (dropitems[indx].length>0))
1334  return h.dropitem(dropitems[indx].shift(), painter.divid, dropopts[indx].shift(), DropNextItem.bind(h, indx, painter));
1335 
1336  dropitems[indx] = null; // mark that all drop items are processed
1337  items[indx] = null; // mark item as ready
1338 
1339  var isany = false;
1340 
1341  for (var cnt = 0; cnt < items.length; ++cnt) {
1342  if (dropitems[cnt]) isany = true;
1343  if (items[cnt]===null) continue; // ignore completed item
1344  isany = true;
1345  if (items_wait[cnt] && items.indexOf(items[cnt])===cnt) {
1346  items_wait[cnt] = false;
1347  h.display(items[cnt], options[cnt], DropNextItem.bind(h,cnt));
1348  }
1349  }
1350 
1351  // only when items drawn and all sub-items dropped, one could perform call-back
1352  if (!isany && call_back) {
1353  JSROOT.CallBack(call_back);
1354  call_back = null;
1355  }
1356  }
1357 
1358  // We start display of all items parallel, but only if they are not the same
1359  for (var i = 0; i < items.length; ++i)
1360  if (!items_wait[i])
1361  h.display(items[i], options[i], DropNextItem.bind(h,i));
1362  });
1363  }
1364 
1365  HierarchyPainter.prototype.reload = function() {
1366  var hpainter = this;
1367  if ('_online' in this.h)
1368  this.OpenOnline(this.h._online, function() {
1369  hpainter.RefreshHtml();
1370  });
1371  }
1372 
1373  HierarchyPainter.prototype.UpdateTreeNode = function() {
1374  // dummy function, will be redefined when jquery part loaded
1375  }
1376 
1377  HierarchyPainter.prototype.activate = function(items, force) {
1378  // activate (select) specified item
1379  // if force specified, all required sub-levels will be opened
1380 
1381  if (typeof items == 'string') items = [ items ];
1382 
1383  var active = [], // array of elements to activate
1384  painter = this, // painter itself
1385  update = []; // array of elements to update
1386  this.ForEach(function(item) { if (item._background) { active.push(item); delete item._background; } });
1387 
1388  function mark_active() {
1389  if (typeof painter.UpdateBackground !== 'function') return;
1390 
1391  for (var n=update.length-1;n>=0;--n)
1392  painter.UpdateTreeNode(update[n]);
1393 
1394  for (var n=0;n<active.length;++n)
1395  painter.UpdateBackground(active[n], force);
1396  }
1397 
1398  function find_next(itemname, prev_found) {
1399  if (itemname === undefined) {
1400  // extract next element
1401  if (items.length == 0) return mark_active();
1402  itemname = items.shift();
1403  }
1404 
1405  var hitem = painter.Find(itemname);
1406 
1407  if (!hitem) {
1408  var d = painter.Find({ name: itemname, last_exists: true, check_keys: true, allow_index: true });
1409  if (!d || !d.last) return find_next();
1410  d.now_found = painter.itemFullName(d.last);
1411 
1412  if (force) {
1413 
1414  // if after last expand no better solution found - skip it
1415  if ((prev_found!==undefined) && (d.now_found === prev_found)) return find_next();
1416 
1417  return painter.expand(d.now_found, function(res) {
1418  if (!res) return find_next();
1419  var newname = painter.itemFullName(d.last);
1420  if (newname.length>0) newname+="/";
1421  find_next(newname + d.rest, d.now_found);
1422  });
1423  }
1424  hitem = d.last;
1425  }
1426 
1427  if (hitem) {
1428  // check that item is visible (opened), otherwise should enable parent
1429 
1430  var prnt = hitem._parent;
1431  while (prnt) {
1432  if (!prnt._isopen) {
1433  if (force) {
1434  prnt._isopen = true;
1435  if (update.indexOf(prnt)<0) update.push(prnt);
1436  } else {
1437  hitem = prnt; break;
1438  }
1439  }
1440  prnt = prnt._parent;
1441  }
1442 
1443  hitem._background = 'grey';
1444  if (active.indexOf(hitem)<0) active.push(hitem);
1445  }
1446 
1447  find_next();
1448  }
1449 
1450  if (force && this.brlayout) {
1451  if (!this.brlayout.browser_kind) return this.CreateBrowser('float', true, find_next);
1452  if (!this.brlayout.browser_visible) this.brlayout.ToggleBrowserVisisbility();
1453  }
1454 
1455  // use recursion
1456  find_next();
1457  }
1458 
1459  HierarchyPainter.prototype.expand = function(itemname, call_back, d3cont, silent) {
1460  var hpainter = this, hitem = this.Find(itemname);
1461 
1462  if (!hitem && d3cont) return JSROOT.CallBack(call_back);
1463 
1464  function DoExpandItem(_item, _obj, _name) {
1465  if (!_name) _name = hpainter.itemFullName(_item);
1466 
1467  var handle = _item._expand ? null : JSROOT.getDrawHandle(_item._kind, "::expand");
1468 
1469  if (_obj && handle && handle.expand_item) {
1470  _obj = _obj[handle.expand_item]; // just take specified field from the object
1471  if (_obj && _obj._typename)
1472  handle = JSROOT.getDrawHandle("ROOT."+_obj._typename, "::expand");
1473  }
1474 
1475  if (handle && handle.expand) {
1476  JSROOT.AssertPrerequisites(handle.prereq, function() {
1477  _item._expand = JSROOT.findFunction(handle.expand);
1478  if (_item._expand) return DoExpandItem(_item, _obj, _name);
1479  JSROOT.CallBack(call_back);
1480  });
1481  return true;
1482  }
1483 
1484  // try to use expand function
1485  if (_obj && _item && (typeof _item._expand === 'function')) {
1486  if (_item._expand(_item, _obj)) {
1487  _item._isopen = true;
1488  if (_item._parent && !_item._parent._isopen) {
1489  _item._parent._isopen = true; // also show parent
1490  if (!silent) hpainter.UpdateTreeNode(_item._parent);
1491  } else {
1492  if (!silent) hpainter.UpdateTreeNode(_item, d3cont);
1493  }
1494  JSROOT.CallBack(call_back, _item);
1495  return true;
1496  }
1497  }
1498 
1499  if (_obj && ObjectHierarchy(_item, _obj)) {
1500  _item._isopen = true;
1501  if (_item._parent && !_item._parent._isopen) {
1502  _item._parent._isopen = true; // also show parent
1503  if (!silent) hpainter.UpdateTreeNode(_item._parent);
1504  } else {
1505  if (!silent) hpainter.UpdateTreeNode(_item, d3cont);
1506  }
1507  JSROOT.CallBack(call_back, _item);
1508  return true;
1509  }
1510 
1511  return false;
1512  }
1513 
1514  if (hitem) {
1515  // item marked as it cannot be expanded, also top item cannot be changed
1516  if ((hitem._more === false) || (!hitem._parent && hitem._childs)) return JSROOT.CallBack(call_back);
1517 
1518  if (hitem._childs && hitem._isopen) {
1519  hitem._isopen = false;
1520  if (!silent) hpainter.UpdateTreeNode(hitem, d3cont);
1521  return JSROOT.CallBack(call_back);
1522  }
1523 
1524  if (hitem._obj && DoExpandItem(hitem, hitem._obj, itemname)) return;
1525  }
1526 
1527  JSROOT.progress("Loading " + itemname);
1528 
1529  this.get(itemname, function(item, obj) {
1530 
1531  JSROOT.progress();
1532 
1533  if (obj && DoExpandItem(item, obj)) return;
1534 
1535  JSROOT.CallBack(call_back);
1536  }, "hierarchy_expand" ); // indicate that we getting element for expand, can handle it differently
1537 
1538  }
1539 
1540  HierarchyPainter.prototype.GetTopOnlineItem = function(item) {
1541  if (item) {
1542  while (item && (!('_online' in item))) item = item._parent;
1543  return item;
1544  }
1545 
1546  if (!this.h) return null;
1547  if ('_online' in this.h) return this.h;
1548  if (this.h._childs && ('_online' in this.h._childs[0])) return this.h._childs[0];
1549  return null;
1550  }
1551 
1552 
1553  HierarchyPainter.prototype.ForEachJsonFile = function(call_back) {
1554  if (!this.h) return;
1555  if ('_jsonfile' in this.h)
1556  return JSROOT.CallBack(call_back, this.h);
1557 
1558  if (this.h._childs)
1559  for (var n = 0; n < this.h._childs.length; ++n) {
1560  var item = this.h._childs[n];
1561  if ('_jsonfile' in item) JSROOT.CallBack(call_back, item);
1562  }
1563  }
1564 
1565  HierarchyPainter.prototype.OpenJsonFile = function(filepath, call_back) {
1566  var isfileopened = false;
1567  this.ForEachJsonFile(function(item) { if (item._jsonfile==filepath) isfileopened = true; });
1568  if (isfileopened) return JSROOT.CallBack(call_back);
1569 
1570  var pthis = this;
1571  JSROOT.NewHttpRequest(filepath, 'object', function(res) {
1572  if (!res) return JSROOT.CallBack(call_back);
1573  var h1 = { _jsonfile: filepath, _kind: "ROOT." + res._typename, _jsontmp: res, _name: filepath.split("/").pop() };
1574  if (res.fTitle) h1._title = res.fTitle;
1575  h1._get = function(item,itemname,callback) {
1576  if (item._jsontmp)
1577  return JSROOT.CallBack(callback, item, item._jsontmp);
1578  JSROOT.NewHttpRequest(item._jsonfile, 'object', function(res) {
1579  item._jsontmp = res;
1580  JSROOT.CallBack(callback, item, item._jsontmp);
1581  }).send();
1582  }
1583  if (pthis.h == null) pthis.h = h1; else
1584  if (pthis.h._kind == 'TopFolder') pthis.h._childs.push(h1); else {
1585  var h0 = pthis.h, topname = ('_jsonfile' in h0) ? "Files" : "Items";
1586  pthis.h = { _name: topname, _kind: 'TopFolder', _childs : [h0, h1] };
1587  }
1588 
1589  pthis.RefreshHtml(call_back);
1590  }).send(null);
1591  }
1592 
1593  HierarchyPainter.prototype.ForEachRootFile = function(call_back) {
1594  if (!this.h) return;
1595  if ((this.h._kind == "ROOT.TFile") && this.h._file)
1596  return JSROOT.CallBack(call_back, this.h);
1597 
1598  if (this.h._childs)
1599  for (var n = 0; n < this.h._childs.length; ++n) {
1600  var item = this.h._childs[n];
1601  if ((item._kind == 'ROOT.TFile') && ('_fullurl' in item))
1602  JSROOT.CallBack(call_back, item);
1603  }
1604  }
1605 
1606  HierarchyPainter.prototype.OpenRootFile = function(filepath, call_back) {
1607  // first check that file with such URL already opened
1608 
1609  var isfileopened = false;
1610  this.ForEachRootFile(function(item) { if (item._fullurl===filepath) isfileopened = true; });
1611  if (isfileopened) return JSROOT.CallBack(call_back);
1612 
1613  var pthis = this;
1614 
1615  JSROOT.progress("Opening " + filepath + " ...");
1616  JSROOT.OpenFile(filepath, function(file) {
1617  JSROOT.progress();
1618  if (!file) {
1619  // make CORS warning
1620  if (!d3.select("#gui_fileCORS").style("background","red").empty())
1621  setTimeout(function() { d3.select("#gui_fileCORS").style("background",''); }, 5000);
1622  return JSROOT.CallBack(call_back, false);
1623  }
1624 
1625  var h1 = pthis.FileHierarchy(file);
1626  h1._isopen = true;
1627  if (pthis.h == null) {
1628  pthis.h = h1;
1629  if (pthis._topname) h1._name = pthis._topname;
1630  } else
1631  if (pthis.h._kind == 'TopFolder') {
1632  pthis.h._childs.push(h1);
1633  } else {
1634  var h0 = pthis.h, topname = (h0._kind == "ROOT.TFile") ? "Files" : "Items";
1635  pthis.h = { _name: topname, _kind: 'TopFolder', _childs : [h0, h1], _isopen: true };
1636  }
1637 
1638  pthis.RefreshHtml(call_back);
1639  });
1640  }
1641 
1642  HierarchyPainter.prototype.ApplyStyle = function(style, call_back) {
1643  if (!style)
1644  return JSROOT.CallBack(call_back);
1645 
1646  if (typeof style === 'object') {
1647  if (style._typename === "TStyle")
1648  JSROOT.extend(JSROOT.gStyle, style);
1649  return JSROOT.CallBack(call_back);
1650  }
1651 
1652  if (typeof style === 'string') {
1653 
1654  var hpainter = this,
1655  item = this.Find( { name: style, allow_index: true, check_keys: true } );
1656 
1657  if (item!==null)
1658  return this.get(item, function(item2, obj) { hpainter.ApplyStyle(obj, call_back); });
1659 
1660  if (style.indexOf('.json') > 0)
1661  return JSROOT.NewHttpRequest(style, 'object', function(res) {
1662  hpainter.ApplyStyle(res, call_back);
1663  }).send(null);
1664  }
1665 
1666  return JSROOT.CallBack(call_back);
1667  }
1668 
1669  HierarchyPainter.prototype.GetFileProp = function(itemname) {
1670  var item = this.Find(itemname);
1671  if (item == null) return null;
1672 
1673  var subname = item._name;
1674  while (item._parent) {
1675  item = item._parent;
1676  if ('_file' in item)
1677  return { kind: "file", fileurl: item._file.fURL, itemname: subname, localfile: !!item._file.fLocalFile };
1678 
1679  if ('_jsonfile' in item)
1680  return { kind: "json", fileurl: item._jsonfile, itemname: subname };
1681 
1682  subname = item._name + "/" + subname;
1683  }
1684 
1685  return null;
1686  }
1687 
1688  JSROOT.MarkAsStreamerInfo = function(h,item,obj) {
1689  // this function used on THttpServer to mark streamer infos list
1690  // as fictional TStreamerInfoList class, which has special draw function
1691  if (obj && (obj._typename=='TList'))
1692  obj._typename = 'TStreamerInfoList';
1693  }
1694 
1695  HierarchyPainter.prototype.GetOnlineItemUrl = function(item) {
1696  // returns URL, which could be used to request item from the online server
1697  if (typeof item == "string") item = this.Find(item);
1698  var prnt = item;
1699  while (prnt && (prnt._online===undefined)) prnt = prnt._parent;
1700  return prnt ? (prnt._online + this.itemFullName(item, prnt)) : null;
1701  }
1702 
1703  HierarchyPainter.prototype.isOnlineItem = function(item) {
1704  return this.GetOnlineItemUrl(item)!==null;
1705  }
1706 
1707  HierarchyPainter.prototype.GetOnlineItem = function(item, itemname, callback, option) {
1708  // method used to request object from the http server
1709 
1710  var url = itemname, h_get = false, req = "", req_kind = "object", pthis = this, draw_handle = null;
1711 
1712  if (option === 'hierarchy_expand') { h_get = true; option = undefined; }
1713 
1714  if (item) {
1715  url = this.GetOnlineItemUrl(item);
1716  var func = null;
1717  if ('_kind' in item) draw_handle = JSROOT.getDrawHandle(item._kind);
1718 
1719  if (h_get) {
1720  req = 'h.json?compact=3';
1721  item._expand = JSROOT.Painter.OnlineHierarchy; // use proper expand function
1722  } else
1723  if ('_make_request' in item) {
1724  func = JSROOT.findFunction(item._make_request);
1725  } else
1726  if ((draw_handle!=null) && ('make_request' in draw_handle)) {
1727  func = draw_handle.make_request;
1728  }
1729 
1730  if (typeof func == 'function') {
1731  // ask to make request
1732  var dreq = func(pthis, item, url, option);
1733  // result can be simple string or object with req and kind fields
1734  if (dreq!=null)
1735  if (typeof dreq == 'string') req = dreq; else {
1736  if ('req' in dreq) req = dreq.req;
1737  if ('kind' in dreq) req_kind = dreq.kind;
1738  }
1739  }
1740 
1741  if ((req.length==0) && (item._kind.indexOf("ROOT.")!=0))
1742  req = 'item.json.gz?compact=3';
1743  }
1744 
1745  if (!itemname && item && ('_cached_draw_object' in this) && (req.length == 0)) {
1746  // special handling for drawGUI when cashed
1747  var obj = this._cached_draw_object;
1748  delete this._cached_draw_object;
1749  return JSROOT.CallBack(callback, item, obj);
1750  }
1751 
1752  if (req.length == 0) req = 'root.json.gz?compact=23';
1753 
1754  if (url.length > 0) url += "/";
1755  url += req;
1756 
1757  var itemreq = JSROOT.NewHttpRequest(url, req_kind, function(obj) {
1758 
1759  var func = null;
1760 
1761  if (!h_get && item && ('_after_request' in item)) {
1762  func = JSROOT.findFunction(item._after_request);
1763  } else if (draw_handle && ('after_request' in draw_handle))
1764  func = draw_handle.after_request;
1765 
1766  if (typeof func == 'function') {
1767  var res = func(pthis, item, obj, option, itemreq);
1768  if ((res!=null) && (typeof res == "object")) obj = res;
1769  }
1770 
1771  JSROOT.CallBack(callback, item, obj);
1772  });
1773 
1774  itemreq.send(null);
1775  }
1776 
1777  JSROOT.Painter.OnlineHierarchy = function(node, obj) {
1778  // central function for expand of all online items
1779 
1780  if (obj && node && ('_childs' in obj)) {
1781 
1782  for (var n=0;n<obj._childs.length;++n)
1783  if (obj._childs[n]._more || obj._childs[n]._childs)
1784  obj._childs[n]._expand = JSROOT.Painter.OnlineHierarchy;
1785 
1786  node._childs = obj._childs;
1787  obj._childs = null;
1788  return true;
1789  }
1790 
1791  return false;
1792  }
1793 
1794  HierarchyPainter.prototype.OpenOnline = function(server_address, user_callback) {
1795  var painter = this;
1796 
1797  function AdoptHierarchy(result) {
1798  painter.h = result;
1799  if (painter.h == null) return;
1800 
1801  if (('_title' in painter.h) && (painter.h._title!='')) document.title = painter.h._title;
1802 
1803  result._isopen = true;
1804 
1805  // mark top hierarchy as online data and
1806  painter.h._online = server_address;
1807 
1808  painter.h._get = function(item, itemname, callback, option) {
1809  painter.GetOnlineItem(item, itemname, callback, option);
1810  }
1811 
1812  painter.h._expand = JSROOT.Painter.OnlineHierarchy;
1813 
1814  var scripts = "", modules = "";
1815  painter.ForEach(function(item) {
1816  if ('_childs' in item) item._expand = JSROOT.Painter.OnlineHierarchy;
1817 
1818  if ('_autoload' in item) {
1819  var arr = item._autoload.split(";");
1820  for (var n = 0; n < arr.length; ++n)
1821  if ((arr[n].length>3) &&
1822  ((arr[n].lastIndexOf(".js")==arr[n].length-3) ||
1823  (arr[n].lastIndexOf(".css")==arr[n].length-4))) {
1824  if (scripts.indexOf(arr[n])<0) scripts+=arr[n]+";";
1825  } else {
1826  if (modules.indexOf(arr[n])<0) modules+=arr[n]+";";
1827  }
1828  }
1829  });
1830 
1831  if (scripts.length > 0) scripts = "user:" + scripts;
1832 
1833  // use AssertPrerequisites, while it protect us from race conditions
1834  JSROOT.AssertPrerequisites(modules + scripts, function() {
1835 
1836  painter.ForEach(function(item) {
1837  if (!('_drawfunc' in item) || !('_kind' in item)) return;
1838  var typename = "kind:" + item._kind;
1839  if (item._kind.indexOf('ROOT.')==0) typename = item._kind.slice(5);
1840  var drawopt = item._drawopt;
1841  if (!JSROOT.canDraw(typename) || (drawopt!=null))
1842  JSROOT.addDrawFunc({ name: typename, func: item._drawfunc, script: item._drawscript, opt: drawopt });
1843  });
1844 
1845  JSROOT.CallBack(user_callback, painter);
1846  });
1847  }
1848 
1849  if (!server_address) server_address = "";
1850 
1851  if (typeof server_address == 'object') {
1852  var h = server_address;
1853  server_address = "";
1854  return AdoptHierarchy(h);
1855  }
1856 
1857  JSROOT.NewHttpRequest(server_address + "h.json?compact=3", 'object', AdoptHierarchy).send(null);
1858  }
1859 
1860  HierarchyPainter.prototype.GetOnlineProp = function(itemname) {
1861  var item = this.Find(itemname);
1862  if (!item) return null;
1863 
1864  var subname = item._name;
1865  while (item._parent) {
1866  item = item._parent;
1867 
1868  if ('_online' in item) {
1869  return {
1870  server : item._online,
1871  itemname : subname
1872  };
1873  }
1874  subname = item._name + "/" + subname;
1875  }
1876 
1877  return null;
1878  }
1879 
1880  HierarchyPainter.prototype.FillOnlineMenu = function(menu, onlineprop, itemname) {
1881 
1882  var painter = this,
1883  node = this.Find(itemname),
1884  sett = JSROOT.getDrawSettings(node._kind, 'nosame;noinspect'),
1885  handle = JSROOT.getDrawHandle(node._kind),
1886  root_type = (typeof node._kind == 'string') ? node._kind.indexOf("ROOT.") == 0 : false;
1887 
1888  if (sett.opts && (node._can_draw !== false)) {
1889  sett.opts.push('inspect');
1890  menu.addDrawMenu("Draw", sett.opts, function(arg) { painter.display(itemname, arg); });
1891  }
1892 
1893  if (!node._childs && (node._more !== false) && (node._more || root_type || sett.expand))
1894  menu.add("Expand", function() { painter.expand(itemname); });
1895 
1896  if (handle && ('execute' in handle))
1897  menu.add("Execute", function() { painter.ExecuteCommand(itemname, menu.tree_node); });
1898 
1899  var drawurl = onlineprop.server + onlineprop.itemname + "/draw.htm", separ = "?";
1900  if (this.IsMonitoring()) {
1901  drawurl += separ + "monitoring=" + this.MonitoringInterval();
1902  separ = "&";
1903  }
1904 
1905  if (sett.opts && (node._can_draw !== false))
1906  menu.addDrawMenu("Draw in new window", sett.opts, function(arg) { window.open(drawurl+separ+"opt=" +arg); });
1907 
1908  if (sett.opts && (sett.opts.length > 0) && root_type && (node._can_draw !== false))
1909  menu.addDrawMenu("Draw as png", sett.opts, function(arg) {
1910  window.open(onlineprop.server + onlineprop.itemname + "/root.png?w=400&h=300&opt=" + arg);
1911  });
1912 
1913  if ('_player' in node)
1914  menu.add("Player", function() { painter.player(itemname); });
1915  }
1916 
1917  HierarchyPainter.prototype.Adopt = function(h) {
1918  this.h = h;
1919  this.RefreshHtml();
1920  }
1921 
1926  HierarchyPainter.prototype.SetMonitoring = function(interval, monitor_on) {
1927 
1928  this._runMonitoring("cleanup");
1929 
1930  if (interval) {
1931  interval = parseInt(interval);
1932  if (!isNaN(interval) && (interval > 0)) {
1933  this._monitoring_interval = Math.max(100,interval);
1934  monitor_on = true;
1935  } else {
1936  this._monitoring_interval = 3000;
1937  }
1938  }
1939 
1940  this._monitoring_on = monitor_on;
1941 
1942  if (this.IsMonitoring())
1943  this._runMonitoring();
1944  }
1945 
1947  HierarchyPainter.prototype._runMonitoring = function(arg) {
1948  if ((arg == "cleanup") || !this.IsMonitoring()) {
1949  if (this._monitoring_handle) {
1950  clearTimeout(this._monitoring_handle);
1951  delete this._monitoring_handle;
1952  }
1953 
1954  if (this._monitoring_frame) {
1955  cancelAnimationFrame(this._monitoring_frame);
1956  delete this._monitoring_frame;
1957  }
1958  return;
1959  }
1960 
1961  if (arg == "frame") {
1962  // process of timeout, request animation frame
1963  delete this._monitoring_handle;
1964  this._monitoring_frame = requestAnimationFrame(this._runMonitoring.bind(this,"draw"));
1965  return;
1966  }
1967 
1968  if (arg == "draw") {
1969  delete this._monitoring_frame;
1970  this.updateAll("monitoring");
1971  }
1972 
1973  this._monitoring_handle = setTimeout(this._runMonitoring.bind(this,"frame"), this.MonitoringInterval());
1974  }
1975 
1977  HierarchyPainter.prototype.MonitoringInterval = function(val) {
1978  return this._monitoring_interval || 3000;
1979  }
1980 
1982  HierarchyPainter.prototype.EnableMonitoring = function(on) {
1983  this.SetMonitoring(undefined, on);
1984  }
1985 
1987  HierarchyPainter.prototype.IsMonitoring = function() {
1988  return this._monitoring_on;
1989  }
1990 
1991  HierarchyPainter.prototype.SetDisplay = function(layout, frameid) {
1992  if (!frameid && (typeof layout == 'object')) {
1993  this.disp = layout;
1994  this.disp_kind = 'custom';
1995  this.disp_frameid = null;
1996  } else {
1997  this.disp_kind = layout;
1998  this.disp_frameid = frameid;
1999  }
2000 
2001  if (!this.register_resize) {
2002  this.register_resize = true;
2003  JSROOT.RegisterForResize(this);
2004  }
2005  }
2006 
2007  HierarchyPainter.prototype.GetLayout = function() {
2008  return this.disp_kind;
2009  }
2010 
2011  HierarchyPainter.prototype.ClearPainter = function(obj_painter) {
2012  this.ForEach(function(item) {
2013  if (item._painter === obj_painter) delete item._painter;
2014  });
2015  }
2016 
2017  HierarchyPainter.prototype.clear = function(withbrowser) {
2018  if (this.disp) {
2019  this.disp.Reset();
2020  delete this.disp;
2021  }
2022 
2023  var plainarr = [];
2024 
2025  this.ForEach(function(item) {
2026  delete item._painter; // remove reference on the painter
2027  // when only display cleared, try to clear all browser items
2028  if (!withbrowser && (typeof item.clear=='function')) item.clear();
2029  if (withbrowser) plainarr.push(item);
2030  });
2031 
2032  if (withbrowser) {
2033  // cleanup all monitoring loops
2034  this.EnableMonitoring(false);
2035  // simplify work for javascript and delete all (ok, most of) cross-references
2036  this.select_main().html("");
2037  plainarr.forEach(function(d) { delete d._parent; delete d._childs; delete d._obj; delete d._d3cont; });
2038  delete this.h;
2039  }
2040  }
2041 
2042  HierarchyPainter.prototype.GetDisplay = function() {
2043  return ('disp' in this) ? this.disp : null;
2044  }
2045 
2046  HierarchyPainter.prototype.CleanupFrame = function(divid) {
2047  // hook to perform extra actions when frame is cleaned
2048 
2049  var lst = JSROOT.cleanup(divid);
2050 
2051  // we remove all painters references from items
2052  if (lst && (lst.length>0))
2053  this.ForEach(function(item) {
2054  if (item._painter && lst.indexOf(item._painter)>=0) delete item._painter;
2055  });
2056  }
2057 
2062  HierarchyPainter.prototype.CreateDisplay = function(callback) {
2063 
2064  if ('disp' in this) {
2065  if ((this.disp.NumDraw() > 0) || (this.disp_kind == "custom")) return JSROOT.CallBack(callback, this.disp);
2066  this.disp.Reset();
2067  delete this.disp;
2068  }
2069 
2070  // check that we can found frame where drawing should be done
2071  if (document.getElementById(this.disp_frameid) == null)
2072  return JSROOT.CallBack(callback, null);
2073 
2074  if ((this.disp_kind == "simple") ||
2075  ((this.disp_kind.indexOf("grid") == 0) && (this.disp_kind.indexOf("gridi") < 0)))
2076  this.disp = new GridDisplay(this.disp_frameid, this.disp_kind);
2077  else
2078  return JSROOT.AssertPrerequisites('jq2d', this.CreateDisplay.bind(this, callback));
2079 
2080  if (this.disp)
2081  this.disp.CleanupFrame = this.CleanupFrame.bind(this);
2082 
2083  JSROOT.CallBack(callback, this.disp);
2084  }
2085 
2092  HierarchyPainter.prototype.CreateCustomDisplay = function(itemname, custom_kind, callback) {
2093 
2094  if (this.disp_kind != "simple")
2095  return this.CreateDisplay(callback);
2096 
2097  this.disp_kind = custom_kind;
2098 
2099  // check if display can be erased
2100  if (this.disp) {
2101  var num = this.disp.NumDraw();
2102  if ((num>1) || ((num==1) && !this.disp.FindFrame(itemname)))
2103  return this.CreateDisplay(callback);
2104  this.disp.Reset();
2105  delete this.disp;
2106  }
2107 
2108  this.CreateDisplay(callback);
2109  }
2110 
2111  HierarchyPainter.prototype.updateOnOtherFrames = function(painter, obj) {
2112  // function should update object drawings for other painters
2113  var mdi = this.disp, handle = null, isany = false;
2114  if (!mdi) return false;
2115 
2116  if (obj._typename) handle = JSROOT.getDrawHandle("ROOT." + obj._typename);
2117  if (handle && handle.draw_field && obj[handle.draw_field])
2118  obj = obj[handle.draw_field];
2119 
2120  mdi.ForEachPainter(function(p, frame) {
2121  if ((p===painter) || (p.GetItemName() != painter.GetItemName())) return;
2122  mdi.ActivateFrame(frame);
2123  if (p.RedrawObject(obj)) isany = true;
2124  });
2125  return isany;
2126  }
2127 
2128  HierarchyPainter.prototype.CheckResize = function(size) {
2129  if (this.disp) this.disp.CheckMDIResize(null, size);
2130  }
2131 
2132  HierarchyPainter.prototype.StartGUI = function(gui_div, gui_call_back, url) {
2133 
2134  function GetOption(opt) {
2135  var res = JSROOT.GetUrlOption(opt, url);
2136  if (!res && gui_div && !gui_div.empty() && gui_div.node().hasAttribute(opt)) res = gui_div.attr(opt);
2137  return res;
2138  }
2139 
2140  function GetOptionAsArray(opt) {
2141  var res = JSROOT.GetUrlOptionAsArray(opt, url);
2142  if (res.length>0 || !gui_div || gui_div.empty()) return res;
2143  while (opt.length>0) {
2144  var separ = opt.indexOf(";");
2145  var part = separ>0 ? opt.substr(0, separ) : opt;
2146  if (separ>0) opt = opt.substr(separ+1); else opt = "";
2147 
2148  var canarray = true;
2149  if (part[0]=='#') { part = part.substr(1); canarray = false; }
2150  if (part==='files') continue; // special case for normal UI
2151 
2152  if (!gui_div.node().hasAttribute(part)) continue;
2153 
2154  var val = gui_div.attr(part);
2155 
2156  if (canarray) res = res.concat(JSROOT.ParseAsArray(val));
2157  else if (val!==null) res.push(val);
2158  }
2159  return res;
2160  }
2161 
2162  var hpainter = this,
2163  prereq = GetOption('prereq') || "",
2164  filesdir = JSROOT.GetUrlOption("path", url) || "", // path used in normal gui
2165  filesarr = GetOptionAsArray("#file;files"),
2166  localfile = GetOption("localfile"),
2167  jsonarr = GetOptionAsArray("#json;jsons"),
2168  expanditems = GetOptionAsArray("expand"),
2169  itemsarr = GetOptionAsArray("#item;items"),
2170  optionsarr = GetOptionAsArray("#opt;opts"),
2171  monitor = GetOption("monitoring"),
2172  layout = GetOption("layout"),
2173  style = GetOptionAsArray("#style"),
2174  statush = 0, status = GetOption("status"),
2175  browser_kind = GetOption("browser"),
2176  browser_configured = !!browser_kind,
2177  title = GetOption("title");
2178 
2179  if (GetOption("float")!==null) { browser_kind = 'float'; browser_configured = true; } else
2180  if (GetOption("fix")!==null) { browser_kind = 'fix'; browser_configured = true; }
2181 
2182  this.no_select = GetOption("noselect");
2183 
2184  if (GetOption('files_monitoring')!==null) this.files_monitoring = true;
2185 
2186  if (title) document.title = title;
2187 
2188  var load = GetOption("load");
2189  if (load) prereq += ";io;2d;load:" + load;
2190 
2191  if (expanditems.length==0 && (GetOption("expand")==="")) expanditems.push("");
2192 
2193  if (filesdir) {
2194  for (var i=0;i<filesarr.length;++i) filesarr[i] = filesdir + filesarr[i];
2195  for (var i=0;i<jsonarr.length;++i) jsonarr[i] = filesdir + jsonarr[i];
2196  }
2197 
2198  if ((itemsarr.length==0) && GetOption("item")==="") itemsarr.push("");
2199 
2200  if ((jsonarr.length==1) && (itemsarr.length==0) && (expanditems.length==0)) itemsarr.push("");
2201 
2202  if (!this.disp_kind) {
2203  if ((typeof layout == "string") && (layout.length > 0))
2204  this.disp_kind = layout;
2205  else
2206  switch (itemsarr.length) {
2207  case 0:
2208  case 1: this.disp_kind = 'simple'; break;
2209  case 2: this.disp_kind = 'vert2'; break;
2210  case 3: this.disp_kind = 'vert21'; break;
2211  case 4: this.disp_kind = 'vert22'; break;
2212  case 5: this.disp_kind = 'vert32'; break;
2213  case 6: this.disp_kind = 'vert222'; break;
2214  case 7: this.disp_kind = 'vert322'; break;
2215  case 8: this.disp_kind = 'vert332'; break;
2216  case 9: this.disp_kind = 'vert333'; break;
2217  default: this.disp_kind = 'flex';
2218  }
2219  }
2220 
2221  if (status==="no") status = null; else
2222  if (status==="off") { this.status_disabled = true; status = null; } else
2223  if (status==="on") status = true; else
2224  if (status!==null) { statush = parseInt(status); if (isNaN(statush) || (statush<5)) statush = 0; status = true; }
2225  if (this.no_select==="") this.no_select = true;
2226 
2227  if (!browser_kind) browser_kind = "fix"; else
2228  if (browser_kind==="no") browser_kind = ""; else
2229  if (browser_kind==="off") { browser_kind = ""; status = null; this.exclude_browser = true; }
2230  if (GetOption("nofloat")!==null) this.float_browser_disabled = true;
2231 
2232  if (this.start_without_browser) browser_kind = "";
2233 
2234  if (status || browser_kind) prereq = "jq2d;" + prereq;
2235 
2236  this._topname = GetOption("topname");
2237 
2238  function OpenAllFiles(res) {
2239  if (browser_kind) { hpainter.CreateBrowser(browser_kind); browser_kind = ""; }
2240  if (status!==null) { hpainter.CreateStatusLine(statush, status); status = null; }
2241  if (jsonarr.length>0)
2242  hpainter.OpenJsonFile(jsonarr.shift(), OpenAllFiles);
2243  else if (filesarr.length>0)
2244  hpainter.OpenRootFile(filesarr.shift(), OpenAllFiles);
2245  else if ((localfile!==null) && (typeof hpainter.SelectLocalFile == 'function')) {
2246  localfile = null; hpainter.SelectLocalFile(OpenAllFiles);
2247  } else if (expanditems.length>0)
2248  hpainter.expand(expanditems.shift(), OpenAllFiles);
2249  else if (style.length>0)
2250  hpainter.ApplyStyle(style.shift(), OpenAllFiles);
2251  else
2252  hpainter.displayAll(itemsarr, optionsarr, function() {
2253  hpainter.RefreshHtml();
2254  hpainter.SetMonitoring(monitor);
2255  JSROOT.CallBack(gui_call_back);
2256  });
2257  }
2258 
2259  function AfterOnlineOpened() {
2260  // check if server enables monitoring
2261 
2262  if (!hpainter.exclude_browser && !browser_configured && ('_browser' in hpainter.h)) {
2263  browser_kind = hpainter.h._browser;
2264  if (browser_kind==="no") browser_kind = ""; else
2265  if (browser_kind==="off") { browser_kind = ""; status = null; hpainter.exclude_browser = true; }
2266  }
2267 
2268  if (('_monitoring' in hpainter.h) && !monitor)
2269  monitor = hpainter.h._monitoring;
2270 
2271  if (('_loadfile' in hpainter.h) && (filesarr.length==0))
2272  filesarr = JSROOT.ParseAsArray(hpainter.h._loadfile);
2273 
2274  if (('_drawitem' in hpainter.h) && (itemsarr.length==0)) {
2275  itemsarr = JSROOT.ParseAsArray(hpainter.h._drawitem);
2276  optionsarr = JSROOT.ParseAsArray(hpainter.h._drawopt);
2277  }
2278 
2279  if (('_layout' in hpainter.h) && !layout && ((hpainter.is_online != "draw") || (itemsarr.length > 1)))
2280  hpainter.disp_kind = hpainter.h._layout;
2281 
2282  if (('_toptitle' in hpainter.h) && hpainter.exclude_browser && document)
2283  document.title = hpainter.h._toptitle;
2284 
2285  if (gui_div)
2286  hpainter.PrepareGuiDiv(gui_div, hpainter.disp_kind);
2287 
2288  OpenAllFiles();
2289  }
2290 
2291  var h0 = null;
2292  if (this.is_online) {
2293  if (typeof GetCachedHierarchy == 'function') h0 = GetCachedHierarchy();
2294  if (typeof h0 !== 'object') h0 = "";
2295  }
2296 
2297  if (h0 !== null)
2298  return this.OpenOnline(h0, AfterOnlineOpened);
2299 
2300  if (gui_div)
2301  this.PrepareGuiDiv(gui_div, this.disp_kind);
2302 
2303  if (prereq.length>0) JSROOT.AssertPrerequisites(prereq, OpenAllFiles);
2304  else OpenAllFiles();
2305  }
2306 
2307  HierarchyPainter.prototype.PrepareGuiDiv = function(myDiv, layout) {
2308 
2309  this.gui_div = myDiv.attr('id');
2310 
2311  this.brlayout = new BrowserLayout(this.gui_div, this);
2312 
2313  this.brlayout.Create(!this.exclude_browser);
2314 
2315  if (!this.exclude_browser) {
2316  var btns = this.brlayout.CreateBrowserBtns();
2317 
2318  JSROOT.ToolbarIcons.CreateSVG(btns, JSROOT.ToolbarIcons.diamand, 15, "toggle fix-pos browser")
2319  .style("margin","3px").on("click", this.CreateBrowser.bind(this, "fix", true));
2320 
2321  if (!this.float_browser_disabled)
2322  JSROOT.ToolbarIcons.CreateSVG(btns, JSROOT.ToolbarIcons.circle, 15, "toggle float browser")
2323  .style("margin","3px").on("click", this.CreateBrowser.bind(this, "float", true));
2324 
2325  if (!this.status_disabled)
2326  JSROOT.ToolbarIcons.CreateSVG(btns, JSROOT.ToolbarIcons.three_circles, 15, "toggle status line")
2327  .style("margin","3px").on("click", this.CreateStatusLine.bind(this, 0, "toggle"));
2328  }
2329 
2330  this.SetDisplay(layout, this.brlayout.drawing_divid());
2331  }
2332 
2333  HierarchyPainter.prototype.CreateStatusLine = function(height, mode) {
2334  if (this.status_disabled || !this.gui_div || !this.brlayout) return '';
2335  return this.brlayout.CreateStatusLine(height, mode);
2336  }
2337 
2338  HierarchyPainter.prototype.CreateBrowser = function(browser_kind, update_html, call_back) {
2339  if (!this.gui_div) return;
2340 
2341  var hpainter = this;
2342  JSROOT.AssertPrerequisites('jq2d', function() {
2343  hpainter.CreateBrowser(browser_kind, update_html, call_back);
2344  });
2345  }
2346 
2347  // ======================================================================================
2348 
2349  JSROOT.BuildNobrowserGUI = function() {
2350  var myDiv = d3.select('#simpleGUI'),
2351  online = false, drawing = false;
2352 
2353  if (myDiv.empty()) {
2354  online = true;
2355  myDiv = d3.select('#onlineGUI');
2356  if (myDiv.empty()) { myDiv = d3.select('#drawGUI'); drawing = true; }
2357  if (myDiv.empty()) return alert('no div for simple nobrowser gui found');
2358  }
2359 
2360  if (myDiv.attr("ignoreurl") === "true")
2361  JSROOT.gStyle.IgnoreUrlOptions = true;
2362 
2363  JSROOT.Painter.readStyleFromURL();
2364 
2365  var guisize = JSROOT.GetUrlOption("divsize");
2366  if (guisize) {
2367  guisize = guisize.split("x");
2368  if (guisize.length != 2) guisize = null;
2369  }
2370 
2371  if (guisize) {
2372  myDiv.style('position',"relative").style('width', guisize[0] + "px").style('height', guisize[1] + "px");
2373  } else {
2374  d3.select('html').style('height','100%');
2375  d3.select('body').style('min-height','100%').style('margin',0).style('overflow',"hidden");
2376  myDiv.style('position',"absolute").style('left',0).style('top',0).style('bottom',0).style('right',0).style('padding',1);
2377  }
2378 
2379  var hpainter = new JSROOT.HierarchyPainter('root', null);
2380 
2381  if (online) hpainter.is_online = drawing ? "draw" : "online";
2382  if (drawing) hpainter.exclude_browser = true;
2383 
2384  hpainter.start_without_browser = true; // indicate that browser not required at the beginning
2385 
2386  hpainter.StartGUI(myDiv, function() {
2387  if (!drawing) return;
2388 
2389  var func = JSROOT.findFunction('GetCachedObject');
2390  var obj = (typeof func == 'function') ? JSROOT.JSONR_unref(func()) : null;
2391  if (obj) hpainter._cached_draw_object = obj;
2392  var opt = JSROOT.GetUrlOption("opt") || "";
2393 
2394  if (JSROOT.GetUrlOption("websocket")!==null) opt+=";websocket";
2395 
2396  hpainter.display("", opt);
2397  });
2398  }
2399 
2400  JSROOT.Painter.drawStreamerInfo = function(divid, lst) {
2401  var painter = new JSROOT.HierarchyPainter('sinfo', divid, 'white');
2402 
2403  painter.h = { _name : "StreamerInfo", _childs : [] };
2404 
2405  for ( var i = 0; i < lst.arr.length; ++i) {
2406  var entry = lst.arr[i]
2407 
2408  if (entry._typename == "TList") continue;
2409 
2410  if (typeof (entry.fName) == 'undefined') {
2411  JSROOT.console("strange element in StreamerInfo with type " + entry._typename);
2412  continue;
2413  }
2414 
2415  var item = {
2416  _name : entry.fName + ";" + entry.fClassVersion,
2417  _kind : "class " + entry.fName,
2418  _title : "class:" + entry.fName + ' version:' + entry.fClassVersion + ' checksum:' + entry.fCheckSum,
2419  _icon: "img_class",
2420  _childs : []
2421  };
2422 
2423  if (entry.fTitle != '') item._title += ' ' + entry.fTitle;
2424 
2425  painter.h._childs.push(item);
2426 
2427  if (typeof entry.fElements == 'undefined') continue;
2428  for ( var l = 0; l < entry.fElements.arr.length; ++l) {
2429  var elem = entry.fElements.arr[l];
2430  if (!elem || !elem.fName) continue;
2431  var info = elem.fTypeName + " " + elem.fName,
2432  title = elem.fTypeName + " type:" + elem.fType;
2433  if (elem.fArrayDim===1)
2434  info += "[" + elem.fArrayLength + "]";
2435  else
2436  for (var dim=0;dim<elem.fArrayDim;++dim)
2437  info+="[" + elem.fMaxIndex[dim] + "]";
2438  if (elem.fBaseVersion===4294967295) info += ":-1"; else
2439  if (elem.fBaseVersion!==undefined) info += ":" + elem.fBaseVersion;
2440  info += ";";
2441  if (elem.fTitle != '') info += " // " + elem.fTitle;
2442 
2443  item._childs.push({ _name : info, _title: title, _kind: elem.fTypeName, _icon: (elem.fTypeName == 'BASE') ? "img_class" : "img_member" });
2444  }
2445  if (item._childs.length == 0) delete item._childs;
2446  }
2447 
2448  // painter.select_main().style('overflow','auto');
2449 
2450  painter.RefreshHtml(function() {
2451  painter.SetDivId(divid);
2452  painter.DrawingReady();
2453  });
2454 
2455  return painter;
2456  }
2457 
2458  // ======================================================================================
2459 
2460  JSROOT.Painter.drawInspector = function(divid, obj) {
2461 
2462  JSROOT.cleanup(divid);
2463 
2464  var painter = new JSROOT.HierarchyPainter('inspector', divid, 'white');
2465  painter.default_by_click = "expand"; // default action
2466  painter.with_icons = false;
2467  painter.h = { _name: "Object", _title: "", _click_action: "expand", _nosimple: false, _do_context: true };
2468  if ((typeof obj.fTitle === 'string') && (obj.fTitle.length>0))
2469  painter.h._title = obj.fTitle;
2470 
2471  if (painter.select_main().classed("jsroot_inspector"))
2472  painter.removeInspector = function() {
2473  this.select_main().remove();
2474  }
2475 
2476  if (obj._typename)
2477  painter.h._title += " type:" + obj._typename;
2478 
2479  if ((typeof obj.fName === 'string') && (obj.fName.length > 0))
2480  painter.h._name = obj.fName;
2481 
2482  // painter.select_main().style('overflow','auto');
2483 
2484  painter.fill_context = function(menu, hitem) {
2485  var sett = JSROOT.getDrawSettings(hitem._kind, 'nosame');
2486  if (sett.opts)
2487  menu.addDrawMenu("nosub:Draw", sett.opts, function(arg) {
2488  if (!hitem || !hitem._obj) return;
2489  var obj = hitem._obj, divid = this.divid;
2490  if (this.removeInspector) {
2491  divid = this.select_main().node().parentNode;
2492  this.removeInspector();
2493  if (arg == "inspect")
2494  return this.ShowInspector(obj);
2495  }
2496  JSROOT.cleanup(divid);
2497  JSROOT.draw(divid, obj, arg);
2498  });
2499  }
2500 
2501  if (JSROOT.IsRootCollection(obj)) {
2502  painter.h._name = obj.name || obj._typename;
2503  ListHierarchy(painter.h, obj);
2504  } else {
2505  ObjectHierarchy(painter.h, obj);
2506  }
2507  painter.RefreshHtml(function() {
2508  painter.SetDivId(divid);
2509  painter.DrawingReady();
2510  });
2511 
2512  return painter;
2513  }
2514 
2515  // ================================================================
2516 
2517  // MDIDisplay - class to manage multiple document interface for drawings
2518 
2519  function MDIDisplay(frameid) {
2520  JSROOT.TBasePainter.call(this);
2521  this.frameid = frameid;
2522  this.SetDivId(frameid);
2523  this.select_main().property('mdi', this);
2524  this.CleanupFrame = JSROOT.cleanup; // use standard cleanup function by default
2525  this.active_frame_title = ""; // keep title of active frame
2526  }
2527 
2528  MDIDisplay.prototype = Object.create(JSROOT.TBasePainter.prototype);
2529 
2530  MDIDisplay.prototype.BeforeCreateFrame = function(title) {
2531  this.active_frame_title = title;
2532  }
2533 
2534  MDIDisplay.prototype.ForEachFrame = function(userfunc, only_visible) {
2535  // method dedicated to iterate over existing panels
2536  // provided userfunc is called with arguments (frame)
2537 
2538  console.warn("ForEachFrame not implemented in MDIDisplay");
2539  }
2540 
2541  MDIDisplay.prototype.ForEachPainter = function(userfunc, only_visible) {
2542  // method dedicated to iterate over existing panles
2543  // provided userfunc is called with arguments (painter, frame)
2544 
2545  this.ForEachFrame(function(frame) {
2546  var dummy = new JSROOT.TObjectPainter();
2547  dummy.SetDivId(frame, -1);
2548  dummy.ForEachPainter(function(painter) { userfunc(painter, frame); });
2549  }, only_visible);
2550  }
2551 
2552  MDIDisplay.prototype.NumDraw = function() {
2553  var cnt = 0;
2554  this.ForEachFrame(function() { ++cnt; });
2555  return cnt;
2556  }
2557 
2558  MDIDisplay.prototype.FindFrame = function(searchtitle, force) {
2559  var found_frame = null;
2560 
2561  this.ForEachFrame(function(frame) {
2562  if (d3.select(frame).attr('frame_title') == searchtitle)
2563  found_frame = frame;
2564  });
2565 
2566  if ((found_frame == null) && force)
2567  found_frame = this.CreateFrame(searchtitle);
2568 
2569  return found_frame;
2570  }
2571 
2572  MDIDisplay.prototype.ActivateFrame = function(frame) {
2573  this.active_frame_title = d3.select(frame).attr('frame_title');
2574  }
2575 
2576  MDIDisplay.prototype.GetActiveFrame = function() {
2577  return this.FindFrame(this.active_frame_title);
2578  }
2579 
2580  MDIDisplay.prototype.CheckMDIResize = function(only_frame_id, size) {
2581  // perform resize for each frame
2582  var resized_frame = null;
2583 
2584  this.ForEachPainter(function(painter, frame) {
2585 
2586  if (only_frame_id && (d3.select(frame).attr('id') != only_frame_id)) return;
2587 
2588  if ((painter.GetItemName()!==null) && (typeof painter.CheckResize == 'function')) {
2589  // do not call resize for many painters on the same frame
2590  if (resized_frame === frame) return;
2591  painter.CheckResize(size);
2592  resized_frame = frame;
2593  }
2594  });
2595  }
2596 
2597  MDIDisplay.prototype.Reset = function() {
2598 
2599  this.active_frame_title = "";
2600 
2601  this.ForEachFrame(this.CleanupFrame);
2602 
2603  this.select_main().html("").property('mdi', null);
2604  }
2605 
2606  MDIDisplay.prototype.Draw = function(title, obj, drawopt) {
2607  // draw object with specified options
2608  if (!obj) return;
2609 
2610  if (!JSROOT.canDraw(obj._typename, drawopt)) return;
2611 
2612  var frame = this.FindFrame(title, true);
2613 
2614  this.ActivateFrame(frame);
2615 
2616  return JSROOT.redraw(frame, obj, drawopt);
2617  }
2618 
2619 
2620  // ==================================================
2621 
2622  function CustomDisplay() {
2623  JSROOT.MDIDisplay.call(this, "dummy");
2624  this.frames = {}; // array of configured frames
2625  }
2626 
2627  CustomDisplay.prototype = Object.create(MDIDisplay.prototype);
2628 
2629  CustomDisplay.prototype.AddFrame = function(divid, itemname) {
2630  if (!(divid in this.frames)) this.frames[divid] = "";
2631 
2632  this.frames[divid] += (itemname + ";");
2633  }
2634 
2635  CustomDisplay.prototype.ForEachFrame = function(userfunc, only_visible) {
2636  var ks = Object.keys(this.frames);
2637  for (var k = 0; k < ks.length; ++k) {
2638  var node = d3.select("#"+ks[k]);
2639  if (!node.empty())
2640  JSROOT.CallBack(userfunc, node.node());
2641  }
2642  }
2643 
2644  CustomDisplay.prototype.CreateFrame = function(title) {
2645 
2646  this.BeforeCreateFrame(title);
2647 
2648  var ks = Object.keys(this.frames);
2649  for (var k = 0; k < ks.length; ++k) {
2650  var items = this.frames[ks[k]];
2651  if (items.indexOf(title+";")>=0)
2652  return d3.select("#"+ks[k]).node();
2653  }
2654  return null;
2655  }
2656 
2657  CustomDisplay.prototype.Reset = function() {
2658  MDIDisplay.prototype.Reset.call(this);
2659  this.ForEachFrame(function(frame) {
2660  d3.select(frame).html("");
2661  });
2662  }
2663 
2664  // ================================================
2665 
2666  function GridDisplay(frameid, kind, kind2) {
2667  // following kinds are supported
2668  // vertical or horizontal - only first letter matters, defines basic orientation
2669  // 'x' in the name disable interactive separators
2670  // v4 or h4 - 4 equal elements in specified direction
2671  // v231 - created 3 vertical elements, first divided on 2, second on 3 and third on 1 part
2672  // v23_52 - create two vertical elements with 2 and 3 subitems, size ratio 5:2
2673  // gridNxM - normal grid layout without interactive separators
2674  // gridiNxM - grid layout with interactive separators
2675  // simple - no layout, full frame used for object drawings
2676 
2677  JSROOT.MDIDisplay.call(this, frameid);
2678 
2679  this.framecnt = 0;
2680  this.getcnt = 0;
2681  this.groups = [];
2682  this.vertical = kind && (kind[0] == 'v');
2683  this.use_separarators = !kind || (kind.indexOf("x")<0);
2684  this.simple_layout = false;
2685 
2686  this.select_main().style('overflow','hidden');
2687 
2688  if (kind === "simple") {
2689  this.simple_layout = true;
2690  this.use_separarators = false;
2691  this.framecnt = 1;
2692  return;
2693  }
2694 
2695  var num = 2, arr = undefined, sizes = undefined;
2696 
2697  if ((kind.indexOf("grid") == 0) || kind2) {
2698  if (kind2) kind = kind + "x" + kind2;
2699  else kind = kind.substr(4).trim();
2700  this.use_separarators = false;
2701  if (kind[0]==="i") {
2702  this.use_separarators = true;
2703  kind = kind.substr(1);
2704  }
2705 
2706  var separ = kind.indexOf("x"), sizex = 3, sizey = 3;
2707 
2708  if (separ > 0) {
2709  sizey = parseInt(kind.substr(separ + 1));
2710  sizex = parseInt(kind.substr(0, separ));
2711  } else {
2712  sizex = sizey = parseInt(kind);
2713  }
2714 
2715  if (isNaN(sizex)) sizex = 3;
2716  if (isNaN(sizey)) sizey = 3;
2717 
2718  if (sizey>1) {
2719  this.vertical = true;
2720  num = sizey;
2721  if (sizex>1) {
2722  arr = new Array(num);
2723  for (var k=0;k<num;++k) arr[k] = sizex;
2724  }
2725  } else
2726  if (sizex > 1) {
2727  this.vertical = false;
2728  num = sizex;
2729  } else {
2730  this.simple_layout = true;
2731  this.use_separarators = false;
2732  this.framecnt = 1;
2733  return;
2734  }
2735  kind = "";
2736  }
2737 
2738  if (kind && kind.indexOf("_")>0) {
2739  var arg = parseInt(kind.substr(kind.indexOf("_")+1), 10);
2740  if (!isNaN(arg) && (arg>10)) {
2741  kind = kind.substr(0, kind.indexOf("_"));
2742  sizes = [];
2743  while (arg>0) {
2744  sizes.unshift(Math.max(arg % 10, 1));
2745  arg = Math.round((arg-sizes[0])/10);
2746  if (sizes[0]===0) sizes[0]=1;
2747  }
2748  }
2749  }
2750 
2751  kind = kind ? parseInt(kind.replace( /^\D+/g, ''), 10) : 0;
2752  if (kind && (kind>1)) {
2753  if (kind<10) {
2754  num = kind;
2755  } else {
2756  arr = [];
2757  while (kind>0) {
2758  arr.unshift(kind % 10);
2759  kind = Math.round((kind-arr[0])/10);
2760  if (arr[0]==0) arr[0]=1;
2761  }
2762  num = arr.length;
2763  }
2764  }
2765 
2766  if (sizes && (sizes.length!==num)) sizes = undefined;
2767 
2768  if (!this.simple_layout)
2769  this.CreateGroup(this, this.select_main(), num, arr, sizes);
2770  }
2771 
2772  GridDisplay.prototype = Object.create(MDIDisplay.prototype);
2773 
2774  GridDisplay.prototype.CreateGroup = function(handle, main, num, childs, sizes) {
2775 
2776  if (!sizes) sizes = new Array(num);
2777  var sum1 = 0, sum2 = 0;
2778  for (var n=0;n<num;++n) sum1 += (sizes[n] || 1);
2779  for (var n=0;n<num;++n) {
2780  sizes[n] = Math.round(100 * (sizes[n] || 1) / sum1);
2781  sum2 += sizes[n];
2782  if (n==num-1) sizes[n] += (100-sum2); // make 100%
2783  }
2784 
2785  for (var cnt = 0; cnt<num; ++cnt) {
2786  var group = { id: cnt, drawid: -1, position: 0, size: sizes[cnt] };
2787  if (cnt>0) group.position = handle.groups[cnt-1].position + handle.groups[cnt-1].size;
2788  group.position0 = group.position;
2789 
2790  if (!childs || !childs[cnt] || childs[cnt]<2) group.drawid = this.framecnt++;
2791 
2792  handle.groups.push(group);
2793 
2794  var elem = main.append("div").attr('groupid', group.id);
2795 
2796  if (handle.vertical)
2797  elem.style('float', 'bottom').style('height',group.size+'%').style('width','100%');
2798  else
2799  elem.style('float', 'left').style('width',group.size+'%').style('height','100%');
2800 
2801  if (group.drawid>=0) {
2802  elem.classed('jsroot_newgrid', true);
2803  if (typeof this.frameid === 'string')
2804  elem.attr('id', this.frameid + "_" + group.drawid);
2805  } else {
2806  elem.style('display','flex').style('flex-direction', handle.vertical ? "row" : "column");
2807  }
2808 
2809  if (childs && (childs[cnt]>1)) {
2810  group.vertical = !handle.vertical;
2811  group.groups = [];
2812  elem.style('overflow','hidden');
2813  this.CreateGroup(group, elem, childs[cnt]);
2814  }
2815  }
2816 
2817  if (this.use_separarators && this.CreateSeparator)
2818  for (var cnt=1;cnt<num;++cnt)
2819  this.CreateSeparator(handle, main, handle.groups[cnt]);
2820  }
2821 
2822  GridDisplay.prototype.ForEachFrame = function(userfunc, only_visible) {
2823  if (this.simple_layout)
2824  userfunc(this.GetFrame());
2825  else
2826  this.select_main().selectAll('.jsroot_newgrid').each(function() {
2827  userfunc(d3.select(this).node());
2828  });
2829  }
2830 
2831  GridDisplay.prototype.GetActiveFrame = function() {
2832  if (this.simple_layout) return this.GetFrame();
2833 
2834  var found = MDIDisplay.prototype.GetActiveFrame.call(this);
2835  if (found) return found;
2836 
2837  this.ForEachFrame(function(frame) {
2838  if (!found) found = frame;
2839  }, true);
2840 
2841  return found;
2842  }
2843 
2844  GridDisplay.prototype.ActivateFrame = function(frame) {
2845  this.active_frame_title = d3.select(frame).attr('frame_title');
2846  }
2847 
2848  GridDisplay.prototype.GetFrame = function(id) {
2849  if (this.simple_layout)
2850  return this.select_main('origin').node();
2851  var res = null;
2852  this.select_main().selectAll('.jsroot_newgrid').each(function() {
2853  if (id-- === 0) res = this;
2854  });
2855  return res;
2856  }
2857 
2858  GridDisplay.prototype.NumGridFrames = function() {
2859  return this.framecnt;
2860  }
2861 
2862  GridDisplay.prototype.CreateFrame = function(title) {
2863  this.BeforeCreateFrame(title);
2864 
2865  var frame = null, maxloop = this.framecnt || 2;
2866 
2867  while (!frame && maxloop--) {
2868  frame = this.GetFrame(this.getcnt);
2869  if (!this.simple_layout && this.framecnt)
2870  this.getcnt = (this.getcnt+1) % this.framecnt;
2871 
2872  if (d3.select(frame).classed("jsroot_fixed_frame")) frame = null;
2873  }
2874 
2875  if (frame) {
2876  this.CleanupFrame(frame);
2877  d3.select(frame).attr('frame_title', title);
2878  }
2879 
2880  return frame;
2881  }
2882 
2883 
2884  // export all functions and classes
2885 
2886  JSROOT.Painter.drawList = drawList;
2887 
2888  JSROOT.Painter.FolderHierarchy = FolderHierarchy;
2889  JSROOT.Painter.ObjectHierarchy = ObjectHierarchy;
2890  JSROOT.Painter.TaskHierarchy = TaskHierarchy;
2891  JSROOT.Painter.ListHierarchy = ListHierarchy;
2892  JSROOT.Painter.KeysHierarchy = KeysHierarchy;
2893 
2894  JSROOT.BrowserLayout = BrowserLayout;
2895  JSROOT.HierarchyPainter = HierarchyPainter;
2896 
2897  JSROOT.MDIDisplay = MDIDisplay;
2898  JSROOT.CustomDisplay = CustomDisplay;
2899  JSROOT.GridDisplay = GridDisplay;
2900 
2901  return JSROOT;
2902 
2903 }));