otsdaq_utilities  v2_05_02_indev
JSRootPainter.jquery.js
1 
4 (function( factory ) {
5  if ( typeof define === "function" && define.amd ) {
6  // AMD. Register as an anonymous module.
7  define( ['jquery', 'jquery-ui', 'd3', 'JSRootPainter'], factory );
8  } else {
9 
10  if (typeof jQuery == 'undefined')
11  throw new Error('jQuery not defined', 'JSRootPainter.jquery.js');
12 
13  if (typeof jQuery.ui == 'undefined')
14  throw new Error('jQuery-ui not defined','JSRootPainter.jquery.js');
15 
16  if (typeof d3 != 'object')
17  throw new Error('This extension requires d3.v3.js', 'JSRootPainter.jquery.js');
18 
19  if (typeof JSROOT == 'undefined')
20  throw new Error('JSROOT is not defined', 'JSRootPainter.jquery.js');
21 
22  if (typeof JSROOT.Painter != 'object')
23  throw new Error('JSROOT.Painter not defined', 'JSRootPainter.jquery.js');
24 
25  // Browser globals
26  factory(jQuery, jQuery.ui, d3, JSROOT);
27  }
28 } (function($, myui, d3, JSROOT) {
29 
30  if ( typeof define === "function" && define.amd )
31  JSROOT.loadScript('$$$style/jquery-ui.css');
32 
33  JSROOT.Painter.createMenu = function(maincallback, menuname) {
34  if (!menuname || (typeof menuname !== 'string')) menuname = 'root_ctx_menu';
35 
36  var menu = { element: null, code: "", cnt: 1, funcs: {}, separ: false };
37 
38  menu.add = function(name, arg, func) {
39  if (name == "separator") { this.code += "<li>-</li>"; this.separ = true; return; }
40 
41  if (name.indexOf("header:")==0) {
42  this.code += "<li class='ui-widget-header' style='padding-left:5px'>"+name.substr(7)+"</li>";
43  return;
44  }
45 
46  if (name=="endsub:") { this.code += "</ul></li>"; return; }
47  var close_tag = "</li>", style = "padding-top:2px;padding-bottom:1px";
48  if (name.indexOf("sub:")==0) { name = name.substr(4); close_tag="<ul>"; style += ";padding-right:2em"}
49 
50  if (typeof arg == 'function') { func = arg; arg = name; }
51 
52  // if ((arg==null) || (typeof arg != 'string')) arg = name;
53 
54  if (name.indexOf("chk:")==0) { name = "<span class='ui-icon ui-icon-check' style='margin:1px'></span>" + name.substr(4); } else
55  if (name.indexOf("unk:")==0) { name = "<span class='ui-icon ui-icon-blank' style='margin:1px'></span>" + name.substr(4); }
56 
57  // special handling of first versions with menu support
58  if (($.ui.version.indexOf("1.10")==0) || ($.ui.version.indexOf("1.9")==0))
59  name = '<a href="#">' + name + '</a>';
60 
61  this.code += "<li cnt='" + this.cnt + "' arg='" + arg + "' style='" + style + "'>" + name + close_tag;
62  if (typeof func == 'function') this.funcs[this.cnt] = func; // keep call-back function
63 
64  this.cnt++;
65  }
66 
67  menu.addchk = function(flag, name, arg, func) {
68  return this.add((flag ? "chk:" : "unk:") + name, arg, func);
69  }
70 
71  menu.size = function() { return this.cnt-1; }
72 
73  menu.addDrawMenu = function(menu_name, opts, call_back) {
74  if (opts==null) opts = new Array;
75  if (opts.length==0) opts.push("");
76 
77  this.add((opts.length > 1) ? ("sub:" + menu_name) : menu_name, opts[0], call_back);
78  if (opts.length<2) return;
79 
80  for (var i=0;i<opts.length;++i) {
81  var name = opts[i];
82  if (name=="") name = '&lt;dflt&gt;';
83  this.add(name, opts[i], call_back);
84  }
85  this.add("endsub:");
86  }
87 
88  menu.remove = function() {
89  if (this.element!==null) {
90  this.element.remove();
91  if (this.close_callback) this.close_callback();
92  document.body.removeEventListener('click', this.remove_bind);
93  }
94  this.element = null;
95  }
96 
97  menu.remove_bind = menu.remove.bind(menu);
98 
99  menu.show = function(event, close_callback) {
100  this.remove();
101 
102  if (typeof close_callback == 'function') this.close_callback = close_callback;
103 
104  document.body.addEventListener('click', this.remove_bind);
105 
106  var oldmenu = document.getElementById(menuname);
107  if (oldmenu) oldmenu.parentNode.removeChild(oldmenu);
108 
109  $(document.body).append('<ul class="jsroot_ctxmenu">' + this.code + '</ul>');
110 
111  this.element = $('.jsroot_ctxmenu');
112 
113  var pthis = this;
114 
115  this.element
116  .attr('id', menuname)
117  .css('left', event.clientX + window.pageXOffset)
118  .css('top', event.clientY + window.pageYOffset)
119  .css('font-size', '80%')
120  .css('position', 'absolute') // this overrides ui-menu-items class property
121  .menu({
122  items: "> :not(.ui-widget-header)",
123  select: function( event, ui ) {
124  var arg = ui.item.attr('arg');
125  var cnt = ui.item.attr('cnt');
126  var func = cnt ? pthis.funcs[cnt] : null;
127  pthis.remove();
128  if (typeof func == 'function') {
129  if ('painter' in menu)
130  func.bind(pthis.painter)(arg); // if 'painter' field set, returned as this to callback
131  else
132  func(arg);
133  }
134  }
135  });
136 
137  var newx = null, newy = null;
138 
139  if (event.clientX + this.element.width() > $(window).width()) newx = $(window).width() - this.element.width() - 20;
140  if (event.clientY + this.element.height() > $(window).height()) newy = $(window).height() - this.element.height() - 20;
141 
142  if (newx!==null) this.element.css('left', (newx>0 ? newx : 0) + window.pageXOffset);
143  if (newy!==null) this.element.css('top', (newy>0 ? newy : 0) + window.pageYOffset);
144  }
145 
146  JSROOT.CallBack(maincallback, menu);
147 
148  return menu;
149  }
150 
151  JSROOT.HierarchyPainter.prototype.isLastSibling = function(hitem) {
152  if (!hitem || !hitem._parent || !hitem._parent._childs) return false;
153  var chlds = hitem._parent._childs;
154  var indx = chlds.indexOf(hitem);
155  if (indx<0) return false;
156  while (++indx < chlds.length)
157  if (!('_hidden' in chlds[indx])) return false;
158  return true;
159  }
160 
161  JSROOT.HierarchyPainter.prototype.addItemHtml = function(hitem, d3prnt, doupdate) {
162  var isroot = !('_parent' in hitem);
163  var has_childs = '_childs' in hitem;
164 
165  if ('_hidden' in hitem) return;
166 
167  var handle = JSROOT.getDrawHandle(hitem._kind),
168  img1 = "", img2 = "", can_click = false;
169 
170  if (handle !== null) {
171  if ('icon' in handle) img1 = handle.icon;
172  if ('icon2' in handle) img2 = handle.icon2;
173  if (('func' in handle) || ('execute' in handle) || ('aslink' in handle) ||
174  (('expand' in handle) && (hitem._more !== false))) can_click = true;
175  }
176 
177  if ('_icon' in hitem) img1 = hitem._icon;
178  if ('_icon2' in hitem) img2 = hitem._icon2;
179  if ((img1.length==0) && ('_online' in hitem))
180  hitem._icon = img1 = "img_globe";
181  if ((img1.length==0) && isroot)
182  hitem._icon = img1 = "img_base";
183 
184  if (hitem._more || ('_expand' in hitem) || ('_player' in hitem))
185  can_click = true;
186 
187  var can_menu = can_click;
188  if (!can_menu && (typeof hitem._kind == 'string') && (hitem._kind.indexOf("ROOT.")==0)) {
189  can_menu = true;
190  can_click = true;
191  }
192 
193  if (img2.length==0) img2 = img1;
194  if (img1.length==0) img1 = (has_childs || hitem._more) ? "img_folder" : "img_page";
195  if (img2.length==0) img2 = (has_childs || hitem._more) ? "img_folderopen" : "img_page";
196 
197  var itemname = this.itemFullName(hitem);
198 
199  var d3cont;
200 
201  if (doupdate) {
202  d3prnt.selectAll("*").remove();
203  d3cont = d3prnt;
204  } else {
205  d3cont = d3prnt.append("div");
206  }
207 
208  d3cont.attr("item", itemname);
209 
210  // build indent
211  var prnt = isroot ? null : hitem._parent;
212  while ((prnt != null) && (prnt != this.h)) {
213  d3cont.insert("div",":first-child")
214  .attr("class", this.isLastSibling(prnt) ? "img_empty" : "img_line");
215  prnt = prnt._parent;
216  }
217 
218  var icon_class = "", plusminus = false;
219 
220  if (isroot) {
221  // for root node no extra code
222  } else
223  if (has_childs) {
224  icon_class = hitem._isopen ? "img_minus" : "img_plus";
225  plusminus = true;
226  } else
227  /*if (hitem._more) {
228  icon_class = "img_plus"; // should be special plus ???
229  plusminus = true;
230  } else */ {
231  icon_class = "img_join";
232  }
233 
234  var h = this;
235 
236  if (icon_class.length > 0) {
237  if (this.isLastSibling(hitem)) icon_class+="bottom";
238  var d3icon = d3cont.append("div").attr('class', icon_class);
239  if (plusminus) d3icon.style('cursor','pointer')
240  .on("click", function() { h.tree_click(this, "plusminus"); });
241  }
242 
243  // make node icons
244 
245  if (this.with_icons) {
246  var icon_name = hitem._isopen ? img2 : img1;
247  var title = hitem._kind ? hitem._kind.replace(/</g,'&lt;').replace(/>/g,'&gt;') : "";
248 
249  var d3img = null;
250 
251  if (icon_name.indexOf("img_")==0) {
252  d3img = d3cont.append("div")
253  .attr("class", icon_name)
254  .attr("title", title);
255  } else {
256  d3img = d3cont.append("img")
257  .attr("src", icon_name)
258  .attr("alt","")
259  .attr("title",title)
260  .style('vertical-align','top')
261  .style('width','18px')
262  .style('height','18px');
263  }
264 
265  if ('_icon_click' in hitem)
266  d3img.on("click", function() { h.tree_click(this, "icon"); });
267  }
268 
269  var d3a = d3cont.append("a");
270  if (can_click || has_childs)
271  d3a.attr("class","h_item")
272  .on("click", function() { h.tree_click(this); });
273 
274  if ('disp_kind' in h) {
275  if (JSROOT.gStyle.DragAndDrop && can_click)
276  this.enable_dragging(d3a.node(), itemname);
277  if (JSROOT.gStyle.ContextMenu && can_menu)
278  d3a.on('contextmenu', function() { h.tree_contextmenu(this); });
279  }
280 
281  var element_name = hitem._name;
282 
283  if ('_realname' in hitem)
284  element_name = hitem._realname;
285 
286  var element_title = "";
287  if ('_title' in hitem) element_title = hitem._title;
288 
289  if ('_fullname' in hitem)
290  element_title += " fullname: " + hitem._fullname;
291 
292  if (element_title.length === 0)
293  element_title = element_name;
294 
295  d3a.attr('title', element_title)
296  .text(element_name + ('_value' in hitem ? ":" : ""));
297 
298  if ('_value' in hitem) {
299  var d3p = d3cont.append("p");
300  if ('_vclass' in hitem) d3p.attr('class', hitem._vclass);
301  if (!hitem._isopen) d3p.html(hitem._value);
302  }
303 
304  if (has_childs && (isroot || hitem._isopen)) {
305  var d3chlds = d3cont.append("div").attr("class", "h_childs");
306  for (var i=0; i< hitem._childs.length;++i) {
307  var chld = hitem._childs[i];
308  chld._parent = hitem;
309  this.addItemHtml(chld, d3chlds);
310  }
311  }
312  }
313 
314  JSROOT.HierarchyPainter.prototype.RefreshHtml = function(callback) {
315 
316  if (this.divid == null) return JSROOT.CallBack(callback);
317  var d3elem = this.select_main();
318 
319  d3elem.html(""); // clear html - most simple way
320 
321  if ((this.h == null) || d3elem.empty())
322  return JSROOT.CallBack(callback);
323 
324  var h = this, factcmds = [], status_item = null;
325  this.ForEach(function(item) {
326  if (('_fastcmd' in item) && (item._kind == 'Command')) factcmds.push(item);
327  if (('_status' in item) && (status_item==null)) status_item = item;
328  });
329 
330  var maindiv =
331  d3elem.append("div")
332  .attr("class", "jsroot")
333  .style("background-color", this.background ? this.background : "")
334  .style('overflow', 'auto')
335  .style('width', '100%')
336  .style('height', '100%')
337  .style('font-size', this.with_icons ? "12px" : "15px");
338 
339  for (var n=0;n<factcmds.length;++n) {
340  var btn =
341  maindiv.append("button")
342  .text("")
343  .attr("class",'fast_command')
344  .attr("item", this.itemFullName(factcmds[n]))
345  .attr("title", factcmds[n]._title)
346  .on("click", function() { h.ExecuteCommand(d3.select(this).attr("item"), this); } );
347 
348 
349  if ('_icon' in factcmds[n])
350  btn.append('img').attr("src", factcmds[n]._icon);
351  }
352 
353  d3p = maindiv.append("p");
354 
355  d3p.append("a").attr("href", '#').text("open all").on("click", function() { h.toggle(true); d3.event.preventDefault(); });
356  d3p.append("text").text(" | ");
357  d3p.append("a").attr("href", '#').text("close all").on("click", function() { h.toggle(false); d3.event.preventDefault(); });
358 
359  if ('_online' in this.h) {
360  d3p.append("text").text(" | ");
361  d3p.append("a").attr("href", '#').text("reload").on("click", function() { h.reload(); d3.event.preventDefault(); });
362  }
363 
364  if ('disp_kind' in this) {
365  d3p.append("text").text(" | ");
366  d3p.append("a").attr("href", '#').text("clear").on("click", function() { h.clear(false); d3.event.preventDefault(); });
367  }
368 
369  this.addItemHtml(this.h, maindiv.append("div").attr("class","h_tree"));
370 
371  if ((status_item!=null) && (JSROOT.GetUrlOption('nostatus')==null)) {
372  var func = JSROOT.findFunction(status_item._status);
373  var hdiv = (typeof func == 'function') ? JSROOT.Painter.ConfigureHSeparator(30) : null;
374  if (hdiv != null)
375  func(hdiv, this.itemFullName(status_item));
376  }
377 
378  JSROOT.CallBack(callback);
379  }
380 
381  JSROOT.HierarchyPainter.prototype.UpdateTreeNode = function(hitem, d3cont) {
382  if ((d3cont===undefined) || d3cont.empty()) {
383  var name = this.itemFullName(hitem);
384  d3cont = this.select_main().select("[item='" + name + "']");
385  //node = $(this.select_main().node()).find("[item='" + name + "']");
386  if (d3cont.empty() && ('_cycle' in hitem))
387  d3cont = this.select_main().select("[item='" + name + ";" + hitem._cycle + "']");
388  if (d3cont.empty()) return;
389  }
390 
391  this.addItemHtml(hitem, d3cont, true);
392  }
393 
394  JSROOT.HierarchyPainter.prototype.tree_click = function(node, place) {
395  if (node===null) return;
396  var d3cont = d3.select(node.parentNode);
397  var itemname = d3cont.attr('item');
398  if (itemname == null) return;
399 
400  var hitem = this.Find(itemname);
401  if (hitem == null) return;
402 
403  var prnt = hitem, dflt = undefined;
404  while (prnt) {
405  if ((dflt = prnt._click_action) !== undefined) break;
406  prnt = prnt._parent;
407  }
408 
409  if (!place || (place=="")) place = "item";
410 
411  if (place == "icon") {
412  if (('_icon_click' in hitem) && (typeof hitem._icon_click == 'function'))
413  if (hitem._icon_click(hitem))
414  this.UpdateTreeNode(hitem, d3cont);
415  return;
416  }
417 
418  // special feature - all items with '_expand' function are not drawn by click
419  if ((place=="item") && ('_expand' in hitem)) place = "plusminus";
420 
421  // special case - one should expand item
422  if (((place == "plusminus") && !('_childs' in hitem) && hitem._more) ||
423  ((place == "item") && (dflt === "expand")))
424  return this.expand(itemname, null, d3cont);
425 
426  if (place == "item") {
427  if ('_player' in hitem)
428  return this.player(itemname);
429 
430  var handle = JSROOT.getDrawHandle(hitem._kind);
431 
432  if (handle != null) {
433  if ('aslink' in handle)
434  return window.open(itemname + "/");
435 
436  if ('func' in handle)
437  return this.display(itemname);
438 
439  if ('execute' in handle)
440  return this.ExecuteCommand(itemname, node.parentNode);
441 
442  if (('expand' in handle) && (hitem._childs == null))
443  return this.expand(itemname, null, d3cont);
444  }
445 
446  if (!hitem._childs && (this.default_by_click === "expand"))
447  return this.expand(itemname, null, d3cont);
448 
449  // cannot draw, but can inspect ROOT objects
450  if ((typeof hitem._kind === "string") && (hitem._kind.indexOf("ROOT.")===0))
451  return this.display(itemname, "inspect");
452 
453  if (!hitem._childs || (hitem === this.h)) return;
454  }
455 
456  if (hitem._isopen)
457  delete hitem._isopen;
458  else
459  hitem._isopen = true;
460 
461  this.UpdateTreeNode(hitem, d3cont);
462  }
463 
464  JSROOT.HierarchyPainter.prototype.tree_contextmenu = function(elem) {
465  d3.event.preventDefault();
466 
467  var itemname = d3.select(elem.parentNode).attr('item');
468 
469  var hitem = this.Find(itemname);
470  if (hitem==null) return;
471 
472  var painter = this;
473 
474  var onlineprop = painter.GetOnlineProp(itemname);
475  var fileprop = painter.GetFileProp(itemname);
476 
477  function qualifyURL(url) {
478  function escapeHTML(s) {
479  return s.split('&').join('&amp;').split('<').join('&lt;').split('"').join('&quot;');
480  }
481  var el = document.createElement('div');
482  el.innerHTML = '<a href="' + escapeHTML(url) + '">x</a>';
483  return el.firstChild.href;
484  }
485 
486  JSROOT.Painter.createMenu(function(menu) {
487 
488  menu['painter'] = painter;
489 
490  if ((itemname == "") && !('_jsonfile' in hitem)) {
491  var addr = "", cnt = 0;
492  function separ() { return cnt++ > 0 ? "&" : "?"; }
493 
494  var files = [];
495  painter.ForEachRootFile(function(item) { files.push(item._file.fFullURL); });
496 
497  if (painter.GetTopOnlineItem()==null)
498  addr = JSROOT.source_dir + "index.htm";
499 
500  if (painter.IsMonitoring())
501  addr += separ() + "monitoring=" + painter.MonitoringInterval();
502 
503  if (files.length==1)
504  addr += separ() + "file=" + files[0];
505  else
506  if (files.length>1)
507  addr += separ() + "files=" + JSON.stringify(files);
508 
509  if (painter['disp_kind'])
510  addr += separ() + "layout=" + painter.disp_kind.replace(/ /g, "");
511 
512  var items = [];
513 
514  if (painter['disp'] != null)
515  painter['disp'].ForEachPainter(function(p) {
516  if (p.GetItemName()!=null)
517  items.push(p.GetItemName());
518  });
519 
520  if (items.length == 1) {
521  addr += separ() + "item=" + items[0];
522  } else if (items.length > 1) {
523  addr += separ() + "items=" + JSON.stringify(items);
524  }
525 
526  menu.add("Direct link", function() { window.open(addr); });
527  menu.add("Only items", function() { window.open(addr + "&nobrowser"); });
528  } else
529  if (onlineprop != null) {
530  painter.FillOnlineMenu(menu, onlineprop, itemname);
531  } else {
532  var opts = JSROOT.getDrawOptions(hitem._kind, 'nosame');
533 
534  if (opts!=null)
535  menu.addDrawMenu("Draw", opts, function(arg) { this.display(itemname, arg); });
536 
537  if ((fileprop!=null) && (opts!=null)) {
538  var filepath = qualifyURL(fileprop.fileurl);
539  if (filepath.indexOf(JSROOT.source_dir) == 0)
540  filepath = filepath.slice(JSROOT.source_dir.length);
541  menu.addDrawMenu("Draw in new window", opts, function(arg) {
542  window.open(JSROOT.source_dir + "index.htm?nobrowser&file=" + filepath + "&item=" + fileprop.itemname+"&opt="+arg);
543  });
544  }
545 
546  if (!('_childs' in hitem) && (hitem._more || !('_more' in hitem)))
547  menu.add("Expand", function() { painter.expand(itemname); });
548  }
549 
550  if (('_menu' in hitem) && (typeof hitem['_menu'] == 'function'))
551  hitem['_menu'](menu, hitem, painter);
552 
553  if (menu.size() > 0) {
554  menu.tree_node = elem.parentNode;
555  if (menu.separ) menu.add("separator"); // add separator at the end
556  menu.add("Close");
557  menu.show(d3.event);
558  }
559 
560  }); // end menu creation
561 
562  return false;
563  }
564 
565  JSROOT.HierarchyPainter.prototype.CreateDisplay = function(callback) {
566  if ('disp' in this) {
567  if (this.disp.NumDraw() > 0) return JSROOT.CallBack(callback, this.disp);
568  this.disp.Reset();
569  delete this.disp;
570  }
571 
572  // check that we can found frame where drawing should be done
573  if (document.getElementById(this.disp_frameid) == null)
574  return JSROOT.CallBack(callback, null);
575 
576  if (this.disp_kind == "tabs")
577  this.disp = new JSROOT.TabsDisplay(this.disp_frameid);
578  else
579  if ((this.disp_kind == "flex") || (this.disp_kind == "flexible"))
580  this.disp = new JSROOT.FlexibleDisplay(this.disp_frameid);
581  else
582  if (this.disp_kind.search("grid") == 0)
583  this.disp = new JSROOT.GridDisplay(this.disp_frameid, this.disp_kind);
584  else
585  if (this.disp_kind == "simple")
586  this.disp = new JSROOT.SimpleDisplay(this.disp_frameid);
587  else
588  this.disp = new JSROOT.CollapsibleDisplay(this.disp_frameid);
589 
590  JSROOT.CallBack(callback, this.disp);
591  }
592 
593  JSROOT.HierarchyPainter.prototype.enable_dragging = function(element, itemname) {
594  $(element).draggable({ revert: "invalid", appendTo: "body", helper: "clone" });
595  }
596 
597  JSROOT.HierarchyPainter.prototype.enable_dropping = function(frame, itemname) {
598  var h = this;
599  $(frame).droppable({
600  hoverClass : "ui-state-active",
601  accept: function(ui) {
602  var dropname = ui.parent().attr('item');
603  if ((dropname == itemname) || (dropname==null)) return false;
604 
605  var ditem = h.Find(dropname);
606  if ((ditem==null) || (!('_kind' in ditem))) return false;
607 
608  return ditem._kind.indexOf("ROOT.")==0;
609  },
610  drop: function(event, ui) {
611  var dropname = ui.draggable.parent().attr('item');
612  if (dropname==null) return false;
613  return h.dropitem(dropname, $(this).attr("id"));
614  }
615  });
616  }
617 
618  // ==================================================
619 
620  JSROOT.CollapsibleDisplay = function(frameid) {
621  JSROOT.MDIDisplay.call(this, frameid);
622  this.cnt = 0; // use to count newly created frames
623  }
624 
625  JSROOT.CollapsibleDisplay.prototype = Object.create(JSROOT.MDIDisplay.prototype);
626 
627  JSROOT.CollapsibleDisplay.prototype.ForEachFrame = function(userfunc, only_visible) {
628  var topid = this.frameid + '_collapsible';
629 
630  if (document.getElementById(topid) == null) return;
631 
632  if (typeof userfunc != 'function') return;
633 
634  $('#' + topid + ' .collapsible_draw').each(function() {
635 
636  // check if only visible specified
637  if (only_visible && $(this).is(":hidden")) return;
638 
639  userfunc($(this).get(0));
640  });
641  }
642 
643  JSROOT.CollapsibleDisplay.prototype.ActivateFrame = function(frame) {
644  if ($(frame).is(":hidden")) {
645  $(frame).prev().toggleClass("ui-accordion-header-active ui-state-active ui-state-default ui-corner-bottom")
646  .find("> .ui-icon").toggleClass("ui-icon-triangle-1-e ui-icon-triangle-1-s").end()
647  .next().toggleClass("ui-accordion-content-active").slideDown(0);
648  }
649  $(frame).prev()[0].scrollIntoView();
650  }
651 
652  JSROOT.CollapsibleDisplay.prototype.CreateFrame = function(title) {
653 
654  var topid = this.frameid + '_collapsible';
655 
656  if (document.getElementById(topid) == null)
657  $("#"+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>');
658 
659  var hid = topid + "_sub" + this.cnt++;
660  var uid = hid + "h";
661 
662  var entryInfo = "<h5 id=\"" + uid + "\">" +
663  "<span class='ui-icon ui-icon-triangle-1-e'></span>" +
664  "<a> " + title + "</a>&nbsp; " +
665  "<button type='button' class='jsroot_collaps_closebtn' style='float:right; width:1.4em' title='close canvas'/>" +
666  " </h5>\n";
667  entryInfo += "<div class='collapsible_draw' id='" + hid + "'></div>\n";
668  $("#" + topid).append(entryInfo);
669 
670  $('#' + uid)
671  .addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-top ui-corner-bottom")
672  .hover(function() { $(this).toggleClass("ui-state-hover"); })
673  .click( function() {
674  $(this).toggleClass("ui-accordion-header-active ui-state-active ui-state-default ui-corner-bottom")
675  .find("> .ui-icon").toggleClass("ui-icon-triangle-1-e ui-icon-triangle-1-s")
676  .end().next().toggleClass("ui-accordion-content-active").slideToggle(0);
677  JSROOT.resize($(this).next().attr('id'));
678  return false;
679  })
680  .next()
681  .addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom")
682  .hide();
683 
684  $('#' + uid).find(" .jsroot_collaps_closebtn")
685  .button({ icons: { primary: "ui-icon-close" }, text: false })
686  .click(function(){
687  JSROOT.cleanup($(this).parent().next().attr('id'));
688  $(this).parent().next().andSelf().remove();
689  });
690 
691  $('#' + uid)
692  .toggleClass("ui-accordion-header-active ui-state-active ui-state-default ui-corner-bottom")
693  .find("> .ui-icon").toggleClass("ui-icon-triangle-1-e ui-icon-triangle-1-s").end().next()
694  .toggleClass("ui-accordion-content-active").slideToggle(0);
695 
696  return $("#" + hid).attr('frame_title', title).css('overflow','hidden').get(0);
697  }
698 
699  // ================================================
700 
701  JSROOT.TabsDisplay = function(frameid) {
702  JSROOT.MDIDisplay.call(this, frameid);
703  this.cnt = 0;
704  }
705 
706  JSROOT.TabsDisplay.prototype = Object.create(JSROOT.MDIDisplay.prototype);
707 
708  JSROOT.TabsDisplay.prototype.ForEachFrame = function(userfunc, only_visible) {
709  var topid = this.frameid + '_tabs';
710 
711  if (document.getElementById(topid) == null) return;
712 
713  if (typeof userfunc != 'function') return;
714 
715  var cnt = -1;
716  var active = $('#' + topid).tabs("option", "active");
717 
718  $('#' + topid + '> .tabs_draw').each(function() {
719  cnt++;
720  if (!only_visible || (cnt == active))
721  userfunc($(this).get(0));
722  });
723  }
724 
725  JSROOT.TabsDisplay.prototype.ActivateFrame = function(frame) {
726  var cnt = 0, id = -1;
727  this.ForEachFrame(function(fr) {
728  if ($(fr).attr('id') == $(frame).attr('id')) id = cnt;
729  cnt++;
730  });
731  $('#' + this.frameid + "_tabs").tabs("option", "active", id);
732  }
733 
734  JSROOT.TabsDisplay.prototype.CreateFrame = function(title) {
735  var topid = this.frameid + '_tabs';
736 
737  var hid = topid + "_sub" + this.cnt++;
738 
739  var li = '<li><a href="#' + hid + '">' + title
740  + '</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>';
741  var cont = '<div class="tabs_draw" id="' + hid + '"></div>';
742 
743  if (document.getElementById(topid) == null) {
744  $("#" + this.frameid).append('<div id="' + topid + '" class="jsroot">' + ' <ul>' + li + ' </ul>' + cont + '</div>');
745 
746  var tabs = $("#" + topid)
747  .css('overflow','hidden')
748  .tabs({
749  heightStyle : "fill",
750  activate : function (event,ui) {
751  $(ui.newPanel).css('overflow', 'hidden');
752  JSROOT.resize($(ui.newPanel).attr('id'));
753  }
754  });
755 
756  tabs.delegate("span.ui-icon-close", "click", function() {
757  var panelId = $(this).closest("li").remove().attr("aria-controls");
758  JSROOT.cleanup(panelId);
759  $("#" + panelId).remove();
760  tabs.tabs("refresh");
761  if ($('#' + topid + '> .tabs_draw').length == 0)
762  $("#" + topid).remove();
763 
764  });
765  } else {
766  $("#" + topid).find("> .ui-tabs-nav").append(li);
767  $("#" + topid).append(cont);
768  $("#" + topid).tabs("refresh");
769  $("#" + topid).tabs("option", "active", -1);
770  }
771  $('#' + hid)
772  .empty()
773  .css('overflow', 'hidden')
774  .attr('frame_title', title);
775 
776  return $('#' + hid).get(0);
777  }
778 
779  // ==================================================
780 
781  JSROOT.FlexibleDisplay = function(frameid) {
782  JSROOT.MDIDisplay.call(this, frameid);
783  this.cnt = 0; // use to count newly created frames
784  }
785 
786  JSROOT.FlexibleDisplay.prototype = Object.create(JSROOT.MDIDisplay.prototype);
787 
788  JSROOT.FlexibleDisplay.prototype.ForEachFrame = function(userfunc, only_visible) {
789  var topid = this.frameid + '_flex';
790 
791  if (document.getElementById(topid) == null) return;
792  if (typeof userfunc != 'function') return;
793 
794  $('#' + topid + ' .flex_draw').each(function() {
795  // check if only visible specified
796  //if (only_visible && $(this).is(":hidden")) return;
797 
798  userfunc($(this).get(0));
799  });
800  }
801 
802  JSROOT.FlexibleDisplay.prototype.ActivateFrame = function(frame) {
803  }
804 
805  JSROOT.FlexibleDisplay.prototype.CreateFrame = function(title) {
806  var topid = this.frameid + '_flex';
807 
808  if (document.getElementById(topid) == null)
809  $("#" + this.frameid).append('<div id="'+ topid + '" class="jsroot" style="overflow:none; height:100%; width:100%"></div>');
810 
811  var top = $("#" + topid);
812 
813  var w = top.width(), h = top.height();
814 
815  var subid = topid + "_frame" + this.cnt;
816 
817  var entry ='<div id="' + subid + '" class="flex_frame" style="position:absolute">' +
818  '<div class="ui-widget-header flex_header">'+
819  '<p>'+title+'</p>' +
820  '<button type="button" style="float:right; width:1.4em"/>' +
821  '<button type="button" style="float:right; width:1.4em"/>' +
822  '<button type="button" style="float:right; width:1.4em"/>' +
823  '</div>' +
824  '<div id="' + subid + '_cont" class="flex_draw"></div>' +
825  '</div>';
826 
827  top.append(entry);
828 
829  function ChangeWindowState(main, state) {
830  var curr = main.prop('state');
831  if (!curr) curr = "normal";
832  main.prop('state', state);
833  if (state==curr) return;
834 
835  if (curr == "normal") {
836  main.prop('original_height', main.height());
837  main.prop('original_width', main.width());
838  main.prop('original_top', main.css('top'));
839  main.prop('original_left', main.css('left'));
840  }
841 
842  main.find(".jsroot_minbutton").find('.ui-icon')
843  .toggleClass("ui-icon-carat-1-s", state!="minimal")
844  .toggleClass("ui-icon-carat-2-n-s", state=="minimal");
845 
846  main.find(".jsroot_maxbutton").find('.ui-icon')
847  .toggleClass("ui-icon-carat-1-n", state!="maximal")
848  .toggleClass("ui-icon-carat-2-n-s", state=="maximal");
849 
850  switch (state) {
851  case "minimal" :
852  main.height(main.find('.flex_header').height()).width("auto");
853  main.find(".flex_draw").css("display","none");
854  main.find(".ui-resizable-handle").css("display","none");
855  break;
856  case "maximal" :
857  main.height("100%").width("100%").css('left','').css('top','');
858  main.find(".flex_draw").css("display","");
859  main.find(".ui-resizable-handle").css("display","none");
860  break;
861  default:
862  main.find(".flex_draw").css("display","");
863  main.find(".ui-resizable-handle").css("display","");
864  main.height(main.prop('original_height'))
865  .width(main.prop('original_width'));
866  if (curr!="minimal")
867  main.css('left', main.prop('original_left'))
868  .css('top', main.prop('original_top'));
869  }
870 
871  if (state !== "minimal")
872  JSROOT.resize(main.find(".flex_draw").get(0));
873  }
874 
875  $("#" + subid)
876  .css('left', parseInt(w * (this.cnt % 5)/10))
877  .css('top', parseInt(h * (this.cnt % 5)/10))
878  .width(Math.round(w * 0.58))
879  .height(Math.round(h * 0.58))
880  .resizable({
881  helper: "jsroot-flex-resizable-helper",
882  start: function(event, ui) {
883  // bring element to front when start resizing
884  $(this).appendTo($(this).parent());
885  },
886  stop: function(event, ui) {
887  var rect = { width : ui.size.width-1, height : ui.size.height - $(this).find(".flex_header").height()-1 };
888  JSROOT.resize($(this).find(".flex_draw").get(0), rect);
889  }
890  })
891  .draggable({
892  containment: "parent",
893  start: function(event, ui) {
894  // bring element to front when start dragging
895  $(this).appendTo($(this).parent());
896  var ddd = $(this).find(".flex_draw");
897 
898  if (ddd.prop('flex_block_drag') === true) {
899  // block dragging when mouse below header
900  var elementMouseIsOver = document.elementFromPoint(event.clientX, event.clientY);
901  var isparent = false;
902  $(elementMouseIsOver).parents().map(function() { if ($(this).get(0) === ddd.get(0)) isparent = true; });
903  if (isparent) return false;
904  }
905  }
906  })
907  .find('.flex_header')
908  // .hover(function() { $(this).toggleClass("ui-state-hover"); })
909  .click(function() {
910  var div = $(this).parent();
911  div.appendTo(div.parent());
912  })
913  .find("button")
914  .first()
915  .attr('title','close canvas')
916  .button({ icons: { primary: "ui-icon-close" }, text: false })
917  .click(function() {
918  var main = $(this).parent().parent();
919  JSROOT.cleanup(main.find(".flex_draw").get(0));
920  main.remove();
921  })
922  .next()
923  .attr('title','maximize canvas')
924  .addClass('jsroot_maxbutton')
925  .button({ icons: { primary: "ui-icon-carat-1-n" }, text: false })
926  .click(function() {
927  var main = $(this).parent().parent();
928  var maximize = $(this).find('.ui-icon').hasClass("ui-icon-carat-1-n");
929  ChangeWindowState(main, maximize ? "maximal" : "normal");
930  })
931  .next()
932  .attr('title','minimize canvas')
933  .addClass('jsroot_minbutton')
934  .button({ icons: { primary: "ui-icon-carat-1-s" }, text: false })
935  .click(function() {
936  var main = $(this).parent().parent();
937  var minimize = $(this).find('.ui-icon').hasClass("ui-icon-carat-1-s");
938  ChangeWindowState(main, minimize ? "minimal" : "normal");
939  });
940 
941  // set default z-index to avoid overlap of these special elements
942  $("#" + subid).find(".ui-resizable-handle").css('z-index', '');
943 
944  //var draw_w = $("#" + subid).width() - 1;
945  //var draw_h = $("#" + subid).height() - $("#" + subid).find(".flex_header").height()-1;
946  //$("#" + subid).find(".flex_draw").width(draw_w).height(draw_h);
947  this.cnt++;
948 
949  return $("#" + subid + "_cont").attr('frame_title', title).get(0);
950  }
951 
952  // ========== performs tree drawing on server ==================
953 
954  JSROOT.TTreePlayer = function(itemname, url, askey, root_version) {
955  JSROOT.TBasePainter.call(this);
956  this.SetItemName(itemname);
957  this.url = url;
958  this.root_version = root_version;
959  this.hist_painter = null;
960  this.askey = askey;
961  return this;
962  }
963 
964  JSROOT.TTreePlayer.prototype = Object.create( JSROOT.TBasePainter.prototype );
965 
966  JSROOT.TTreePlayer.prototype.Show = function(divid) {
967  this.drawid = divid + "_draw";
968 
969  $("#" + divid)
970  .html("<div class='treedraw_buttons' style='padding-left:0.5em'>" +
971  "<button class='treedraw_exe'>Draw</button>" +
972  " Expr:<input class='treedraw_varexp' style='width:12em'></input> " +
973  "<button class='treedraw_more'>More</button>" +
974  "</div>" +
975  "<div id='" + this.drawid + "' style='width:100%'></div>");
976 
977  var player = this;
978 
979  $("#" + divid).find('.treedraw_exe').click(function() { player.PerformDraw(); });
980  $("#" + divid).find('.treedraw_varexp')
981  .val("px:py")
982  .keyup(function(e){
983  if(e.keyCode == 13) player.PerformDraw();
984  });
985 
986  $("#" + divid).find('.treedraw_more').click(function() {
987  $(this).remove();
988  $("#" + divid).find(".treedraw_buttons")
989  .append(" Cut:<input class='treedraw_cut' style='width:8em'></input>"+
990  " Opt:<input class='treedraw_opt' style='width:5em'></input>"+
991  " Num:<input class='treedraw_number' style='width:7em'></input>" +
992  " First:<input class='treedraw_first' style='width:7em'></input>");
993 
994  $("#" + divid +" .treedraw_opt").val("");
995  $("#" + divid +" .treedraw_number").val("").spinner({ numberFormat: "n", min: 0, page: 1000});
996  $("#" + divid +" .treedraw_first").val("").spinner({ numberFormat: "n", min: 0, page: 1000});
997  });
998 
999  this.CheckResize();
1000 
1001  this.SetDivId(divid);
1002  }
1003 
1004  JSROOT.TTreePlayer.prototype.PerformDraw = function() {
1005 
1006  var frame = $(this.select_main().node());
1007 
1008  var url = this.url + '/exe.json.gz?compact=3&method=Draw';
1009  var expr = frame.find('.treedraw_varexp').val();
1010  var hname = "h_tree_draw";
1011 
1012  var pos = expr.indexOf(">>");
1013  if (pos<0) {
1014  expr += ">>" + hname;
1015  } else {
1016  hname = expr.substr(pos+2);
1017  if (hname[0]=='+') hname = hname.substr(1);
1018  var pos2 = hname.indexOf("(");
1019  if (pos2>0) hname = hname.substr(0, pos2);
1020  }
1021 
1022  if (frame.find('.treedraw_more').length==0) {
1023  var cut = frame.find('.treedraw_cut').val();
1024  var option = frame.find('.treedraw_opt').val();
1025  var nentries = frame.find('.treedraw_number').val();
1026  var firstentry = frame.find('.treedraw_first').val();
1027 
1028  url += '&prototype="const char*,const char*,Option_t*,Long64_t,Long64_t"&varexp="' + expr + '"&selection="' + cut + '"';
1029 
1030  // if any of optional arguments specified, specify all of them
1031  if ((option!="") || (nentries!="") || (firstentry!="")) {
1032  if (nentries=="") nentries = (this.root_version >= 394499) ? "TTree::kMaxEntries": "1000000000"; // kMaxEntries available since ROOT 6.05/03
1033  if (firstentry=="") firstentry = "0";
1034  url += '&option="' + option + '"&nentries=' + nentries + '&firstentry=' + firstentry;
1035  }
1036  } else {
1037  url += '&prototype="Option_t*"&opt="' + expr + '"';
1038  }
1039  url += '&_ret_object_=' + hname;
1040 
1041  var player = this;
1042 
1043  function SubmitDrawRequest() {
1044  JSROOT.NewHttpRequest(url, 'object', function(res) {
1045  if (res==null) return;
1046  $("#"+player.drawid).empty();
1047  player.hist_painter = JSROOT.draw(player.drawid, res)
1048  }).send();
1049  }
1050 
1051  if (this.askey) {
1052  // first let read tree from the file
1053  this.askey = false;
1054  JSROOT.NewHttpRequest(this.url + "/root.json", 'text', SubmitDrawRequest).send();
1055  } else SubmitDrawRequest();
1056  }
1057 
1058  JSROOT.TTreePlayer.prototype.CheckResize = function(force) {
1059  var main = $(this.select_main().node());
1060 
1061  $("#" + this.drawid).width(main.width());
1062  var h = main.height();
1063  var h0 = main.find(".treedraw_buttons").height();
1064  if (h>h0+30) $("#" + this.drawid).height(h - 1 - h0);
1065 
1066  if (this.hist_painter) {
1067  this.hist_painter.CheckResize(force);
1068  }
1069  }
1070 
1071  JSROOT.drawTreePlayer = function(hpainter, itemname, askey) {
1072 
1073  var url = hpainter.GetOnlineItemUrl(itemname);
1074  if (url == null) return null;
1075 
1076  var top = hpainter.GetTopOnlineItem(hpainter.Find(itemname));
1077  if (top == null) return null;
1078  var root_version = ('_root_version' in top) ? top._root_version : 336417; // by default use version number 5-34-32
1079 
1080  var mdi = hpainter.GetDisplay();
1081  if (mdi == null) return null;
1082 
1083  var frame = mdi.FindFrame(itemname, true);
1084  if (frame==null) return null;
1085 
1086  var divid = d3.select(frame).attr('id');
1087 
1088  var player = new JSROOT.TTreePlayer(itemname, url, askey, root_version);
1089  player.Show(divid);
1090  return player;
1091  }
1092 
1093  JSROOT.drawTreePlayerKey = function(hpainter, itemname) {
1094  // function used when tree is not yet loaded on the server
1095 
1096  return JSROOT.drawTreePlayer(hpainter, itemname, true);
1097  }
1098 
1099 
1100  // =======================================================================
1101 
1102  JSROOT.Painter.separ = null;
1103 
1104  JSROOT.Painter.AdjustLayout = function(left, height, firsttime) {
1105  if (this.separ == null) return;
1106 
1107  if (left!=null) {
1108  var wdiff = $("#"+this.separ.left).outerWidth() - $("#"+this.separ.left).width();
1109  var w = 5;
1110  $("#"+this.separ.vertical).css('left', left + "px").width(w).css('top','1px');
1111  $("#"+this.separ.left).width(left-wdiff-1).css('top','1px');
1112  $("#"+this.separ.right).css('left',left+w+"px").css('top','1px');
1113  if (!this.separ.horizontal) {
1114  $("#"+this.separ.vertical).css('bottom', '1px');
1115  $("#"+this.separ.left).css('bottom', '1px');
1116  $("#"+this.separ.right).css('bottom', '1px');
1117  }
1118  }
1119 
1120  if ((height!=null) && this.separ.horizontal) {
1121  var diff = $("#"+this.separ.bottom).outerHeight() - $("#"+this.separ.bottom).height();
1122  height -= 2*diff;
1123  if (height<5) height = 5;
1124  var bot = height + diff;
1125  $('#'+this.separ.bottom).height(height);
1126  var h = 5;
1127  $("#"+this.separ.horizontal).css('bottom', bot + 'px').height(h);
1128  bot += h;
1129  $("#"+this.separ.left).css('bottom', bot + 'px');
1130  }
1131 
1132  if (this.separ.horizontal)
1133  if (this.separ.hpart) {
1134  var ww = $("#"+this.separ.left).outerWidth() - 2;
1135  $('#'+this.separ.bottom).width(ww);
1136  $("#"+this.separ.horizontal).width(ww);
1137  } else {
1138  var bot = $("#"+this.separ.left).css('bottom');
1139  $("#"+this.separ.vertical).css('bottom', bot);
1140  $("#"+this.separ.right).css('bottom', bot);
1141  }
1142 
1143  if (firsttime || (this.separ.handle==null)) return;
1144 
1145  if (typeof this.separ.handle == 'function') this.separ.handle(); else
1146  if ((typeof this.separ.handle == 'object') &&
1147  (typeof this.separ.handle['CheckResize'] == 'function')) this.separ.handle.CheckResize();
1148  }
1149 
1150  JSROOT.Painter.ConfigureVSeparator = function(handle) {
1151 
1152  JSROOT.Painter.separ = { handle: handle, left: "left-div", right: "right-div", vertical: "separator-div",
1153  horizontal : null, bottom : null, hpart: true };
1154 
1155  $("#separator-div").addClass("separator").draggable({
1156  axis: "x" , zIndex: 100, cursor: "ew-resize",
1157  helper : function() { return $("#separator-div").clone().attr('id','separator-clone').css('background-color','grey'); },
1158  stop: function(event,ui) {
1159  event.stopPropagation();
1160  var left = ui.position.left;
1161  $("#separator-clone").remove();
1162  JSROOT.Painter.AdjustLayout(left, null, false);
1163  }
1164  });
1165 
1166  var w0 = Math.round($(window).width() * 0.2);
1167  if (w0<300) w0 = Math.min(300, Math.round($(window).width() * 0.5));
1168 
1169  JSROOT.Painter.AdjustLayout(w0, null, true);
1170  }
1171 
1172  JSROOT.Painter.ConfigureHSeparator = function(height, onlyleft) {
1173 
1174  if ((JSROOT.Painter.separ == null) ||
1175  (JSROOT.Painter.separ.horizontal != null)) return null;
1176 
1177  JSROOT.Painter.separ['horizontal'] = 'horizontal-separator-div';
1178  JSROOT.Painter.separ['bottom'] = 'bottom-div';
1179  JSROOT.Painter.separ.hpart = (onlyleft === true);
1180 
1181  var prnt = $("#"+this.separ.left).parent();
1182 
1183  prnt.append('<div id="horizontal-separator-div" class="separator" style="left:1px; right:1px; height:4px; bottom:16px; cursor: ns-resize"></div>');
1184  prnt.append('<div id="bottom-div" class="column" style="left:1px; right:1px; height:15px; bottom:1px"></div>');
1185 
1186  $("#horizontal-separator-div").addClass("separator").draggable({
1187  axis: "y" , zIndex: 100, cursor: "ns-resize",
1188  helper : function() { return $("#horizontal-separator-div").clone().attr('id','horizontal-separator-clone').css('background-color','grey'); },
1189  stop: function(event,ui) {
1190  event.stopPropagation();
1191  var top = $(window).height() - ui.position.top;
1192  $('#horizontal-separator-clone').remove();
1193  JSROOT.Painter.AdjustLayout(null, top, false);
1194  }
1195  });
1196 
1197  JSROOT.Painter.AdjustLayout(null, height, false);
1198 
1199  return JSROOT.Painter.separ.bottom;
1200  }
1201 
1202  return JSROOT.Painter;
1203 
1204 }));
1205