Tutorials: Intro, command, pref, menu, munger

Creating a Menu

This time, we're going to be making a menu item for our /slap command, which we've been making over the previous tutorials. This menu item will let you right-click a line of text, and slap whoever said it.

How to Define Menu

Surprisingly enough, we'll be using the Menu Manager for this tutorial, and it lets us add menu items like this function:

   client.menuManager.appendMenuItem(parentNode, beforeNode, commandName, attribs);

The following is taken directly from the source, and quickly describes this function's parameters.

Parameter Description
parentNode DOM Node to insert into.
beforeNode DOM Node already contained by parentNode, to insert before.
command A reference to the CommandRecord this menu item will represent.
attribs Object containing HTML attributes to set on the element.

This clearly isn't going to be quite as simple as in the previous tutorials, since we want to put a new item in the middle of an existing menu. This is why the function we're using has some strange paramters, but they will all be explained in detail later.

  • It's probably fairly clear what the command parameter will be for - the name of the command to run, of course!
  • attribs is a little more fun, it's a JavaScript object with all the other properties we want set on the menu item, which include the label displayed to the user. We won't be needing any of the cool stuff here, but we'll mention it in a bit.
  • So where does out menu item show up? That's what parentNode and beforeNode are for. We need to pass in the menu (as parentNode) and, if we want, the item to place ours before. We can of course leave beforeNode as null to add our item to the end instead.

Our Menu Item

Now we come to defining our menu item. But before we can call the Menu Manager we need to find the menu we're changing. To do this, we need to investigate the Command Manager, to find the right item. I have written the following utility function for just this purpose:

   function getMenuItemForCommand(menuManager, commandName, menuName) {
       // Find the menu node for a particular command. Pass in the Menu
       // Manager, name of the command, and the name of the menu (a
       // command may be in multiple menus).
       
       var commands = menuManager.commandManager.commands;
       
       // Make sure we've got a real command.
       if (!(commandName in commands))
           return null;
       var command = commands[commandName];
       
       // And check this command has GUI items to check.
       if (!("uiElements" in command))
           return null;
       var ui = command.uiElements;
       
       // Go through each item, and check if it's "on" the menu we want.
       for (var i = 0; i < ui.length; i++) {
           if (ui[i].parentNode &&
               ui[i].parentNode.getAttribute("menuName") == menuName)
           {
               return ui[i];
           }
       }
   }

This function will return the actual menu item associated with a command, on a given menu, if it exists, or null if it has any problems. This means we can find the right menu item, even if the command has menu items on multiple menus.

   var whoisMenu = getMenuItemForCommand(client.menuManager, "whois", "context:messages");

This line will (using the above function) locate the menu item for the /whois command on the context menu in the main display. We will also use it on "context:userlist" menu to find the menu item on that content menu.

Why <nickname>?

When a menu is opened in ChatZilla, is is given a "context" object. This is just a JavaScript object, with a bunch of properties set containing information about the current state and thing that was clicked. When right-clicking on a user, like the two menus we're dealing with here, one of these properties is called nickname and contains the nickname of the user we right-clicked.

To run commands from the menus, the Menu Manager must decide both whether it is enabled, and how to fill in the parameters defined for it. This is done by mapping the context object to the parameters, so by naming our paramaeter nickname it will get the nickname from this context object. A menu item is only enabled if all it's required parameters have values on this context object.

Bringing it All Together

We have the basics ready now how to add the item, and how to find where we want it so lets go do it.

   // Get the menu positions.
   var whoisMenu1 = getMenuItemForCommand(client.menuManager, "whois", "context:messages");
   var whoisMenu2 = getMenuItemForCommand(client.menuManager, "whois", "context:userlist");
   
   // Add the command.
   client.menuManager.appendMenuItem(whoisMenu1.parentNode, whoisMenu1, "slap", { label: "Slap" });
   client.menuManager.appendMenuItem(whoisMenu2.parentNode, whoisMenu2, "slap", { label: "Slap" });

Simple, no? Well, just a quick explanation of what's going on...

  1. We use the function getMenuItemForCommand to locate the "whois" menu item on two existing menus.
  2. We stick a new menu item for "slap" into these two menus, just after "whois".

    Notice that we're passing a label to the Menu Manager in the last parameter. This is so that it's labeled "Slap" not "slap" since it defaults to using the command name. We'll probably cover some of the interesting things you can put in here in a later tutorial (FIXME: Using the Menu Manager?).

Now that we've finished this tutorial, here's the full code for referrence.

Powered by the Content Parser System, copyright 2002 - 2010 James G. Ross.