/**
* Implements hook_block_view().
* @param {String} delta
* @param {Object} region
* @return {String}
*/
function menu_block_view(delta, region) {
// NOTE: When rendering a jQM data-role="navbar" you can't place an
// empty list (<ul></ul>) in it, this will cause an error:
// https://github.com/jquery/jquery-mobile/issues/5141
// So we must check to make sure we have any items before rendering the
// menu since our theme_item_list implementation returns empty lists
// for jQM pageshow async list item data retrieval and display.
try {
// Load the menu.
var menu = drupalgap.menus[delta];
// Since menu link paths may have an 'access_callback' handler that needs
// to make an async call to the server (e.g. local tasks), we'll utilize a
// pageshow handler to render the menu, so for now just render an empty
// placeholder and pageshow handler.
var container_id = menu_container_id(delta);
var data_role = null;
if (region.attributes && region.attributes['data-role']) {
data_role = region.attributes['data-role'];
}
// If the menu is wrapped, check to see if any wrap_options attributes were
// attached. If any were provided use them to build the div container
// attributes. We will always overwrite the container id though, since it is
// generated by the system (and used to dynamically inject the menu html).
var container_attributes = {};
if (
typeof menu.options !== 'undefined' &&
typeof menu.options.wrap !== 'undefined' &&
menu.options.wrap && menu.options.wrap_options &&
menu.options.wrap_options.attributes
) { container_attributes = menu.options.wrap_options.attributes; }
container_attributes.id = container_id;
return '<div ' + drupalgap_attributes(container_attributes) + '></div>' +
drupalgap_jqm_page_event_script_code({
page_id: drupalgap_get_page_id(),
jqm_page_event: 'pageshow',
jqm_page_event_callback: 'menu_block_view_pageshow',
jqm_page_event_args: JSON.stringify({
menu_name: delta,
container_id: container_id,
'data-role': data_role
})
}, delta);
}
catch (error) { console.log('menu_block_view - ' + error); }
}
/**
* The pageshow handler for menu blocks.
* @param {Object} options
*/
function menu_block_view_pageshow(options) {
try {
var html = '';
// Grab current path so we can watch out for any menu links that match it.
var path = drupalgap_path_get();
// Are we about to view a normal menu, or the local task menu?
var delta = options.menu_name;
if (delta == 'primary_local_tasks') {
// LOCAL TASKS MENU LINKS
// For the current page's router path, grab any local task menu links add
// them into the menu. Note, local tasks are located in a menu link item's
// children, or its parent's children (including itself), if there are
// any. Local tasks typically have argument wildcards in them, so we'll
// replace their wildcards with the current args.
var router_path = drupalgap_router_path_get();
if (drupalgap.menu_links[router_path]) {
// Determine the parent path, if any.
var parent = null;
if (drupalgap.menu_links[router_path].parent) {
parent = drupalgap.menu_links[router_path].parent;
}
// Then extract the local tasks paths array.
var local_tasks = null;
if (drupalgap.menu_links[router_path].children) {
local_tasks = drupalgap.menu_links[router_path].children;
}
else if (
parent && drupalgap.menu_links[parent] &&
drupalgap.menu_links[parent].children
) { local_tasks = drupalgap.menu_links[parent].children; }
var args = arg();
// Define a success callback that will be called later on...
var _success = function(result) {
try {
var menu_items = [];
var link_path = '';
if (local_tasks && !empty(local_tasks)) {
for (var index in local_tasks) {
if (!local_tasks.hasOwnProperty(index)) { continue; }
var local_task = local_tasks[index];
if (drupalgap.menu_links[local_task] && (
drupalgap.menu_links[local_task].type ==
'MENU_DEFAULT_LOCAL_TASK' ||
drupalgap.menu_links[local_task].type ==
'MENU_LOCAL_TASK'
)) {
if (drupalgap_menu_access(local_task, null, result)) {
menu_items.push(drupalgap.menu_links[local_task]);
}
}
}
}
// If there was only one local task menu item, and it is the default
// local task, don't render the menu, otherwise render the menu as
// an item list as long as there are items to render.
if (menu_items.length == 1 &&
menu_items[0].type == 'MENU_DEFAULT_LOCAL_TASK'
) { html = ''; }
else {
var items = [];
for (var index in menu_items) {
if (!menu_items.hasOwnProperty(index)) { continue; }
var item = menu_items[index];
// Make a deep copy of the menu link so we don't modify it.
var link = jQuery.extend(true, {}, item);
// If there are no link options, set up defaults.
if (!link.options) { link.options = { attributes: { } }; }
else if (!link.options.attributes) {
link.options.attributes = { };
}
// If the link points to the current path, set it as active.
// We first need to figure out which path to check, by default
// use the link path, but if its a default local task, use its
// parent path.
var path_to_check = link.path;
if (link.type == 'MENU_DEFAULT_LOCAL_TASK' && link.parent) {
path_to_check = link.parent;
link.path = arg(null, link.parent).join('/');
}
if (path_to_check == router_path) {
if (!link.options.attributes['class']) {
link.options.attributes['class'] = '';
}
link.options.attributes['class'] +=
' ui-btn ui-btn-active ui-state-persist ';
}
items.push(
l(
link.title,
drupalgap_place_args_in_path(link.path),
link.options
)
);
}
if (items.length > 0) {
html = theme('item_list', {'items': items});
}
}
// Inject the html.
$('#' + options.container_id).html(html).trigger('create');
// If the block's region is a jQM navbar, refresh the navbar.
if (options['data-role'] && options['data-role'] == 'navbar') {
$('#' + options.container_id).navbar();
}
// Optionally remove the placeholder wrapper.
var menu = drupalgap.menus[options.menu_name];
if (
typeof menu.options !== 'undefined' &&
(typeof menu.options.wrap === 'undefined' || !menu.options.wrap)
) { $('#' + options.container_id).children().unwrap(); }
}
catch (error) {
console.log('menu_block_view_pageshow - success - ' + error);
}
};
// First, determine if any child has an entity arg in the path, and/or
// an access_callback handler.
var has_entity_arg = false;
var has_access_callback = false;
if (local_tasks) {
for (var index in local_tasks) {
if (!local_tasks.hasOwnProperty(index)) { continue; }
var local_task = local_tasks[index];
if (drupalgap.menu_links[local_task] &&
(
drupalgap.menu_links[local_task].type ==
'MENU_DEFAULT_LOCAL_TASK' ||
drupalgap.menu_links[local_task].type ==
'MENU_LOCAL_TASK'
)
) {
if (drupalgap_path_has_entity_arg(arg(null, local_task))) {
has_entity_arg = true;
}
if (
typeof
drupalgap.menu_links[local_task].access_callback !==
'undefined'
) { has_access_callback = true; }
}
}
}
// If we have an entity arg, and an access_callback, let's load up the
// entity asynchronously.
if (has_entity_arg && has_access_callback) {
var found_int_arg = false;
var int_arg_index = null;
for (var i = 0; i < args.length; i++) {
if (is_int(parseInt(args[i]))) {
// Save the arg index so we can replace it later.
int_arg_index = i;
found_int_arg = true;
break;
}
}
if (!found_int_arg) { _success(null); return; }
// Determine the naming convention for the entity load function.
var load_function_prefix = args[0]; // default
if (args[0] == 'taxonomy') {
if (args[1] == 'vocabulary' || args[1] == 'term') {
load_function_prefix = args[0] + '_' + args[1];
}
}
var load_function = load_function_prefix + '_load';
// If the load function exists, load the entity.
if (function_exists(load_function)) {
var entity_fn = window[load_function];
// Load the entity. MVC items need to pass along the module name and
// model type to its load function. All other entity load functions
// just need the entity id.
var entity_id = parseInt(args[int_arg_index]);
if (args[0] == 'item') {
entity = entity_fn(args[1], args[2], entity_id);
_success(entity);
}
else {
// Force a reset if we are editing the entity.
var reset = false;
if (arg(2) == 'edit') { reset = true; }
// Load the entity asynchronously.
entity_fn(entity_id, { reset: reset, success: _success });
}
}
else {
console.log('menu_block_view_pageshow - load function not ' +
'implemented! ' + load_function
);
}
}
else { _success(null); }
}
}
else {
// ALL OTHER MENU LINKS
// If the block's corresponding menu exists, and it has links, iterate
// over each link, add it to an items array, then theme an item list.
var menu = false;
if (drupalgap.menus[delta] && drupalgap.menus[delta].links) {
menu = drupalgap.menus[delta];
var items = [];
for (var index in menu.links) {
if (!menu.links.hasOwnProperty(index)) { continue; }
var menu_link = menu.links[index];
// Make a deep copy of the menu link so we don't modify it.
var link = jQuery.extend(true, {}, menu_link);
// If there are no link options, set up defaults.
if (!link.options) { link.options = {attributes: {}}; }
else if (!link.options.attributes) { link.options.attributes = {}; }
if (!link.options.attributes['class']) {
link.options.attributes['class'] = '';
}
// Extract the link's class attribute.
var class_names = link.options.attributes['class'];
// If the link points to the current path, set it as active.
if (link.path == path) {
if (class_names.indexOf('ui-btn') == -1) {
class_names += ' ui-btn';
}
if (class_names.indexOf('ui-btn-active') == -1) {
class_names += ' ui-btn-active';
}
if (class_names.indexOf('ui-state-persist') == -1) {
class_names += ' ui-state-persist';
}
}
// If there was a data-icon attibute on the link, let's add its
// equivalent css class name to the link (if it isn't already
// present), otherwise jQM won't render the icon properly. Sounds
// like a jQM bug.
if (
link.options.attributes['data-icon'] &&
class_names.indexOf(link.options.attributes['data-icon']) == -1
) {
class_names +=
' ui-icon-' + link.options.attributes['data-icon'] + ' ';
}
// Finally toss the class attribute back on the link and add the
// link to the items array.
link.options.attributes['class'] = class_names + ' ';
items.push(l(t(link.title), link.path, link.options));
}
if (items.length > 0) {
// Pass along any menu attributes.
var attributes = null;
if (menu.options && menu.options.attributes) {
attributes = drupalgap.menus[delta].options.attributes;
}
html = theme('item_list', {'items': items, 'attributes': attributes});
}
}
// Inject the html.
$('#' + options.container_id).html(html).trigger('create');
// Remove the placeholder wrapper, unless we were instructed not to.
var wrap = false;
if (
menu && typeof menu.options !== 'undefined' &&
typeof menu.options.wrap !== 'undefined' && menu.options.wrap
) { wrap = true; }
if (!wrap) { $('#' + options.container_id).children().unwrap(); }
}
}
catch (error) { console.log('menu_block_view_pageshow - ' + error); }
}
/**
* Implements hook_install().
*/
function menu_install() {
try {
// Grab the list of system menus and save each.
var system_menus = menu_list_system_menus();
for (var menu_name in system_menus) {
if (!system_menus.hasOwnProperty(menu_name)) { continue; }
var menu = system_menus[menu_name];
menu_save(menu);
}
}
catch (error) { console.log('menu_install - ' + error); }
}
/**
* Returns a JSON object that can be used as default options for a menu object.
* @return {Object}
*/
function menu_popup_get_default_options() {
return {
attributes: {
'data-role': 'listview'
},
wrap: true,
wrap_options: {
attributes: {
'data-role': 'popup'
}
}
};
}
/**
* Given a menu region link, this will return its data JSON object, or null if
* no data exists.
* @param {Object} region_link
* @return {*)
*/
function menu_region_link_get_data(region_link) {
try {
// Extract the data associated with this link. If it has a 'region'
// property then it is coming from a hook_menu, if it doesn't then it
// is coming from settings.js.
var data = null;
if (typeof region_link.region === 'undefined') {
data = region_link; // link defined in settings.js
// @TODO - we need to warn people that they can't make a custom menu
// with a machine name of 'regions' now that this machine name is a
// "system" name for rendering links in regions.
}
// link defined via hook_menu()
else { data = region_link.region; }
return data;
}
catch (error) { console.log('menu_region_link_get_data - ' + error); }
}
/**
* Given a menu region link's class name, this will return what side of the ui
* it is on, returns left by default, unless it specifically contains the
* ui-btn-right class.
* @param {String} class_name
* @return {String)
*/
function menu_region_link_get_side(class_name) {
try {
var side = 'left';
if (class_name.indexOf('ui-btn-right') != -1) { side = 'right'; }
return side;
}
catch (error) { console.log('menu_region_link_get_side - ' + error); }
}
/**
* Given a menu, this adds it to drupalgap.menus. See menu_list_system_menus
* for examples of a menu JSON object.
* @param {Object} menu
*/
function menu_save(menu) {
try {
drupalgap.menus[menu.menu_name] = menu;
}
catch (error) { console.log('menu_save - ' + error); }
}
/**
* Given a menu name, this will return it from drupalgap.menus, or return null
* if it doesn't exist.
* @param {String} name
* @return {*}
*/
function menu_load(name) {
try {
if (typeof drupalgap.menus[name] !== 'undefined') {
return drupalgap.menus[name];
}
return null;
}
catch (error) { console.log('menu_load - ' + error); }
}
/**
* Given a menu name, this will return its container id for that page. You may
* optionally pass in a page id as the second argument, otherwise it will use
* the current page id.
* @param {String} menu_name
* @return {String}
*/
function menu_container_id(menu_name) {
try {
var page_id = null;
if (arguments[1]) { page_id = arguments[1]; }
else { page_id = drupalgap_get_page_id(); }
return page_id + '_menu_' + menu_name;
}
catch (error) { console.log('menu_container_id - ' + error); }
}