artdaq_node_server  v1_00_08
 All Classes Namespaces Files Variables Pages
JSRootPainter.js
Go to the documentation of this file.
1 
4 (function() {
5 
6  if (typeof JSROOT != 'object') {
7  var e1 = new Error('JSROOT is not defined');
8  e1.source = 'JSRootPainter.js';
9  throw e1;
10  }
11 
12  if (typeof d3 != 'object') {
13  var e1 = new Error('This extension requires d3.v3.js');
14  e1.source = 'JSRootPainter.js';
15  throw e1;
16  }
17 
18  if (typeof JSROOT.Painter == 'object') {
19  var e1 = new Error('JSROOT.Painter already defined');
20  e1.source = 'JSRootPainter.js';
21  throw e1;
22  }
23 
24  // list of user painters, called with arguments painter(vis, obj, opt)
25  JSROOT.fDrawFunc = new Array;
26 
27  JSROOT.addDrawFunc = function(_name, _func) {
28  JSROOT.fDrawFunc.push({ name:_name, func:_func });
29  }
30 
31  JSROOT.gStyle = {
32  Tooltip : true, // tooltip on/off
33  ContextMenu : true,
34  Zooming : true,
35  MoveResize : true, // enable move and resize of elements like statbox, title, pave, colz
36  DragAndDrop : false,
37  OptimizeDraw : 1, // drawing optimization: 0 - disabled, 1 - only for large (>5000 bins) histograms, 2 - always
38  DefaultCol : 1, // default col option 1-svg, 2-canvas
39  AutoStat : true,
40  OptStat : 1111,
41  StatNDC : { fX1NDC : 0.78, fY1NDC: 0.75, fX2NDC: 0.98, fY2NDC: 0.91 },
42  StatText : { fTextAngle: 0, fTextSize: 9, fTextAlign: 12, fTextColor: 1, fTextFont: 42 },
43  StatFill : { fFillColor: 0, fFillStyle: 1001 },
44  TimeOffset : 788918400000, // UTC time at 01/01/95
45  StatFormat : function(v) { return (Math.abs(v) < 1e5) ? v.toFixed(5) : v.toExponential(7); },
46  StatEntriesFormat : function(v) { return (Math.abs(v) < 1e7) ? v.toFixed(0) : v.toExponential(7); }
47  };
48 
52  JSROOT.Painter = {};
53 
54  JSROOT.Painter.readStyleFromURL = function(url) {
55  var optimize = JSROOT.GetUrlOption("optimize", url);
56  if (optimize=="") JSROOT.gStyle.OptimizeDraw = 2; else
57  if (optimize!=null) {
58  JSROOT.gStyle.OptimizeDraw = parseInt(optimize);
59  if (JSROOT.gStyle.OptimizeDraw==NaN) JSROOT.gStyle.OptimizeDraw = 2;
60  }
61 
62  var inter = JSROOT.GetUrlOption("interactive", url);
63  if ((inter=="") || (inter=="1")) inter = "11111"; else
64  if (inter=="0") inter = "00000";
65  if ((inter!=null) && (inter.length==5)) {
66  JSROOT.gStyle.Tooltip = (inter.charAt(0) != '0');
67  JSROOT.gStyle.ContextMenu = (inter.charAt(1) != '0');
68  JSROOT.gStyle.Zooming = (inter.charAt(2) != '0');
69  JSROOT.gStyle.MoveResize = (inter.charAt(3) != '0');
70  JSROOT.gStyle.DragAndDrop = (inter.charAt(4) != '0');
71  }
72 
73  var col = JSROOT.GetUrlOption("col", url);
74  if (col!=null) {
75  col = parseInt(col);
76  if ((col!=NaN) && (col>0) && (col<4)) JSROOT.gStyle.DefaultCol = col;
77  }
78  }
79 
83  JSROOT.Painter.createmenu = function(event, menuname) {
84 
85  if (!menuname) menuname = "root_ctx_menu";
86 
87  var xMousePosition = event.clientX + window.pageXOffset;
88  var yMousePosition = event.clientY + window.pageYOffset;
89 
90  var x = document.getElementById(menuname);
91  if (x) x.parentNode.removeChild(x);
92 
93  var d = document.createElement('div');
94  d.setAttribute('class', 'ctxmenu');
95  d.setAttribute('id', menuname);
96  document.body.appendChild(d);
97  d.style.left = xMousePosition + "px";
98  d.style.top = yMousePosition + "px";
99  d.onmouseover = function(e) {
100  this.style.cursor = 'pointer';
101  }
102  d.onclick = function(e) {
103  var x = document.getElementById(menuname);
104  if (x) x.parentNode.removeChild(x);
105  }
106 
107  document.body.onclick = function(e) {
108  var x = document.getElementById(menuname);
109  if (x)
110  x.parentNode.removeChild(x);
111  }
112 
113  return d;
114  }
115 
119  JSROOT.Painter.menuitem = function(menu, txt, func) {
120  var p = document.createElement('p');
121  menu.appendChild(p);
122  p.onclick = func;
123  p.setAttribute('class', 'ctxline');
124  p.innerHTML = txt;
125  }
126 
127  JSROOT.Painter.Coord = {
128  kCARTESIAN : 1,
129  kPOLAR : 2,
130  kCYLINDRICAL : 3,
131  kSPHERICAL : 4,
132  kRAPIDITY : 5
133  }
134 
136  JSROOT.Painter.root_colors = function() {
137  var colorMap = new Array('white', 'black', 'red', 'green', 'blue',
138  'rgb(255,255,0)', 'rgb(255,0,255)', 'rgb(0,255,255)',
139  'rgb(89, 211,84)', 'rgb(89,84,216)', 'rgb(254,254,254)',
140  'rgb(191,181,173)', 'rgb(76,76,76)', 'rgb(102,102,102)',
141  'rgb(127,127,127)', 'rgb(153,153,153)', 'rgb(178,178,178)',
142  'rgb(204,204,204)', 'rgb(229,229,229)', 'rgb(242,242,242)',
143  'rgb(204,198,170)', 'rgb(204,198,170)', 'rgb(193,191,168)',
144  'rgb(186,181,163)', 'rgb(178,165,150)', 'rgb(183,163,155)',
145  'rgb(173,153,140)', 'rgb(155,142,130)', 'rgb(135,102,86)',
146  'rgb(175,206,198)', 'rgb(132,193,163)', 'rgb(137,168,160)',
147  'rgb(130,158,140)', 'rgb(173,188,198)', 'rgb(122,142,153)',
148  'rgb(117,137,145)', 'rgb(104,130,150)', 'rgb(109,122,132)',
149  'rgb(124,153,209)', 'rgb(127,127,155)', 'rgb(170,165,191)',
150  'rgb(211,206,135)', 'rgb(221,186,135)', 'rgb(188,158,130)',
151  'rgb(198,153,124)', 'rgb(191,130,119)', 'rgb(206,94,96)',
152  'rgb(170,142,147)', 'rgb(165,119,122)', 'rgb(147,104,112)',
153  'rgb(211,89,84)');
154 
155  var circleColors = [ 632, 416, 600, 400, 616, 432 ];
156 
157  var rectangleColors = [ 800, 820, 840, 860, 880, 900 ];
158 
159  var set1 = [ 255, 204, 204, 255, 153, 153, 204, 153, 153, 255, 102, 102,
160  204, 102, 102, 153, 102, 102, 255, 51, 51, 204, 51, 51, 153, 51,
161  51, 102, 51, 51, 255, 0, 0, 204, 0, 0, 153, 0, 0, 102, 0, 0, 51, 0, 0 ];
162  var set2 = [ 204, 255, 204, 153, 255, 153, 153, 204, 153, 102, 255, 102,
163  102, 204, 102, 102, 153, 102, 51, 255, 51, 51, 204, 51, 51, 153,
164  51, 51, 102, 51, 0, 255, 0, 0, 204, 0, 0, 153, 0, 0, 102, 0, 0, 51, 0 ];
165  var set3 = [ 204, 204, 255, 153, 153, 255, 153, 153, 204, 102, 102, 255,
166  102, 102, 204, 102, 102, 153, 51, 51, 255, 51, 51, 204, 51, 51,
167  153, 51, 51, 102, 0, 0, 255, 0, 0, 204, 0, 0, 153, 0, 0, 102, 0, 0, 51 ];
168  var set4 = [ 255, 255, 204, 255, 255, 153, 204, 204, 153, 255, 255, 102,
169  204, 204, 102, 153, 153, 102, 255, 255, 51, 204, 204, 51, 153, 153,
170  51, 102, 102, 51, 255, 255, 0, 204, 204, 0, 153, 153, 0, 102, 102, 0, 51, 51, 0 ];
171  var set5 = [ 255, 204, 255, 255, 153, 255, 204, 153, 204, 255, 102, 255,
172  204, 102, 204, 153, 102, 153, 255, 51, 255, 204, 51, 204, 153, 51,
173  153, 102, 51, 102, 255, 0, 255, 204, 0, 204, 153, 0, 153, 102, 0, 102, 51, 0, 51 ];
174  var set6 = [ 204, 255, 255, 153, 255, 255, 153, 204, 204, 102, 255, 255,
175  102, 204, 204, 102, 153, 153, 51, 255, 255, 51, 204, 204, 51, 153,
176  153, 51, 102, 102, 0, 255, 255, 0, 204, 204, 0, 153, 153, 0, 102, 102, 0, 51, 51 ];
177 
178  var circleSets = new Array(set1, set2, set3, set4, set5, set6);
179 
180  var set7 = [ 255, 204, 153, 204, 153, 102, 153, 102, 51, 153, 102, 0,
181  204, 153, 51, 255, 204, 102, 255, 153, 0, 255, 204, 51, 204, 153,
182  0, 255, 204, 0, 255, 153, 51, 204, 102, 0, 102, 51, 0, 153, 51, 0,
183  204, 102, 51, 255, 153, 102, 255, 102, 0, 255, 102, 51, 204, 51, 0,
184  255, 51, 0 ];
185  var set8 = [ 153, 255, 51, 102, 204, 0, 51, 102, 0, 51, 153, 0, 102, 204,
186  51, 153, 255, 102, 102, 255, 0, 102, 255, 51, 51, 204, 0, 51, 255,
187  0, 204, 255, 153, 153, 204, 102, 102, 153, 51, 102, 153, 0, 153,
188  204, 51, 204, 255, 102, 153, 255, 0, 204, 255, 51, 153, 204, 0,
189  204, 255, 0 ];
190  var set9 = [ 153, 255, 204, 102, 204, 153, 51, 153, 102, 0, 153, 102, 51,
191  204, 153, 102, 255, 204, 0, 255, 102, 51, 255, 204, 0, 204, 153, 0,
192  255, 204, 51, 255, 153, 0, 204, 102, 0, 102, 51, 0, 153, 51, 51,
193  204, 102, 102, 255, 153, 0, 255, 153, 51, 255, 102, 0, 204, 51, 0,
194  255, 51 ];
195  var set10 = [ 153, 204, 255, 102, 153, 204, 51, 102, 153, 0, 51, 153, 51,
196  102, 204, 102, 153, 255, 0, 102, 255, 51, 102, 255, 0, 51, 204, 0,
197  51, 255, 51, 153, 255, 0, 102, 204, 0, 51, 102, 0, 102, 153, 51,
198  153, 204, 102, 204, 255, 0, 153, 255, 51, 204, 255, 0, 153, 204, 0,
199  204, 255 ];
200  var set11 = [ 204, 153, 255, 153, 102, 204, 102, 51, 153, 102, 0, 153,
201  153, 51, 204, 204, 102, 255, 153, 0, 255, 204, 51, 255, 153, 0,
202  204, 204, 0, 255, 153, 51, 255, 102, 0, 204, 51, 0, 102, 51, 0,
203  153, 102, 51, 204, 153, 102, 255, 102, 0, 255, 102, 51, 255, 51, 0,
204  204, 51, 0, 255 ];
205  var set12 = [ 255, 51, 153, 204, 0, 102, 102, 0, 51, 153, 0, 51, 204, 51,
206  102, 255, 102, 153, 255, 0, 102, 255, 51, 102, 204, 0, 51, 255, 0,
207  51, 255, 153, 204, 204, 102, 153, 153, 51, 102, 153, 0, 102, 204,
208  51, 153, 255, 102, 204, 255, 0, 153, 204, 0, 153, 255, 51, 204,
209  255, 0, 153 ];
210 
211  var rectSets = new Array(set7, set8, set9, set10, set11, set12);
212 
213  for (var i = 0; i < 6; i++) {
214  for (var j = 0; j < 15; j++) {
215  var colorn = circleColors[i] + j - 10;
216  colorMap[colorn] = 'rgb(' + circleSets[i][3 * j] + ',' + circleSets[i][3 * j + 1] + ',' + circleSets[i][3 * j + 2] + ')';
217  colorn = rectangleColors[i] + j - 9;
218  colorMap[colorn] = 'rgb(' + rectSets[i][3 * j] + ',' + rectSets[i][3 * j + 1] + ',' + rectSets[i][3 * j + 2] + ')';
219  }
220  }
221  return colorMap;
222  }();
223 
224  JSROOT.Painter.adoptRootColors = function(objarr) {
225  if (!objarr || !objarr.arr) return;
226 
227  for (var n in objarr.arr) {
228  var col = objarr.arr[n];
229  if ((col==null) || (col['_typename'] != 'TColor')) continue;
230 
231  var num = col.fNumber;
232  if ((num<0) || (num>4096)) continue;
233 
234  var rgb = "rgb(" + (col.fRed*255).toFixed(0) + "," + (col.fGreen*255).toFixed(0) + "," + (col.fBlue*255).toFixed(0) + ")";
235 
236  if (rgb == 'rgb(255,255,255)') rgb = 'white';
237 
238  while (num>JSROOT.Painter.root_colors.length)
239  JSROOT.Painter.root_colors.push(rgb);
240 
241  if (JSROOT.Painter.root_colors[num] != rgb) {
242  JSROOT.Painter.root_colors[num] = rgb;
243  }
244  }
245  }
246 
247  JSROOT.Painter.root_line_styles = new Array("", "", "3, 3", "1, 2",
248  "3, 4, 1, 4", "5, 3, 1, 3", "5, 3, 1, 3, 1, 3, 1, 3", "5, 5",
249  "5, 3, 1, 3, 1, 3", "20, 5", "20, 10, 1, 10", "1, 2");
250 
251  // Initialize ROOT markers
252  JSROOT.Painter.root_markers = new Array('fcircle', 'fcircle', 'fcross',
253  'dcross', 'ocircle', 'gcross', 'fcircle', 'fcircle', 'fcircle',
254  'fcircle', 'fcircle', 'fcircle', 'fcircle', 'fcircle', 'fcircle',
255  'fcircle', 'fcircle', 'fcircle', 'fcircle', 'fcircle', 'fcircle',
256  'fsquare', 'ftriangle-up', 'ftriangle-down', 'ocircle', 'osquare',
257  'otriangle-up', 'odiamond', 'ocross', 'fstar', 'ostar', 'dcross',
258  'otriangle-down', 'fdiamond', 'fcross');
259 
261  JSROOT.Painter.createAttMarker = function(attmarker) {
262 
263  var marker_name = JSROOT.Painter.root_markers[attmarker['fMarkerStyle']];
264 
265  var info = { shape: 0, toFill: true, toRotate: false };
266 
267  if (typeof (marker_name) != 'undefined') {
268  switch (marker_name.charAt(0)) {
269  case 'd': info.shape = 7; break;
270  case 'o': info.toFill = false; break;
271  case 'g': info.toRotate = true; break;
272  }
273 
274  switch (marker_name.substr(1)) {
275  case "circle": info.shape = 0; break;
276  case "cross": info.shape = 1; break;
277  case "diamond": info.shape = 2; break;
278  case "square": info.shape = 3; break;
279  case "triangle-up": info.shape = 4; break;
280  case "triangle-down": info.shape = 5; break;
281  case "star": info.shape = 6; break;
282  }
283  }
284 
285  var markerSize = attmarker['fMarkerSize'];
286 
287  var markerScale = (info.shape == 0) ? 32 : 64;
288  if (attmarker['fMarkerStyle'] == 1) markerScale = 1;
289 
290  var marker_color = JSROOT.Painter.root_colors[attmarker['fMarkerColor']];
291 
292  var res = { stroke: marker_color, fill: marker_color, marker: "" };
293  if (!info.toFill) res['fill'] = 'none';
294 
295  if (info.shape==6)
296  res['marker'] = "M " + (-4*markerSize) + " " + (-1*markerSize) +
297  " L " + 4*markerSize + " " + (-1*markerSize) +
298  " L " + (-2.4*markerSize) + " " + 4*markerSize +
299  " L 0 " + (-4*markerSize) +
300  " L " + 2.8*markerSize + " " + 4*markerSize + " z";
301  else
302  if (info.shape==7)
303  res['marker'] = "M " + (-4*markerSize) + " " + (-4*markerSize) +
304  " L " + 4*markerSize + " " + 4*markerSize +
305  " M 0 " + (-4*markerSize) + " 0 " + 4*markerSize +
306  " M " + 4*markerSize + " " + (-4*markerSize) +
307  " L " + (-4*markerSize) + " " + 4*markerSize +
308  " M " + (-4*markerSize) + " 0 L " + 4*markerSize + " 0";
309  else
310  res['marker'] = d3.svg.symbol().type(d3.svg.symbolTypes[info.shape]).size(markerSize * markerScale);
311 
312  res.SetMarker = function(selection) {
313  selection.style("fill", this.fill)
314  selection.style("stroke", this.stroke)
315  selection.attr("d", this.marker);
316 
317  }
318  res.func = res.SetMarker.bind(res);
319 
320  return res;
321  }
322 
323  JSROOT.Painter.createAttLine = function(attline, borderw) {
324 
325  var color = 0, _width = 0, style = 0;
326 
327  if (attline=='black') { color = 1; _width = 1; } else
328  if (attline=='none') { _width = 0; } else
329  if (typeof attline == 'object') {
330  if ('fLineColor' in attline) color = attline['fLineColor'];
331  if ('fLineWidth' in attline) _width = attline['fLineWidth'];
332  if ('fLineStyle' in attline) style = attline['fLineStyle'];
333  }
334  if (borderw!=null) _width = borderw;
335 
336  var line = {
337  color: JSROOT.Painter.root_colors[color],
338  width: _width,
339  dash: JSROOT.Painter.root_line_styles[style]
340  };
341 
342  if ((_width==0) || (color==0)) line.color = 'none';
343 
344  line.SetLine = function(selection) {
345  selection.style('stroke', this.color);
346  if (this.color!='none') {
347  selection.style('stroke-width', this.width);
348  selection.style('stroke-dasharray', this.dash);
349  }
350  }
351  line.func = line.SetLine.bind(line);
352 
353  return line;
354  }
355 
356 
357  JSROOT.Painter.clearCuts = function(chopt) {
358  /* decode string "chopt" and remove graphical cuts */
359  var left = chopt.indexOf('[');
360  var right = chopt.indexOf(']');
361  if ((left>=0) && (right>=0) && (left<right))
362  for (var i = left; i <= right; i++) chopt[i] = ' ';
363  return chopt;
364  }
365 
366  JSROOT.Painter.root_fonts = new Array('Arial', 'Times New Roman',
367  'bold Times New Roman', 'bold italic Times New Roman', 'Arial',
368  'oblique Arial', 'bold Arial', 'bold oblique Arial', 'Courier New',
369  'oblique Courier New', 'bold Courier New', 'bold oblique Courier New',
370  'Symbol', 'Times New Roman', 'Wingdings', 'Symbol');
371 
372  JSROOT.Painter.getFontDetails = function(fontIndex, size) {
373 
374  var fontName = JSROOT.Painter.root_fonts[Math.floor(fontIndex / 10)];
375 
376  var res = { name: "Arial", size: 11, weight: null, style: null };
377 
378  if (size != null) res.size = Math.round(size);
379 
380  if (fontName == null)
381  fontName = "";
382 
383  if (fontName.indexOf("bold") != -1) {
384  res.weight = "bold";
385  // The first 5 characters are removed because "bold " is always first
386  // when it occurs
387  fontName = fontName.substring(5, fontName.length);
388  }
389  if (fontName.charAt(0) == 'i') {
390  res.style = "italic";
391  fontName = fontName.substring(7, fontName.length);
392  } else if (fontName.charAt(0) == 'o') {
393  res.style = "oblique";
394  fontName = fontName.substring(8, fontName.length);
395  }
396  if (name == 'Symbol') {
397  res.weight = null;
398  res.style = null;
399  }
400 
401  res.name = fontName;
402 
403  res.SetFont = function(selection) {
404  selection.attr("font-family", this.name)
405  .attr("font-size", this.size)
406  .attr("xml:space","preserve");
407  if (this.weight!=null)
408  selection.attr("font-weight", this.weight);
409  if (this.style!=null)
410  selection.attr("font-style", this.style);
411  }
412 
413  res.stringWidth = function(svg, line) {
414  /* compute the bounding box of a string by using temporary svg:text */
415  var text = svg.append("svg:text")
416  .attr("class", "temp_text")
417  .attr("xml:space","preserve")
418  .style("opacity", 0)
419  .text(line);
420  this.SetFont(text);
421  var w = text.node().getBBox().width;
422  text.remove();
423  return w;
424  }
425 
426  res.func = res.SetFont.bind(res);
427 
428  return res;
429  }
430 
431 
432  JSROOT.Painter.padtoX = function(pad, x) {
433  // Convert x from pad to X.
434  if (pad['fLogx'] && x < 50)
435  return Math.exp(2.302585092994 * x);
436  return x;
437  }
438 
439  JSROOT.Painter.moveChildToEnd = function(child) {
440  if (!child) return;
441  var prnt = child.node().parentNode;
442  prnt.removeChild(child.node());
443  prnt.appendChild(child.node());
444  }
445 
446  JSROOT.Painter.ytoPad = function(y, pad) {
447  if (pad['fLogy']) {
448  if (y > 0)
449  y = JSROOT.Math.log10(y);
450  else
451  y = pad['fUymin'];
452  }
453  return y;
454  };
455 
469  JSROOT.Painter.HLStoRGB = function(h, l, s) {
470  var r, g, b;
471  if (s < 1e-300) {
472  r = g = b = l; // achromatic
473  } else {
474  function hue2rgb(p, q, t) {
475  if (t < 0) t += 1;
476  if (t > 1) t -= 1;
477  if (t < 1 / 6) return p + (q - p) * 6 * t;
478  if (t < 1 / 2) return q;
479  if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
480  return p;
481  }
482  var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
483  var p = 2 * l - q;
484  r = hue2rgb(p, q, h + 1 / 3);
485  g = hue2rgb(p, q, h);
486  b = hue2rgb(p, q, h - 1 / 3);
487  }
488  return 'rgb(' + Math.round(r * 255) + ', ' + Math.round(g * 255) + ', ' + Math.round(b * 255) + ')';
489  }
490 
491  JSROOT.Painter.chooseTimeFormat = function(range, nticks) {
492  if (nticks < 1) nticks = 1;
493  var awidth = range / nticks;
494  var reasformat = 0;
495 
496  // code from TAxis::ChooseTimeFormat
497  // width in seconds ?
498  if (awidth >= .5) {
499  reasformat = 1;
500  // width in minutes ?
501  if (awidth >= 30) {
502  awidth /= 60; reasformat = 2;
503  // width in hours ?
504  if (awidth >= 30) {
505  awidth /= 60; reasformat = 3;
506  // width in days ?
507  if (awidth >= 12) {
508  awidth /= 24; reasformat = 4;
509  // width in months ?
510  if (awidth >= 15.218425) {
511  awidth /= 30.43685; reasformat = 5;
512  // width in years ?
513  if (awidth >= 6) {
514  awidth /= 12; reasformat = 6;
515  if (awidth >= 2) {
516  awidth /= 12; reasformat = 7;
517  }
518  }
519  }
520  }
521  }
522  }
523  }
524 
525  switch (reasformat) {
526  case 0: return "%S";
527  case 1: return "%Mm%S";
528  case 2: return "%Hh%M";
529  case 3: return "%d-%Hh";
530  case 4: return "%d/%m";
531  case 5: return "%d/%m/%y";
532  case 6: return "%d/%m/%y";
533  case 7: return "%m/%y";
534  }
535 
536  return "%Y";
537  }
538 
539  JSROOT.Painter.getTimeFormat = function(axis) {
540  var timeFormat = axis['fTimeFormat'];
541  var idF = timeFormat.indexOf('%F');
542  if (idF >= 0)
543  return timeFormat.substr(0, idF);
544  return timeFormat;
545  }
546 
547  JSROOT.Painter.getTimeOffset = function(axis) {
548  var timeFormat = axis['fTimeFormat'];
549 
550  var idF = timeFormat.indexOf('%F');
551 
552  if (idF >= 0) {
553  var lnF = timeFormat.length;
554  var stringtimeoffset = timeFormat.substr(idF + 2, lnF);
555  for (var i = 0; i < 3; ++i)
556  stringtimeoffset = stringtimeoffset.replace('-', '/');
557  // special case, used from DABC painters
558  if ((stringtimeoffset == "0") || (stringtimeoffset == "")) return 0;
559 
560  var stimeoffset = new Date(stringtimeoffset);
561  var timeoffset = stimeoffset.getTime();
562  var ids = stringtimeoffset.indexOf('s');
563  if (ids >= 0) {
564  var lns = stringtimeoffset.length;
565  var sdp = stringtimeoffset.substr(ids + 1, lns);
566  var dp = parseFloat(sdp);
567  timeoffset += dp;
568  }
569  return timeoffset;
570  }
571 
572  return JSROOT.gStyle['TimeOffset'];
573  }
574 
575  JSROOT.Painter.formatExp = function(label) {
576  var str = label;
577  if (parseFloat(str) == 1.0) return '1';
578  if (parseFloat(str) == 10.0) return '10';
579  var str = str.replace('e+', 'x10@');
580  var str = str.replace('e-', 'x10@-');
581  var _val = str.substring(0, str.indexOf('@'));
582  var _exp = str.substr(str.indexOf('@'));
583  _val = _val.replace('@', '');
584  _exp = _exp.replace('@', '');
585  var u, size = _exp.length;
586  for (var j = 0; j < size; ++j) {
587  var u, c = _exp.charAt(j);
588  if (c == '+') u = '\u207A'; else
589  if (c == '-') u = '\u207B'; else {
590  var e = parseInt(c);
591  if (e == 1) u = String.fromCharCode(0xB9); else
592  if (e > 1 && e < 4) u = String.fromCharCode(0xB0 + e); else
593  u = String.fromCharCode(0x2070 + e);
594  }
595  _exp = _exp.replace(c, u);
596  }
597  _val = _val.replace('1x', '');
598  return _val + _exp;
599  };
600 
601  JSROOT.Painter.translateExp = function(str) {
602  var lstr = str.match(/\^{[0-9]*}/gi);
603  if (lstr != null) {
604  var symbol = '';
605  for (var i = 0; i < lstr.length; ++i) {
606  symbol = lstr[i].replace(' ', '');
607  symbol = symbol.replace('^{', ''); // &sup
608  symbol = symbol.replace('}', ''); // ;
609  var size = symbol.length;
610  for (var j = 0; j < size; ++j) {
611  var c = symbol.charAt(j);
612  var u, e = parseInt(c);
613  if (e == 1) u = String.fromCharCode(0xB9);
614  else if (e > 1 && e < 4) u = String.fromCharCode(0xB0 + e);
615  else u = String.fromCharCode(0x2070 + e);
616  symbol = symbol.replace(c, u);
617  }
618  str = str.replace(lstr[i], symbol);
619  }
620  }
621  return str;
622  };
623 
624  JSROOT.Painter.symbols_map = {
625  // greek letters
626  '#alpha' : '\u03B1',
627  '#beta' : '\u03B2',
628  '#chi' : '\u03C7',
629  '#delta' : '\u03B4',
630  '#varepsilon' : '\u03B5',
631  '#phi' : '\u03C6',
632  '#gamma' : '\u03B3',
633  '#eta' : '\u03B7',
634  '#iota' : '\u03B9',
635  '#varphi' : '\u03C6',
636  '#kappa' : '\u03BA',
637  '#lambda' : '\u03BB',
638  '#mu' : '\u03BC',
639  '#nu' : '\u03BD',
640  '#omicron' : '\u03BF',
641  '#pi' : '\u03C0',
642  '#theta' : '\u03B8',
643  '#rho' : '\u03C1',
644  '#sigma' : '\u03C3',
645  '#tau' : '\u03C4',
646  '#upsilon' : '\u03C5',
647  '#varomega' : '\u03D6',
648  '#omega' : '\u03C9',
649  '#xi' : '\u03BE',
650  '#psi' : '\u03C8',
651  '#zeta' : '\u03B6',
652  '#Alpha' : '\u0391',
653  '#Beta' : '\u0392',
654  '#Chi' : '\u03A7',
655  '#Delta' : '\u0394',
656  '#Epsilon' : '\u0395',
657  '#Phi' : '\u03A6',
658  '#Gamma' : '\u0393',
659  '#Eta' : '\u0397',
660  '#Iota' : '\u0399',
661  '#vartheta' : '\u03D1',
662  '#Kappa' : '\u039A',
663  '#Lambda' : '\u039B',
664  '#Mu' : '\u039C',
665  '#Nu' : '\u039D',
666  '#Omicron' : '\u039F',
667  '#Pi' : '\u03A0',
668  '#Theta' : '\u0398',
669  '#Rho' : '\u03A1',
670  '#Sigma' : '\u03A3',
671  '#Tau' : '\u03A4',
672  '#Upsilon' : '\u03A5',
673  '#varsigma' : '\u03C2',
674  '#Omega' : '\u03A9',
675  '#Xi' : '\u039E',
676  '#Psi' : '\u03A8',
677  '#Zeta' : '\u0396',
678  '#varUpsilon' : '\u03D2',
679  '#epsilon' : '\u03B5',
680  // math symbols
681 
682  '#sqrt' : '\u221A',
683 
684  // from TLatex tables #2 & #3
685  '#leq' : '\u2264',
686  '#/' : '\u2044',
687  '#infty' : '\u221E',
688  '#voidb' : '\u0192',
689  '#club' : '\u2663',
690  '#diamond' : '\u2666',
691  '#heart' : '\u2665',
692  '#spade' : '\u2660',
693  '#leftrightarrow' : '\u2194',
694  '#leftarrow' : '\u2190',
695  '#uparrow' : '\u2191',
696  '#rightarrow' : '\u2192',
697  '#downarrow' : '\u2193',
698  '#circ' : '\u02C6', // ^
699  '#pm' : '\xB1',
700  '#doublequote' : '\u2033',
701  '#geq' : '\u2265',
702  '#times' : '\xD7',
703  '#propto' : '\u221D',
704  '#partial' : '\u2202',
705  '#bullet' : '\u2022',
706  '#divide' : '\xF7',
707  '#neq' : '\u2260',
708  '#equiv' : '\u2261',
709  '#approx' : '\u2248', // should be \u2245 ?
710  '#3dots' : '\u2026',
711  '#cbar' : '\u007C',
712  '#topbar' : '\xAF',
713  '#downleftarrow' : '\u21B5',
714  '#aleph' : '\u2135',
715  '#Jgothic' : '\u2111',
716  '#Rgothic' : '\u211C',
717  '#voidn' : '\u2118',
718  '#otimes' : '\u2297',
719  '#oplus' : '\u2295',
720  '#oslash' : '\u2205',
721  '#cap' : '\u2229',
722  '#cup' : '\u222A',
723  '#supseteq' : '\u2287',
724  '#supset' : '\u2283',
725  '#notsubset' : '\u2284',
726  '#subseteq' : '\u2286',
727  '#subset' : '\u2282',
728  '#int' : '\u222B',
729  '#in' : '\u2208',
730  '#notin' : '\u2209',
731  '#angle' : '\u2220',
732  '#nabla' : '\u2207',
733  '#oright' : '\xAE',
734  '#ocopyright' : '\xA9',
735  '#trademark' : '\u2122',
736  '#prod' : '\u220F',
737  '#surd' : '\u221A',
738  '#upoint' : '\u22C5',
739  '#corner' : '\xAC',
740  '#wedge' : '\u2227',
741  '#vee' : '\u2228',
742  '#Leftrightarrow' : '\u21D4',
743  '#Leftarrow' : '\u21D0',
744  '#Uparrow' : '\u21D1',
745  '#Rightarrow' : '\u21D2',
746  '#Downarrow' : '\u21D3',
747  '#LT' : '\x3C',
748  '#void1' : '\xAE',
749  '#copyright' : '\xA9',
750  '#void3' : '\u2122',
751  '#sum' : '\u2211',
752  '#arctop' : '',
753  '#lbar' : '',
754  '#arcbottom' : '',
755  '#void8' : '',
756  '#bottombar' : '\u230A',
757  '#arcbar' : '',
758  '#ltbar' : '',
759  '#AA' : '\u212B',
760  '#aa' : '\u00E5',
761  '#void06' : '',
762  '#GT' : '\x3E',
763  '#forall' : '\u2200',
764  '#exists' : '\u2203',
765  '#bar' : '',
766  '#vec' : '',
767  '#dot' : '\u22C5',
768  '#hat' : '\xB7',
769  '#ddot' : '',
770  '#acute' : '\acute',
771  '#grave' : '',
772  '#check' : '\u2713',
773  '#tilde' : '\u02DC',
774  '#slash' : '\u2044',
775  '#hbar' : '\u0127',
776  '#box' : '',
777  '#Box' : '',
778  '#parallel' : '',
779  '#perp' : '\u22A5',
780  '#odot' : ''
781  };
782 
783  JSROOT.Painter.translateLaTeX = function(string) {
784  var str = string;
785  str = this.translateExp(str);
786  while (str.indexOf('^{o}') != -1)
787  str = str.replace('^{o}', '\xBA');
788  var lstr = str.match(/\#sqrt{(.*?)}/gi);
789  if (lstr != null)
790  for (var i = 0; i < lstr.length; ++i) {
791  var symbol = lstr[i].replace(' ', '');
792  symbol = symbol.replace('#sqrt{', '#sqrt');
793  symbol = symbol.replace('}', '');
794  str = str.replace(lstr[i], symbol);
795  }
796  lstr = str.match(/\_{(.*?)}/gi);
797  if (lstr != null)
798  for (var i = 0; i < lstr.length; ++i) {
799  var symbol = lstr[i].replace(' ', '');
800  symbol = symbol.replace('_{', ''); // &sub
801  symbol = symbol.replace('}', ''); // ;
802  str = str.replace(lstr[i], symbol);
803  }
804  lstr = str.match(/\^{(.*?)}/gi);
805  if (lstr != null)
806  for (i = 0; i < lstr.length; ++i) {
807  var symbol = lstr[i].replace(' ', '');
808  symbol = symbol.replace('^{', ''); // &sup
809  symbol = symbol.replace('}', ''); // ;
810  str = str.replace(lstr[i], symbol);
811  }
812  while (str.indexOf('#/') != -1)
813  str = str.replace('#/', JSROOT.Painter.symbols_map['#/']);
814  for ( var x in JSROOT.Painter.symbols_map) {
815  while (str.indexOf(x) != -1)
816  str = str.replace(x, JSROOT.Painter.symbols_map[x]);
817  }
818  return str;
819  }
820 
821  // ==============================================================================
822 
823  JSROOT.TBasePainter = function() {
824  }
825 
826  JSROOT.TBasePainter.prototype.Cleanup = function() {
827  // generic method to cleanup painter
828  }
829 
830  JSROOT.TBasePainter.prototype.GetObject = function() {
831  return null;
832  }
833 
834  JSROOT.TBasePainter.prototype.UpdateObject = function(obj) {
835  return false;
836  }
837 
838  JSROOT.TBasePainter.prototype.RedrawPad = function(resize) {
839  }
840 
841  JSROOT.TBasePainter.prototype.RedrawObject = function(obj) {
842  if (this.UpdateObject(obj)) {
843  var current = document.body.style.cursor;
844  document.body.style.cursor = 'wait';
845  this.RedrawPad();
846  document.body.style.cursor = current;
847  }
848  }
849 
850  JSROOT.TBasePainter.prototype.CheckResize = function(force) {
851  }
852 
853  JSROOT.TBasePainter.prototype.SetDivId = function(divid) {
854  // base painter does not creates canvas or frames
855  // it registered in the first child element
856 
857  this['divid'] = divid;
858 
859  $("#" + divid).children().eq(0).prop('painter', this);
860  }
861 
862  // ==============================================================================
863 
864  JSROOT.TObjectPainter = function(obj) {
865  JSROOT.TBasePainter.call(this);
866  this.obj_typename = (obj!=null) && ('_typename' in obj) ? obj['_typename'] : "";
867  this.draw_g = null; // container for all draw objects
868  this.pad_name = ""; // name of pad where object is drawn
869  this.main = null; // main painter, received from pad
870  }
871 
872  JSROOT.TObjectPainter.prototype = Object.create(JSROOT.TBasePainter.prototype);
873 
874  JSROOT.TObjectPainter.prototype.CheckResize = function(force) {
875  // no canvas - no resize
876  var can = this.svg_canvas();
877 
878  var pad_painter = can ? can['pad_painter'] : null;
879 
880  if (pad_painter) pad_painter.CheckCanvasResize();
881  }
882 
883  JSROOT.TObjectPainter.prototype.RemoveDrawG = function() {
884  // generic method to delete all graphical elements, associated with
885  // painter may not work for all cases
886 
887  if (this.draw_g != null) {
888  this.draw_g.remove();
889  this.draw_g = null;
890  }
891  }
892 
893  JSROOT.TObjectPainter.prototype.RecreateDrawG = function(take_pad, layer) {
894  //this.RemoveDrawG();
895 
896  if (this.draw_g)
897  this.draw_g.selectAll("*").remove();
898 
899  if (take_pad) {
900  if (layer==null) layer = ".text_layer"
901  if (!this.draw_g)
902  this.draw_g = this.svg_pad(true).select(layer).append("svg:g");
903  } else {
904  var frame = this.svg_frame(true);
905 
906  var w = frame.attr("width");
907  var h = frame.attr("height");
908 
909  if (!this.draw_g) {
910  if (layer==null) layer = ".main_layer";
911  this.draw_g = frame.select(layer).append("svg");
912  }
913 
914  this.draw_g.attr("x", 0)
915  .attr("y", 0)
916  .attr("width",w)
917  .attr("height", h)
918  .attr("viewBox", "0 0 " + w + " " + h)
919  .attr('overflow', 'hidden');
920  }
921  }
922 
924  JSROOT.TObjectPainter.prototype.svg_canvas = function(asselect) {
925  var res = d3.select("#" + this.divid + " .root_canvas");
926  return asselect ? res : res.node();
927  }
928 
930  JSROOT.TObjectPainter.prototype.svg_pad = function(asselect) {
931  var c = this.svg_canvas(true);
932  if (this.pad_name != '')
933  c = c.select("[pad=" + this.pad_name + ']');
934  return asselect ? c : c.node();
935  }
936 
937  JSROOT.TObjectPainter.prototype.root_pad = function() {
938  var p = this.svg_pad();
939  var pad_painter = p ? p['pad_painter'] : null;
940  return pad_painter ? pad_painter.pad : null;
941  }
942 
944  JSROOT.TObjectPainter.prototype.svg_frame = function(asselect) {
945  var f = this.svg_pad(true).select(".root_frame");
946  return asselect ? f : f.node();
947  }
948 
950  JSROOT.TObjectPainter.prototype.main_painter = function() {
951  if (!this.main) {
952  var svg_p = this.svg_pad();
953  if (svg_p) this.main = svg_p['mainpainter'];
954  }
955  return this.main;
956  }
957 
958  JSROOT.TObjectPainter.prototype.is_main_painter = function() {
959  return this == this.main_painter();
960  }
961 
962  JSROOT.TObjectPainter.prototype.SetDivId = function(divid, is_main) {
963  // Assigns id of top element (normally <div></div> where drawing is done
964  // is_main - -1 - not add to painters list,
965  // 0 - normal painter,
966  // 1 - major objects like TH1/TH2
967  // In some situations canvas may not exists - for instance object drawn as html, not as svg.
968  // In such case the only painter will be assigned to the first element
969 
970  this['divid'] = divid;
971 
972  if (is_main == null) is_main = 0;
973 
974  this['create_canvas'] = false;
975 
976  // SVG element where canvas is drawn
977  var svg_c = this.svg_canvas();
978 
979  if ((svg_c==null) && (is_main>0)) {
980  JSROOT.Painter.drawCanvas(divid, null);
981  svg_c = this.svg_canvas();
982  this['create_canvas'] = true;
983  }
984 
985  if (svg_c == null) {
986  if ((is_main < 0) || (this.obj_typename=="TCanvas")) return;
987 
988  console.log("Special case for " + this.obj_typename + " assign painter to first DOM element");
989  $("#" + divid).children().eq(0).prop('painter', this);
990  return;
991  }
992 
993  // SVG element where current pad is drawn (can be canvas itself)
994  this.pad_name = svg_c['current_pad'];
995 
996  if (is_main < 0) return;
997 
998  // create TFrame element if not exists when
999  if ((is_main > 0) && (this.svg_frame()==null)) {
1000  JSROOT.Painter.drawFrame(divid, null);
1001  if (this.svg_frame()==null) return alert("Fail to draw dummy TFrame");
1002  this['create_canvas'] = true;
1003  }
1004 
1005  var svg_p = this.svg_pad();
1006  if (svg_p['pad_painter'] != this)
1007  svg_p['pad_painter'].painters.push(this);
1008 
1009  if ((is_main > 0) && (svg_p['mainpainter']==null))
1010  // when this is first main painter in the pad
1011  svg_p['mainpainter'] = this;
1012  }
1013 
1014  JSROOT.TObjectPainter.prototype.createAttFill = function(attfill, pattern, color) {
1015 
1016  if ((pattern==null) && attfill) pattern = attfill['fFillStyle'];
1017  if ((color==null) && attfill) color = attfill['fFillColor'];
1018 
1019  var fill = { color: "none" };
1020  fill.SetFill = function(selection) {
1021  selection.style('fill', this.color);
1022  if ('antialias' in this)
1023  selection.style('antialias', this.antialias);
1024  }
1025  fill.func = fill.SetFill.bind(fill);
1026 
1027  if (typeof attfill == 'string') {
1028  fill.color = attfill;
1029  return fill;
1030  }
1031 
1032  if ((pattern < 1001) || ((pattern >= 4000) && (pattern <= 4100))) return fill;
1033 
1034  fill.color = JSROOT.Painter.root_colors[color];
1035  if (typeof fill.color != 'string') fill.color = "none";
1036 
1037  var svg = this.svg_canvas(true);
1038 
1039  if ((pattern < 3000) || (pattern>3025) || svg.empty()) return fill;
1040 
1041  var id = "pat_" + pattern + "_" + color;
1042 
1043  fill.color = "url(#" + id + ")";
1044  fill.antialias = false;
1045 
1046  if (document.getElementById(id) != null) return fill;
1047 
1048  var line_color = JSROOT.Painter.root_colors[color];
1049 
1050  switch (pattern) {
1051  case 3001:
1052  svg.append('svg:pattern')
1053  .attr("id", id).attr("patternUnits","userSpaceOnUse")
1054  .attr("width", "3px").attr("height", "2px").style("stroke", line_color)
1055  .append('svg:rect')
1056  .attr("x", 0).attr("y", 0).attr("width", 1).attr("height", 1).style("stroke",line_color)
1057  .append('svg:rect')
1058  .attr("x", 2).attr("y", 0).attr("width", 1).attr("height", 1).style("stroke", line_color)
1059  .append('svg:rect')
1060  .attr("x", 1).attr("y", 1).attr("width", 1).attr("height", 1).style("stroke", line_color);
1061  break;
1062  case 3002:
1063  svg.append('svg:pattern')
1064  .attr("id", id).attr("patternUnits", "userSpaceOnUse")
1065  .attr("width", "4px").attr("height", "2px").style("stroke", line_color)
1066  .append('svg:rect')
1067  .attr("x", 1).attr("y", 0).attr("width", 1).attr("height", 1).style("stroke", line_color)
1068  .append('svg:rect')
1069  .attr("x", 3).attr("y", 1).attr("width", 1).attr("height", 1).style("stroke", line_color);
1070  break;
1071  case 3003:
1072  svg.append('svg:pattern')
1073  .attr("id", id).attr("patternUnits", "userSpaceOnUse")
1074  .attr("width", "4px").attr("height", "4px").style("stroke", line_color)
1075  .append('svg:rect')
1076  .attr("x", 2).attr("y", 1).attr("width", 1).attr("height", 1).style("stroke", line_color)
1077  .append('svg:rect')
1078  .attr("x", 0).attr("y", 3).attr("width", 1).attr("height", 1).style("stroke", line_color);
1079  break;
1080  case 3004:
1081  svg.append('svg:pattern')
1082  .attr("id", id).attr("patternUnits", "userSpaceOnUse")
1083  .attr("width", "8px").attr("height", "8px").style("stroke", line_color)
1084  .append("svg:line")
1085  .attr("x1", 8).attr("y1", 0).attr("x2", 0).attr("y2", 8)
1086  .style("stroke",line_color).style("stroke-width", 1);
1087  break;
1088  case 3005:
1089  svg.append('svg:pattern')
1090  .attr("id", id).attr("patternUnits", "userSpaceOnUse")
1091  .attr("width", "8px").attr("height", "8px").style("stroke", line_color)
1092  .append("svg:line")
1093  .attr("x1", 0).attr("y1", 0).attr("x2", 8).attr("y2", 8)
1094  .style("stroke",line_color).style("stroke-width", 1);
1095  break;
1096  case 3006:
1097  svg.append('svg:pattern')
1098  .attr("id", id).attr("patternUnits", "userSpaceOnUse")
1099  .attr("width", "4px").attr("height", "4px").style("stroke", line_color)
1100  .append("svg:line")
1101  .attr("x1", 1).attr("y1", 0).attr("x2", 1).attr("y2", 3)
1102  .style("stroke",line_color).style("stroke-width", 1);
1103  break;
1104  case 3007:
1105  svg.append('svg:pattern')
1106  .attr("id", id).attr("patternUnits","userSpaceOnUse")
1107  .attr("width", "4px").attr("height", "4px").style("stroke", line_color)
1108  .append("svg:line")
1109  .attr("x1", 0).attr("y1", 1).attr("x2", 3).attr("y2", 1)
1110  .style("stroke",line_color).style("stroke-width", 1);
1111  break;
1112  default: /* == 3004 */
1113  svg.append('svg:pattern')
1114  .attr("id", id).attr("patternUnits","userSpaceOnUse")
1115  .attr("width", "8px").attr("height", "8px").style("stroke", line_color)
1116  .append("svg:line")
1117  .attr("x1", 8).attr("y1", 0).attr("x2", 0).attr("y2", 8)
1118  .style("stroke",line_color).style("stroke-width", 1);
1119  break;
1120  }
1121 
1122  return fill;
1123  }
1124 
1125 
1126  JSROOT.TObjectPainter.prototype.ForEachPainter = function(userfunc) {
1127  // Iterate over all known painters
1128  var svg_c = this.svg_canvas();
1129  if (svg_c!=null) {
1130  userfunc(svg_c['pad_painter']);
1131  var painters = svg_c['pad_painter'].painters;
1132  for (var k in painters) userfunc(painters[k]);
1133  } else {
1134  var painter = $("#" + this.divid).children().eq(0).prop('painter');
1135  if (painter!=null) userfunc(painter);
1136  }
1137  }
1138 
1139  JSROOT.TObjectPainter.prototype.Cleanup = function() {
1140  // generic method to cleanup painters
1141  $("#" + this.divid).empty();
1142  }
1143 
1144  JSROOT.TObjectPainter.prototype.RedrawPad = function(resize) {
1145  // call Redraw methods for each painter in the frame
1146  // if selobj specified, painter with selected object will be redrawn
1147 
1148  var pad = this.svg_pad();
1149 
1150  var pad_painter = pad ? pad['pad_painter'] : null;
1151 
1152  if (pad_painter) pad_painter.Redraw(true);
1153  }
1154 
1155  JSROOT.TObjectPainter.prototype.RemoveDrag = function(id) {
1156  var drag_rect_name = id + "_drag_rect";
1157  var resize_rect_name = id + "_resize_rect";
1158  if (this[drag_rect_name]) {
1159  this[drag_rect_name].remove();
1160  this[drag_rect_name] = null;
1161  }
1162  if (this[resize_rect_name]) {
1163  this[resize_rect_name].remove();
1164  this[resize_rect_name] = null;
1165  }
1166  }
1167 
1168  JSROOT.TObjectPainter.prototype.AddDrag = function(id, draw_g, callback) {
1169  if (!JSROOT.gStyle.MoveResize) return;
1170 
1171  var pthis = this;
1172 
1173  var drag_rect_name = id + "_drag_rect";
1174  var resize_rect_name = id + "_resize_rect";
1175 
1176  var rect_width = function() { return Number(draw_g.attr("width")); }
1177  var rect_height = function() { return Number(draw_g.attr("height")); }
1178 
1179  var acc_x = 0, acc_y = 0, pad_w = 1, pad_h = 1;
1180 
1181  var drag_move = d3.behavior.drag().origin(Object)
1182  .on("dragstart", function() {
1183  d3.event.sourceEvent.preventDefault();
1184 
1185  acc_x = 0; acc_y = 0;
1186  pad_w = Number(pthis.svg_pad(true).attr("width")) - rect_width();
1187  pad_h = Number(pthis.svg_pad(true).attr("height")) - rect_height();
1188 
1189  pthis[drag_rect_name] =
1190  pthis.svg_pad(true)
1191  .append("rect")
1192  .attr("class", "zoom")
1193  .attr("id", drag_rect_name)
1194  .attr("x", draw_g.attr("x"))
1195  .attr("y", draw_g.attr("y"))
1196  .attr("width", rect_width())
1197  .attr("height", rect_height())
1198  .style("cursor", "move");
1199  }).on("drag", function() {
1200  d3.event.sourceEvent.preventDefault();
1201 
1202  var x = Number(pthis[drag_rect_name].attr("x"));
1203  var y = Number(pthis[drag_rect_name].attr("y"));
1204  var dx = d3.event.dx, dy = d3.event.dy;
1205 
1206  if (((acc_x<0) && (dx>0)) || ((acc_x>0) && (dx<0))) { acc_x += dx; dx = 0; }
1207  if (((acc_y<0) && (dy>0)) || ((acc_y>0) && (dy<0))) { acc_y += dy; dy = 0; }
1208 
1209  if ((x + dx < 0) || (x +dx > pad_w)) acc_x += dx; else x+=dx;
1210  if ((y+dy < 0) || (y+dy > pad_h)) acc_y += dy; else y += dy;
1211 
1212  pthis[drag_rect_name].attr("x", x);
1213  pthis[drag_rect_name].attr("y", y);
1214 
1215  JSROOT.Painter.moveChildToEnd(pthis[drag_rect_name]);
1216 
1217  d3.event.sourceEvent.stopPropagation();
1218  }).on("dragend", function() {
1219  d3.event.sourceEvent.preventDefault();
1220 
1221  pthis[drag_rect_name].style("cursor", "auto");
1222 
1223  var x = Number(pthis[drag_rect_name].attr("x"));
1224  var y = Number(pthis[drag_rect_name].attr("y"));
1225 
1226  var dx = x - Number(draw_g.attr("x"));
1227  var dy = y - Number(draw_g.attr("y"));
1228 
1229  pthis[drag_rect_name].remove();
1230  pthis[drag_rect_name] = null;
1231 
1232  draw_g.attr("x", x).attr("y", y);
1233 
1234  callback.move(x, y, dx, dy);
1235 
1236  pthis[resize_rect_name]
1237  .attr("x", rect_width() - 20)
1238  .attr("y", rect_height() - 20);
1239  });
1240 
1241  var drag_resize = d3.behavior.drag().origin(Object)
1242  .on( "dragstart", function() {
1243  d3.event.sourceEvent.stopPropagation();
1244  d3.event.sourceEvent.preventDefault();
1245 
1246  acc_x = 0; acc_y = 0;
1247  pad_w = Number(pthis.svg_pad(true).attr("width")) - Number(draw_g.attr("x"));
1248  pad_h = Number(pthis.svg_pad(true).attr("height")) - Number(draw_g.attr("y"));
1249  pthis[drag_rect_name] =
1250  pthis.svg_pad(true)
1251  .append("rect")
1252  .attr("class", "zoom")
1253  .attr("id", drag_rect_name)
1254  .attr("x", draw_g.attr("x"))
1255  .attr("y", draw_g.attr("y"))
1256  .attr("width", rect_width())
1257  .attr("height", rect_height())
1258  .style("cursor", "se-resize");
1259  }).on("drag", function() {
1260  d3.event.sourceEvent.preventDefault();
1261 
1262  var w = Number(pthis[drag_rect_name].attr("width"));
1263  var h = Number(pthis[drag_rect_name].attr("height"));
1264  var dx = d3.event.dx, dy = d3.event.dy;
1265  if ((acc_x>0) && (dx<0)) { acc_x += dx; dx = 0; }
1266  if ((acc_y>0) && (dy<0)) { acc_y += dy; dy = 0; }
1267  if (w+dx > pad_w) acc_x += dx; else w+=dx;
1268  if (h+dy > pad_h) acc_y += dy; else h+=dy;
1269  pthis[drag_rect_name].attr("width", w);
1270  pthis[drag_rect_name].attr("height", h);
1271 
1272  JSROOT.Painter.moveChildToEnd(pthis[drag_rect_name]);
1273 
1274  d3.event.sourceEvent.stopPropagation();
1275  }).on( "dragend", function() {
1276  d3.event.sourceEvent.preventDefault();
1277  pthis[drag_rect_name].style("cursor", "auto");
1278 
1279  var newwidth = Number(pthis[drag_rect_name].attr("width"));
1280  var newheight = Number(pthis[drag_rect_name].attr("height"));
1281 
1282  draw_g.attr('width', newwidth).attr('height', newheight);
1283 
1284  pthis[drag_rect_name].remove();
1285  pthis[drag_rect_name] = null;
1286 
1287  callback.resize(newwidth, newheight);
1288 
1289  // do it after call-back - rectangle has correct coordinates
1290  pthis[resize_rect_name]
1291  .attr("x", newwidth - 20)
1292  .attr("y", newheight - 20);
1293  });
1294 
1295  draw_g.call(drag_move);
1296 
1297  this[resize_rect_name] =
1298  draw_g.append("rect")
1299  .attr("class", resize_rect_name)
1300  .style("opacity", "0")
1301  .style("cursor", "se-resize")
1302  .attr("x", rect_width() - 20)
1303  .attr("y", rect_height() - 20)
1304  .attr("width", 20)
1305  .attr("height", 20)
1306  .call(drag_resize);
1307  }
1308 
1309  JSROOT.TObjectPainter.prototype.FindPainterFor = function(selobj,selname) {
1310  // try to find painter for sepcified object
1311  // can be used to find painter for some special objects, registered as
1312  // histogram functions
1313 
1314  var painters = this.svg_pad() ? this.svg_pad().pad_painter.painters : null;
1315  if (painters == null) return null;
1316 
1317  for (var n in painters) {
1318  var pobj = painters[n].GetObject();
1319  if (pobj==null) continue;
1320 
1321  if (selobj && (pobj === selobj)) return painters[n];
1322 
1323  if (selname && ('fName' in pobj) && (pobj['fName']==selname)) return painters[n];
1324  }
1325 
1326  return null;
1327  }
1328 
1329  JSROOT.TObjectPainter.prototype.Redraw = function() {
1330  // basic method, should be reimplemented in all derived objects
1331  // for the case when drawing should be repeated, probably with different
1332  // options
1333  }
1334 
1335  // ===========================================================
1336 
1337  JSROOT.TFramePainter = function(tframe) {
1338  JSROOT.TObjectPainter.call(this, tframe);
1339  this.tframe = tframe;
1340  }
1341 
1342  JSROOT.TFramePainter.prototype = Object.create(JSROOT.TObjectPainter.prototype);
1343 
1344  JSROOT.TFramePainter.prototype.GetObject = function() {
1345  return this.tframe;
1346  }
1347 
1348  JSROOT.TFramePainter.prototype.Shrink = function(shrink_left, shrink_right) {
1349  var ndc = this.svg_frame() ? this.svg_frame()['NDC'] : null;
1350  if (ndc) {
1351  ndc.x1 += shrink_left;
1352  ndc.x2 -= shrink_right;
1353  }
1354  }
1355 
1356  JSROOT.TFramePainter.prototype.DrawFrameSvg = function() {
1357  var width = Number(this.svg_pad(true).attr("width")),
1358  height = Number(this.svg_pad(true).attr("height"));
1359  var w = width, h = height;
1360 
1361  var ndc = this.svg_frame() ? this.svg_frame()['NDC'] : null;
1362  if (ndc == null) ndc = { x1 : 0.07, y1 : 0.12, x2 : 0.95, y2 : 0.88 };
1363 
1364  var root_pad = this.root_pad();
1365 
1366  var lm = width * ndc.x1;
1367  var rm = width * (1 - ndc.x2);
1368  var tm = height * ndc.y1;
1369  var bm = height * (1 - ndc.y2);
1370 
1371  var framecolor = this.createAttFill('white'),
1372  lineatt = JSROOT.Painter.createAttLine('black'),
1373  bordermode = 0, bordersize = 0;
1374 
1375  if (this.tframe) {
1376  bordermode = this.tframe['fBorderMode'];
1377  bordersize = this.tframe['fBorderSize'];
1378  lineatt = JSROOT.Painter.createAttLine(this.tframe);
1379  if (root_pad) {
1380  var xspan = width / Math.abs(root_pad['fX2'] - root_pad['fX1']);
1381  var yspan = height / Math.abs(root_pad['fY2'] - root_pad['fY1']);
1382  var px1 = (this.tframe['fX1'] - root_pad['fX1']) * xspan;
1383  var py1 = (this.tframe['fY1'] - root_pad['fY1']) * yspan;
1384  var px2 = (this.tframe['fX2'] - root_pad['fX1']) * xspan;
1385  var py2 = (this.tframe['fY2'] - root_pad['fY1']) * yspan;
1386  var pxl, pxt, pyl, pyt;
1387  if (px1 < px2) { pxl = px1; pxt = px2; }
1388  else { pxl = px2; pxt = px1; }
1389  if (py1 < py2) { pyl = py1; pyt = py2; }
1390  else { pyl = py2; pyt = py1; }
1391  lm = pxl;
1392  bm = pyl;
1393  w = pxt - pxl;
1394  h = pyt - pyl;
1395  tm = height - pyt;
1396  rm = width - pxt;
1397  } else {
1398  lm = this.tframe['fX1'] * width;
1399  tm = this.tframe['fY1'] * height;
1400  bm = (1.0 - this.tframe['fY2']) * height;
1401  rm = (1.0 - this.tframe['fX2'] + shrink_right) * width;
1402  w -= (lm + rm);
1403  h -= (tm + bm);
1404  }
1405  framecolor = this.createAttFill(this.tframe);
1406  } else {
1407  if (root_pad) {
1408  framecolor = this.createAttFill(null, root_pad['fFrameFillStyle'], root_pad['fFrameFillColor']);
1409  }
1410  w -= (lm + rm);
1411  h -= (tm + bm);
1412  }
1413 
1414  // force white color for the frame
1415  if (framecolor.color == 'none') framecolor.color = 'white';
1416 
1417  // this is svg:g object - container for every other items belonging to frame
1418  var frame_g = this.svg_pad(true).select(".root_frame");
1419 
1420  var top_rect = null;
1421 
1422  if (frame_g.empty()) {
1423  frame_g = this.svg_pad(true).select(".frame_layer").append("svg:g").attr("class", "root_frame");
1424 
1425  top_rect = frame_g.append("svg:rect");
1426 
1427  // append for the moment three layers - for drawing and axis
1428  frame_g.append('svg:g').attr('class','grid_layer');
1429  frame_g.append('svg:g').attr('class','main_layer');
1430  frame_g.append('svg:g').attr('class','axis_layer');
1431  } else {
1432  top_rect = frame_g.select("rect");
1433  }
1434 
1435  // calculate actual NDC coordinates, use them to properly locate PALETTE
1436  frame_g.node()['NDC'] = {
1437  x1 : lm / width,
1438  x2 : (lm + w) / width,
1439  y1 : tm / height,
1440  y2 : (tm + h) / height
1441  };
1442 
1443  // simple workaround to access painter via frame container
1444  frame_g.node()['frame_painter'] = this;
1445 
1446  lm = Math.round(lm); tm = Math.round(tm);
1447  w = Math.round(w); h = Math.round(h);
1448 
1449  frame_g.attr("x", lm)
1450  .attr("y", tm)
1451  .attr("width", w)
1452  .attr("height", h)
1453  .attr("transform", "translate(" + lm + "," + tm + ")");
1454 
1455  top_rect.attr("x", 0)
1456  .attr("y", 0)
1457  .attr("width", w)
1458  .attr("height", h)
1459  .call(framecolor.func)
1460  .call(lineatt.func);
1461  }
1462 
1463  JSROOT.TFramePainter.prototype.Redraw = function() {
1464  this.DrawFrameSvg();
1465  }
1466 
1467  JSROOT.Painter.drawFrame = function(divid, obj) {
1468  var p = new JSROOT.TFramePainter(obj);
1469  p.SetDivId(divid);
1470  p.DrawFrameSvg();
1471  return p;
1472  }
1473 
1474  // =========================================================================
1475 
1476  JSROOT.TF1Painter = function(tf1) {
1477  JSROOT.TObjectPainter.call(this, tf1);
1478  this.tf1 = tf1;
1479  }
1480 
1481  JSROOT.TF1Painter.prototype = Object.create(JSROOT.TObjectPainter.prototype);
1482 
1483  JSROOT.TF1Painter.prototype.GetObject = function() {
1484  return this.tf1;
1485  }
1486 
1487  JSROOT.TF1Painter.prototype.Redraw = function() {
1488  this.DrawBins();
1489  }
1490 
1491  JSROOT.TF1Painter.prototype.Eval = function(x) {
1492  return this.tf1.evalPar(x);
1493  }
1494 
1495  JSROOT.TF1Painter.prototype.CreateDummyHisto = function() {
1496  var xmin = 0, xmax = 0, ymin = 0, ymax = 0;
1497  if (this.tf1['fNsave'] > 0) {
1498  // in the case where the points have been saved, useful for example
1499  // if we don't have the user's function
1500  var nb_points = this.tf1['fNpx'];
1501  for (var i = 0; i < nb_points; ++i) {
1502  var h = this.tf1['fSave'][i];
1503  if ((i == 0) || (h > ymax))
1504  ymax = h;
1505  if ((i == 0) || (h < ymin))
1506  ymin = h;
1507  }
1508  xmin = this.tf1['fSave'][nb_points + 1];
1509  xmax = this.tf1['fSave'][nb_points + 2];
1510  } else {
1511  // we don't have the points, so let's try to interpret the function
1512  // use fNpfits instead of fNpx if possible (to use more points)
1513  if (this.tf1['fNpfits'] <= 103)
1514  this.tf1['fNpfits'] = 103;
1515  xmin = this.tf1['fXmin'];
1516  xmax = this.tf1['fXmax'];
1517 
1518  var nb_points = Math.max(this.tf1['fNpx'], this.tf1['fNpfits']);
1519 
1520  var binwidthx = (xmax - xmin) / nb_points;
1521  var left = -1, right = -1;
1522  for (var i = 0; i < nb_points; ++i) {
1523  var h = this.Eval(xmin + (i * binwidthx));
1524  if (isNaN(h)) continue;
1525 
1526  if (left < 0) {
1527  left = i;
1528  ymax = h;
1529  ymin = h;
1530  }
1531  if ((right < 0) || (right == i - 1))
1532  right = i;
1533 
1534  if (h > ymax)
1535  ymax = h;
1536  if (h < ymin)
1537  ymin = h;
1538  }
1539 
1540  if (left < right) {
1541  xmax = xmin + right * binwidthx;
1542  xmin = xmin + left * binwidthx;
1543  }
1544  }
1545 
1546  if (ymax > 0.0) ymax *= 1.05;
1547  if (ymin < 0.0) ymin *= 1.05;
1548 
1549  var histo = JSROOT.Create("TH1I");
1550 
1551  histo['fName'] = this.tf1['fName'] + "_hist";
1552  histo['fTitle'] = this.tf1['fTitle'];
1553 
1554  histo['fXaxis']['fXmin'] = xmin;
1555  histo['fXaxis']['fXmax'] = xmax;
1556  histo['fYaxis']['fXmin'] = ymin;
1557  histo['fYaxis']['fXmax'] = ymax;
1558 
1559  return histo;
1560  }
1561 
1562  JSROOT.TF1Painter.prototype.CreateBins = function() {
1563 
1564  var pthis = this;
1565 
1566  if (this.tf1['fNsave'] > 0) {
1567  // in the case where the points have been saved, useful for example
1568  // if we don't have the user's function
1569  var nb_points = this.tf1['fNpx'];
1570 
1571  var xmin = this.tf1['fSave'][nb_points + 1];
1572  var xmax = this.tf1['fSave'][nb_points + 2];
1573  var binwidthx = (xmax - xmin) / nb_points;
1574 
1575  this['bins'] = d3.range(nb_points).map(function(p) {
1576  return {
1577  x : xmin + (p * binwidthx),
1578  y : pthis.tf1['fSave'][p]
1579  };
1580  });
1581  this['interpolate_method'] = 'monotone';
1582  } else {
1583  if (this.tf1['fNpfits'] <= 103)
1584  this.tf1['fNpfits'] = 333;
1585  var xmin = this.tf1['fXmin'];
1586  var xmax = this.tf1['fXmax'];
1587  var nb_points = Math.max(this.tf1['fNpx'], this.tf1['fNpfits']);
1588  var binwidthx = (xmax - xmin) / nb_points;
1589  this['bins'] = d3.range(nb_points).map(function(p) {
1590  var xx = xmin + (p * binwidthx);
1591  var yy = pthis.Eval(xx);
1592  if (isNaN(yy)) yy = 0;
1593  return {
1594  x : xx,
1595  y : yy
1596  };
1597  });
1598  this['interpolate_method'] = 'cardinal-open';
1599  }
1600  }
1601 
1602  JSROOT.TF1Painter.prototype.DrawBins = function() {
1603  var w = Number(this.svg_frame(true).attr("width")),
1604  h = Number(this.svg_frame(true).attr("height"));
1605 
1606  this.RecreateDrawG();
1607 
1608  var pthis = this;
1609  var x = this.main_painter().x;
1610  var y = this.main_painter().y;
1611 
1612  var attline = JSROOT.Painter.createAttLine(this.tf1);
1613  var fill = this.createAttFill(this.tf1);
1614  if (fill.color == 'white') fill.color = 'none';
1615 
1616  var line = d3.svg.line()
1617  .x(function(d) { return x(d.x).toFixed(1); })
1618  .y(function(d) { return y(d.y).toFixed(1); })
1619  .interpolate(this.interpolate_method);
1620 
1621  var area = d3.svg.area()
1622  .x(function(d) { return x(d.x).toFixed(1); })
1623  .y1(h)
1624  .y0(function(d) { return y(d.y).toFixed(1); });
1625 
1626  if (attline.color != "none")
1627  this.draw_g.append("svg:path")
1628  .attr("class", "line")
1629  .attr("d",line(pthis.bins))
1630  .style("fill", "none")
1631  .call(attline.func);
1632 
1633  if (fill.color != "none")
1634  this.draw_g.append("svg:path")
1635  .attr("class", "area")
1636  .attr("d",area(pthis.bins))
1637  .style("stroke", "none")
1638  .call(fill.func);
1639 
1640  // add tooltips
1641  if (JSROOT.gStyle.Tooltip)
1642  this.draw_g.selectAll()
1643  .data(this.bins).enter()
1644  .append("svg:circle")
1645  .attr("cx", function(d) { return x(d.x).toFixed(1); })
1646  .attr("cy", function(d) { return y(d.y).toFixed(1); })
1647  .attr("r", 4)
1648  .style("opacity", 0)
1649  .append("svg:title")
1650  .text( function(d) { return "x = " + d.x.toPrecision(4) + " \ny = " + d.y.toPrecision(4); });
1651  }
1652 
1653  JSROOT.TF1Painter.prototype.UpdateObject = function(obj) {
1654  if (obj['_typename'] != this.tf1['_typename']) return false;
1655  // TODO: realy update object content
1656  this.tf1 = obj;
1657  this.CreateBins();
1658  return true;
1659  }
1660 
1661  JSROOT.Painter.drawFunction = function(divid, tf1) {
1662  var painter = new JSROOT.TF1Painter(tf1);
1663 
1664  painter.SetDivId(divid, -1);
1665 
1666  if (painter.main_painter() == null) {
1667  var histo = painter.CreateDummyHisto();
1668  JSROOT.Painter.drawHistogram1D(divid, histo);
1669  }
1670 
1671  painter.SetDivId(divid);
1672 
1673  painter.CreateBins();
1674 
1675  painter.DrawBins();
1676 
1677  return painter;
1678  }
1679 
1680  // =======================================================================
1681 
1682  JSROOT.TGraphPainter = function(graph) {
1683  JSROOT.TObjectPainter.call(this, graph);
1684  this.graph = graph;
1685  this.ownhisto = false; // indicate if graph histogram was drawn for axes
1686  }
1687 
1688  JSROOT.TGraphPainter.prototype = Object.create(JSROOT.TObjectPainter.prototype);
1689 
1690  JSROOT.TGraphPainter.prototype.GetObject = function() {
1691  return this.graph;
1692  }
1693 
1694  JSROOT.TGraphPainter.prototype.Redraw = function() {
1695  this.DrawBins();
1696  }
1697 
1698  JSROOT.TGraphPainter.prototype.DecodeOptions = function(opt) {
1699  this.logx = false;
1700  this.logy = false;
1701  this.logz = false;
1702  this.gridx = false;
1703  this.gridy = false;
1704  this.draw_all = true;
1705  this.optionLine = 0;
1706  this.optionAxis = 0;
1707  this.optionCurve = 0;
1708  this.optionStar = 0;
1709  this.optionMark = 0;
1710  this.optionBar = 0;
1711  this.optionR = 0;
1712  this.optionOne = 0;
1713  this.optionE = 0;
1714  this.optionFill = 0;
1715  this.optionZ = 0;
1716  this.optionCurveFill = 0;
1717  this.draw_errors = true;
1718  this.optionNone = 0; // no any drawing
1719  this.opt = "LP";
1720 
1721  if ((opt != null) && (opt != "")) {
1722  this.opt = opt.toUpperCase();
1723  this.opt.replace('SAME', '');
1724  }
1725 
1726  if (this.opt.indexOf('L') != -1)
1727  this.optionLine = 1;
1728  if (this.opt.indexOf('A') != -1)
1729  this.optionAxis = 1;
1730  if (this.opt.indexOf('C') != -1)
1731  this.optionCurve = 1;
1732  if (this.opt.indexOf('*') != -1)
1733  this.optionStar = 1;
1734  if (this.opt.indexOf('P') != -1)
1735  this.optionMark = 1;
1736  if (this.opt.indexOf('B') != -1)
1737  this.optionBar = 1;
1738  if (this.opt.indexOf('R') != -1)
1739  this.optionR = 1;
1740  if (this.opt.indexOf('1') != -1)
1741  this.optionOne = 1;
1742  if (this.opt.indexOf('F') != -1)
1743  this.optionFill = 1;
1744  if (this.opt.indexOf('2') != -1 || this.opt.indexOf('3') != -1
1745  || this.opt.indexOf('4') != -1 || this.opt.indexOf('5') != -1)
1746  this.optionE = 1;
1747 
1748  // if no drawing option is selected and if opt<>' ' nothing is done.
1749  if (this.optionLine + this.optionFill + this.optionCurve + this.optionStar + this.optionMark + this.optionBar + this.optionE == 0) {
1750  if (this.opt.length == 0)
1751  this.optionLine = 1;
1752  else {
1753  this.optionNone = 1;
1754  return;
1755  }
1756  }
1757  if (this.optionStar)
1758  this.graph['fMarkerStyle'] = 3;
1759 
1760  if (this.optionCurve && this.optionFill) {
1761  this.optionCurveFill = 1;
1762  this.optionFill = 0;
1763  }
1764 
1765  var pad = this.root_pad();
1766  if (pad) {
1767  this.logx = pad['fLogx'];
1768  this.logy = pad['fLogy'];
1769  this.logz = pad['fLogz'];
1770  this.gridx = pad['fGridx'];
1771  this.gridy = pad['fGridy'];
1772  }
1773 
1774  this.xaxis_type = this.logx ? 'logarithmic' : 'linear';
1775  this.yaxis_type = this.logy ? 'logarithmic' : 'linear';
1776 
1777  if (this.graph['_typename'] == 'TGraph') {
1778  // check for axis scale format, and convert if required
1779  if (this.graph['fHistogram']['fXaxis']['fTimeDisplay']) {
1780  this.xaxis_type = 'datetime';
1781  }
1782 
1783  if (this.graph['fHistogram']['fYaxis']['fTimeDisplay']) {
1784  this.yaxis_type = 'datetime';
1785  }
1786  } else if (this.graph['_typename'] == 'TGraphErrors') {
1787  this.maxEX = d3.max(this.graph['fEX']);
1788  this.maxEY = d3.max(this.graph['fEY']);
1789  if (this.maxEX < 1.0e-300 && this.maxEY < 1.0e-300)
1790  this.draw_errors = false;
1791  }
1792  this.seriesType = 'scatter';
1793  if (this.optionBar == 1) this.seriesType = 'bar';
1794  this.showMarker = false;
1795  if (this.optionMark == 1 || this.optionStar == 1) this.showMarker = true;
1796 
1797  if (this.optionLine == 1 || this.optionCurve == 1 || this.optionFill == 1)
1798  this.seriesType = 'line';
1799 
1800  if (this.optionBar == 1) {
1801  this.binwidthx = (this.graph['fHistogram']['fXaxis']['fXmax'] -
1802  this.graph['fHistogram']['fXaxis']['fXmin'])
1803  / (this.graph['fNpoints'] - 1);
1804  }
1805  }
1806 
1807  JSROOT.TGraphPainter.prototype.CreateBins = function() {
1808  var pthis = this;
1809 
1810  var npoints = this.graph['fNpoints'];
1811  if ((this.graph._typename=="TCutG") && (npoints>3)) npoints--;
1812 
1813  this.lineatt = JSROOT.Painter.createAttLine(this.graph);
1814 
1815  this.bins = d3.range(npoints).map(
1816  function(p) {
1817  if (pthis.optionBar == 1) {
1818  return {
1819  x : pthis.graph['fX'][p] - (pthis.binwidthx / 2),
1820  y : pthis.graph['fY'][p], // graph['fHistogram']['fXaxis']['fXmin'],
1821  bw : pthis.binwidthx,
1822  bh : pthis.graph['fY'][p]
1823  }
1824  } else if (pthis.graph['_typename'] == 'TGraphErrors') {
1825  return {
1826  x : pthis.graph['fX'][p],
1827  y : pthis.graph['fY'][p],
1828  exlow : pthis.graph['fEX'][p],
1829  exhigh : pthis.graph['fEX'][p],
1830  eylow : pthis.graph['fEY'][p],
1831  eyhigh : pthis.graph['fEY'][p]
1832  };
1833  } else if (pthis.graph['_typename'] == 'TGraphAsymmErrors'
1834  || pthis.graph['_typename'].match(/^RooHist/)) {
1835  return {
1836  x : pthis.graph['fX'][p],
1837  y : pthis.graph['fY'][p],
1838  exlow : pthis.graph['fEXlow'][p],
1839  exhigh : pthis.graph['fEXhigh'][p],
1840  eylow : pthis.graph['fEYlow'][p],
1841  eyhigh : pthis.graph['fEYhigh'][p]
1842  };
1843  } else {
1844  return {
1845  x : pthis.graph['fX'][p],
1846  y : pthis.graph['fY'][p]
1847  };
1848  }
1849  });
1850 
1851  this.exclusionGraph = false;
1852  if (this.lineatt.width <= 99) return;
1853 
1854  // special handling of exclusion graphs
1855 
1856  this.exclusionGraph = true;
1857 
1858  var normx, normy;
1859  var n = this.graph['fNpoints'];
1860  var xo = new Array(n + 2),
1861  yo = new Array(n + 2),
1862  xt = new Array(n + 2),
1863  yt = new Array(n + 2),
1864  xf = new Array(2 * n + 2),
1865  yf = new Array(2 * n + 2);
1866  // negative value means another side of the line...
1867 
1868 
1869 
1870  var a, i, j, nf, wk = 1;
1871  if (this.lineatt.width > 32767) {
1872  this.lineatt.width = 65536 - this.lineatt.width;
1873  wk = -1;
1874  }
1875  wk *= (this.lineatt.width / 100) * 0.005;
1876  this.lineatt.width = this.lineatt.width % 100; // line width
1877  if (this.lineatt.width > 0) this.optionLine = 1;
1878 
1879  var w = Number(this.svg_frame(true).attr("width")),
1880  h = Number(this.svg_frame(true).attr("height"));
1881 
1882  var ratio = w / h;
1883 
1884  var xmin = this.main_painter().xmin, xmax = this.main_painter().xmax,
1885  ymin = this.main_painter().ymin, ymax = this.main_painter().ymax;
1886  for (i = 0; i < n; i++) {
1887  xo[i] = (this.graph['fX'][i] - xmin) / (xmax - xmin);
1888  yo[i] = (this.graph['fY'][i] - ymin) / (ymax - ymin);
1889  if (w > h)
1890  yo[i] = yo[i] / ratio;
1891  else if (h > w)
1892  xo[i] = xo[i] / ratio;
1893  }
1894  // The first part of the filled area is made of the graph points.
1895  // Make sure that two adjacent points are different.
1896  xf[0] = xo[0];
1897  yf[0] = yo[0];
1898  nf = 0;
1899  for (i = 1; i < n; i++) {
1900  if (xo[i] == xo[i - 1] && yo[i] == yo[i - 1]) continue;
1901  nf++;
1902  xf[nf] = xo[i];
1903  if (xf[i] == xf[i - 1])
1904  xf[i] += 0.000001; // add an epsilon to avoid exact vertical
1905  // lines.
1906  yf[nf] = yo[i];
1907  }
1908  // For each graph points a shifted points is computed to build up
1909  // the second part of the filled area. First and last points are
1910  // treated as special cases, outside of the loop.
1911  if (xf[1] == xf[0]) {
1912  a = Math.PI / 2.0;
1913  } else {
1914  a = Math.atan((yf[1] - yf[0]) / (xf[1] - xf[0]));
1915  }
1916  if (xf[0] <= xf[1]) {
1917  xt[0] = xf[0] - wk * Math.sin(a);
1918  yt[0] = yf[0] + wk * Math.cos(a);
1919  } else {
1920  xt[0] = xf[0] + wk * Math.sin(a);
1921  yt[0] = yf[0] - wk * Math.cos(a);
1922  }
1923  if (xf[nf] == xf[nf - 1]) {
1924  a = Math.PI / 2.0;
1925  } else {
1926  a = Math.atan((yf[nf] - yf[nf - 1]) / (xf[nf] - xf[nf - 1]));
1927  }
1928  if (xf[nf] >= xf[nf - 1]) {
1929  xt[nf] = xf[nf] - wk * Math.sin(a);
1930  yt[nf] = yf[nf] + wk * Math.cos(a);
1931  } else {
1932  xt[nf] = xf[nf] + wk * Math.sin(a);
1933  yt[nf] = yf[nf] - wk * Math.cos(a);
1934  }
1935 
1936  var a1, a2, a3, xi0, yi0, xi1, yi1, xi2, yi2;
1937  for (i = 1; i < nf; i++) {
1938  xi0 = xf[i];
1939  yi0 = yf[i];
1940  xi1 = xf[i + 1];
1941  yi1 = yf[i + 1];
1942  xi2 = xf[i - 1];
1943  yi2 = yf[i - 1];
1944  if (xi1 == xi0) {
1945  a1 = Math.PI / 2.0;
1946  } else {
1947  a1 = Math.atan((yi1 - yi0) / (xi1 - xi0));
1948  }
1949  if (xi1 < xi0)
1950  a1 = a1 + Math.PI;
1951  if (xi2 == xi0) {
1952  a2 = Math.PI / 2.0;
1953  } else {
1954  a2 = Math.atan((yi0 - yi2) / (xi0 - xi2));
1955  }
1956  if (xi0 < xi2)
1957  a2 = a2 + Math.PI;
1958  x1 = xi0 - wk * Math.sin(a1);
1959  y1 = yi0 + wk * Math.cos(a1);
1960  x2 = xi0 - wk * Math.sin(a2);
1961  y2 = yi0 + wk * Math.cos(a2);
1962  xm = (x1 + x2) * 0.5;
1963  ym = (y1 + y2) * 0.5;
1964  if (xm == xi0) {
1965  a3 = Math.PI / 2.0;
1966  } else {
1967  a3 = Math.atan((ym - yi0) / (xm - xi0));
1968  }
1969  x3 = xi0 - wk * Math.sin(a3 + (Math.PI / 2.0));
1970  y3 = yi0 + wk * Math.cos(a3 + (Math.PI / 2.0));
1971  // Rotate (x3,y3) by PI around (xi0,yi0) if it is not on the (xm,ym)
1972  // side.
1973  if ((xm - xi0) * (x3 - xi0) < 0 && (ym - yi0) * (y3 - yi0) < 0) {
1974  x3 = 2 * xi0 - x3;
1975  y3 = 2 * yi0 - y3;
1976  }
1977  if ((xm == x1) && (ym == y1)) {
1978  x3 = xm;
1979  y3 = ym;
1980  }
1981  xt[i] = x3;
1982  yt[i] = y3;
1983  }
1984  // Close the polygon if the first and last points are the same
1985  if (xf[nf] == xf[0] && yf[nf] == yf[0]) {
1986  xm = (xt[nf] + xt[0]) * 0.5;
1987  ym = (yt[nf] + yt[0]) * 0.5;
1988  if (xm == xf[0]) {
1989  a3 = Math.PI / 2.0;
1990  } else {
1991  a3 = Math.atan((ym - yf[0]) / (xm - xf[0]));
1992  }
1993  x3 = xf[0] + wk * Math.sin(a3 + (Math.PI / 2.0));
1994  y3 = yf[0] - wk * Math.cos(a3 + (Math.PI / 2.0));
1995  if ((xm - xf[0]) * (x3 - xf[0]) < 0 && (ym - yf[0]) * (y3 - yf[0]) < 0) {
1996  x3 = 2 * xf[0] - x3;
1997  y3 = 2 * yf[0] - y3;
1998  }
1999  xt[nf] = x3;
2000  xt[0] = x3;
2001  yt[nf] = y3;
2002  yt[0] = y3;
2003  }
2004  // Find the crossing segments and remove the useless ones
2005  var xc, yc, c1, b1, c2, b2;
2006  var cross = false;
2007  var nf2 = nf;
2008  for (i = nf2; i > 0; i--) {
2009  for (j = i - 1; j > 0; j--) {
2010  if (xt[i - 1] == xt[i] || xt[j - 1] == xt[j])
2011  continue;
2012  c1 = (yt[i - 1] - yt[i]) / (xt[i - 1] - xt[i]);
2013  b1 = yt[i] - c1 * xt[i];
2014  c2 = (yt[j - 1] - yt[j]) / (xt[j - 1] - xt[j]);
2015  b2 = yt[j] - c2 * xt[j];
2016  if (c1 != c2) {
2017  xc = (b2 - b1) / (c1 - c2);
2018  yc = c1 * xc + b1;
2019  if (xc > Math.min(xt[i], xt[i - 1])
2020  && xc < Math.max(xt[i], xt[i - 1])
2021  && xc > Math.min(xt[j], xt[j - 1])
2022  && xc < Math.max(xt[j], xt[j - 1])
2023  && yc > Math.min(yt[i], yt[i - 1])
2024  && yc < Math.max(yt[i], yt[i - 1])
2025  && yc > Math.min(yt[j], yt[j - 1])
2026  && yc < Math.max(yt[j], yt[j - 1])) {
2027  nf++;
2028  xf[nf] = xt[i];
2029  yf[nf] = yt[i];
2030  nf++;
2031  xf[nf] = xc;
2032  yf[nf] = yc;
2033  i = j;
2034  cross = true;
2035  break;
2036  } else {
2037  continue;
2038  }
2039  } else {
2040  continue;
2041  }
2042  }
2043  if (!cross) {
2044  nf++;
2045  xf[nf] = xt[i];
2046  yf[nf] = yt[i];
2047  }
2048  cross = false;
2049  }
2050  nf++;
2051  xf[nf] = xt[0];
2052  yf[nf] = yt[0];
2053  nf++;
2054  for (i = 0; i < nf; i++) {
2055  if (w > h) {
2056  xf[i] = xmin + (xf[i] * (xmax - xmin));
2057  yf[i] = ymin + (yf[i] * (ymax - ymin)) * ratio;
2058  } else if (h > w) {
2059  xf[i] = xmin + (xf[i] * (xmax - xmin)) * ratio;
2060  yf[i] = ymin + (yf[i] * (ymax - ymin));
2061  } else {
2062  xf[i] = xmin + (xf[i] * (xmax - xmin));
2063  yf[i] = ymin + (yf[i] * (ymax - ymin));
2064  }
2065  if (this.logx && xf[i] <= 0.0)
2066  xf[i] = xmin;
2067  if (this.logy && yf[i] <= 0.0)
2068  yf[i] = ymin;
2069  }
2070 
2071  this.excl = d3.range(nf).map(function(p) { return { x : xf[p], y : yf[p] }; });
2072 
2073  this.excl_ff = 1;
2074 
2075  /* some clean-up */
2076  xo.splice(0, xo.length);
2077  yo.splice(0, yo.length);
2078  xo = null;
2079  yo = null;
2080  xt.splice(0, xt.length);
2081  yt.splice(0, yt.length);
2082  xt = null;
2083  yt = null;
2084  xf.splice(0, xf.length);
2085  yf.splice(0, yf.length);
2086  xf = null;
2087  yf = null;
2088  }
2089 
2090  JSROOT.TGraphPainter.prototype.DrawBins = function() {
2091 
2092  var w = Number(this.svg_frame(true).attr("width")),
2093  h = Number(this.svg_frame(true).attr("height"));
2094 
2095  this.RecreateDrawG();
2096 
2097  var pthis = this;
2098 
2099  var fill = this.createAttFill(this.graph);
2100 
2101  function TooltipText(d) {
2102 
2103  var res = "x = " + pthis.main_painter().AxisAsText("x", d.x) + "\n" +
2104  "y = " + pthis.main_painter().AxisAsText("y", d.y);
2105 
2106  if (pthis.draw_errors && ('exlow' in d) && ((d.exlow!=0) || (d.exhigh!=0)))
2107  res += "\nerror x = -" + pthis.main_painter().AxisAsText("x", d.exlow) +
2108  "/+" + pthis.main_painter().AxisAsText("x", d.exhigh);
2109 
2110  if (pthis.draw_errors && ('eylow' in d) && ((d.eylow!=0) || (d.eyhigh!=0)) )
2111  res += "\nerror y = -" + pthis.main_painter().AxisAsText("y", d.eylow) +
2112  "/+" + pthis.main_painter().AxisAsText("y", d.eyhigh);
2113 
2114  return res;
2115  }
2116 
2117  var x = this.main_painter().x;
2118  var y = this.main_painter().y;
2119  var line = d3.svg.line()
2120  .x(function(d) { return Math.round(x(d.x)); })
2121  .y(function(d) { return Math.round(y(d.y)); });
2122 
2123  if (this.seriesType == 'bar') {
2124  var fillcolor = JSROOT.Painter.root_colors[this.graph['fFillColor']];
2125  if (typeof (fillcolor) == 'undefined') fillcolor = "rgb(204,204,204)";
2126  /* filled bar graph */
2127  var xdom = this.main_painter().x.domain();
2128  var xfactor = xdom[1] - xdom[0];
2129  this.draw_errors = false;
2130 
2131  var nodes = this.draw_g.selectAll("bar_graph")
2132  .data(pthis.bins).enter()
2133  .append("svg:rect")
2134  .attr("x", function(d) { return x(d.x) })
2135  .attr("y", function(d) { return y(d.y) })
2136  .attr("width", function(d) { return (w / (xdom[1] - xdom[0])) - 1 })
2137  .attr("height", function(d) { return y(d.y) - y(d.y + d.bh); })
2138  .style("fill", fillcolor);
2139 
2140  if (JSROOT.gStyle.Tooltip)
2141  nodes.append("svg:title").text(function(d) { return "x = " + d.x.toPrecision(4) + " \nentries = " + d.y.toPrecision(4); });
2142  }
2143  if (this.exclusionGraph) {
2144  /* first draw exclusion area, and then the line */
2145  this.showMarker = false;
2146 
2147  this.draw_g.append("svg:path")
2148  .attr("d", line(pthis.excl))
2149  .style("stroke", "none")
2150  .style("stroke-width", pthis.excl_ff)
2151  .call(fill.func)
2152  .style('opacity', 0.75);
2153  }
2154 
2155  if (this.seriesType == 'line') {
2156 
2157  var close_symbol = "";
2158  if (this.graph._typename=="TCutG") close_symbol = " Z";
2159 
2160  var lineatt = this.lineatt;
2161  if (this.optionLine == 0) lineatt = JSROOT.Painter.createAttLine('none');
2162 
2163  if (this.optionFill == 1) {
2164 
2165  } else {
2166  fill.color = 'none';
2167  }
2168 
2169  this.draw_g.append("svg:path")
2170  .attr("d", line(pthis.bins) + close_symbol)
2171  .attr("class", "draw_line")
2172  .call(lineatt.func)
2173  .call(fill.func);
2174 
2175  // do not add tooltip for line, when we wants to add markers
2176  if (JSROOT.gStyle.Tooltip && !this.showMarker)
2177  this.draw_g.selectAll("draw_line")
2178  .data(pthis.bins).enter()
2179  .append("svg:circle")
2180  .attr("cx", function(d) { return Math.round(x(d.x)); })
2181  .attr("cy", function(d) { return Math.round(y(d.y)); })
2182  .attr("r", 3)
2183  .style("opacity", 0)
2184  .append("svg:title")
2185  .text(TooltipText);
2186  }
2187 
2188  if (this.draw_errors)
2189  this.draw_errors = (this.graph['_typename'] == 'TGraphErrors' ||
2190  this.graph['_typename'] == 'TGraphAsymmErrors' ||
2191  this.graph['_typename'].match(/^RooHist/)) && !this.optionBar;
2192 
2193  var nodes = null;
2194 
2195  if (this.draw_errors || this.showMarker) {
2196  var draw_bins = new Array;
2197  for (var i in this.bins) {
2198  var pnt = this.bins[i];
2199  var grx = x(pnt.x);
2200  var gry = y(pnt.y);
2201  if ((grx<0) || (grx>w) || (gry<0) || (gry>h)) continue;
2202 
2203  // caluclate graphical coordinates
2204  pnt['grx1'] = grx.toFixed(1);
2205  pnt['gry1'] = gry.toFixed(1);
2206  if (pnt.exlow > 0) pnt['grx0'] = (x(pnt.x - pnt.exlow) - grx).toFixed(1);
2207  if (pnt.exhigh > 0) pnt['grx2'] = (x(pnt.x + pnt.exhigh) - grx).toFixed(1);
2208  if (pnt.eylow > 0) pnt['gry0'] = (y(pnt.y - pnt.eylow) - gry).toFixed(1);
2209  if (pnt.eyhigh > 0) pnt['gry2'] = (y(pnt.y + pnt.eyhigh) - gry).toFixed(1);
2210 
2211  draw_bins.push(pnt);
2212  }
2213  // here are up to five elements are collected, try to group them
2214  nodes = this.draw_g.selectAll("g.node")
2215  .data(draw_bins)
2216  .enter()
2217  .append("svg:g")
2218  .attr("transform", function(d) { return "translate(" + d.grx1 + "," + d.gry1 + ")"; })
2219  }
2220 
2221  if (JSROOT.gStyle.Tooltip && nodes)
2222  nodes.append("svg:title").text(TooltipText);
2223 
2224  if (this.draw_errors) {
2225  // than doing filer append error bars
2226  nodes.filter(function(d) { return (d.exlow > 0) || (d.exhigh > 0); })
2227  .append("svg:line")
2228  .attr("x1", function(d) { return d.grx0; })
2229  .attr("y1", 0)
2230  .attr("x2", function(d) { return d.grx2; })
2231  .attr("y2", 0)
2232  .style("stroke", this.lineatt.color)
2233  .style("stroke-width", this.lineatt.width);
2234 
2235  nodes.filter(function(d) { return (d.exlow > 0); })
2236  .append("svg:line")
2237  .attr("y1", -3)
2238  .attr("x1", function(d) { return d.grx0; })
2239  .attr("y2", 3)
2240  .attr("x2", function(d) { return d.grx0; })
2241  .style("stroke", this.lineatt.color)
2242  .style("stroke-width", this.lineatt.width);
2243 
2244  nodes.filter(function(d) { return (d.exhigh > 0); })
2245  .append("svg:line")
2246  .attr("y1", -3)
2247  .attr("x1", function(d) { return d.grx2; })
2248  .attr("y2", 3)
2249  .attr("x2", function(d) { return d.grx2; })
2250  .style("stroke", this.lineatt.color)
2251  .style( "stroke-width", this.lineatt.width);
2252 
2253  // Add y-error indicators
2254 
2255  nodes.filter(function(d) { return (d.eylow > 0) || (d.eyhigh > 0); })
2256  .append("svg:line")
2257  .attr("x1", 0)
2258  .attr("y1", function(d) { return d.gry0; })
2259  .attr("x2", 0)
2260  .attr("y2", function(d) { return d.gry2; })
2261  .style("stroke", this.lineatt.color)
2262  .style("stroke-width", this.lineatt.width);
2263 
2264  nodes.filter(function(d) { return (d.eylow > 0); })
2265  .append("svg:line")
2266  .attr("x1", -3)
2267  .attr("y1", function(d) { return d.gry0; })
2268  .attr("x2", 3)
2269  .attr("y2", function(d) { return d.gry0; })
2270  .style("stroke", this.lineatt.color)
2271  .style("stroke-width", this.lineatt.width);
2272 
2273  nodes.filter(function(d) { return (d.eyhigh > 0); })
2274  .append("svg:line")
2275  .attr("x1", -3)
2276  .attr("y1", function(d) { return d.gry2; })
2277  .attr("x2", 3)
2278  .attr("y2", function(d) { return d.gry2; })
2279  .style("stroke", this.lineatt.color)
2280  .style("stroke-width", this.lineatt.width);
2281  }
2282 
2283  if (this.showMarker) {
2284  /* Add markers */
2285  var marker = JSROOT.Painter.createAttMarker(this.graph);
2286 
2287  nodes.append("svg:path").call(marker.func);
2288  }
2289  }
2290 
2291  JSROOT.TGraphPainter.prototype.UpdateObject = function(obj) {
2292  if (obj['_typename'] != this.graph['_typename'])
2293  return false;
2294 
2295  // if our own histogram was used as axis drawing, we need update histogram as well
2296  if (this.ownhisto)
2297  this.main_painter().UpdateObject(obj['fHistogram']);
2298 
2299  // TODO: make real update of TGraph object content
2300  this.graph['fX'] = obj['fX'];
2301  this.graph['fY'] = obj['fY'];
2302  this.graph['fNpoints'] = obj['fNpoints'];
2303  this.CreateBins();
2304  return true;
2305  }
2306 
2307  JSROOT.Painter.drawGraph = function(divid, graph, opt) {
2308 
2309  var painter = new JSROOT.TGraphPainter(graph);
2310  painter.SetDivId(divid, -1); // just to get access to existing elements
2311 
2312  if (painter.main_painter() == null) {
2313  if (graph['fHistogram']==null) {
2314  alert("drawing first graph without fHistogram field, not (yet) supported");
2315  return null;
2316  }
2317  JSROOT.Painter.drawHistogram1D(divid, graph['fHistogram']);
2318  painter.ownhisto = true;
2319  }
2320 
2321  painter.SetDivId(divid);
2322 
2323  painter.DecodeOptions(opt);
2324 
2325  painter.CreateBins();
2326 
2327  painter.DrawBins();
2328 
2329  return painter;
2330  }
2331 
2332  // ============================================================
2333 
2334  JSROOT.TPavePainter = function(pave) {
2335  JSROOT.TObjectPainter.call(this, pave);
2336  this.pavetext = pave;
2337  this.Enabled = true;
2338  }
2339 
2340  JSROOT.TPavePainter.prototype = Object.create(JSROOT.TObjectPainter.prototype);
2341 
2342  JSROOT.TPavePainter.prototype.GetObject = function() {
2343  return this.pavetext;
2344  }
2345 
2346  JSROOT.TPavePainter.prototype.DrawPaveText = function() {
2347  var pavetext = this.pavetext;
2348 
2349  var w = Number(this.svg_pad(true).attr("width")),
2350  h = Number(this.svg_pad(true).attr("height"));
2351 
2352  var pos_x = Math.round(pavetext['fX1NDC'] * w);
2353 
2354  var pos_y = Math.round((1.0 - pavetext['fY1NDC']) * h);
2355  var width = Math.round(Math.abs(pavetext['fX2NDC'] - pavetext['fX1NDC']) * w);
2356  var height = Math.round(Math.abs(pavetext['fY2NDC'] - pavetext['fY1NDC']) * h);
2357  pos_y -= height;
2358  var nlines = pavetext['fLines'].arr.length;
2359  var tcolor = JSROOT.Painter.root_colors[pavetext['fTextColor']];
2360  var scolor = JSROOT.Painter.root_colors[pavetext['fShadowColor']];
2361  var fcolor = this.createAttFill(pavetext);
2362 
2363  // align = 10*HorizontalAlign + VerticalAlign
2364  // 1=left adjusted, 2=centered, 3=right adjusted
2365  // 1=bottom adjusted, 2=centered, 3=top adjusted
2366  // "middle", "start", "end"
2367  var align = 'start', halign = Math.round(pavetext['fTextAlign'] / 10);
2368  var baseline = 'bottom', valign = pavetext['fTextAlign'] % 10;
2369  if (halign == 1) align = 'start';
2370  else if (halign == 2) align = 'middle';
2371  else if (halign == 3) align = 'end';
2372  if (valign == 1) baseline = 'bottom';
2373  else if (valign == 2) baseline = 'middle';
2374  else if (valign == 3) baseline = 'top';
2375 
2376  var h_margin = Math.round(pavetext['fMargin'] * width); // horizontal margin
2377 
2378  var font = JSROOT.Painter.getFontDetails(pavetext['fTextFont'], height / (nlines * 1.2));
2379 
2380  var lwidth = pavetext['fBorderSize'] ? pavetext['fBorderSize'] : 0;
2381  var attline = JSROOT.Painter.createAttLine(pavetext, lwidth>0 ? 1 : 0);
2382 
2383  var first_stat = 0, num_cols = 0, maxlw = 0;
2384  var lines = new Array;
2385 
2386  // adjust font size
2387  for (var j = 0; j < nlines; ++j) {
2388  var line = JSROOT.Painter.translateLaTeX(pavetext['fLines'].arr[j]['fTitle']);
2389  lines.push(line);
2390  var lw = h_margin + font.stringWidth(this.svg_pad(true), line) + h_margin;
2391  if (lw > maxlw) maxlw = lw;
2392  if ((j == 0) || (line.indexOf('|') < 0)) continue;
2393  if (first_stat === 0) first_stat = j;
2394  var parts = line.split("|");
2395  if (parts.length > num_cols)
2396  num_cols = parts.length;
2397  }
2398 
2399  if (maxlw > width)
2400  font.size = Math.floor(font.size * (width / maxlw));
2401  else
2402  if ((nlines==1) && (lwidth==0) && (maxlw < width - 40)) {
2403  // adjust invisible size of the pave for comfort resizing
2404  var diff = width - maxlw;
2405  width -= diff;
2406  pos_x += diff/2;
2407  pavetext['fX1NDC'] = pos_x / w;
2408  pavetext['fX2NDC'] = (pos_x + width) / w;
2409  }
2410 
2411  var h_margin = Math.round(pavetext['fMargin'] * width); // horizontal margin again
2412  var text_pos_x = h_margin / 2; // position of text inside <g> element
2413  if (nlines == 1)
2414  switch (halign) {
2415  case 1: text_pos_x = h_margin; break;
2416  case 2: text_pos_x = width / 2; break;
2417  case 3: text_pos_x = width - h_margin; break;
2418  }
2419 
2420  var pthis = this;
2421 
2422  // container used to recalculate coordinates
2423  this.RecreateDrawG(true,".stat_layer");
2424 
2425  // position and size required only for drag functions
2426  this.draw_g
2427  .attr("x", pos_x)
2428  .attr("y", pos_y)
2429  .attr("width", width)
2430  .attr("height", height)
2431  .attr("transform", "translate(" + pos_x + "," + pos_y + ")");
2432 
2433  this.draw_g.append("rect")
2434  .attr("x", 0)
2435  .attr("y", 0)
2436  .attr("width", width)
2437  .attr("height", height)
2438  .call(fcolor.func)
2439  .call(attline.func);
2440 
2441  // for characters like 'p' or 'y' several more pixels required to stay in the box when drawn in last line
2442  var stepy = (height - 0.2*font.size) / nlines;
2443 
2444  if (nlines == 1) {
2445  this.draw_g.append("text")
2446  .attr("text-anchor", align)
2447  .attr("x", text_pos_x)
2448  .attr("y", ((height / 2) + (font.size / 3)).toFixed(1))
2449  .call(font.func)
2450  .attr("fill", tcolor)
2451  .text(lines[0]);
2452  } else {
2453 
2454  for (var j = 0; j < nlines; ++j) {
2455  var jcolor = JSROOT.Painter.root_colors[pavetext['fLines'].arr[j]['fTextColor']];
2456  if (pavetext['fLines'].arr[j]['fTextColor'] == 0) jcolor = tcolor;
2457  var posy = (j+0.5)*stepy + font.size*0.5 - 1;
2458 
2459  if (pavetext['_typename'] == 'TPaveStats') {
2460  if ((first_stat > 0) && (j >= first_stat)) {
2461  var parts = lines[j].split("|");
2462  for (var n = 0; n < parts.length; n++)
2463  this.draw_g.append("text")
2464  .attr("text-anchor", "middle")
2465  .attr("x", (width * (n + 0.5) / num_cols).toFixed(1))
2466  .attr("y", posy.toFixed(1))
2467  .call(font.func)
2468  .attr("fill", jcolor)
2469  .text(parts[n]);
2470  } else if ((j == 0) || (lines[j].indexOf('=') < 0)) {
2471  this.draw_g.append("text")
2472  .attr("text-anchor", (j == 0) ? "middle" : "start")
2473  .attr("x", ((j == 0) ? width / 2 : pavetext['fMargin'] * width).toFixed(1))
2474  .attr("y", posy.toFixed(1))
2475  .call(font.func)
2476  .attr("fill", jcolor)
2477  .text(lines[j]);
2478  } else {
2479  var parts = lines[j].split("=");
2480  for (var n = 0; n < 2; n++)
2481  this.draw_g.append("text")
2482  .attr("text-anchor", (n == 0) ? "start" : "end")
2483  .attr("x", ((n == 0) ? pavetext['fMargin'] * width : (1 - pavetext['fMargin']) * width).toFixed(1))
2484  .attr("y", posy.toFixed(1))
2485  .call(font.func)
2486  .attr("fill", jcolor)
2487  .text(parts[n]);
2488  }
2489  } else {
2490  this.draw_g.append("text")
2491  .attr("text-anchor", "start")
2492  .attr("x", text_pos_x.toFixed(1))
2493  .attr("y", posy.toFixed(1))
2494  .call(font.func)
2495  .attr("fill", jcolor)
2496  .text(lines[j]);
2497  }
2498  }
2499  }
2500 
2501  if (pavetext['fBorderSize'] && (pavetext['_typename'] == 'TPaveStats')) {
2502  this.draw_g.append("svg:line")
2503  .attr("class", "pavedraw")
2504  .attr("x1", 0)
2505  .attr("y1", stepy)
2506  .attr("x2", width)
2507  .attr("y2", stepy)
2508  .call(attline.func);
2509  }
2510 
2511  if ((first_stat > 0) && (num_cols > 1)) {
2512  for (var nrow = first_stat; nrow < nlines; nrow++)
2513  this.draw_g.append("svg:line")
2514  .attr("x1", 0)
2515  .attr("y1", nrow * stepy)
2516  .attr("x2", width)
2517  .attr("y2", nrow * stepy)
2518  .call(attline.func);
2519 
2520  for (var ncol = 0; ncol < num_cols - 1; ncol++)
2521  this.draw_g.append("svg:line")
2522  .attr("x1", width / num_cols * (ncol + 1))
2523  .attr("y1", first_stat * stepy)
2524  .attr("x2", width / num_cols * (ncol + 1))
2525  .attr("y2", height)
2526  .call(attline.func);
2527  }
2528 
2529  if (lwidth && lwidth > 1) {
2530  this.draw_g.append("svg:line")
2531  .attr("x1", width + (lwidth / 2))
2532  .attr("y1", lwidth + 1)
2533  .attr("x2", width + (lwidth / 2))
2534  .attr("y2", height + lwidth - 1)
2535  .style("stroke", attline.color)
2536  .style("stroke-width", lwidth);
2537  this.draw_g.append("svg:line")
2538  .attr("x1", lwidth + 1)
2539  .attr("y1", height + (lwidth / 2))
2540  .attr("x2", width + lwidth - 1)
2541  .attr("y2", height + (lwidth / 2))
2542  .style("stroke", attline.color)
2543  .style("stroke-width", lwidth);
2544  }
2545 
2546  this.AddDrag("stat", this.draw_g, {
2547  move : function(x, y, dx, dy) {
2548  pthis.draw_g.attr("transform", "translate(" + x + "," + y + ")");
2549 
2550  pthis.pavetext['fX1NDC'] += dx / Number(pthis.svg_pad(true).attr("width"));
2551  pthis.pavetext['fX2NDC'] += dx / Number(pthis.svg_pad(true).attr("width"));
2552  pthis.pavetext['fY1NDC'] -= dy / Number(pthis.svg_pad(true).attr("height"));
2553  pthis.pavetext['fY2NDC'] -= dy / Number(pthis.svg_pad(true).attr("height"));
2554  },
2555  resize : function(width, height) {
2556  pthis.pavetext['fX2NDC'] = pthis.pavetext['fX1NDC'] + width / Number(pthis.svg_pad(true).attr("width"));
2557  pthis.pavetext['fY1NDC'] = pthis.pavetext['fY2NDC'] - height / Number(pthis.svg_pad(true).attr("height"));
2558 
2559  pthis.DrawPaveText();
2560  }
2561  });
2562  }
2563 
2564  JSROOT.TPavePainter.prototype.AddLine = function(txt) {
2565  this.pavetext.AddText(txt);
2566  //this.pavetext['fLines'].arr.push({'fTitle' : txt, "fTextColor" : 1 });
2567  }
2568 
2569  JSROOT.TPavePainter.prototype.IsStats = function() {
2570  if (!this.pavetext) return false;
2571  return this.pavetext['fName'] == "stats";
2572  }
2573 
2574  JSROOT.TPavePainter.prototype.FillStatistic = function() {
2575  if (!this.IsStats()) return;
2576 
2577  var dostat = new Number(this.pavetext['fOptStat']);
2578  if (!dostat) dostat = new Number(JSROOT.gStyle.OptStat);
2579 
2580  // we take histogram from first painter
2581  if ('FillStatistic' in this.main_painter()) {
2582 
2583  // make empty at the beginning
2584  this.pavetext['fLines'].arr.length = 0;
2585 
2586  this.main_painter().FillStatistic(this, dostat);
2587  }
2588  }
2589 
2590  JSROOT.TPavePainter.prototype.UpdateObject = function(obj) {
2591  if (obj._typename != 'TPaveText') return false;
2592  this.pavetext['fLines'] = JSROOT.clone(obj['fLines']);
2593  return true;
2594  }
2595 
2596  JSROOT.TPavePainter.prototype.Redraw = function() {
2597 
2598  this.RemoveDrawG();
2599 
2600  // if pavetext artificially disabled, do not redraw it
2601  if (!this.Enabled) {
2602  this.RemoveDrag("stat");
2603  return;
2604  }
2605 
2606  this.FillStatistic();
2607 
2608  this.DrawPaveText();
2609  }
2610 
2611  JSROOT.Painter.drawPaveText = function(divid, pavetext) {
2612  if (pavetext['fX1NDC'] < 0.0 || pavetext['fY1NDC'] < 0.0 ||
2613  pavetext['fX1NDC'] > 1.0 || pavetext['fY1NDC'] > 1.0)
2614  return null;
2615 
2616  var painter = new JSROOT.TPavePainter(pavetext);
2617 
2618  painter.SetDivId(divid);
2619 
2620  // refill statistic in any case
2621  // if ('_AutoCreated' in pavetext)
2622  painter.FillStatistic();
2623 
2624  painter.DrawPaveText();
2625 
2626  return painter;
2627  }
2628 
2629  // ===========================================================================
2630 
2631  JSROOT.TPadPainter = function(pad, iscan) {
2632  JSROOT.TObjectPainter.call(this, pad);
2633  if (this.obj_typename=="") this.obj_typename = iscan ? "TCanvas" : "TPad";
2634  this.pad = pad;
2635  this.iscan = iscan; // indicate if workign with canvas
2636  this.painters = new Array; // complete list of all painters in the pad
2637  }
2638 
2639  JSROOT.TPadPainter.prototype = Object.create(JSROOT.TObjectPainter.prototype);
2640 
2641  JSROOT.TPadPainter.prototype.CreateCanvasSvg = function(only_resize) {
2642 
2643  var render_to = $("#" + this.divid);
2644 
2645  var w = render_to.width(), h = render_to.height();
2646 
2647  var svg = null;
2648 
2649  if (only_resize) {
2650  svg = this.svg_canvas(true);
2651  if ((svg.property('last_width') == w) && (svg.property('last_height') == h)) return false;
2652  } else {
2653 
2654  if (h < 10) {
2655  // set aspect ratio for the place, where object will be drawn
2656 
2657  var factor = 0.66;
2658 
2659  // for TCanvas reconstruct ratio between width and height
2660  if ((this.pad!=null) && ('fCw' in this.pad) && ('fCh' in this.pad) && (this.pad['fCw'] > 0)) {
2661  factor = this.pad['fCh'] / this.pad['fCw'];
2662  if ((factor < 0.1) || (factor > 10))
2663  factor = 0.66;
2664  }
2665 
2666  h = w * factor;
2667 
2668  render_to.height(h);
2669  }
2670 
2671  var fill = null;
2672 
2673  if (this.pad && 'fFillColor' in this.pad)
2674  fill = this.createAttFill(this.pad);
2675  else
2676  fill = this.createAttFill('white');
2677 
2678  render_to.css("background-color", fill.color);
2679 
2680  svg = d3.select("#" + this.divid)
2681  .append("svg")
2682  .attr("class", "root_canvas")
2683  .style("background-color", fill.color)
2684  .property('pad_painter', this) // this is custom property
2685  .property('mainpainter', null) // this is custom property
2686  .property('current_pad', "") // this is custom property
2687 
2688  svg.append("svg:g").attr("class","frame_layer");
2689  svg.append("svg:g").attr("class","text_layer");
2690  svg.append("svg:g").attr("class","stat_layer");
2691  }
2692 
2693 
2694  svg.attr("width", w)
2695  .attr("height", h)
2696  .attr("viewBox", "0 0 " + w + " " + h)
2697  .property('last_width', w)
2698  .property('last_height', h);
2699 
2700  return true;
2701  }
2702 
2703 
2704  JSROOT.TPadPainter.prototype.CreatePadSvg = function(only_resize) {
2705  var width = Number(this.svg_canvas(true).attr("width")),
2706  height = Number(this.svg_canvas(true).attr("height"));
2707  var x = Math.round(this.pad['fAbsXlowNDC'] * width);
2708  var y = Math.round(height - this.pad['fAbsYlowNDC'] * height);
2709  var w = Math.round(this.pad['fAbsWNDC'] * width);
2710  var h = Math.round(this.pad['fAbsHNDC'] * height);
2711  y -= h;
2712 
2713  var fill = this.createAttFill(this.pad);
2714  var attline = JSROOT.Painter.createAttLine(this.pad)
2715  if (this.pad['fBorderMode'] == 0) attline.color = 'none';
2716 
2717  var svg_pad = null, svg_rect = null;
2718 
2719  if (only_resize) {
2720  svg_pad = this.svg_pad(true);
2721  svg_rect = svg_pad.select(".root_pad_border");
2722  } else {
2723  svg_pad = this.svg_canvas(true).append("g")
2724  .attr("class", "root_pad")
2725  .attr("pad", this.pad['fName']) // set extra attribute to mark pad name
2726  .property('pad_painter', this) // this is custom property
2727  .property('mainpainter', null); // this is custom property
2728  svg_rect = svg_pad.append("svg:rect").attr("class", "root_pad_border");
2729  svg_pad.append("svg:g").attr("class","frame_layer");
2730  svg_pad.append("svg:g").attr("class","text_layer");
2731  svg_pad.append("svg:g").attr("class","stat_layer");
2732  }
2733 
2734  svg_pad.attr("width", w)
2735  .attr("height", h)
2736  .attr("viewBox", x + " " + y + " " + (x+w) + " " + (y+h))
2737  .attr("transform", "translate(" + x + "," + y + ")");
2738 
2739  svg_rect.attr("x", 0)
2740  .attr("y", 0)
2741  .attr("width", w)
2742  .attr("height", h)
2743  .call(fill.func)
2744  .call(attline.func);
2745  }
2746 
2747  JSROOT.TPadPainter.prototype.CheckColors = function(can) {
2748  if (can==null) return;
2749  for (var i in can.fPrimitives.arr) {
2750  var obj = can.fPrimitives.arr[i];
2751  if (obj==null) continue;
2752  if ((obj._typename=="TObjArray") && (obj.name == "ListOfColors")) {
2753  JSROOT.Painter.adoptRootColors(obj);
2754  can.fPrimitives.arr.splice(i,1);
2755  can.fPrimitives.opt.splice(i,1);
2756  return;
2757  }
2758  }
2759  }
2760 
2761  JSROOT.TPadPainter.prototype.DrawPrimitives = function() {
2762  if (this.pad==null) return;
2763 
2764  for (var i in this.pad.fPrimitives.arr) {
2765  var pp = JSROOT.draw(this.divid, this.pad.fPrimitives.arr[i], this.pad.fPrimitives.opt[i]);
2766  if (pp) pp['_primitive'] = true; // mark painter as belonging to primitive
2767  }
2768  }
2769 
2770  JSROOT.TPadPainter.prototype.Redraw = function(resize) {
2771  if (resize && !this.iscan) this.CreatePadSvg(true);
2772 
2773  // at the moment canvas painter donot redraw its subitems
2774  for (var i in this.painters)
2775  this.painters[i].Redraw(resize);
2776  }
2777 
2778 
2779  JSROOT.TPadPainter.prototype.CheckCanvasResize = function() {
2780  if (!this.iscan) return;
2781 
2782  var changed = this.CreateCanvasSvg(true);
2783  if (changed) this.Redraw(true);
2784  }
2785 
2786 
2787  JSROOT.TPadPainter.prototype.UpdateObject = function(obj) {
2788 
2789  if ((obj == null) || !('fPrimitives' in obj)) return false;
2790 
2791  if (this.iscan) this.CheckColors(obj);
2792 
2793  if (obj.fPrimitives.arr.length != this.pad.fPrimitives.arr.length) return false;
2794 
2795  var isany = false, p = 0;
2796 
2797  for (var n in obj.fPrimitives.arr) {
2798  var sub = obj.fPrimitives.arr[n];
2799 
2800  while (p<this.painters.length) {
2801  var pp = this.painters[p++];
2802  if (!('_primitive' in pp)) continue;
2803  if (pp.UpdateObject(sub)) isany = true;
2804  break;
2805  }
2806  }
2807 
2808  return isany;
2809  }
2810 
2811  JSROOT.Painter.drawCanvas = function(divid, can) {
2812  var painter = new JSROOT.TPadPainter(can, true);
2813  painter.SetDivId(divid, -1); // just assign id
2814  painter.CreateCanvasSvg();
2815  painter.SetDivId(divid); // now add to painters list
2816 
2817  if (can==null) {
2818  JSROOT.Painter.drawFrame(divid, null);
2819  } else {
2820  painter.CheckColors(can);
2821  painter.DrawPrimitives();
2822  }
2823 
2824  return painter;
2825  }
2826 
2827  JSROOT.Painter.drawPad = function(divid, pad) {
2828 
2829  var painter = new JSROOT.TPadPainter(pad, false);
2830  painter.SetDivId(divid); // pad painter will be registered in the canvas painters list
2831 
2832  painter.CreatePadSvg();
2833 
2834  painter.pad_name = pad['fName'];
2835 
2836  // we select current pad, where all drawing is performed
2837  var prev_name = painter.svg_canvas()['current_pad'];
2838  painter.svg_canvas()['current_pad'] = pad['fName'];
2839 
2840  painter.DrawPrimitives();
2841 
2842  // we restore previous pad name
2843  painter.svg_canvas()['current_pad'] = prev_name;
2844 
2845  return painter;
2846  }
2847 
2848  // ===========================================================================
2849 
2850  JSROOT.TColzPalettePainter = function(palette) {
2851  JSROOT.TObjectPainter.call(this, palette);
2852  this.palette = palette;
2853  }
2854 
2855  JSROOT.TColzPalettePainter.prototype = Object.create(JSROOT.TObjectPainter.prototype);
2856 
2857  JSROOT.TColzPalettePainter.prototype.GetObject = function() {
2858  return this.palette;
2859  }
2860 
2861  JSROOT.TColzPalettePainter.prototype.DrawPalette = function() {
2862  var palette = this.palette;
2863  var axis = palette['fAxis'];
2864 
2865  var minbin = this.main_painter().minbin;
2866  var maxbin = this.main_painter().maxbin;
2867  var nbr1 = axis['fNdiv'] % 100;
2868  if (nbr1<=0) nbr1 = 8;
2869 
2870  var width = Number(this.svg_pad(true).attr("width")),
2871  height = Number(this.svg_pad(true).attr("height"));
2872 
2873  var s_height = Math.round(Math.abs(palette['fY2NDC'] - palette['fY1NDC']) * height);
2874 
2875  var axisOffset = axis['fLabelOffset'] * width;
2876  var tickSize = axis['fTickSize'] * width;
2877 
2878  var z = d3.scale.linear().clamp(true).domain([ minbin, maxbin ]).range( [ s_height, 0 ]).nice();
2879 
2880  var labelfont = JSROOT.Painter.getFontDetails(axis['fLabelFont'], axis['fLabelSize'] * height);
2881 
2882  var pos_x = Math.round(palette['fX1NDC'] * width);
2883  var pos_y = Math.round(height*(1 - palette['fY1NDC']));
2884 
2885  var s_width = Math.round(Math.abs(palette['fX2NDC'] - palette['fX1NDC']) * width);
2886  pos_y -= s_height;
2887 
2888  // Draw palette pad
2889  this.RecreateDrawG(true, ".text_layer");
2890 
2891  this.draw_g
2892  .attr("x", pos_x).attr("y", pos_y) // position required only for drag functions
2893  .attr("width", s_width).attr("height", s_height) // dimension required only for drag functions
2894  .attr("transform", "translate(" + pos_x + ", " + pos_y + ")");
2895 
2896  var paletteColors = this.main_painter().paletteColors;
2897 
2898  // Draw palette
2899  var rectHeight = 1. * s_height / paletteColors.length;
2900 
2901  this.draw_g.selectAll("colorRect")
2902  .data(paletteColors)
2903  .enter()
2904  .append("svg:rect")
2905  .attr("class", "colorRect")
2906  .attr("x", 0)
2907  .attr("y", function(d, i) { return (s_height - (i + 1) * rectHeight).toFixed(1); })
2908  .attr("width", s_width)
2909  .attr("height", rectHeight.toFixed(1))
2910  .attr("fill", function(d) { return d; })
2911  .attr("stroke", function(d) { return d; });
2912  //.append("svg:title").text(function(d) { return "color" + d; });
2913  /*
2914  * Build and draw axes
2915  */
2916 
2917  var z_axis = d3.svg.axis().scale(z)
2918  .orient("right")
2919  .tickPadding(axisOffset)
2920  .tickSize(-tickSize, -tickSize / 2, 0)
2921  .ticks(nbr1);
2922 
2923  var zax = this.draw_g.append("svg:g")
2924  .attr("class", "zaxis")
2925  .attr("transform", "translate(" + s_width + ", 0)")
2926  .call(z_axis);
2927 
2928  zax.selectAll("text")
2929  .call(labelfont.func)
2930  .attr("fill", JSROOT.Painter.root_colors[axis['fLabelColor']]);
2931 
2932  /*
2933  * Add palette axis title
2934  */
2935  var title = axis['fTitle'];
2936  if (title != "" && typeof (axis['fTitleFont']) != 'undefined') {
2937  var titlefont = JSROOT.Painter.getFontDetails(axis['fTitleFont'], axis['fTitleSize'] * height);
2938  this.draw_g.append("text")
2939  .attr("class", "Z axis label")
2940  .attr("x", s_width + labelfont.size)
2941  .attr("y", s_height)
2942  .attr("text-anchor", "end")
2943  .call(titlefont.func);
2944  }
2945 
2946  var pthis = this;
2947 
2948  this.AddDrag("colz", this.draw_g, {
2949  move : function(x, y, dx, dy) {
2950 
2951  pthis.draw_g.attr("transform", "translate(" + x + "," + y + ")");
2952 
2953  pthis.palette['fX1NDC'] += dx / Number(pthis.svg_pad(true).attr("width"));
2954  pthis.palette['fX2NDC'] += dx / Number(pthis.svg_pad(true).attr("width"));
2955  pthis.palette['fY1NDC'] -= dy / Number(pthis.svg_pad(true).attr("height"));
2956  pthis.palette['fY2NDC'] -= dy / Number(pthis.svg_pad(true).attr("height"));
2957  },
2958  resize : function(width, height) {
2959  pthis.palette['fX2NDC'] = pthis.palette['fX1NDC'] + width / Number(pthis.svg_pad(true).attr("width"));
2960  pthis.palette['fY1NDC'] = pthis.palette['fY2NDC'] - height / Number(pthis.svg_pad(true).attr("height"));
2961 
2962  pthis.RemoveDrawG();
2963  pthis.DrawPalette();
2964  }
2965  });
2966  }
2967 
2968  JSROOT.TColzPalettePainter.prototype.Redraw = function() {
2969 
2970  var enabled = true;
2971 
2972  if ('options' in this.main_painter())
2973  enabled = (this.main_painter().options.Zscale > 0) && (this.main_painter().options.Color > 0);
2974 
2975  if (enabled) {
2976  this.DrawPalette();
2977  } else {
2978  // if palette artificially disabled, do not redraw it
2979  this.RemoveDrawG();
2980  this.RemoveDrag("colz");
2981  }
2982  }
2983 
2984  JSROOT.Painter.drawPaletteAxis = function(divid, palette) {
2985  var painter = new JSROOT.TColzPalettePainter(palette);
2986 
2987  painter.SetDivId(divid);
2988 
2989  painter.DrawPalette();
2990 
2991  return painter;
2992  }
2993 
2994  // =============================================================
2995 
2996  JSROOT.THistPainter = function(histo) {
2997  JSROOT.TObjectPainter.call(this, histo);
2998  this.histo = histo;
2999  this.shrink_frame_left = 0.;
3000  this.draw_content = true;
3001  this.nbinsx = 0;
3002  this.nbinsy = 0;
3003  }
3004 
3005  JSROOT.THistPainter.prototype = Object.create(JSROOT.TObjectPainter.prototype);
3006 
3007  JSROOT.THistPainter.prototype.GetObject = function() {
3008  return this.histo;
3009  }
3010 
3011  JSROOT.THistPainter.prototype.IsTProfile = function() {
3012  return this.histo && this.histo['_typename'] == 'TProfile';
3013  }
3014 
3015  JSROOT.THistPainter.prototype.IsTH2Poly = function() {
3016  return this.histo && this.histo['_typename'].match(/^TH2Poly/);
3017  }
3018 
3019  JSROOT.THistPainter.prototype.Dimension = function() {
3020  if (!this.histo) return 0;
3021  if ('fDimension' in this.histo) return this.histo['fDimension'];
3022  if (this.histo['_typename'].match(/^TH2/)) return 2;
3023  if (this.histo['_typename'].match(/^TH3/)) return 3;
3024  return 1;
3025  }
3026 
3027  JSROOT.THistPainter.prototype.DecodeOptions = function(opt) {
3028  if ((opt == null) || (opt == "")) opt = this.histo['fOption'];
3029 
3030  /* decode string 'opt' and fill the option structure */
3031  var hdim = this.Dimension();
3032  var nch = opt.length;
3033  var option = {
3034  Axis: 0, Bar: 0, Curve: 0, Error: 0, Hist: 0, Line: 0,
3035  Mark: 0, Fill: 0, Same: 0, Scat: 0, Func: 0, Star: 0,
3036  Arrow: 0, Box: 0, Text: 0, Char: 0, Color: 0, Contour: 0,
3037  Lego: 0, Surf: 0, Off: 0, Tri: 0, Proj: 0, AxisPos: 0,
3038  Spec: 0, Pie: 0, List: 0, Zscale: 0, FrontBox: 1, BackBox: 1,
3039  System: JSROOT.Painter.Coord.kCARTESIAN,
3040  HighRes: 0, Zero: 0, Logx: 0, Logy: 0, Logz: 0, Gridx: 0, Gridy: 0
3041  };
3042  // check for graphical cuts
3043  var chopt = opt.toUpperCase();
3044  chopt = JSROOT.Painter.clearCuts(chopt);
3045  if (hdim > 1) option.Scat = 1;
3046  if (!nch) option.Hist = 1;
3047  if (this.IsTProfile()) option.Error = 2;
3048  if ('fFunctions' in this.histo) option.Func = 1;
3049 
3050  if (chopt.indexOf('LOGX') != -1) {
3051  option.Logx = 1;
3052  chopt = chopt.replace('LOGX', '');
3053  }
3054  if (chopt.indexOf('LOGY') != -1) {
3055  option.Logy = 1;
3056  chopt = chopt.replace('LOGY', '');
3057  }
3058 
3059  var l = chopt.indexOf('SPEC');
3060  if (l != -1) {
3061  option.Scat = 0;
3062  chopt = chopt.replace('SPEC', ' ');
3063  var bs = 0;
3064  l = chopt.indexOf('BF(');
3065  if (l != -1) bs = parseInt(chopt)
3066  option.Spec = Math.max(1600, bs);
3067  return option;
3068  }
3069  if (chopt.indexOf('GL') != -1) chopt = chopt.replace('GL', ' ');
3070  if (chopt.indexOf('X+') != -1) {
3071  option.AxisPos = 10;
3072  chopt = chopt.replace('X+', ' ');
3073  }
3074  if (chopt.indexOf('Y+') != -1) {
3075  option.AxisPos += 1;
3076  chopt = chopt.replace('Y+', ' ');
3077  }
3078  if ((option.AxisPos == 10 || option.AxisPos == 1) && (nch == 2))
3079  option.Hist = 1;
3080  if (option.AxisPos == 11 && nch == 4)
3081  option.Hist = 1;
3082  if (chopt.indexOf('SAMES') != -1) {
3083  if (nch == 5) option.Hist = 1;
3084  option.Same = 2;
3085  chopt = chopt.replace('SAMES', ' ');
3086  }
3087  if (chopt.indexOf('SAME') != -1) {
3088  if (nch == 4) option.Hist = 1;
3089  option.Same = 1;
3090  chopt = chopt.replace('SAME', ' ');
3091  }
3092  if (chopt.indexOf('PIE') != -1) {
3093  option.Pie = 1;
3094  chopt = chopt.replace('PIE', ' ');
3095  }
3096  l = chopt.indexOf('LEGO');
3097  if (l != -1) {
3098  option.Scat = 0;
3099  option.Lego = 1;
3100  chopt = chopt.replace('LEGO', ' ');
3101  if (chopt[l + 4] == '1') {
3102  option.Lego = 11;
3103  chopt[l + 4] = ' ';
3104  }
3105  if (chopt[l + 4] == '2') {
3106  option.Lego = 12;
3107  chopt[l + 4] = ' ';
3108  }
3109  if (chopt[l + 4] == '3') {
3110  option.Lego = 13;
3111  chopt[l + 4] = ' ';
3112  }
3113  l = chopt.indexOf('FB');
3114  if (l != -1) {
3115  option.FrontBox = 0;
3116  chopt = chopt.replace('FB', ' ');
3117  }
3118  l = chopt.indexOf('BB');
3119  if (l != -1) {
3120  option.BackBox = 0;
3121  chopt = chopt.replace('BB', ' ');
3122  }
3123  l = chopt.indexOf('0');
3124  if (l != -1) {
3125  option.Zero = 1;
3126  chopt = chopt.replace('0', ' ');
3127  }
3128  }
3129  l = chopt.indexOf('SURF');
3130  if (l != -1) {
3131  option.Scat = 0;
3132  option.Surf = 1;
3133  chopt = chopt.replace('SURF', ' ');
3134  if (chopt[l + 4] == '1') {
3135  option.Surf = 11;
3136  chopt[l + 4] = ' ';
3137  }
3138  if (chopt[l + 4] == '2') {
3139  option.Surf = 12;
3140  chopt[l + 4] = ' ';
3141  }
3142  if (chopt[l + 4] == '3') {
3143  option.Surf = 13;
3144  chopt[l + 4] = ' ';
3145  }
3146  if (chopt[l + 4] == '4') {
3147  option.Surf = 14;
3148  chopt[l + 4] = ' ';
3149  }
3150  if (chopt[l + 4] == '5') {
3151  option.Surf = 15;
3152  chopt[l + 4] = ' ';
3153  }
3154  if (chopt[l + 4] == '6') {
3155  option.Surf = 16;
3156  chopt[l + 4] = ' ';
3157  }
3158  if (chopt[l + 4] == '7') {
3159  option.Surf = 17;
3160  chopt[l + 4] = ' ';
3161  }
3162  l = chopt.indexOf('FB');
3163  if (l != -1) {
3164  option.FrontBox = 0;
3165  chopt = chopt.replace('FB', ' ');
3166  }
3167  l = chopt.indexOf('BB');
3168  if (l != -1) {
3169  option.BackBox = 0;
3170  chopt = chopt.replace('BB', ' ');
3171  }
3172  }
3173  l = chopt.indexOf('TF3');
3174  if (l != -1) {
3175  l = chopt.indexOf('FB');
3176  if (l != -1) {
3177  option.FrontBox = 0;
3178  chopt = chopt.replace('FB', ' ');
3179  }
3180  l = chopt.indexOf('BB');
3181  if (l != -1) {
3182  option.BackBox = 0;
3183  chopt = chopt.replace('BB', ' ');
3184  }
3185  }
3186  l = chopt.indexOf('ISO');
3187  if (l != -1) {
3188  l = chopt.indexOf('FB');
3189  if (l != -1) {
3190  option.FrontBox = 0;
3191  chopt = chopt.replace('FB', ' ');
3192  }
3193  l = chopt.indexOf('BB');
3194  if (l != -1) {
3195  option.BackBox = 0;
3196  chopt = chopt.replace('BB', ' ');
3197  }
3198  }
3199  l = chopt.indexOf('LIST');
3200  if (l != -1) {
3201  option.List = 1;
3202  chopt = chopt.replace('LIST', ' ');
3203  }
3204  l = chopt.indexOf('CONT');
3205  if (l != -1) {
3206  chopt = chopt.replace('CONT', ' ');
3207  if (hdim > 1) {
3208  option.Scat = 0;
3209  option.Contour = 1;
3210  if (chopt[l + 4] == '1') {
3211  option.Contour = 11;
3212  chopt[l + 4] = ' ';
3213  }
3214  if (chopt[l + 4] == '2') {
3215  option.Contour = 12;
3216  chopt[l + 4] = ' ';
3217  }
3218  if (chopt[l + 4] == '3') {
3219  option.Contour = 13;
3220  chopt[l + 4] = ' ';
3221  }
3222  if (chopt[l + 4] == '4') {
3223  option.Contour = 14;
3224  chopt[l + 4] = ' ';
3225  }
3226  if (chopt[l + 4] == '5') {
3227  option.Contour = 15;
3228  chopt[l + 4] = ' ';
3229  }
3230  } else {
3231  option.Hist = 1;
3232  }
3233  }
3234  l = chopt.indexOf('HBAR');
3235  if (l != -1) {
3236  option.Hist = 0;
3237  option.Bar = 20;
3238  chopt = chopt.replace('HBAR', ' ');
3239  if (chopt[l + 4] == '1') {
3240  option.Bar = 21;
3241  chopt[l + 4] = ' ';
3242  }
3243  if (chopt[l + 4] == '2') {
3244  option.Bar = 22;
3245  chopt[l + 4] = ' ';
3246  }
3247  if (chopt[l + 4] == '3') {
3248  option.Bar = 23;
3249  chopt[l + 4] = ' ';
3250  }
3251  if (chopt[l + 4] == '4') {
3252  option.Bar = 24;
3253  chopt[l + 4] = ' ';
3254  }
3255  }
3256  l = chopt.indexOf('BAR');
3257  if (l != -1) {
3258  option.Hist = 0;
3259  option.Bar = 10;
3260  chopt = chopt.replace('BAR', ' ');
3261  if (chopt[l + 3] == '1') {
3262  option.Bar = 11;
3263  chopt[l + 3] = ' ';
3264  }
3265  if (chopt[l + 3] == '2') {
3266  option.Bar = 12;
3267  chopt[l + 3] = ' ';
3268  }
3269  if (chopt[l + 3] == '3') {
3270  option.Bar = 13;
3271  chopt[l + 3] = ' ';
3272  }
3273  if (chopt[l + 3] == '4') {
3274  option.Bar = 14;
3275  chopt[l + 3] = ' ';
3276  }
3277  }
3278  l = chopt.indexOf('ARR');
3279  if (l != -1) {
3280  chopt = chopt.replace('ARR', ' ');
3281  if (hdim > 1) {
3282  option.Arrow = 1;
3283  option.Scat = 0;
3284  } else {
3285  option.Hist = 1;
3286  }
3287  }
3288  l = chopt.indexOf('BOX');
3289  if (l != -1) {
3290  chopt = chopt.replace('BOX', ' ');
3291  if (hdim > 1) {
3292  Hoption.Scat = 0;
3293  Hoption.Box = 1;
3294  if (chopt[l + 3] == '1') {
3295  option.Box = 11;
3296  chopt[l + 3] = ' ';
3297  }
3298  } else {
3299  option.Hist = 1;
3300  }
3301  }
3302 
3303  l = chopt.indexOf('COL');
3304  if (l!=-1) {
3305  var name = 'COL';
3306 
3307  if (chopt.charAt(l+3)=='1') { option.Color = 1; name += "1"; l++; } else
3308  if (chopt.charAt(l+3)=='2') { option.Color = 2; name += "2"; l++; } else
3309  if (chopt.charAt(l+3)=='3') { option.Color = 3; name += "3"; l++; } else
3310  option.Color = JSROOT.gStyle.DefaultCol;
3311 
3312  if (chopt.charAt(l+4)=='Z') { option.Zscale = 1; name += 'Z'; }
3313  chopt = chopt.replace(name, '');
3314  if (hdim == 1) {
3315  option.Hist = 1;
3316  } else {
3317  option.Scat = 0;
3318  }
3319  }
3320 
3321  if (chopt.indexOf('CHAR') != -1) {
3322  option.Char = 1;
3323  chopt = chopt.replace('CHAR', ' ');
3324  option.Scat = 0;
3325  }
3326  l = chopt.indexOf('FUNC');
3327  if (l != -1) {
3328  option.Func = 2;
3329  chopt = chopt.replace('FUNC', ' ');
3330  option.Hist = 0;
3331  }
3332  l = chopt.indexOf('HIST');
3333  if (l != -1) {
3334  option.Hist = 2;
3335  chopt = chopt.replace('HIST', ' ');
3336  option.Func = 0;
3337  option.Error = 0;
3338  }
3339  if (chopt.indexOf('AXIS') != -1) {
3340  option.Axis = 1;
3341  chopt = chopt.replace('AXIS', ' ');
3342  }
3343  if (chopt.indexOf('AXIG') != -1) {
3344  option.Axis = 2;
3345  chopt = chopt.replace('AXIG', ' ');
3346  }
3347  if (chopt.indexOf('SCAT') != -1) {
3348  option.Scat = 1;
3349  chopt = chopt.replace('SCAT', ' ');
3350  }
3351  l = chopt.indexOf('TEXT');
3352  if (l != -1) {
3353  var angle = parseInt(chopt);
3354  if (!isNaN(angle)) {
3355  if (angle < 0)
3356  angle = 0;
3357  if (angle > 90)
3358  angle = 90;
3359  option.Text = 1000 + angle;
3360  } else {
3361  option.Text = 1;
3362  }
3363  chopt = chopt.replace('TEXT', ' ');
3364  l = chopt.indexOf('N');
3365  if (l != -1 && this.IsTH2Poly())
3366  option.Text += 3000;
3367  option.Scat = 0;
3368  }
3369  if (chopt.indexOf('POL') != -1) {
3370  option.System = JSROOT.Painter.Coord.kPOLAR;
3371  chopt = chopt.replace('POL', ' ');
3372  }
3373  if (chopt.indexOf('CYL') != -1) {
3374  option.System = JSROOT.Painter.Coord.kCYLINDRICAL;
3375  chopt = chopt.replace('CYL', ' ');
3376  }
3377  if (chopt.indexOf('SPH') != -1) {
3378  option.System = JSROOT.Painter.Coord.kSPHERICAL;
3379  chopt = chopt.replace('SPH', ' ');
3380  }
3381  l = chopt.indexOf('PSR');
3382  if (l != -1) {
3383  option.System = JSROOT.Painter.Coord.kRAPIDITY;
3384  chopt = chopt.replace('PSR', ' ');
3385  }
3386  l = chopt.indexOf('TRI');
3387  if (l != -1) {
3388  option.Scat = 0;
3389  option.Color = 0;
3390  option.Tri = 1;
3391  chopt = chopt.replace('TRI', ' ');
3392  l = chopt.indexOf('FB');
3393  if (l != -1) {
3394  option.FrontBox = 0;
3395  chopt = chopt.replace('FB', ' ');
3396  }
3397  l = chopt.indexOf('BB');
3398  if (l != -1) {
3399  option.BackBox = 0;
3400  chopt = chopt.replace('BB', ' ');
3401  }
3402  l = chopt.indexOf('ERR');
3403  if (l != -1)
3404  chopt = chopt.replace('ERR', ' ');
3405  }
3406  l = chopt.indexOf('AITOFF');
3407  if (l != -1) {
3408  Hoption.Proj = 1;
3409  chopt = chopt.replace('AITOFF', ' '); // Aitoff projection
3410  }
3411  l = chopt.indexOf('MERCATOR');
3412  if (l != -1) {
3413  option.Proj = 2;
3414  chopt = chopt.replace('MERCATOR', ' '); // Mercator projection
3415  }
3416  l = chopt.indexOf('SINUSOIDAL');
3417  if (l != -1) {
3418  option.Proj = 3;
3419  chopt = chopt.replace('SINUSOIDAL', ' '); // Sinusoidal
3420  // projection
3421  }
3422  l = chopt.indexOf('PARABOLIC');
3423  if (l != -1) {
3424  option.Proj = 4;
3425  chopt = chopt.replace('PARABOLIC', ' '); // Parabolic
3426  // projection
3427  }
3428  if (option.Proj > 0) {
3429  option.Scat = 0;
3430  option.Contour = 14;
3431  }
3432  if (chopt.indexOf('A') != -1)
3433  option.Axis = -1;
3434  if (chopt.indexOf('B') != -1)
3435  option.Bar = 1;
3436  if (chopt.indexOf('C') != -1) {
3437  option.Curve = 1;
3438  option.Hist = -1;
3439  }
3440  if (chopt.indexOf('F') != -1)
3441  option.Fill = 1;
3442  if (chopt.indexOf('][') != -1) {
3443  option.Off = 1;
3444  option.Hist = 1;
3445  }
3446  if (chopt.indexOf('F2') != -1) option.Fill = 2;
3447  if (chopt.indexOf('L') != -1) {
3448  option.Line = 1;
3449  option.Hist = -1;
3450  }
3451  if (chopt.indexOf('P') != -1) {
3452  option.Mark = 1;
3453  option.Hist = -1;
3454  }
3455  if (chopt.indexOf('Z') != -1) option.Zscale = 1;
3456  if (chopt.indexOf('*') != -1) option.Star = 1;
3457  if (chopt.indexOf('H') != -1) option.Hist = 2;
3458  if (chopt.indexOf('P0') != -1) option.Mark = 10;
3459  if (this.IsTH2Poly()) {
3460  if (option.Fill + option.Line + option.Mark != 0) option.Scat = 0;
3461  }
3462 
3463  if (chopt.indexOf('E') != -1) {
3464  if (hdim == 1) {
3465  option.Error = 1;
3466  if (chopt.indexOf('E0') != -1) option.Error = 10;
3467  if (chopt.indexOf('E1') != -1) option.Error = 11;
3468  if (chopt.indexOf('E2') != -1) option.Error = 12;
3469  if (chopt.indexOf('E3') != -1) option.Error = 13;
3470  if (chopt.indexOf('E4') != -1) option.Error = 14;
3471  if (chopt.indexOf('E5') != -1) option.Error = 15;
3472  if (chopt.indexOf('E6') != -1) option.Error = 16;
3473  if (chopt.indexOf('X0') != -1) {
3474  if (option.Error == 1) option.Error += 20;
3475  option.Error += 10;
3476  }
3477  if (option.Text && this.IsTProfile()) {
3478  option.Text += 2000;
3479  option.Error = 0;
3480  }
3481  } else {
3482  if (option.Error == 0) {
3483  option.Error = 100;
3484  option.Scat = 0;
3485  }
3486  if (option.Text) {
3487  option.Text += 2000;
3488  option.Error = 0;
3489  }
3490  }
3491  }
3492  if (chopt.indexOf('9') != -1) option.HighRes = 1;
3493  if (option.Surf == 15) {
3494  if (option.System == JSROOT.Painter.Coord.kPOLAR
3495  || option.System == JSROOT.Painter.Coord.kCARTESIAN) {
3496  option.Surf = 13;
3497  // Warning('MakeChopt','option SURF5 is not supported in Cartesian
3498  // and Polar modes');
3499  }
3500  }
3501 
3502  // Check options incompatibilities
3503  if (option.Bar == 1) option.Hist = -1;
3504 
3505  return option;
3506  }
3507 
3508  JSROOT.THistPainter.prototype.ScanContent = function() {
3509  // function will be called once new histogram or
3510  // new histogram content is assigned
3511  // one should find min,max,nbins, maxcontent values
3512 
3513  alert("HistPainter.prototype.ScanContent not implemented");
3514  }
3515 
3516  JSROOT.THistPainter.prototype.CheckPadOptions = function() {
3517 
3518  var pad = this.root_pad();
3519 
3520  if (pad!=null) {
3521  // Copy options from current pad
3522  this.options.Logx = pad['fLogx'];
3523  this.options.Logy = pad['fLogy'];
3524  this.options.Logz = pad['fLogz'];
3525  this.options.Gridx = pad['fGridx'];
3526  this.options.Gridy = pad['fGridy'];
3527  }
3528 
3529  if (this.main_painter() !== this) return;
3530 
3531  this['zoom_xmin'] = 0;
3532  this['zoom_xmax'] = 0;
3533  this['zoom_xpad'] = true; // indicate that zooming specified from pad
3534 
3535  this['zoom_ymin'] = 0;
3536  this['zoom_ymax'] = 0;
3537  this['zoom_ypad'] = true; // indicate that zooming specified from pad
3538 
3539  if ((pad!=null) && ('fUxmin' in pad) && !this.create_canvas) {
3540  this['zoom_xmin'] = pad.fUxmin;
3541  this['zoom_xmax'] = pad.fUxmax;
3542  this['zoom_ymin'] = pad.fUymin;
3543  this['zoom_ymax'] = pad.fUymax;
3544 
3545  if (pad.fLogx > 0) {
3546  this['zoom_xmin'] = Math.exp(this['zoom_xmin'] * Math.log(10));
3547  this['zoom_xmax'] = Math.exp(this['zoom_xmax'] * Math.log(10));
3548  }
3549 
3550  if (pad.fLogy > 0) {
3551  this['zoom_ymin'] = Math.exp(this['zoom_ymin'] * Math.log(10));
3552  this['zoom_ymax'] = Math.exp(this['zoom_ymax'] * Math.log(10));
3553  }
3554  }
3555  }
3556 
3557 
3558  JSROOT.THistPainter.prototype.UpdateObject = function(obj) {
3559  if (obj['_typename'] != this.histo['_typename']) {
3560  alert("JSROOT.THistPainter.UpdateObject - wrong class " + obj['_typename'] + " expected " + this.histo['_typename']);
3561  return false;
3562  }
3563 
3564  // TODO: simple replace of object does not help - one can have different
3565  // complex relations between histo and stat box, histo and colz axis,
3566  // on could have THStack or TMultiGraph object
3567  // The only that could be done is update of content
3568 
3569  // this.histo = obj;
3570 
3571  this.histo['fArray'] = obj['fArray'];
3572  this.histo['fN'] = obj['fN'];
3573  this.histo['fTitle'] = obj['fTitle'];
3574  this.histo['fXaxis']['fNbins'] = obj['fXaxis']['fNbins'];
3575  this.histo['fXaxis']['fXmin'] = obj['fXaxis']['fXmin'];
3576  this.histo['fXaxis']['fXmax'] = obj['fXaxis']['fXmax'];
3577  this.histo['fYaxis']['fXmin'] = obj['fYaxis']['fXmin'];
3578  this.histo['fYaxis']['fXmax'] = obj['fYaxis']['fXmax'];
3579 
3580  if (this.IsTProfile()) {
3581  this.histo['fBinEntries'] = obj['fBinEntries'];
3582  this.histo['fSumw2'] = obj['fSumw2'];
3583  }
3584 
3585  this.ScanContent();
3586 
3587  return true;
3588  }
3589 
3590  JSROOT.THistPainter.prototype.CreateXY = function() {
3591  // here we create x,y objects which maps our physical coordnates into pixels
3592  // while only first painter really need such object, all others just reuse it
3593 
3594  if (!this.is_main_painter()) {
3595  this['x'] = this.main_painter()['x'];
3596  this['y'] = this.main_painter()['y'];
3597  return;
3598  }
3599 
3600  var w = Number(this.svg_frame(true).attr("width")),
3601  h = Number(this.svg_frame(true).attr("height"));
3602 
3603  this['scale_xmin'] = this.xmin;
3604  this['scale_xmax'] = this.xmax;
3605  if (this.zoom_xmin != this.zoom_xmax) {
3606  this['scale_xmin'] = this.zoom_xmin;
3607  this['scale_xmax'] = this.zoom_xmax;
3608  }
3609 
3610  if (this.options.Logx) {
3611  if (this.scale_xmax <= 0) this.scale_xmax = 0;
3612 
3613  if ((this.scale_xmin <= 0) && (this.nbinsx>0))
3614  for (var i=0;i<this.nbinsx;i++) {
3615  var left = this.xmin + i*this.binwidthx;
3616  if (left>0) { this.scale_xmin = left; break; }
3617  }
3618 
3619  if ((this.scale_xmin <= 0) || (this.scale_xmin >= this.scale_xmax)) {
3620  this.scale_xmin = this.scale_xmax * 0.0001;
3621  }
3622 
3623  this['x'] = d3.scale.log().domain([ this.scale_xmin, this.scale_xmax ]).range([ 0, w ]); // .clamp(true);
3624  } else {
3625  this['x'] = d3.scale.linear().domain([ this.scale_xmin, this.scale_xmax ]).range([ 0, w ]);
3626  }
3627 
3628  this['scale_ymin'] = this.ymin;
3629  this['scale_ymax'] = this.ymax;
3630 
3631  if (this.zoom_ypad) {
3632  if (this.histo.fMinimum != -1111) this.zoom_ymin = this.histo.fMinimum;
3633  if (this.histo.fMaximum != -1111) this.zoom_ymax = this.histo.fMaximum;
3634  this['zoom_ypad'] = false;
3635  }
3636 
3637  if (this.zoom_ymin != this.zoom_ymax) {
3638  this['scale_ymin'] = this.zoom_ymin;
3639  this['scale_ymax'] = this.zoom_ymax;
3640  }
3641 
3642  if (this.options.Logy) {
3643  if (this.scale_ymax <= 0) this.scale_ymax = 1;
3644 
3645  if ((this.scale_ymin <= 0) && (this.nbinsy>0))
3646  for (var i=0;i<this.nbinsy;i++) {
3647  var down = this.ymin + i*this.binwidthy;
3648  if (down>0) { this.scale_ymin = down; break; }
3649  }
3650 
3651  if ((this.scale_ymin <= 0) && ('ymin_nz' in this) && (this.ymin_nz > 0))
3652  this.scale_ymin = 0.3*this.ymin_nz;
3653 
3654  if ((this.scale_ymin <= 0) || (this.scale_ymin >= this.scale_ymax))
3655  this.scale_ymin = 0.000001 * this.scale_ymax;
3656  this['y'] = d3.scale.log().domain([ this.scale_ymin, this.scale_ymax ]).range([ h, 0 ]); // .clamp(true);
3657  } else {
3658  this['y'] = d3.scale.linear().domain([ this.scale_ymin, this.scale_ymax ]).range([ h, 0 ]);
3659  }
3660  }
3661 
3662  JSROOT.THistPainter.prototype.DrawGrids = function() {
3663  // grid can only be drawn by first painter
3664  if (!this.is_main_painter()) return;
3665 
3666  var layer = this.svg_frame(true).select(".grid_layer");
3667 
3668  layer.selectAll(".xgrid").remove();
3669  layer.selectAll(".ygrid").remove();
3670  /* add a grid on x axis, if the option is set */
3671 
3672  // add a grid on x axis, if the option is set
3673  if (this.options.Gridx) {
3674 
3675  var h = Number(this.svg_frame(true).attr("height"));
3676 
3677  var xticks = this.x.ticks(this.x_nticks);
3678 
3679  layer.selectAll(".xgrid")
3680  .data(xticks).enter()
3681  .append("svg:line")
3682  .attr("class", "xgrid")
3683  .attr("x1", this.x)
3684  .attr("y1", h)
3685  .attr("x2", this.x)
3686  .attr("y2",0)
3687  .style("stroke", "black")
3688  .style("stroke-width", 1)
3689  .style("stroke-dasharray", JSROOT.Painter.root_line_styles[11]);
3690  }
3691 
3692  // add a grid on y axis, if the option is set
3693  if (this.options.Gridy) {
3694  var w = Number(this.svg_frame(true).attr("width"));
3695 
3696  var yticks = this.y.ticks(this.y_nticks);
3697 
3698  layer.selectAll('.ygrid')
3699  .data(yticks).enter()
3700  .append("svg:line")
3701  .attr("class", "ygrid")
3702  .attr("x1", 0)
3703  .attr("y1", this.y)
3704  .attr("x2", w)
3705  .attr("y2", this.y)
3706  .style("stroke", "black")
3707  .style("stroke-width", 1)
3708  .style("stroke-dasharray", JSROOT.Painter.root_line_styles[11]);
3709  }
3710  }
3711 
3712  JSROOT.THistPainter.prototype.DrawBins = function() {
3713  alert("HistPainter.DrawBins not implemented");
3714  }
3715 
3716  JSROOT.THistPainter.prototype.AxisAsText = function(axis, value) {
3717  if (axis == "x") {
3718  // this is indication
3719  if ('dfx' in this) {
3720  return this.dfx(new Date(this.timeoffsetx + value * 1000));
3721  }
3722 
3723  if (Math.abs(value) < 1e-14)
3724  if (Math.abs(this.xmax - this.xmin) > 1e-5)
3725  value = 0;
3726  return value.toPrecision(4);
3727  }
3728 
3729  if (axis == "y") {
3730  if ('dfy' in this) {
3731  return this.dfy(new Date(this.timeoffsety + value * 1000));
3732  }
3733  if (Math.abs(value) < 1e-14)
3734  if (Math.abs(this.ymax - this.ymin) > 1e-5)
3735  value = 0;
3736  return value.toPrecision(4);
3737  }
3738 
3739  return value.toPrecision(4);
3740  }
3741 
3742  JSROOT.THistPainter.prototype.DrawAxes = function(shrink_forbidden) {
3743  // axes can be drawn only for main histogram
3744 
3745  if (!this.is_main_painter()) return;
3746 
3747  var w = Number(this.svg_frame(true).attr("width")),
3748  h = Number(this.svg_frame(true).attr("height"));
3749  var noexpx = this.histo['fXaxis'].TestBit(JSROOT.EAxisBits.kNoExponent);
3750  var noexpy = this.histo['fYaxis'].TestBit(JSROOT.EAxisBits.kNoExponent);
3751  var moreloglabelsx = this.histo['fXaxis'].TestBit(JSROOT.EAxisBits.kMoreLogLabels);
3752  var moreloglabelsy = this.histo['fYaxis'].TestBit(JSROOT.EAxisBits.kMoreLogLabels);
3753  if (this.histo['fXaxis']['fXmax'] < 100 && this.histo['fXaxis']['fXmax'] / this.histo['fXaxis']['fXmin'] < 100) noexpx = true;
3754  if (this.histo['fYaxis']['fXmax'] < 100 && this.histo['fYaxis']['fXmax'] / this.histo['fYaxis']['fXmin'] < 100) noexpy = true;
3755 
3756  var xax_g = this.svg_frame(true).selectAll(".xaxis_container");
3757  if (xax_g.empty())
3758  xax_g = this.svg_frame(true).select(".axis_layer").append("svg:g").attr("class","xaxis_container");
3759 
3760  xax_g.selectAll("*").remove();
3761  xax_g.attr("transform", "translate(0," + h + ")");
3762 
3763  var yax_g = this.svg_frame(true).selectAll(".yaxis_container");
3764  if (yax_g.empty()) yax_g = this.svg_frame(true).select(".axis_layer").append("svg:g").attr("class", "yaxis_container");
3765  yax_g.selectAll("*").remove();
3766 
3767  var x_axis = null, y_axis = null;
3768 
3769  var ndivx = this.histo['fXaxis']['fNdivisions'];
3770  this['x_nticks'] = ndivx % 100; // used also to draw grids
3771  var n2ax = (ndivx % 10000 - this.x_nticks) / 100;
3772  var n3ax = ndivx / 10000;
3773 
3774  var ndivy = this.histo['fYaxis']['fNdivisions'];
3775  this['y_nticks'] = ndivy % 100; // used also to draw grids
3776  var n2ay = (ndivy % 10000 - this.y_nticks) / 100;
3777  var n3ay = ndivy / 10000;
3778 
3779  /* X-axis label */
3780  var label = JSROOT.Painter.translateLaTeX(this.histo['fXaxis']['fTitle']);
3781  var xAxisLabelOffset = 3 + (this.histo['fXaxis']['fLabelOffset'] * h);
3782 
3783  var xlabelfont = JSROOT.Painter.getFontDetails(this.histo['fXaxis']['fLabelFont'], this.histo['fXaxis']['fLabelSize'] * h);
3784 
3785  var ylabelfont = JSROOT.Painter.getFontDetails(this.histo['fYaxis']['fLabelFont'], this.histo['fYaxis']['fLabelSize'] * h);
3786 
3787  if (label.length > 0) {
3788  var xtitlefont = JSROOT.Painter.getFontDetails(this.histo['fXaxis']['fTitleFont'], this.histo['fXaxis']['fTitleSize'] * h);
3789  xax_g.append("text")
3790  .attr("class", "x_axis_label")
3791  .attr("x", w)
3792  .attr("y", xlabelfont.size + xAxisLabelOffset * this.histo['fXaxis']['fTitleOffset'] + xtitlefont.size)
3793  .attr("text-anchor", "end")
3794  .call(xtitlefont.func)
3795  .text(label);
3796  }
3797 
3798  /* Y-axis label */
3799  label = JSROOT.Painter.translateLaTeX(this.histo['fYaxis']['fTitle']);
3800 
3801  var yAxisLabelOffset = 3 + (this.histo['fYaxis']['fLabelOffset'] * w);
3802 
3803  if (label.length > 0) {
3804  var ytitlefont = JSROOT.Painter.getFontDetails(this.histo['fYaxis']['fTitleFont'], this.histo['fYaxis']['fTitleSize'] * h);
3805  yax_g.append("text")
3806  .attr("class", "y_axis_label")
3807  .attr("x", 0)
3808  .attr("y", - ylabelfont.size - ytitlefont.size - yAxisLabelOffset * this.histo['fYaxis']['fTitleOffset'])
3809  .call(ytitlefont.func)
3810  .attr("text-anchor", "end")
3811  .text(label)
3812  .attr("transform", "rotate(270, 0, 0)");
3813  }
3814 
3815  var xAxisColor = this.histo['fXaxis']['fAxisColor'];
3816  var xDivLength = this.histo['fXaxis']['fTickLength'] * h;
3817  var yAxisColor = this.histo['fYaxis']['fAxisColor'];
3818  var yDivLength = this.histo['fYaxis']['fTickLength'] * w;
3819 
3820  var pthis = this;
3821 
3822  /*
3823  * Define the scales, according to the information from the pad
3824  */
3825  var xrange = this.xmax - this.xmin;
3826  if (this.histo['fXaxis']['fTimeDisplay']) {
3827  if (this.x_nticks > 8) this.x_nticks = 8;
3828 
3829  var timeformatx = JSROOT.Painter.getTimeFormat(this.histo['fXaxis']);
3830 
3831  this['timeoffsetx'] = JSROOT.Painter.getTimeOffset(this.histo['fXaxis']);
3832 
3833  var scale_xrange = this.scale_xmax - this.scale_xmin;
3834 
3835  if ((timeformatx.length == 0) || (scale_xrange < 0.1 * xrange)) {
3836  timeformatx = JSROOT.Painter.chooseTimeFormat(scale_xrange, this.x_nticks);
3837  }
3838 
3839  this['dfx'] = d3.time.format(timeformatx);
3840 
3841  x_axis = d3.svg.axis().scale(this.x).orient("bottom")
3842  .tickPadding(xAxisLabelOffset)
3843  .tickSize(-xDivLength, -xDivLength / 2, -xDivLength / 4)
3844  .tickFormat(function(d) { return pthis.dfx(new Date(pthis.timeoffsetx + d * 1000)); })
3845  .ticks(this.x_nticks);
3846  } else if (this.options.Logx) {
3847  x_axis = d3.svg.axis().scale(this.x).orient("bottom")
3848  .tickPadding(xAxisLabelOffset)
3849  .tickSize(-xDivLength, -xDivLength / 2, -xDivLength / 4)
3850  .tickFormat(function(d) {
3851  var val = parseFloat(d);
3852  var vlog = Math.abs(JSROOT.Math.log10(val));
3853  if (moreloglabelsx) {
3854  if (vlog % 1 < 0.7 || vlog % 1 > 0.9999) {
3855  if (noexpx)
3856  return val.toFixed();
3857  else
3858  return JSROOT.Painter.formatExp(val.toExponential(0));
3859  } else
3860  return null;
3861  } else {
3862  if (vlog % 1 < 0.0001 || vlog % 1 > 0.9999) {
3863  if (noexpx)
3864  return val.toFixed();
3865  else
3866  return JSROOT.Painter.formatExp(val.toExponential(0));
3867  } else
3868  return null;
3869  }
3870  });
3871  } else {
3872  x_axis = d3.svg.axis()
3873  .scale(this.x)
3874  .orient("bottom")
3875  .tickPadding(xAxisLabelOffset)
3876  .tickSize(-xDivLength, -xDivLength / 2, -xDivLength / 4)
3877  .tickFormat(function(d) {
3878  // avoid rounding problems around 0
3879  if ((Math.abs(d) < 1e-14) && (Math.abs(xrange) > 1e-5)) d = 0;
3880  return parseFloat(d.toPrecision(12)); })
3881  .ticks(this.x_nticks);
3882  }
3883 
3884  var yrange = this.ymax - this.ymin;
3885  if (this.histo['fYaxis']['fTimeDisplay']) {
3886  if (this.y_nticks > 8) this.y_nticks = 8;
3887  var timeformaty = JSROOT.Painter.getTimeFormat(this.histo['fYaxis']);
3888 
3889  this['timeoffsety'] = JSROOT.Painter
3890  .getTimeOffset(this.histo['fYaxis']);
3891 
3892  var scale_yrange = this.scale_ymax - this.scale_ymin;
3893 
3894  if ((timeformaty.length == 0) || (scale_yrange < 0.1 * yrange))
3895  timeformaty = JSROOT.Painter.chooseTimeFormat(scale_yrange, this.y_nticks);
3896 
3897  this['dfy'] = d3.time.format(timeformaty);
3898 
3899  y_axis = d3.svg.axis()
3900  .scale(this.y)
3901  .orient("left")
3902  .tickPadding(yAxisLabelOffset)
3903  .tickSize(-yDivLength, -yDivLength / 2,-yDivLength / 4)
3904  .tickFormat(function(d) { return pthis.dfy(new Date(pthis.timeoffsety + d * 1000)); })
3905  .ticks(this.y_nticks);
3906  } else if (this.options.Logy) {
3907  y_axis = d3.svg.axis()
3908  .scale(this.y)
3909  .orient("left")
3910  .tickPadding(yAxisLabelOffset)
3911  .tickSize(-yDivLength, -yDivLength / 2, -yDivLength / 4)
3912  .tickFormat(function(d) {
3913  var val = parseFloat(d);
3914  var vlog = Math.abs(JSROOT.Math.log10(val));
3915  if (moreloglabelsy) {
3916  if (vlog % 1 < 0.7 || vlog % 1 > 0.9999) {
3917  if (noexpy)
3918  return val.toFixed();
3919  else
3920  return JSROOT.Painter.formatExp(val.toExponential(0));
3921  } else
3922  return null;
3923  } else {
3924  if (vlog % 1 < 0.0001 || vlog % 1 > 0.9999) {
3925  if (noexpy)
3926  return val.toFixed();
3927  else
3928  return JSROOT.Painter.formatExp(val.toExponential(0));
3929  } else
3930  return null;
3931  }
3932  });
3933  } else {
3934  if (this.y_nticks >= 10) this.y_nticks -= 2;
3935  y_axis = d3.svg.axis()
3936  .scale(this.y)
3937  .orient("left")
3938  .tickPadding(yAxisLabelOffset)
3939  .tickSize(-yDivLength, -yDivLength / 2,-yDivLength / 4)
3940  .tickFormat(function(d) {
3941  if ((Math.abs(d) < 1e-14) && (Math.abs(yrange) > 1e-5)) d = 0;
3942  return parseFloat(d.toPrecision(12)); })
3943  .ticks(this.y_nticks);
3944  }
3945 
3946  xax_g.append("svg:g").attr("class", "xaxis").call(x_axis);
3947 
3948  // this is additional ticks, required in d3.v3
3949  if ((n2ax > 0) && !this.options.Logx) {
3950  var x_axis_sub =
3951  d3.svg.axis().scale(this.x).orient("bottom")
3952  .tickPadding(xAxisLabelOffset).innerTickSize(-xDivLength / 2)
3953  .tickFormat(function(d) { return; })
3954  .ticks(this.x.ticks(this.x_nticks).length * n2ax);
3955 
3956  xax_g.append("svg:g").attr("class", "xaxis").call(x_axis_sub);
3957  }
3958 
3959  yax_g.append("svg:g").attr("class", "yaxis").call(y_axis);
3960 
3961  // this is additional ticks, required in d3.v3
3962  if ((n2ay > 0) && !this.options.Logy) {
3963  var y_axis_sub = d3.svg.axis().scale(this.y).orient("left")
3964  .tickPadding(yAxisLabelOffset).innerTickSize(-yDivLength / 2)
3965  .tickFormat(function(d) { return; })
3966  .ticks(this.y.ticks(this.y_nticks).length * n2ay);
3967 
3968  yax_g.append("svg:g").attr("class", "yaxis").call(y_axis_sub);
3969  }
3970 
3971  xax_g.selectAll("text").call(xlabelfont.func);
3972 
3973  yax_g.selectAll("text").call(ylabelfont.func);
3974 
3975  // we will use such rect for zoom selection
3976  if (JSROOT.gStyle.Zooming) {
3977  xax_g.append("svg:rect")
3978  .attr("class", "xaxis_zoom")
3979  .attr("x", 0)
3980  .attr("y", 0)
3981  .attr("width", w)
3982  .attr("height", xlabelfont.size + 3)
3983  .style('opacity', "0");
3984 
3985  // we will use such rect for zoom selection
3986  yax_g.append("svg:rect")
3987  .attr("class", "yaxis_zoom")
3988  .attr("x",-2 * ylabelfont.size - 3)
3989  .attr("y", 0)
3990  .attr("width", 2 * ylabelfont.size + 3)
3991  .attr("height", h)
3992  .style('opacity', "0");
3993  }
3994 
3995  if ((shrink_forbidden==null) && typeof yax_g.node()['getBoundingClientRect'] == 'function') {
3996 
3997  var rect1 = yax_g.node().getBoundingClientRect();
3998  var rect2 = this.svg_pad().getBoundingClientRect();
3999  var position = rect1.left - rect2.left;
4000 
4001  var shrink = 0.;
4002 
4003  if (position < 0) {
4004  shrink = -position/w + 0.001;
4005  this.shrink_frame_left += shrink;
4006  } else
4007  if ((this.shrink_frame_left > 0) && (position/w > this.shrink_frame_left)) {
4008  shrink = -this.shrink_frame_left;
4009  this.shrink_frame_left = 0.;
4010  }
4011 
4012  if (shrink != 0) {
4013  this.svg_frame()['frame_painter'].Shrink(shrink, 0);
4014  this.svg_frame()['frame_painter'].Redraw();
4015  this.CreateXY();
4016  this.DrawAxes(true);
4017  }
4018  }
4019  }
4020 
4021  JSROOT.THistPainter.prototype.DrawTitle = function() {
4022 
4023  var painter = this.FindPainterFor(null,"title");
4024 
4025  if (painter!=null) {
4026  painter.pavetext.Clear();
4027  painter.pavetext.AddText(this.histo['fTitle']);
4028  } else {
4029 
4030  var pavetext = JSROOT.Create("TPaveText");
4031 
4032  jQuery.extend(pavetext, { fName: "title",
4033  fX1NDC: 0.2809483, fY1NDC: 0.9339831,
4034  fX2NDC: 0.7190517, fY2NDC: 0.995});
4035  pavetext.AddText(this.histo['fTitle']);
4036 
4037  painter = JSROOT.Painter.drawPaveText(this.divid, pavetext);
4038  }
4039  }
4040 
4041  JSROOT.THistPainter.prototype.ToggleStat = function() {
4042 
4043  var stat = this.FindStat();
4044 
4045  if (stat == null) {
4046 
4047  // when statbox created first time, one need to draw it
4048  stat = this.CreateStat();
4049 
4050  this.Redraw();
4051 
4052  return;
4053  }
4054 
4055  var statpainter = this.FindPainterFor(stat);
4056  if (statpainter == null) {
4057  alert("Did not found painter for existing stat??");
4058  return;
4059  }
4060 
4061  statpainter.Enabled = !statpainter.Enabled;
4062 
4063  // when stat box is drawed, it always can be draw individualy while it
4064  // should be last for colz RedrawPad is used
4065  statpainter.Redraw();
4066  }
4067 
4068  JSROOT.THistPainter.prototype.GetSelectIndex = function(axis, size, add) {
4069  // be aware - here indexs starts from 0
4070  var indx = 0;
4071  var obj = this.main_painter();
4072  if (obj == null) obj = this;
4073  var nbin = 0;
4074  if (!add) add = 0;
4075 
4076  if (axis == "x") {
4077  nbin = this.nbinsx;
4078  if (obj.zoom_xmin != obj.zoom_xmax) {
4079  if (size == "left")
4080  indx = Math.floor((obj.zoom_xmin - this.xmin) / this.binwidthx + add);
4081  else
4082  indx = Math.round((obj.zoom_xmax - this.xmin) / this.binwidthx + 0.5 + add);
4083  } else {
4084  indx = (size == "left") ? 0 : nbin;
4085  }
4086 
4087  } else
4088  if (axis == "y") {
4089  nbin = this.nbinsy;
4090  if (obj.zoom_ymin != obj.zoom_ymax) {
4091  if (size == "left")
4092  indx = Math.floor((obj.zoom_ymin - this.ymin) / this.binwidthy + add);
4093  else
4094  indx = Math.round((obj.zoom_ymax - this.ymin) / this.binwidthy + 0.5 + add);
4095  } else {
4096  indx = (size == "left") ? 0 : nbin;
4097  }
4098  }
4099 
4100  if (size == "left") {
4101  if (indx < 0) indx = 0;
4102  } else {
4103  if (indx > nbin) indx = nbin;
4104  }
4105 
4106  return indx;
4107  }
4108 
4109  JSROOT.THistPainter.prototype.FindStat = function() {
4110 
4111  if ('fFunctions' in this.histo)
4112  for ( var i in this.histo.fFunctions.arr) {
4113 
4114  var func = this.histo.fFunctions.arr[i];
4115 
4116  if (func['_typename'] == 'TPaveText' || func['_typename'] == 'TPaveStats') {
4117  return func;
4118  }
4119  }
4120 
4121  return null;
4122  }
4123 
4124  JSROOT.THistPainter.prototype.CreateStat = function() {
4125 
4126  if (!this.draw_content) return null;
4127  if (this.FindStat() != null) return null;
4128 
4129  var stats = JSROOT.Create('TPaveStats');
4130  jQuery.extend(stats, { _AutoCreated: true,
4131  fName : 'stats',
4132  fOptStat: JSROOT.gStyle.OptStat,
4133  fBorderSize : 1} );
4134  jQuery.extend(stats, JSROOT.gStyle.StatNDC);
4135  jQuery.extend(stats, JSROOT.gStyle.StatText);
4136  jQuery.extend(stats, JSROOT.gStyle.StatFill);
4137 
4138  if (this.histo['_typename'] && (this.histo['_typename'].match(/^TProfile/) || this.histo['_typename'].match(/^TH2/)))
4139  stats['fY1NDC'] = 0.67;
4140 
4141  stats.AddText(this.histo['fName']);
4142 
4143  if (!'fFunctions' in this.histo)
4144  this.histo['fFunctions'] = JSROOT.Create("TList");
4145 
4146  this.histo.fFunctions.arr.push(stats);
4147 
4148  return stats;
4149  }
4150 
4151  JSROOT.THistPainter.prototype.DrawFunctions = function() {
4152 
4153  // draw statistics box & other TPaveTexts, which are belongs to histogram
4154  // should be called once to create all painters, which are than updated separately
4155 
4156  if (!('fFunctions' in this.histo)) return;
4157  // if (this.options.Func == 0) return; // in some cases on need to disable
4158  // functions drawing
4159 
4160  var lastpainter = this;
4161 
4162  var kNotDraw = JSROOT.BIT(9); // don't draw the function (TF1) when in a TH1
4163 
4164  var EStatusBits = {
4165  kCanDelete : JSROOT.BIT(0), // if object in a list can be deleted
4166  kMustCleanup : JSROOT.BIT(3), // if object destructor must call RecursiveRemove()
4167  kObjInCanvas : JSROOT.BIT(3), // for backward compatibility only, use kMustCleanup
4168  kIsReferenced : JSROOT.BIT(4), // if object is referenced by a TRef or TRefArray
4169  kHasUUID : JSROOT.BIT(5), // if object has a TUUID (its fUniqueID=UUIDNumber)
4170  kCannotPick : JSROOT.BIT(6), // if object in a pad cannot be picked
4171  kNoContextMenu : JSROOT.BIT(8), // if object does not want context menu
4172  kInvalidObject : JSROOT.BIT(13) // if object ctor succeeded but object should not be used
4173  }
4174 
4175  for ( var i in this.histo.fFunctions.arr) {
4176 
4177  var func = this.histo.fFunctions.arr[i];
4178 
4179  var funcpainter = this.FindPainterFor(func);
4180 
4181  // no need to do something if painter for object was already done
4182  // object will be redraw automatically
4183  if (funcpainter != null) continue;
4184 
4185  if (func['_typename'] == 'TPaveText' || func['_typename'] == 'TPaveStats') {
4186  funcpainter = JSROOT.Painter.drawPaveText(this.divid, func);
4187  } else
4188 
4189  if (func['_typename'] == 'TF1') {
4190  var is_pad = this.root_pad() != null;
4191  if ((!is_pad && !func.TestBit(kNotDraw))
4192  || (is_pad && func.TestBit(EStatusBits.kObjInCanvas)))
4193  funcpainter = JSROOT.Painter.drawFunction(this.divid, func);
4194  } else
4195 
4196  if (func['_typename'] == 'TPaletteAxis') {
4197  funcpainter = JSROOT.Painter.drawPaletteAxis(this.divid, func);
4198  }
4199  }
4200  }
4201 
4202  JSROOT.THistPainter.prototype.Redraw = function() {
4203  this.CreateXY();
4204  this.DrawAxes();
4205  this.DrawGrids();
4206  this.DrawBins();
4207  if (this.create_canvas) this.DrawTitle();
4208  }
4209 
4210  JSROOT.THistPainter.prototype.Unzoom = function(dox, doy) {
4211  var obj = this.main_painter();
4212  if (!obj) obj = this;
4213 
4214  var changed = false;
4215 
4216  if (dox) {
4217  if (obj['zoom_xmin'] != obj['zoom_xmax']) changed = true;
4218  obj['zoom_xmin'] = 0;
4219  obj['zoom_xmax'] = 0;
4220  }
4221  if (doy) {
4222  if (obj['zoom_ymin'] != obj['zoom_ymax']) changed = true;
4223  obj['zoom_ymin'] = 0;
4224  obj['zoom_ymax'] = 0;
4225  }
4226 
4227  if (changed) this.RedrawPad();
4228  }
4229 
4230  JSROOT.THistPainter.prototype.Zoom = function(xmin, xmax, ymin, ymax) {
4231  var obj = this.main_painter();
4232  if (!obj) obj = this;
4233 
4234  var isany = false;
4235 
4236  if ((xmin != xmax) && (Math.abs(xmax-xmin) > obj.binwidthx*2.0)) {
4237  obj['zoom_xmin'] = xmin;
4238  obj['zoom_xmax'] = xmax;
4239  isany = true;
4240  }
4241  if ((ymin != ymax) && (Math.abs(ymax-ymin) > (('binwidthy' in obj) ? (obj.binwidthy*2.0) : Math.abs(obj.ymax-obj.ymin)*1e-6))) {
4242  obj['zoom_ymin'] = ymin;
4243  obj['zoom_ymax'] = ymax;
4244  isany = true;
4245  }
4246 
4247  if (isany) this.RedrawPad();
4248  }
4249 
4250  JSROOT.THistPainter.prototype.AddInteractive = function() {
4251 
4252  // only first painter in list allowed to add interactive functionality to the main pad
4253  if (!JSROOT.gStyle.Zooming || !this.is_main_painter()) return;
4254 
4255  // if (!this.draw_content) return;
4256 
4257  var width = Number(this.svg_frame(true).attr("width")),
4258  height = Number(this.svg_frame(true).attr("height"));
4259  var e, origin, curr = null, rect = null;
4260  var lasttouch = new Date(0);
4261 
4262  var zoom_kind = 0; // 0 - none, 1 - XY, 2 - only X, 3 - only Y, (+100 for touches)
4263 
4264  var disable_tooltip = false;
4265 
4266  // var zoom = d3.behavior.zoom().x(this.x).y(this.y);
4267 
4268  var pthis = this;
4269 
4270  function closeAllExtras() {
4271  var x = document.getElementById('root_ctx_menu');
4272  if (x) x.parentNode.removeChild(x);
4273  if (rect != null) { rect.remove(); rect = null; }
4274  zoom_kind = 0;
4275  if (disable_tooltip) {
4276  JSROOT.gStyle.Tooltip = true;
4277  disable_tooltip = false;
4278  }
4279  }
4280 
4281  function showContextMenu() {
4282 
4283  d3.event.preventDefault();
4284 
4285  // ignore context menu when touches zooming is ongoing
4286  if (zoom_kind > 100) return;
4287 
4288  // suppress any running zomming
4289  closeAllExtras();
4290 
4291  var menu = JSROOT.Painter.createmenu(d3.event, 'root_ctx_menu');
4292 
4293  menu['painter'] = pthis;
4294 
4295  JSROOT.Painter.menuitem(menu, pthis.histo['fName']);
4296  JSROOT.Painter.menuitem(menu, "----------------");
4297 
4298  pthis.FillContextMenu(menu);
4299  }
4300 
4301  function startTouchSel() {
4302 
4303  // in case when zooming was started, block any other kind of events
4304  if (zoom_kind != 0) {
4305  d3.event.preventDefault();
4306  d3.event.stopPropagation();
4307  return;
4308  }
4309 
4310  // update frame dimensions while frame could be resized
4311  width = Number(pthis.svg_frame(true).attr("width"));
4312  height = Number(pthis.svg_frame(true).attr("height"));
4313 
4314  e = this;
4315  // var t = d3.event.changedTouches;
4316  var arr = d3.touches(e);
4317 
4318  // only double-touch will be handled
4319  if (arr.length == 1) {
4320 
4321  var now = new Date();
4322  var diff = now.getTime() - lasttouch.getTime();
4323 
4324  if ((diff < 300) && (curr != null)
4325  && (Math.abs(curr[0] - arr[0][0]) < 30)
4326  && (Math.abs(curr[1] - arr[0][1]) < 30)) {
4327 
4328  d3.event.preventDefault();
4329  d3.event.stopPropagation();
4330 
4331  closeAllExtras();
4332  pthis.Unzoom(true, true);
4333  } else {
4334  lasttouch = now;
4335  curr = arr[0];
4336  }
4337  }
4338 
4339  if (arr.length != 2) return;
4340 
4341  d3.event.preventDefault();
4342 
4343  closeAllExtras();
4344 
4345  var pnt1 = arr[0];
4346  var pnt2 = arr[1];
4347 
4348  curr = new Array; // minimum
4349  origin = new Array; // maximum
4350 
4351  curr.push(Math.min(pnt1[0], pnt2[0]));
4352  curr.push(Math.min(pnt1[1], pnt2[1]));
4353  origin.push(Math.max(pnt1[0], pnt2[0]));
4354  origin.push(Math.max(pnt1[1], pnt2[1]));
4355 
4356  if (curr[0] < 0) {
4357  zoom_kind = 103; // only y
4358  curr[0] = 0;
4359  origin[0] = width;
4360  } else if (origin[1] > height) {
4361  zoom_kind = 102; // only x
4362  curr[1] = 0;
4363  origin[1] = height;
4364  } else {
4365  zoom_kind = 101; // x and y
4366  }
4367 
4368  // d3.select("body").classed("noselect", true);
4369  // d3.select("body").style("-webkit-user-select", "none");
4370 
4371  rect = pthis.svg_frame(true).append("rect")
4372  .attr("class", "zoom")
4373  .attr("id", "zoomRect")
4374  .attr("x", curr[0])
4375  .attr("y", curr[1])
4376  .attr("width", origin[0] - curr[0])
4377  .attr("height", origin[1] - curr[1]);
4378 
4379  // pthis.svg_frame(true).on("dblclick", unZoom);
4380 
4381  d3.select(window).on("touchmove.zoomRect", moveTouchSel)
4382  .on("touchcancel.zoomRect", endTouchSel)
4383  .on("touchend.zoomRect", endTouchSel, true);
4384  d3.event.stopPropagation();
4385  }
4386 
4387  function moveTouchSel() {
4388  if (zoom_kind < 100) return;
4389 
4390  d3.event.preventDefault();
4391 
4392  // var t = d3.event.changedTouches;
4393  var arr = d3.touches(e);
4394 
4395  if (arr.length != 2) {
4396  closeAllExtras();
4397  zoom_kind = 0;
4398  return;
4399  }
4400 
4401  var pnt1 = arr[0];
4402  var pnt2 = arr[1];
4403 
4404  if (zoom_kind != 103) {
4405  curr[0] = Math.min(pnt1[0], pnt2[0]);
4406  origin[0] = Math.max(pnt1[0], pnt2[0]);
4407  }
4408  if (zoom_kind != 102) {
4409  curr[1] = Math.min(pnt1[1], pnt2[1]);
4410  origin[1] = Math.max(pnt1[1], pnt2[1]);
4411  }
4412 
4413  rect.attr("x", curr[0])
4414  .attr("y", curr[1])
4415  .attr("width", origin[0] - curr[0])
4416  .attr("height", origin[1] - curr[1]);
4417 
4418  if (JSROOT.gStyle.Tooltip && ((origin[0] - curr[0]>10) || (origin[1] - curr[1]>10))) {
4419  JSROOT.gStyle.Tooltip = false;
4420  disable_tooltip = true;
4421  }
4422 
4423  d3.event.stopPropagation();
4424  }
4425 
4426  function endTouchSel() {
4427 
4428  if (zoom_kind < 100) return;
4429 
4430  d3.event.preventDefault();
4431  d3.select(window).on("touchmove.zoomRect", null)
4432  .on("touchend.zoomRect", null)
4433  .on("touchcancel.zoomRect", null);
4434  d3.select("body").classed("noselect", false);
4435 
4436  var xmin = 0, xmax = 0, ymin = 0, ymax = 0;
4437 
4438  var isany = false;
4439 
4440  if ((zoom_kind != 103) && (Math.abs(curr[0] - origin[0]) > 10)) {
4441  xmin = Math.min(pthis.x.invert(origin[0]), pthis.x.invert(curr[0]));
4442  xmax = Math.max(pthis.x.invert(origin[0]), pthis.x.invert(curr[0]));
4443  isany = true;
4444  }
4445 
4446  if ((zoom_kind != 102) && (Math.abs(curr[1] - origin[1]) > 10)) {
4447  ymin = Math.min(pthis.y.invert(origin[1]), pthis.y.invert(curr[1]));
4448  ymax = Math.max(pthis.y.invert(origin[1]), pthis.y.invert(curr[1]));
4449  isany = true;
4450  }
4451 
4452  d3.select("body").style("-webkit-user-select", "auto");
4453 
4454  if (disable_tooltip)
4455  JSROOT.gStyle.Tooltip = true;
4456 
4457  rect.remove();
4458  rect = null;
4459  zoom_kind = 0;
4460 
4461  if (isany) pthis.Zoom(xmin, xmax, ymin, ymax);
4462 
4463  d3.event.stopPropagation();
4464  }
4465 
4466  function detectLeftButton(event) {
4467  if ('buttons' in event) return event.buttons === 1;
4468  else if ('which' in event) return event.which === 1;
4469  else return event.button === 1;
4470  }
4471 
4472  function startRectSel() {
4473 
4474  // use only left button
4475  // if (!detectLeftButton(d3.event)) return;
4476 
4477  // ignore when touch selection is actiavated
4478  if (zoom_kind > 100) return;
4479 
4480  d3.event.preventDefault();
4481 
4482  // update frame dimensions while frame could be resized
4483  width = Number(pthis.svg_frame(true).attr("width"));
4484  height = Number(pthis.svg_frame(true).attr("height"));
4485 
4486  closeAllExtras();
4487 
4488  e = this;
4489  origin = d3.mouse(e);
4490 
4491  curr = new Array;
4492  curr.push(Math.max(0, Math.min(width, origin[0])));
4493  curr.push(Math.max(0, Math.min(height, origin[1])));
4494 
4495  if (origin[0] < 0) {
4496  zoom_kind = 3; // only y
4497  origin[0] = 0;
4498  origin[1] = curr[1];
4499  curr[0] = width;
4500  curr[1] += 1;
4501  } else if (origin[1] > height) {
4502  zoom_kind = 2; // only x
4503  origin[0] = curr[0];
4504  origin[1] = 0;
4505  curr[0] += 1;
4506  curr[1] = height;
4507  } else {
4508  zoom_kind = 1; // x and y
4509  origin[0] = curr[0];
4510  origin[1] = curr[1];
4511  }
4512 
4513  // d3.select("body").classed("noselect", true);
4514  // d3.select("body").style("-webkit-user-select", "none");
4515 
4516  rect = pthis.svg_frame(true)
4517  .append("rect")
4518  .attr("class", "zoom")
4519  .attr("id", "zoomRect");
4520 
4521  pthis.svg_frame(true).on("dblclick", unZoom);
4522 
4523  d3.select(window).on("mousemove.zoomRect", moveRectSel)
4524  .on("mouseup.zoomRect", endRectSel, true);
4525 
4526  d3.event.stopPropagation();
4527  }
4528 
4529  function unZoom() {
4530  d3.event.preventDefault();
4531  var m = d3.mouse(e);
4532  closeAllExtras();
4533  if (m[0] < 0) pthis.Unzoom(false, true); else
4534  if (m[1] > height) pthis.Unzoom(true, false); else {
4535  pthis.Unzoom(true, true);
4536  pthis.svg_frame(true).on("dblclick", null);
4537  }
4538  }
4539 
4540  function moveRectSel() {
4541 
4542  if ((zoom_kind == 0) || (zoom_kind > 100)) return;
4543 
4544  d3.event.preventDefault();
4545  var m = d3.mouse(e);
4546 
4547  m[0] = Math.max(0, Math.min(width, m[0]));
4548  m[1] = Math.max(0, Math.min(height, m[1]));
4549 
4550  switch (zoom_kind) {
4551  case 1: curr[0] = m[0]; curr[1] = m[1]; break;
4552  case 2: curr[0] = m[0]; break;
4553  case 3: curr[1] = m[1]; break;
4554  }
4555 
4556  rect.attr("x", Math.min(origin[0], curr[0]))
4557  .attr("y", Math.min(origin[1], curr[1]))
4558  .attr("width", Math.abs(curr[0] - origin[0]))
4559  .attr("height", Math.abs(curr[1] - origin[1]));
4560 
4561  if (JSROOT.gStyle.Tooltip && ((Math.abs(curr[0] - origin[0])>10) || (Math.abs(curr[1] - origin[1])>10))) {
4562  JSROOT.gStyle.Tooltip = false;
4563  disable_tooltip = true;
4564  }
4565  }
4566 
4567  function endRectSel() {
4568  if ((zoom_kind == 0) || (zoom_kind > 100)) return;
4569 
4570  d3.event.preventDefault();
4571  // d3.select(window).on("touchmove.zoomRect",
4572  // null).on("touchend.zoomRect", null);
4573  d3.select(window).on("mousemove.zoomRect", null)
4574  .on("mouseup.zoomRect", null);
4575  d3.select("body").classed("noselect", false);
4576 
4577  var m = d3.mouse(e);
4578 
4579  m[0] = Math.max(0, Math.min(width, m[0]));
4580  m[1] = Math.max(0, Math.min(height, m[1]));
4581 
4582  switch (zoom_kind) {
4583  case 1: curr[0] = m[0]; curr[1] = m[1]; break;
4584  case 2: curr[0] = m[0]; break; // only X
4585  case 3: curr[1] = m[1]; break; // only Y
4586  }
4587 
4588  var xmin = 0, xmax = 0, ymin = 0, ymax = 0;
4589 
4590  var isany = false;
4591 
4592  if ((zoom_kind != 3) && (Math.abs(curr[0] - origin[0]) > 10)) {
4593  xmin = Math.min(pthis.x.invert(origin[0]), pthis.x.invert(curr[0]));
4594  xmax = Math.max(pthis.x.invert(origin[0]), pthis.x.invert(curr[0]));
4595  isany = true;
4596  }
4597 
4598  if ((zoom_kind != 2) && (Math.abs(curr[1] - origin[1]) > 10)) {
4599  ymin = Math.min(pthis.y.invert(origin[1]), pthis.y.invert(curr[1]));
4600  ymax = Math.max(pthis.y.invert(origin[1]), pthis.y.invert(curr[1]));
4601  isany = true;
4602  }
4603 
4604  d3.select("body").style("-webkit-user-select", "auto");
4605 
4606  if (disable_tooltip) {
4607  JSROOT.gStyle.Tooltip = true;
4608  disable_tooltip = false;
4609  }
4610 
4611  rect.remove();
4612  rect = null;
4613  zoom_kind = 0;
4614 
4615  if (isany) pthis.Zoom(xmin, xmax, ymin, ymax);
4616  }
4617 
4618  this.svg_frame(true).on("mousedown", startRectSel);
4619  this.svg_frame(true).on("touchstart", startTouchSel);
4620  this.svg_frame(true).on("contextmenu", showContextMenu);
4621 
4622  }
4623 
4624  JSROOT.THistPainter.prototype.FillContextMenu = function(menu) {
4625  JSROOT.Painter.menuitem(menu, "Unzoom X", function() {
4626  menu['painter'].Unzoom(true, false);
4627  });
4628  JSROOT.Painter.menuitem(menu, "Unzoom Y", function() {
4629  menu['painter'].Unzoom(false, true);
4630  });
4631  JSROOT.Painter.menuitem(menu, "Unzoom", function() {
4632  menu['painter'].Unzoom(true, true);
4633  });
4634 
4635  JSROOT.Painter.menuitem(menu, JSROOT.gStyle.Tooltip ? "Disable tooltip" : "Enable tooltip", function() {
4636  JSROOT.gStyle.Tooltip = !JSROOT.gStyle.Tooltip;
4637  menu['painter'].RedrawPad();
4638  });
4639 
4640  if (this.options) {
4641 
4642  var item = this.options.Logx > 0 ? "Linear X" : "Log X";
4643 
4644  JSROOT.Painter.menuitem(menu, item, function() {
4645  menu['painter'].options.Logx = 1 - menu['painter'].options.Logx;
4646  menu['painter'].RedrawPad();
4647  });
4648 
4649  var item = this.options.Logy > 0 ? "Linear Y" : "Log Y";
4650  JSROOT.Painter.menuitem(menu, item, function() {
4651  menu['painter'].options.Logy = 1 - menu['painter'].options.Logy;
4652  menu['painter'].RedrawPad();
4653  });
4654  }
4655  if (this.draw_content)
4656  JSROOT.Painter.menuitem(menu, "Toggle stat", function() {
4657  menu['painter'].ToggleStat();
4658  });
4659  }
4660 
4661  // ======= TH1 painter================================================
4662 
4663  JSROOT.TH1Painter = function(histo) {
4664  JSROOT.THistPainter.call(this, histo);
4665  }
4666 
4667  JSROOT.TH1Painter.prototype = Object.create(JSROOT.THistPainter.prototype);
4668 
4669  JSROOT.TH1Painter.prototype.ScanContent = function() {
4670 
4671  // from here we analyze object content
4672  // therefore code will be moved
4673  this.fill = this.createAttFill(this.histo);
4674  if (this.fill.color == 'white') this.fill.color = 'none';
4675 
4676  this.attline = JSROOT.Painter.createAttLine(this.histo);
4677 
4678  var hmin = 0, hmin_nz = 0, hmax = 0, hsum = 0;
4679 
4680  var profile = this.IsTProfile();
4681 
4682  this.nbinsx = this.histo['fXaxis']['fNbins'];
4683 
4684  for (var i = 0; i < this.nbinsx; ++i) {
4685  var value = this.histo.getBinContent(i + 1);
4686  hsum += profile ? this.histo.fBinEntries[i + 1] : value;
4687  if (i == 0) hmin = hmax = value;
4688  if (value < hmin) hmin = value; else
4689  if (value > hmax) hmax = value;
4690  if (value > 0)
4691  if ((hmin_nz == 0) || (value<hmin_nz)) hmin_nz = value;
4692  }
4693 
4694  // account overflow/underflow bins
4695  if (profile)
4696  hsum += this.histo.fBinEntries[0] + this.histo.fBinEntries[this.nbinsx + 1];
4697  else
4698  hsum += this.histo.getBinContent(0) + this.histo.getBinContent(this.nbinsx + 1);
4699 
4700  this.stat_entries = hsum;
4701 
4702  // used in CreateXY and tooltip providing
4703  this.xmin = this.histo['fXaxis']['fXmin'];
4704  this.xmax = this.histo['fXaxis']['fXmax'];
4705 
4706  this.binwidthx = (this.xmax - this.xmin);
4707  if (this.nbinsx > 0)
4708  this.binwidthx = this.binwidthx / this.nbinsx;
4709 
4710  this.ymin = this.histo['fYaxis']['fXmin'];
4711  this.ymax = this.histo['fYaxis']['fXmax'];
4712  this.ymin_nz = hmin_nz; // value can be used to show optimal log scale
4713 
4714  if ((this.nbinsx == 0) || ((Math.abs(hmin) < 1e-300 && Math.abs(hmax) < 1e-300))) {
4715  if (this.histo['fMinimum'] != -1111) this.ymin = this.histo['fMinimum'];
4716  if (this.histo['fMaximum'] != -1111) this.ymax = this.histo['fMaximum'];
4717  this.draw_content = false;
4718  } else {
4719  if (this.histo['fMinimum'] != -1111) hmin = this.histo['fMinimum'];
4720  if (this.histo['fMaximum'] != -1111) hmax = this.histo['fMaximum'];
4721  if (hmin >= hmax) {
4722  if (hmin == 0) { this.ymax = 0; this.ymax = 1; } else
4723  if (hmin < 0) { this.ymin = 2 * hmin; this.ymax = 0; }
4724  else { this.ymin = 0; this.ymax = hmin * 2; }
4725  } else {
4726  var dy = (hmax - hmin) * 0.1;
4727  this.ymin = hmin - dy;
4728  if ((this.ymin < 0) && (hmin >= 0)) this.ymin = 0;
4729  this.ymax = hmax + dy;
4730  }
4731  this.draw_content = true;
4732  }
4733 
4734  // If no any draw options specified, do not try draw histogram
4735  if (this.options.Bar == 0 && this.options.Hist == 0
4736  && this.options.Error == 0 && this.options.Same == 0) {
4737  this.draw_content = false;
4738  }
4739  if (this.options.Axis > 0) { // Paint histogram axis only
4740  this.draw_content = false;
4741  }
4742  }
4743 
4744  JSROOT.TH1Painter.prototype.CountStat = function(cond) {
4745  var profile = this.IsTProfile();
4746 
4747  var stat_sumw = 0, stat_sumwx = 0, stat_sumwx2 = 0, stat_sumwy = 0, stat_sumwy2 = 0;
4748 
4749  var left = this.GetSelectIndex("x", "left");
4750  var right = this.GetSelectIndex("x", "right");
4751 
4752  var xx = 0, w = 0, xmax = null, wmax = null;
4753 
4754  for (var i = left; i < right; i++) {
4755  xx = this.xmin + (i + 0.5) * this.binwidthx;
4756 
4757  if ((cond!=null) && !cond(xx)) continue;
4758 
4759  if (profile) {
4760  w = this.histo.fBinEntries[i + 1];
4761  stat_sumwy += this.histo.fArray[i + 1];
4762  stat_sumwy2 += this.histo.fSumw2[i + 1];
4763  } else {
4764  w = this.histo.getBinContent(i + 1);
4765  }
4766 
4767  if ((xmax==null) || (w>wmax)) { xmax = xx; wmax = w; }
4768 
4769  stat_sumw += w;
4770  stat_sumwx += w * xx;
4771  stat_sumwx2 += w * xx * xx;
4772  }
4773 
4774  var res = { meanx: 0, meany: 0, rmsx: 0, rmsy: 0, integral: stat_sumw, entries: this.stat_entries, xmax:0, wmax:0 };
4775 
4776  if (stat_sumw > 0) {
4777  res.meanx = stat_sumwx / stat_sumw;
4778  res.meany = stat_sumwy / stat_sumw;
4779  res.rmsx = Math.sqrt(stat_sumwx2 / stat_sumw - res.meanx * res.meanx);
4780  res.rmsy = Math.sqrt(stat_sumwy2 / stat_sumw - res.meany * res.meany);
4781  }
4782 
4783  if (xmax!=null) {
4784  res.xmax = xmax;
4785  res.wmax = wmax;
4786  }
4787 
4788  return res;
4789  }
4790 
4791  JSROOT.TH1Painter.prototype.FillStatistic = function(stat, dostat) {
4792  if (!this.histo) return false;
4793 
4794  var data = this.CountStat();
4795 
4796  var print_name = Math.floor(dostat % 10);
4797  var print_entries = Math.floor(dostat / 10) % 10;
4798  var print_mean = Math.floor(dostat / 100) % 10;
4799  var print_rms = Math.floor(dostat / 1000) % 10;
4800  var print_under = Math.floor(dostat / 10000) % 10;
4801  var print_over = Math.floor(dostat / 100000) % 10;
4802  var print_integral = Math.floor(dostat / 1000000) % 10;
4803  var print_skew = Math.floor(dostat / 10000000) % 10;
4804  var print_kurt = Math.floor(dostat / 100000000) % 10;
4805 
4806  if (print_name > 0)
4807  stat.AddLine(this.histo['fName']);
4808 
4809  if (this.IsTProfile()) {
4810 
4811  if (print_entries > 0)
4812  stat.AddLine("Entries = " + JSROOT.gStyle.StatEntriesFormat(data.entries));
4813 
4814  if (print_mean > 0) {
4815  stat.AddLine("Mean = " + JSROOT.gStyle.StatFormat(data.meanx));
4816  stat.AddLine("Mean y = " + JSROOT.gStyle.StatFormat(data.meany));
4817  }
4818 
4819  if (print_rms > 0) {
4820  stat.AddLine("RMS = " + JSROOT.gStyle.StatFormat(data.rmsx));
4821  stat.AddLine("RMS y = " + JSROOT.gStyle.StatFormat(data.rmsy));
4822  }
4823 
4824  } else {
4825 
4826  if (print_entries > 0)
4827  stat.AddLine("Entries = " + JSROOT.gStyle.StatEntriesFormat(data.entries));
4828 
4829  if (print_mean > 0) {
4830  stat.AddLine("Mean = " + JSROOT.gStyle.StatFormat(data.meanx));
4831  }
4832 
4833  if (print_rms > 0) {
4834  stat.AddLine("RMS = " + JSROOT.gStyle.StatFormat(data.rmsx));
4835  }
4836 
4837  if (print_under > 0) {
4838  var res = 0;
4839  if (this.histo['fArray'].length > 0)
4840  res = this.histo['fArray'][0];
4841  stat.AddLine("Underflow = " + JSROOT.gStyle.StatFormat(res));
4842  }
4843 
4844  if (print_over > 0) {
4845  var res = 0;
4846  if (this.histo['fArray'].length > 0)
4847  res = this.histo['fArray'][this.histo['fArray'].length - 1];
4848  stat.AddLine("Overflow = " + JSROOT.gStyle.StatFormat(res));
4849  }
4850 
4851  if (print_integral > 0) {
4852  stat.AddLine("Integral = " + JSROOT.gStyle.StatEntriesFormat(data.integral));
4853  }
4854 
4855  if (print_skew > 0)
4856  stat.AddLine("Skew = not avail");
4857 
4858  if (print_kurt > 0)
4859  stat.AddLine("Kurt = not avail");
4860  }
4861 
4862  // adjust the size of the stats box with the number of lines
4863  var nlines = stat.pavetext['fLines'].arr.length;
4864  var stath = nlines * JSROOT.gStyle.StatFontSize;
4865  if (stath <= 0 || 3 == (JSROOT.gStyle.StatFont % 10)) {
4866  stath = 0.25 * nlines * JSROOT.gStyle.StatH;
4867  stat.pavetext['fY1NDC'] = 0.93 - stath;
4868  stat.pavetext['fY2NDC'] = 0.93;
4869  }
4870 
4871  return true;
4872  }
4873 
4874  JSROOT.TH1Painter.prototype.CreateDrawBins = function(width, height, exclude_zeros) {
4875  // method is called directly before bins must be drawn
4876 
4877  var left = this.GetSelectIndex("x", "left", -1);
4878  var right = this.GetSelectIndex("x", "right", 2);
4879 
4880  var draw_bins = new Array;
4881 
4882  var can_optimize = ((JSROOT.gStyle.OptimizeDraw > 0) && (right-left > 5000)) ||
4883  ((JSROOT.gStyle.OptimizeDraw > 1) && (right-left > 2*width));
4884 
4885  var x1, x2 = this.xmin + left * this.binwidthx;
4886  var grx1 = -1111, grx2 = -1111, gry;
4887 
4888  var point = null;
4889  var searchmax = false;
4890 
4891  for (var i = left; i < right; i++) {
4892  // if interval wider than specified range, make it shorter
4893  x1 = x2;
4894  x2 += this.binwidthx;
4895 
4896  if (this.options.Logx && (x1 <= 0)) continue;
4897 
4898  grx1 = grx2;
4899  grx2 = this.x(x2);
4900  if (grx1 < 0) grx1 = this.x(x1);
4901 
4902  var pmax = i, cont = this.histo.getBinContent(i + 1);
4903 
4904  if (can_optimize) {
4905  searchmax = !searchmax;
4906 
4907  // consider all points which are not far than 0.5 pixel away
4908  while ((i+1<right) && (this.x(x2 + this.binwidthx) < grx2 + 0.5)) {
4909  i++; x2 += this.binwidthx;
4910  var ccc = this.histo.getBinContent(i + 1);
4911  if (searchmax ? ccc>cont : ccc<cont) {
4912  cont = ccc;
4913  pmax = i;
4914  }
4915  }
4916  grx2 = this.x(x2);
4917  }
4918 
4919  // exclude zero bins from profile drawings
4920  if (exclude_zeros && (cont==0)) continue;
4921 
4922  if (this.options.Logy && (cont < this.scale_ymin))
4923  gry = height + 10;
4924  else
4925  gry = this.y(cont);
4926 
4927  point = { x : grx1, y : gry };
4928 
4929  if (this.options.Error > 0) {
4930  point['xerr'] = (grx2 - grx1) / 2;
4931  point['yerr'] = gry - this.y(cont + this.histo.getBinError(pmax + 1));
4932  }
4933 
4934  if (this.options.Error > 0) {
4935  point['x'] = (grx1 + grx2) / 2;
4936  point['tip'] = "x = " + this.AxisAsText("x", (x1 + x2)/2 ) + "\n" +
4937  "y = " + this.AxisAsText("y", cont) + "\n" +
4938  "error x = " + ((x2 - x1) / 2).toPrecision(4) + "\n" +
4939  "error y = " + this.histo.getBinError(pmax + 1).toPrecision(4);
4940  } else {
4941  point['width'] = grx2 - grx1;
4942 
4943  point['tip'] = "bin = " + (pmax + 1) + "\n" +
4944  "x = [" + this.AxisAsText("x", x1) + ", " + this.AxisAsText("x", x2) + "]\n" +
4945  "entries = " + cont;
4946  }
4947 
4948  draw_bins.push(point);
4949  }
4950 
4951  // if we need to draw line or area, we need extra point for correct drawing
4952  if ((right == this.nbinsx) && (this.options.Error == 0) && (point!=null)) {
4953  var extrapoint = jQuery.extend(true, {}, point);
4954  extrapoint.x = grx2;
4955  draw_bins.push(extrapoint);
4956  }
4957 
4958  return draw_bins;
4959  }
4960 
4961  JSROOT.TH1Painter.prototype.DrawAsMarkers = function(draw_bins, w, h) {
4962 
4963  /* Add a panel for each data point */
4964  var draw_bins = this.CreateDrawBins(w, h, this.IsTProfile() || (this.Mark==10));
4965 
4966  // here are up to five elements are collected, try to group them
4967  var nodes = this.draw_g.selectAll("g")
4968  .data(draw_bins)
4969  .enter()
4970  .append("svg:g")
4971  .attr("transform", function(d) { return "translate(" + d.x.toFixed(1) + "," + d.y.toFixed(1) + ")";});
4972 
4973  if (JSROOT.gStyle.Tooltip)
4974  nodes.append("svg:title").text(function(d) { return d.tip; });
4975 
4976  var xerr = null, yerr = null;
4977 
4978  /* Draw x-error indicators */
4979  if (this.options.Error > 0)
4980  nodes.append("svg:line")
4981  .attr("x1", function(d) { return (-d.xerr).toFixed(1); })
4982  .attr("y1", 0)
4983  .attr("x2", function(d) { return d.xerr.toFixed(1); })
4984  .attr("y2", 0)
4985  .call(this.attline.func);
4986 
4987  if (this.options.Error == 11) {
4988  nodes.append("svg:line")
4989  .attr("y1", -3)
4990  .attr("x1", function(d) { return (-d.xerr).toFixed(1); })
4991  .attr("y2", 3)
4992  .attr("x2", function(d) { return (-d.xerr).toFixed(1); })
4993  .call(this.attline.func);
4994  nodes.append("svg:line")
4995  .attr("y1", -3)
4996  .attr("x1", function(d) { return d.xerr.toFixed(1); })
4997  .attr("y2", 3)
4998  .attr("x2", function(d) { return d.xerr.toFixed(1); })
4999  .call(this.attline.func);
5000  }
5001 
5002  /* Draw y-error indicators */
5003  if (this.options.Error > 0)
5004  nodes.append("svg:line")
5005  .attr("x1", 0)
5006  .attr("y1", function(d) { return (-d.yerr).toFixed(1); })
5007  .attr("x2", 0)
5008  .attr("y2", function(d) { return d.yerr.toFixed(1); })
5009  .call(this.attline.func);
5010 
5011  if (this.options.Error == 11) {
5012  nodes.append("svg:line")
5013  .attr("x1", -3)
5014  .attr("y1", function(d) { return (-d.yerr).toFixed(1); })
5015  .attr("x2", 3)
5016  .attr("y2", function(d) { return (-d.yerr).toFixed(1); })
5017  .call(this.attline.func);
5018  nodes.append("svg:line")
5019  .attr("x1", -3)
5020  .attr("y1", function(d) { return d.yerr.toFixed(1); })
5021  .attr("x2", 3)
5022  .attr("y2", function(d) { return d.yerr.toFixed(1); })
5023  .call(this.attline.func);
5024  }
5025 
5026  // draw dot markers only when no error was drawn
5027  if ((this.histo['fMarkerStyle'] == 1) && (this.options.Error > 0)) return;
5028 
5029  var marker = JSROOT.Painter.createAttMarker(this.histo);
5030 
5031  nodes.append("svg:path").call(marker.func);
5032  }
5033 
5034  JSROOT.TH1Painter.prototype.DrawBins = function() {
5035 
5036  var width = Number(this.svg_frame(true).attr("width")),
5037  height = Number(this.svg_frame(true).attr("height"));
5038 
5039  if (!this.draw_content || (width<=0) || (height<=0)) {
5040  this.RemoveDrawG();
5041  return;
5042  }
5043 
5044  this.RecreateDrawG();
5045 
5046  if (this.IsTProfile() || (this.options.Error > 0) || (this.options.Mark > 0))
5047  return this.DrawAsMarkers(width, height);
5048 
5049  var draw_bins = this.CreateDrawBins(width, height);
5050 
5051  if (this.fill.color != 'none') {
5052 
5053  // histogram filling
5054  var area = d3.svg.area()
5055  .x(function(d) { return d.x.toFixed(1); })
5056  .y0(function(d) { return d.y.toFixed(1); })
5057  .y1(function(d) { return height; })
5058  .interpolate("step-after");
5059 
5060  this.draw_g.append("svg:path")
5061  .attr("d", area(draw_bins))
5062  .call(this.attline.func)
5063  .call(this.fill.func);
5064  } else {
5065 
5066  var line = d3.svg.line()
5067  .x(function(d) { return d.x.toFixed(1); })
5068  .y(function(d) { return d.y.toFixed(1); })
5069  .interpolate("step-after");
5070 
5071  this.draw_g
5072  .append("svg:path")
5073  .attr("d", line(draw_bins))
5074  .call(this.attline.func)
5075  .style("fill", "none");
5076  }
5077 
5078  if (JSROOT.gStyle.Tooltip) {
5079  // TODO: limit number of tooltips by number of visible pixels
5080  this.draw_g.selectAll("selections")
5081  .data(draw_bins).enter()
5082  .append("svg:line")
5083  .attr("x1", function(d) { return d.x + d.width / 2; })
5084  .attr("y1", function(d) { return Math.max(0, d.y); })
5085  .attr("x2", function(d) { return d.x + d.width / 2; })
5086  .attr("y2", function(d) { return height; })
5087  .style("opacity", 0)
5088  .style("stroke", "#4572A7")
5089  .style("stroke-width", function(d) { return d.width; })
5090  .on('mouseover', function() {
5091  if (JSROOT.gStyle.Tooltip && (d3.select(this).style("opacity")=="0"))
5092  d3.select(this).transition().duration(100).style("opacity", "0.3");
5093  })
5094  .on('mouseout', function() {
5095  d3.select(this).transition().duration(100).style("opacity", "0");
5096  })
5097  .append("svg:title").text(function(d) { return d.tip; });
5098  }
5099  }
5100 
5101  JSROOT.TH1Painter.prototype.FillContextMenu = function(menu) {
5102  JSROOT.THistPainter.prototype.FillContextMenu.call(this, menu);
5103  if (this.draw_content)
5104  JSROOT.Painter.menuitem(menu, "Auto zoom-in", function() { menu['painter'].AutoZoom(); });
5105  }
5106 
5107  JSROOT.TH1Painter.prototype.AutoZoom = function() {
5108  var left = this.GetSelectIndex("x", "left", -1);
5109  var right = this.GetSelectIndex("x", "right", 1);
5110 
5111  var dist = (right - left);
5112 
5113  if (dist == 0)
5114  return;
5115 
5116  var min = this.histo.getBinContent(left + 1);
5117 
5118  // first find minimum
5119  for (var indx = left; indx < right; indx++)
5120  if (this.histo.getBinContent(indx + 1) < min)
5121  min = this.histo.getBinContent(indx + 1);
5122 
5123  while ((left < right) && (this.histo.getBinContent(left + 1) <= min)) left++;
5124  while ((left < right) && (this.histo.getBinContent(right) <= min)) right--;
5125 
5126  if ((right - left < dist) && (left < right))
5127  this.Zoom(this.xmin + left * this.binwidthx, this.xmin + right * this.binwidthx, 0, 0);
5128  }
5129 
5130  JSROOT.Painter.drawHistogram1D = function(divid, histo, opt) {
5131 
5132  // create painter and add it to canvas
5133  var painter = new JSROOT.TH1Painter(histo);
5134  painter.SetDivId(divid, 1);
5135 
5136  // here we deciding how histogram will look like and how will be shown
5137  painter.options = painter.DecodeOptions(opt);
5138 
5139  painter.CheckPadOptions();
5140 
5141  painter.ScanContent();
5142 
5143  painter.CreateXY();
5144 
5145  painter.DrawAxes();
5146 
5147  painter.DrawGrids();
5148 
5149  painter.DrawBins();
5150 
5151  if (painter.create_canvas) painter.DrawTitle();
5152 
5153  if (JSROOT.gStyle.AutoStat && painter.create_canvas) painter.CreateStat();
5154 
5155  painter.DrawFunctions();
5156 
5157  painter.AddInteractive();
5158 
5159  return painter;
5160  }
5161 
5162  // ==================== painter for TH2 histograms ==============================
5163 
5164  JSROOT.TH2Painter = function(histo) {
5165  JSROOT.THistPainter.call(this, histo);
5166  this.paletteColors = [];
5167  }
5168 
5169  JSROOT.TH2Painter.prototype = Object.create(JSROOT.THistPainter.prototype);
5170 
5171  JSROOT.TH2Painter.prototype.FillContextMenu = function(menu) {
5172  JSROOT.THistPainter.prototype.FillContextMenu.call(this, menu);
5173  JSROOT.Painter.menuitem(menu, "Auto zoom-in", function() { menu['painter'].AutoZoom(); });
5174  JSROOT.Painter.menuitem(menu, "Draw in 3D", function() { menu['painter'].Draw3D(); });
5175  JSROOT.Painter.menuitem(menu, "Toggle col", function() {
5176  if (menu['painter'].options.Color == 0)
5177  menu['painter'].options.Color = JSROOT.gStyle.DefaultCol;
5178  else
5179  menu['painter'].options.Color = -1 * menu['painter'].options.Color;
5180  menu['painter'].RedrawPad();
5181  });
5182 
5183  if (this.options.Color > 0)
5184  JSROOT.Painter.menuitem(menu, "Toggle colz", function() { menu['painter'].ToggleColz(); });
5185  }
5186 
5187  JSROOT.TH2Painter.prototype.FindPalette = function(remove) {
5188  if ('fFunctions' in this.histo)
5189  for ( var i in this.histo.fFunctions.arr) {
5190  var func = this.histo.fFunctions.arr[i];
5191  if (func['_typename'] != 'TPaletteAxis')
5192  continue;
5193  if (remove) {
5194  this.histo.fFunctions.arr.splice(i, 1);
5195  return null;
5196  }
5197 
5198  return func;
5199  }
5200 
5201  return null;
5202  }
5203 
5204  JSROOT.TH2Painter.prototype.ToggleColz = function() {
5205  if (this.FindPalette() == null) {
5206  var shrink = this.CreatePalette(0.04);
5207  this.svg_frame()['frame_painter'].Shrink(0, shrink);
5208  this.options.Zscale = 1;
5209  // one should draw palette
5210  JSROOT.Painter.drawPaletteAxis(this.divid, this.FindPalette());
5211  } else {
5212  if (this.options.Zscale > 0)
5213  this.options.Zscale = 0;
5214  else
5215  this.options.Zscale = 1;
5216  }
5217 
5218  this.RedrawPad();
5219  }
5220 
5221  JSROOT.TH2Painter.prototype.AutoZoom = function() {
5222  var i1 = this.GetSelectIndex("x", "left", -1);
5223  var i2 = this.GetSelectIndex("x", "right", 1);
5224  var j1 = this.GetSelectIndex("y", "left", -1);
5225  var j2 = this.GetSelectIndex("y", "right", 1);
5226 
5227  if ((i1 == i2) || (j1 == j2)) return;
5228 
5229  var min = this.histo.getBinContent(i1 + 1, j1 + 1);
5230 
5231  // first find minimum
5232  for (var i = i1; i < i2; i++)
5233  for (var j = j1; j < j2; j++)
5234  if (this.histo.getBinContent(i + 1, j + 1) < min)
5235  min = this.histo.getBinContent(i + 1, j + 1);
5236 
5237  var ileft = i2, iright = i1, jleft = j2, jright = j1;
5238 
5239  for (var i = i1; i < i2; i++)
5240  for (var j = j1; j < j2; j++)
5241  if (this.histo.getBinContent(i + 1, j + 1) > min) {
5242  if (i < ileft) ileft = i;
5243  if (i >= iright) iright = i + 1;
5244  if (j < jleft) jleft = j;
5245  if (j >= jright) jright = j + 1;
5246  }
5247 
5248  var xmin = 0, xmax = 0, ymin = 0, ymax = 0;
5249 
5250  if ((ileft > i1 || iright < i2) && (ileft < iright - 1)) {
5251  xmin = this.xmin + ileft * this.binwidthx;
5252  xmax = this.xmin + iright * this.binwidthx;
5253  }
5254 
5255  if ((jleft > j1 || jright < j2) && (jleft < jright - 1)) {
5256  ymin = this.ymin + jleft * this.binwidthy;
5257  ymax = this.ymin + jright * this.binwidthy;
5258  }
5259 
5260  this.Zoom(xmin, xmax, ymin, ymax);
5261  }
5262 
5263  JSROOT.TH2Painter.prototype.CreatePalette = function(rel_width) {
5264  if (this.FindPalette() != null) return 0.;
5265 
5266  if (!rel_width || rel_width <= 0) rel_width = 0.04;
5267 
5268  var pal = {};
5269  pal['_typename'] = 'TPaletteAxis';
5270  pal['fName'] = 'palette';
5271 
5272  pal['_AutoCreated'] = true;
5273 
5274  pal['fX1NDC'] = this.svg_frame()['NDC'].x2 - rel_width;
5275  pal['fY1NDC'] = this.svg_frame()['NDC'].y1;
5276  pal['fX2NDC'] = this.svg_frame()['NDC'].x2;
5277  pal['fY2NDC'] = this.svg_frame()['NDC'].y2;
5278  pal['fInit'] = 1;
5279  pal['fShadowColor'] = 1;
5280  pal['fCorenerRadius'] = 0;
5281  pal['fResizing'] = false;
5282  pal['fBorderSize'] = 4;
5283  pal['fName'] = "TPave";
5284  pal['fOption'] = "br";
5285  pal['fLineColor'] = 1;
5286  pal['fLineSyle'] = 1;
5287  pal['fLineWidth'] = 1;
5288  pal['fFillColor'] = 1;
5289  pal['fFillSyle'] = 1;
5290 
5291  var axis = {};
5292 
5293  axis['_typename'] = 'TGaxis';
5294  axis['fTickSize'] = 0.03;
5295  axis['fLabelOffset'] = 0.005;
5296  axis['fLabelSize'] = 0.035;
5297  axis['fTitleOffset'] = 1;
5298  axis['fTitleSize'] = 0.035;
5299  axis['fNdiv'] = 8;
5300  axis['fLabelColor'] = 1;
5301  axis['fLabelFont'] = 42;
5302  axis['fChopt'] = "";
5303  axis['fName'] = "";
5304  axis['fTitle'] = "";
5305  axis['fTimeFormat'] = "";
5306  axis['fFunctionName'] = "";
5307  axis['fWmin'] = 0;
5308  axis['fWmax'] = 100;
5309  axis['fLineColor'] = 1;
5310  axis['fLineSyle'] = 1;
5311  axis['fLineWidth'] = 1;
5312  axis['fTextAngle'] = 0;
5313  axis['fTextSize'] = 0.04;
5314  axis['fTextAlign'] = 11;
5315  axis['fTextColor'] = 1;
5316  axis['fTextFont'] = 42;
5317 
5318  pal['fAxis'] = axis;
5319 
5320  if (!'fFunctions' in this.histo)
5321  this.histo['fFunctions'] = JSROOT.Create("TList");
5322 
5323  // place colz in the beginning, that stat box is always drawn on the top
5324  this.histo.fFunctions.arr.unshift(pal);
5325 
5326  // and at the end try to check how much place will be used by the labels
5327  // in the palette
5328 
5329  var width = Number(this.svg_frame(true).attr("width")),
5330  height = Number(this.svg_frame(true).attr("height"));
5331 
5332  var axisOffset = Math.round(axis['fLabelOffset'] * width);
5333  var tickSize = Math.round(axis['fTickSize'] * width);
5334  var axisfont = JSROOT.Painter.getFontDetails(axis['fLabelFont'], axis['fLabelSize'] * height);
5335 
5336  var ticks = d3.scale.linear().clamp(true)
5337  .domain([ this.minbin, this.maxbin ])
5338  .range([ height, 0 ]).nice().ticks(axis['fNdiv'] % 100);
5339 
5340  var maxlen = 0;
5341  for (var i in ticks) {
5342  var len = axisfont.stringWidth(this.svg_frame(true), ticks[i]);
5343  if (len > maxlen) maxlen = len;
5344  }
5345 
5346  var rel = (maxlen + axisOffset) / width;
5347 
5348  if (pal['fX2NDC'] + rel > 0.98) {
5349  var shift = pal['fX2NDC'] + rel - 0.98;
5350 
5351  pal['fX1NDC'] -= shift;
5352  pal['fX2NDC'] -= shift;
5353  rel_width += shift;
5354  }
5355 
5356  return rel_width + 0.01;
5357  }
5358 
5359  JSROOT.TH2Painter.prototype.ScanContent = function() {
5360  this.fillcolor = JSROOT.Painter.root_colors[this.histo['fFillColor']];
5361  // if (this.histo['fFillColor'] == 0) this.fillcolor = '#4572A7'; // why?
5362 
5363  this.attline = JSROOT.Painter.createAttLine(this.histo);
5364  if (this.attline.color == 'none') this.attline.color = '#4572A7';
5365 
5366  this.nbinsx = this.histo['fXaxis']['fNbins'];
5367  this.nbinsy = this.histo['fYaxis']['fNbins'];
5368 
5369  // used in CreateXY method
5370  this.xmin = this.histo['fXaxis']['fXmin'];
5371  this.xmax = this.histo['fXaxis']['fXmax'];
5372  this.ymin = this.histo['fYaxis']['fXmin'];
5373  this.ymax = this.histo['fYaxis']['fXmax'];
5374 
5375  this.binwidthx = (this.xmax - this.xmin);
5376  if (this.nbinsx > 0)
5377  this.binwidthx = this.binwidthx / this.nbinsx;
5378 
5379  this.binwidthy = (this.ymax - this.ymin);
5380  if (this.nbinsy > 0)
5381  this.binwidthy = this.binwidthy / this.nbinsy
5382 
5383  this.gmaxbin = this.histo.getBinContent(1, 1);
5384  this.gminbin = this.gmaxbin; // global min/max, used at the moment in 3D drawing
5385  for (var i = 0; i < this.nbinsx; ++i) {
5386  for (var j = 0; j < this.nbinsy; ++j) {
5387  var bin_content = this.histo.getBinContent(i + 1, j + 1);
5388  if (bin_content < this.gminbin) this.gminbin = bin_content; else
5389  if (bin_content > this.gmaxbin) this.gmaxbin = bin_content;
5390  }
5391  }
5392 
5393  // used to enable/disable stat box
5394  this.draw_content = this.gmaxbin > 0;
5395  }
5396 
5397  JSROOT.TH2Painter.prototype.CountStat = function(cond) {
5398  var stat_sum0 = 0, stat_sumx1 = 0, stat_sumy1 = 0, stat_sumx2 = 0, stat_sumy2 = 0, stat_sumxy2 = 0;
5399 
5400  var res = { entries: 0, integral: 0, meanx: 0, meany: 0, rmsx: 0, rmsy: 0, matrix : [], xmax: 0, ymax:0, wmax: null };
5401  for (var n = 0; n < 9; n++) res.matrix.push(0);
5402 
5403  var xleft = this.GetSelectIndex("x", "left");
5404  var xright = this.GetSelectIndex("x", "right");
5405 
5406  var yleft = this.GetSelectIndex("y", "left");
5407  var yright = this.GetSelectIndex("y", "right");
5408 
5409  for (var xi = 0; xi <= this.nbinsx + 1; xi++) {
5410  var xside = (xi <= xleft) ? 0 : (xi > xright ? 2 : 1);
5411  var xx = this.xmin + (xi - 0.5) * this.binwidthx;
5412 
5413  for (var yi = 0; yi <= this.nbinsx + 1; yi++) {
5414  var yside = (yi <= yleft) ? 0 : (yi > yright ? 2 : 1);
5415  var yy = this.ymin + (yi - 0.5) * this.binwidthy;
5416 
5417  var zz = this.histo.getBinContent(xi, yi);
5418 
5419  res.entries += zz;
5420 
5421  res.matrix[yside * 3 + xside] += zz;
5422 
5423  if ((xside != 1) || (yside != 1)) continue;
5424 
5425  if ((cond!=null) && !cond(xx,yy)) continue;
5426 
5427  if ((res.wmax==null) || (zz>res.wmax)) { res.wmax = zz; res.xmax = xx; res.ymax = yy; }
5428 
5429  stat_sum0 += zz;
5430  stat_sumx1 += xx * zz;
5431  stat_sumy1 += yy * zz;
5432  stat_sumx2 += xx * xx * zz;
5433  stat_sumy2 += yy * yy * zz;
5434  stat_sumxy2 += xx * yy * zz;
5435  }
5436  }
5437 
5438  if (stat_sum0 > 0) {
5439  res.meanx = stat_sumx1 / stat_sum0;
5440  res.meany = stat_sumy1 / stat_sum0;
5441  res.rmsx = Math.sqrt(stat_sumx2 / stat_sum0 - res.meanx * res.meanx);
5442  res.rmsy = Math.sqrt(stat_sumy2 / stat_sum0 - res.meany * res.meany);
5443  }
5444 
5445  if (res.wmax==null) res.wmax = 0;
5446  res.integral = stat_sum0;
5447 
5448  return res;
5449  }
5450 
5451  JSROOT.TH2Painter.prototype.FillStatistic = function(stat, dostat) {
5452  if (!this.histo) return false;
5453 
5454  var data = this.CountStat();
5455 
5456  var print_name = Math.floor(dostat % 10);
5457  var print_entries = Math.floor(dostat / 10) % 10;
5458  var print_mean = Math.floor(dostat / 100) % 10;
5459  var print_rms = Math.floor(dostat / 1000) % 10;
5460  var print_under = Math.floor(dostat / 10000) % 10;
5461  var print_over = Math.floor(dostat / 100000) % 10;
5462  var print_integral = Math.floor(dostat / 1000000) % 10;
5463  var print_skew = Math.floor(dostat / 10000000) % 10;
5464  var print_kurt = Math.floor(dostat / 100000000) % 10;
5465 
5466  if (print_name > 0)
5467  stat.AddLine(this.histo['fName']);
5468 
5469  if (print_entries > 0)
5470  stat.AddLine("Entries = " + JSROOT.gStyle.StatEntriesFormat(data.entries));
5471 
5472  if (print_mean > 0) {
5473  stat.AddLine("Mean x = " + JSROOT.gStyle.StatFormat(data.meanx));
5474  stat.AddLine("Mean y = " + JSROOT.gStyle.StatFormat(data.meany));
5475  }
5476 
5477  if (print_rms > 0) {
5478  stat.AddLine("RMS x = " + JSROOT.gStyle.StatFormat(data.rmsx));
5479  stat.AddLine("RMS y = " + JSROOT.gStyle.StatFormat(data.rmsy));
5480  }
5481 
5482  if (print_integral > 0) {
5483  stat.AddLine("Integral = " + JSROOT.gStyle.StatEntriesFormat(data.matrix[4]));
5484  }
5485 
5486  if (print_skew > 0) {
5487  stat.AddLine("Skewness x = <undef>");
5488  stat.AddLine("Skewness y = <undef>");
5489  }
5490 
5491  if (print_kurt > 0)
5492  stat.AddLine("Kurt = <undef>");
5493 
5494  if ((print_under > 0) || (print_over > 0)) {
5495  var m = data.matrix;
5496 
5497  stat.AddLine("" + m[6].toFixed(0) + " | " + m[7].toFixed(0) + " | " + m[7].toFixed(0));
5498  stat.AddLine("" + m[3].toFixed(0) + " | " + m[4].toFixed(0) + " | " + m[5].toFixed(0));
5499  stat.AddLine("" + m[0].toFixed(0) + " | " + m[1].toFixed(0) + " | " + m[2].toFixed(0));
5500  }
5501 
5502  // adjust the size of the stats box wrt the number of lines
5503  var nlines = stat.pavetext['fLines'].arr.length;
5504  var stath = nlines * JSROOT.gStyle.StatFontSize;
5505  if (stath <= 0 || 3 == (JSROOT.gStyle.StatFont % 10)) {
5506  stath = 0.25 * nlines * JSROOT.gStyle.StatH;
5507  stat.pavetext['fY1NDC'] = 0.93 - stath;
5508  stat.pavetext['fY2NDC'] = 0.93;
5509  }
5510  return true;
5511  }
5512 
5513  JSROOT.TH2Painter.prototype.getValueColor = function(zc) {
5514  var wmin = this.minbin, wmax = this.maxbin;
5515  var wlmin = wmin, wlmax = wmax;
5516  var ndivz = this.histo['fContour'].length;
5517  if (ndivz < 16) ndivz = 16;
5518  var scale = ndivz / (wlmax - wlmin);
5519  if (this.options.Logz) {
5520  if (wmin <= 0 && wmax > 0)
5521  wmin = Math.min(1.0, 0.001 * wmax);
5522  wlmin = Math.log(wmin) / Math.log(10);
5523  wlmax = Math.log(wmax) / Math.log(10);
5524  }
5525 
5526  if (this.paletteColors.length == 0) {
5527  var saturation = 1, lightness = 0.5, maxHue = 280, minHue = 0, maxPretty = 50;
5528  for (var i = 0; i < maxPretty; i++) {
5529  var hue = (maxHue - (i + 1) * ((maxHue - minHue) / maxPretty)) / 360.0;
5530  var rgbval = JSROOT.Painter.HLStoRGB(hue, lightness, saturation);
5531  this.paletteColors.push(rgbval);
5532  }
5533  }
5534  if (this.options.Logz) zc = Math.log(zc) / Math.log(10);
5535  if (zc < wlmin) zc = wlmin;
5536  var ncolors = this.paletteColors.length;
5537  var color = Math.round(0.01 + (zc - wlmin) * scale);
5538  var theColor = Math.round((color + 0.99) * ncolors / ndivz) - 1;
5539  var icol = theColor % ncolors;
5540  if (icol < 0) icol = 0;
5541 
5542  return this.paletteColors[icol];
5543  }
5544 
5545  JSROOT.TH2Painter.prototype.CreateDrawBins = function(w, h, coordinates_kind, tipkind) {
5546  var i1 = this.GetSelectIndex("x", "left", 0);
5547  var i2 = this.GetSelectIndex("x", "right", 0);
5548  var j1 = this.GetSelectIndex("y", "left", 0);
5549  var j2 = this.GetSelectIndex("y", "right", 0);
5550 
5551  var x1, y1, x2, y2, grx1, gry1, grx2, gry2, fillcol, shrx, shry, binz, point, wx ,wy;
5552 
5553  // first found min/max values in selected range
5554  this.maxbin = this.minbin = this.histo.getBinContent(i1 + 1, j1 + 1);
5555  for (var i = i1; i < i2; i++) {
5556  for (var j = j1; j < j2; j++) {
5557  binz = this.histo.getBinContent(i + 1, j + 1);
5558  if (binz>this.maxbin) this.maxbin = binz; else
5559  if (binz<this.minbin) this.minbin = binz;
5560  }
5561  }
5562 
5563  var xfactor = 1, yfactor = 1;
5564  if (coordinates_kind == 1) {
5565  xfactor = 0.5 * w / (i2 - i1) / (this.maxbin - this.minbin);
5566  yfactor = 0.5 * h / (j2 - j1) / (this.maxbin - this.minbin);
5567  }
5568 
5569  var local_bins = new Array;
5570 
5571  x2 = this.xmin + i1 * this.binwidthx;
5572  grx2 = -11111;
5573  for (var i = i1; i < i2; i++) {
5574  x1 = x2;
5575  x2 += this.binwidthx;
5576 
5577  if (this.options.Logx && (x1 <= 0)) continue;
5578 
5579  grx1 = grx2;
5580  if (grx1 < 0) grx1 = this.x(x1);
5581  grx2 = this.x(x2);
5582 
5583  y2 = this.ymin + j1 * this.binwidthy;
5584  gry2 = -1111;
5585  for (var j = j1; j < j2; j++) {
5586  y1 = y2;
5587  y2 += this.binwidthy;
5588  if (this.options.Logy && (y1 <= 0)) continue;
5589  gry1 = gry2;
5590  if (gry1 < 0) gry1 = this.y(y1);
5591  gry2 = this.y(y2);
5592  binz = this.histo.getBinContent(i + 1, j + 1);
5593  if ((binz == 0) || (binz < this.minbin)) continue;
5594 
5595  switch (coordinates_kind) {
5596  case 0:
5597  point = {
5598  x : grx1,
5599  y : gry2,
5600  width : grx2 - grx1 + 1, // +1 to fill gaps between colored bins
5601  height : gry1 - gry2 + 1,
5602  stroke : "none",
5603  fill : this.getValueColor(binz)
5604  }
5605  point['tipcolor'] = (point['fill'] == "black") ? "grey" : "black";
5606  break;
5607 
5608  case 1:
5609  shrx = xfactor * (this.maxbin - binz);
5610  shry = yfactor * (this.maxbin - binz);
5611  point = {
5612  x : grx1 + shrx,
5613  y : gry2 + shry,
5614  width : grx2 - grx1 - 2 * shrx,
5615  height : gry1 - gry2 - 2 * shry,
5616  stroke : this.attline.color,
5617  fill : this.fillcolor
5618  }
5619  point['tipcolor'] = (point['fill'] == "black") ? "grey" : "black";
5620  break;
5621 
5622  case 2:
5623  point = {
5624  x : (x1 + x2) / 2,
5625  y : (y1 + y2) / 2,
5626  z : binz
5627  }
5628  break;
5629  }
5630 
5631  if (tipkind == 1)
5632  point['tip'] = "x = [" + this.AxisAsText("x", x1) + ", " + this.AxisAsText("x", x2) + "]\n" +
5633  "y = [" + this.AxisAsText("y", y1) + ", " + this.AxisAsText("y", y2) + "]\n" +
5634  "entries = " + binz;
5635  else if (tipkind == 2)
5636  point['tip'] = "x = " + this.AxisAsText("x", x1) + "\n" +
5637  "y = " + this.AxisAsText("y", y1) + "\n" +
5638  "entries = " + binz;
5639 
5640  local_bins.push(point);
5641  }
5642  }
5643 
5644  return local_bins;
5645  }
5646 
5647  JSROOT.TH2Painter.prototype.DrawSimpleCanvas = function(w,h) {
5648 
5649  var i1 = this.GetSelectIndex("x", "left", 0);
5650  var i2 = this.GetSelectIndex("x", "right", 0);
5651  var j1 = this.GetSelectIndex("y", "left", 0);
5652  var j2 = this.GetSelectIndex("y", "right", 0);
5653 
5654  this.maxbin = this.minbin = this.histo.getBinContent(i1 + 1, j1 + 1);
5655  for (var i = i1; i < i2; i++) {
5656  for (var j = j1; j < j2; j++) {
5657  binz = this.histo.getBinContent(i + 1, j + 1);
5658  if (binz>this.maxbin) this.maxbin = binz; else
5659  if (binz<this.minbin) this.minbin = binz;
5660  }
5661  }
5662 
5663  var dx = i2-i1, dy = j2-j1;
5664 
5665  var canvas =
5666  this.draw_g.append("foreignObject")
5667  .attr("width", w)
5668  .attr("height", h)
5669  .append("xhtml:canvas")
5670  .attr("width", dx)
5671  .attr("height", dy)
5672  .attr("style", "width: " + w + "px; height: "+ h + "px");
5673 
5674  var context = canvas.node().getContext("2d");
5675  var image = context.createImageData(dx, dy);
5676 
5677  var p = -1;
5678 
5679  for (var j = j2-1; j >= j1; j--) {
5680  for (var i = i1; i < i2; i++) {
5681  var bin = this.histo.getBinContent(i + 1, j + 1);
5682  var col = bin>this.minbin ? this.getValueColor(bin) : 'white';
5683  var c = d3.rgb(col);
5684  image.data[++p] = c.r;
5685  image.data[++p] = c.g;
5686  image.data[++p] = c.b;
5687  image.data[++p] = 255;
5688  }
5689  }
5690 
5691  context.putImageData(image, 0, 0);
5692  }
5693 
5694  JSROOT.TH2Painter.prototype.DrawNormalCanvas = function(w,h) {
5695 
5696  var local_bins = this.CreateDrawBins(w, h, 0, 0);
5697 
5698  var foreignObject = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject' );
5699  // var body = document.createElement( 'body' );
5700 
5701  var canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
5702 
5703  $(canvas).attr('width', w).attr('height',h)
5704 
5705 // $(body).append(canvas);
5706 
5707  $(foreignObject).attr("width", w).attr("height", h).append(canvas);
5708 
5709  $(this.draw_g.node()).append(foreignObject);
5710 
5711  var ctx = canvas.getContext("2d");
5712 
5713 /*
5714  var canvas =
5715  this.draw_g.append("foreignObject")
5716  .attr("width", w)
5717  .attr("height", h)
5718  .append("xhtml:canvas")
5719  .attr("width", w)
5720  .attr("height", h)
5721  .attr("style", "width: " + w + "px; height: "+ h + "px");
5722  var ctx = canvas.node().getContext("2d");
5723 */
5724  for (var i in local_bins) {
5725  var bin = local_bins[i];
5726  ctx.fillStyle = bin.fill;
5727  ctx.fillRect(bin.x,bin.y,bin.width,bin.height);
5728  }
5729 
5730  ctx.stroke();
5731  }
5732 
5733 
5734  JSROOT.TH2Painter.prototype.DrawBins = function() {
5735 
5736  this.RecreateDrawG();
5737 
5738  var w = Number(this.svg_frame(true).attr("width")),
5739  h = Number(this.svg_frame(true).attr("height"));
5740 
5741  if (this.options.Color==2)
5742  return this.DrawSimpleCanvas(w,h);
5743 
5744  if (this.options.Color==3)
5745  return this.DrawNormalCanvas(w,h);
5746 
5747 
5748  // this.options.Scat =1;
5749  // this.histo['fMarkerStyle'] = 2;
5750 
5751  var draw_markers = (this.options.Scat > 0 && this.histo['fMarkerStyle'] > 1);
5752  var normal_coordinates = (this.options.Color > 0) || draw_markers;
5753 
5754  var tipkind = 0;
5755  if (JSROOT.gStyle.Tooltip) tipkind = draw_markers ? 2 : 1;
5756 
5757  var local_bins = this.CreateDrawBins(w, h, normal_coordinates ? 0 : 1, tipkind);
5758 
5759  if (draw_markers) {
5760  // Add markers
5761  var marker = JSROOT.Painter.createAttMarker(this.histo);
5762 
5763  var markers =
5764  this.draw_g.selectAll(".marker")
5765  .data(local_bins)
5766  .enter().append("svg:path")
5767  .attr("class", "marker")
5768  .attr("transform", function(d) { return "translate(" + d.x.toFixed(1) + "," + d.y.toFixed(1) + ")" })
5769  .call(marker.func);
5770 
5771  if (JSROOT.gStyle.Tooltip)
5772  markers.append("svg:title").text(function(d) { return d.tip; });
5773  } else {
5774  var drawn_bins = this.draw_g.selectAll(".bins")
5775  .data(local_bins).enter()
5776  .append("svg:rect")
5777  .attr("class", "bins")
5778  .attr("x", function(d) { return d.x.toFixed(1); })
5779  .attr("y", function(d) { return d.y.toFixed(1); })
5780  .attr("width", function(d) { return d.width.toFixed(1); })
5781  .attr("height", function(d) { return d.height.toFixed(1); })
5782  .style("stroke", function(d) { return d.stroke; })
5783  .style("fill", function(d) {
5784  this['f0'] = d.fill;
5785  this['f1'] = d.tipcolor;
5786  return d.fill;
5787  });
5788 
5789  if (JSROOT.gStyle.Tooltip)
5790  drawn_bins
5791  .on('mouseover', function() {
5792  if (JSROOT.gStyle.Tooltip)
5793  d3.select(this).transition().duration(100).style("fill", this['f1']);
5794  })
5795  .on('mouseout', function() {
5796  d3.select(this).transition().duration(100).style("fill", this['f0']);
5797  })
5798  .append("svg:title").text(function(d) { return d.tip; });
5799  }
5800 
5801  delete local_bins;
5802  }
5803 
5804  JSROOT.TH2Painter.prototype.Draw2D = function() {
5805 
5806  if (this.options.Lego>0) this.options.Lego = 0;
5807 
5808  if (this['done2d']) return;
5809 
5810  // check if we need to create palette
5811  if ((this.FindPalette() == null) && this.create_canvas && (this.options.Zscale > 0)) {
5812  // create pallette
5813  var shrink = this.CreatePalette(0.04);
5814  this.svg_frame()['frame_painter'].Shrink(0, shrink);
5815  this.svg_frame()['frame_painter'].Redraw();
5816  this.CreateXY();
5817  } else if (this.options.Zscale == 0) {
5818  // delete palette - it may appear there due to previous draw options
5819  this.FindPalette(true);
5820  }
5821 
5822  // check if we need to create statbox
5823  if (JSROOT.gStyle.AutoStat && this.create_canvas)
5824  this.CreateStat();
5825 
5826  this.DrawAxes();
5827 
5828  this.DrawGrids();
5829 
5830  this.DrawBins();
5831 
5832  if (this.create_canvas) this.DrawTitle();
5833 
5834  this.DrawFunctions();
5835 
5836  this.AddInteractive();
5837 
5838  this['done2d'] = true; // indicate that 2d drawing was once done
5839  }
5840 
5841  JSROOT.TH2Painter.prototype.Draw3D = function() {
5842 
5843  if (this.options.Lego<=0) this.options.Lego = 1;
5844  var painter = this;
5845 
5846  JSROOT.AssertPrerequisites('3d', function() {
5847  JSROOT.Painter.real_drawHistogram2D(painter);
5848  });
5849  }
5850 
5851  JSROOT.Painter.drawHistogram2D = function(divid, histo, opt) {
5852 
5853  // create painter and add it to canvas
5854  var painter = new JSROOT.TH2Painter(histo);
5855 
5856  painter.SetDivId(divid, 1);
5857 
5858  // here we deciding how histogram will look like and how will be shown
5859  painter.options = painter.DecodeOptions(opt);
5860 
5861  painter.CheckPadOptions();
5862 
5863  painter.ScanContent();
5864 
5865  painter.CreateXY();
5866 
5867  if (painter.options.Lego > 0)
5868  painter.Draw3D();
5869  else
5870  painter.Draw2D();
5871 
5872  return painter;
5873  }
5874 
5875  JSROOT.Painter.drawHistogram3D = function(divid, obj, opt) {
5876  JSROOT.AssertPrerequisites('3d', function() {
5877  JSROOT.Painter.real_drawHistogram3D(divid, obj, opt);
5878  });
5879  }
5880 
5881  // ====================================================================
5882 
5883  JSROOT.THStackPainter = function(stack) {
5884  JSROOT.TObjectPainter.call(this, stack);
5885  this.stack = stack;
5886  this.nostack = false;
5887  this.firstpainter = null;
5888  this.painters = new Array; // keep painters to be able update objects
5889  }
5890 
5891  JSROOT.THStackPainter.prototype = Object.create(JSROOT.TObjectPainter.prototype);
5892 
5893  JSROOT.THStackPainter.prototype.GetObject = function() {
5894  return this.stack;
5895  }
5896 
5897  JSROOT.THStackPainter.prototype.drawStack = function(opt) {
5898 
5899  var pad = this.root_pad();
5900  var histos = this.stack['fHists'];
5901  var nhists = histos.arr.length;
5902 
5903  if (opt == null) opt = "";
5904  else opt = opt.toLowerCase();
5905  var lsame = false;
5906  if (opt.indexOf("same") != -1) {
5907  lsame = true;
5908  opt.replace("same", "");
5909  }
5910  // compute the min/max of each axis
5911  var i, h;
5912  var xmin = 0, xmax = 0, ymin = 0, ymax = 0;
5913  for (var i = 0; i < nhists; ++i) {
5914  h = histos.arr[i];
5915  if (i == 0 || h['fXaxis']['fXmin'] < xmin)
5916  xmin = h['fXaxis']['fXmin'];
5917  if (i == 0 || h['fXaxis']['fXmax'] > xmax)
5918  xmax = h['fXaxis']['fXmax'];
5919  if (i == 0 || h['fYaxis']['fXmin'] < ymin)
5920  ymin = h['fYaxis']['fXmin'];
5921  if (i == 0 || h['fYaxis']['fXmax'] > ymax)
5922  ymax = h['fYaxis']['fXmax'];
5923  }
5924  this.nostack = opt.indexOf("nostack") == -1 ? false : true;
5925  if (!this.nostack)
5926  this.stack.buildStack();
5927 
5928  var themin, themax;
5929  if (this.stack['fMaximum'] == -1111) themax = this.stack.getMaximum(opt);
5930  else themax = this.stack['fMaximum'];
5931  if (this.stack['fMinimum'] == -1111) {
5932  themin = this.stack.getMinimum(opt);
5933  if (pad && pad['fLogy']) {
5934  if (themin > 0)
5935  themin *= .9;
5936  else
5937  themin = themax * 1.e-3;
5938  } else if (themin > 0)
5939  themin = 0;
5940  } else
5941  themin = this.stack['fMinimum'];
5942  if (!('fHistogram' in this.stack)) {
5943  h = this.stack['fHists'].arr[0];
5944  this.stack['fHistogram'] = JSROOT.Create("TH1I");
5945  this.stack['fHistogram']['fName'] = "unnamed";
5946  this.stack['fHistogram']['fXaxis'] = JSROOT.clone(h['fXaxis']);
5947  this.stack['fHistogram']['fYaxis'] = JSROOT.clone(h['fYaxis']);
5948  this.stack['fHistogram']['fXaxis']['fXmin'] = xmin;
5949  this.stack['fHistogram']['fXaxis']['fXmax'] = xmax;
5950  this.stack['fHistogram']['fYaxis']['fXmin'] = ymin;
5951  this.stack['fHistogram']['fYaxis']['fXmax'] = ymax;
5952  }
5953  this.stack['fHistogram']['fTitle'] = this.stack['fTitle'];
5954  // var histo = JSROOT.clone(stack['fHistogram']);
5955  var histo = this.stack['fHistogram'];
5956  if (!histo.TestBit(JSROOT.TH1StatusBits.kIsZoomed)) {
5957  if (this.nostack && this.stack['fMaximum'] != -1111)
5958  histo['fMaximum'] = this.stack['fMaximum'];
5959  else {
5960  if (pad && pad['fLogy'])
5961  histo['fMaximum'] = themax * (1 + 0.2 * JSROOT.Math.log10(themax / themin));
5962  else
5963  histo['fMaximum'] = 1.05 * themax;
5964  }
5965  if (this.nostack && this.stack['fMinimum'] != -1111)
5966  histo['fMinimum'] = this.stack['fMinimum'];
5967  else {
5968  if (pad && pad['fLogy'])
5969  histo['fMinimum'] = themin / (1 + 0.5 * JSROOT.Math.log10(themax / themin));
5970  else
5971  histo['fMinimum'] = themin;
5972  }
5973  }
5974  if (!lsame) {
5975 
5976  var hopt = histo['fOption'];
5977  if ((opt != "") && (hopt.indexOf(opt) == -1))
5978  hopt += opt;
5979 
5980  if (histo['_typename'].match(/^TH1/))
5981  this.firstpainter = JSROOT.Painter.drawHistogram1D(this.divid, histo, hopt);
5982  else
5983  if (histo['_typename'].match(/^TH2/))
5984  this.firstpainter = JSROOT.Painter.drawHistogram2D(this.divid, histo, hopt);
5985 
5986  }
5987  for (var i = 0; i < nhists; ++i) {
5988  if (this.nostack)
5989  h = histos.arr[i];
5990  else
5991  h = this.stack['fStack'].arr[nhists - i - 1];
5992 
5993  var hopt = h['fOption'];
5994  if ((opt != "") && (hopt.indexOf(opt) == -1)) hopt += opt;
5995  hopt += "same";
5996 
5997  if (h['_typename'].match(/^TH1/)) {
5998  var subpainter = JSROOT.Painter.drawHistogram1D(this.divid, h, hopt);
5999  this.painters.push(subpainter);
6000  }
6001  }
6002  }
6003 
6004  JSROOT.THStackPainter.prototype.UpdateObject = function(obj) {
6005  var isany = false;
6006  if (this.firstpainter)
6007  if (this.firstpainter.UpdateObject(obj['fHistogram'])) isany = true;
6008 
6009  var histos = obj['fHists'];
6010  var nhists = histos.arr.length;
6011 
6012  for (var i = 0; i < nhists; ++i) {
6013  var h = null;
6014 
6015  if (this.nostack)
6016  h = histos.arr[i];
6017  else
6018  h = obj['fStack'].arr[nhists - i - 1];
6019 
6020  if (this.painters[i].UpdateObject(h)) isany = true;
6021  }
6022 
6023  return isany;
6024  }
6025 
6026 
6027  JSROOT.Painter.drawHStack = function(divid, stack, opt) {
6028  // paint the list of histograms
6029  // By default, histograms are shown stacked.
6030  // -the first histogram is paint
6031  // -then the sum of the first and second, etc
6032 
6033  if (!'fHists' in stack) return;
6034  if (stack['fHists'].arr.length == 0) return;
6035 
6036  var painter = new JSROOT.THStackPainter(stack);
6037  painter.SetDivId(divid);
6038 
6039  painter.drawStack(opt);
6040 
6041  return painter
6042  }
6043 
6044  // ==============================================================================
6045 
6046  JSROOT.TLegendPainter = function(legend) {
6047  JSROOT.TObjectPainter.call(this, legend);
6048  this.legend = legend;
6049  }
6050 
6051  JSROOT.TLegendPainter.prototype = Object.create(JSROOT.TObjectPainter.prototype);
6052 
6053  JSROOT.TLegendPainter.prototype.GetObject = function() {
6054  return this.legend;
6055  }
6056 
6057  JSROOT.TLegendPainter.prototype.drawLegend = function() {
6058  this.RecreateDrawG(true, ".text_layer");
6059 
6060  var svg = this.svg_pad(true);
6061  var pave = this.legend;
6062 
6063  var x = 0, y = 0, w = 0, h = 0;
6064  if (pave['fInit'] == 0) {
6065  x = pave['fX1'] * Number(svg.attr("width"));
6066  y = Number(svg.attr("height")) - pave['fY1']
6067  * Number(svg.attr("height"));
6068  w = (pave['fX2'] - pave['fX1']) * Number(svg.attr("width"));
6069  h = (pave['fY2'] - pave['fY1']) * Number(svg.attr("height"));
6070  } else {
6071  x = pave['fX1NDC'] * Number(svg.attr("width"));
6072  y = Number(svg.attr("height")) - pave['fY1NDC']
6073  * Number(svg.attr("height"));
6074  w = (pave['fX2NDC'] - pave['fX1NDC']) * Number(svg.attr("width"));
6075  h = (pave['fY2NDC'] - pave['fY1NDC']) * Number(svg.attr("height"));
6076  }
6077  y -= h;
6078  var lwidth = pave['fBorderSize'] ? pave['fBorderSize'] : 0;
6079  var fill = this.createAttFill(pave);
6080  var lcolor = JSROOT.Painter.createAttLine(pave, lwidth);
6081 
6082  var p = this.draw_g
6083  .attr("x", x)
6084  .attr("y", y)
6085  .attr("width", w)
6086  .attr("height", h)
6087  .attr("transform", "translate(" + x + "," + y + ")");
6088 
6089  p.append("svg:rect")
6090  .attr("x", 0)
6091  .attr("y", 0)
6092  .attr("width", w)
6093  .attr("height", h)
6094  .call(fill.func)
6095  .style("stroke-width", lwidth ? 1 : 0)
6096  .style("stroke", lcolor.color);
6097 
6098  var tcolor = JSROOT.Painter.root_colors[pave['fTextColor']];
6099  var tpos_x = pave['fMargin'] * w;
6100  var nlines = pave.fPrimitives.arr.length;
6101  var font = JSROOT.Painter.getFontDetails(pave['fTextFont'], h / (nlines * 1.5));
6102 
6103  var max_len = 0, mul = 1.4;
6104  for (var j = 0; j < nlines; ++j) {
6105  var line = JSROOT.Painter.translateLaTeX(pave.fPrimitives.arr[j]['fLabel']);
6106  var lw = tpos_x + font.stringWidth(svg, line);
6107  if (lw > max_len) max_len = lw;
6108  }
6109  if (max_len > w) {
6110  font.size = Math.floor(font.size * 0.95 * (w / max_len));
6111  mul *= 0.95 * (max_len / w);
6112  }
6113  var x1 = pave['fX1NDC'];
6114  var x2 = pave['fX2NDC'];
6115  var y1 = pave['fY1NDC'];
6116  var y2 = pave['fY2NDC'];
6117  var margin = pave['fMargin'] * (x2 - x1) / pave['fNColumns'];
6118  var yspace = (y2 - y1) / nlines;
6119  var ytext = y2 + 0.5 * yspace; // y-location of 0th entry
6120  var boxw = margin * 0.35;
6121 
6122  for (var i = 0; i < nlines; ++i) {
6123  var leg = pave.fPrimitives.arr[i];
6124  var lopt = leg['fOption'].toLowerCase();
6125 
6126  var string = leg['fLabel'];
6127 
6128  var pos_y = ((i + 1) * (font.size * mul)) - (font.size / 3);
6129  var tpos_y = (i + 1) * (font.size * mul);
6130  if (nlines == 1) {
6131  var pos_y = (h * 0.75) - (font.size / 3);
6132  var tpos_y = h * 0.75;
6133  }
6134 
6135  var attfill = leg;
6136  var attmarker = leg;
6137  var attline = leg;
6138 
6139  var mo = leg['fObject'];
6140 
6141  if ((mo != null) && (typeof mo == 'object')) {
6142  if ('fLineColor' in mo) attline = mo;
6143  if ('fFillColor' in mo) attfill = mo;
6144  if ('fMarkerColor' in mo) attmarker = mo;
6145  }
6146 
6147  var fill = this.createAttFill(attfill);
6148  var llll = JSROOT.Painter.createAttLine(attline);
6149 
6150  p.append("text")
6151  .attr("class", "text")
6152  .attr("text-anchor", "start")
6153  .attr("x", tpos_x)
6154  .attr("y", tpos_y)
6155  .call(font.func)
6156  .attr("fill", tcolor)
6157  .text(string);
6158 
6159  // Draw fill pattern (in a box)
6160  if (lopt.indexOf('f') != -1) {
6161  // box total height is yspace*0.7
6162  // define x,y as the center of the symbol for this entry
6163  var xsym = margin / 2;
6164  var ysym = ytext;
6165  var xf = new Array(4), yf = new Array(4);
6166  xf[0] = xsym - boxw;
6167  yf[0] = ysym - yspace * 0.35;
6168  xf[1] = xsym + boxw;
6169  yf[1] = yf[0];
6170  xf[2] = xf[1];
6171  yf[2] = ysym + yspace * 0.35;
6172  xf[3] = xf[0];
6173  yf[3] = yf[2];
6174  for (var j = 0; j < 4; j++) {
6175  xf[j] = xf[j] * Number(svg.attr("width"));
6176  yf[j] = yf[j] * Number(svg.attr("height"));
6177  }
6178  var ww = xf[1] - xf[0];
6179  var hh = yf[2] - yf[0];
6180  pos_y = pos_y - (hh / 2);
6181  var pos_x = (tpos_x / 2) - (ww / 2);
6182 
6183  p.append("svg:rect")
6184  .attr("x", pos_x)
6185  .attr("y", pos_y)
6186  .attr("width", ww)
6187  .attr("height", hh)
6188  .call(llll.func)
6189  .call(fill.func);
6190  }
6191  // Draw line
6192  if (lopt.indexOf('l') != -1) {
6193 
6194  // line total length (in x) is margin*0.8
6195  var line_length = (0.7 * pave['fMargin']) * w;
6196  var pos_x = (tpos_x - line_length) / 2;
6197  p.append("svg:line")
6198  .attr("x1", pos_x)
6199  .attr("y1", pos_y)
6200  .attr("x2", pos_x + line_length)
6201  .attr("y2", pos_y)
6202  .call(llll.func);
6203  }
6204  // Draw error only
6205  if (lopt.indexOf('e') != -1 && (lopt.indexOf('l') == -1 || lopt.indexOf('f') != -1)) {
6206  }
6207  // Draw Polymarker
6208  if (lopt.indexOf('p') != -1) {
6209 
6210  var line_length = (0.7 * pave['fMargin']) * w;
6211  var pos_x = tpos_x / 2;
6212 
6213  var marker = JSROOT.Painter.createAttMarker(attmarker);
6214  p.append("svg:path")
6215  .attr("transform", function(d) { return "translate(" + pos_x + "," + pos_y + ")"; })
6216  .call(marker.func);
6217  }
6218  }
6219  if (lwidth && lwidth > 1) {
6220  p.append("svg:line")
6221  .attr("x1", w + (lwidth / 2))
6222  .attr("y1", lwidth + 1)
6223  .attr("x2", w + (lwidth / 2))
6224  .attr("y2", h + lwidth - 1)
6225  .call(lcolor.func);
6226  p.append("svg:line")
6227  .attr("x1", lwidth + 1)
6228  .attr("y1", h + (lwidth / 2))
6229  .attr("x2", w + lwidth - 1)
6230  .attr("y2", h + (lwidth / 2))
6231  .style("stroke", lcolor)
6232  .call(lcolor.func);
6233  }
6234 
6235  var pthis = this;
6236 
6237  this.AddDrag('leg', this.draw_g, {
6238  move : function(x, y, dx, dy) {
6239  pthis.draw_g.attr("transform", "translate(" + x + "," + y + ")");
6240 
6241  pave['fX1NDC'] += dx / Number(pthis.svg_pad(true).attr("width"));
6242  pave['fX2NDC'] += dx / Number(pthis.svg_pad(true).attr("width"));
6243  pave['fY1NDC'] -= dy / Number(pthis.svg_pad(true).attr("height"));
6244  pave['fY2NDC'] -= dy / Number(pthis.svg_pad(true).attr("height"));
6245  },
6246  resize : function(width, height) {
6247  pave['fX2NDC'] = pave['fX1NDC'] + width / Number(pthis.svg_pad(true).attr("width"));
6248  pave['fY1NDC'] = pave['fY2NDC'] - height / Number(pthis.svg_pad(true).attr("height"));
6249 
6250  pthis.drawLegend();
6251  }
6252  });
6253  }
6254 
6255  JSROOT.TLegendPainter.prototype.Redraw = function() {
6256  this.drawLegend();
6257  }
6258 
6259  JSROOT.Painter.drawLegend = function(divid, obj, opt) {
6260  var painter = new JSROOT.TLegendPainter(obj);
6261  painter.SetDivId(divid);
6262  painter.Redraw();
6263  return painter;
6264  }
6265 
6266  // =============================================================
6267 
6268  JSROOT.TMultiGraphPainter = function(mgraph) {
6269  JSROOT.TObjectPainter.call(this, mgraph);
6270  this.mgraph = mgraph;
6271  this.firstpainter = null;
6272  this.painters = new Array; // keep painters to be able update objects
6273  }
6274 
6275  JSROOT.TMultiGraphPainter.prototype = Object.create(JSROOT.TObjectPainter.prototype);
6276 
6277  JSROOT.TMultiGraphPainter.prototype.GetObject = function() {
6278  return this.mgraph;
6279  }
6280 
6281  JSROOT.TMultiGraphPainter.prototype.UpdateObject = function(obj) {
6282 
6283  if ((obj==null) || (obj['_typename'] != 'TMultiGraph')) return false;
6284 
6285  var histo = obj['fHistogram'];
6286  var graphs = obj['fGraphs'];
6287 
6288  var isany = false;
6289  if (this.firstpainter && histo)
6290  if (this.firstpainter.UpdateObject(histo)) isany = true;
6291 
6292  for (var i in graphs.arr) {
6293  if (i>=this.painters.length) break;
6294  if (this.painters[i].UpdateObject(graphs.arr[i])) isany = true;
6295  }
6296 
6297  return isany;
6298  }
6299 
6300  JSROOT.TMultiGraphPainter.prototype.drawMultiGraph = function(opt) {
6301  var maximum, minimum, rwxmin = 0, rwxmax = 0, rwymin = 0, rwymax = 0, uxmin = 0, uxmax = 0, dx, dy;
6302  var histo = this.mgraph['fHistogram'];
6303  var graphs = this.mgraph['fGraphs'];
6304  var scalex = 1, scaley = 1;
6305  var logx = false, logy = false, logz = false, gridx = false, gridy = false;
6306  var draw_all = true;
6307 
6308  var pad = this.root_pad();
6309 
6310  if (pad!=null) {
6311  rwxmin = pad.fUxmin;
6312  rwxmax = pad.fUxmax;
6313  rwymin = pad.fUymin;
6314  rwymax = pad.fUymax;
6315  logx = pad['fLogx'];
6316  logy = pad['fLogy'];
6317  logz = pad['fLogz'];
6318  gridx = pad['fGridx'];
6319  gridy = pad['fGridy'];
6320  }
6321  if (histo!=null) {
6322  minimum = histo['fYaxis']['fXmin'];
6323  maximum = histo['fYaxis']['fXmax'];
6324  if (pad) {
6325  uxmin = JSROOT.Painter.padtoX(pad, rwxmin);
6326  uxmax = JSROOT.Painter.padtoX(pad, rwxmax);
6327  }
6328  } else {
6329  for (var i = 0; i < graphs.arr.length; ++i) {
6330  var r = graphs.arr[i].ComputeRange();
6331  if ((i==0) || (r.xmin < rwxmin)) rwxmin = r.xmin;
6332  if ((i==0) || (r.ymin < rwymin)) rwymin = r.ymin;
6333  if ((i==0) || (r.xmax > rwxmax)) rwxmax = r.xmax;
6334  if ((i==0) || (r.ymax > rwymax)) rwymax = r.ymax;
6335  }
6336  if (rwxmin == rwxmax)
6337  rwxmax += 1.;
6338  if (rwymin == rwymax)
6339  rwymax += 1.;
6340  dx = 0.05 * (rwxmax - rwxmin);
6341  dy = 0.05 * (rwymax - rwymin);
6342  uxmin = rwxmin - dx;
6343  uxmax = rwxmax + dx;
6344  if (logy) {
6345  if (rwymin <= 0)
6346  rwymin = 0.001 * rwymax;
6347  minimum = rwymin / (1 + 0.5 * JSROOT.Math.log10(rwymax / rwymin));
6348  maximum = rwymax * (1 + 0.2 * JSROOT.Math.log10(rwymax / rwymin));
6349  } else {
6350  minimum = rwymin - dy;
6351  maximum = rwymax + dy;
6352  }
6353  if (minimum < 0 && rwymin >= 0)
6354  minimum = 0;
6355  if (maximum > 0 && rwymax <= 0)
6356  maximum = 0;
6357  }
6358  if (this.mgraph['fMinimum'] != -1111)
6359  rwymin = minimum = this.mgraph['fMinimum'];
6360  if (this.mgraph['fMaximum'] != -1111)
6361  rwymax = maximum = this.mgraph['fMaximum'];
6362  if (uxmin < 0 && rwxmin >= 0) {
6363  if (logx) uxmin = 0.9 * rwxmin;
6364  // else uxmin = 0;
6365  }
6366  if (uxmax > 0 && rwxmax <= 0) {
6367  if (logx) uxmax = 1.1 * rwxmax;
6368  }
6369  if (minimum < 0 && rwymin >= 0) {
6370  if (logy) minimum = 0.9 * rwymin;
6371  }
6372  if (maximum > 0 && rwymax <= 0) {
6373  if (logy) maximum = 1.1 * rwymax;
6374  }
6375  if (minimum <= 0 && logy)
6376  minimum = 0.001 * maximum;
6377  if (uxmin <= 0 && logx) {
6378  if (uxmax > 1000)
6379  uxmin = 1;
6380  else
6381  uxmin = 0.001 * uxmax;
6382  }
6383  rwymin = minimum;
6384  rwymax = maximum;
6385  if (histo!=null) {
6386  histo['fYaxis']['fXmin'] = rwymin;
6387  histo['fYaxis']['fXmax'] = rwymax;
6388  }
6389 
6390  // Create a temporary histogram to draw the axis (if necessary)
6391  if (!histo) {
6392  histo = JSROOT.Create("TH1I");
6393  histo['fXaxis']['fXmin'] = rwxmin;
6394  histo['fXaxis']['fXmax'] = rwxmax;
6395  histo['fYaxis']['fXmin'] = rwymin;
6396  histo['fYaxis']['fXmax'] = rwymax;
6397  }
6398 
6399  // histogram painter will be first in the pad, will define axis and
6400  // interactive actions
6401  this.firstpainter = JSROOT.Painter.drawHistogram1D(this.divid, histo);
6402 
6403  for ( var i in graphs.arr) {
6404  var subpainter = JSROOT.Painter.drawGraph(this.divid, graphs.arr[i]);
6405  this.painters.push(subpainter);
6406  }
6407  }
6408 
6409  JSROOT.Painter.drawMultiGraph = function(divid, mgraph, opt) {
6410  var painter = new JSROOT.TMultiGraphPainter(mgraph);
6411  painter.SetDivId(divid);
6412  painter.drawMultiGraph(opt);
6413  return painter;
6414  }
6415 
6416  // =====================================================================================
6417 
6418  JSROOT.TTextPainter = function(text) {
6419  JSROOT.TObjectPainter.call(this, text);
6420  this.text = text;
6421  }
6422 
6423  JSROOT.TTextPainter.prototype = Object.create(JSROOT.TObjectPainter.prototype);
6424 
6425  JSROOT.TTextPainter.prototype.GetObject = function() {
6426  return this.text;
6427  }
6428 
6429  JSROOT.TTextPainter.prototype.drawPaveLabel = function() {
6430 
6431  this.RecreateDrawG(true, ".text_layer");
6432 
6433  var pavelabel = this.text;
6434 
6435  var w = Number(this.svg_pad(true).attr("width")),
6436  h = Number(this.svg_pad(true).attr("height"));
6437 
6438  var pos_x = pavelabel['fX1NDC'] * w;
6439  var pos_y = (1.0 - pavelabel['fY1NDC']) * h;
6440  var width = Math.abs(pavelabel['fX2NDC'] - pavelabel['fX1NDC']) * w;
6441  var height = Math.abs(pavelabel['fY2NDC'] - pavelabel['fY1NDC']) * h;
6442  pos_y -= height;
6443  var fcolor = this.createAttFill(pavelabel);
6444  var tcolor = JSROOT.Painter.root_colors[pavelabel['fTextColor']];
6445  var scolor = JSROOT.Painter.root_colors[pavelabel['fShadowColor']];
6446 
6447  // align = 10*HorizontalAlign + VerticalAlign
6448  // 1=left adjusted, 2=centered, 3=right adjusted
6449  // 1=bottom adjusted, 2=centered, 3=top adjusted
6450  var align = 'start', halign = Math.round(pavelabel['fTextAlign'] / 10);
6451  var baseline = 'bottom', valign = pavelabel['fTextAlign'] % 10;
6452  if (halign == 1) align = 'start';
6453  else if (halign == 2) align = 'middle';
6454  else if (halign == 3) align = 'end';
6455  if (valign == 1) baseline = 'bottom';
6456  else if (valign == 2) baseline = 'middle';
6457  else if (valign == 3) baseline = 'top';
6458  var lmargin = 0;
6459  switch (halign) {
6460  case 1: lmargin = pavelabel['fMargin'] * width; break;
6461  case 2: lmargin = width / 2; break;
6462  case 3: lmargin = width - (pavelabel['fMargin'] * width); break;
6463  }
6464  var lwidth = pavelabel['fBorderSize'] ? pavelabel['fBorderSize'] : 0;
6465  var font = JSROOT.Painter.getFontDetails(pavelabel['fTextFont'], height / 1.9);
6466 
6467  var lcolor = JSROOT.Painter.createAttLine(pavelabel, lwidth);
6468 
6469  var pave = this.draw_g
6470  .attr("width", width)
6471  .attr("height", height)
6472  .attr("transform", "translate(" + pos_x + "," + pos_y + ")");
6473 
6474  pave.append("svg:rect")
6475  .attr("x", 0)
6476  .attr("y", 0)
6477  .attr("width", width)
6478  .attr("height", height)
6479  .call(fcolor.func)
6480  .style("stroke-width", lwidth ? 1 : 0)
6481  .style("stroke", lcolor.color);
6482 
6483  var line = JSROOT.Painter.translateLaTeX(pavelabel['fLabel']);
6484 
6485  var lw = font.stringWidth(this.svg_pad(true), line);
6486  if (lw > width) font.size = Math.floor(font.size * (width / lw));
6487 
6488  pave.append("text")
6489  .attr("class", "text")
6490  .attr("text-anchor", align)
6491  .attr("x", lmargin)
6492  .attr("y", (height / 2) + (font.size / 3))
6493  .call(font.func)
6494  .attr("fill", tcolor)
6495  .text(line);
6496 
6497  if (lwidth && lwidth > 1) {
6498  pave.append("svg:line")
6499  .attr("x1", width + (lwidth / 2))
6500  .attr("y1", lwidth + 1)
6501  .attr("x2", width + (lwidth / 2))
6502  .attr("y2", height + lwidth - 1)
6503  .call(lcolor.func);
6504  pave.append("svg:line")
6505  .attr("x1", lwidth + 1)
6506  .attr("y1", height + (lwidth / 2))
6507  .attr("x2", width + lwidth - 1)
6508  .attr("y2", height + (lwidth / 2))
6509  .style("stroke", lcolor)
6510  .call(lcolor.func);
6511  }
6512  }
6513 
6514  JSROOT.TTextPainter.prototype.drawText = function() {
6515  this.RecreateDrawG(true, ".text_layer");
6516 
6517  var kTextNDC = JSROOT.BIT(14);
6518 
6519  var w = Number(this.svg_pad(true).attr("width")),
6520  h = Number(this.svg_pad(true).attr("height"));
6521  var align = 'start', halign = Math.round(this.text['fTextAlign'] / 10);
6522  var baseline = 'bottom', valign = this.text['fTextAlign'] % 10;
6523  if (halign == 1) align = 'start';
6524  else if (halign == 2) align = 'middle';
6525  else if (halign == 3) align = 'end';
6526  if (valign == 1) baseline = 'bottom';
6527  else if (valign == 2) baseline = 'middle';
6528  else if (valign == 3) baseline = 'top';
6529  var lmargin = 0;
6530  switch (halign) {
6531  case 1: lmargin = this.text['fMargin'] * w; break;
6532  case 2: lmargin = w / 2; break;
6533  case 3: lmargin = w - (this.text['fMargin'] * w); break;
6534  }
6535  var pos_x = this.text['fX'], pos_y = this.text['fY'];
6536  if (this.text.TestBit(kTextNDC)) {
6537  pos_x = pos_x * w;
6538  pos_y = (1 - pos_y) * h;
6539  } else
6540  if (this.main_painter()!=null) {
6541  pos_x = this.main_painter().x(pos_x);
6542  pos_y = this.main_painter().y(pos_y);
6543  } else
6544  if (this.root_pad()!=null) {
6545  var pad = this.root_pad();
6546  if (pad['fLogx'])
6547  pos_x = (pos_x > 0) ? JSROOT.Math.log10(pos_x) : pad['fUxmin'];
6548  if (pad['fLogy'])
6549  pos_y = (pos_y > 0) ? JSROOT.Math.log10(pos_y) : pad['fUymin'];
6550 
6551  pos_x = ((Math.abs(pad['fX1']) + pos_x) / (pad['fX2'] - pad['fX1'])) * w;
6552  pos_y = (1 - ((Math.abs(pad['fY1']) + pos_y) / (pad['fY2'] - pad['fY1']))) * h;
6553  } else {
6554  alert("Cannot draw text at x/y coordinates without real TPad object");
6555  pos_x = w/2;
6556  pos_y = h/2;
6557  }
6558 
6559  var tcolor = JSROOT.Painter.root_colors[this.text['fTextColor']];
6560  var font = JSROOT.Painter.getFontDetails(this.text['fTextFont'], this.text['fTextSize'] * Math.min(w,h));
6561 
6562  var string = this.text['fTitle'];
6563  // translate the LaTeX symbols
6564  if (this.text['_typename'] == 'TLatex')
6565  string = JSROOT.Painter.translateLaTeX(string);
6566 
6567  this.draw_g.append("text")
6568  .attr("class", "text")
6569  .attr("x", pos_x.toFixed(1))
6570  .attr("y", pos_y.toFixed(1))
6571  .call(font.func)
6572  .attr("text-anchor", align)
6573  .attr("fill", tcolor)
6574  .text(string);
6575  }
6576 
6577  JSROOT.TTextPainter.prototype.UpdateObject = function(obj) {
6578  if (this.text['_typename'] != obj['_typename']) return false;
6579  if (this.text['_typename'] == 'TPaveLabel') {
6580  this.text['fLabel'] = obj['fLabel'];
6581  } else {
6582  this.text['fTitle'] = obj['fTitle'];
6583  }
6584 
6585  return true;
6586  }
6587 
6588 
6589  JSROOT.TTextPainter.prototype.Redraw = function() {
6590  if (this.text['_typename'] == 'TPaveLabel')
6591  this.drawPaveLabel();
6592  else
6593  this.drawText();
6594  }
6595 
6596  JSROOT.Painter.drawText = function(divid, text) {
6597  var painter = new JSROOT.TTextPainter(text);
6598  painter.SetDivId(divid);
6599  painter.Redraw();
6600  return painter;
6601  }
6602 
6603  JSROOT.Painter.drawStreamerInfo = function(divid, obj) {
6604  $("#" + divid).css({ overflow : 'auto' });
6605  var painter = new JSROOT.HierarchyPainter('sinfo', divid, true);
6606  painter.ShowStreamerInfo(obj);
6607  return painter;
6608  }
6609 
6610  // =========== painter of hierarchical structures =================================
6611 
6612  JSROOT.HList = [];
6613 
6614  JSROOT.DelHList = function(_name) {
6615  for ( var i in JSROOT.HList)
6616  if (JSROOT.HList[i].name == _name) {
6617  var old = JSROOT.HList[i];
6618  JSROOT.HList.splice(i, 1);
6619  delete old;
6620  return true;
6621  }
6622  }
6623 
6624  JSROOT.AddHList = function(_name, _h) {
6625  JSROOT.DelHList(_name);
6626  JSROOT.HList.push({
6627  name : _name,
6628  h : _h
6629  });
6630  }
6631 
6632  JSROOT.H = function(name) {
6633  for ( var i in JSROOT.HList)
6634  if (JSROOT.HList[i].name == name)
6635  return JSROOT.HList[i].h;
6636  return null;
6637  }
6638 
6639  JSROOT.HierarchyPainter = function(name, frameid, local) {
6640  JSROOT.TBasePainter.call(this);
6641  this.name = name;
6642  this.frameid = frameid;
6643  this.h = null; // hierarchy
6644  this.local = local;
6645  if (!this.local) JSROOT.AddHList(name, this);
6646  }
6647 
6648  JSROOT.HierarchyPainter.prototype = Object.create(JSROOT.TBasePainter.prototype);
6649 
6650  JSROOT.HierarchyPainter.prototype.Cleanup = function() {
6651  if (!this.local) JSROOT.DelHList(this.name);
6652  }
6653 
6654  JSROOT.HierarchyPainter.prototype.GlobalName = function(suffix) {
6655  var res = "JSROOT.H(\'" + this.name + "\')";
6656  if (suffix != null) res += suffix;
6657  return res;
6658  }
6659 
6660  JSROOT.HierarchyPainter.prototype.ListHierarchy = function(folder, lst) {
6661  folder['_childs'] = [];
6662  for ( var i in lst.arr) {
6663  var obj = lst.arr[i];
6664  var item = {
6665  _name : obj['fName'],
6666  _kind : "ROOT." + obj['_typename'],
6667  _readobj : obj
6668  };
6669  folder._childs.push(item);
6670  }
6671  }
6672 
6673  JSROOT.HierarchyPainter.prototype.StreamerInfoHierarchy = function(folder, lst) {
6674  folder['_childs'] = [];
6675 
6676  for ( var i in lst.arr) {
6677  var entry = lst.arr[i]
6678 
6679  if (typeof (entry['fName']) == 'undefined') {
6680  console.log("strange element in StreamerInfo with name " + entry['fName']);
6681  continue;
6682  }
6683 
6684  var item = {
6685  _name : entry['fName'],
6686  _kind : "",
6687  _childs : []
6688  };
6689 
6690  folder._childs.push(item);
6691 
6692  item._childs.push({ _name : 'Checksum: ' + entry['fCheckSum'] });
6693  item._childs.push({ _name : 'Class version: ' + entry['fClassVersion'] });
6694  if (entry['fTitle'] != '') item._childs.push({ _name : 'Title: ' + entry['fTitle'] });
6695  if (typeof entry['fElements'] == 'undefined') continue;
6696  for ( var l in entry['fElements']['arr']) {
6697  var elem = entry['fElements']['arr'][l];
6698  if ((elem == null) || (typeof (elem['fName']) == 'undefined')) continue;
6699  var info = elem['fTypeName'] + " " + elem['fName'] + ";";
6700  if (elem['fTitle'] != '') info += " // " + elem['fTitle'];
6701  item._childs.push({ _name : info });
6702  }
6703  }
6704  }
6705 
6706  JSROOT.HierarchyPainter.prototype.TreeHierarchy = function(node, obj) {
6707  node._childs = [];
6708 
6709  for ( var i in obj['fBranches'].arr) {
6710  var branch = obj['fBranches'].arr[i];
6711  var nb_leaves = branch['fLeaves'].arr.length;
6712 
6713  // display branch with only leaf as leaf
6714  if (nb_leaves == 1 && branch['fLeaves'].arr[0]['fName'] == branch['fName']) nb_leaves = 0;
6715 
6716  var subitem = {
6717  _name : branch['fName'],
6718  _kind : nb_leaves > 0 ? "ROOT.TBranch" : "ROOT.TLeafF"
6719  }
6720 
6721  node._childs.push(subitem);
6722 
6723  if (nb_leaves > 0) {
6724  subitem._childs = [];
6725  for (var j = 0; j < nb_leaves; ++j) {
6726  var leafitem = {
6727  _name : branch['fLeaves'].arr[j]['fName'],
6728  _kind : "ROOT.TLeafF"
6729  }
6730  subitem._childs.push(leafitem);
6731  }
6732  }
6733  }
6734  }
6735 
6736  JSROOT.HierarchyPainter.prototype.KeysHierarchy = function(folder, keys, file) {
6737  folder['_childs'] = [];
6738 
6739  var painter = this;
6740  for (var i in keys) {
6741  var key = keys[i];
6742 
6743  var item = {
6744  _name : key['fName'] + ";" + key['fCycle'],
6745  _kind : "ROOT." + key['fClassName'],
6746  _title : key['fTitle'],
6747  _keyname : key['fName'],
6748  _readobj : null
6749  };
6750 
6751  if ('fRealName' in key)
6752  item['_realname'] = key['fRealName'] + ";" + key['fCycle'];
6753 
6754  if ((key['fClassName'] == 'TTree' || key['fClassName'] == 'TNtuple')) {
6755  item["_more"] = true;
6756 
6757  item['_expand'] = function(node, obj) {
6758  painter.TreeHierarchy(node, obj);
6759  return true;
6760  }
6761  } else if (key['fClassName'] == 'TDirectory' || key['fClassName'] == 'TDirectoryFile') {
6762  item["_more"] = true;
6763  item["_isdir"] = true;
6764  item['_expand'] = function(node, obj) {
6765  painter.KeysHierarchy(node, obj.fKeys);
6766  return true;
6767  }
6768  } else if ((key['fClassName'] == 'TList') && (key['fName'] == 'StreamerInfo') && (file != null)) {
6769  item['_name'] = 'StreamerInfo';
6770  item['_kind'] = "ROOT.TStreamerInfoList";
6771  item['_title'] = "List of streamer infos for binary I/O";
6772  item['_readobj'] = file.fStreamerInfos;
6773  item['_expand'] = function(node, obj) {
6774  painter.StreamerInfoHierarchy(node, obj);
6775  return true;
6776  }
6777 
6778  } else if (key['fClassName'] == 'TList'
6779  || key['fClassName'] == 'TObjArray'
6780  || key['fClassName'] == 'TClonesArray') {
6781  item["_more"] = true;
6782  item['_expand'] = function(node, obj) {
6783  painter.ListHierarchy(node, obj);
6784  return true;
6785  }
6786  }
6787 
6788  folder._childs.push(item);
6789  }
6790  }
6791 
6792  JSROOT.HierarchyPainter.prototype.FileHierarchy = function(file) {
6793  var painter = this;
6794 
6795  var folder = {
6796  _name : file.fFileName,
6797  _kind : "ROOT.TFile",
6798  _file : file,
6799  // this is normal get method, where item name is used
6800  _get : function(item, callback) {
6801  if ((this._file == null) || (item._readobj != null)) {
6802  if (typeof callback == 'function')
6803  callback(item, item._readobj);
6804  return;
6805  }
6806 
6807  var fullname = painter.itemFullName(item, this);
6808  // var pos = fullname.lastIndexOf(";");
6809  // if (pos>0) fullname = fullname.slice(0, pos);
6810 
6811  this._file.ReadObject(fullname, function(obj) {
6812  item._readobj = obj;
6813  if ('_expand' in item)
6814  item._name = item._keyname; // remove cycle number for
6815  // objects supporting expand
6816  if (typeof callback == 'function')
6817  callback(item, obj);
6818  });
6819  },
6820  // this is alternative get method, where items may not exists (due to
6821  // missing/not-read subfolder)
6822  _getdirect : function(itemname, callback) {
6823  this._file.ReadObject(itemname, function(obj) {
6824  if (typeof callback == 'function')
6825  callback(itemname, obj);
6826  });
6827  }
6828  };
6829 
6830  this.KeysHierarchy(folder, file.fKeys, file);
6831 
6832  return folder;
6833  }
6834 
6835  JSROOT.HierarchyPainter.prototype.Find = function(itemname, force) {
6836 
6837  function find_in_hierarchy(top, fullname) {
6838 
6839  if (!fullname || fullname.length == 0) return top;
6840 
6841  var pos = -1;
6842 
6843  function process_child(child) {
6844  // set parent pointer when searching child
6845  child['_parent'] = top;
6846  if ((pos + 1 == fullname.length) || (pos < 0)) return child;
6847 
6848  return find_in_hierarchy(child, fullname.substr(pos + 1));
6849  }
6850 
6851  do {
6852  // we try to find element with slashes inside
6853  pos = fullname.indexOf("/", pos + 1);
6854 
6855  var localname = (pos < 0) ? fullname : fullname.substr(0, pos);
6856 
6857  for (var i in top._childs)
6858  if (top._childs[i]._name == localname)
6859  return process_child(top._childs[i]);
6860 
6861  if (force) {
6862  // if didnot found element with given name we just generate it
6863  if (! ('_childs' in top)) top['_childs'] = [];
6864  var child = { _name: localname };
6865  top['_childs'].push(child);
6866  return process_child(child);
6867  }
6868  } while (pos > 0);
6869 
6870  return null;
6871  }
6872 
6873  return find_in_hierarchy(this.h, itemname);
6874  }
6875 
6876  JSROOT.HierarchyPainter.prototype.itemFullName = function(node, uptoparent) {
6877  var res = "";
6878 
6879  while ('_parent' in node) {
6880  if (res.length > 0) res = "/" + res;
6881  res = node._name + res;
6882  node = node._parent;
6883  if ((uptoparent != null) && (node == uptoparent)) break;
6884  }
6885 
6886  return res;
6887  }
6888 
6889  JSROOT.HierarchyPainter.prototype.CheckCanDo = function(node) {
6890  var cando = { expand : false, display : false, scan : true, open : false,
6891  img1 : "", img2 : "", html : "", ctxt : false };
6892 
6893  var kind = node["_kind"];
6894  if (kind == null) kind = "";
6895 
6896  cando.expand = ('_more' in node);
6897 
6898  if (node == this.h) {
6899  cando.ctxt = true;
6900  } else if (kind == "ROOT.Session") {
6901  cando.img1 = "img_globe";
6902  } else if (kind.match(/^ROOT.TH1/)) {
6903  cando.img1 = "img_histo1d";
6904  cando.scan = false;
6905  cando.display = true;
6906  } else if (kind.match(/^ROOT.TH2/)) {
6907  cando.img1 = "img_histo2d";
6908  cando.scan = false;
6909  cando.display = true;
6910  } else if (kind.match(/^ROOT.TH3/)) {
6911  cando.img1 = "img_histo3d";
6912  cando.scan = false;
6913  cando.display = true;
6914  } else if (kind == "ROOT.TCanvas") {
6915  cando.img1 = "img_canvas";
6916  cando.display = true;
6917  } else if (kind == "ROOT.TProfile") {
6918  cando.img1 = "img_profile";
6919  cando.display = true;
6920  } else if (kind.match(/^ROOT.TGraph/) || (kind=="TCutG")) {
6921  cando.img1 = "img_graph";
6922  cando.display = true;
6923  } else if (kind == "ROOT.TF1") {
6924  cando.img1 = "img_graph";
6925  cando.display = true;
6926  } else if (kind == "ROOT.TTree") {
6927  cando.img1 = "img_tree";
6928  } else if (kind == "ROOT.TFolder") {
6929  cando.img1 = "img_folder";
6930  cando.img2 = "img_folderopen";
6931  } else if (kind == "ROOT.TNtuple")
6932  cando.img1 = "img_tree";
6933  else if (kind == "ROOT.TBranch")
6934  cando.img1 = "img_branch";
6935  else if (kind.match(/^ROOT.TLeaf/))
6936  cando.img1 = "img_leaf";
6937  else if (kind == "ROOT.TStreamerInfoList") {
6938  cando.img1 = 'img_question';
6939  cando.expand = false;
6940  cando.display = true;
6941  } else if ((kind.indexOf("ROOT.") == 0) && JSROOT.canDraw(kind.slice(5))) {
6942  cando.img1 = "img_histo1d";
6943  cando.scan = false;
6944  cando.display = true;
6945  }
6946 
6947  return cando;
6948  }
6949 
6950  JSROOT.HierarchyPainter.prototype.RefreshHtml = function(force) {
6951  if (this.frameid == null) return;
6952  var elem = $("#" + this.frameid);
6953  if (elem.length == 0) return;
6954 
6955  if (this.h == null) return elem.html("<h2>null</h2>");
6956 
6957  this['html'] = "<p>";
6958 
6959  this['html'] += "<a href='#open_all'>open all</a>";
6960  this['html'] += "| <a href='#close_all'>close all</a>";
6961  if ('_online' in this.h)
6962  this['html'] += "| <a href='#reload'>reload</a>";
6963  else
6964  this['html'] += "<a/>"
6965 
6966  if ('disp_kind' in this)
6967  this['html'] += "| <a href='#clear'>clear</a>";
6968  else
6969  this['html'] += "<a/>"
6970 
6971  this['html'] += "</p>";
6972 
6973  this['html'] += '<div class="h_tree">'
6974  this.addItemHtml(this.h, null);
6975  this['html'] += '</div>';
6976 
6977  var h = this;
6978 
6979  var items = elem.html(this['html'])
6980  .find(".h_item")
6981  .click(function() { h.tree_click($(this)); });
6982 
6983  if ('disp_kind' in h) {
6984  if (JSROOT.gStyle.DragAndDrop)
6985  items.draggable({ revert: "invalid", appendTo: "body", helper: "clone" });
6986 
6987  if (JSROOT.gStyle.ContextMenu)
6988  items.on('contextmenu', function(e) { h.tree_contextmenu($(this), e); })
6989  }
6990 
6991  elem.find(".plus_minus").click(function() { h.tree_click($(this),true); });
6992 
6993  elem.find("a").first().click(function() { h.toggle(true); return false; })
6994  .next().click(function() { h.toggle(false); return false; })
6995  .next().click(function() { h.reload(); return false; })
6996  .next().click(function() { h.clear(); return false; });
6997  }
6998 
6999  JSROOT.HierarchyPainter.prototype.isLastSibling = function(hitem) {
7000  return hitem && hitem._parent && hitem._parent._childs &&
7001  (hitem._parent._childs.indexOf(hitem) == hitem._parent._childs.length-1);
7002  }
7003 
7004  JSROOT.HierarchyPainter.prototype.addItemHtml = function(hitem, parent) {
7005  var isroot = (parent == null);
7006  var has_childs = '_childs' in hitem;
7007  var cando = this.CheckCanDo(hitem);
7008 
7009  if (!isroot) hitem._parent = parent;
7010 
7011  var can_click = false;
7012 
7013  if (!has_childs || !cando.scan) {
7014  if (cando.expand) {
7015  can_click = true;
7016  if (cando.img1.length == 0) {
7017  cando.img1 = 'img_folder';
7018  cando.img2 = 'img_folderopen';
7019  }
7020  } else
7021  if (cando.display) {
7022  can_click = true;
7023  } else
7024  if (cando.html.length > 0) can_click = true;
7025  }
7026 
7027  if (!('_icon' in hitem)) hitem['_icon'] = cando.img1;
7028  if (!('_icon2' in hitem)) hitem['_icon2'] = cando.img2;
7029  if (hitem['_icon2']=="") hitem['_icon2'] = hitem['_icon'];
7030 
7031  // assign node icons
7032 
7033  if (!hitem['_icon'])
7034  hitem['_icon'] = has_childs ? "img_folder" : "img_page";
7035 
7036  if (!hitem['_icon2'])
7037  hitem['_icon2'] = has_childs ? "img_folderopen" : "img_page";
7038 
7039  if (isroot)
7040  hitem['_icon'] = hitem['_icon2'] = "img_base";
7041 
7042  var itemname = this.itemFullName(hitem);
7043 
7044  this['html'] += '<div item="' + itemname + '">';
7045 
7046  // build indent
7047  var sindent = "";
7048  var prnt = isroot ? null : hitem._parent;
7049  while ((prnt != null) && (prnt != this.h)) {
7050  sindent = '<div class="' + (this.isLastSibling(prnt) ? "img_empty" : "img_line") + '"/>' + sindent;
7051  prnt = prnt._parent;
7052  }
7053  this['html'] += sindent;
7054 
7055  var icon_class = "", plusminus = false;
7056 
7057  if (isroot) {
7058  // for root node no extra code
7059  } else
7060  if (has_childs) {
7061  icon_class = hitem._isopen ? "img_minus" : "img_plus";
7062  plusminus = true;
7063  } else {
7064  icon_class = "img_join";
7065  }
7066 
7067  if (icon_class.length > 0) {
7068  this['html'] += '<div class="' + icon_class;
7069  if (this.isLastSibling(hitem)) this['html'] += "bottom";
7070  if (plusminus) this['html'] += ' plus_minus" style="cursor:pointer';
7071  this['html'] += '"/>';
7072  }
7073 
7074  // make node icons
7075 
7076  var icon_name = hitem._isopen ? hitem._icon2 : hitem._icon;
7077 
7078  if (icon_name.indexOf("img_")==0)
7079  this['html'] += '<div class="' + icon_name + '"/>';
7080  else
7081  this['html'] += '<img src="' + icon_name + '" alt=""/>';
7082 
7083  this['html'] += '<a';
7084  if (can_click || has_childs) this['html'] +=' class="h_item"';
7085 
7086  var element_name = hitem._name;
7087 
7088  if ('_realname' in hitem)
7089  element_name = hitem._realname;
7090 
7091  var element_title = "";
7092  if ('_title' in hitem) element_title = hitem._title;
7093 
7094  if ('_fullname' in hitem)
7095  element_title += " fullname: " + hitem['_fullname'];
7096 
7097  if (element_title.length == 0) element_title = element_name;
7098 
7099  this['html'] += ' title="' + element_title + '"';
7100  this['html'] += '>' + element_name + '</a>';
7101 
7102  if (has_childs && (isroot || hitem._isopen)) {
7103  this['html'] += '<div class="h_childs">';
7104  for (var i in hitem._childs)
7105  this.addItemHtml(hitem._childs[i], hitem);
7106  this['html'] += '</div>';
7107  }
7108 
7109  this['html'] += '</div>';
7110  }
7111 
7112  JSROOT.HierarchyPainter.prototype.tree_click = function(node, plusminus) {
7113 
7114  var itemname = node.parent().attr('item');
7115 
7116  if (itemname==null) return;
7117 
7118  var hitem = this.Find(itemname);
7119  if (hitem==null) return;
7120 
7121  if (!plusminus) {
7122  var cando = this.CheckCanDo(hitem);
7123 
7124  if (cando.open && (cando.html.length>0))
7125  return window.open(cando.html);
7126 
7127  if (cando.expand && (hitem['_childs'] == null))
7128  return this.expand(itemname, hitem, node.parent());
7129 
7130  if (cando.display)
7131  return this.display(itemname);
7132 
7133  if (!('_childs' in hitem) || (hitem === this.h)) return;
7134  }
7135 
7136  if (hitem._isopen)
7137  delete hitem._isopen;
7138  else
7139  hitem._isopen = true;
7140 
7141  this.UpdateTreeNode(node.parent(), hitem);
7142  }
7143 
7144  JSROOT.HierarchyPainter.prototype.open = function(itemname) {
7145  console.log("open() no longer available");
7146  }
7147 
7148  JSROOT.HierarchyPainter.prototype.UpdateTreeNode = function(node, hitem) {
7149  var has_childs = '_childs' in hitem;
7150 
7151  var newname = hitem._isopen ? hitem._icon2 : hitem._icon;
7152  var oldname = hitem._isopen ? hitem._icon : hitem._icon2;
7153 
7154  var img = node.find("a").first().prev();
7155 
7156  if (newname.indexOf("img_")<0) {
7157  img.attr("src", newname);
7158  } else {
7159  if (newname!=oldname)
7160  img.switchClass(oldname, newname);
7161  }
7162 
7163  img = img.prev();
7164 
7165  var h = this;
7166 
7167  var new_class = hitem._isopen ? "img_minus" : "img_plus";
7168  if (this.isLastSibling(hitem)) new_class += "bottom";
7169 
7170  if (img.hasClass("plus_minus")) {
7171  img.attr('class', new_class + " plus_minus");
7172  } else
7173  if (has_childs) {
7174  img.attr('class', new_class + " plus_minus");
7175  img.css('cursor', 'pointer');
7176  img.click(function() { h.tree_click($(this), true); });
7177  }
7178 
7179  var childs = node.children().last();
7180  if (childs.hasClass("h_childs")) childs.remove();
7181 
7182  var display_childs = has_childs && hitem._isopen;
7183  if (!display_childs) return;
7184 
7185  this['html'] = '<div class="h_childs">';
7186  for (var i in hitem._childs)
7187  this.addItemHtml(hitem._childs[i], hitem);
7188  this['html'] += '</div>';
7189  node.append(this['html']);
7190  childs = node.children().last();
7191 
7192  var items = childs.find(".h_item")
7193  .click(function() { h.tree_click($(this)); });
7194 
7195  if ('disp_kind' in h) {
7196  if (JSROOT.gStyle.DragAndDrop)
7197  items.draggable({ revert: "invalid", appendTo: "body", helper: "clone" });
7198 
7199  if (JSROOT.gStyle.ContextMenu)
7200  items.on('contextmenu', function(e) { h.tree_contextmenu($(this), e); })
7201  }
7202 
7203  childs.find(".plus_minus").click(function() { h.tree_click($(this), true); });
7204  }
7205 
7206  JSROOT.HierarchyPainter.prototype.toggle = function(status) {
7207  var painter = this;
7208 
7209  var toggleItem = function(hitem) {
7210 
7211  if (hitem != painter.h)
7212  if (status)
7213  hitem._isopen = true;
7214  else
7215  delete hitem._isopen;
7216 
7217  if ('_childs' in hitem)
7218  for ( var i in hitem._childs)
7219  toggleItem(hitem._childs[i]);
7220  }
7221 
7222  toggleItem(this.h);
7223 
7224  this.RefreshHtml();
7225  }
7226 
7227  JSROOT.HierarchyPainter.prototype.get = function(itemname, callback, options) {
7228  var item = this.Find(itemname);
7229 
7230  // process get in central method of hierarchy item (if exists)
7231  if ((item == null) && ('_getdirect' in this.h))
7232  return this.h._getdirect(itemname, callback);
7233 
7234  // normally search _get method in the parent items
7235  var curr = item;
7236  while (curr != null) {
7237  if (('_get' in curr) && (typeof (curr._get) == 'function'))
7238  return curr._get(item, callback);
7239  curr = ('_parent' in curr) ? curr['_parent'] : null;
7240  }
7241 
7242  if (typeof callback == 'function')
7243  callback(item, null);
7244  }
7245 
7246  JSROOT.HierarchyPainter.prototype.draw = function(divid, obj, drawopt) {
7247  // just envelope, one should be able to redefine it for sub-classes
7248  return JSROOT.draw(divid, obj, drawopt);
7249  }
7250 
7251  JSROOT.HierarchyPainter.prototype.display = function(itemname, drawopt, call_back) {
7252 
7253  function do_call_back(res) {
7254  if (typeof call_back=='function') call_back(res);
7255  }
7256 
7257  if (!this.CreateDisplay()) return do_call_back(null);
7258 
7259  var h = this;
7260 
7261  var mdi = h['disp'];
7262 
7263  var updating = drawopt=="update";
7264 
7265  if (updating) {
7266  var item = h.Find(itemname);
7267  if ((item==null) || ('_doing_update' in item)) return do_call_back(null);
7268  item['_doing_update'] = true;
7269  }
7270 
7271  h.get(itemname, function(item, obj) {
7272 
7273  if (updating) delete item['_doing_update'];
7274  if (obj==null) return do_call_back(null);
7275 
7276  var painter = null;
7277 
7278  var pos = drawopt ? drawopt.indexOf("divid:") : -1;
7279  if (pos>=0) {
7280  var divid = drawopt.slice(pos+6);
7281  drawopt = drawopt.slice(0, pos);
7282  painter = h.draw(divid, obj, drawopt);
7283  } else
7284  mdi.ForEachPainter(function(p, frame) {
7285  if (p['_hitemname'] != itemname) return;
7286  painter = p;
7287  mdi.ActivateFrame(frame);
7288  painter.RedrawObject(obj);
7289  });
7290 
7291  if (painter==null) {
7292  if (updating) {
7293  console.log("something went wrong - did not found painter when doing update of " + itemname);
7294  } else {
7295  var frame = mdi.FindFrame(itemname, true);
7296  if (JSROOT.gStyle.DragAndDrop)
7297  frame.addClass("ui-state-default");
7298  painter = h.draw(frame.attr("id"), obj, drawopt);
7299 
7300  mdi.ActivateFrame(frame);
7301 
7302  if (JSROOT.gStyle.DragAndDrop)
7303  frame.droppable({
7304  hoverClass : "ui-state-active",
7305  accept: function(ui) {
7306  var dropname = ui.parent().attr('item');
7307  if (dropname == itemname) return false;
7308 
7309  var ditem = h.Find(dropname);
7310  if (ditem==null) return false;
7311 
7312  return ditem._kind.indexOf("ROOT.")==0;
7313  },
7314  drop: function(event, ui) {
7315  var dropname = ui.draggable.parent().attr('item');
7316  return h.dropitem(dropname, frame.attr("id"));
7317  }
7318  });
7319  }
7320  }
7321 
7322  if (painter) painter['_hitemname'] = itemname; // mark painter as created from hierarchy
7323 
7324  do_call_back(painter);
7325  });
7326  }
7327 
7328  JSROOT.HierarchyPainter.prototype.dropitem = function(itemname, divid) {
7329  var h = this;
7330  var mdi = h['disp'];
7331 
7332  h.get(itemname, function(item, obj) {
7333  if (obj==null) return;
7334  var painter = h.draw(divid, obj, "same");
7335  if (painter) painter['_hitemname'] = itemname;
7336  });
7337 
7338  return true;
7339  }
7340 
7341 
7342  JSROOT.HierarchyPainter.prototype.updateAll = function() {
7343  // method can be used to fetch new objects and update all existing drawings
7344 
7345  var mdi = this['disp'];
7346  if (mdi == null) return;
7347 
7348  var allitems = [];
7349 
7350  // first collect items
7351  mdi.ForEachPainter(function(p) {
7352  if (('_hitemname' in p) && (allitems.indexOf(p['_hitemname'])<0)) allitems.push(p['_hitemname']);
7353  }, true); // only visible panels are considered
7354 
7355  // than call display with update
7356  for (var cnt in allitems)
7357  this.display(allitems[cnt], "update");
7358  }
7359 
7360  JSROOT.HierarchyPainter.prototype.displayAll = function(items, options) {
7361  if ((items == null) || (items.length == 0)) return;
7362  if (!this.CreateDisplay()) return;
7363  if (options == null) options = [];
7364  while (options.length < items.length)
7365  options.push("");
7366 
7367  var mdi = this['disp'];
7368 
7369  var dropitems = new Array(items.length);
7370 
7371  // First of all check that items are exists, look for cycle extension
7372  for (var i in items) {
7373  if (this.Find(items[i])) continue;
7374  if (this.Find(items[i] + ";1")) { items[i] += ";1"; continue; }
7375 
7376  var pos = items[i].indexOf("+");
7377  if ((pos>0) && this.Find(items[i].slice(0,pos))) {
7378  dropitems[i] = items[i].slice(pos+1);
7379  items[i] = items[i].slice(0,pos);
7380  }
7381  }
7382 
7383  // Than create empty frames for each item
7384  for (var i in items)
7385  mdi.CreateFrame(items[i]);
7386 
7387  var h = this;
7388 
7389  // Display items
7390  for (var i in items)
7391  this.display(items[i], options[i], function(painter) {
7392  if ((painter==0) || (dropitems[i]==null)) return;
7393  h.dropitem(dropitems[i], painter.divid);
7394  });
7395  }
7396 
7397  JSROOT.HierarchyPainter.prototype.reload = function() {
7398  if ('_online' in this.h)
7399  this.OpenOnline(this.h['_online']);
7400  }
7401 
7402  JSROOT.HierarchyPainter.prototype.expand = function(itemname, item0, node) {
7403  var painter = this;
7404 
7405  if (node==null)
7406  node = $("#" + this.frameid).find("[item='" + itemname + "']");
7407 
7408  if (node.length==0)
7409  return console.log("Did not found node with item = " + itemname);
7410 
7411  if (item0==null) item0 = this.Find(itemname);
7412  if (item0==null) return;
7413  item0['_doing_expand'] = true;
7414 
7415  this.get(itemname, function(item, obj) {
7416  delete item0['_doing_expand'];
7417  if ((item == null) || (obj == null)) return;
7418 
7419  var curr = item;
7420  while (curr != null) {
7421  if (('_expand' in curr) && (typeof (curr['_expand']) == 'function')) {
7422  if (curr['_expand'](item, obj)) {
7423  var itemname = painter.itemFullName(item);
7424  node.attr('item', itemname);
7425  node.find("a").text(item._name);
7426  item._isopen = true;
7427  painter.UpdateTreeNode(node, item);
7428  }
7429  return;
7430  }
7431  curr = ('_parent' in curr) ? curr['_parent'] : null;
7432  }
7433  });
7434  }
7435 
7436  JSROOT.HierarchyPainter.prototype.OpenRootFile = function(filepath, andThan) {
7437  var pthis = this;
7438 
7439  var f = new JSROOT.TFile(filepath, function(file) {
7440  if (file == null) return;
7441  // for the moment file is the only entry
7442  pthis.h = pthis.FileHierarchy(file);
7443 
7444  pthis.RefreshHtml();
7445 
7446  if (typeof andThan == 'function') andThan();
7447  });
7448  }
7449 
7450  JSROOT.HierarchyPainter.prototype.GetFileProp = function(itemname) {
7451  var item = this.Find(itemname);
7452  if (item == null) return null;
7453 
7454  var subname = item._name;
7455  while (item._parent != null) {
7456  item = item._parent;
7457  if ('_file' in item) {
7458  return {
7459  fileurl : item._file.fURL,
7460  itemname : subname
7461  };
7462  }
7463  subname = item._name + "/" + subname;
7464  }
7465 
7466  return null;
7467  }
7468 
7469  JSROOT.HierarchyPainter.prototype.CompleteOnline = function(ready_callback) {
7470  // method called at the moment when new description (h.json) is loaded
7471  // and before any graphical element is created
7472  // one can load extra scripts here or assign draw functions
7473  ready_callback();
7474  }
7475 
7476  JSROOT.HierarchyPainter.prototype.OpenOnline = function(server_address, user_callback) {
7477  if (!server_address) server_address = "";
7478 
7479  var painter = this;
7480 
7481  var req = JSROOT.NewHttpRequest(server_address + "h.json?compact=3", 'object', function(result) {
7482  painter.h = result;
7483  if (painter.h == null) return;
7484 
7485  // mark top hierarchy as online data and
7486  painter.h['_online'] = server_address;
7487 
7488  painter.h['_get'] = function(item, callback) {
7489 
7490  var url = painter.itemFullName(item);
7491  if (url.length > 0) url += "/";
7492  var h_get = ('_more' in item) || ('_doing_expand' in item);
7493  url += h_get ? 'h.json?compact=3' : 'root.json.gz?compact=3';
7494 
7495  var itemreq = JSROOT.NewHttpRequest(url, 'object', function(obj) {
7496  if ((obj != null) && !h_get && (item._name === "StreamerInfo")
7497  && (obj['_typename'] === 'TList'))
7498  obj['_typename'] = 'TStreamerInfoList';
7499 
7500  if (typeof callback == 'function')
7501  callback(item, obj);
7502  });
7503 
7504  itemreq.send(null);
7505  }
7506 
7507  painter.h['_expand'] = function(node, obj) {
7508  // central function for all expand
7509 
7510  if ((obj != null) && (node != null) && ('_childs' in obj)) {
7511  node._childs = obj._childs;
7512  obj._childs = null;
7513  return true;
7514  }
7515  return false;
7516  }
7517 
7518  painter.CompleteOnline(function() {
7519  if (painter.h != null)
7520  painter.RefreshHtml(true);
7521 
7522  if (typeof user_callback == 'function')
7523  user_callback(painter);
7524  });
7525  });
7526 
7527  req.send(null);
7528  }
7529 
7530  JSROOT.HierarchyPainter.prototype.GetOnlineProp = function(itemname) {
7531  var item = this.Find(itemname);
7532  if (item == null) return null;
7533 
7534  var subname = item._name;
7535  while (item._parent != null) {
7536  item = item._parent;
7537 
7538  if ('_online' in item) {
7539  return {
7540  server : item['_online'],
7541  itemname : subname
7542  };
7543  }
7544  subname = item._name + "/" + subname;
7545  }
7546 
7547  return null;
7548  }
7549 
7550  JSROOT.HierarchyPainter.prototype.FillOnlineMenu = function(menu, onlineprop, itemname) {
7551 
7552  var painter = this;
7553 
7554  var node = this.Find(itemname);
7555  var cando = this.CheckCanDo(node);
7556 
7557  if (cando.display)
7558  JSROOT.Painter.menuitem(menu, "Draw", function() { painter.display(itemname); });
7559 
7560  if (cando.expand || cando.display)
7561  JSROOT.Painter.menuitem(menu, "Expand", function() { painter.expand(itemname); });
7562 
7563  var drawurl = onlineprop.server + onlineprop.itemname + "/draw.htm";
7564  if (this.IsMonitoring())
7565  drawurl += "?monitoring=" + this.MonitoringInterval();
7566 
7567  if (cando.display)
7568  JSROOT.Painter.menuitem(menu, "Draw in new window", function() { window.open(drawurl); });
7569 
7570  if (cando.display)
7571  JSROOT.Painter.menuitem(menu, "Draw as png", function() {
7572  window.open(onlineprop.server + onlineprop.itemname + "/root.png?w=400&h=300&opt=");
7573  });
7574  }
7575 
7576  JSROOT.HierarchyPainter.prototype.ShowStreamerInfo = function(sinfo) {
7577  this.h = { _name : "StreamerInfo" };
7578  this.StreamerInfoHierarchy(this.h, sinfo);
7579  this.RefreshHtml();
7580  }
7581 
7582  JSROOT.HierarchyPainter.prototype.Adopt = function(h) {
7583  this.h = h;
7584  this.RefreshHtml();
7585  }
7586 
7587  JSROOT.HierarchyPainter.prototype.MonitoringInterval = function() {
7588  // returns interval
7589  var monitor = this['_monitoring_interval'];
7590  if (monitor == null) {
7591  monitor = JSROOT.GetUrlOption("monitoring");
7592  if ((monitor == "") || (monitor==null)) monitor = 3000;
7593  else monitor = parseInt(monitor);
7594  if ((monitor == NaN) || (monitor<=0)) monitor = 3000;
7595  this['_monitoring_interval'] = monitor;
7596  }
7597  return monitor;
7598  }
7599 
7600  JSROOT.HierarchyPainter.prototype.EnableMonitoring = function(on) {
7601  this['_monitoring_on'] = on;
7602  }
7603 
7604  JSROOT.HierarchyPainter.prototype.IsMonitoring = function() {
7605  return this['_monitoring_on'];
7606  }
7607 
7608  JSROOT.HierarchyPainter.prototype.tree_contextmenu = function(node, event) {
7609  event.preventDefault();
7610 
7611  var itemname = node.parent().attr('item');
7612 
7613  var hitem = this.Find(itemname);
7614  if (hitem==null) return;
7615 
7616  var cando = this.CheckCanDo(hitem);
7617 
7618  if (!cando.display && !cando.ctxt && (itemname!="")) return;
7619 
7620  var onlineprop = this.GetOnlineProp(itemname);
7621  var fileprop = this.GetFileProp(itemname);
7622 
7623  var menu = JSROOT.Painter.createmenu(event);
7624 
7625  function qualifyURL(url) {
7626  function escapeHTML(s) {
7627  return s.split('&').join('&amp;').split('<').join('&lt;').split('"').join('&quot;');
7628  }
7629  var el = document.createElement('div');
7630  el.innerHTML = '<a href="' + escapeHTML(url) + '">x</a>';
7631  return el.firstChild.href;
7632  }
7633 
7634  var painter = this;
7635 
7636  if (itemname == "") {
7637  var addr = "";
7638  if ('_online' in this.h) {
7639  addr = "/?";
7640  if (this.IsMonitoring())
7641  addr += "monitoring=" + this.MonitoringInterval();
7642  } else if ('_file' in this.h) {
7643  addr = JSROOT.source_dir + "index.htm?";
7644  addr += "file=" + this.h['_file'].fURL;
7645  }
7646 
7647  if (this['disp_kind']) {
7648  if (addr.length > 2) addr += "&";
7649  addr += "layout=" + this['disp_kind'].replace(/ /g, "");
7650  }
7651 
7652  var items = [];
7653 
7654  if (this['disp'] != null)
7655  this['disp'].ForEachPainter(function(painter) {
7656  if ('_hitemname' in painter)
7657  items.push(painter['_hitemname']);
7658  });
7659 
7660  if (items.length == 1) {
7661  if (addr.length > 2) addr += "&";
7662  addr += "item=" + items[0];
7663  } else if (items.length > 1) {
7664  if (addr.length > 2) addr += "&";
7665  addr += "items=" + JSON.stringify(items);
7666  }
7667 
7668  JSROOT.Painter.menuitem(menu, "Direct link", function() { window.open(addr); });
7669  JSROOT.Painter.menuitem(menu, "Only items", function() { window.open(addr + "&nobrowser"); });
7670  } else
7671  if (onlineprop != null) {
7672  this.FillOnlineMenu(menu, onlineprop, itemname);
7673  } else
7674  if (fileprop != null) {
7675  JSROOT.Painter.menuitem(menu, "Draw", function() { painter.display(itemname); });
7676  var filepath = qualifyURL(fileprop.fileurl);
7677  if (filepath.indexOf(JSROOT.source_dir) == 0)
7678  filepath = filepath.slice(JSROOT.source_dir.length);
7679  JSROOT.Painter.menuitem(menu, "Draw in new window", function() {
7680  window.open(JSROOT.source_dir + "index.htm?nobrowser&file=" + filepath + "&item=" + fileprop.itemname);
7681  });
7682  }
7683 
7684  JSROOT.Painter.menuitem(menu, "Close", function() {});
7685 
7686  return false;
7687  }
7688 
7689  JSROOT.HierarchyPainter.prototype.SetDisplay = function(kind, frameid) {
7690  this['disp_kind'] = kind;
7691  this['disp_frameid'] = frameid;
7692  }
7693 
7694  JSROOT.HierarchyPainter.prototype.clear = function() {
7695  if ('disp' in this)
7696  this['disp'].Reset();
7697  }
7698 
7699  JSROOT.HierarchyPainter.prototype.CreateDisplay = function(force) {
7700  if ('disp' in this) {
7701  if (!force && this['disp'].NumDraw() > 0) return true;
7702  this['disp'].Reset();
7703  delete this['disp'];
7704  }
7705 
7706  // check that we can found frame where drawing should be done
7707  if (document.getElementById(this['disp_frameid']) == null) return false;
7708 
7709  if (this['disp_kind'] == "tabs")
7710  this['disp'] = new JSROOT.TabsDisplay(this['disp_frameid']);
7711  else
7712  if (this['disp_kind'].search("grid") == 0)
7713  this['disp'] = new JSROOT.GridDisplay(this['disp_frameid'], this['disp_kind']);
7714  else
7715  this['disp'] = new JSROOT.CollapsibleDisplay(this['disp_frameid']);
7716 
7717  return true;
7718  }
7719 
7720  JSROOT.HierarchyPainter.prototype.CheckResize = function(force) {
7721  if ('disp' in this)
7722  this['disp'].CheckResize();
7723  }
7724 
7725  // ================================================================
7726 
7727  // JSROOT.MDIDisplay - class to manage multiple document interface for drawings
7728 
7729  JSROOT.MDIDisplay = function(frameid) {
7730  this.frameid = frameid;
7731  }
7732 
7733  JSROOT.MDIDisplay.prototype.ForEachFrame = function(userfunc, only_visible) {
7734  // method dedicated to iterate over existing panles
7735  // provided userfunc is called with arguemnts (frame)
7736 
7737  alert("ForEachFrame not implemented");
7738  }
7739 
7740  JSROOT.MDIDisplay.prototype.ForEachPainter = function(userfunc, only_visible) {
7741  // method dedicated to iterate over existing panles
7742  // provided userfunc is called with arguemnts (painter, frame)
7743 
7744  this.ForEachFrame(function(frame) {
7745  var dummy = new JSROOT.TObjectPainter();
7746  dummy.SetDivId($(frame).attr('id'), -1);
7747  dummy.ForEachPainter(function(painter) { userfunc(painter, frame); });
7748  }, only_visible);
7749  }
7750 
7751  JSROOT.MDIDisplay.prototype.NumDraw = function() {
7752  var cnt = 0;
7753  this.ForEachFrame(function() { cnt++; });
7754  return cnt;
7755  }
7756 
7757  JSROOT.MDIDisplay.prototype.FindFrame = function(searchtitle, force) {
7758  var found_frame = null;
7759 
7760  this.ForEachFrame(function(frame) {
7761  if (frame.prop('title') == searchtitle)
7762  found_frame = frame;
7763  });
7764 
7765  if ((found_frame == null) && force)
7766  found_frame = this.CreateFrame(searchtitle);
7767 
7768  return found_frame;
7769  }
7770 
7771  JSROOT.MDIDisplay.prototype.FindPainter = function(title) {
7772  var res = null;
7773  this.ForEachPainter(function(p,f) {
7774  if ((res==null) && (f.prop('title')==title)) res = p;
7775  });
7776  return res;
7777  }
7778 
7779  JSROOT.MDIDisplay.prototype.ActivateFrame = function(frame) {
7780  // do nothing by default
7781  }
7782 
7783  JSROOT.MDIDisplay.prototype.CheckResize = function() {
7784  this.ForEachPainter(function(painter) {
7785  if (('_hitemname' in painter) && (typeof painter['CheckResize'] == 'function'))
7786  painter.CheckResize();
7787  });
7788  }
7789 
7790  JSROOT.MDIDisplay.prototype.Reset = function() {
7791  this.ForEachPainter(function(painter) {
7792  if (('_hitemname' in painter) && (typeof painter['Clenaup'] == 'function'))
7793  painter.Clenaup();
7794  });
7795 
7796  document.getElementById(this.frameid).innerHTML = '';
7797  }
7798 
7799  JSROOT.MDIDisplay.prototype.Draw = function(title, obj, drawopt) {
7800  // draw object with specified options
7801  if (!obj) return;
7802 
7803  var painter = this.FindPainter(title);
7804  var frame = this.FindFrame(title);
7805 
7806  if (painter!=null) {
7807  this.ActivateFrame(frame);
7808  painter.RedrawObject(obj);
7809  return painter;
7810  }
7811 
7812  if (!JSROOT.canDraw(obj['_typename'], drawopt)) return;
7813 
7814  if (frame == null)
7815  frame = this.CreateFrame(title);
7816 
7817  this.ActivateFrame(frame);
7818 
7819  return JSROOT.draw($(frame).attr("id"), obj, drawopt);
7820  }
7821 
7822  // ==================================================
7823 
7824  JSROOT.CloseCollapsible = function(e, el) {
7825  var sel = $(el)[0].textContent;
7826  if (typeof (sel) == 'undefined')
7827  return;
7828  sel.replace(' x', '');
7829  sel.replace(';', '');
7830  sel.replace(' ', '');
7831  $(el).next().andSelf().remove();
7832  e.stopPropagation();
7833  };
7834 
7835  JSROOT.CollapsibleDisplay = function(frameid) {
7836  JSROOT.MDIDisplay.call(this, frameid);
7837  this.cnt = 0; // use to count newly created frames
7838  }
7839 
7840  JSROOT.CollapsibleDisplay.prototype = Object.create(JSROOT.MDIDisplay.prototype);
7841 
7842  JSROOT.CollapsibleDisplay.prototype.ForEachFrame = function(userfunc, only_visible) {
7843  var topid = this.frameid + '_collapsible';
7844 
7845  if (document.getElementById(topid) == null) return;
7846 
7847  if (typeof userfunc != 'function') return;
7848 
7849  $('#' + topid + ' .collapsible_draw').each(function() {
7850 
7851  // check if only visible specified
7852  if (only_visible && $(this).is(":hidden")) return;
7853 
7854  userfunc($(this));
7855  });
7856  }
7857 
7858  JSROOT.CollapsibleDisplay.prototype.ActivateFrame = function(frame) {
7859  if ($(frame).is(":hidden")) {
7860  $(frame).prev().toggleClass("ui-accordion-header-active ui-state-active ui-state-default ui-corner-bottom")
7861  .find("> .ui-icon").toggleClass("ui-icon-triangle-1-e ui-icon-triangle-1-s").end()
7862  .next().toggleClass("ui-accordion-content-active").slideDown(0);
7863  }
7864  $(frame).prev()[0].scrollIntoView();
7865  }
7866 
7867  JSROOT.CollapsibleDisplay.prototype.CreateFrame = function(title) {
7868 
7869  var topid = this.frameid + '_collapsible';
7870 
7871  if (document.getElementById(topid) == null)
7872  $("#right-div").append('<div id="'+ topid + '" class="ui-accordion ui-accordion-icons ui-widget ui-helper-reset" style="overflow:auto; overflow-y:scroll; height:100%"></div>');
7873 
7874  var hid = topid + "_sub" + this.cnt++;
7875  var uid = hid + "h";
7876 
7877  var entryInfo = "<h5 id=\"" + uid + "\"><a> " + title + "</a>&nbsp; </h5>\n";
7878  entryInfo += "<div class='collapsible_draw' id='" + hid + "'></div>\n";
7879  $("#" + topid).append(entryInfo);
7880 
7881  $('#' + uid)
7882  .addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-top ui-corner-bottom")
7883  .hover(function() { $(this).toggleClass("ui-state-hover"); })
7884  .prepend('<span class="ui-icon ui-icon-triangle-1-e"></span>')
7885  .append('<button type="button" class="closeButton" title="close canvas" onclick="JSROOT.CloseCollapsible(event, \'#'
7886  + uid + '\')"><img class="img_remove" src="" alt=""/></button>')
7887  .click( function() {
7888  $(this).toggleClass("ui-accordion-header-active ui-state-active ui-state-default ui-corner-bottom")
7889  .find("> .ui-icon").toggleClass("ui-icon-triangle-1-e ui-icon-triangle-1-s")
7890  .end().next().toggleClass("ui-accordion-content-active").slideToggle(0);
7891  return false;
7892  })
7893  .next()
7894  .addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom")
7895  .hide();
7896 
7897  $('#' + uid)
7898  .toggleClass("ui-accordion-header-active ui-state-active ui-state-default ui-corner-bottom")
7899  .find("> .ui-icon").toggleClass("ui-icon-triangle-1-e ui-icon-triangle-1-s").end().next()
7900  .toggleClass("ui-accordion-content-active").slideToggle(0);
7901 
7902  // $('#'+uid)[0].scrollIntoView();
7903 
7904  $("#" + hid).prop('title', title);
7905 
7906  return $("#" + hid);
7907  }
7908 
7909  // ================================================
7910 
7911  JSROOT.TabsDisplay = function(frameid) {
7912  JSROOT.MDIDisplay.call(this, frameid);
7913  this.cnt = 0;
7914  }
7915 
7916  JSROOT.TabsDisplay.prototype = Object.create(JSROOT.MDIDisplay.prototype);
7917 
7918  JSROOT.TabsDisplay.prototype.ForEachFrame = function(userfunc, only_visible) {
7919  var topid = this.frameid + '_tabs';
7920 
7921  if (document.getElementById(topid) == null) return;
7922 
7923  if (typeof userfunc != 'function') return;
7924 
7925  var cnt = -1;
7926  var active = $('#' + topid).tabs("option", "active");
7927 
7928  $('#' + topid + ' .tabs_draw').each(function() {
7929  // check if only_visible specified
7930  if (only_visible && (cnt++ != active)) return;
7931 
7932  userfunc($(this));
7933  });
7934  }
7935 
7936  JSROOT.TabsDisplay.prototype.ActivateFrame = function(frame) {
7937  var cnt = 0, id = -1;
7938  this.ForEachFrame(function(fr) {
7939  if ($(fr).attr('id') == frame.attr('id')) id = cnt;
7940  cnt++;
7941  });
7942 
7943  $('#' + this.frameid + "_tabs").tabs("option", "active", id);
7944  }
7945 
7946  JSROOT.TabsDisplay.prototype.CreateFrame = function(title) {
7947  var topid = this.frameid + '_tabs';
7948 
7949  var hid = topid + "_sub" + this.cnt++;
7950 
7951  var li = '<li><a href="#' + hid + '">' + title
7952  + '</a><span class="ui-icon ui-icon-close" role="presentation">Remove Tab</span></li>';
7953  var cont = '<div class="tabs_draw" id="' + hid + '"></div>';
7954 
7955  if (document.getElementById(topid) == null) {
7956  $("#" + this.frameid).append('<div id="' + topid + '">' + ' <ul>' + li + ' </ul>' + cont + '</div>');
7957 
7958  var tabs = $("#" + topid).tabs({ heightStyle : "fill" });
7959 
7960  tabs.delegate("span.ui-icon-close", "click", function() {
7961  var panelId = $(this).closest("li").remove().attr("aria-controls");
7962  $("#" + panelId).remove();
7963  tabs.tabs("refresh");
7964  });
7965  } else {
7966 
7967  // var tabs = $("#tabs").tabs();
7968 
7969  $("#" + topid).find(".ui-tabs-nav").append(li);
7970  $("#" + topid).append(cont);
7971  $("#" + topid).tabs("refresh");
7972  $("#" + topid).tabs("option", "active", -1);
7973  }
7974  $('#' + hid).empty();
7975  $('#' + hid).prop('title', title);
7976  return $('#' + hid);
7977  }
7978 
7979  // ================================================
7980 
7981  JSROOT.GridDisplay = function(frameid, sizex, sizey) {
7982  // create grid display object
7983  // one could use followinf arguments
7984  // new JSROOT.GridDisplay('yourframeid','4x4');
7985  // new JSROOT.GridDisplay('yourframeid','3x2');
7986  // new JSROOT.GridDisplay('yourframeid', 3, 4);
7987 
7988  JSROOT.MDIDisplay.call(this, frameid);
7989  this.cnt = 0;
7990  if (typeof sizex == "string") {
7991  if (sizex.search("grid") == 0)
7992  sizex = sizex.slice(4).trim();
7993 
7994  var separ = sizex.search("x");
7995 
7996  if (separ > 0) {
7997  sizey = parseInt(sizex.slice(separ + 1));
7998  sizex = parseInt(sizex.slice(0, separ));
7999  } else {
8000  sizex = parseInt(sizex);
8001  sizey = sizex;
8002  }
8003 
8004  if (sizex == NaN)
8005  sizex = 3;
8006  if (sizey == NaN)
8007  sizey = 3;
8008  }
8009 
8010  if (!sizex)
8011  sizex = 3;
8012  if (!sizey)
8013  sizey = sizex;
8014  this.sizex = sizex;
8015  this.sizey = sizey;
8016  }
8017 
8018  JSROOT.GridDisplay.prototype = Object.create(JSROOT.MDIDisplay.prototype);
8019 
8020  JSROOT.GridDisplay.prototype.IsSingle = function() {
8021  return (this.sizex <= 1) && (this.sizey <= 1);
8022  }
8023 
8024  JSROOT.GridDisplay.prototype.ForEachFrame = function(userfunc, only_visible) {
8025  if (typeof userfunc != 'function') return;
8026 
8027  if (this.IsSingle()) {
8028  var elem = $("#"+this.frameid);
8029  if (elem.prop('title')!=null)
8030  userfunc(elem);
8031  return;
8032  }
8033 
8034  var topid = this.frameid + '_grid';
8035 
8036  if (document.getElementById(topid) == null) return;
8037 
8038  for (var cnt = 0; cnt < this.sizex * this.sizey; cnt++) {
8039 
8040  var elem = $( "#" + topid + "_" + cnt);
8041 
8042  if (elem.prop('title')!="")
8043  userfunc(elem);
8044  }
8045  }
8046 
8047  JSROOT.GridDisplay.prototype.CreateFrame = function(title) {
8048 
8049  var hid = this.frameid;
8050 
8051  if (!this.IsSingle()) {
8052  var topid = this.frameid + '_grid';
8053  if (document.getElementById(topid) == null) {
8054 
8055  var main = $("#" + this.frameid);
8056 
8057  var h = Math.floor(main.height() / this.sizey);
8058  var w = Math.floor(main.width() / this.sizex);
8059 
8060  var content = "<table id='" + topid + "' style='width:100%; height:100%; table-layout:fixed'>";
8061  var cnt = 0;
8062  for (var i = 0; i < this.sizey; i++) {
8063  content += "<tr>";
8064  for (var j = 0; j < this.sizex; j++)
8065  content += "<td><div id='" + topid + "_" + cnt++ + "'></div></td>";
8066  content += "</tr>";
8067  }
8068  content += "</table>";
8069 
8070  main.empty();
8071  main.append(content);
8072 
8073  main.find("[id^=" + this.frameid + "_grid_]").width(w).height(h);
8074 // .droppable({ accepted: ".h_item", drop: function(event, ui) { console.log("drop!"); } });
8075  }
8076 
8077  hid = topid + "_" + this.cnt;
8078  if (++this.cnt >= this.sizex * this.sizey) this.cnt = 0;
8079  }
8080 
8081  $("#" + hid).empty();
8082  $("#" + hid).prop('title', title);
8083 
8084  return $('#' + hid);
8085  }
8086 
8087  JSROOT.GridDisplay.prototype.Reset = function() {
8088  JSROOT.MDIDisplay.prototype.Reset.call(this);
8089  if (this.IsSingle())
8090  $("#" + this.frameid).prop('title', null);
8091  this.cnt = 0;
8092  }
8093 
8094  JSROOT.GridDisplay.prototype.CheckResize = function() {
8095 
8096  var main = $("#" + this.frameid);
8097 
8098  var h = Math.floor(main.height() / this.sizey);
8099  var w = Math.floor(main.width() / this.sizex);
8100 
8101  // console.log("big width = " + main.width() + " height = " + main.height());
8102 
8103  // set height for all table cells, it is not done automatically by browser
8104  // for (var cnt=0;cnt<this.sizex*this.sizey;cnt++)
8105  // $("#" + this.frameid + "_grid_"+cnt).height(h);
8106 
8107  // $("[id^=" + this.frameid + "_grid_]").height(h).width(w);
8108 
8109  main.find("[id^=" + this.frameid + "_grid_]").height(h).width(w);
8110 
8111  JSROOT.MDIDisplay.prototype.CheckResize.call(this);
8112  }
8113 
8114  JSROOT.RegisterForResize = function(handle, delay) {
8115  // function used to react on browser window resize event
8116  // While many resize events could come in short time,
8117  // resize will be handled with delay after last resize event
8118  // handle can be function or object with CheckResize function
8119  // one could specify delay after which resize event will be handled
8120 
8121  var myInterval = null;
8122  var myCounter = -1;
8123  var myPeriod = delay ? delay / 2.5 : 100;
8124  if (myPeriod < 20) myPeriod = 20;
8125 
8126  function ResizeTimer() {
8127  if (myCounter < 0) return;
8128  myCounter += 1;
8129  if (myCounter < 3) return;
8130 
8131  if (myInterval != null) {
8132  clearInterval(myInterval);
8133  myInterval = null;
8134  }
8135  myCounter = -1;
8136 
8137  document.body.style.cursor = 'wait';
8138 
8139  if (typeof handle == 'function') handle();
8140  else if ((typeof handle == 'object') && (typeof handle['CheckResize'] == 'function'))
8141  handle.CheckResize();
8142 
8143  document.body.style.cursor = 'auto';
8144  }
8145 
8146  function ProcessResize() {
8147  if (myInterval == null) {
8148  myInterval = setInterval(ResizeTimer, myPeriod);
8149  }
8150  myCounter = 0;
8151  }
8152 
8153  window.addEventListener('resize', ProcessResize);
8154  }
8155 
8156  JSROOT.addDrawFunc("TCanvas", JSROOT.Painter.drawCanvas);
8157  JSROOT.addDrawFunc("TPad", JSROOT.Painter.drawPad);
8158  JSROOT.addDrawFunc("TFrame", JSROOT.Painter.drawFrame);
8159  JSROOT.addDrawFunc("TLegend", JSROOT.Painter.drawLegend);
8160  JSROOT.addDrawFunc("TPaveText", JSROOT.Painter.drawPaveText);
8161  JSROOT.addDrawFunc("TPaveStats", JSROOT.Painter.drawPaveText);
8162  JSROOT.addDrawFunc("TLatex", JSROOT.Painter.drawText);
8163  JSROOT.addDrawFunc("TText", JSROOT.Painter.drawText);
8164  JSROOT.addDrawFunc("TPaveLabel", JSROOT.Painter.drawText);
8165  JSROOT.addDrawFunc(/^TH1/, JSROOT.Painter.drawHistogram1D);
8166  JSROOT.addDrawFunc("TProfile", JSROOT.Painter.drawHistogram1D);
8167  JSROOT.addDrawFunc(/^TH2/, JSROOT.Painter.drawHistogram2D);
8168  JSROOT.addDrawFunc(/^TH3/, JSROOT.Painter.drawHistogram3D);
8169  JSROOT.addDrawFunc("THStack", JSROOT.Painter.drawHStack);
8170  JSROOT.addDrawFunc("TF1", JSROOT.Painter.drawFunction);
8171  JSROOT.addDrawFunc(/^TGraph/, JSROOT.Painter.drawGraph);
8172  JSROOT.addDrawFunc("TCutG", JSROOT.Painter.drawGraph);
8173  JSROOT.addDrawFunc(/^RooHist/, JSROOT.Painter.drawGraph);
8174  JSROOT.addDrawFunc(/^RooCurve/, JSROOT.Painter.drawGraph);
8175  JSROOT.addDrawFunc("TMultiGraph", JSROOT.Painter.drawMultiGraph);
8176  JSROOT.addDrawFunc("TStreamerInfoList", JSROOT.Painter.drawStreamerInfo);
8177 
8178  JSROOT.getDrawFunc = function(classname) {
8179  if (typeof classname != 'string') return null;
8180 
8181  for (var i in JSROOT.fDrawFunc) {
8182  if ((typeof JSROOT.fDrawFunc[i].name) === "string") {
8183  if (JSROOT.fDrawFunc[i].name == classname) return JSROOT.fDrawFunc[i].func;
8184  } else {
8185  if (classname.match(JSROOT.fDrawFunc[i].name)) return JSROOT.fDrawFunc[i].func;
8186  }
8187  }
8188  return null;
8189  }
8190 
8191  JSROOT.canDraw = function(classname) {
8192  return JSROOT.getDrawFunc(classname) != null;
8193  }
8194 
8198  JSROOT.draw = function(divid, obj, opt) {
8199  if ((typeof obj != 'object') || (!('_typename' in obj))) return null;
8200 
8201  var draw_func = JSROOT.getDrawFunc(obj['_typename']);
8202 
8203  if (draw_func==null) return null;
8204 
8205  return draw_func(divid, obj, opt);
8206  }
8207 
8213  JSROOT.redraw = function(divid, obj, opt) {
8214  if (obj==null) return;
8215 
8216  var can = d3.select("#" + divid + " .root_canvas");
8217  var can_painter = can.node() ? can.node()['pad_painter'] : null;
8218 
8219  if (can_painter != null) {
8220  if (obj._typename=="TCanvas") {
8221  can_painter.RedrawObject(obj);
8222  return can_painter;
8223  }
8224 
8225  for (var i in can_painter.painters) {
8226  var obj0 = can_painter.painters[i].GetObject();
8227 
8228  if ((obj0 != null) && (obj0._typename == obj._typename))
8229  if (can_painter.painters[i].UpdateObject(obj)) {
8230  can_painter.RedrawPad();
8231  return can_painter.painters[i];
8232  }
8233  }
8234  }
8235 
8236  if (can_painter)
8237  console.log("Cannot find painter to update object of type " + obj._typename);
8238 
8239  $("#"+divid).empty();
8240  return JSROOT.draw(divid, obj, opt);
8241  }
8242 
8243 })();
8244 
8245 // JSRootPainter.js ends
8246 
8247 // example of user code for streamer and painter
8248 
8249 /*
8250 
8251  (function(){
8252 
8253  Amore_String_Streamer = function(buf, obj, prop, streamer) {
8254  console.log("read property " + prop + " of typename " + streamer[prop]['typename']);
8255  obj[prop] = buf.ReadTString();
8256  }
8257 
8258  Amore_Draw = function(divid, obj, opt) { // custom draw function.
8259  return JSROOT.draw(divid, obj['fVal'], opt);
8260  }
8261 
8262  JSROOT.addUserStreamer("amore::core::String_t", Amore_String_Streamer);
8263 
8264  JSROOT.addDrawFunc("amore::core::MonitorObjectHisto<TH1F>", Amore_Draw);
8265 
8266 })();
8267 
8268 */
8269