otsdaq_utilities  v2_05_02_indev
JSRootPainter.jquery.js
1 
4 (function( factory ) {
5  if ( typeof define === "function" && define.amd ) {
6  define( ['jquery', 'jquery-ui', 'd3', 'JSRootPainter', 'JSRootPainter.hierarchy'], factory );
7  } else {
8 
9  if (typeof jQuery == 'undefined')
10  throw new Error('jQuery not defined', 'JSRootPainter.jquery.js');
11 
12  if (typeof jQuery.ui == 'undefined')
13  throw new Error('jQuery-ui not defined','JSRootPainter.jquery.js');
14 
15  if (typeof d3 != 'object')
16  throw new Error('This extension requires d3.v3.js', 'JSRootPainter.jquery.js');
17 
18  if (typeof JSROOT == 'undefined')
19  throw new Error('JSROOT is not defined', 'JSRootPainter.jquery.js');
20 
21  if (typeof JSROOT.Painter != 'object')
22  throw new Error('JSROOT.Painter not defined', 'JSRootPainter.jquery.js');
23 
24  factory(jQuery, jQuery.ui, d3, JSROOT);
25  }
26 } (function($, myui, d3, JSROOT) {
27 
28  "use strict";
29 
30  JSROOT.sources.push("jq2d");
31 
32  if ( typeof define === "function" && define.amd )
33  JSROOT.loadScript('$$$style/jquery-ui.css');
34 
35  JSROOT.Painter.createMenu = function(painter, maincallback) {
36  var menuname = 'root_ctx_menu';
37 
38  if (!maincallback && typeof painter==='function') { maincallback = painter; painter = null; }
39 
40  var menu = { painter: painter, element: null, code: "", cnt: 1, funcs: {}, separ: false };
41 
42  menu.add = function(name, arg, func, title) {
43  if (name == "separator") { this.code += "<li>-</li>"; this.separ = true; return; }
44 
45  if (name.indexOf("header:")==0) {
46  this.code += "<li class='ui-widget-header' style='padding:3px; padding-left:5px;'>"+name.substr(7)+"</li>";
47  return;
48  }
49 
50  if (name=="endsub:") { this.code += "</ul></li>"; return; }
51 
52  var item = "", close_tag = "</li>";
53  title = title ? " title='" + title + "'" : "";
54  if (name.indexOf("sub:")==0) { name = name.substr(4); close_tag = "<ul>"; }
55 
56  if (typeof arg == 'function') { func = arg; arg = name; }
57 
58  if (name.indexOf("chk:")==0) { item = "<span class='ui-icon ui-icon-check' style='margin:1px'></span>"; name = name.substr(4); } else
59  if (name.indexOf("unk:")==0) { item = "<span class='ui-icon ui-icon-blank' style='margin:1px'></span>"; name = name.substr(4); }
60 
61  // special handling of first versions with menu support
62  if (($.ui.version.indexOf("1.10")==0) || ($.ui.version.indexOf("1.9")==0))
63  item = '<a href="#">' + item + name + '</a>';
64  else if ($.ui.version.indexOf("1.11")==0)
65  item += name;
66  else
67  item = "<div" + title + ">" + item + name + "</div>";
68 
69  this.code += "<li cnt='" + this.cnt + ((arg !== undefined) ? "' arg='" + arg : "") + "'>" + item + close_tag;
70  if (typeof func == 'function') this.funcs[this.cnt] = func; // keep call-back function
71 
72  this.cnt++;
73  }
74 
75  menu.addchk = function(flag, name, arg, func) {
76  return this.add((flag ? "chk:" : "unk:") + name, arg, func);
77  }
78 
79  menu.size = function() { return this.cnt-1; }
80 
81  menu.addDrawMenu = function(menu_name, opts, call_back) {
82  if (!opts) opts = [];
83  if (opts.length==0) opts.push("");
84 
85  var without_sub = false;
86  if (menu_name.indexOf("nosub:")==0) {
87  without_sub = true;
88  menu_name = menu_name.substr(6);
89  }
90 
91  if (opts.length === 1) {
92  if (opts[0]==='inspect') menu_name = menu_name.replace("Draw", "Inspect");
93  return this.add(menu_name, opts[0], call_back);
94  }
95 
96  if (!without_sub) this.add("sub:" + menu_name, opts[0], call_back);
97 
98  for (var i=0;i<opts.length;++i) {
99  var name = opts[i];
100  if (name=="") name = '&lt;dflt&gt;';
101 
102  var group = i+1;
103  if ((opts.length>5) && (name.length>0)) {
104  // check if there are similar options, which can be grouped once again
105  while ((group<opts.length) && (opts[group].indexOf(name)==0)) group++;
106  }
107 
108  if (without_sub) name = menu_name + " " + name;
109 
110  if (group < i+2) {
111  this.add(name, opts[i], call_back);
112  } else {
113  this.add("sub:" + name, opts[i], call_back);
114  for (var k=i+1;k<group;++k)
115  this.add(opts[k], opts[k], call_back);
116  this.add("endsub:");
117  i = group-1;
118  }
119  }
120  if (!without_sub) this.add("endsub:");
121  }
122 
123  menu.remove = function() {
124  if (this.element!==null) {
125  this.element.remove();
126  if (this.close_callback) this.close_callback();
127  document.body.removeEventListener('click', this.remove_bind);
128  }
129  this.element = null;
130  }
131 
132  menu.remove_bind = menu.remove.bind(menu);
133 
134  menu.show = function(event, close_callback) {
135  this.remove();
136 
137  if (typeof close_callback == 'function') this.close_callback = close_callback;
138 
139  document.body.addEventListener('click', this.remove_bind);
140 
141  var oldmenu = document.getElementById(menuname);
142  if (oldmenu) oldmenu.parentNode.removeChild(oldmenu);
143 
144  $(document.body).append('<ul class="jsroot_ctxmenu">' + this.code + '</ul>');
145 
146  this.element = $('.jsroot_ctxmenu');
147 
148  var pthis = this;
149 
150  this.element
151  .attr('id', menuname)
152  .css('left', event.clientX + window.pageXOffset)
153  .css('top', event.clientY + window.pageYOffset)
154 // .css('font-size', '80%')
155  .css('position', 'absolute') // this overrides ui-menu-items class property
156  .menu({
157  items: "> :not(.ui-widget-header)",
158  select: function( event, ui ) {
159  var arg = ui.item.attr('arg'),
160  cnt = ui.item.attr('cnt'),
161  func = cnt ? pthis.funcs[cnt] : null;
162  pthis.remove();
163  if (typeof func == 'function') {
164  if ('painter' in menu)
165  func.bind(pthis.painter)(arg); // if 'painter' field set, returned as this to callback
166  else
167  func(arg);
168  }
169  }
170  });
171 
172  var newx = null, newy = null;
173 
174  if (event.clientX + this.element.width() > $(window).width()) newx = $(window).width() - this.element.width() - 20;
175  if (event.clientY + this.element.height() > $(window).height()) newy = $(window).height() - this.element.height() - 20;
176 
177  if (newx!==null) this.element.css('left', (newx>0 ? newx : 0) + window.pageXOffset);
178  if (newy!==null) this.element.css('top', (newy>0 ? newy : 0) + window.pageYOffset);
179  }
180 
181  JSROOT.CallBack(maincallback, menu);
182 
183  return menu;
184  }
185 
186  // =================================================================================================
187 
188  var BrowserLayout = JSROOT.BrowserLayout;
189 
192  BrowserLayout.prototype.SetBrowserTitle = function(title) {
193  var main = d3.select("#" + this.gui_div + " .jsroot_browser");
194  if (!main.empty())
195  main.select(".jsroot_browser_title").text(title);
196  }
197 
198  BrowserLayout.prototype.ToggleBrowserKind = function(kind) {
199 
200  if (!this.gui_div) return;
201 
202  if (!kind) {
203  if (!this.browser_kind) return;
204  kind = (this.browser_kind === "float") ? "fix" : "float";
205  }
206 
207  var main = d3.select("#"+this.gui_div+" .jsroot_browser"),
208  jmain = $(main.node()),
209  area = jmain.find(".jsroot_browser_area"),
210  pthis = this;
211 
212  if (this.browser_kind === "float") {
213  area.css('bottom', '0px')
214  .css('top', '0px')
215  .css('width','').css('height','')
216  .toggleClass('jsroot_float_browser', false)
217  .resizable("destroy")
218  .draggable("destroy");
219  } else if (this.browser_kind === "fix") {
220  main.select(".jsroot_v_separator").remove();
221  area.css('left', '0px');
222  d3.select("#"+this.gui_div+"_drawing").style('left','0px'); // reset size
223  main.select(".jsroot_h_separator").style('left','0px');
224  d3.select("#"+this.gui_div+"_status").style('left','0px'); // reset left
225  pthis.CheckResize();
226  }
227 
228  this.browser_kind = kind;
229  this.browser_visible = true;
230 
231  if (kind==="float") {
232  area.css('bottom', '40px')
233  .toggleClass('jsroot_float_browser', true)
234  .resizable({
235  containment: "parent",
236  minWidth: 100,
237  resize: function( event, ui ) {
238  pthis.SetButtonsPosition();
239  },
240  stop: function( event, ui ) {
241  var bottom = $(this).parent().innerHeight() - ui.position.top - ui.size.height;
242  if (bottom<7) $(this).css('height', "").css('bottom', 0);
243  }
244  })
245  .draggable({
246  containment: "parent",
247  handle : $("#"+this.gui_div).find(".jsroot_browser_title"),
248  snap: true,
249  snapMode: "inner",
250  snapTolerance: 10,
251  drag: function( event, ui ) {
252  pthis.SetButtonsPosition();
253  },
254  stop: function( event, ui ) {
255  var bottom = $(this).parent().innerHeight() - $(this).offset().top - $(this).outerHeight();
256  if (bottom<7) $(this).css('height', "").css('bottom', 0);
257  }
258  });
259  this.AdjustBrowserSize();
260 
261  } else {
262 
263  area.css('left',0).css('top',0).css('bottom',0).css('height','');
264 
265  var vsepar =
266  main.append('div')
267  .classed("jsroot_separator", true).classed('jsroot_v_separator', true)
268  .style('position', 'absolute').style('top',0).style('bottom',0);
269  // creation of vertical separator
270  $(vsepar.node()).draggable({
271  axis: "x" , cursor: "ew-resize",
272  containment: "parent",
273  helper : function() { return $(this).clone().css('background-color','grey'); },
274  drag: function(event,ui) {
275  pthis.SetButtonsPosition();
276  pthis.AdjustSeparator(ui.position.left, null);
277  },
278  stop: function(event,ui) {
279  pthis.CheckResize();
280  }
281  });
282 
283  this.AdjustSeparator(250, null, true, true);
284  }
285 
286  this.SetButtonsPosition();
287  }
288 
289  BrowserLayout.prototype.SetButtonsPosition = function() {
290  if (!this.gui_div) return;
291 
292  var jmain = $("#"+this.gui_div+" .jsroot_browser"),
293  btns = jmain.find(".jsroot_browser_btns"),
294  top = 7, left = 7;
295 
296  if (!btns.length) return;
297 
298  if (this.browser_visible) {
299  var area = jmain.find(".jsroot_browser_area"),
300  off0 = jmain.offset(), off1 = area.offset();
301  top = off1.top - off0.top + 7;
302  left = off1.left - off0.left + area.innerWidth() - 27;
303  }
304 
305  btns.css('left', left+'px').css('top', top+'px');
306  }
307 
308  BrowserLayout.prototype.AdjustBrowserSize = function(onlycheckmax) {
309  if (!this.gui_div || (this.browser_kind !== "float")) return;
310 
311  var jmain = $("#" + this.gui_div + " .jsroot_browser");
312  if (!jmain.length) return;
313 
314  var area = jmain.find(".jsroot_browser_area"),
315  cont = jmain.find(".jsroot_browser_hierarchy"),
316  chld = cont.children(":first");
317 
318  if (onlycheckmax) {
319  if (area.parent().innerHeight() - 10 < area.innerHeight())
320  area.css('bottom', '0px').css('top','0px');
321  return;
322  }
323 
324  if (!chld.length) return;
325 
326  var h1 = cont.innerHeight(),
327  h2 = chld.innerHeight();
328 
329  if ((h2!==undefined) && (h2<h1*0.7)) area.css('bottom', '');
330  }
331 
332  BrowserLayout.prototype.ToggleBrowserVisisbility = function(fast_close) {
333  if (!this.gui_div || (typeof this.browser_visible==='string')) return;
334 
335  var main = d3.select("#" + this.gui_div + " .jsroot_browser"),
336  area = main.select('.jsroot_browser_area');
337 
338  if (area.empty()) return;
339 
340  var vsepar = main.select(".jsroot_v_separator"),
341  drawing = d3.select("#" + this.gui_div + "_drawing"),
342  tgt = area.property('last_left'),
343  tgt_separ = area.property('last_vsepar'),
344  tgt_drawing = area.property('last_drawing');
345 
346  if (!this.browser_visible) {
347  if (fast_close) return;
348  area.property('last_left', null).property('last_vsepar',null).property('last_drawing', null);
349  } else {
350  area.property('last_left', area.style('left'));
351  if (!vsepar.empty()) {
352  area.property('last_vsepar', vsepar.style('left'));
353  area.property('last_drawing', drawing.style('left'));
354  }
355  tgt = (-$(area.node()).outerWidth(true)-10).toString() + "px";
356  var mainw = $(main.node()).outerWidth(true);
357 
358  if (vsepar.empty() && ($(area.node()).offset().left > mainw/2)) tgt = (mainw+10) + "px";
359 
360  tgt_separ = "-10px";
361  tgt_drawing = "0px";
362  }
363 
364  var pthis = this, visible_at_the_end = !this.browser_visible, _duration = fast_close ? 0 : 700;
365 
366  this.browser_visible = 'changing';
367 
368  area.transition().style('left', tgt).duration(_duration).on("end", function() {
369  if (fast_close) return;
370  pthis.browser_visible = visible_at_the_end;
371  if (visible_at_the_end) pthis.SetButtonsPosition();
372  });
373 
374  if (!visible_at_the_end)
375  main.select(".jsroot_browser_btns").transition().style('left', '7px').style('top', '7px').duration(_duration);
376 
377  if (!vsepar.empty()) {
378  vsepar.transition().style('left', tgt_separ).duration(_duration);
379  drawing.transition().style('left', tgt_drawing).duration(_duration).on("end", this.CheckResize.bind(this));
380  }
381 
382  if (this.status_layout && (this.browser_kind == 'fix')) {
383  main.select(".jsroot_h_separator").transition().style('left', tgt_drawing).duration(_duration);
384  main.select(".jsroot_status_area").transition().style('left', tgt_drawing).duration(_duration);
385  }
386  }
387 
389  BrowserLayout.prototype.Toggle = function(browser_kind) {
390  if (this.browser_visible!=='changing') {
391  if (browser_kind === this.browser_kind) this.ToggleBrowserVisisbility();
392  else this.ToggleBrowserKind(browser_kind);
393  }
394  }
395 
396  BrowserLayout.prototype.DeleteContent = function() {
397  var main = d3.select("#" + this.gui_div + " .jsroot_browser");
398  if (main.empty()) return;
399 
400  this.CreateStatusLine("delete");
401  var vsepar = main.select(".jsroot_v_separator");
402  if (!vsepar.empty())
403  $(vsepar.node()).draggable('destroy');
404 
405  this.ToggleBrowserVisisbility(true);
406 
407  main.selectAll("*").remove();
408  delete this.browser_visible;
409  delete this.browser_kind;
410 
411  this.CheckResize();
412  }
413 
415  BrowserLayout.prototype.CreateStatusLine = function(height, mode) {
416  var main = d3.select("#"+this.gui_div+" .jsroot_browser");
417  if (main.empty()) return '';
418 
419  var id = this.gui_div + "_status",
420  line = d3.select("#"+id), skip_height_check = false,
421  is_visible = !line.empty();
422 
423  if (mode==="toggle") { mode = !is_visible; skip_height_check = (height === this.last_hsepar_height); } else
424  if (height==="delete") { mode = false; height = 0; delete this.status_layout; } else
425  if (mode===undefined) { mode = true; this.status_layout = "app"; }
426 
427  if (is_visible) {
428  if ((mode === true) || (this.status_layout==="app")) return id;
429 
430  var hsepar = main.select(".jsroot_h_separator");
431 
432  $(hsepar.node()).draggable("destroy");
433 
434  hsepar.remove();
435  line.remove();
436 
437  delete this.status_layout;
438 
439  if (this.status_handler && (JSROOT.Painter.ShowStatus === this.status_handler)) {
440  delete JSROOT.Painter.ShowStatus;
441  delete this.status_handler;
442  }
443 
444  this.AdjustSeparator(null, 0, true);
445  return "";
446  }
447 
448  if (mode === false) return "";
449 
450  var left_pos = d3.select("#" + this.gui_div + "_drawing").style('left');
451 
452  line = main.insert("div",".jsroot_browser_area").attr("id",id)
453  .classed("jsroot_status_area", true)
454  .style('position',"absolute").style('left',left_pos).style('height',"20px").style('bottom',0).style('right',0)
455  .style('margin',0).style('border',0);
456 
457  var hsepar = main.insert("div",".jsroot_browser_area")
458  .classed("jsroot_separator", true).classed("jsroot_h_separator", true)
459  .style('position','absolute').style('left',left_pos).style('right',0).style('bottom','20px').style('height','5px');
460 
461  var pthis = this;
462 
463  $(hsepar.node()).draggable({
464  axis: "y" , cursor: "ns-resize", containment: "parent",
465  helper: function() { return $(this).clone().css('background-color','grey'); },
466  drag: function(event,ui) {
467  pthis.AdjustSeparator(null, -ui.position.top);
468  },
469  stop: function(event,ui) {
470  pthis.CheckResize();
471  }
472  });
473 
474  if (!height || (typeof height === 'string')) height = this.last_hsepar_height || 20;
475 
476  this.AdjustSeparator(null, height, true);
477 
478  if (this.status_layout == "app") return id;
479 
480  this.status_layout = new JSROOT.GridDisplay(id, 'horizx4_1213');
481 
482  var frame_titles = ['object name','object title','mouse coordinates','object info'];
483  for (var k=0;k<4;++k)
484  d3.select(this.status_layout.GetFrame(k)).attr('title', frame_titles[k]).style('overflow','hidden')
485  .append("label").attr("class","jsroot_status_label");
486 
487  this.status_handler = this.ShowStatus.bind(this);
488 
489  JSROOT.Painter.ShowStatus = this.status_handler;
490 
491  return id;
492  }
493 
494  BrowserLayout.prototype.AdjustSeparator = function(vsepar, hsepar, redraw, first_time) {
495 
496  if (!this.gui_div) return;
497 
498  var main = d3.select("#" + this.gui_div + " .jsroot_browser"), w = 5;
499 
500  if ((hsepar===null) && first_time && !main.select(".jsroot_h_separator").empty()) {
501  // if separator set for the first time, check if status line present
502  hsepar = main.select(".jsroot_h_separator").style('bottom');
503  if ((typeof hsepar=='string') && (hsepar.indexOf('px')==hsepar.length-2))
504  hsepar = hsepar.substr(0,hsepar.length-2);
505  else
506  hsepar = null;
507  }
508 
509  if (hsepar!==null) {
510  hsepar = parseInt(hsepar);
511  var elem = main.select(".jsroot_h_separator"), hlimit = 0;
512 
513  if (!elem.empty()) {
514  if (hsepar<0) hsepar += ($(main.node()).outerHeight(true) - w);
515  if (hsepar<5) hsepar = 5;
516  this.last_hsepar_height = hsepar;
517  elem.style('bottom', hsepar+'px').style('height', w+'px');
518  d3.select("#" + this.gui_div + "_status").style('height', hsepar+'px');
519  hlimit = (hsepar+w) + 'px';
520  }
521 
522  d3.select("#" + this.gui_div + "_drawing").style('bottom',hlimit);
523  }
524 
525  if (vsepar!==null) {
526  vsepar = parseInt(vsepar);
527  if (vsepar<50) vsepar = 50;
528  main.select(".jsroot_browser_area").style('width',(vsepar-5)+'px');
529  d3.select("#" + this.gui_div + "_drawing").style('left',(vsepar+w)+'px');
530  main.select(".jsroot_h_separator").style('left', (vsepar+w)+'px');
531  d3.select("#" + this.gui_div + "_status").style('left',(vsepar+w)+'px');
532  main.select(".jsroot_v_separator").style('left',vsepar+'px').style('width',w+"px");
533  }
534 
535  if (redraw) this.CheckResize();
536  }
537 
538  BrowserLayout.prototype.ShowStatus = function(name, title, info, coordinates) {
539  if (!this.status_layout) return;
540 
541  $(this.status_layout.GetFrame(0)).children('label').text(name || "");
542  $(this.status_layout.GetFrame(1)).children('label').text(title || "");
543  $(this.status_layout.GetFrame(2)).children('label').text(coordinates || "");
544  $(this.status_layout.GetFrame(3)).children('label').text(info || "");
545 
546  if (!this.status_layout.first_check) {
547  this.status_layout.first_check = true;
548  var maxh = 0;
549  for (var n=0;n<4;++n)
550  maxh = Math.max(maxh, $(this.status_layout.GetFrame(n)).children('label').outerHeight());
551  if ((maxh>5) && ((maxh>this.last_hsepar_height) || (maxh<this.last_hsepar_height+5)))
552  this.AdjustSeparator(null, maxh, true);
553  }
554  }
555 
556  // =================================================================================================
557 
558  var HierarchyPainter = JSROOT.HierarchyPainter;
559 
560  HierarchyPainter.prototype.isLastSibling = function(hitem) {
561  if (!hitem || !hitem._parent || !hitem._parent._childs) return false;
562  var chlds = hitem._parent._childs, indx = chlds.indexOf(hitem);
563  if (indx<0) return false;
564  while (++indx < chlds.length)
565  if (!('_hidden' in chlds[indx])) return false;
566  return true;
567  }
568 
569  HierarchyPainter.prototype.addItemHtml = function(hitem, d3prnt, arg) {
570 
571  if (!hitem || ('_hidden' in hitem)) return true;
572 
573  var isroot = (hitem === this.h),
574  has_childs = ('_childs' in hitem),
575  handle = JSROOT.getDrawHandle(hitem._kind),
576  img1 = "", img2 = "", can_click = false, break_list = false,
577  d3cont, itemname = this.itemFullName(hitem);
578 
579  if (handle !== null) {
580  if ('icon' in handle) img1 = handle.icon;
581  if ('icon2' in handle) img2 = handle.icon2;
582  if ((img1.length==0) && (typeof handle.icon_get == 'function'))
583  img1 = handle.icon_get(hitem, this);
584  if (('func' in handle) || ('execute' in handle) || ('aslink' in handle) ||
585  (('expand' in handle) && (hitem._more !== false))) can_click = true;
586  }
587 
588  if ('_icon' in hitem) img1 = hitem._icon;
589  if ('_icon2' in hitem) img2 = hitem._icon2;
590  if ((img1.length==0) && ('_online' in hitem))
591  hitem._icon = img1 = "img_globe";
592  if ((img1.length==0) && isroot)
593  hitem._icon = img1 = "img_base";
594 
595  if (hitem._more || ('_expand' in hitem) || ('_player' in hitem))
596  can_click = true;
597 
598  var can_menu = can_click;
599  if (!can_menu && (typeof hitem._kind == 'string') && (hitem._kind.indexOf("ROOT.")==0))
600  can_menu = can_click = true;
601 
602  if (img2.length==0) img2 = img1;
603  if (img1.length==0) img1 = (has_childs || hitem._more) ? "img_folder" : "img_page";
604  if (img2.length==0) img2 = (has_childs || hitem._more) ? "img_folderopen" : "img_page";
605 
606  if (arg === "update") {
607  d3prnt.selectAll("*").remove();
608  d3cont = d3prnt;
609  } else {
610  d3cont = d3prnt.append("div");
611  if (arg && (arg >= (hitem._parent._show_limit || JSROOT.gStyle.HierarchyLimit))) break_list = true;
612  }
613 
614  hitem._d3cont = d3cont.node(); // set for direct referencing
615  d3cont.attr("item", itemname);
616 
617  // line with all html elements for this item (excluding childs)
618  var d3line = d3cont.append("div").attr('class','h_line');
619 
620  // build indent
621  var prnt = isroot ? null : hitem._parent;
622  while (prnt && (prnt !== this.h)) {
623  d3line.insert("div",":first-child")
624  .attr("class", this.isLastSibling(prnt) ? "img_empty" : "img_line");
625  prnt = prnt._parent;
626  }
627 
628  var icon_class = "", plusminus = false;
629 
630  if (isroot) {
631  // for root node no extra code
632  } else
633  if (has_childs && !break_list) {
634  icon_class = hitem._isopen ? "img_minus" : "img_plus";
635  plusminus = true;
636  } else
637  /*if (hitem._more) {
638  icon_class = "img_plus"; // should be special plus ???
639  plusminus = true;
640  } else */ {
641  icon_class = "img_join";
642  }
643 
644  var h = this;
645 
646  if (icon_class.length > 0) {
647  if (break_list || this.isLastSibling(hitem)) icon_class += "bottom";
648  var d3icon = d3line.append("div").attr('class', icon_class);
649  if (plusminus) d3icon.style('cursor','pointer')
650  .on("click", function() { h.tree_click(this, "plusminus"); });
651  }
652 
653  // make node icons
654 
655  if (this.with_icons && !break_list) {
656  var icon_name = hitem._isopen ? img2 : img1;
657 
658  var d3img;
659 
660  if (icon_name.indexOf("img_")==0)
661  d3img = d3line.append("div")
662  .attr("class", icon_name)
663  .attr("title", hitem._kind);
664  else
665  d3img = d3line.append("img")
666  .attr("src", icon_name)
667  .attr("alt","")
668  .attr("title", hitem._kind)
669  .style('vertical-align','top').style('width','18px').style('height','18px');
670 
671  if (('_icon_click' in hitem) || (handle && ('icon_click' in handle)))
672  d3img.on("click", function() { h.tree_click(this, "icon"); });
673  }
674 
675  var d3a = d3line.append("a");
676  if (can_click || has_childs || break_list)
677  d3a.attr("class","h_item")
678  .on("click", function() { h.tree_click(this); });
679 
680  if (break_list) {
681  hitem._break_point = true; // indicate that list was broken here
682  d3a.attr('title', 'there are ' + (hitem._parent._childs.length-arg) + ' more items')
683  .text("...more...");
684  return false;
685  }
686 
687  if ('disp_kind' in h) {
688  if (JSROOT.gStyle.DragAndDrop && can_click)
689  this.enable_dragging(d3a.node(), itemname);
690  if (JSROOT.gStyle.ContextMenu && can_menu)
691  d3a.on('contextmenu', function() { h.tree_contextmenu(this); });
692 
693  d3a.on("mouseover", function() { h.tree_mouseover(true, this); })
694  .on("mouseleave", function() { h.tree_mouseover(false, this); });
695  } else
696  if (hitem._direct_context && JSROOT.gStyle.ContextMenu)
697  d3a.on('contextmenu', function() { h.direct_contextmenu(this); });
698 
699  var element_name = hitem._name, element_title = "";
700 
701  if ('_realname' in hitem)
702  element_name = hitem._realname;
703 
704  if ('_title' in hitem)
705  element_title = hitem._title;
706 
707  if ('_fullname' in hitem)
708  element_title += " fullname: " + hitem._fullname;
709 
710  if (!element_title)
711  element_title = element_name;
712 
713  d3a.attr('title', element_title)
714  .text(element_name + ('_value' in hitem ? ":" : ""))
715  .style('background', hitem._background ? hitem._background : null);
716 
717  if ('_value' in hitem) {
718  var d3p = d3line.append("p");
719  if ('_vclass' in hitem) d3p.attr('class', hitem._vclass);
720  if (!hitem._isopen) d3p.html(hitem._value);
721  }
722 
723  if (has_childs && (isroot || hitem._isopen)) {
724  var d3chlds = d3cont.append("div").attr("class", "h_childs");
725  for (var i=0; i< hitem._childs.length; ++i) {
726  var chld = hitem._childs[i];
727  chld._parent = hitem;
728  if (!this.addItemHtml(chld, d3chlds, i)) break; // if too many items, skip rest
729  }
730  }
731 
732  return true;
733  }
734 
735  HierarchyPainter.prototype.toggleOpenState = function(isopen, h) {
736  var hitem = h ? h : this.h;
737 
738  if (!('_childs' in hitem)) {
739  if (!isopen || this.with_icons || (!hitem._expand && (hitem._more !== true))) return false;
740  this.expand(this.itemFullName(hitem));
741  if (hitem._childs) hitem._isopen = true;
742  return true;
743  }
744 
745  if ((hitem != this.h) && isopen && !hitem._isopen) {
746  // when there are childs and they are not see, simply show them
747  hitem._isopen = true;
748  return true;
749  }
750 
751  var change_child = false;
752  for (var i=0; i < hitem._childs.length; ++i)
753  if (this.toggleOpenState(isopen, hitem._childs[i])) change_child = true;
754 
755  if ((hitem != this.h) && !isopen && hitem._isopen && !change_child) {
756  // if none of the childs can be closed, than just close that item
757  delete hitem._isopen;
758  return true;
759  }
760 
761  if (!h) this.RefreshHtml();
762 
763  return false;
764  }
765 
766  HierarchyPainter.prototype.RefreshHtml = function(callback) {
767 
768  if (!this.divid) return JSROOT.CallBack(callback);
769 
770  var d3elem = this.select_main();
771 
772  d3elem.html("")
773  .style('overflow','hidden') // clear html - most simple way
774  .style('display','flex')
775  .style('flex-direction','column');
776 
777  var h = this, factcmds = [], status_item = null;
778  this.ForEach(function(item) {
779  delete item._d3cont; // remove html container
780  if (('_fastcmd' in item) && (item._kind == 'Command')) factcmds.push(item);
781  if (('_status' in item) && !status_item) status_item = item;
782  });
783 
784  if (!this.h || d3elem.empty())
785  return JSROOT.CallBack(callback);
786 
787  if (factcmds.length) {
788  var fastbtns = d3elem.append("div").attr("class","jsroot");
789  for (var n=0;n<factcmds.length;++n) {
790  var btn = fastbtns.append("button")
791  .text("")
792  .attr("class",'fast_command')
793  .attr("item", this.itemFullName(factcmds[n]))
794  .attr("title", factcmds[n]._title)
795  .on("click", function() { h.ExecuteCommand(d3.select(this).attr("item"), this); } );
796 
797  if ('_icon' in factcmds[n])
798  btn.append('img').attr("src", factcmds[n]._icon);
799  }
800  }
801 
802  var d3btns = d3elem.append("p").attr("class", "jsroot").style("margin-bottom","3px").style("margin-top",0);
803  d3btns.append("a").attr("class", "h_button").text("open all")
804  .attr("title","open all items in the browser").on("click", h.toggleOpenState.bind(h,true));
805  d3btns.append("text").text(" | ");
806  d3btns.append("a").attr("class", "h_button").text("close all")
807  .attr("title","close all items in the browser").on("click", h.toggleOpenState.bind(h,false));
808 
809  if (typeof h.removeInspector == 'function') {
810  d3btns.append("text").text(" | ");
811  d3btns.append("a").attr("class", "h_button").text("remove")
812  .attr("title","remove inspector").on("click", h.removeInspector.bind(h));
813  }
814 
815  if ('_online' in this.h) {
816  d3btns.append("text").text(" | ");
817  d3btns.append("a").attr("class", "h_button").text("reload")
818  .attr("title","reload object list from the server").on("click", h.reload.bind(h));
819  }
820 
821  if ('disp_kind' in this) {
822  d3btns.append("text").text(" | ");
823  d3btns.append("a").attr("class", "h_button").text("clear")
824  .attr("title","clear all drawn objects").on("click", h.clear.bind(h,false));
825  }
826 
827  var maindiv =
828  d3elem.append("div")
829  .attr("class", "jsroot")
830  .style('font-size', this.with_icons ? "12px" : "15px")
831  .style("overflow","auto")
832  .style("flex","1");
833 
834  if (this.background) // case of object inspector and streamer infos display
835  maindiv.style("background-color", this.background)
836  .style('margin', '2px').style('padding', '2px');
837 
838  this.addItemHtml(this.h, maindiv.append("div").attr("class","h_tree"));
839 
840  if (status_item && !this.status_disabled && (JSROOT.GetUrlOption('nostatus')===null)) {
841  var func = JSROOT.findFunction(status_item._status);
842  var hdiv = (typeof func == 'function') ? this.CreateStatusLine() : null;
843  if (hdiv) func(hdiv, this.itemFullName(status_item));
844  }
845 
846  JSROOT.CallBack(callback);
847  }
848 
849  HierarchyPainter.prototype.UpdateTreeNode = function(hitem, d3cont) {
850  if ((d3cont===undefined) || d3cont.empty()) {
851  d3cont = d3.select(hitem._d3cont ? hitem._d3cont : null);
852  var name = this.itemFullName(hitem);
853  if (d3cont.empty())
854  d3cont = this.select_main().select("[item='" + name + "']");
855  if (d3cont.empty() && ('_cycle' in hitem))
856  d3cont = this.select_main().select("[item='" + name + ";" + hitem._cycle + "']");
857  if (d3cont.empty()) return;
858  }
859 
860  this.addItemHtml(hitem, d3cont, "update");
861 
862  if (this.brlayout) this.brlayout.AdjustBrowserSize(true);
863  }
864 
865  HierarchyPainter.prototype.UpdateBackground = function(hitem, scroll_into_view) {
866 
867  if (!hitem || !hitem._d3cont) return;
868 
869  var d3cont = d3.select(hitem._d3cont);
870 
871  if (d3cont.empty()) return;
872 
873  var d3a = d3cont.select(".h_item");
874 
875  d3a.style('background', hitem._background ? hitem._background : null);
876 
877  if (scroll_into_view && hitem._background)
878  d3a.node().scrollIntoView(false);
879  }
880 
882  HierarchyPainter.prototype.tree_click = function(node, place) {
883  if (!node) return;
884 
885  var d3cont = d3.select(node.parentNode.parentNode),
886  itemname = d3cont.attr('item'),
887  hitem = itemname ? this.Find(itemname) : null;
888  if (!hitem) return;
889 
890  if (hitem._break_point) {
891  // special case of more item
892 
893  delete hitem._break_point;
894 
895  // update item itself
896  this.addItemHtml(hitem, d3cont, "update");
897 
898  var prnt = hitem._parent, indx = prnt._childs.indexOf(hitem),
899  d3chlds = d3.select(d3cont.node().parentNode);
900 
901  if (indx<0) return console.error('internal error');
902 
903  prnt._show_limit = (prnt._show_limit || JSROOT.gStyle.HierarchyLimit) * 2;
904 
905  for (var n=indx+1;n<prnt._childs.length;++n) {
906  var chld = prnt._childs[n];
907  chld._parent = prnt;
908  if (!this.addItemHtml(chld, d3chlds, n)) break; // if too many items, skip rest
909  }
910 
911  return;
912  }
913 
914  var prnt = hitem, dflt = undefined;
915  while (prnt) {
916  if ((dflt = prnt._click_action) !== undefined) break;
917  prnt = prnt._parent;
918  }
919 
920  if (!place || (place=="")) place = "item";
921 
922  var sett = JSROOT.getDrawSettings(hitem._kind), handle = sett.handle;
923 
924  if (place == "icon") {
925  var func = null;
926  if (typeof hitem._icon_click == 'function') func = hitem._icon_click; else
927  if (handle && typeof handle.icon_click == 'function') func = handle.icon_click;
928  if (func && func(hitem,this))
929  this.UpdateTreeNode(hitem, d3cont);
930  return;
931  }
932 
933  // special feature - all items with '_expand' function are not drawn by click
934  if ((place=="item") && ('_expand' in hitem) && !d3.event.ctrlKey && !d3.event.shiftKey) place = "plusminus";
935 
936  // special case - one should expand item
937  if (((place == "plusminus") && !('_childs' in hitem) && hitem._more) ||
938  ((place == "item") && (dflt === "expand"))) {
939  return this.expand(itemname, null, d3cont);
940  }
941 
942  if (place == "item") {
943 
944  if ('_player' in hitem)
945  return this.player(itemname);
946 
947  if (handle && handle.aslink)
948  return window.open(itemname + "/");
949 
950  if (handle && handle.execute)
951  return this.ExecuteCommand(itemname, node.parentNode);
952 
953  if (handle && handle.ignore_online && this.isOnlineItem(hitem)) return;
954 
955  var can_draw = hitem._can_draw,
956  can_expand = hitem._more,
957  dflt_expand = (this.default_by_click === "expand"),
958  drawopt = "";
959 
960  if (d3.event.shiftKey) {
961  drawopt = (handle && handle.shift) ? handle.shift : "inspect";
962  if ((drawopt==="inspect") && handle && handle.noinspect) drawopt = "";
963  }
964  if (handle && handle.ctrl && d3.event.ctrlKey) drawopt = handle.ctrl;
965 
966  if (!drawopt) {
967  for (var pitem = hitem._parent; !!pitem; pitem = pitem._parent) {
968  if (pitem._painter) { can_draw = false; if (can_expand===undefined) can_expand = false; break; }
969  }
970  }
971 
972  if (hitem._childs) can_expand = false;
973 
974  if (can_draw === undefined) can_draw = sett.draw;
975  if (can_expand === undefined) can_expand = sett.expand;
976 
977  if (can_draw && can_expand && !drawopt) {
978  // if default action specified as expand, disable drawing
979  if (dflt_expand || (handle && (handle.dflt === 'expand'))) can_draw = false; else
980  if (this.isItemDisplayed(itemname)) can_draw = false; // if already displayed, try to expand
981  }
982 
983  if (can_draw)
984  return this.display(itemname, drawopt);
985 
986  if (can_expand || dflt_expand)
987  return this.expand(itemname, null, d3cont);
988 
989  // cannot draw, but can inspect ROOT objects
990  if ((typeof hitem._kind === "string") && (hitem._kind.indexOf("ROOT.")===0) && sett.inspect && (can_draw!==false))
991  return this.display(itemname, "inspect");
992 
993  if (!hitem._childs || (hitem === this.h)) return;
994  }
995 
996  if (hitem._isopen)
997  delete hitem._isopen;
998  else
999  hitem._isopen = true;
1000 
1001  this.UpdateTreeNode(hitem, d3cont);
1002  }
1003 
1004  HierarchyPainter.prototype.tree_mouseover = function(on, elem) {
1005  var itemname = d3.select(elem.parentNode.parentNode).attr('item');
1006 
1007  var hitem = this.Find(itemname);
1008  if (!hitem) return;
1009 
1010  var painter, prnt = hitem;
1011  while (prnt && !painter) {
1012  painter = prnt._painter;
1013  prnt = prnt._parent;
1014  }
1015 
1016  if (painter && typeof painter.MouseOverHierarchy === 'function')
1017  painter.MouseOverHierarchy(on, itemname, hitem);
1018  }
1019 
1020  HierarchyPainter.prototype.direct_contextmenu = function(elem) {
1021  // this is alternative context menu, used in the object inspector
1022 
1023  d3.event.preventDefault();
1024  var itemname = d3.select(elem.parentNode.parentNode).attr('item');
1025  var hitem = this.Find(itemname);
1026  if (!hitem) return;
1027 
1028  if (typeof this.fill_context !== 'function') return;
1029 
1030  JSROOT.Painter.createMenu(this, function(menu) {
1031 
1032  menu.painter.fill_context(menu, hitem);
1033 
1034  if (menu.size() > 0) {
1035  menu.tree_node = elem.parentNode;
1036  menu.show(d3.event);
1037  }
1038  });
1039  }
1040 
1041  HierarchyPainter.prototype.tree_contextmenu = function(elem) {
1042  // this is handling of context menu request for the normal objects browser
1043 
1044  d3.event.preventDefault();
1045 
1046  var itemname = d3.select(elem.parentNode.parentNode).attr('item');
1047 
1048  var hitem = this.Find(itemname);
1049  if (!hitem) return;
1050 
1051  var painter = this,
1052  onlineprop = painter.GetOnlineProp(itemname),
1053  fileprop = painter.GetFileProp(itemname);
1054 
1055  function qualifyURL(url) {
1056  function escapeHTML(s) {
1057  return s.split('&').join('&amp;').split('<').join('&lt;').split('"').join('&quot;');
1058  }
1059  var el = document.createElement('div');
1060  el.innerHTML = '<a href="' + escapeHTML(url) + '">x</a>';
1061  return el.firstChild.href;
1062  }
1063 
1064  JSROOT.Painter.createMenu(painter, function(menu) {
1065 
1066  if ((itemname == "") && !('_jsonfile' in hitem)) {
1067  var addr = "", cnt = 0;
1068  function separ() { return cnt++ > 0 ? "&" : "?"; }
1069 
1070  var files = [];
1071  painter.ForEachRootFile(function(item) { files.push(item._file.fFullURL); });
1072 
1073  if (!painter.GetTopOnlineItem())
1074  addr = JSROOT.source_dir + "index.htm";
1075 
1076  if (painter.IsMonitoring())
1077  addr += separ() + "monitoring=" + painter.MonitoringInterval();
1078 
1079  if (files.length==1)
1080  addr += separ() + "file=" + files[0];
1081  else
1082  if (files.length>1)
1083  addr += separ() + "files=" + JSON.stringify(files);
1084 
1085  if (painter['disp_kind'])
1086  addr += separ() + "layout=" + painter.disp_kind.replace(/ /g, "");
1087 
1088  var items = [];
1089 
1090  if (painter.disp)
1091  painter.disp.ForEachPainter(function(p) {
1092  if (p.GetItemName()!=null)
1093  items.push(p.GetItemName());
1094  });
1095 
1096  if (items.length == 1) {
1097  addr += separ() + "item=" + items[0];
1098  } else if (items.length > 1) {
1099  addr += separ() + "items=" + JSON.stringify(items);
1100  }
1101 
1102  menu.add("Direct link", function() { window.open(addr); });
1103  menu.add("Only items", function() { window.open(addr + "&nobrowser"); });
1104  } else if (onlineprop) {
1105  painter.FillOnlineMenu(menu, onlineprop, itemname);
1106  } else {
1107  var sett = JSROOT.getDrawSettings(hitem._kind, 'nosame');
1108 
1109  // allow to draw item even if draw function is not defined
1110  if (hitem._can_draw) {
1111  if (!sett.opts) sett.opts = [""];
1112  if (sett.opts.indexOf("")<0) sett.opts.unshift("");
1113  }
1114 
1115  if (sett.opts)
1116  menu.addDrawMenu("Draw", sett.opts, function(arg) { this.display(itemname, arg); });
1117 
1118  if (fileprop && sett.opts && !fileprop.localfile) {
1119  var filepath = qualifyURL(fileprop.fileurl);
1120  if (filepath.indexOf(JSROOT.source_dir) == 0)
1121  filepath = filepath.slice(JSROOT.source_dir.length);
1122  filepath = fileprop.kind + "=" + filepath;
1123  if (fileprop.itemname.length > 0) {
1124  var name = fileprop.itemname;
1125  if (name.search(/\+| |\,/)>=0) name = "\'" + name + "\'";
1126  filepath += "&item=" + name;
1127  }
1128 
1129  menu.addDrawMenu("Draw in new tab", sett.opts, function(arg) {
1130  window.open(JSROOT.source_dir + "index.htm?nobrowser&"+filepath +"&opt="+arg);
1131  });
1132  }
1133 
1134  if (sett.expand && !('_childs' in hitem) && (hitem._more || !('_more' in hitem)))
1135  menu.add("Expand", function() { painter.expand(itemname); });
1136 
1137  if (hitem._kind === "ROOT.TStyle")
1138  menu.add("Apply", function() { painter.ApplyStyle(itemname); });
1139  }
1140 
1141  if (typeof hitem._menu == 'function')
1142  hitem._menu(menu, hitem, painter);
1143 
1144  if (menu.size() > 0) {
1145  menu.tree_node = elem.parentNode;
1146  if (menu.separ) menu.add("separator"); // add separator at the end
1147  menu.add("Close");
1148  menu.show(d3.event);
1149  }
1150 
1151  }); // end menu creation
1152 
1153  return false;
1154  }
1155 
1161  HierarchyPainter.prototype.CreateDisplay = function(callback) {
1162 
1163  if ('disp' in this) {
1164  if (this.disp.NumDraw() > 0) return JSROOT.CallBack(callback, this.disp);
1165  this.disp.Reset();
1166  delete this.disp;
1167  }
1168 
1169  // check that we can found frame where drawing should be done
1170  if (document.getElementById(this.disp_frameid) == null)
1171  return JSROOT.CallBack(callback, null);
1172 
1173  if (this.disp_kind == "tabs")
1174  this.disp = new TabsDisplay(this.disp_frameid);
1175  else
1176  if (this.disp_kind.indexOf("flex")==0)
1177  this.disp = new FlexibleDisplay(this.disp_frameid);
1178  else
1179  if (this.disp_kind.indexOf("coll")==0)
1180  this.disp = new CollapsibleDisplay(this.disp_frameid);
1181  else
1182  this.disp = new JSROOT.GridDisplay(this.disp_frameid, this.disp_kind);
1183 
1184  if (this.disp)
1185  this.disp.CleanupFrame = this.CleanupFrame.bind(this);
1186 
1187  JSROOT.CallBack(callback, this.disp);
1188  }
1189 
1190  HierarchyPainter.prototype.enable_dragging = function(element, itemname) {
1191  $(element).draggable({ revert: "invalid", appendTo: "body", helper: "clone" });
1192  }
1193 
1194  HierarchyPainter.prototype.enable_dropping = function(frame, itemname) {
1195  var h = this;
1196  $(frame).droppable({
1197  hoverClass : "ui-state-active",
1198  accept: function(ui) {
1199  var dropname = ui.parent().parent().attr('item');
1200  if ((dropname == itemname) || !dropname) return false;
1201 
1202  var ditem = h.Find(dropname);
1203  if (!ditem || (!('_kind' in ditem))) return false;
1204 
1205  return ditem._kind.indexOf("ROOT.")==0;
1206  },
1207  drop: function(event, ui) {
1208  var dropname = ui.draggable.parent().parent().attr('item');
1209  if (!dropname) return false;
1210  return h.dropitem(dropname, $(this).attr("id"));
1211  }
1212  });
1213  }
1214 
1215  HierarchyPainter.prototype.CreateBrowser = function(browser_kind, update_html, call_back) {
1216 
1217  if (!this.gui_div || this.exclude_browser || !this.brlayout) return false;
1218 
1219  var main = d3.select("#" + this.gui_div + " .jsroot_browser"),
1220  jmain = $(main.node());
1221 
1222  // one requires top-level container
1223  if (main.empty()) return false;
1224 
1225  if ((browser_kind==="float") && this.float_browser_disabled) browser_kind = "fix";
1226 
1227  if (!main.select('.jsroot_browser_area').empty()) {
1228  // this is case when browser created,
1229  // if update_html specified, hidden state will be toggled
1230 
1231  if (update_html) this.brlayout.Toggle(browser_kind);
1232 
1233  JSROOT.CallBack(call_back);
1234 
1235  return true;
1236  }
1237 
1238  var guiCode = "<p class='jsroot_browser_version'><a href='https://root.cern/js/'>JSROOT</a> version <span style='color:green'><b>" + JSROOT.version + "</b></span></p>";
1239 
1240  if (this.is_online) {
1241  guiCode +='<p> Hierarchy in <a href="h.json">json</a> and <a href="h.xml">xml</a> format</p>'
1242  + '<div style="display:flex;flex-direction:row;">'
1243  + '<label style="margin-right:5px; vertical-align:middle;">'
1244  + '<input style="vertical-align:middle;" type="checkbox" name="monitoring" class="gui_monitoring"/>'
1245  + 'Monitoring</label>';
1246  } else if (!this.no_select) {
1247  var myDiv = d3.select("#"+this.gui_div),
1248  files = myDiv.attr("files") || "../files/hsimple.root",
1249  path = JSROOT.GetUrlOption("path") || myDiv.attr("path") || "",
1250  arrFiles = files.split(';');
1251 
1252  guiCode +=
1253  '<input type="text" value="" style="width:95%; margin:5px;border:2px;" class="gui_urlToLoad" title="input file name"/>'
1254  +'<div style="display:flex;flex-direction:row;padding-top:5px">'
1255  +'<select class="gui_selectFileName" style="flex:1;padding:2px;" title="select file name"'
1256  +'<option value="" selected="selected"></option>';
1257  for (var i in arrFiles)
1258  guiCode += '<option value = "' + path + arrFiles[i] + '">' + arrFiles[i] + '</option>';
1259  guiCode += '</select>'
1260  +'<input type="file" class="gui_localFile" accept=".root" style="display:none"/><output id="list" style="display:none"></output>'
1261  +'<input type="button" value="..." class="gui_fileBtn" style="min-width:3em;padding:3px;margin-left:5px;margin-right:5px;" title="select local file for reading"/><br/>'
1262  +'</div>'
1263  +'<p id="gui_fileCORS"><small><a href="https://github.com/root-project/jsroot/blob/master/docs/JSROOT.md#reading-root-files-from-other-servers">Read docu</a>'
1264  +' how to open files from other servers.</small></p>'
1265  +'<div style="display:flex;flex-direction:row">'
1266  +'<input style="padding:3px;margin-right:5px;"'
1267  +' class="gui_ReadFileBtn" type="button" title="Read the Selected File" value="Load"/>'
1268  +'<input style="padding:3px;margin-right:5px;"'
1269  +' class="gui_ResetUIBtn" type="button" title="Close all opened files and clear drawings" value="Reset"/>'
1270  } else if (this.no_select == "file") {
1271  guiCode += '<div style="display:flex;flex-direction:row">';
1272  }
1273 
1274  if (this.is_online || !this.no_select || this.no_select=="file")
1275  guiCode += '<select style="padding:2px;margin-right:5px;" title="layout kind" class="gui_layout"></select>'
1276  + '</div>';
1277 
1278  guiCode += '<div id="' + this.gui_div+'_browser_hierarchy" class="jsroot_browser_hierarchy"></div>';
1279 
1280  this.brlayout.SetBrowserContent(guiCode);
1281 
1282  this.brlayout.SetBrowserTitle(this.is_online ? 'ROOT online server' : 'Read a ROOT file');
1283 
1284  var hpainter = this, localfile_read_callback = null;
1285 
1286  if (!this.is_online && !this.no_select) {
1287 
1288  this.ReadSelectedFile = function() {
1289  var filename = main.select(".gui_urlToLoad").property('value').trim();
1290  if (!filename) return;
1291 
1292  if ((filename.toLowerCase().lastIndexOf(".json") == filename.length-5))
1293  this.OpenJsonFile(filename);
1294  else
1295  this.OpenRootFile(filename);
1296  }
1297 
1298  jmain.find(".gui_selectFileName").val("").change(function() {
1299  jmain.find(".gui_urlToLoad").val($(this).val());
1300  });
1301  jmain.find(".gui_fileBtn").button().click(function() {
1302  jmain.find(".gui_localFile").click();
1303  });
1304 
1305  jmain.find(".gui_ReadFileBtn").button().click(function(){
1306  hpainter.ReadSelectedFile();
1307  });
1308 
1309  jmain.find(".gui_ResetUIBtn").button().click(function(){
1310  hpainter.clear(true);
1311  });
1312 
1313  jmain.find(".gui_urlToLoad").keyup(function(e) {
1314  if (e.keyCode == 13) hpainter.ReadSelectedFile();
1315  });
1316 
1317  jmain.find(".gui_localFile").change(function(evnt) {
1318  var files = evnt.target.files;
1319 
1320  for (var n=0;n<files.length;++n) {
1321  var f = files[n];
1322  main.select(".gui_urlToLoad").property('value', f.name);
1323  if (hpainter) hpainter.OpenRootFile(f, localfile_read_callback);
1324  }
1325 
1326  localfile_read_callback = null;
1327  });
1328 
1329  this.SelectLocalFile = function(read_callback) {
1330  localfile_read_callback = read_callback;
1331  $("#" + this.gui_div + " .jsroot_browser").find(".gui_localFile").click();
1332  }
1333  }
1334 
1335  var jlayout = jmain.find(".gui_layout");
1336  if (jlayout.length) {
1337  var lst = ['simple', 'vert2', 'vert3', 'vert231', 'horiz2', 'horiz32', 'flex',
1338  'grid 2x2', 'grid 1x3', 'grid 2x3', 'grid 3x3', 'grid 4x4', 'collapsible', 'tabs'];
1339 
1340  for (var k=0;k<lst.length;++k){
1341  var opt = document.createElement('option');
1342  opt.value = lst[k];
1343  opt.innerHTML = lst[k];
1344  jlayout.get(0).appendChild(opt);
1345  }
1346 
1347  jlayout.change(function() {
1348  hpainter.SetDisplay($(this).val() || 'collapsible', hpainter.gui_div + "_drawing");
1349  });
1350  }
1351 
1352  this.SetDivId(this.gui_div + '_browser_hierarchy');
1353 
1354  if (update_html) {
1355  this.RefreshHtml();
1356  this.InitializeBrowser();
1357  }
1358 
1359  this.brlayout.ToggleBrowserKind(browser_kind || "fix");
1360 
1361  JSROOT.CallBack(call_back);
1362 
1363  return true;
1364  }
1365 
1366  HierarchyPainter.prototype.InitializeBrowser = function() {
1367 
1368  var main = d3.select("#" + this.gui_div + " .jsroot_browser");
1369  if (main.empty() || !this.brlayout) return;
1370  var jmain = $(main.node()), hpainter = this;
1371 
1372  if (this.brlayout) this.brlayout.AdjustBrowserSize();
1373 
1374  var selects = main.select(".gui_layout").node();
1375 
1376  if (selects) {
1377  var found = false;
1378  for (var i in selects.options) {
1379  var s = selects.options[i].text;
1380  if (typeof s !== 'string') continue;
1381  if ((s == this.GetLayout()) || (s.replace(/ /g,"") == this.GetLayout())) {
1382  selects.selectedIndex = i; found = true;
1383  break;
1384  }
1385  }
1386  if (!found) {
1387  var opt = document.createElement('option');
1388  opt.innerHTML = opt.value = this.GetLayout();
1389  selects.appendChild(opt);
1390  selects.selectedIndex = selects.options.length-1;
1391  }
1392  }
1393 
1394  if (this.is_online) {
1395  if (this.h && this.h._toptitle)
1396  this.brlayout.SetBrowserTitle(this.h._toptitle);
1397  jmain.find(".gui_monitoring")
1398  .prop('checked', this.IsMonitoring())
1399  .click(function() {
1400  hpainter.EnableMonitoring(this.checked);
1401  hpainter.updateAll(!this.checked);
1402  });
1403  } else if (!this.no_select) {
1404  var fname = "";
1405  this.ForEachRootFile(function(item) { if (!fname) fname = item._fullurl; });
1406  jmain.find(".gui_urlToLoad").val(fname);
1407  }
1408  }
1409 
1410  HierarchyPainter.prototype.EnableMonitoring = function(on) {
1411  this.SetMonitoring(undefined, on);
1412 
1413  var chkbox = d3.select("#" + this.gui_div + " .jsroot_browser .gui_monitoring");
1414  if (!chkbox.empty() && (chkbox.property('checked') !== on))
1415  chkbox.property('checked', on);
1416  }
1417 
1418  HierarchyPainter.prototype.CreateStatusLine = function(height, mode) {
1419  if (this.status_disabled || !this.gui_div || !this.brlayout) return '';
1420  return this.brlayout.CreateStatusLine(height, mode);
1421  }
1422 
1423  JSROOT.BuildGUI = function() {
1424  var myDiv = d3.select('#simpleGUI'), online = false;
1425 
1426  if (myDiv.empty()) {
1427  myDiv = d3.select('#onlineGUI');
1428  if (myDiv.empty()) return alert('no div for gui found');
1429  online = true;
1430  }
1431 
1432  if (myDiv.attr("ignoreurl") === "true")
1433  JSROOT.gStyle.IgnoreUrlOptions = true;
1434 
1435  if ((JSROOT.GetUrlOption("nobrowser")!==null) || (myDiv.attr("nobrowser") && myDiv.attr("nobrowser")!=="false"))
1436  return JSROOT.BuildNobrowserGUI();
1437 
1438  JSROOT.Painter.readStyleFromURL();
1439 
1440  var hpainter = new JSROOT.HierarchyPainter('root', null);
1441 
1442  hpainter.is_online = online;
1443 
1444  hpainter.StartGUI(myDiv, hpainter.InitializeBrowser.bind(hpainter));
1445  }
1446 
1447  // ==================================================
1448 
1449  function CollapsibleDisplay(frameid) {
1450  JSROOT.MDIDisplay.call(this, frameid);
1451  this.cnt = 0; // use to count newly created frames
1452  }
1453 
1454  CollapsibleDisplay.prototype = Object.create(JSROOT.MDIDisplay.prototype);
1455 
1456  CollapsibleDisplay.prototype.ForEachFrame = function(userfunc, only_visible) {
1457  var topid = this.frameid + '_collapsible';
1458 
1459  if (document.getElementById(topid) == null) return;
1460 
1461  if (typeof userfunc != 'function') return;
1462 
1463  $('#' + topid + ' .collapsible_draw').each(function() {
1464 
1465  // check if only visible specified
1466  if (only_visible && $(this).is(":hidden")) return;
1467 
1468  userfunc($(this).get(0));
1469  });
1470  }
1471 
1472  CollapsibleDisplay.prototype.GetActiveFrame = function() {
1473  var found = JSROOT.MDIDisplay.prototype.GetActiveFrame.call(this);
1474  if (found && !$(found).is(":hidden")) return found;
1475 
1476  found = null;
1477  this.ForEachFrame(function(frame) {
1478  if (!found) found = frame;
1479  }, true);
1480 
1481  return found;
1482  }
1483 
1484  CollapsibleDisplay.prototype.ActivateFrame = function(frame) {
1485  if ($(frame).is(":hidden")) {
1486  $(frame).prev().toggleClass("ui-accordion-header-active ui-state-active ui-state-default ui-corner-bottom")
1487  .find("> .ui-icon").toggleClass("ui-icon-triangle-1-e ui-icon-triangle-1-s").end()
1488  .next().toggleClass("ui-accordion-content-active").slideDown(0);
1489  }
1490  $(frame).prev()[0].scrollIntoView();
1491  // remember title
1492  this.active_frame_title = d3.select(frame).attr('frame_title');
1493  }
1494 
1495  CollapsibleDisplay.prototype.CreateFrame = function(title) {
1496 
1497  this.BeforeCreateFrame(title);
1498 
1499  var topid = this.frameid + '_collapsible';
1500 
1501  if (document.getElementById(topid) == null)
1502  $("#"+this.frameid).append('<div id="'+ topid + '" class="jsroot ui-accordion ui-accordion-icons ui-widget ui-helper-reset" style="overflow:auto; overflow-y:scroll; height:100%; padding-left: 2px; padding-right: 2px"></div>');
1503 
1504  var mdi = this,
1505  hid = topid + "_sub" + this.cnt++,
1506  uid = hid + "h",
1507  entryInfo = "<h5 id=\"" + uid + "\">" +
1508  "<span class='ui-icon ui-icon-triangle-1-e'></span>" +
1509  "<a> " + title + "</a>&nbsp; " +
1510  "<button type='button' class='jsroot_collaps_closebtn' style='float:right; width:1.4em' title='close canvas'/>" +
1511  " </h5>\n" +
1512  "<div class='collapsible_draw' id='" + hid + "'></div>\n";
1513 
1514  $("#" + topid).append(entryInfo);
1515 
1516  $('#' + uid)
1517  .addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-top ui-corner-bottom")
1518  .hover(function() { $(this).toggleClass("ui-state-hover"); })
1519  .click( function() {
1520  $(this).toggleClass("ui-accordion-header-active ui-state-active ui-state-default ui-corner-bottom")
1521  .find("> .ui-icon").toggleClass("ui-icon-triangle-1-e ui-icon-triangle-1-s")
1522  .end().next().toggleClass("ui-accordion-content-active").slideToggle(0);
1523  var sub = $(this).next(), hide_drawing = sub.is(":hidden");
1524  sub.css('display', hide_drawing ? 'none' : '');
1525  if (!hide_drawing) JSROOT.resize(sub.get(0));
1526  })
1527  .next()
1528  .addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom")
1529  .hide();
1530 
1531  $('#' + uid).find(" .jsroot_collaps_closebtn")
1532  .button({ icons: { primary: "ui-icon-close" }, text: false })
1533  .click(function(){
1534  mdi.CleanupFrame($(this).parent().next().attr('id'));
1535  $(this).parent().next().remove(); // remove drawing
1536  $(this).parent().remove(); // remove header
1537  });
1538 
1539  $('#' + uid)
1540  .toggleClass("ui-accordion-header-active ui-state-active ui-state-default ui-corner-bottom")
1541  .find("> .ui-icon").toggleClass("ui-icon-triangle-1-e ui-icon-triangle-1-s").end().next()
1542  .toggleClass("ui-accordion-content-active").slideToggle(0);
1543 
1544  return $("#" + hid).attr('frame_title', title).css('overflow','hidden')
1545  .attr('can_resize','height') // inform JSROOT that it can resize height of the
1546  .css('position','relative') // this required for correct positioning of 3D canvas in WebKit
1547  .get(0);
1548  }
1549 
1550  // ================================================
1551 
1552  function TabsDisplay(frameid) {
1553  JSROOT.MDIDisplay.call(this, frameid);
1554  this.cnt = 0;
1555  }
1556 
1557  TabsDisplay.prototype = Object.create(JSROOT.MDIDisplay.prototype);
1558 
1559  TabsDisplay.prototype.ForEachFrame = function(userfunc, only_visible) {
1560  var topid = this.frameid + '_tabs';
1561 
1562  if (document.getElementById(topid) == null) return;
1563 
1564  if (typeof userfunc != 'function') return;
1565 
1566  var cnt = -1;
1567  var active = $('#' + topid).tabs("option", "active");
1568 
1569  $('#' + topid + '> .tabs_draw').each(function() {
1570  cnt++;
1571  if (!only_visible || (cnt == active))
1572  userfunc($(this).get(0));
1573  });
1574  }
1575 
1576  TabsDisplay.prototype.GetActiveFrame = function() {
1577  var found = null;
1578  this.ForEachFrame(function(frame) {
1579  if (!found) found = frame;
1580  }, true);
1581 
1582  return found;
1583  }
1584 
1585  TabsDisplay.prototype.ActivateFrame = function(frame) {
1586  var cnt = 0, id = -1;
1587  this.ForEachFrame(function(fr) {
1588  if ($(fr).attr('id') == $(frame).attr('id')) id = cnt;
1589  cnt++;
1590  });
1591  $('#' + this.frameid + "_tabs").tabs("option", "active", id);
1592 
1593  this.active_frame_title = d3.select(frame).attr('frame_title');
1594  }
1595 
1596  TabsDisplay.prototype.CreateFrame = function(title) {
1597 
1598  this.BeforeCreateFrame(title);
1599 
1600  var mdi = this,
1601  topid = this.frameid + '_tabs',
1602  hid = topid + "_sub" + this.cnt++,
1603  li = '<li><a href="#' + hid + '">' + title
1604  + '</a><span class="ui-icon ui-icon-close" style="float: left; margin: 0.4em 0.2em 0 0; cursor: pointer;" role="presentation">Remove Tab</span></li>',
1605  cont = '<div class="tabs_draw" id="' + hid + '"></div>';
1606 
1607  if (document.getElementById(topid) == null) {
1608  $("#" + this.frameid).append('<div id="' + topid + '" class="jsroot">' + ' <ul>' + li + ' </ul>' + cont + '</div>');
1609 
1610  var tabs = $("#" + topid)
1611  .css('overflow','hidden')
1612  .tabs({
1613  heightStyle : "fill",
1614  activate : function (event,ui) {
1615  $(ui.newPanel).css('overflow', 'hidden');
1616  JSROOT.resize($(ui.newPanel).get(0));
1617  }
1618  });
1619 
1620  tabs.delegate("span.ui-icon-close", "click", function() {
1621  var panelId = $(this).closest("li").remove().attr("aria-controls");
1622  mdi.CleanupFrame(panelId);
1623  $("#" + panelId).remove();
1624  tabs.tabs("refresh");
1625  if ($('#' + topid + '> .tabs_draw').length == 0)
1626  $("#" + topid).remove();
1627 
1628  });
1629  } else {
1630  $("#" + topid).find("> .ui-tabs-nav").append(li);
1631  $("#" + topid).append(cont);
1632  $("#" + topid).tabs("refresh");
1633  $("#" + topid).tabs("option", "active", -1);
1634  }
1635  $('#' + hid)
1636  .empty()
1637  .css('overflow', 'hidden')
1638  .attr('frame_title', title);
1639 
1640  return $('#' + hid).get(0);
1641  }
1642 
1643  TabsDisplay.prototype.CheckMDIResize = function(frame_id, size) {
1644  $("#" + this.frameid + '_tabs').tabs("refresh");
1645  JSROOT.MDIDisplay.prototype.CheckMDIResize.call(this, frame_id, size);
1646  }
1647 
1648  // ==================================================
1649 
1650  function FlexibleDisplay(frameid) {
1651  JSROOT.MDIDisplay.call(this, frameid);
1652  this.cnt = 0; // use to count newly created frames
1653  }
1654 
1655  FlexibleDisplay.prototype = Object.create(JSROOT.MDIDisplay.prototype);
1656 
1657  FlexibleDisplay.prototype.ForEachFrame = function(userfunc, only_visible) {
1658  var topid = this.frameid + '_flex';
1659 
1660  if (document.getElementById(topid) == null) return;
1661  if (typeof userfunc != 'function') return;
1662 
1663  $('#' + topid + ' .flex_draw').each(function() {
1664  // check if only visible specified
1665  if (only_visible && $(this).is(":hidden")) return;
1666 
1667  userfunc($(this).get(0));
1668  });
1669  }
1670 
1671  FlexibleDisplay.prototype.GetActiveFrame = function() {
1672  var found = JSROOT.MDIDisplay.prototype.GetActiveFrame.call(this);
1673  if (found && !$(found).is(":hidden")) return found;
1674 
1675  found = null;
1676  this.ForEachFrame(function(frame) {
1677  if (!found) found = frame;
1678  }, true);
1679 
1680  return found;
1681  }
1682 
1683  FlexibleDisplay.prototype.ActivateFrame = function(frame) {
1684  this.active_frame_title = d3.select(frame).attr('frame_title');
1685  }
1686 
1687  FlexibleDisplay.prototype.CreateFrame = function(title) {
1688 
1689  this.BeforeCreateFrame(title);
1690 
1691  var topid = this.frameid + '_flex';
1692 
1693  if (document.getElementById(topid) == null)
1694  $("#" + this.frameid).append('<div id="'+ topid + '" class="jsroot" style="overflow:none; height:100%; width:100%"></div>');
1695 
1696  var mdi = this,
1697  top = $("#" + topid),
1698  w = top.width(),
1699  h = top.height(),
1700  subid = topid + "_frame" + this.cnt;
1701 
1702  var entry ='<div id="' + subid + '" class="flex_frame" style="position:absolute">' +
1703  '<div class="ui-widget-header flex_header">'+
1704  '<p>'+title+'</p>' +
1705  '<button type="button" style="float:right; width:1.4em"/>' +
1706  '<button type="button" style="float:right; width:1.4em"/>' +
1707  '<button type="button" style="float:right; width:1.4em"/>' +
1708  '</div>' +
1709  '<div id="' + subid + '_cont" class="flex_draw"></div>' +
1710  '</div>';
1711 
1712  top.append(entry);
1713 
1714  function PopupWindow(div) {
1715  if (div === 'first') {
1716  div = null;
1717  $('#' + topid + ' .flex_frame').each(function() {
1718  if (!$(this).is(":hidden") && ($(this).prop('state') != "minimal")) div = $(this);
1719  });
1720  if (!div) return;
1721  }
1722 
1723  div.appendTo(div.parent());
1724 
1725  if (div.prop('state') == "minimal") return;
1726 
1727  div = div.find(".flex_draw").get(0);
1728  var dummy = new JSROOT.TObjectPainter();
1729  dummy.SetDivId(div, -1);
1730  JSROOT.Painter.SelectActivePad({ pp: dummy.canv_painter(), active: true });
1731 
1732  JSROOT.resize(div);
1733  }
1734 
1735  function ChangeWindowState(main, state) {
1736  var curr = main.prop('state');
1737  if (!curr) curr = "normal";
1738  main.prop('state', state);
1739  if (state==curr) return;
1740 
1741  if (curr == "normal") {
1742  main.prop('original_height', main.height());
1743  main.prop('original_width', main.width());
1744  main.prop('original_top', main.css('top'));
1745  main.prop('original_left', main.css('left'));
1746  }
1747 
1748  main.find(".jsroot_minbutton").find('.ui-icon')
1749  .toggleClass("ui-icon-triangle-1-s", state!="minimal")
1750  .toggleClass("ui-icon-triangle-2-n-s", state=="minimal");
1751 
1752  main.find(".jsroot_maxbutton").find('.ui-icon')
1753  .toggleClass("ui-icon-triangle-1-n", state!="maximal")
1754  .toggleClass("ui-icon-triangle-2-n-s", state=="maximal");
1755 
1756  switch (state) {
1757  case "minimal":
1758  main.height(main.find('.flex_header').height()).width("auto");
1759  main.find(".flex_draw").css("display","none");
1760  main.find(".ui-resizable-handle").css("display","none");
1761  break;
1762  case "maximal":
1763  main.height("100%").width("100%").css('left','').css('top','');
1764  main.find(".flex_draw").css("display","");
1765  main.find(".ui-resizable-handle").css("display","none");
1766  break;
1767  default:
1768  main.find(".flex_draw").css("display","");
1769  main.find(".ui-resizable-handle").css("display","");
1770  main.height(main.prop('original_height'))
1771  .width(main.prop('original_width'));
1772  if (curr!="minimal")
1773  main.css('left', main.prop('original_left'))
1774  .css('top', main.prop('original_top'));
1775  }
1776 
1777  if (state !== "minimal")
1778  PopupWindow(main);
1779  else
1780  PopupWindow("first");
1781  }
1782 
1783  $("#" + subid)
1784  .css('left', parseInt(w * (this.cnt % 5)/10))
1785  .css('top', parseInt(h * (this.cnt % 5)/10))
1786  .width(Math.round(w * 0.58))
1787  .height(Math.round(h * 0.58))
1788  .resizable({
1789  helper: "jsroot-flex-resizable-helper",
1790  start: function(event, ui) {
1791  // bring element to front when start resizing
1792  PopupWindow($(this));
1793  },
1794  stop: function(event, ui) {
1795  var rect = { width : ui.size.width-1, height : ui.size.height - $(this).find(".flex_header").height()-1 };
1796  JSROOT.resize($(this).find(".flex_draw").get(0), rect);
1797  }
1798  })
1799  .draggable({
1800  containment: "parent",
1801  start: function(event, ui) {
1802  // bring element to front when start dragging
1803  PopupWindow($(this));
1804 
1805  var ddd = $(this).find(".flex_draw");
1806 
1807  // block dragging when mouse below header
1808  var elementMouseIsOver = document.elementFromPoint(event.clientX, event.clientY);
1809  var isparent = false;
1810  $(elementMouseIsOver).parents().map(function() { if ($(this).get(0) === ddd.get(0)) isparent = true; });
1811  if (isparent) return false;
1812  }
1813  })
1814  .click(function() { PopupWindow($(this)); })
1815  .find('.flex_header')
1816  // .hover(function() { $(this).toggleClass("ui-state-hover"); })
1817  .click(function() {
1818  PopupWindow($(this).parent());
1819  })
1820  .dblclick(function() {
1821  var main = $(this).parent();
1822  if (main.prop('state') == "normal")
1823  ChangeWindowState(main, "maximal");
1824  else
1825  ChangeWindowState(main, "normal");
1826  })
1827  .find("button")
1828  .first()
1829  .attr('title','close canvas')
1830  .button({ icons: { primary: "ui-icon-close" }, text: false })
1831  .click(function() {
1832  var main = $(this).parent().parent();
1833  mdi.CleanupFrame(main.find(".flex_draw").get(0));
1834  main.remove();
1835  PopupWindow('first'); // set active as first window
1836  })
1837  .next()
1838  .attr('title','maximize canvas')
1839  .addClass('jsroot_maxbutton')
1840  .button({ icons: { primary: "ui-icon-triangle-1-n" }, text: false })
1841  .click(function() {
1842  var main = $(this).parent().parent();
1843  var maximize = $(this).find('.ui-icon').hasClass("ui-icon-triangle-1-n");
1844  ChangeWindowState(main, maximize ? "maximal" : "normal");
1845  })
1846  .next()
1847  .attr('title','minimize canvas')
1848  .addClass('jsroot_minbutton')
1849  .button({ icons: { primary: "ui-icon-triangle-1-s" }, text: false })
1850  .click(function() {
1851  var main = $(this).parent().parent();
1852  var minimize = $(this).find('.ui-icon').hasClass("ui-icon-triangle-1-s");
1853  ChangeWindowState(main, minimize ? "minimal" : "normal");
1854  });
1855 
1856  // set default z-index to avoid overlap of these special elements
1857  $("#" + subid).find(".ui-resizable-handle").css('z-index', '');
1858 
1859  this.cnt++;
1860 
1861  return $("#" + subid + "_cont").attr('frame_title', title).get(0);
1862  }
1863 
1864  // ================== new grid with flexible boundaries ========
1865 
1866  JSROOT.GridDisplay.prototype.CreateSeparator = function(handle, main, group) {
1867  var separ = $(main.append("div").node());
1868 
1869  separ.toggleClass('jsroot_separator', true)
1870  .toggleClass(handle.vertical ? 'jsroot_hline' : 'jsroot_vline', true)
1871  .prop('handle', handle)
1872  .attr('separator-id', group.id)
1873  .css('position','absolute')
1874  .css(handle.vertical ? 'top' : 'left', "calc(" + group.position+"% - 2px)")
1875  .css(handle.vertical ? 'width' : 'height', (handle.size || 100)+"%")
1876  .css(handle.vertical ? 'height' : 'width', '5px')
1877  .css('cursor', handle.vertical ? "ns-resize" : "ew-resize");
1878 
1879  separ.bind('changePosition', function(e, drag_ui) {
1880  var handle = $(this).prop('handle'),
1881  id = parseInt($(this).attr('separator-id')),
1882  pos = handle.groups[id].position;
1883 
1884  if (drag_ui === 'restore') {
1885  pos = handle.groups[id].position0;
1886  } else
1887  if (drag_ui && drag_ui.offset) {
1888  if (handle.vertical)
1889  pos = Math.round((drag_ui.offset.top+2-$(this).parent().offset().top)/$(this).parent().innerHeight()*100);
1890  else
1891  pos = Math.round((drag_ui.offset.left+2-$(this).parent().offset().left)/$(this).parent().innerWidth()*100);
1892  }
1893 
1894  var diff = handle.groups[id].position - pos;
1895 
1896  if (Math.abs(diff)<0.3) return; // if no significant change, do nothing
1897 
1898  // do not change if size too small
1899  if (Math.min(handle.groups[id-1].size-diff, handle.groups[id].size+diff) < 5) return;
1900 
1901  handle.groups[id-1].size -= diff;
1902  handle.groups[id].size += diff;
1903  handle.groups[id].position = pos;
1904 
1905  function SetGroupSize(prnt, grid) {
1906  var name = handle.vertical ? 'height' : 'width',
1907  size = handle.groups[grid].size+'%';
1908  prnt.children("[groupid='"+grid+"']").css(name, size)
1909  .children(".jsroot_separator").css(name, size);
1910  }
1911 
1912  $(this).css(handle.vertical ? 'top' : 'left', "calc("+pos+"% - 2px)");
1913 
1914  SetGroupSize($(this).parent(), id-1);
1915  SetGroupSize($(this).parent(), id);
1916 
1917  if (drag_ui === 'restore') {
1918  $(this).trigger('resizeGroup', id-1);
1919  $(this).trigger('resizeGroup', id);
1920  }
1921  });
1922 
1923  separ.bind('resizeGroup', function(e, grid) {
1924  var sel = $(this).parent().children("[groupid='"+grid+"']");
1925  if (!sel.hasClass('jsroot_newgrid')) sel = sel.find(".jsroot_newgrid");
1926  sel.each(function() { JSROOT.resize($(this).get(0)); });
1927  });
1928 
1929  separ.dblclick(function() {
1930  $(this).trigger('changePosition', 'restore');
1931  });
1932 
1933  separ.draggable({
1934  axis: handle.vertical ? "y" : "x",
1935  cursor: handle.vertical ? "ns-resize" : "ew-resize",
1936  containment: "parent",
1937  helper : function() { return $(this).clone().css('background-color','grey'); },
1938  start: function(event,ui) {
1939  // remember start position
1940  var handle = $(this).prop('handle'),
1941  id = parseInt($(this).attr('separator-id'));
1942  handle.groups[id].startpos = handle.groups[id].position;
1943  },
1944  drag: function(event,ui) {
1945  $(this).trigger('changePosition', ui);
1946  },
1947  stop: function(event,ui) {
1948  // verify if start position was changed
1949  var handle = $(this).prop('handle'),
1950  id = parseInt($(this).attr('separator-id'));
1951  if (Math.abs(handle.groups[id].startpos - handle.groups[id].position)<0.5) return;
1952 
1953  $(this).trigger('resizeGroup', id-1);
1954  $(this).trigger('resizeGroup', id);
1955  }
1956  });
1957  }
1958 
1959  // ========== performs tree drawing on server ==================
1960 
1961  JSROOT.CreateTreePlayer = function(player) {
1962 
1963  player.draw_first = true;
1964 
1965  player.ConfigureOnline = function(itemname, url, askey, root_version, dflt_expr) {
1966  this.SetItemName(itemname, "", this);
1967  this.url = url;
1968  this.root_version = root_version;
1969  this.askey = askey;
1970  this.dflt_expr = dflt_expr;
1971  }
1972 
1973  player.ConfigureTree = function(tree) {
1974  this.local_tree = tree;
1975  }
1976 
1977  player.KeyUp = function(e) {
1978  if (e.keyCode == 13) this.PerformDraw();
1979  }
1980 
1981  player.ShowExtraButtons = function(args) {
1982  var main = $(this.select_main().node());
1983 
1984  main.find(".treedraw_buttons")
1985  .append(" Cut: <input class='treedraw_cut ui-corner-all ui-widget' style='width:8em;margin-left:5px' title='cut expression'></input>"+
1986  " Opt: <input class='treedraw_opt ui-corner-all ui-widget' style='width:5em;margin-left:5px' title='histogram draw options'></input>"+
1987  " Num: <input class='treedraw_number' style='width:7em;margin-left:5px' title='number of entries to process (default all)'></input>" +
1988  " First: <input class='treedraw_first' style='width:7em;margin-left:5px' title='first entry to process (default first)'></input>" +
1989  " <button class='treedraw_clear' title='Clear drawing'>Clear</button>");
1990 
1991  var page = 1000, numentries = undefined, p = this;
1992  if (this.local_tree) numentries = this.local_tree.fEntries || 0;
1993 
1994  main.find(".treedraw_cut").val(args && args.parse_cut ? args.parse_cut : "").keyup(this.keyup);
1995  main.find(".treedraw_opt").val(args && args.drawopt ? args.drawopt : "").keyup(this.keyup);
1996  main.find(".treedraw_number").val(args && args.numentries ? args.numentries : "").spinner({ numberFormat: "n", min: 0, page: 1000, max: numentries }).keyup(this.keyup);
1997  main.find(".treedraw_first").val(args && args.firstentry ? args.firstentry : "").spinner({ numberFormat: "n", min: 0, page: 1000, max: numentries }).keyup(this.keyup);
1998  main.find(".treedraw_clear").button().click(function() { JSROOT.cleanup(p.drawid); });
1999  }
2000 
2001  player.Show = function(divid, args) {
2002  this.drawid = divid + "_draw";
2003 
2004  this.keyup = this.KeyUp.bind(this);
2005 
2006  var show_extra = args && (args.parse_cut || args.numentries || args.firstentry);
2007 
2008  var main = $("#" + divid);
2009 
2010  main.html("<div class='treedraw_buttons' style='padding-left:0.5em'>" +
2011  "<button class='treedraw_exe' title='Execute draw expression'>Draw</button>" +
2012  " Expr:<input class='treedraw_varexp ui-corner-all ui-widget' style='width:12em;margin-left:5px' title='draw expression'></input> " +
2013  (show_extra ? "" : "<button class='treedraw_more'>More</button>") +
2014  "</div>" +
2015  "<hr/>" +
2016  "<div id='" + this.drawid + "' style='width:100%'></div>");
2017 
2018  // only when main html element created, one can set divid
2019  this.SetDivId(divid);
2020 
2021  var p = this;
2022 
2023  if (this.local_tree)
2024  main.find('.treedraw_buttons').attr('title', "Tree draw player for: " + this.local_tree.fName);
2025  main.find('.treedraw_exe').button().click(function() { p.PerformDraw(); });
2026  main.find('.treedraw_varexp')
2027  .val(args && args.parse_expr ? args.parse_expr : (this.dflt_expr || "px:py"))
2028  .keyup(this.keyup);
2029 
2030  if (show_extra) {
2031  this.ShowExtraButtons(args);
2032  } else {
2033  main.find('.treedraw_more').button().click(function() {
2034  $(this).remove();
2035  p.ShowExtraButtons();
2036  });
2037  }
2038 
2039  this.CheckResize();
2040  }
2041 
2042  player.PerformLocalDraw = function() {
2043  if (!this.local_tree) return;
2044 
2045  var frame = $(this.select_main().node()),
2046  args = { expr: frame.find('.treedraw_varexp').val() };
2047 
2048  if (frame.find('.treedraw_more').length==0) {
2049  args.cut = frame.find('.treedraw_cut').val();
2050  if (!args.cut) delete args.cut;
2051 
2052  args.drawopt = frame.find('.treedraw_opt').val();
2053  if (args.drawopt === "dump") { args.dump = true; args.drawopt = ""; }
2054  if (!args.drawopt) delete args.drawopt;
2055 
2056  args.numentries = parseInt(frame.find('.treedraw_number').val());
2057  if (isNaN(args.numentries)) delete args.numentries;
2058 
2059  args.firstentry = parseInt(frame.find('.treedraw_first').val());
2060  if (isNaN(args.firstentry)) delete args.firstentry;
2061  }
2062 
2063  var p = this;
2064 
2065  if (args.drawopt) JSROOT.cleanup(p.drawid);
2066 
2067  p.local_tree.Draw(args, function(histo, hopt, intermediate) {
2068  JSROOT.redraw(p.drawid, histo, hopt);
2069  });
2070  }
2071 
2072  player.PerformDraw = function() {
2073 
2074  if (this.local_tree) return this.PerformLocalDraw();
2075 
2076  var frame = $(this.select_main().node()),
2077  url = this.url + '/exe.json.gz?compact=3&method=Draw',
2078  expr = frame.find('.treedraw_varexp').val(),
2079  hname = "h_tree_draw", option = "",
2080  pos = expr.indexOf(">>");
2081 
2082  if (pos<0) {
2083  expr += ">>" + hname;
2084  } else {
2085  hname = expr.substr(pos+2);
2086  if (hname[0]=='+') hname = hname.substr(1);
2087  var pos2 = hname.indexOf("(");
2088  if (pos2>0) hname = hname.substr(0, pos2);
2089  }
2090 
2091  if (frame.find('.treedraw_more').length==0) {
2092  var cut = frame.find('.treedraw_cut').val(),
2093  nentries = frame.find('.treedraw_number').val(),
2094  firstentry = frame.find('.treedraw_first').val();
2095 
2096  option = frame.find('.treedraw_opt').val();
2097 
2098  url += '&prototype="const char*,const char*,Option_t*,Long64_t,Long64_t"&varexp="' + expr + '"&selection="' + cut + '"';
2099 
2100  // provide all optional arguments - default value kMaxEntries not works properly in ROOT6
2101  if (nentries=="") nentries = (this.root_version >= 394499) ? "TTree::kMaxEntries": "1000000000"; // kMaxEntries available since ROOT 6.05/03
2102  if (firstentry=="") firstentry = "0";
2103  url += '&option="' + option + '"&nentries=' + nentries + '&firstentry=' + firstentry;
2104  } else {
2105  url += '&prototype="Option_t*"&opt="' + expr + '"';
2106  }
2107  url += '&_ret_object_=' + hname;
2108 
2109  var player = this;
2110 
2111  function SubmitDrawRequest() {
2112  JSROOT.NewHttpRequest(url, 'object', function(res) {
2113  if (!res) return;
2114  JSROOT.cleanup(player.drawid);
2115  JSROOT.draw(player.drawid, res, option);
2116  }).send();
2117  }
2118 
2119  if (this.askey) {
2120  // first let read tree from the file
2121  this.askey = false;
2122  JSROOT.NewHttpRequest(this.url + "/root.json", 'text', SubmitDrawRequest).send();
2123  } else {
2124  SubmitDrawRequest();
2125  }
2126  }
2127 
2128  player.CheckResize = function(arg) {
2129  var main = $(this.select_main().node());
2130 
2131  $("#" + this.drawid).width(main.width());
2132  var h = main.height(),
2133  h0 = main.find(".treedraw_buttons").outerHeight(true),
2134  h1 = main.find("hr").outerHeight(true);
2135 
2136  $("#" + this.drawid).height(h - h0 - h1 - 2);
2137 
2138  JSROOT.resize(this.drawid);
2139  }
2140 
2141  return player;
2142  }
2143 
2146 
2147  JSROOT.drawTreePlayer = function(hpainter, itemname, askey, asleaf) {
2148 
2149  var item = hpainter.Find(itemname),
2150  top = hpainter.GetTopOnlineItem(item),
2151  draw_expr = "", leaf_cnt = 0;
2152  if (!item || !top) return null;
2153 
2154  if (asleaf) {
2155  draw_expr = item._name;
2156  while (item && !item._ttree) item = item._parent;
2157  if (!item) return null;
2158  itemname = hpainter.itemFullName(item);
2159  }
2160 
2161  var url = hpainter.GetOnlineItemUrl(itemname);
2162  if (!url) return null;
2163 
2164  var root_version = top._root_version ? parseInt(top._root_version) : 396545; // by default use version number 6-13-01
2165 
2166  var mdi = hpainter.GetDisplay();
2167  if (!mdi) return null;
2168 
2169  var frame = mdi.FindFrame(itemname, true);
2170  if (!frame) return null;
2171 
2172  var divid = d3.select(frame).attr('id'),
2173  player = new JSROOT.TBasePainter();
2174 
2175  if (item._childs && !asleaf)
2176  for (var n=0;n<item._childs.length;++n) {
2177  var leaf = item._childs[n];
2178  if (leaf && leaf._kind && (leaf._kind.indexOf("ROOT.TLeaf")==0) && (leaf_cnt<2)) {
2179  if (leaf_cnt++ > 0) draw_expr+=":";
2180  draw_expr+=leaf._name;
2181  }
2182  }
2183 
2184  JSROOT.CreateTreePlayer(player);
2185  player.ConfigureOnline(itemname, url, askey, root_version, draw_expr);
2186  player.Show(divid);
2187 
2188  return player;
2189  }
2190 
2193  JSROOT.drawTreePlayerKey = function(hpainter, itemname) {
2194  return JSROOT.drawTreePlayer(hpainter, itemname, true);
2195  }
2196 
2199  JSROOT.drawLeafPlayer = function(hpainter, itemname) {
2200  return JSROOT.drawTreePlayer(hpainter, itemname, false, true);
2201  }
2202 
2203  // =======================================================================
2204 
2205  JSROOT.Painter.ConfigureVSeparator = function(handle) {
2206  // FIXME: obsolete, will be removed
2207  }
2208 
2209  JSROOT.Painter.AdjustLayout = function(left, height, firsttime) {
2210  // FIXME: obsolete, will be removed
2211  if (JSROOT.hpainter && JSROOT.hpainter.brlayout)
2212  JSROOT.hpainter.brlayout.AdjustSeparator(left, height, true);
2213  }
2214 
2215  JSROOT.Painter.ConfigureHSeparator = function(height) {
2216  // FIXME: obsolete, will be removed
2217  if (!JSROOT.hpainter) return "";
2218 
2219  return JSROOT.hpainter.CreateStatusLine(height);
2220  }
2221 
2222  return JSROOT;
2223 
2224 }));
2225