otsdaq_utilities  v2_05_02_indev
d3_tooltip.js
1 // d3.tip
2 // Copyright (c) 2013 Justin Palmer
3 //
4 // Tooltips for d3.js SVG visualizations
5 
6 // Public - contructs a new tooltip
7 //
8 // Returns a tip
9 d3.tip = function() {
10  var direction = d3_tip_direction,
11  offset = d3_tip_offset,
12  html = d3_tip_html,
13  node = initNode(),
14  svg = null,
15  point = null,
16  target = null
17 
18  function tip(vis) {
19  svg = getSVGNode(vis)
20  point = svg.createSVGPoint()
21  document.body.appendChild(node)
22  }
23 
24  // Public - show the tooltip on the screen
25  //
26  // Returns a tip
27  tip.show = function() {
28  var args = Array.prototype.slice.call(arguments)
29  if(args[args.length - 1] instanceof SVGElement) target = args.pop()
30 
31  var content = html.apply(this, args),
32  poffset = offset.apply(this, args),
33  dir = direction.apply(this, args),
34  nodel = d3.select(node), i = 0,
35  coords
36 
37  nodel.html(content)
38  .style({ opacity: 1, 'pointer-events': 'all' })
39 
40  while(i--) nodel.classed(directions[i], false)
41  coords = direction_callbacks.get(dir).apply(this)
42  nodel.classed(dir, true).style({
43  top: (coords.top + poffset[0]) + 'px',
44  left: (coords.left + poffset[1]) + 'px'
45  })
46 
47  return tip
48  }
49 
50  // Public - hide the tooltip
51  //
52  // Returns a tip
53  tip.hide = function() {
54  nodel = d3.select(node)
55  nodel.style({ opacity: 0, 'pointer-events': 'none' })
56  return tip
57  }
58 
59  // Public: Proxy attr calls to the d3 tip container. Sets or gets attribute value.
60  //
61  // n - name of the attribute
62  // v - value of the attribute
63  //
64  // Returns tip or attribute value
65  tip.attr = function(n, v) {
66  if (arguments.length < 2 && typeof n === 'string') {
67  return d3.select(node).attr(n)
68  } else {
69  var args = Array.prototype.slice.call(arguments)
70  d3.selection.prototype.attr.apply(d3.select(node), args)
71  }
72 
73  return tip
74  }
75 
76  // Public: Proxy style calls to the d3 tip container. Sets or gets a style value.
77  //
78  // n - name of the property
79  // v - value of the property
80  //
81  // Returns tip or style property value
82  tip.style = function(n, v) {
83  if (arguments.length < 2 && typeof n === 'string') {
84  return d3.select(node).style(n)
85  } else {
86  var args = Array.prototype.slice.call(arguments)
87  d3.selection.prototype.style.apply(d3.select(node), args)
88  }
89 
90  return tip
91  }
92 
93  // Public: Set or get the direction of the tooltip
94  //
95  // v - One of n(north), s(south), e(east), or w(west), nw(northwest),
96  // sw(southwest), ne(northeast) or se(southeast)
97  //
98  // Returns tip or direction
99  tip.direction = function(v) {
100  if (!arguments.length) return direction
101  direction = v == null ? v : d3.functor(v)
102 
103  return tip
104  }
105 
106  // Public: Sets or gets the offset of the tip
107  //
108  // v - Array of [x, y] offset
109  //
110  // Returns offset or
111  tip.offset = function(v) {
112  if (!arguments.length) return offset
113  offset = v == null ? v : d3.functor(v)
114 
115  return tip
116  }
117 
118  // Public: sets or gets the html value of the tooltip
119  //
120  // v - String value of the tip
121  //
122  // Returns html value or tip
123  tip.html = function(v) {
124  if (!arguments.length) return html
125  html = v == null ? v : d3.functor(v)
126 
127  return tip
128  }
129 
130  function d3_tip_direction() { return 'n' }
131  function d3_tip_offset() { return [0, 0] }
132  function d3_tip_html() { return ' ' }
133 
134  var direction_callbacks = d3.map({
135  n: direction_n,
136  s: direction_s,
137  e: direction_e,
138  w: direction_w,
139  nw: direction_nw,
140  ne: direction_ne,
141  sw: direction_sw,
142  se: direction_se
143  }),
144 
145  directions = direction_callbacks.keys()
146 
147  function direction_n() {
148  var bbox = getScreenBBox()
149  return {
150  top: bbox.n.y - node.offsetHeight,
151  left: bbox.n.x - node.offsetWidth / 2
152  }
153  }
154 
155  function direction_s() {
156  var bbox = getScreenBBox()
157  return {
158  top: bbox.s.y,
159  left: bbox.s.x - node.offsetWidth / 2
160  }
161  }
162 
163  function direction_e() {
164  var bbox = getScreenBBox()
165  return {
166  top: bbox.e.y - node.offsetHeight / 2,
167  left: bbox.e.x
168  }
169  }
170 
171  function direction_w() {
172  var bbox = getScreenBBox()
173  return {
174  top: bbox.w.y - node.offsetHeight / 2,
175  left: bbox.w.x - node.offsetWidth
176  }
177  }
178 
179  function direction_nw() {
180  var bbox = getScreenBBox()
181  return {
182  top: bbox.nw.y - node.offsetHeight,
183  left: bbox.nw.x - node.offsetWidth
184  }
185  }
186 
187  function direction_ne() {
188  var bbox = getScreenBBox()
189  return {
190  top: bbox.ne.y - node.offsetHeight,
191  left: bbox.ne.x
192  }
193  }
194 
195  function direction_sw() {
196  var bbox = getScreenBBox()
197  return {
198  top: bbox.sw.y,
199  left: bbox.sw.x - node.offsetWidth
200  }
201  }
202 
203  function direction_se() {
204  var bbox = getScreenBBox()
205  return {
206  top: bbox.se.y,
207  left: bbox.e.x
208  }
209  }
210 
211  function initNode() {
212  var node = d3.select(document.createElement('div'))
213  node.style({
214  position: 'absolute',
215  opacity: 0,
216  pointerEvents: 'none',
217  boxSizing: 'border-box'
218  })
219 
220  return node.node()
221  }
222 
223  function getSVGNode(el) {
224  el = el.node()
225  if(el.tagName.toLowerCase() == 'svg')
226  return el
227 
228  return el.ownerSVGElement
229  }
230 
231  // Private - gets the screen coordinates of a shape
232  //
233  // Given a shape on the screen, will return an SVGPoint for the directions
234  // n(north), s(south), e(east), w(west), ne(northeast), se(southeast), nw(northwest),
235  // sw(southwest).
236  //
237  // +-+-+
238  // | |
239  // + +
240  // | |
241  // +-+-+
242  //
243  // Returns an Object {n, s, e, w, nw, sw, ne, se}
244  function getScreenBBox() {
245  var targetel = target || d3.event.target,
246  bbox = {},
247  matrix = targetel.getScreenCTM(),
248  tbbox = targetel.getBBox(),
249  width = tbbox.width,
250  height = tbbox.height,
251  x = tbbox.x,
252  y = tbbox.y,
253  scrollTop = document.documentElement.scrollTop || document.body.scrollTop,
254  scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft
255 
256 
257  point.x = x + scrollLeft
258  point.y = y + scrollTop
259  bbox.nw = point.matrixTransform(matrix)
260  point.x += width
261  bbox.ne = point.matrixTransform(matrix)
262  point.y += height
263  bbox.se = point.matrixTransform(matrix)
264  point.x -= width
265  bbox.sw = point.matrixTransform(matrix)
266  point.y -= height / 2
267  bbox.w = point.matrixTransform(matrix)
268  point.x += width
269  bbox.e = point.matrixTransform(matrix)
270  point.x -= width / 2
271  point.y -= height / 2
272  bbox.n = point.matrixTransform(matrix)
273  point.y += height
274  bbox.s = point.matrixTransform(matrix)
275 
276  return bbox
277  }
278 
279  return tip
280 };