mirror of
https://github.com/Sarjuuk/aowow.git
synced 2025-11-29 15:58:16 +08:00
* split global.js into its components, so it can be reasonably processed by setup * make reputation requirements configurable * move Markup and Locale back into global.js (removed associated build scripts) * extend Icon to display iconId in lightbox popup
988 lines
24 KiB
JavaScript
988 lines
24 KiB
JavaScript
/*
|
|
Menu class
|
|
*/
|
|
|
|
var MENU_IDX_ID = 0; // ID: A number or string; null makes the menu item a separator
|
|
var MENU_IDX_NAME = 1; // Name: A string
|
|
var MENU_IDX_URL = 2; // URL: A string for the URL, or a function to call when the menu item is clicked
|
|
var MENU_IDX_SUB = 3; // Submenu: Child menu
|
|
var MENU_IDX_OPT = 4; // Options: JSON array with additional options
|
|
|
|
var Menu = new function()
|
|
{
|
|
var self = this;
|
|
|
|
/***********/
|
|
/* PUBLIC */
|
|
/**********/
|
|
|
|
self.add = function(node, menu, opt) // Add a menu to a DOM element
|
|
{
|
|
if(!opt) opt = $.noop;
|
|
|
|
var $node = $(node);
|
|
|
|
$node.data('menu', menu);
|
|
|
|
if(opt.showAtCursor)
|
|
{
|
|
$node.click(rootNodeClick);
|
|
}
|
|
else
|
|
{
|
|
$node
|
|
.mouseover(rootNodeOver)
|
|
.mouseout(rootNodeOut);
|
|
}
|
|
};
|
|
|
|
self.remove = function(node) // Remove menu from DOM element
|
|
{
|
|
$(node)
|
|
.data('menu', null)
|
|
.unbind('click', rootNodeClick)
|
|
.unbind('mouseover', rootNodeOver)
|
|
.unbind('mouseout', rootNodeOut);
|
|
};
|
|
|
|
self.show = function(menu, node)
|
|
{
|
|
var $node = $(node);
|
|
|
|
show(menu, $node);
|
|
}
|
|
|
|
self.showAtCursor = function(menu, event) // {event} must have been normalized by jQuery
|
|
{
|
|
showAtXY(menu, event.pageX, event.pageY);
|
|
}
|
|
|
|
self.showAtXY = function(menu, x, y)
|
|
{
|
|
showAtXY(menu, x, y);
|
|
}
|
|
|
|
self.hide = function()
|
|
{
|
|
rootNodeOut();
|
|
}
|
|
|
|
self.addButtons = function(dest, menu) // Create a set of menu buttons (as seen on the homepage or in the top bar)
|
|
{
|
|
var $dest = $(dest);
|
|
if(!$dest.length)
|
|
return;
|
|
|
|
var $buttons = $('<span class="menu-buttons"></span>');
|
|
|
|
$.each(menu, function(idx, menuItem)
|
|
{
|
|
if(isSeparator(menuItem)) return;
|
|
|
|
var $a = $('<a></a>')
|
|
var $span = $('<span></span>', {
|
|
text: menuItem[MENU_IDX_NAME]
|
|
}).appendTo($a);
|
|
|
|
self.linkifyItem(menuItem, $a);
|
|
|
|
if(hasSubmenu(menuItem))
|
|
{
|
|
$span.addClass('hassubmenu');
|
|
self.add($a, menuItem[MENU_IDX_SUB]);
|
|
}
|
|
|
|
$buttons.append($a);
|
|
});
|
|
|
|
$dest.append($buttons);
|
|
};
|
|
|
|
self.linkifyItem = function(menuItem, $a) // Also used by PageTemplate when generating the top tabs
|
|
{
|
|
var opt = self.getItemOpt(menuItem);
|
|
|
|
if(!menuItem[MENU_IDX_URL]) // Unlinked
|
|
{
|
|
$a.attr('href', 'javascript:;');
|
|
$a.addClass('unlinked');
|
|
|
|
return;
|
|
}
|
|
|
|
if(typeof menuItem[MENU_IDX_URL] == 'function') // Function
|
|
{
|
|
$a.attr('href', 'javascript:;');
|
|
$a.click(hide);
|
|
$a.click(menuItem[MENU_IDX_URL]);
|
|
}
|
|
else // URL
|
|
{
|
|
var url = self.getItemUrl(menuItem);
|
|
$a.attr('href', url);
|
|
|
|
if(opt.newWindow || g_isExternalUrl(url))
|
|
$a.attr('target', '_blank');
|
|
|
|
if(opt.rel)
|
|
$a.attr('rel', opt.rel);
|
|
}
|
|
if(typeof menuItem[MENU_IDX_OPT] == 'object' && menuItem[MENU_IDX_OPT].className)
|
|
$a.addClass(menuItem[MENU_IDX_OPT].className);
|
|
};
|
|
|
|
self.updateItem = function(menuItem) // Also used by PageTemplate when the breadcrumb changes
|
|
{
|
|
var $a = menuItem.$a;
|
|
if(!$a)
|
|
return;
|
|
|
|
var opt = self.getItemOpt(menuItem);
|
|
|
|
$a.removeClass('checked tinyicon icon');
|
|
$a.css('background-image', '');
|
|
|
|
if(menuItem.checked)
|
|
{
|
|
$a.addClass('checked');
|
|
}
|
|
else if(opt.tinyIcon)
|
|
{
|
|
$a.addClass('tinyicon');
|
|
$a.css('background-image', 'url(' + (opt.tinyIcon.indexOf('/') != -1 ? opt.tinyIcon : g_staticUrl + '/images/wow/icons/tiny/' + opt.tinyIcon.toLowerCase() + '.gif') + ')');
|
|
}
|
|
else if(opt.icon)
|
|
{
|
|
$a.addClass('icon');
|
|
$a.css('background-image', 'url(' + opt.icon + ')');
|
|
}
|
|
else if(opt.socketColor && g_file_gems[opt.socketColor])
|
|
{
|
|
$a.addClass('socket-' + g_file_gems[opt.socketColor]);
|
|
}
|
|
|
|
var className = (opt['class'] || opt.className);
|
|
if(className)
|
|
$a.addClass(className);
|
|
};
|
|
|
|
// MENU UTILITY FUNCTIONS
|
|
|
|
self.hasMenu = function(node)
|
|
{
|
|
var $node = $(node);
|
|
|
|
return $node.data('menu') != null;
|
|
}
|
|
|
|
self.modifyUrl = function(menuItem, params, opt)
|
|
{
|
|
var json = { params: params, opt: opt };
|
|
|
|
traverseMenuItem(menuItem, function(x)
|
|
{
|
|
x.modifyUrl = json;
|
|
});
|
|
|
|
PageTemplate.updateBreadcrumb();
|
|
};
|
|
|
|
self.fixUrls = function(menu, url, opt)
|
|
{
|
|
opt = opt || {};
|
|
|
|
opt.hash = (opt.hash ? '#' + opt.hash : '');
|
|
|
|
fixUrls(menu, url, opt, 0);
|
|
};
|
|
|
|
self.sort = function(menu) // Sort a specific menu
|
|
{
|
|
if(hasSeparators(menu))
|
|
sortSeparatedMenu(menu);
|
|
else
|
|
sortMenu(menu);
|
|
};
|
|
|
|
self.sortSubmenus = function(menu, paths) // Sort the submenus of {menu} specified by {paths}
|
|
{
|
|
$.each(paths, function(idx, path)
|
|
{
|
|
var submenu = self.findItem(menu, path);
|
|
if(submenu && submenu[MENU_IDX_SUB])
|
|
self.sort(submenu[MENU_IDX_SUB]);
|
|
});
|
|
};
|
|
|
|
self.implode = function(menu, opt) // Return a new menu where items preceded by a heading are merged into a single item w/ a submenu
|
|
{
|
|
if(!opt) opt = $.noop;
|
|
|
|
var result = [];
|
|
var groupedMenu;
|
|
|
|
if(opt.createHeadinglessGroup)
|
|
{
|
|
groupedMenu = [];
|
|
result.push([0, '', null, groupedMenu]);
|
|
}
|
|
|
|
$.each(menu, function(idx, menuItem)
|
|
{
|
|
if(isSeparator(menuItem))
|
|
{
|
|
groupedMenu = [];
|
|
result.push([0, menuItem[MENU_IDX_NAME], null, groupedMenu]);
|
|
}
|
|
else
|
|
{
|
|
if(groupedMenu)
|
|
groupedMenu.push(menuItem);
|
|
else
|
|
result.push(menuItem);
|
|
}
|
|
});
|
|
|
|
return result;
|
|
};
|
|
|
|
self.findItem = function(menu, path) // Return the menu item specified by {path} in {menu}
|
|
{
|
|
return self.getFullPath(menu, path).pop();
|
|
};
|
|
|
|
self.getFullPath = function(menu, path) // Return an array with all the menu items specified by {path} in {menu}
|
|
{
|
|
var result = [];
|
|
|
|
for(var i = 0; i < path.length; ++i)
|
|
{
|
|
var pos = findItemPosById(menu, path[i]);
|
|
|
|
if(pos != -1)
|
|
{
|
|
var menuItem = menu[pos];
|
|
menuItem.parentMenu = menu;
|
|
menu = menuItem[MENU_IDX_SUB];
|
|
|
|
result.push(menuItem);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
self.getItemUrl = function(menuItem)
|
|
{
|
|
var url = menuItem[MENU_IDX_URL];
|
|
if(!url)
|
|
return null;
|
|
|
|
var opt = self.getItemOpt(menuItem);
|
|
|
|
if(menuItem.modifyUrl)
|
|
url = g_modifyUrl(url, menuItem.modifyUrl.params, menuItem.modifyUrl.opt);
|
|
|
|
return url;
|
|
};
|
|
|
|
self.getItemOpt = function(menuItem)
|
|
{
|
|
if(!menuItem[MENU_IDX_OPT])
|
|
menuItem[MENU_IDX_OPT] = {};
|
|
|
|
return menuItem[MENU_IDX_OPT];
|
|
};
|
|
|
|
self.removeItemById = function(menu, id)
|
|
{
|
|
var pos = findItemPosById(menu, id);
|
|
if(pos != -1)
|
|
menu.splice(pos, 1);
|
|
};
|
|
|
|
self.setSubmenu = function (menuItem, submenu)
|
|
{
|
|
menuItem[MENU_IDX_SUB] = submenu;
|
|
};
|
|
|
|
|
|
/***********/
|
|
/* PRIVATE */
|
|
/***********/
|
|
|
|
var DELAY_BEFORESHOW = 25;
|
|
var DELAY_BEFOREHIDE = 333;
|
|
|
|
var MAX_COLUMNS = 4;
|
|
|
|
// Should match the styles used in Menu.css
|
|
var SIZE_SHADOW_WIDTH = 6;
|
|
var SIZE_SHADOW_HEIGHT = 6;
|
|
var SIZE_BORDER_HEIGHT = 3;
|
|
var SIZE_MENUITEM_HEIGHT = 26;
|
|
|
|
var inited = false;
|
|
var timer;
|
|
var $rootNode;
|
|
var visibleMenus = {};
|
|
var openLinks = {};
|
|
var divCache = {};
|
|
var menuItemsCache = {};
|
|
var nextUniqueId = 0;
|
|
|
|
function init()
|
|
{
|
|
if(inited) return;
|
|
inited = true;
|
|
|
|
// Run a test to get the height of a menu item (in case it's different in some browser/OS combos)
|
|
var $div = $('<div class="menu"><a href="#"><span>ohai</span></a></div>').css({left: '-1000px', top: '-1000px'}).appendTo(document.body);
|
|
var height = $div.children('a').outerHeight();
|
|
$div.remove();
|
|
if(height > 15) // Safety
|
|
SIZE_MENUITEM_HEIGHT = height;
|
|
}
|
|
|
|
function show(menu, $node)
|
|
{
|
|
if($rootNode)
|
|
$rootNode.removeClass('open');
|
|
|
|
$rootNode = $node;
|
|
$rootNode.addClass('open');
|
|
|
|
displayFirstMenu(menu);
|
|
}
|
|
|
|
function hide()
|
|
{
|
|
if($rootNode)
|
|
{
|
|
$rootNode.removeClass('open');
|
|
$rootNode = null;
|
|
}
|
|
|
|
hideMenus(0);
|
|
}
|
|
|
|
function showAtXY(menu, x, y)
|
|
{
|
|
clearTimeout(timer);
|
|
|
|
displayFirstMenu(menu, x, y);
|
|
}
|
|
|
|
function displayFirstMenu(menu, x, y)
|
|
{
|
|
closeLinks(0);
|
|
displayMenu(menu, 0, x, y);
|
|
hideMenus(1);
|
|
}
|
|
|
|
function displayMenu(menu, depth, x, y)
|
|
{
|
|
init();
|
|
|
|
beforeShowMenu(menu);
|
|
|
|
var $div = createDiv(depth);
|
|
var $menuItems = createMenuItems(menu);
|
|
var $menu = createMenu($menuItems, depth);
|
|
|
|
$div.append($menu);
|
|
|
|
var animated = !isMenuVisible(depth);
|
|
visibleMenus[depth] = $div;
|
|
|
|
var pos = getMenuPosition($div, depth, x, y);
|
|
$div.css({
|
|
left: pos.x + 'px',
|
|
top: pos.y + 'px'
|
|
});
|
|
|
|
revealDiv($div, animated);
|
|
}
|
|
|
|
function createDiv(depth)
|
|
{
|
|
if(divCache[depth])
|
|
{
|
|
var $div = divCache[depth];
|
|
$div.children().detach(); // Similar to $div.empty(), except custom data is preserved.
|
|
return $div;
|
|
}
|
|
|
|
var $div = $('<div class="menu"></div>')
|
|
.mouseover(menuDivOver)
|
|
.mouseleave(menuDivOut)
|
|
.delegate('a', 'mouseenter', { depth: depth }, menuItemOver)
|
|
.delegate('a', 'click', menuItemClick);
|
|
|
|
$div.appendTo(document.body);
|
|
|
|
divCache[depth] = $div;
|
|
return $div;
|
|
}
|
|
|
|
function createMenuItems(menu)
|
|
{
|
|
var uid = getUniqueId(menu);
|
|
|
|
if(menuItemsCache[uid])
|
|
return menuItemsCache[uid];
|
|
|
|
var nextSeparator; // Used to make sure a separator is always followed by a visible item
|
|
var menuItems = [];
|
|
|
|
$.each(menu, function(idx, menuItem)
|
|
{
|
|
if(!isItemVisible(menuItem)) return;
|
|
|
|
$a = createMenuItem(menuItem);
|
|
|
|
if(isSeparator(menuItem))
|
|
{
|
|
nextSeparator = $a;
|
|
return;
|
|
}
|
|
|
|
if(nextSeparator)
|
|
{
|
|
menuItems.push(nextSeparator);
|
|
nextSeparator = null;
|
|
}
|
|
|
|
menuItems.push($a);
|
|
});
|
|
|
|
var $menuItems = $(menuItems);
|
|
|
|
menuItemsCache[menu] = $menuItems;
|
|
return $menuItems;
|
|
}
|
|
|
|
function createMenuItem(menuItem)
|
|
{
|
|
beforeCreateMenuItem(menuItem);
|
|
|
|
var $a = $('<a></a>');
|
|
|
|
menuItem.$a = $a;
|
|
$a.data('menuItem', menuItem);
|
|
|
|
self.linkifyItem(menuItem, $a);
|
|
self.updateItem(menuItem);
|
|
|
|
// Separator
|
|
if(isSeparator(menuItem))
|
|
{
|
|
$a.addClass('separator');
|
|
$a.text(menuItem[MENU_IDX_NAME]);
|
|
|
|
return $a;
|
|
}
|
|
|
|
var $span = $('<span></span>');
|
|
$span.text(menuItem[MENU_IDX_NAME]);
|
|
$span.appendTo($a);
|
|
|
|
// Submenu
|
|
if(hasSubmenu(menuItem))
|
|
$span.addClass('hassubmenu');
|
|
|
|
return $a;
|
|
}
|
|
|
|
function createMenu($menuItems, depth)
|
|
{
|
|
var $a = $rootNode;
|
|
var $w = $(window);
|
|
var nItems = $menuItems.length;
|
|
var availableHeight = $w.height() - (SIZE_BORDER_HEIGHT * 2) - SIZE_SHADOW_HEIGHT;
|
|
var nItemsThatCanFit = Math.floor(Math.max(0, availableHeight) / SIZE_MENUITEM_HEIGHT);
|
|
|
|
// 1 column
|
|
if(nItemsThatCanFit >= nItems)
|
|
{
|
|
var $outerDiv = $('<div class="menu-outer"></div>');
|
|
var $innerDiv = $('<div class="menu-inner"></div>');
|
|
|
|
$menuItems.each(function () { $innerDiv.append(this) });
|
|
$outerDiv.append($innerDiv);
|
|
|
|
$outerDiv.contextmenu($WH.rf); // aowow custom: prevent browser context menu when right clicking to get our context menu as it placed under the mouse cursor
|
|
|
|
return $outerDiv;
|
|
}
|
|
|
|
// Multiple columns
|
|
var nColumns = Math.min(MAX_COLUMNS, Math.ceil(nItems / nItemsThatCanFit));
|
|
var nItemsPerColumn = Math.ceil(nItems / nColumns);
|
|
var nItemsAdded = 0;
|
|
var nItemsRemaining = nItems;
|
|
var $holder = $('<div></div>');
|
|
|
|
while(nItemsRemaining > 0)
|
|
{
|
|
var $outerDiv = $('<div class="menu-outer"></div>');
|
|
var $innerDiv = $('<div class="menu-inner"></div>');
|
|
|
|
var nItemsToAdd = Math.min(nItemsRemaining, nItemsPerColumn);
|
|
var start = nItemsAdded;
|
|
var end = start + nItemsToAdd;
|
|
|
|
$menuItems.slice(start, end).each(function() { $innerDiv.append(this) });
|
|
$outerDiv.append($innerDiv);
|
|
$holder.append($outerDiv);
|
|
|
|
nItemsAdded += nItemsToAdd;
|
|
nItemsRemaining -= nItemsToAdd;
|
|
}
|
|
|
|
return $holder;
|
|
}
|
|
|
|
function getMenuPosition($div, depth, x, y)
|
|
{
|
|
if(depth == 0)
|
|
return getFirstMenuPosition($div, depth, x, y);
|
|
|
|
return getSubmenuPosition($div, depth);
|
|
}
|
|
|
|
function getFirstMenuPosition($div, depth, x, y)
|
|
{
|
|
var viewport = g_getViewport();
|
|
var menuWidth = $div.width();
|
|
var menuHeight = $div.height();
|
|
var menuWidthShadow = menuWidth + SIZE_SHADOW_WIDTH;
|
|
var menuHeightShadow = menuHeight + SIZE_SHADOW_HEIGHT;
|
|
|
|
var showingAtCursor = (x != null && y != null);
|
|
if(showingAtCursor)
|
|
{
|
|
if(y + menuHeightShadow > viewport.b) // Make sure menu doesn't overflow vertically
|
|
y = Math.max(viewport.t, viewport.b - menuHeightShadow); // Place along the bottom edge if it does
|
|
}
|
|
else
|
|
{
|
|
var $a = $rootNode;
|
|
var offset = $a.offset();
|
|
|
|
// Place underneath the root node by default
|
|
x = offset.left
|
|
y = offset.top + $a.outerHeight();
|
|
|
|
// Show menu upwards if it's going to be outside the viewport otherwise (only if there's room)
|
|
if(y + menuHeightShadow > viewport.b && offset.top >= menuHeightShadow)
|
|
y = offset.top - menuHeightShadow;
|
|
}
|
|
|
|
if(x + menuWidthShadow > viewport.r) // Make sure menu doesn't overflow horizontally
|
|
x = Math.max(viewport.l, viewport.r - menuWidthShadow); // Place along the right edge if it does
|
|
|
|
return {x: x, y: y};
|
|
}
|
|
|
|
function getSubmenuPosition($div, depth)
|
|
{
|
|
var viewport = g_getViewport();
|
|
var menuWidth = $div.width();
|
|
var menuHeight = $div.height();
|
|
var menuWidthShadow = menuWidth + SIZE_SHADOW_WIDTH;
|
|
var menuHeightShadow = menuHeight + SIZE_SHADOW_HEIGHT;
|
|
|
|
var $a = openLinks[depth - 1];
|
|
var offset = $a.offset();
|
|
var openToTheLeft = false;
|
|
|
|
// Show to the right of the menu item
|
|
x = offset.left + $a.outerWidth() - 5;
|
|
y = offset.top - 2;
|
|
|
|
if(x + menuWidthShadow > viewport.r) // Make sure menu doesn't overflow horizontally
|
|
openToTheLeft = true;
|
|
|
|
if(openToTheLeft)
|
|
x = Math.max(viewport.l, offset.left - menuWidth);
|
|
|
|
if(y + menuHeightShadow > viewport.b) // Make sure menu doesn't overflow vertically
|
|
y = Math.max(viewport.t, viewport.b - menuHeightShadow); // Place along the bottom edge if it does
|
|
|
|
return {x: x, y: y};
|
|
}
|
|
|
|
function revealDiv($div, animated)
|
|
{
|
|
if(animated)
|
|
{
|
|
$div.css({
|
|
opacity: '0'
|
|
})
|
|
.show()
|
|
.animate({
|
|
opacity: '1'
|
|
}, 'fast', null, doneRevealing);
|
|
}
|
|
else
|
|
$div.show();
|
|
}
|
|
|
|
function doneRevealing(a)
|
|
{
|
|
$(this).css('opacity', ''); // Remove opacity once animation is over to prevent a bug in IE8
|
|
}
|
|
|
|
function hideMenus(depth)
|
|
{
|
|
while(visibleMenus[depth])
|
|
{
|
|
visibleMenus[depth].stop().hide();
|
|
visibleMenus[depth] = null;
|
|
|
|
++depth;
|
|
}
|
|
}
|
|
|
|
function closeLinks(depth)
|
|
{
|
|
while(openLinks[depth])
|
|
{
|
|
openLinks[depth].removeClass('open');
|
|
openLinks[depth] = null;
|
|
|
|
++depth;
|
|
}
|
|
}
|
|
|
|
function isMenuVisible(depth)
|
|
{
|
|
return visibleMenus[depth || 0] != null;
|
|
}
|
|
|
|
// MENU UTILITY FUNCTIONS
|
|
|
|
function getItemId(menuItem)
|
|
{
|
|
return menuItem[MENU_IDX_ID];
|
|
}
|
|
|
|
function isSeparator(menuItem)
|
|
{
|
|
return menuItem[MENU_IDX_ID] == null; // Separators don't have an ID
|
|
}
|
|
|
|
function hasSeparators(menu)
|
|
{
|
|
return $WH.in_array(menu, true, isSeparator) != -1;
|
|
}
|
|
|
|
function hasSubmenu(menuItem)
|
|
{
|
|
return menuItem[MENU_IDX_SUB] != null;
|
|
}
|
|
|
|
function findItemPosById(menu, id)
|
|
{
|
|
return $WH.in_array(menu, id, getItemId);
|
|
}
|
|
|
|
function hasPermissions(menuItem)
|
|
{
|
|
var opt = self.getItemOpt(menuItem);
|
|
|
|
if(opt.requiredAccess && !User.hasPermissions(opt.requiredAccess))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
function isItemVisible(menuItem)
|
|
{
|
|
if(!hasPermissions(menuItem))
|
|
return false;
|
|
|
|
if(hasSubmenu(menuItem))
|
|
{
|
|
if(!hasVisibleItems(menuItem[MENU_IDX_SUB]))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function hasVisibleItems(menu)
|
|
{
|
|
return $WH.in_array(menu, true, isSubitemVisible) != -1;
|
|
}
|
|
|
|
function isSubitemVisible(menuItem)
|
|
{
|
|
return !isSeparator(menuItem) && hasPermissions(menuItem);
|
|
}
|
|
|
|
function getUniqueId(menu)
|
|
{
|
|
if(menu.uniqueId == null)
|
|
menu.uniqueId = nextUniqueId++;
|
|
|
|
return menu.uniqueId;
|
|
}
|
|
|
|
function traverseMenu(menu, func)
|
|
{
|
|
$.each(menu, function(idx, menuItem)
|
|
{
|
|
traverseMenuItem(menuItem, func);
|
|
});
|
|
}
|
|
|
|
function traverseMenuItem(menuItem, func)
|
|
{
|
|
func(menuItem);
|
|
if(hasSubmenu(menuItem))
|
|
traverseMenu(menuItem[MENU_IDX_SUB], func);
|
|
}
|
|
|
|
function fixUrls(menu, url, opt, depth)
|
|
{
|
|
$.each(menu, function(idx, menuItem)
|
|
{
|
|
if(menuItem === undefined) return;
|
|
if(isSeparator(menuItem)) return;
|
|
|
|
if(menuItem[MENU_IDX_URL] == null) // Don't override if already set
|
|
menuItem[MENU_IDX_URL] = url + menuItem[MENU_IDX_ID] + opt.hash;
|
|
|
|
if(hasSubmenu(menuItem))
|
|
{
|
|
var accumulate = true;
|
|
|
|
if(opt.useSimpleIds)
|
|
accumulate = false;
|
|
else if(opt.useSimpleIdsAfter != null && depth >= opt.useSimpleIdsAfter)
|
|
accumulate = false;
|
|
|
|
var nextUrl = url;
|
|
if(accumulate)
|
|
nextUrl += menuItem[MENU_IDX_ID] + '.';
|
|
fixUrls(menuItem[MENU_IDX_SUB], nextUrl, opt, depth + 1);
|
|
}
|
|
});
|
|
}
|
|
|
|
function sortMenu(menu)
|
|
{
|
|
menu.sort(function(a, b)
|
|
{
|
|
return $WH.strcmp(a[MENU_IDX_NAME], b[MENU_IDX_NAME]);
|
|
});
|
|
}
|
|
|
|
function sortSeparatedMenu(menu)
|
|
{
|
|
// Sort each group separately so headings stay where they are.
|
|
|
|
var implodedMenu = self.implode(menu, { createHeadinglessGroup: true });
|
|
|
|
$.each(implodedMenu, function(idx, submenu)
|
|
{
|
|
sortMenu(submenu[MENU_IDX_SUB]);
|
|
});
|
|
|
|
explodeInto(menu, implodedMenu);
|
|
};
|
|
|
|
function explodeInto(menu, implodedMenu) // Reverse of implode
|
|
{
|
|
menu.splice(0, menu.length); // Clear menu
|
|
|
|
$.each(implodedMenu, function(idx, menuItem)
|
|
{
|
|
if(menuItem[MENU_IDX_NAME])
|
|
menu.push([, menuItem[MENU_IDX_NAME]]); // Heading
|
|
|
|
$.each(menuItem[MENU_IDX_SUB], function(idx, menuItem)
|
|
{
|
|
menu.push(menuItem);
|
|
});
|
|
});
|
|
}
|
|
|
|
// EVENTS
|
|
|
|
function beforeCreateMenuItem(menuItem)
|
|
{
|
|
var opt = self.getItemOpt(menuItem);
|
|
|
|
if(opt.checkedUrl && location.href.match(opt.checkedUrl))
|
|
menuItem.checked = true;
|
|
}
|
|
|
|
function beforeShowMenu(menu)
|
|
{
|
|
if(menu.onBeforeShow)
|
|
menu.onBeforeShow(menu);
|
|
|
|
$.each(menu, function(idx, menuItem)
|
|
{
|
|
var opt = self.getItemOpt(menuItem);
|
|
|
|
if(opt.onBeforeShow)
|
|
opt.onBeforeShow(menuItem);
|
|
});
|
|
}
|
|
|
|
function rootNodeOver(event)
|
|
{
|
|
clearTimeout(timer);
|
|
|
|
var $node = $(this);
|
|
|
|
if(!isMenuVisible())
|
|
{
|
|
// Use a small delay the 1st time to prevent undesired appearances
|
|
timer = setTimeout(show.bind(null, $node.data('menu'), $node), DELAY_BEFORESHOW);
|
|
return;
|
|
}
|
|
|
|
show($node.data('menu'), $node);
|
|
}
|
|
|
|
function rootNodeOut(event)
|
|
{
|
|
clearTimeout(timer);
|
|
|
|
if(isMenuVisible())
|
|
timer = setTimeout(hide, DELAY_BEFOREHIDE);
|
|
}
|
|
|
|
function rootNodeClick(event)
|
|
{
|
|
clearTimeout(timer);
|
|
|
|
self.showAtCursor($(this).data('menu'), event);
|
|
}
|
|
|
|
function menuDivOver(event)
|
|
{
|
|
clearTimeout(timer);
|
|
}
|
|
|
|
function menuDivOut(event)
|
|
{
|
|
clearTimeout(timer);
|
|
timer = setTimeout(hide, DELAY_BEFOREHIDE);
|
|
}
|
|
|
|
function menuItemOver(event)
|
|
{
|
|
clearTimeout(timer);
|
|
|
|
var $a = $(this);
|
|
var depth = event.data.depth;
|
|
|
|
closeLinks(depth);
|
|
|
|
var menuItem = $a.data('menuItem');
|
|
|
|
var deepestDepth = depth;
|
|
if(menuItem && hasSubmenu(menuItem))
|
|
{
|
|
$a.addClass('open');
|
|
openLinks[depth] = $a;
|
|
|
|
displayMenu(menuItem[MENU_IDX_SUB], depth + 1);
|
|
|
|
++deepestDepth;
|
|
}
|
|
|
|
hideMenus(deepestDepth + 1);
|
|
}
|
|
|
|
function menuItemClick(event)
|
|
{
|
|
var $a = $(this);
|
|
var menuItem = $a.data('menuItem');
|
|
|
|
if(!menuItem)
|
|
return;
|
|
|
|
var opt = self.getItemOpt(menuItem);
|
|
|
|
if(opt.onClick)
|
|
opt.onClick();
|
|
}
|
|
};
|
|
|
|
Menu.fixUrls(mn_achievements, '?achievements=');
|
|
Menu.fixUrls(mn_classes, '?class=');
|
|
Menu.fixUrls(mn_currencies, '?currencies=');
|
|
Menu.fixUrls(mn_factions, '?factions=');
|
|
Menu.fixUrls(mn_items, '?items=');
|
|
Menu.fixUrls(mn_itemSets, '?itemsets&filter=cl=', { hash: '0-2+1' });
|
|
Menu.fixUrls(mn_npcs, '?npcs=');
|
|
Menu.fixUrls(mn_objects, '?objects=');
|
|
Menu.fixUrls(mn_petCalc, '?petcalc=');
|
|
Menu.fixUrls(mn_pets, '?pets=');
|
|
Menu.fixUrls(mn_quests, '?quests=');
|
|
Menu.fixUrls(mn_races, '?race=');
|
|
Menu.fixUrls(mn_spells, '?spells=');
|
|
Menu.fixUrls(mn_titles, '?titles=');
|
|
Menu.fixUrls(mn_zones, '?zones=');
|
|
|
|
$(document).ready(function() // Locale is only known later
|
|
{
|
|
// if(Locale.getId() == LOCALE_ENUS)
|
|
// return;
|
|
|
|
Menu.sort(mn_classes);
|
|
Menu.sort(mn_database);
|
|
Menu.sortSubmenus(mn_items,
|
|
[
|
|
[4, 1], // Armor > Cloth
|
|
[4, 2], // Armor > Leather
|
|
[4, 3], // Armor > Mail
|
|
[4, 4], // Armor > Plate
|
|
[1], // Containers
|
|
[0], // Consumables
|
|
[16], // Glyphs
|
|
[7], // Trade Goods
|
|
[6], // Projectiles
|
|
[9] // Recipes
|
|
]);
|
|
Menu.sort(mn_itemSets);
|
|
Menu.sort(mn_npcs);
|
|
Menu.sort(mn_objects);
|
|
Menu.sort(mn_talentCalc);
|
|
Menu.sort(mn_petCalc);
|
|
Menu.sort(mn_pets);
|
|
Menu.sort(mn_races);
|
|
Menu.sort(mn_skills);
|
|
Menu.sortSubmenus(mn_spells,
|
|
[
|
|
[7], // Abilities
|
|
[-2], // Talents
|
|
[-3], // Pet Abilities
|
|
[11], // Professions
|
|
[9] // Secondary Skills
|
|
]);
|
|
|
|
// aowow - why wasn't this already sorted..?
|
|
Menu.sortSubmenus(mn_quests,
|
|
[
|
|
[0],
|
|
[1],
|
|
[2],
|
|
[3],
|
|
[4],
|
|
[5],
|
|
[6],
|
|
[7],
|
|
[8],
|
|
[9],
|
|
[10]
|
|
]);
|
|
});
|