Labs/Jetpack/Reboot/JEP/112: Difference between revisions

From MozillaWiki
< Labs‎ | Jetpack‎ | Reboot‎ | JEP
Jump to navigation Jump to search
(→‎JEP 112 - Context Menu: Updated status)
 
(30 intermediate revisions by 2 users not shown)
Line 2: Line 2:


* Champion: Drew Willcoxon - adw at mozilla.com
* Champion: Drew Willcoxon - adw at mozilla.com
* Status: Accepted/In-Queue
* Status: Accepted/Initial implementation introduced in Jetpack SDK 0.3
* Bug Ticket:
* Bug Ticket: {{bug|548590}}
* Type: API
* Type: API


=== Proposal ===
=== Proposal ===


Provide access to the page's context menu.
Provide access to the page's context menu through a module named <tt>context-menu</tt>.


This proposal recognizes the following principles:
This proposal recognizes the following principles:
Line 22: Line 22:


* Adding an item that acts on a portion of the page -- a node, selection, etc.  Examples:
* Adding an item that acts on a portion of the page -- a node, selection, etc.  Examples:
** An image-editing extension wants to add an "Edit Image" item when the context menu is invoked on an image.
** An image-editing extension adds an "Edit Image" item when the context menu is invoked on an image.
** A search extension wants to add a "Search" item when the context menu is invoked while a selection exists.
** A search extension adds a "Search" item when the context menu is invoked while a selection exists.
** An extension adds an "Open Text Link" item when the selection contains a URL outside of an anchor.
** A DOM inspection extension adds an "Inspect This Node" item when the context menu is invoked anywhere in the page.
* Adding an item that acts on the entire page.  Examples:
* Adding an item that acts on the entire page.  Examples:
** An extension that makes it easy to edit page source wants to add an "Edit Page Source" item when the context menu is invoked anywhere on the page.
** An extension that makes it easy to edit page source adds an "Edit Page Source" item when the context menu is invoked anywhere on the page.
** An image-editing extension wants to add an "Edit Page Images" item when the context menu is invoked anywhere on a page that has at least one image.
** An image-editing extension adds an "Edit Page Images" item when the context menu is invoked anywhere on a page that has at least one image.


=== Non-Use Cases ===
=== Non-Use Cases ===


* Adding an item unrelated to the page and its content.  Examples:
* Adding an item unrelated to the page and its content.  Examples:
** A tab-related extension wants to add an item that provides access to all the user's tabs.
** A tab-related extension adds an item that provides access to all the user's tabs.
** An extension wants to add an item when it's three o'clock.
** An extension adds an item when it's three o'clock.
* Adding multiple items to the top-level context menu.  (Although the API makes it possible, it doesn't necessarily make it simple.)
* Radio and checkbox items.
* Radio and checkbox items.
* The ability to modify the menu just before it's shown, and the ability to modify individual items already created by the extension. (The API makes these possible as a side effect, but it's not straightforward.)
* The ability to modify the menu just before it's shown, and the ability to modify individual items already created by the extension.
* The ability to modify existing items other than to replace them with new items.
* The ability to modify existing items other than to replace them with new items.


Line 44: Line 45:
* Menuitem icons should be package-able in XPIs, reference-able from code.
* Menuitem icons should be package-able in XPIs, reference-able from code.


=== Internal Methods ===
=== API Methods ===


This JEP doesn't export anything into the Cuddlefish Platform Library.
==== add ====


=== API Methods ===
<tt>add(menuitem)</tt>
 
Adds an item to the context menu.  The position at which the item is inserted is at the discretion of Jetpack.


