We recently updated our AlphabetizeMenus component to work with 11g. Do not worry, it is still backwards compatible with 10g. You can grab the newest version build_5_20110913 here. Sometimes a picture is worth a thousand words, so I will demonstrate with some before and after screenshots. After the screenshots will be an in-depth code review of what all is being done to accomplish menu sorting on both 10g and 11g.
Trays – Before (unsorted)

Trays – After (sorted)

Top Menus – Before (unsorted)

Top Menus – After (sorted)

The first thing we do in this component is hook into the dynamichtml include for custom_finish_layout_init. We want to first call all other includes that might be using this hook by calling super.custom_finish_layout_init. Almost any time you want to add something to a dynamichtml include, the first thing you should do is call super on that include.
<@dynamichtml custom_finish_layout_init@>
<$include super.custom_finish_layout_init$>
Next, we want to determine what version of UCM this component is installed on. There is an Idoc Script variable, ProductVersion, in 11g that will start with 11 if the UCM instance is 11g. If it is, we call a custom include for 11g. Otherwise, we call a custom include for 10g.
<$if ProductVersion like "11*"$>
<$include alphabetize_menus_11g$>
<$else$>
<$include alphabetize_menus_10g$>
<$endif$>
And here is all that code put together.
<@dynamichtml custom_finish_layout_init@>
<$include super.custom_finish_layout_init$>
<$if isTrue(#env.EnableMenuSorting)$>
<$if ProductVersion like "11*"$>
<$include alphabetize_menus_11g$>
<$else$>
<$include alphabetize_menus_10g$>
<$endif$>
<$endif$>
<@end@>
10g Menus
We first create a new array. Then we get the children of the node that was passed in.
var sortData = new Array();
var children = navBuilder.getNodeById(menuID).childNodes;
Next, we want to loop through the children. If we find children of children (I suppose you would call these grandchildren), then we recursively call the sortMenu function. After we have finished dealing with any children, we build a JSON style array object containing the node object (what object type that may be) and the text label.
for (var i = 0; i < children.length; i++) {
if (children[i].childNodes.length > 0) {
this.sortMenu(children[i].getAttribute("id"));
}
sortData[sortData.length] = {
id: children[i].getAttribute("id"),
label: children[i].getAttribute("label")
};
}
Next, we need to sort the array of items on the current level and we do this by defining our own JavaScript sort method.
sortData.sort (
function (a, b) {
if (a.label < b.label)
return -1;
if (a.label > b.label)
return 1;
return 0; [[% a == b %]]
}
);
After our data (for the current level) is sorted, we loop over the sorted array and add/remove the items to get them in the correctly alphabetized order.
for (var i = 0; i < sortData.length; i++) {
navBuilder.moveItemInto(menuID, sortData[i].id, false);
if (its.safari) {
navBuilder.deleteItem(sortData[i].id);
}
}
The last step is to call our menuSortingMachine.sortMenu function. Most people choose to pass in “NAVTREE” which will sort all the menus (except the menubar level items such as My Content Server, Adminsitration, Browse Content, etc.). However, you can pass in a specific “ADMINISTRATION” and that will sort only that menu and its children.
menuSortingMachine.sortMenu("NAVTREE");
And finally all the 10g Trays and Top Menus layout sorting code together.
var menuSortingMachine = {
sortMenu:function(menuID) {
var sortData = new Array();
var children = navBuilder.getNodeById(menuID).childNodes;
for (var i = 0; i < children.length; i++) {
if (children[i].childNodes.length > 0) {
this.sortMenu(children[i].getAttribute("id"));
}
sortData[sortData.length] = {
id: children[i].getAttribute("id"),
label: children[i].getAttribute("label")
};
}
sortData.sort (
function (a, b) {
if (a.label < b.label)
return -1;
if (a.label > b.label)
return 1;
return 0; [[% a == b %]]
}
);
for (var i = 0; i < sortData.length; i++) {
navBuilder.moveItemInto(menuID, sortData[i].id, false);
if (its.safari) {
navBuilder.deleteItem(sortData[i].id);
}
}
}
};
[[% menuSortingMachine.sortMenu("ADMINISTRATION"); %]]
menuSortingMachine.sortMenu("NAVTREE");
11g Menus
The navBuilder object that we used in 10g should be considered deprecated in 11g. Instead, we have new objects we can use manipulate menus such as: YAHOO.widget.MenuManager, YAHOO.widget.Menu, YAHOO.widget.MenuItem, YAHOO.widget.TreeView, YAHOO.widget.Node
oMenuBarA is a YAHOO.widget.MenuBar for menuA. This is the menu that contains items such(such as “Search” and “New Check In”).
oMenuBarB is a YAHOO.widget.MenuBar for menuB.
oTreeViewA is a YAHOO.idc.widget.TrayTreeView for the side tray (in Trays layout). Many of the methods from YAHOO.widget.TreeView can be used for the oTreeViewA object.
For more information on the new menu objects in 11g, seeKyle Hatlestad’s post Modifying Navigation Menus in UCM 11g.
11g Top Menus
The first thing we want to do is get the submenus or what we called children in 10g.
var subMenus = menu.getSubmenus();
Next. we want to loop through the submenus and recursively call the sortMenu function.
for (var i = 0; i < subMenus.length; i++) {
menuSortingMachine.sortMenu(subMenus[i], new Boolean(true));
}
Next, we always add a check to make sure we want to sort this level and check to make sure the menu parent exists.
if (sortLevel == true || typeof menu.parent != "undefined") {}
Next, we get an array of the current level’s children menuItems and we also create a new array to hold our sorted data.
var items = menu.getItems();
var sortData = new Array();
Next, we want to loop through the menuItems and build a JSON style array object containing the MenuItem object and the text label.
for (var j = 0; j < items.length; j++) {
sortData[j] = {
element: items[j].element,
label: items[j].cfg.getProperty("text")
};
}
Like in 10g, we need to sort the array of items on the current level and we do this by defining our own JavaScript sort method.
sortData.sort (
function (a, b) {
if (a.label < b.label)
return -1;
if (a.label > b.label)
return 1;
return 0; [[% a == b %]]
}
);
Similar to 10g, after our data (for the current level) is sorted, we loop over the sorted array and add the items to get them in the correctly alphabetized order.
for (var k = 0; k < sortData.length; k++) {
menu.element.appendChild(sortData[k].element);
}
The last step is to call the menuSortingMachine.sortMenu function. We first check to make sure the variable (oMenuBarA or oMenuBarB has a value) and if so, we can use that menu to start sorting from. If we want the menubar level items for oMenuBarA or oMenuBarB to get sorted alphabetically, we can pass in true boolean value.
if (typeof oMenuBarA != "undefined" && oMenuBarA != undefined) {
menuSortingMachine.sortMenu(oMenuBarA, new Boolean(false));
}
if (typeof oMenuBarB != "undefined" && oMenuBarB != undefined) {
menuSortingMachine.sortMenu(oMenuBarB, new Boolean(false));
[[% menuSortingMachine.sortMenu(YAHOO.widget.MenuManager.getMenu("MENU_B_ADMINISTRATION"), new Boolean(true)) %]];
}
And finally all the Top Menus layout sorting code together.
var menuSortingMachine = {
sortMenu:function(menu, sortLevel) {
var subMenus = menu.getSubmenus();
for (var i = 0; i < subMenus.length; i++) {
menuSortingMachine.sortMenu(subMenus[i], new Boolean(true));
}
if (sortLevel == true || typeof menu.parent != "undefined") {
var items = menu.getItems();
var sortData = new Array();
for (var j = 0; j < items.length; j++) {
sortData[j] = {
element: items[j].element,
label: items[j].cfg.getProperty("text")
};
}
sortData.sort (
function (a, b) {
if (a.label < b.label) {
return -1;
}
else if (a.label > b.label) {
return 1;
}
return 0; [[% a == b %]]
}
);
for (var k = 0; k < sortData.length; k++) {
menu.element.appendChild(sortData[k].element);
}
}
}
};
if (typeof oMenuBarA != "undefined" && oMenuBarA != undefined) {
menuSortingMachine.sortMenu(oMenuBarA, new Boolean(false));
}
if (typeof oMenuBarB != "undefined" && oMenuBarB != undefined) {
menuSortingMachine.sortMenu(oMenuBarB, new Boolean(false));
[[% menuSortingMachine.sortMenu(YAHOO.widget.MenuManager.getMenu("MENU_B_ADMINISTRATION"), new Boolean(true)) %]];
}
11g Trays Menus
For Trays layout, we can do very similar code to that for 10g, except we have some different objects and method calls. We first create a new array. Then we get the children of the node that was passed in.
var sortData = new Array();
var children = node.children;
Similar to 10g, we want to loop through the children and recursively call the sortMenu function. Then we build a JSON style array object containing the YAHOO.widget.Node object and the text label.
for (var i = 0; i < children.length; i++) {
if (children[i].children.length > 0) {
this.sortMenu(children[i]);
}
sortData[sortData.length] = {
node: children[i],
label: children[i].label
};
}
Like in 10g, we need to sort the array of items on the current level and we do this by defining our own JavaScript sort method.
sortData.sort (
function (a, b) {
if (a.label < b.label)
return -1;
if (a.label > b.label)
return 1;
return 0; [[% a == b %]]
}
);
Similar to 10g, after our data (for the current level) is sorted, we loop over the sorted array and add/remove the items to get them in the correctly alphabetized order.
for (var i = 0; i < sortData.length; i++) {
oTreeViewA.popNode(sortData[i].node);
node.appendChild(sortData[i].node);
}
The last step is to call the menuSortingMachine.sortMenu function. We first check to make sure the variable (oTreeViewA has a value) and then we can use the getRoot() method on the oTreeViewA variable to get the root node to start sorting from.
if (typeof oTreeViewA != "undefined" && oTreeViewA != undefined) {
menuSortingMachineTrays.sortMenu(oTreeViewA.getRoot());
}
And finally all the Trays layout sorting code together.
var menuSortingMachineTrays = {
sortMenu:function(node) {
var sortData = new Array();
var children = node.children;
for (var i = 0; i < children.length; i++) {
if (children[i].children.length > 0) {
this.sortMenu(children[i]);
}
sortData[sortData.length] = {
node: children[i],
label: children[i].label
};
}
sortData.sort (
function (a, b) {
if (a.label < b.label)
return -1;
if (a.label > b.label)
return 1;
return 0; [[% a == b %]]
}
);
for (var i = 0; i < sortData.length; i++) {
oTreeViewA.popNode(sortData[i].node);
node.appendChild(sortData[i].node);
}
}
};
if (typeof oTreeViewA != "undefined" && oTreeViewA != undefined) {
menuSortingMachineTrays.sortMenu(oTreeViewA.getRoot());
}
Note: Any code you see between [[% %]] is an Idoc Script comment.