Jetpack's menu API allows features to access and modify the browser's built-in menus. Features can also create new menus and attach them almost anywhere, chrome or content, as popup menus or context menus.
Two namespaces are associated with this API: jetpack.menu
, which provides access to the browser's menus, and jetpack.Menu
, the constructor for making new menus. The API is fairly comprehensive, so you may want to start by reading through some examples.
Because it is still under development, the API currently lives in the future and must be imported before it is used:
jetpack.future.import("menu");
Menus
All menus in Jetpack are jetpack.Menu
objects, including both built-in Firefox menus and menus that features create. To create a new menu, use one of the jetpack.Menu
constructors below.
Constructors
jetpack.Menu()
Creates an empty menu.
jetpack.Menu(menuitems)
Creates a menu with the given items.
Parameters
-
menuitems
- An array of menuitems.
jetpack.Menu(properties)
Creates a menu with specific properties.
Parameters
-
properties
-
An object defining any of the properties below. The
items
property may be used to define an array of menuitems.
Properties
Property | Type | Description |
beforeHide |
function | A function invoked just before the menu is hidden. If the menu is a context menu, it is called as beforeHide(menu, context) and otherwise as beforeHide(menu) . menu is the Menu object to which beforeHide is attached. context is an object describing the context in which the menu was shown. |
beforeShow |
function | A function invoked just before the menu is shown. If the menu is a context menu, it is called as beforeShow(menu, context) and otherwise as beforeShow(menu) . menu is the Menu object to which beforeShow is attached. context is an object describing the context in which the menu was shown. beforeShow may modify the menu, and when the menu is shown, it will reflect the changes. |
isShowing |
boolean | True if the menu is currently visible and false otherwise. Read-only. |
items |
array | An array of menuitems in the menu. Read-only. |
Methods
add(items) |
clear() |
contextOn(node) |
hide() |
insertBefore(newItems, target) |
item(target) |
popupOn(node) |
remove(target) |
replace(target, newItems) |
reset() |
set(items) |
show(anchorNode) |
add(items)
Adds items to the menu. The position at which the items are added is at Jetpack's discretion. This method and set()
are the recommended methods of adding items to a menu.
Parameters
-
items
- A single menuitem or an array of menuitems.
clear()
Removes all items from the menu, even items not added by the feature.
contextOn(node)
Binds the menu to a given node as its context menu.
Parameters
-
node
-
The menu is attached to this node, which may be either a raw DOM node or a DOM node wrapped by
jQuery
.
hide()
Hides the menu if it is showing.
insertBefore(newItems, target)
Inserts new items before an existing item.
Parameters
-
newItems
- A single menuitem or an array of menuitems.
-
target
- Indicates the existing item. See Targets. If no such target exists, the position at which the items are added is at the discretion of Jetpack.
item(target)
Returns an item in the menu. The item may be modified. If the menu is hidden, it will reflect the changes when it is next shown. If the menu is visible, it will reflect the changes immediately.
Parameters
-
target
- Indicates the existing item. See Targets.
Return value
A Menuitem
object, or null
if no such target exists.
popupOn(node)
Binds the menu to a given node. The menu will be shown when the node is left-clicked.
Parameters
-
node
-
The menu is attached to this node, which may be either a raw DOM node or a DOM node wrapped by
jQuery
.
remove(target)
Removes an item from the menu.
Parameters
-
target
- Indicates the existing item to remove. See Targets. If no such target exists, the call silently fails.
replace(target, newItems)
Replaces an item with new items.
Parameters
-
target
- Indicates the existing item to replace. See Targets. If no such target exists, the call silently fails.
-
newItems
- A single menuitem or an array of menuitems.
reset()
Reverts all the changes to the menu that the feature has made. For menus that the feature creates, this is equivalent to clear()
.
set(items)
Equivalent to calling reset()
and then add(items)
. This method and add()
are the recommended methods of adding items to a menu.
Parameters
-
items
- A single menuitem or an array of menuitems.
show(anchorNode)
Shows the menu immediately.
Parameters
-
anchorNode
-
The menu will pop up on this node, which may be either a raw DOM node or a DOM node wrapped by
jQuery
.
Menuitems
Menus in Jetpack contain Menuitem
objects, including both built-in Firefox menus and menus that features create. No Menuitem
constructor is exposed, because Jetpack automatically boxes simple JavaScript objects into Menuitem
objects.
Creating menuitems
To pass a new menuitem into the API, pass one of the following types:
null
A simple menu separator. (Note that any falsey value will suffice, including undefined
, null
, and the empty string. null
is recommended because it stands out.)
function
A menuitem that will update itself when its menu is shown.
The function is invoked just before the item's menu is shown and when the items
array of the item's menu is retrieved. It must return a non-function menuitem. If the item belongs to a context menu, the function is called as function(context)
and otherwise function()
. context
is an object describing the context in which the menu was shown.
string
A simple menuitem with the given string label.
object
A menuitem with specific properties. The object may define any of the properties listed below.
Properties
Property | Type | Description |
command |
function |
A function that will be called when the menuitem is clicked. If the
Due to a platform bug in Firefox, on OS X only, for menus in the menu bar only, command functions defined on menuitems with submenus are not called when descendant menuitems are clicked. See bug 534014 for details and a workaround.
|
data |
string | An arbitrary string that the feature may associate with the menuitem. |
disabled |
boolean | If true , the menuitem is disabled. |
icon |
string | The URL of an icon to display in the menuitem. Note that some environments, notably Gnome 2.28, do not support menuitem icons either by default or at all. |
label |
string | The label of the menuitem. |
menu |
jetpack.Menu |
If defined, the menuitem expands into a submenu. |
mnemonic |
string | A character that is used as the item's shortcut key while the menu is open. It should be one of the characters that appears in the item's label. On some platforms this character is underlined in the label. |
type |
string | Currently only "separator" is supported. If this property is undefined, the item is a normal menuitem. Note that when type == "separator" , any other properties (such as label ) may be specified. Fancy separators may behave differently on different platforms, however. |
xulId |
string | The ID of the menuitem's backing XUL element, exposed for the benefit of advanced developers. |
Menu bar menus
jetpack.menu
When you need to expose functionality through a menu but no menu in particular, do The Right Thing by using jetpack.menu
, the "Jetpack menu." jetpack.menu
is a jetpack.Menu
object corresponding to a menu or region within a menu that Jetpack sets aside for features. The actual menu is up to Jetpack, but currently it is the Tools menu. In the future it may be a submenu of the Tools menu, for example.
Features meant for wide release should prefer jetpack.menu
to the jetpack.menu.*
menus because:
- Firefox's menus are subject to change and in fact will be changing in Firefox 3.7. By exposing functionality through
jetpack.menu
, a feature is guaranteed an easy transition. - Many users, especially those new to Firefox, don't realize the distinction between add-ons and the browser itself. If a user forgets or is unable to tell whether a certain menuitem is part of Firefox or provided by a feature, she does not know where to turn when she has problems with it.
- If many features' menus are local to one area, the user need not hunt and peck through many menus trying to find a particular item of a feature.
- No one likes it when add-ons clutter her Tools menu willy-nilly.
- It's less code. (OK, slightly less.)
This is only a recommended practice; developers are of course free to do as they wish.
jetpack.menu.file
The browser's File menu. A jetpack.Menu
object.
jetpack.menu.edit
The browser's Edit menu. A jetpack.Menu
object.
jetpack.menu.view
The browser's View menu. A jetpack.Menu
object.
jetpack.menu.history
The browser's History menu. A jetpack.Menu
object.
jetpack.menu.bookmarks
The browser's Bookmarks menu. A jetpack.Menu
object.
jetpack.menu.tools
The browser's Tools menu. A jetpack.Menu
object.
Context menus
Context menus behave a little differently from other menus. Unlike the Tools menu, which is always in the same place and whose items basically remain constant, context menus don't really exist until they pop up in a certain context. For example, the items of the page's context menu change depending on what the user clicks: images have a context menu, links have a context menu, and so on. Does the page have a single context menu or many?
ContextMenuSet
Jetpack's solution is to expose ContextMenuSet
objects. A ContextMenuSet
represents a set of context menus. jetpack.menu.context.page
is the set of context menus that appear on the page: the image menu, link menu, and so on.
ContextMenuSet
defines many of the same methods that jetpack.Menu
does. Features can use these methods to modify all the menus in a set at once. They are:
add(items)
clear()
insertBefore(newItems, target)
remove(target)
replace(target, newItems)
reset()
set(items)
The following jetpack.Menu
properties can also be defined on a ContextMenuSet
:
beforeHide
beforeShow
Note that a jetpack.Menu
object is passed to beforeHide
and beforeShow
, since they are called during a context menu's invocation. They may then modify the menu.
Individual menus are drawn from the set using on()
:
on(selector)
Returns a new ContextMenuSet
whose context menus are those that arise from nodes that match the given CSS selector(s). The selector must be a true CSS selector, not a jQuery or other type of pseudo-selector.
Note: Selectors don't automatically include the children of nodes they match. For example, jetpack.menu.context.page.on("a[href]")
does not match images contained in links. To match links and all elements contained in links, use jetpack.menu.context.page.on("a[href], a[href] *")
. A future version of Jetpack may change this behavior; see bug 527924. What do you think? Leave a comment on that bug or send a message to the mailing list.
Parameters
-
selector
-
One or more CSS selectors. A string. Examples:
"a[href]"
,"a[href] > img"
,"a[href], img"
.
Return value
A new ContextMenuSet
object.
jetpack.menu.context
The feature's context menu. All of the feature's context menus are exposed through jetpack.menu.context
, including slidebar, panel, toolbar, and status bar item context menus. A ContextMenuSet
object.
jetpack.menu.context.browser
The chrome context menu. This is the context menu that appears when right-clicking on an element in Firefox's interface, such as a bookmark on the bookmarks toolbar. A ContextMenuSet
object.
Note: Sometimes jetpack.menu.context.browser.on()
does not work as expected. For example, jetpack.menu.context.browser.on("*").add("foo")
does not add "foo" to the tab strip's and location bar's context menus, but it does on the bookmark toolbar's context menu. To work around this limitation, try using beforeShow
and examining the node that the user clicked. For example, to add an item to the tabs' context menu, try:
jetpack.menu.context.browser.beforeShow = function (menu, context) { menu.reset(); if (context.node.localName === "tab") menu.add("Hey, a tab!"); };
(The reason this problem exists is because document.querySelectorAll()
does not match anonymous content in XUL. Many parts of Firefox's interface are actually anonymous content. A future version of Jetpack will hopefully provide a better solution.)
jetpack.menu.context.page
The content context menu. This is the context menu that appears when right-clicking on a Web page. A ContextMenuSet
object.
Context objects
Some callbacks passed into the API are called with a context
object, such as ContextMenuSet.beforeShow()
. Context objects are objects that describe the context in which a context menu is shown. They have the following properties:
Property | Description |
node |
The DOM node on which the menu is shown. This is the node the user clicked to open the menu. Be careful when dealing with nested nodes. For example, if the user clicks on an image inside a link, this property will be set to the image, not the link. Similarly, if the user clicks a node within a div , this property will be the node, not the div . |
document |
The content document in which the menu is shown. |
window |
The content window in which the menu is shown. |
Targets
Some methods act on existing items within a menu. Existing items are always identified using targets. A target is a string, regular expression, or integer.
A string target is case-insensitively matched against the label
, id
, and xulId
properties of menuitems.
A regular expression target is tested against the label
, id
, and xulId
properties of menuitems.
An integer target indicates a zero-based index within a menu. Integer targets can be negative: -1 indicates the last item in a menu, -2 the second-to-last, and so on.
Multiple items in a menu may match a target, but action is only ever taken on the first matching item.
Examples
Before running any examples, import the API from the future:
jetpack.future.import("menu");
Snippets
Add a single, static menuitem to the Jetpack menu that doesn't do anything:
jetpack.menu.add("Two Drink Holders and a Captain's Chair");
Add a menuitem to the Jetpack menu that displays the current date and time each time it's opened:
jetpack.menu.add(function () new Date().toString());
Click an item in the Jetpack menu to be notified of the current date and time:
jetpack.menu.add({ label: "Show Current Date and Time", command: function () jetpack.notifications.show(new Date()) });
The same, except on the content context menu:
jetpack.menu.context.page.add({ label: "Show Current Date and Time", command: function () jetpack.notifications.show(new Date()) });
Create a submenu within the content context menu. When the user clicks an item, she's notified of her choice. Note that the submenu contains many items, including a menu separator:
jetpack.menu.context.page.add({ label: "Ice Cream", icon: "https://example.com/ice-cream.png", menu: new jetpack.Menu(["Vanilla", "Chocolate", "Pistachio", null, "None"]), command: function (menuitem) jetpack.notifications.show(menuitem.label) });
Add an item to the hyperlink context menu that tweets the link:
jetpack.menu.context.page.on("a").add(function (context) { return { label: "Tweet", command: function () jetpack.lib.twitter.statuses.update({ status: context.node.href }) }; ));
Add an item to the page's context menu depending on some complex criteria that can't be completely expressed via a CSS selector:
jetpack.menu.context.page.beforeShow = function (menu, context) { menu.reset(); if (matchesMyCriteria(context)) menu.add("Match!"); };
Add an item to both the hyperlink context menu and the image context menu:
jetpack.menu.context.page.on("a, img").set("A Link or Image");
Add an item to the image context menu, but only for images contained in hyperlinks:
jetpack.menu.context.page.on("a > img").set("An Image Inside a Link");
Add a "Recent Tweets" submenu to the Jetpack menu. (Assume we've defined a getRecentTweets()
, which invokes a callback with an array of strings.) When the menu is shown, it displays a "Loading..." item. If the menu remains open when getRecentTweets()
receives data from the network and calls done()
, the "Loading..." item is replaced with the tweets, one item per tweet:
jetpack.menu
with jetpack.menu.context.page
to see the effect on the content context menu. See bug 526382 for more information.jetpack.menu.add({ label: "Recent Tweets", menu: new jetpack.Menu({ beforeShow: function (menu) { menu.set("Loading..."); getRecentTweets(function done(tweets) menu.set(tweets)); } }) });
When the user selects some text on a page, the context menu normally displays a simple item that searches for it. Replace that item with a menu that lets the user search either Google or Wikipedia:
jetpack.menu.context.page.replace("Search", function (context) { return { label: "Search for " + jetpack.selection.text, menu: new jetpack.Menu([ { label: "Google", icon: "https://www.google.com/favicon.ico", data: "https://www.google.com/search?q=" }, { label: "Wikipedia", icon: "https://en.wikipedia.org/favicon.ico", data: "https://en.wikipedia.org/wiki/" } ]), command: function (menuitem) { context.window.location.href = menuitem.data + jetpack.selection.text; } }; });
Create some div
buttons (e.g., in a slidebar or status bar item) and specify their context menu:
for (let i = 0; i < 10; i++) { var button = $('<div class="button" />', document); buttonContainer.append(button); } jetpack.menu.context.on(".button").add(["Do This", "Do That"]);
Create a div
button (e.g., in a slidebar or status bar item) and attach menus directly to it. contextMenu
becomes the button's context menu. Left-click the button to show popupMenu
:
var button = $("<div />", document); button.text("Click Me"); var contextMenu = new jetpack.Menu(["Do This", "Do That", "And the Other"]); contextMenu.contextOn(button); var popupMenu = new jetpack.Menu(["Frumpy", "Frimpy", "Frompy"]); popupMenu.popupOn(button);
Complete jetpacks
See a complete, real-word example that you can install in the simple storage documentation. Check also jetpacks tagged with "tutorial" at the Jetpack Gallery.