(function(exports) { var bootstrap = (typeof exports.bootstrap === "object") ? exports.bootstrap : (exports.bootstrap = {}); bootstrap.tooltip = function() { var tooltip = function(selection) { selection.each(setup); }, animation = d3.functor(false), html = d3.functor(false), title = function() { var title = this.getAttribute("data-original-title"); if (title) { return title; } else { title = this.getAttribute("title"); this.removeAttribute("title"); this.setAttribute("data-original-title", title); } return title; }, over = "mouseenter.tooltip", out = "mouseleave.tooltip", placements = "top left bottom right".split(" "), placement = d3.functor("top"); tooltip.title = function(_) { if (arguments.length) { title = d3.functor(_); return tooltip; } else { return title; } }; tooltip.html = function(_) { if (arguments.length) { html = d3.functor(_); return tooltip; } else { return html; } }; tooltip.placement = function(_) { if (arguments.length) { placement = d3.functor(_); return tooltip; } else { return placement; } }; tooltip.show = function(selection) { selection.each(show); }; tooltip.hide = function(selection) { selection.each(hide); }; tooltip.toggle = function(selection) { selection.each(toggle); }; tooltip.destroy = function(selection) { selection .on(over, null) .on(out, null) .attr("title", function() { return this.getAttribute("data-original-title") || this.getAttribute("title"); }) .attr("data-original-title", null) .select(".tooltip") .remove(); }; function setup() { var root = d3.select(this), animate = animation.apply(this, arguments), tip = root.append("div") .attr("class", "tooltip"); if (animate) { tip.classed("fade", true); } // TODO "inside" checks? tip.append("div") .attr("class", "tooltip-arrow"); tip.append("div") .attr("class", "tooltip-inner"); var place = placement.apply(this, arguments); tip.classed(place, true); root.on(over, show); root.on(out, hide); } function show() { var root = d3.select(this), content = title.apply(this, arguments), tip = root.select(".tooltip") .classed("in", true), markup = html.apply(this, arguments), innercontent = tip.select(".tooltip-inner")[markup ? "html" : "text"](content), place = placement.apply(this, arguments), outer = getPosition(root.node()), inner = getPosition(tip.node()), pos; switch (place) { case "top": pos = {x: outer.x + (outer.w - inner.w) / 2, y: outer.y - inner.h}; break; case "right": pos = {x: outer.x + outer.w, y: outer.y + (outer.h - inner.h) / 2}; break; case "left": pos = {x: outer.x - inner.w, y: outer.y + (outer.h - inner.h) / 2}; break; case "bottom": pos = {x: Math.max(0, outer.x + (outer.w - inner.w) / 2), y: outer.y + outer.h}; break; } tip.style(pos ? {left: ~~pos.x + "px", top: ~~pos.y + "px"} : {left: null, top: null}); this.tooltipVisible = true; } function hide() { d3.select(this).select(".tooltip") .classed("in", false); this.tooltipVisible = false; } function toggle() { if (this.tooltipVisible) { hide.apply(this, arguments); } else { show.apply(this, arguments); } } return tooltip; }; function getPosition(node) { var mode = d3.select(node).style('position'); if (mode === 'absolute' || mode === 'static') { return { x: node.offsetLeft, y: node.offsetTop, w: node.offsetWidth, h: node.offsetHeight }; } else { return { x: 0, y: 0, w: node.offsetWidth, h: node.offsetHeight }; } } })(this);