view.inc in Accordion Menu 7
Same filename and directory in other branches
Provides view routines.
@author Jim Berry ("solotandem", http://drupal.org/user/240748)
File
includes/view.incView source
<?php
/**
* @file
* Provides view routines.
*
* @author Jim Berry ("solotandem", http://drupal.org/user/240748)
*/
/**
* Implements hook_block_view().
*/
function _accordion_menu_block_view($delta) {
// Add stylesheet.
drupal_add_css(drupal_get_path('module', 'accordion_menu') . '/accordion_menu.css', array(
'group' => CSS_DEFAULT,
'every_page' => TRUE,
));
$config = accordion_menu_config($delta);
$config['menu_name'] = strtolower(str_replace(array(
'_',
' ',
), '-', $config['menu_name']));
// Get menu tree.
if (module_exists('menu_block') && !empty($config['block_source'])) {
$menu_block_config = menu_block_get_config($config['block_source']);
// Call routine based on module version.
$info = system_get_info('module', 'menu_block');
$version = str_replace('7.x-', '', $info['version']);
if (version_compare($version, '2.4') >= 0) {
$tree = menu_tree_block_data($menu_block_config);
}
else {
$tree = menu_tree_build($menu_block_config);
}
}
else {
$tree = menu_tree_page_data($config['menu_source']);
}
// Localize the tree.
if (module_exists('i18n_menu')) {
$tree = i18n_menu_localize_tree($tree);
}
// Alter the tree and config before rendering.
drupal_alter('accordion_menu_tree', $tree, $config);
$active_menu = '';
$content = accordion_menu_output($tree, $config, $active_menu);
// Set active menu to false and collapsible: true.
if ($active_menu === '' || !$config['menu_expanded']) {
$active_menu = 'false';
}
// For jQuery 1.10 options, see http://jqueryui.com/accordion/.
foreach (accordion_menu_js_settings($delta, $config) as $key => $value) {
$parts = explode('_', $key);
if (count($parts) > 1) {
// Example: change auto_height to $autoHeight.
$key = $parts[0] . ucfirst($parts[1]);
}
if ($key != 'icons' && $value != 'true' && $value != 'false') {
$value = "'{$value}'";
}
$options[] = "{$key}: {$value}";
}
$options[] = "active: " . $active_menu;
// Other settings tried before settling on this code:
// - collapsible: false
// - event: 'click'
// - navigation: true
$js = "jQuery(function() {\n";
// Add accordion options.
$js .= " jQuery('.accordion-menu-" . $delta . "').accordion({ " . implode(', ', $options) . " });\n";
// Unbind the accordion effect for "empty" headers.
$js .= " jQuery('.accordion-menu-" . $delta . " .accordion-header.no-children').each(function(index, element) {\n jQuery(this)\n .unbind()\n .children('.ui-icon')\n .removeClass('{$config['header_icon']}')\n .addClass('{$config['empty_icon']}');\n });\n";
$js .= "});";
drupal_add_library('system', 'ui.accordion');
if ($config['animate'] != 'false' && $config['animate'] != 'swing') {
// Include script for non-default easing method.
drupal_add_library('system', 'effects');
}
drupal_add_js($js, array(
'type' => 'inline',
'scope' => $config['script_scope'],
'group' => JS_DEFAULT,
));
// Assemble the render arrays for the menu tree.
$data = array();
$data['subject'] = '<none>';
$data['content']['#content'] = $content;
if (!empty($data['content']['#content'])) {
$data['content']['#theme'] = array(
'accordion_menu_wrapper__' . str_replace('-', '_', $config['delta']),
'accordion_menu_wrapper__' . str_replace('-', '_', $config['menu_name']),
'accordion_menu_wrapper',
);
$data['content']['#config'] = $config;
$data['content']['#delta'] = $delta;
}
else {
$data['content'] = '';
}
return $data;
}
/**
* Implements hook_block().
*/
function accordion_menu_block_OLD($op = 'list', $delta = 0, $edit = array()) {
}
/**
* Returns jQuery UI accordion settings.
*
* @todo String input needs to be passed through check_plain().
*
* @param string $delta
* The delta that uniquely identifies the block in the block system.
* @param array $config
* The configuration settings for the menu block.
*
* @return array
* An associative array of JS settings with booleans converted to strings.
*/
function accordion_menu_js_settings($delta, array $config) {
$config_nojs = array(
'delta' => $delta,
'menu_name' => 'Menu ' . $delta,
'menu_source' => 'navigation',
'block_source' => '',
'script_scope' => 'footer',
'header_link' => FALSE,
'menu_expanded' => FALSE,
);
// @todo Use json_encode on $config.
$config = array_diff_key($config, $config_nojs);
foreach (array(
'collapsible',
'icons',
) as $key) {
$config[$key] = accordion_menu_boolean_option($config[$key]);
}
if ($config['icons'] == 'true') {
$value1 = $config['header_icon'];
$value2 = $config['active_icon'];
$config[$key] = "{ header: '{$value1}', activeHeader: '{$value2}' }";
}
unset($config['header_icon']);
unset($config['active_icon']);
unset($config['empty_icon']);
return $config;
}
/**
* Returns string representation of boolean option.
*
* @param string $boolean
* A boolean value.
*
* @return string
* A string representation of the boolean value.
*/
function accordion_menu_boolean_option($boolean) {
return $boolean ? 'true' : 'false';
}
/**
* Returns a render array of the accordion menu tree.
*
* @param array $tree
* An associative array of the menu tree.
* @param array $config
* The configuration settings for the menu block.
* @param string $active_menu
* The active menu item for the JS settings.
*
* @return array
* An render array of the menu tree.
*
* @see menu_tree_output()
*/
function accordion_menu_output(array $tree, array $config, &$active_menu = '') {
$build = $items = array();
// Pull out just the menu items we are going to render so that we
// get an accurate count for the first/last classes.
foreach ($tree as $data) {
if ($data['link']['access'] && !$data['link']['hidden']) {
$items[] = $data;
}
}
$item_count = count($items);
$flip = array(
'even' => 'odd',
'odd' => 'even',
);
$zebra = 'even';
foreach ($items as $i => $data) {
$subtree = _accordion_menu_subtree($data['link']);
$subtree = menu_tree_output($subtree);
// Add CSS classes.
$class = array();
$class[] = 'accordion-header accordion-header-' . ($i + 1);
if ($i == 0) {
$class[] = 'first';
}
if ($i == $item_count - 1) {
$class[] = 'last';
}
if ($subtree) {
$class[] = 'has-children';
}
else {
$class[] = 'no-children';
}
if ($data['link']['in_active_trail']) {
$class[] = 'active-trail';
$data['link']['localized_options']['attributes']['class'][] = 'active-trail';
}
if ($data['link']['href'] == $_GET['q'] || $data['link']['href'] == '<front>' && drupal_is_front_page()) {
$class[] = 'active';
}
$class[] = $zebra = $flip[$zebra];
$class[] = 'menu-mlid-' . $data['link']['mlid'];
// Distinguish our "span" element from that added by ui accordion.
$data['link']['localized_options']['attributes']['class'][] = 'accordion-link';
// Allow menu-specific theme overrides.
$element['#theme'] = array(
'accordion_menu_header__' . str_replace('-', '_', $config['delta']),
'accordion_menu_header__' . str_replace('-', '_', $config['menu_name']),
'accordion_menu_header',
);
$element['#attributes']['class'] = $class;
$element['#title'] = $data['link']['title'];
$element['#href'] = $data['link']['href'];
$element['#localized_options'] = !empty($data['link']['localized_options']) ? $data['link']['localized_options'] : array();
$element['#below'] = $subtree;
$element['#original_link'] = $data['link'];
$element['#bid'] = array(
'module' => 'accordion_menu',
'delta' => $config['delta'],
);
$element['#config'] = $config;
$element['#count'] = $i + 1;
// Index using the link's unique mlid.
$build[$data['link']['mlid']] = $element;
if ($data['link']['in_active_trail'] == '1') {
$active_menu = $i;
}
}
if ($build) {
// Make sure drupal_render() does not re-order the links.
$build['#sorted'] = TRUE;
}
return $build;
// Add class to handle flash of unstyled content in IE 7 and 8.
// @todo Reincorporate this.
$output = str_replace('ul class="menu"', 'ul class="menu hide-at-first"', $output);
}
/**
* Returns the tree of items below a menu item.
*
* @param array $item
* A menu item whose children are to be found.
*
* @return array
* An associative array of the items below the menu item.
*/
function _accordion_menu_subtree(array $item) {
static $index = array();
static $indexed = array();
// This looks expensive, but menu_tree_all_data uses static caching.
$tree = menu_tree_all_data($item['menu_name']);
// Index the tree to find the path to this item.
if (!isset($indexed[$item['menu_name']])) {
$index += _accordion_menu_index($tree);
$indexed[$item['menu_name']] = TRUE;
}
// Traverse the tree.
foreach ($index[$item['mlid']]['parents'] as $mlid) {
$key = $index[$mlid]['key'];
if (!isset($tree[$key])) {
return array();
}
$tree = $tree[$key]['below'];
}
$key = $index[$item['mlid']]['key'];
return isset($tree[$key]) ? $tree[$key]['below'] : array();
}
/**
* Returns the menu tree indexed by mlid.
*
* This is needed to identify the items without relying on titles. This function
* is recursive.
*
* @param array $tree
* A tree of menu items such as the return value of menu_tree_all_data()
*
* @return
* An array associating mlid values with the internal keys of the menu tree.
*/
function _accordion_menu_index(array $tree, array $ancestors = array(), $parent = NULL) {
$index = array();
if ($parent) {
$ancestors[] = $parent;
}
foreach ($tree as $key => $item) {
$index[$item['link']['mlid']] = array(
'key' => $key,
'parents' => $ancestors,
);
if (!empty($item['below'])) {
$index += _accordion_menu_index($item['below'], $ancestors, $item['link']['mlid']);
}
}
return $index;
}
/**
* Returns HTML for the accordion menu header and submenu items.
*
* @param array $variables
* An associative array containing:
* - element: The menu object that is being formatted.
*
* @ingroup themeable
*/
function theme_accordion_menu_header(array $variables) {
$element = $variables['element'];
$sub_menu = '';
$header = check_plain($element['#config']['header']);
$header_link = $element['#config']['header_link'];
if ($element['#below']) {
$sub_menu = drupal_render($element['#below']);
}
if (!$header_link && !empty($element['#below'])) {
// @todo Allow the title to be output without cleansing based on user input.
// Merge in defaults.
$options = $element['#localized_options'];
$options += array(
'attributes' => array(),
'html' => FALSE,
);
$tag = 'span';
$link = '<' . $tag . drupal_attributes($options['attributes']) . '>' . ($options['html'] ? $element['#title'] : check_plain($element['#title'])) . '</' . $tag . '>';
}
else {
$link = l($element['#title'], $element['#href'], $element['#localized_options']);
}
// The standard menu rendering nests the sub_menu items beneath the header.
// For the accordion effect to work, the sub_menu must be wrapped in a "div"
// following the header element.
$output = '<' . $header . drupal_attributes($element['#attributes']) . '>' . $link . '</' . $header . '>' . "\n";
return $output . '<div class="accordion-content-' . $element['#count'] . '">' . $sub_menu . '</div>' . "\n";
}
/**
* Process variables for accordion-menu-wrapper.tpl.php.
*
* @see accordion-menu-wrapper.tpl.php
*
* @ingroup themeable
*/
function template_preprocess_accordion_menu_wrapper(array &$variables) {
$variables['classes_array'][] = 'accordion-menu-' . $variables['config']['delta'];
$variables['classes_array'][] = 'accordion-menu-name-' . $variables['config']['menu_name'];
$variables['classes_array'][] = 'accordion-menu-source-' . $variables['config']['menu_source'];
}
Functions
Name | Description |
---|---|
accordion_menu_block_OLD | Implements hook_block(). |
accordion_menu_boolean_option | Returns string representation of boolean option. |
accordion_menu_js_settings | Returns jQuery UI accordion settings. |
accordion_menu_output | Returns a render array of the accordion menu tree. |
template_preprocess_accordion_menu_wrapper | Process variables for accordion-menu-wrapper.tpl.php. |
theme_accordion_menu_header | Returns HTML for the accordion menu header and submenu items. |
_accordion_menu_block_view | Implements hook_block_view(). |
_accordion_menu_index | Returns the menu tree indexed by mlid. |
_accordion_menu_subtree | Returns the tree of items below a menu item. |