mirror of
https://github.com/Sarjuuk/aowow.git
synced 2025-11-29 15:58:16 +08:00
Template/Update (Part 47)
* 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
This commit is contained in:
987
setup/tools/filegen/templates/global.js/menu.js
Normal file
987
setup/tools/filegen/templates/global.js/menu.js
Normal file
@@ -0,0 +1,987 @@
|
||||
/*
|
||||
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]
|
||||
]);
|
||||
});
|
||||
Reference in New Issue
Block a user