function nodeFromString(str) { var div = document.createElement("div"); div.innerHTML = str; return div.firstChild; } function make_hover_css(pos) { var pretty = window.innerWidth > 600; var padding = pretty? 18 : 12; var outer_padding = pretty ? 18 : 0; var bbox = document.querySelector("body").getBoundingClientRect(); var left = pos[0] - bbox.left, top = pos[1] - bbox.top; var width = Math.min(window.innerWidth-2*outer_padding, 648); left = Math.min(left, window.innerWidth-width-outer_padding); width = width - 2*padding; return (`position: absolute; background-color: #FFF; opacity: 0.95; max-width: ${width}px; top: ${top}px; left: ${left}px; border: 1px solid rgba(0, 0, 0, 0.25); padding: ${padding}px; border-radius: ${pretty? 3 : 0}px; box-shadow: 0px 2px 10px 2px rgba(0, 0, 0, 0.2); z-index: ${1e6};`); } function DtHoverBox(div_id) { this.div = document.querySelector("#"+div_id); this.visible = false; this.bindDivEvents(); DtHoverBox.box_map[div_id] = this; } DtHoverBox.box_map = {}; DtHoverBox.get_box = function get_box(div_id) { if (div_id in DtHoverBox.box_map) { return DtHoverBox.box_map[div_id]; } else { return new DtHoverBox(div_id); } } DtHoverBox.prototype.show = function show(pos){ this.visible = true; this.div.setAttribute("style", make_hover_css(pos) ); for (var box_id in DtHoverBox.box_map) { var box = DtHoverBox.box_map[box_id]; if (box != this) box.hide(); } } DtHoverBox.prototype.showAtNode = function showAtNode(node){ var bbox = node.getBoundingClientRect(); this.show([bbox.right, bbox.bottom]); } DtHoverBox.prototype.hide = function hide(){ this.visible = false; if (this.div) this.div.setAttribute("style", "display:none"); if (this.timeout) clearTimeout(this.timeout); } DtHoverBox.prototype.stopTimeout = function stopTimeout() { if (this.timeout) clearTimeout(this.timeout); } DtHoverBox.prototype.extendTimeout = function extendTimeout(T) { //console.log("extend", T) var this_ = this; this.stopTimeout(); this.timeout = setTimeout(function(){this_.hide();}.bind(this), T); } // Bind events to a link to open this box DtHoverBox.prototype.bind = function bind(node) { if (typeof node == "string"){ node = document.querySelector(node); } node.addEventListener("mouseover", function(){ if (!this.visible) this.showAtNode(node); this.stopTimeout(); }.bind(this)); node.addEventListener("mouseout", function(){this.extendTimeout(250);}.bind(this)); node.addEventListener("touchstart", function(e) { if (this.visible) { this.hide(); } else { this.showAtNode(node); } // Don't trigger body touchstart event when touching link e.stopPropagation(); }.bind(this)); } DtHoverBox.prototype.bindDivEvents = function bindDivEvents(){ // For mice, same behavior as hovering on links this.div.addEventListener("mouseover", function(){ if (!this.visible) this.showAtNode(node); this.stopTimeout(); }.bind(this)); this.div.addEventListener("mouseout", function(){this.extendTimeout(250);}.bind(this)); // Don't trigger body touchstart event when touching within box this.div.addEventListener("touchstart", function(e){e.stopPropagation();}); // Close box when touching outside box document.body.addEventListener("touchstart", function(){this.hide();}.bind(this)); } var hover_es = document.querySelectorAll("span[data-hover-ref]"); hover_es = [].slice.apply(hover_es); hover_es.forEach(function(e,n){ var ref_id = e.getAttribute("data-hover-ref"); DtHoverBox.get_box(ref_id).bind(e); })