==== add(context, menuitem) ====
; menuitem: The menuitem to add.  See [[#Creating New Menuitems]].  <tt>Separator</tt>s, however, can't be added.


Adds an item to the context menu.  The position at which the item is inserted is at the discretion of Jetpack.
==== insertBefore ====


; context : A value describing the context under which to add the item.  See [[#Specifying Contexts]].
<span style="background-color: #fcc;"><em>This part of the proposal is under review.</em></span>
; menuitem : The menuitem to add.  See [[#Specifying New Menuitems]].


==== insertBefore(context, target, menuitem) ====
<tt>insertBefore(target, menuitem)</tt>


Inserts a new item before an existing one.  If the target item does not exist, this function silently fails, and the context menu not modified.
Inserts a new item before an existing one.  If the target item does not exist, this function silently fails, and the context menu not modified.


; context : A value describing the context under which to add the item.  See [[#Specifying Contexts]].
; target: A value describing the existing item before which to insert the new item.  See [[#Specifying Existing Menuitems]].
; target : A value describing the existing item before which to insert the new item.  See [[#Specifying Existing Menuitems]].
; menuitem: The menuitem to add.  See [[#Creating New Menuitems]].
; menuitem : The menuitem to add.  See [[#Specifying New Menuitems]].


==== replace(context, target, menuitem) ====
==== remove ====
 
<tt>remove(item)</tt>
 
Permanently removes an item previously added to the context menu.  This method should not be used to temporarily remove an item under particular contexts; for that, set an appropriate context when creating the item.  See [[#Specifying Contexts]].
 
; item: A menuitem that was previously created and added to the menu.  See [[#Creating New Menuitems]].
 
==== replace ====
 
<span style="background-color: #fcc;"><em>This part of the proposal is under review.</em></span>
 
<tt>replace(target, menuitem)</tt>


Replaces an existing item in the context menu.  If the target item does not exist, this function silently fails, and the context menu is not modified.
Replaces an existing item in the context menu.  If the target item does not exist, this function silently fails, and the context menu is not modified.


; context : A value describing the context under which to add the item.  See [[#Specifying Contexts]].
; target: A value describing the existing item to replace.  See [[#Specifying Existing Menuitems]].
; target : A value describing the existing item to replace.  See [[#Specifying Existing Menuitems]].
; menuitem: The menuitem to add.  See [[#Creating New Menuitems]].
; menuitem : The menuitem to add.  See [[#Specifying New Menuitems]].


==== Specifying Contexts ====
=== Specifying Contexts ===


As its name implies, the context menu should be used only when some particular context arises.  Context can be related to page content or the page itself, but it should never be external to the page.
Items should be added to the context menu, as its name implies, only when some particular context arises.  Context can be related to page content or the page itself, but it should never be external to the page. (See [[#Non-Use Cases]] for examples.)


A context must be specified when modifying the menu.  When the menu is invoked under a specified context, all of the context's associated modifications are appliedWhen the menu is invoked without matching any specified context, no modifications are applied.
Instead of requiring consumers to manually add and remove items when particular contexts arise, this proposal introduces the notion that items are bound to one or more contexts, much as event listeners are bound to events.  When the context menu is invoked, all of the items bound to the context giving rise to the menu are added to the menuIf no items are bound to the context, no items are added to the menu. This binding occurs through an item's <tt>context</tt> property.


Contexts may be specified with any of the following types:
Contexts may be specified with any of the following types:


; string : A CSS selector.  The context arises when the menu is invoked on a node that matches this selector.
; string: A CSS selector.  The context arises when the menu is invoked on a node that either matches this selector or has an ancestor that matches.
 
; undefined or null: The page context.  The context arises when the menu is invoked on a non-interactive portion of the page.  The definition of "non-interactive" is at Jetpack's discretion.
 
; function: An arbitrary predicate.  The context arises when the function returns true.  The function is passed an object describing the current context.  See [[#Context_Descriptions]].


; function : An arbitrary predicate.  The context arises when the function returns true.
; array: An array of any of the other types.  The context arises when any context in the array does.


==== Specifying New Menuitems ====
An item's <tt>context</tt> property is a collection, similar to event listener collections common throughout Jetpack's various APIs.  A single context may be bound by assigning one of the above types to the <tt>context</tt> property either on construction or after:


New menuitems are specified using the following types:
<pre class="brush:js;toolbar:false;">
var item = contextMenu.Item({ context: "img" });
item.context = "img";
</pre>


; string: A simple menuitem with the given string label.
Multiple contexts may be bound by assigning an array of contexts either on construction or after:


; null: A simple menu separator. (Note that any falsey value will suffice, including <tt>undefined</tt>, <tt>null</tt>, and the empty string.  <tt>null</tt> is recommended because it stands out.)
<pre class="brush:js;toolbar:false;">
var item = contextMenu.Item({ context: ["img", "a[href]"] });
item.context = ["img", "a[href]"];
</pre>


; object: A menuitem with specific properties. The object may define any of the following properties:
Contexts may also be bound by calling the collection's <tt>add</tt> method, which takes a single context or array of contexts:
 
<pre class="brush:js;toolbar:false;">
item.context.add("img");
item.context.add(["img", "a[href]"]);
</pre>
 
Finally, contexts may be unbound by calling the collection's <tt>remove</tt> method, which takes a single context or array of contexts :
 
<pre class="brush:js;toolbar:false;">
item.context.remove("img");
item.context.remove(["img", "a[href]"]);
</pre>
 
When an item is bound to more than one context, it is added to menus arising from any of those contexts.
 
=== Creating New Menuitems ===
 
New menuitems are created using one of the constructors exported by the module, <tt>Item</tt>, <tt>Menu</tt>, and <tt>Separator</tt>.
 
==== Item ====
 
<tt>Item(options)</tt>
 
The <tt>Item</tt> constructor creates simple menuitems.
 
; options: An object that defines the following properties:


<blockquote>
<blockquote>


; command: A function that will be called when the menuitem is clicked.  If the <tt>menu</tt> property is present, <tt>command</tt> will instead be called when any of the item's descendents is clickedIn that case, the commands of descendants will be invoked firstIn either case, it is called as <tt>command(context, data)</tt><tt>data</tt> is the <tt>data</tt> property of the item that was selected or <tt>undefined</tt> if the selected item has no data.  <tt>context</tt> is an object describing the context in which the menu was invoked. It has the following properties:
; context: If the item is added to the top-level context menu, this specifies the context under which the item will appearSee [[#Specifying Contexts]]If undefined, the page context is assumedThis property is ignored if the item is contained in a submenu.


<blockquote>
; data: An optional, arbitrary string that the extension may associate with the menuitem.


; node: The node the user clicked to invoke the menu.
; icon: The URL of an icon to display in the menuitem.  Optional.  Note that some environments, [https://bugzilla.mozilla.org/show_bug.cgi?id=527253 notably Gnome 2.28], do not support menuitem icons either by default or at all.


; document: The document containing <tt>node</tt>, i.e., <tt>node.ownerDocument</tt>.
; label: The label of the menuitem, a string. Required.


; window: The window containing <tt>document</tt>, i.e., <tt>node.ownerDocument.defaultView</tt>.
; onClick: An optional function that will be called when the menuitem is clicked.  It is called as <tt>onClick(contextObj, item)</tt>. <tt>contextObj</tt> is an object describing the context in which the menu was invoked. See [[#Context Descriptions]]. <tt>item</tt> is the item itself.


</blockquote>
</blockquote>


; data: An arbitrary string that the extension may associate with the menuitem.
==== Menu ====
 
<tt>Menu(options)</tt>
 
The <tt>Menu</tt> constructor creates items that expand into submenus.
 
; options: An object that defines the following properties:
 
<blockquote>
 
; context: If the item is added to the top-level context menu, this specifies the context under which the item will appear.  See [[#Specifying Contexts]].  If undefined, the page context is assumed.  This property is ignored if the item is contained in a submenu.
 
; icon: The URL of an icon to display in the menuitem.  Optional.  Note that some environments, [https://bugzilla.mozilla.org/show_bug.cgi?id=527253 notably Gnome 2.28], do not support menuitem icons either by default or at all.


; icon: The URL of an icon to display in the menuitem.  Note that some environments, [https://bugzilla.mozilla.org/show_bug.cgi?id=527253 notably Gnome 2.28], do not support menuitem icons either by default or at all.
; items: An array of menuitems that the submenu will contain.


; label: The label of the menuitem, a string.
; label: The label of the menuitem, a string.


; menu: An arrayIf defined, the menuitem expands into a submenuEach element in the array must be a menuitem.
; onClick: An optional function that will be called when any of the item's descendants is clicked(The commands of descendants are invoked first, in a bottom-up, bubbling manner.)  It is called as <tt>onClick(contextObj, item)</tt><tt>contextObj</tt> is an object describing the context in which the menu was invoked.  See [[#Context Descriptions]].  <tt>item</tt> is the item that was clicked.


</blockquote>
</blockquote>


==== Specifying Existing Menuitems ====
==== Separator ====
 
<tt>Separator()</tt>
 
The <tt>Separator</tt> constructor creates menu separators.  Separators can only be contained in <tt>Menu</tt>s; they can't be added to the top-level context menu.
 
=== Specifying Existing Menuitems ===
 
<span style="background-color: #fcc;"><em>This part of the proposal is under review.</em></span>
 
Items that are part of the context menu by default are identified by case-sensitive strings IDs.
 
TODO: Table of IDs, making up our own where XUL IDs don't exist, are inconsistent, or are ugly.  What to do about non-Firefox apps?
 
=== Context Descriptions ===
 
It would be useful if menuitem callbacks had a way of examining the context in which they are invoked.  For example, a command that downloads images needs to know the URL of the image that the user right-clicked when she opened the context menu.
 
When a menuitem's callback is called, it is passed an object describing the context in which the context menu was invoked.  This object has the following properties:
 
<blockquote>
 
; node: The node the user clicked to invoke the menu.
 
; document: The document containing <tt>node</tt>, i.e., <tt>node.ownerDocument</tt>.


Existing menuitems are always identified using targets. A target is either a string or regular expression. A string target is case-insensitively matched against the labels and IDs of menuitems. A regular expression target is tested against labels and IDs.
; window: The window containing <tt>document</tt>, i.e., <tt>node.ownerDocument.defaultView</tt>.


Multiple items in a menu may match a target, but action is only ever taken on the first matching item.
</blockquote>


=== Example Usage ===
=== Example Usage ===
Line 131: Line 215:


// Add "Edit Image" on images.
// Add "Edit Image" on images.
contextMenu.add("img", {
var imgCssSelector = "img";
var editImageItem = contextMenu.Item({
   label: "Edit Image",
   label: "Edit Image",
   command: function (context) {
   onClick: function (context) {
     editImage(context.node.src);
     editImage(context.node.src);
   }
   },
  context: imgCssSelector
});
});
contextMenu.add(editImageItem);


// Replace the "Search" menuitem with a menu that searches Google or Wikipedia
// Replace the "Search" menuitem with a menu that searches Google or
// when there is selected text.
// Wikipedia when there is selected text.
//
// NOTE: The API used by this example is under review.
var selection = require("selection");
var selection = require("selection");
contextMenu.replace(
function selectionExists() {
   function () {
  return !!selection.text;
     return !!selection.text;
}
var searchMenu = contextMenu.Menu({
  label: "Search",
   onClick: function (context, item) {
     context.window.location.href = item.data + selection.text;
   },
   },
   "Search",
   context: selectionExists,
   {
   items: [
    label: "Search",
     contextMenu.Item({
     menu: [
       label: "Google",
       { label: "Google", data: "http://www.google.com/search?q=" },
      data: "http://www.google.com/search?q="
       { label: "Wikipedia", data: "http://en.wikipedia.org/wiki/" }
    }),
    ],
    contextMenu.Item({
    command: function (context, data) {
       label: "Wikipedia",
      context.window.location.href = data + selection.text;
      data: "http://en.wikipedia.org/wiki/"
    }
    })
  }
  ]
);
});
contextMenu.replace("context-searchselect", searchMenu);


// Add "Edit Page Source" under the universal context.
// Add "Edit Page Source" when the menu is invoked on a
contextMenu.add(
// non-interactive portion of the page.
   function () {
var pageSourceItem = contextMenu.Item({
     return true;
  label: "Edit Page Source",
   onClick: function (context) {
     editSource(context.document.URL);
   },
   },
   {
   context: null
    label: "Edit Page Source",
});
    command: function (context) {
contextMenu.add(pageSourceItem);
      editSource(context.document.URL);
    }
  }
);


// Add "Edit Page Images" when the page contains at least one image.
// Add "Edit Page Images" when the page contains at least one image.
contextMenu.add(
function pageHasImages(context) {
   function (context) {
  return !!context.document.querySelector("img");
     return !!context.document.querySelector("img");
}
var editImagesItem = contextMenu.Item({
   label: "Edit Page Images",
  onClick: function (context) {
     var imgNodes = context.document.querySelectorAll("img");
    editImages(imgNodes);
   },
   },
   {
   context: pageHasImages
    label: "Edit Page Images",
});
    command: function (context) {
contextMenu.add(editImagesItem);
      var imgNodes = context.document.querySelectorAll("img");
      editImages(imgNodes);
    }
  }
);
</pre>
</pre>
=== Issues ===
* [[#Specifying Contexts|Specifying contexts]]
** People will see random code on the web that passes <tt>"*"</tt> as the context.  They won't understand what it means, they'll copy and paste, ruin the purpose of contexts.
*** Don't allow <tt>"*"</tt>?  Easy to circumvent, e.g., <tt>"html *"</tt> or <tt>"body *"</tt>, but at least it's clearer that <tt>"html *"</tt> is a selector.
*** Don't allow selectors that match <tt>html</tt> or <tt>body</tt>?  No, responsible devs that need to match the entire page but no part in particular will use those.  Doesn't prevent <tt>"body *"</tt> anyway.
*** Don't allow selectors that match every node in the page?  How to check that?
** Passing <tt>"*"</tt> is easier than passing a function or any other selector.
* [[#Specifying New Menuitems|Icons]]: Icon URLs should be able to point to resources in the XPI.  How?
* [[#Specifying New Menuitems|Context objects]]: It's simpler just to pass the node the user clicked, but that forces you to do <tt>node.ownerDocument.defaultView</tt> just to get the window.  Trade-off?
* [[#Specifying New Menuitems|Command]]: What should be the name of the <tt>command</tt> function on menuitems?

Latest revision as of 04:50, 21 April 2010

JEP 112 - Context Menu

  • Champion: Drew Willcoxon - adw at mozilla.com
  • Status: Accepted/Initial implementation introduced in Jetpack SDK 0.3
  • Bug Ticket: bug 548590
  • Type: API

Proposal

Provide access to the page's context menu through a module named context-menu.

This proposal recognizes the following principles:

  1. The domain of browser extensions is not a single page but the set of all the user's pages.
  2. The page's context menu is for UI related to the page and its content. It should not be used for contexts external to the page. It should not be used simply because it's handy.

Multiple extensions may simultaneously modify the context menu, but their modifications should not conflict. For example, if two extensions both replace a menuitem M, the net effect should be that M is removed from the menu and both extensions' items are present in place of M. Modifications to the menu are always performed on the unmodified menu and then merged to form the modified menu.

We mention briefly that HTML5 includes a specification for menus, but no browser implements it yet, and its scope is not that of browser extensions anyway.

Use Cases

  • Adding an item that acts on a portion of the page -- a node, selection, etc. Examples:
    • An image-editing extension adds an "Edit Image" item when the context menu is invoked on an image.
    • A search extension adds a "Search" item when the context menu is invoked while a selection exists.
    • An extension adds an "Open Text Link" item when the selection contains a URL outside of an anchor.
    • A DOM inspection extension adds an "Inspect This Node" item when the context menu is invoked anywhere in the page.
  • Adding an item that acts on the entire page. Examples:
    • An extension that makes it easy to edit page source adds an "Edit Page Source" item when the context menu is invoked anywhere on the page.
    • An image-editing extension adds an "Edit Page Images" item when the context menu is invoked anywhere on a page that has at least one image.

Non-Use Cases

  • Adding an item unrelated to the page and its content. Examples:
    • A tab-related extension adds an item that provides access to all the user's tabs.
    • An extension adds an item when it's three o'clock.
  • Radio and checkbox items.
  • The ability to modify the menu just before it's shown, and the ability to modify individual items already created by the extension.
  • The ability to modify existing items other than to replace them with new items.

Dependencies & Requirements

  • Access to content pages and their DOMs.
  • Access to the browser's DOM, including popupshowing and popuphiding events.
  • Menuitem icons should be package-able in XPIs, reference-able from code.

API Methods

add

add(menuitem)

Adds an item to the context menu. The position at which the item is inserted is at the discretion of Jetpack.

menuitem
The menuitem to add. See #Creating New Menuitems. Separators, however, can't be added.

insertBefore

This part of the proposal is under review.

insertBefore(target, menuitem)

Inserts a new item before an existing one. If the target item does not exist, this function silently fails, and the context menu not modified.

target
A value describing the existing item before which to insert the new item. See #Specifying Existing Menuitems.
menuitem
The menuitem to add. See #Creating New Menuitems.

remove

remove(item)

Permanently removes an item previously added to the context menu. This method should not be used to temporarily remove an item under particular contexts; for that, set an appropriate context when creating the item. See #Specifying Contexts.

item
A menuitem that was previously created and added to the menu. See #Creating New Menuitems.

replace

This part of the proposal is under review.

replace(target, menuitem)

Replaces an existing item in the context menu. If the target item does not exist, this function silently fails, and the context menu is not modified.

target
A value describing the existing item to replace. See #Specifying Existing Menuitems.
menuitem
The menuitem to add. See #Creating New Menuitems.

Specifying Contexts

Items should be added to the context menu, as its name implies, only when some particular context arises. Context can be related to page content or the page itself, but it should never be external to the page. (See #Non-Use Cases for examples.)

Instead of requiring consumers to manually add and remove items when particular contexts arise, this proposal introduces the notion that items are bound to one or more contexts, much as event listeners are bound to events. When the context menu is invoked, all of the items bound to the context giving rise to the menu are added to the menu. If no items are bound to the context, no items are added to the menu. This binding occurs through an item's context property.

Contexts may be specified with any of the following types:

string
A CSS selector. The context arises when the menu is invoked on a node that either matches this selector or has an ancestor that matches.
undefined or null
The page context. The context arises when the menu is invoked on a non-interactive portion of the page. The definition of "non-interactive" is at Jetpack's discretion.
function
An arbitrary predicate. The context arises when the function returns true. The function is passed an object describing the current context. See #Context_Descriptions.
array
An array of any of the other types. The context arises when any context in the array does.

An item's context property is a collection, similar to event listener collections common throughout Jetpack's various APIs. A single context may be bound by assigning one of the above types to the context property either on construction or after:

var item = contextMenu.Item({ context: "img" });
item.context = "img";

Multiple contexts may be bound by assigning an array of contexts either on construction or after:

var item = contextMenu.Item({ context: ["img", "a[href]"] });
item.context = ["img", "a[href]"];

Contexts may also be bound by calling the collection's add method, which takes a single context or array of contexts:

item.context.add("img");
item.context.add(["img", "a[href]"]);

Finally, contexts may be unbound by calling the collection's remove method, which takes a single context or array of contexts :

item.context.remove("img");
item.context.remove(["img", "a[href]"]);

When an item is bound to more than one context, it is added to menus arising from any of those contexts.

Creating New Menuitems

New menuitems are created using one of the constructors exported by the module, Item, Menu, and Separator.

Item

Item(options)

The Item constructor creates simple menuitems.

options
An object that defines the following properties:
context
If the item is added to the top-level context menu, this specifies the context under which the item will appear. See #Specifying Contexts. If undefined, the page context is assumed. This property is ignored if the item is contained in a submenu.
data
An optional, arbitrary string that the extension may associate with the menuitem.
icon
The URL of an icon to display in the menuitem. Optional. Note that some environments, notably Gnome 2.28, do not support menuitem icons either by default or at all.
label
The label of the menuitem, a string. Required.
onClick
An optional function that will be called when the menuitem is clicked. It is called as onClick(contextObj, item). contextObj is an object describing the context in which the menu was invoked. See #Context Descriptions. item is the item itself.

Menu

Menu(options)

The Menu constructor creates items that expand into submenus.

options
An object that defines the following properties:
context
If the item is added to the top-level context menu, this specifies the context under which the item will appear. See #Specifying Contexts. If undefined, the page context is assumed. This property is ignored if the item is contained in a submenu.
icon
The URL of an icon to display in the menuitem. Optional. Note that some environments, notably Gnome 2.28, do not support menuitem icons either by default or at all.
items
An array of menuitems that the submenu will contain.
label
The label of the menuitem, a string.
onClick
An optional function that will be called when any of the item's descendants is clicked. (The commands of descendants are invoked first, in a bottom-up, bubbling manner.) It is called as onClick(contextObj, item). contextObj is an object describing the context in which the menu was invoked. See #Context Descriptions. item is the item that was clicked.

Separator

Separator()

The Separator constructor creates menu separators. Separators can only be contained in Menus; they can't be added to the top-level context menu.

Specifying Existing Menuitems

This part of the proposal is under review.

Items that are part of the context menu by default are identified by case-sensitive strings IDs.

TODO: Table of IDs, making up our own where XUL IDs don't exist, are inconsistent, or are ugly. What to do about non-Firefox apps?

Context Descriptions

It would be useful if menuitem callbacks had a way of examining the context in which they are invoked. For example, a command that downloads images needs to know the URL of the image that the user right-clicked when she opened the context menu.

When a menuitem's callback is called, it is passed an object describing the context in which the context menu was invoked. This object has the following properties:

node
The node the user clicked to invoke the menu.
document
The document containing node, i.e., node.ownerDocument.
window
The window containing document, i.e., node.ownerDocument.defaultView.

Example Usage

var contextMenu = require("context-menu");

// Add "Edit Image" on images.
var imgCssSelector = "img";
var editImageItem = contextMenu.Item({
  label: "Edit Image",
  onClick: function (context) {
    editImage(context.node.src);
  },
  context: imgCssSelector
});
contextMenu.add(editImageItem);

// Replace the "Search" menuitem with a menu that searches Google or
// Wikipedia when there is selected text.
//
// NOTE: The API used by this example is under review.
var selection = require("selection");
function selectionExists() {
  return !!selection.text;
}
var searchMenu = contextMenu.Menu({
  label: "Search",
  onClick: function (context, item) {
    context.window.location.href = item.data + selection.text;
  },
  context: selectionExists,
  items: [
    contextMenu.Item({
      label: "Google",
      data: "http://www.google.com/search?q="
    }),
    contextMenu.Item({
      label: "Wikipedia",
      data: "http://en.wikipedia.org/wiki/"
    })
  ]
});
contextMenu.replace("context-searchselect", searchMenu);

// Add "Edit Page Source" when the menu is invoked on a
// non-interactive portion of the page.
var pageSourceItem = contextMenu.Item({
  label: "Edit Page Source",
  onClick: function (context) {
    editSource(context.document.URL);
  },
  context: null
});
contextMenu.add(pageSourceItem);

// Add "Edit Page Images" when the page contains at least one image.
function pageHasImages(context) {
  return !!context.document.querySelector("img");
}
var editImagesItem = contextMenu.Item({
  label: "Edit Page Images",
  onClick: function (context) {
    var imgNodes = context.document.querySelectorAll("img");
    editImages(imgNodes);
  },
  context: pageHasImages
});
contextMenu.add(editImagesItem);