| Index: resources/bookmark_manager/js/cr/ui/contextmenuhandler.js
|
| ===================================================================
|
| --- resources/bookmark_manager/js/cr/ui/contextmenuhandler.js (revision 0)
|
| +++ resources/bookmark_manager/js/cr/ui/contextmenuhandler.js (revision 0)
|
| @@ -0,0 +1,211 @@
|
| +// Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +cr.define('cr.ui', function() {
|
| +
|
| + /**
|
| + * Handles context menus.
|
| + * @constructor
|
| + */
|
| + function ContextMenuHandler() {}
|
| +
|
| + ContextMenuHandler.prototype = {
|
| +
|
| + /**
|
| + * The menu that we are currently showing.
|
| + * @type {cr.ui.Menu}
|
| + */
|
| + menu_: null,
|
| + get menu() {
|
| + return this.menu_;
|
| + },
|
| +
|
| + /**
|
| + * Shows a menu as a context menu.
|
| + * @param {!Event} e The event triggering the show (usally a contextmenu
|
| + * event).
|
| + * @param {!cr.ui.Menu} menu The menu to show.
|
| + */
|
| + showMenu: function(e, menu) {
|
| + this.menu_ = menu;
|
| +
|
| + menu.style.display = 'block';
|
| + // when the menu is shown we steal all keyboard events.
|
| + menu.ownerDocument.addEventListener('keydown', this, true);
|
| + menu.ownerDocument.addEventListener('mousedown', this, true);
|
| + menu.ownerDocument.addEventListener('blur', this, true);
|
| + menu.addEventListener('activate', this);
|
| + this.positionMenu_(e, menu);
|
| + },
|
| +
|
| + /**
|
| + * Hide the currently shown menu.
|
| + */
|
| + hideMenu: function() {
|
| + var menu = this.menu;
|
| + if (!menu)
|
| + return;
|
| +
|
| + menu.style.display = 'none';
|
| + menu.ownerDocument.removeEventListener('keydown', this, true);
|
| + menu.ownerDocument.removeEventListener('mousedown', this, true);
|
| + menu.ownerDocument.removeEventListener('blur', this, true);
|
| + menu.removeEventListener('activate', this);
|
| + menu.selectedIndex = -1;
|
| + this.menu_ = null;
|
| +
|
| + // On windows we might hide the menu in a right mouse button up and if
|
| + // that is the case we wait some short period before we allow the menu
|
| + // to be shown again.
|
| + this.hideTimestamp_ = Date.now();
|
| + },
|
| +
|
| + /**
|
| + * Positions the menu
|
| + * @param {!Event} e The event object triggering the showing.
|
| + * @param {!cr.ui.Menu} menu The menu to position.
|
| + * @private
|
| + */
|
| + positionMenu_: function(e, menu) {
|
| + // TODO(arv): Handle scrolled documents when needed.
|
| +
|
| + var x, y;
|
| + // When the user presses the context menu key (on the keyboard) we need
|
| + // to detect this.
|
| + if (e.screenX == 0 && e.screenY == 0) {
|
| + var rect = e.currentTarget.getBoundingClientRect();
|
| + x = rect.left;
|
| + y = rect.top;
|
| + } else {
|
| + x = e.clientX;
|
| + y = e.clientY;
|
| + }
|
| +
|
| + var menuRect = menu.getBoundingClientRect();
|
| + var bodyRect = menu.ownerDocument.body.getBoundingClientRect();
|
| +
|
| + // Does menu fit below?
|
| + if (y + menuRect.height > bodyRect.height) {
|
| + // Does menu fit above?
|
| + if (y - menuRect.height >= 0) {
|
| + y -= menuRect.height;
|
| + } else {
|
| + // Menu did not fit above nor below.
|
| + y = 0;
|
| + // We could resize the menu here but lets not worry about that at this
|
| + // point.
|
| + }
|
| + }
|
| +
|
| + // Does menu fit to the right?
|
| + if (x + menuRect.width > bodyRect.width) {
|
| + // Does menu fit to the left?
|
| + if (x - menuRect.width >= 0) {
|
| + x -= menuRect.width;
|
| + } else {
|
| + // Menu did not fit to the right nor to the left.
|
| + x = 0;
|
| + // We could resize the menu here but lets not worry about that at this
|
| + // point.
|
| + }
|
| + }
|
| +
|
| + menu.style.left = x + 'px';
|
| + menu.style.top = y + 'px';
|
| + },
|
| +
|
| + /**
|
| + * Handles event callbacks.
|
| + * @param {!Event} e The event object.
|
| + */
|
| + handleEvent: function(e) {
|
| + // Context menu is handled even when we have no menu.
|
| + if (e.type != 'contextmenu' && !this.menu)
|
| + return;
|
| +
|
| + switch (e.type) {
|
| + case 'mousedown':
|
| + if (!this.menu.contains(e.target))
|
| + this.hideMenu();
|
| + else
|
| + e.preventDefault();
|
| + break;
|
| + case 'keydown':
|
| + // keyIdentifier does not report 'Esc' correctly
|
| + if (e.keyCode == 27 /* Esc */) {
|
| + this.hideMenu();
|
| +
|
| + // If the menu is visible we let it handle all the keyboard events.
|
| + } else if (this.menu) {
|
| + this.menu.handleKeyDown(e);
|
| + e.preventDefault();
|
| + e.stopPropagation();
|
| + }
|
| + break;
|
| +
|
| + case 'activate':
|
| + case 'blur':
|
| + this.hideMenu();
|
| + break;
|
| +
|
| + case 'contextmenu':
|
| + if ((!this.menu || !this.menu.contains(e.target)) &&
|
| + (!this.hideTimestamp_ || Date.now() - this.hideTimestamp_ > 50))
|
| + this.showMenu(e, e.currentTarget.contextMenu);
|
| + e.preventDefault();
|
| + // Don't allow elements further up in the DOM to show their menus.
|
| + e.stopPropagation();
|
| + break;
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Adds a contextMenu property to an element or element class.
|
| + * @param {!Element|!Function} element The element or class to add the
|
| + * contextMenu property to.
|
| + */
|
| + addContextMenuProperty: function(element) {
|
| + if (typeof element == 'function')
|
| + element = element.prototype;
|
| +
|
| + element.__defineGetter__('contextMenu', function() {
|
| + return this.contextMenu_;
|
| + });
|
| + element.__defineSetter__('contextMenu', function(menu) {
|
| + var oldContextMenu = this.contextMenu;
|
| +
|
| + if (typeof menu == 'string' && menu[0] == '#') {
|
| + menu = this.ownerDocument.getElementById(menu.slice(1));
|
| + cr.ui.decorate(menu, Menu);
|
| + }
|
| +
|
| + if (menu === oldContextMenu)
|
| + return;
|
| +
|
| + if (oldContextMenu && !menu)
|
| + this.removeEventListener('contextmenu', contextMenuHandler);
|
| + if (menu && !oldContextMenu)
|
| + this.addEventListener('contextmenu', contextMenuHandler);
|
| +
|
| + this.contextMenu_ = menu;
|
| +
|
| + if (menu && menu.id)
|
| + this.setAttribute('contextmenu', '#' + menu.id);
|
| +
|
| + cr.dispatchPropertyChange(this, 'contextMenu', menu, oldContextMenu);
|
| + });
|
| + }
|
| + };
|
| +
|
| + /**
|
| + * The singleton context menu handler.
|
| + * @type {!ContextMenuHandler}
|
| + */
|
| + var contextMenuHandler = new ContextMenuHandler;
|
| +
|
| + // Export
|
| + return {
|
| + contextMenuHandler: contextMenuHandler
|
| + };
|
| +});
|
|
|