/* * Copyright (C) 2013, 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ WebInspector.HoverMenu = class HoverMenu extends WebInspector.Object { constructor(delegate) { super(); this.delegate = delegate; this._element = document.createElement("div"); this._element.className = "hover-menu"; this._element.addEventListener("transitionend", this, true); this._outlineElement = this._element.appendChild(document.createElementNS("http://www.w3.org/2000/svg", "svg")); this._button = this._element.appendChild(document.createElement("img")); this._button.addEventListener("click", this); } // Public get element() { return this._element; } present(rects) { this._outlineElement.textContent = ""; document.body.appendChild(this._element); this._drawOutline(rects); this._element.classList.add(WebInspector.HoverMenu.VisibleClassName); window.addEventListener("scroll", this, true); } dismiss(discrete) { if (this._element.parentNode !== document.body) return; if (discrete) this._element.remove(); this._element.classList.remove(WebInspector.HoverMenu.VisibleClassName); window.removeEventListener("scroll", this, true); } // Protected handleEvent(event) { switch (event.type) { case "scroll": if (!this._element.contains(event.target)) this.dismiss(true); break; case "click": this._handleClickEvent(event); break; case "transitionend": if (!this._element.classList.contains(WebInspector.HoverMenu.VisibleClassName)) this._element.remove(); break; } } // Private _handleClickEvent(event) { if (this.delegate && typeof this.delegate.hoverMenuButtonWasPressed === "function") this.delegate.hoverMenuButtonWasPressed(this); } _drawOutline(rects) { var buttonWidth = this._button.width; var buttonHeight = this._button.height; // Add room for the button on the last line. var lastRect = rects.pop(); lastRect.size.width += buttonWidth; rects.push(lastRect); if (rects.length === 1) this._drawSingleLine(rects[0]); else if (rects.length === 2 && rects[0].minX() >= rects[1].maxX()) this._drawTwoNonOverlappingLines(rects); else this._drawOverlappingLines(rects); var bounds = WebInspector.Rect.unionOfRects(rects).pad(3); // padding + 1/2 stroke-width var style = this._element.style; style.left = bounds.minX() + "px"; style.top = bounds.minY() + "px"; style.width = bounds.size.width + "px"; style.height = bounds.size.height + "px"; this._outlineElement.style.width = bounds.size.width + "px"; this._outlineElement.style.height = bounds.size.height + "px"; this._button.style.left = (lastRect.maxX() - bounds.minX() - buttonWidth) + "px"; this._button.style.top = (lastRect.maxY() - bounds.minY() - buttonHeight) + "px"; } _addRect(rect) { var r = 4; var svgRect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); svgRect.setAttribute("x", 1); svgRect.setAttribute("y", 1); svgRect.setAttribute("width", rect.size.width); svgRect.setAttribute("height", rect.size.height); svgRect.setAttribute("rx", r); svgRect.setAttribute("ry", r); return this._outlineElement.appendChild(svgRect); } _addPath(commands, tx, ty) { var path = document.createElementNS("http://www.w3.org/2000/svg", "path"); path.setAttribute("d", commands.join(" ")); path.setAttribute("transform", "translate(" + (tx + 1) + "," + (ty + 1) + ")"); return this._outlineElement.appendChild(path); } _drawSingleLine(rect) { this._addRect(rect.pad(2)); } _drawTwoNonOverlappingLines(rects) { var r = 4; var firstRect = rects[0].pad(2); var secondRect = rects[1].pad(2); var tx = -secondRect.minX(); var ty = -firstRect.minY(); var rect = firstRect; this._addPath([ "M", rect.maxX(), rect.minY(), "H", rect.minX() + r, "q", -r, 0, -r, r, "V", rect.maxY() - r, "q", 0, r, r, r, "H", rect.maxX() ], tx, ty); rect = secondRect; this._addPath([ "M", rect.minX(), rect.minY(), "H", rect.maxX() - r, "q", r, 0, r, r, "V", rect.maxY() - r, "q", 0, r, -r, r, "H", rect.minX() ], tx, ty); } _drawOverlappingLines(rects) { var PADDING = 2; var r = 4; var minX = Number.MAX_VALUE; var maxX = -Number.MAX_VALUE; for (var rect of rects) { var minX = Math.min(rect.minX(), minX); var maxX = Math.max(rect.maxX(), maxX); } minX -= PADDING; maxX += PADDING; var minY = rects[0].minY() - PADDING; var maxY = rects.lastValue.maxY() + PADDING; var firstLineMinX = rects[0].minX() - PADDING; var lastLineMaxX = rects.lastValue.maxX() + PADDING; if (firstLineMinX === minX && lastLineMaxX === maxX) return this._addRect(new WebInspector.Rect(minX, minY, maxX - minX, maxY - minY)); var lastLineMinY = rects.lastValue.minY() + PADDING; if (rects[0].minX() === minX + PADDING) { return this._addPath([ "M", minX + r, minY, "H", maxX - r, "q", r, 0, r, r, "V", lastLineMinY - r, "q", 0, r, -r, r, "H", lastLineMaxX + r, "q", -r, 0, -r, r, "V", maxY - r, "q", 0, r, -r, r, "H", minX + r, "q", -r, 0, -r, -r, "V", minY + r, "q", 0, -r, r, -r ], -minX, -minY); } var firstLineMaxY = rects[0].maxY() - PADDING; if (rects.lastValue.maxX() === maxX - PADDING) { return this._addPath([ "M", firstLineMinX + r, minY, "H", maxX - r, "q", r, 0, r, r, "V", maxY - r, "q", 0, r, -r, r, "H", minX + r, "q", -r, 0, -r, -r, "V", firstLineMaxY + r, "q", 0, -r, r, -r, "H", firstLineMinX - r, "q", r, 0, r, -r, "V", minY + r, "q", 0, -r, r, -r ], -minX, -minY); } return this._addPath([ "M", firstLineMinX + r, minY, "H", maxX - r, "q", r, 0, r, r, "V", lastLineMinY - r, "q", 0, r, -r, r, "H", lastLineMaxX + r, "q", -r, 0, -r, r, "V", maxY - r, "q", 0, r, -r, r, "H", minX + r, "q", -r, 0, -r, -r, "V", firstLineMaxY + r, "q", 0, -r, r, -r, "H", firstLineMinX - r, "q", r, 0, r, -r, "V", minY + r, "q", 0, -r, r, -r ], -minX, -minY); } }; WebInspector.HoverMenu.VisibleClassName = "visible";