summaryrefslogtreecommitdiffstats
path: root/chromium/third_party/WebKit/Source/devtools/front_end/ui/SoftContextMenu.js
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/WebKit/Source/devtools/front_end/ui/SoftContextMenu.js')
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/SoftContextMenu.js386
1 files changed, 386 insertions, 0 deletions
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/SoftContextMenu.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/SoftContextMenu.js
new file mode 100644
index 00000000000..a4626e36b1b
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/SoftContextMenu.js
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2011 Google 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. ``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
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @param {!Array.<!WebInspector.ContextMenuItem>} items
+ * @param {!WebInspector.SoftContextMenu=} parentMenu
+ */
+WebInspector.SoftContextMenu = function(items, parentMenu)
+{
+ this._items = items;
+ this._parentMenu = parentMenu;
+}
+
+WebInspector.SoftContextMenu.prototype = {
+ /**
+ * @param {!Event} event
+ */
+ show: function(event)
+ {
+ this._x = event.x;
+ this._y = event.y;
+ this._time = new Date().getTime();
+
+ // Absolutely position menu for iframes.
+ var absoluteX = event.pageX;
+ var absoluteY = event.pageY;
+ var targetElement = event.target;
+ while (targetElement && window !== targetElement.ownerDocument.defaultView) {
+ var frameElement = targetElement.ownerDocument.defaultView.frameElement;
+ absoluteY += frameElement.totalOffsetTop();
+ absoluteX += frameElement.totalOffsetLeft();
+ targetElement = frameElement;
+ }
+
+ // Create context menu.
+ var targetRect;
+ this._contextMenuElement = document.createElement("div");
+ this._contextMenuElement.className = "soft-context-menu";
+ this._contextMenuElement.tabIndex = 0;
+ this._contextMenuElement.style.top = absoluteY + "px";
+ this._contextMenuElement.style.left = absoluteX + "px";
+
+ this._contextMenuElement.addEventListener("mouseup", consumeEvent, false);
+ this._contextMenuElement.addEventListener("keydown", this._menuKeyDown.bind(this), false);
+
+ for (var i = 0; i < this._items.length; ++i)
+ this._contextMenuElement.appendChild(this._createMenuItem(this._items[i]));
+
+ // Install glass pane capturing events.
+ if (!this._parentMenu) {
+ this._glassPaneElement = document.createElement("div");
+ this._glassPaneElement.className = "soft-context-menu-glass-pane";
+ this._glassPaneElement.tabIndex = 0;
+ this._glassPaneElement.addEventListener("mouseup", this._glassPaneMouseUp.bind(this), false);
+ this._glassPaneElement.appendChild(this._contextMenuElement);
+ document.body.appendChild(this._glassPaneElement);
+ this._focus();
+ } else
+ this._parentMenu._parentGlassPaneElement().appendChild(this._contextMenuElement);
+
+ // Re-position menu in case it does not fit.
+ if (document.body.offsetWidth < this._contextMenuElement.offsetLeft + this._contextMenuElement.offsetWidth)
+ this._contextMenuElement.style.left = (absoluteX - this._contextMenuElement.offsetWidth) + "px";
+ if (document.body.offsetHeight < this._contextMenuElement.offsetTop + this._contextMenuElement.offsetHeight)
+ this._contextMenuElement.style.top = (document.body.offsetHeight - this._contextMenuElement.offsetHeight) + "px";
+
+ event.consume(true);
+ },
+
+ _parentGlassPaneElement: function()
+ {
+ if (this._glassPaneElement)
+ return this._glassPaneElement;
+ if (this._parentMenu)
+ return this._parentMenu._parentGlassPaneElement();
+ return null;
+ },
+
+ _createMenuItem: function(item)
+ {
+ if (item.type === "separator")
+ return this._createSeparator();
+
+ if (item.type === "subMenu")
+ return this._createSubMenu(item);
+
+ var menuItemElement = document.createElement("div");
+ menuItemElement.className = "soft-context-menu-item";
+
+ var checkMarkElement = document.createElement("span");
+ checkMarkElement.textContent = "\u2713 "; // Checkmark Unicode symbol
+ checkMarkElement.className = "soft-context-menu-item-checkmark";
+ if (!item.checked)
+ checkMarkElement.style.opacity = "0";
+
+ menuItemElement.appendChild(checkMarkElement);
+ menuItemElement.appendChild(document.createTextNode(item.label));
+
+ menuItemElement.addEventListener("mousedown", this._menuItemMouseDown.bind(this), false);
+ menuItemElement.addEventListener("mouseup", this._menuItemMouseUp.bind(this), false);
+
+ // Manually manage hover highlight since :hover does not work in case of click-and-hold menu invocation.
+ menuItemElement.addEventListener("mouseover", this._menuItemMouseOver.bind(this), false);
+ menuItemElement.addEventListener("mouseout", this._menuItemMouseOut.bind(this), false);
+
+ menuItemElement._actionId = item.id;
+ return menuItemElement;
+ },
+
+ _createSubMenu: function(item)
+ {
+ var menuItemElement = document.createElement("div");
+ menuItemElement.className = "soft-context-menu-item";
+ menuItemElement._subItems = item.subItems;
+
+ // Occupy the same space on the left in all items.
+ var checkMarkElement = document.createElement("span");
+ checkMarkElement.textContent = "\u2713 "; // Checkmark Unicode symbol
+ checkMarkElement.className = "soft-context-menu-item-checkmark";
+ checkMarkElement.style.opacity = "0";
+ menuItemElement.appendChild(checkMarkElement);
+
+ var subMenuArrowElement = document.createElement("span");
+ subMenuArrowElement.textContent = "\u25B6"; // BLACK RIGHT-POINTING TRIANGLE
+ subMenuArrowElement.className = "soft-context-menu-item-submenu-arrow";
+
+ menuItemElement.appendChild(document.createTextNode(item.label));
+ menuItemElement.appendChild(subMenuArrowElement);
+
+ menuItemElement.addEventListener("mousedown", this._menuItemMouseDown.bind(this), false);
+ menuItemElement.addEventListener("mouseup", this._menuItemMouseUp.bind(this), false);
+
+ // Manually manage hover highlight since :hover does not work in case of click-and-hold menu invocation.
+ menuItemElement.addEventListener("mouseover", this._menuItemMouseOver.bind(this), false);
+ menuItemElement.addEventListener("mouseout", this._menuItemMouseOut.bind(this), false);
+
+ return menuItemElement;
+ },
+
+ _createSeparator: function()
+ {
+ var separatorElement = document.createElement("div");
+ separatorElement.className = "soft-context-menu-separator";
+ separatorElement._isSeparator = true;
+ separatorElement.addEventListener("mouseover", this._hideSubMenu.bind(this), false);
+ separatorElement.createChild("div", "separator-line");
+ return separatorElement;
+ },
+
+ _menuItemMouseDown: function(event)
+ {
+ // Do not let separator's mouse down hit menu's handler - we need to receive mouse up!
+ event.consume(true);
+ },
+
+ _menuItemMouseUp: function(event)
+ {
+ this._triggerAction(event.target, event);
+ event.consume();
+ },
+
+ _focus: function()
+ {
+ this._contextMenuElement.focus();
+ },
+
+ _triggerAction: function(menuItemElement, event)
+ {
+ if (!menuItemElement._subItems) {
+ this._discardMenu(true, event);
+ if (typeof menuItemElement._actionId !== "undefined") {
+ WebInspector.contextMenuItemSelected(menuItemElement._actionId);
+ delete menuItemElement._actionId;
+ }
+ return;
+ }
+
+ this._showSubMenu(menuItemElement, event);
+ event.consume();
+ },
+
+ _showSubMenu: function(menuItemElement, event)
+ {
+ if (menuItemElement._subMenuTimer) {
+ clearTimeout(menuItemElement._subMenuTimer);
+ delete menuItemElement._subMenuTimer;
+ }
+ if (this._subMenu)
+ return;
+
+ this._subMenu = new WebInspector.SoftContextMenu(menuItemElement._subItems, this);
+ this._subMenu.show(this._buildMouseEventForSubMenu(menuItemElement));
+ },
+
+ _buildMouseEventForSubMenu: function(subMenuItemElement)
+ {
+ var subMenuOffset = { x: subMenuItemElement.offsetWidth - 3, y: subMenuItemElement.offsetTop - 1 };
+ var targetX = this._x + subMenuOffset.x;
+ var targetY = this._y + subMenuOffset.y;
+ var targetPageX = parseInt(this._contextMenuElement.style.left, 10) + subMenuOffset.x;
+ var targetPageY = parseInt(this._contextMenuElement.style.top, 10) + subMenuOffset.y;
+ return { x: targetX, y: targetY, pageX: targetPageX, pageY: targetPageY, consume: function() {} };
+ },
+
+ _hideSubMenu: function()
+ {
+ if (!this._subMenu)
+ return;
+ this._subMenu._discardSubMenus();
+ this._focus();
+ },
+
+ _menuItemMouseOver: function(event)
+ {
+ this._highlightMenuItem(event.target);
+ },
+
+ _menuItemMouseOut: function(event)
+ {
+ if (!this._subMenu || !event.relatedTarget) {
+ this._highlightMenuItem(null);
+ return;
+ }
+
+ var relatedTarget = event.relatedTarget;
+ if (this._contextMenuElement.isSelfOrAncestor(relatedTarget) || relatedTarget.classList.contains("soft-context-menu-glass-pane"))
+ this._highlightMenuItem(null);
+ },
+
+ _highlightMenuItem: function(menuItemElement)
+ {
+ if (this._highlightedMenuItemElement === menuItemElement)
+ return;
+
+ this._hideSubMenu();
+ if (this._highlightedMenuItemElement) {
+ this._highlightedMenuItemElement.classList.remove("soft-context-menu-item-mouse-over");
+ if (this._highlightedMenuItemElement._subItems && this._highlightedMenuItemElement._subMenuTimer) {
+ clearTimeout(this._highlightedMenuItemElement._subMenuTimer);
+ delete this._highlightedMenuItemElement._subMenuTimer;
+ }
+ }
+ this._highlightedMenuItemElement = menuItemElement;
+ if (this._highlightedMenuItemElement) {
+ this._highlightedMenuItemElement.classList.add("soft-context-menu-item-mouse-over");
+ this._contextMenuElement.focus();
+ if (this._highlightedMenuItemElement._subItems && !this._highlightedMenuItemElement._subMenuTimer)
+ this._highlightedMenuItemElement._subMenuTimer = setTimeout(this._showSubMenu.bind(this, this._highlightedMenuItemElement, this._buildMouseEventForSubMenu(this._highlightedMenuItemElement)), 150);
+ }
+ },
+
+ _highlightPrevious: function()
+ {
+ var menuItemElement = this._highlightedMenuItemElement ? this._highlightedMenuItemElement.previousSibling : this._contextMenuElement.lastChild;
+ while (menuItemElement && menuItemElement._isSeparator)
+ menuItemElement = menuItemElement.previousSibling;
+ if (menuItemElement)
+ this._highlightMenuItem(menuItemElement);
+ },
+
+ _highlightNext: function()
+ {
+ var menuItemElement = this._highlightedMenuItemElement ? this._highlightedMenuItemElement.nextSibling : this._contextMenuElement.firstChild;
+ while (menuItemElement && menuItemElement._isSeparator)
+ menuItemElement = menuItemElement.nextSibling;
+ if (menuItemElement)
+ this._highlightMenuItem(menuItemElement);
+ },
+
+ _menuKeyDown: function(event)
+ {
+ switch (event.keyIdentifier) {
+ case "Up":
+ this._highlightPrevious(); break;
+ case "Down":
+ this._highlightNext(); break;
+ case "Left":
+ if (this._parentMenu) {
+ this._highlightMenuItem(null);
+ this._parentMenu._focus();
+ }
+ break;
+ case "Right":
+ if (!this._highlightedMenuItemElement)
+ break;
+ if (this._highlightedMenuItemElement._subItems) {
+ this._showSubMenu(this._highlightedMenuItemElement, this._buildMouseEventForSubMenu(this._highlightedMenuItemElement));
+ this._subMenu._focus();
+ this._subMenu._highlightNext();
+ }
+ break;
+ case "U+001B": // Escape
+ this._discardMenu(true, event); break;
+ case "Enter":
+ if (!isEnterKey(event))
+ break;
+ // Fall through
+ case "U+0020": // Space
+ if (this._highlightedMenuItemElement)
+ this._triggerAction(this._highlightedMenuItemElement, event);
+ break;
+ }
+ event.consume(true);
+ },
+
+ _glassPaneMouseUp: function(event)
+ {
+ // Return if this is simple 'click', since dispatched on glass pane, can't use 'click' event.
+ if (event.x === this._x && event.y === this._y && new Date().getTime() - this._time < 300)
+ return;
+ this._discardMenu(true, event);
+ event.consume();
+ },
+
+ /**
+ * @param {boolean} closeParentMenus
+ * @param {!Event=} event
+ */
+ _discardMenu: function(closeParentMenus, event)
+ {
+ if (this._subMenu && !closeParentMenus)
+ return;
+ if (this._glassPaneElement) {
+ var glassPane = this._glassPaneElement;
+ delete this._glassPaneElement;
+ // This can re-enter discardMenu due to blur.
+ document.body.removeChild(glassPane);
+ if (this._parentMenu) {
+ delete this._parentMenu._subMenu;
+ if (closeParentMenus)
+ this._parentMenu._discardMenu(closeParentMenus, event);
+ }
+
+ if (event)
+ event.consume(true);
+ } else if (this._parentMenu && this._contextMenuElement.parentElement) {
+ this._discardSubMenus();
+ if (closeParentMenus)
+ this._parentMenu._discardMenu(closeParentMenus, event);
+
+ if (event)
+ event.consume(true);
+ }
+ },
+
+ _discardSubMenus: function()
+ {
+ if (this._subMenu)
+ this._subMenu._discardSubMenus();
+ this._contextMenuElement.remove();
+ if (this._parentMenu)
+ delete this._parentMenu._subMenu;
+ }
+}
+
+if (!InspectorFrontendHost.showContextMenu) {
+
+InspectorFrontendHost.showContextMenu = function(event, items)
+{
+ new WebInspector.SoftContextMenu(items).show(event);
+}
+
+}