You are here

dhtml_menu.module in DHTML Menu 5

DHTML menus

Allow click expansion of the menu tree via javascript, with graceful degradation.

File

dhtml_menu.module
View source
<?php

/**
 * @file
 * DHTML menus
 *
 * Allow click expansion of the menu tree via javascript, with 
 * graceful degradation.
 */

/**
 * Implementation of hook_menu().
 */
function dhtml_menu_menu($may_cache) {
  if ($may_cache) {
    $items[] = array(
      'path' => 'admin/settings/dhtml_menu',
      'title' => t('DHTML Menu'),
      'description' => t('Configure jQuery slide effect and Duplicated menu items.'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'dhtml_menu_settings',
      ),
      'access' => user_access('administer menu'),
    );
    $items[] = array(
      'path' => 'admin/settings/dhtml_menu/replace-navigation',
      'title' => t('Replace navigation block'),
      'callback' => '_dhtml_menu_replace',
      'access' => user_access('administer menu'),
      'type' => MENU_CALLBACK,
    );
    return $items;
  }
  else {
    $path = drupal_get_path('module', 'dhtml_menu');
    drupal_add_js(array(
      'dhtmlMenu' => array(
        'useEffects' => variable_get('dhtml_menu_use_effects', 1),
      ),
    ), 'setting');
    drupal_add_js($path . '/dhtml_menu.js');
  }
}

/**
 * Implementation of hook_block().
 * Basically a copy of menu_block(). We're providing our own version
 * of each menu.
 */
function dhtml_menu_block($op = 'list', $delta = 0) {
  global $user;
  if ($op == 'list') {
    $blocks = array();
    $root_menus = menu_get_root_menus();
    foreach ($root_menus as $mid => $title) {
      $blocks[$mid]['info'] = t('DHTML: ') . $title;
    }
    return $blocks;
  }
  else {
    if ($op == 'view') {
      $item = menu_get_item($delta);
      if ($user->uid > 0 && check_plain($item['title']) == t('Navigation')) {
        $data['subject'] = $user->name;
      }
      else {
        $data['subject'] = check_plain($item['title']);
      }
      $data['content'] = theme('dhtml_menu_tree', $delta);
      return $data;
    }
  }
}

/**
 * DHTML Menu Settings page.
 * @ingroup form
 */
function dhtml_menu_settings() {
  $form['dhtml_menu_use_effects'] = array(
    '#type' => 'checkbox',
    '#title' => t('jQuery slide effect'),
    '#description' => t('Menu slides when it opens and closes.'),
    '#default_value' => variable_get('dhtml_menu_use_effects', 1),
  );

  //   $form['dhtml_menu_bullet'] = array(
  //     '#type'          => 'checkbox',
  //     '#title'         => t('Item bullets'),
  //     '#description'   => t('The menu only collapses/exapand when clicking over the item bullet. WARNING: some themes do not show bullets!'),
  //     '#default_value' => variable_get('dhtml_menu_bullet', 0)
  //   );
  $form['dhtml_menu_duplicated'] = array(
    '#type' => 'textarea',
    '#title' => t('Duplicated menu items'),
    '#description' => t('You can specify a list of menu items that should be displayed twice: Once as a parent item that expands and collapses the sub-menu and again as its own child item, which points directly to the page (the parent items still links on double-click). Enter one internal path (like "admin") on each line. To specify a custom link text, put it after the path, separated by a space.'),
    '#rows' => 4,
    '#default_value' => variable_get('dhtml_menu_duplicated', 'admin'),
  );
  return system_settings_form($form);
}

/**
 * Replaces the navigation block in all themes by
 * assigning its values to this module's block, then
 * disabling the normal block.
 */
function _dhtml_menu_replace() {

  // Ensure that the block table is up to date.
  _block_rehash();

  // Get the navigation blocks of all themes
  $res = db_query("SELECT status, weight, region, visibility, pages, title, theme\n    FROM {blocks} WHERE module = 'user' AND delta = 1");
  while ($row = db_fetch_array($res)) {
    $navigation_settings[$row['theme']] = $row;
  }

  // Enable the equivalent DHTML menu and
  // disable the original one
  foreach ($navigation_settings as $theme => $row) {
    db_query("UPDATE {blocks} SET status = %d, weight = %d, region = '%s',\n      visibility = %d, pages = %d, title = '%s'\n      WHERE module = 'dhtml_menu'\n      AND delta = 1 AND theme = '%s'", $row);

    // relies on $mid of 'Navigation' menu being 1, see dhtml_menu_block('list')
    db_query("UPDATE {blocks} SET status = 0, region = ''\n      WHERE module = 'user' AND delta = 1 AND theme = '%s'", $theme);
  }
  drupal_set_message(t('Your navigation block has been replaced with its DHTML equivalent in all currently enabled themes.'), 'status');

  // go to block administration
  drupal_goto('admin/build/block');

  // Its generally not necessary, but for safety, return.
  return;
}

/**
 * Fetch duplicated menu items - this is a configurable setting.
 */
function _dhtml_get_add_links() {
  static $dhtml_menu_duplicated;
  if (!isset($dhtml_menu_duplicated)) {
    $dhtml_menu_duplicated = array();
    $text = variable_get('dhtml_menu_duplicated', 'admin');
    $text = preg_split('/\\n/', $text, -1, PREG_SPLIT_NO_EMPTY);

    // explode returns array(0 => '') for $text == ''
    foreach ($text as $line) {
      $line = trim($line);
      preg_match('/^([^ ]+)( (.*))?$/', $line, $match);
      $dhtml_menu_duplicated[$match[1]] = !empty($match[3]) ? $match[3] : TRUE;
    }
  }
  return $dhtml_menu_duplicated;
}

/**
 * Convert menus into DHTML menus
 * @param $pid
 *    Number, parent menu item ID
 */
function dhtml_menu_tree($pid = 1) {
  $menu = menu_get_menu();
  $output = '';
  if (isset($menu['visible'][$pid]) && !empty($menu['visible'][$pid]['children'])) {
    foreach ($menu['visible'][$pid]['children'] as $mid) {
      $counter = !empty($menu['visible'][$mid]['children']) ? 0 : 1;
      $output .= theme('dhtml_menu_item', $mid, theme('dhtml_menu_tree', $mid, false), $counter);
    }
  }
  return $output;
}

/**
 * Format each menu item
 * @ingroup themable
 */
function theme_dhtml_menu_item($mid, $children = '', $leaf = true) {
  static $expanded = null;
  $active = $display = '';
  if (!is_array($expanded)) {
    $expanded = explode(',', $_COOKIE['dhtml_menu']);
  }
  if ($mid == menu_get_active_nontask_item()) {
    $active = ' active';
  }
  if ($leaf) {
    return "  <li class=\"leaf{$active}\">" . menu_item_link($mid) . "</li>\n";
  }
  else {
    $menu = menu_get_menu();
    $type = isset($menu['visible'][$mid]['type']) ? $menu['visible'][$mid]['type'] : null;
    if (menu_in_active_trail($mid) || $type & MENU_EXPANDED || in_array("sub{$mid}", $expanded)) {
      $state = 'expanded';
    }
    else {
      $state = 'collapsed';
      $display = 'style="display: none;"';
    }
    $output = "<li class=\"menutitle {$state}\" id=\"menu-sub{$mid}\" >";
    $output .= menu_item_link($mid);
    $output .= "<div class=\"submenu\" id=\"sub{$mid}\" {$display}>\n";
    $output .= "{$children}\n</div>\n";
    $output .= "</li>\n";
    return $output;
  }
}

/**
 * Format each menu tree
 * @ingroup themable
 */
function theme_dhtml_menu_tree($pid = 1, $toplevel = true) {
  if ($tree = dhtml_menu_tree($pid)) {
    $add_links = _dhtml_get_add_links();
    $menu = menu_get_menu();
    if (!empty($add_links[$menu['items'][$pid]['path']])) {
      $duplication = menu_get_item($pid);
      if ($add_links[$menu['items'][$pid]['path']] !== TRUE) {
        $duplication['title'] = $add_links[$menu['items'][$pid]['path']];
      }
      $tree = "<li class='leaf menu-duplicated'>" . theme('menu_item_link', $duplication, $duplication) . "</li>\n" . $tree;
    }
    $class = 'menu dhtml_menu';
    if (!empty($toplevel)) {
      $class .= ' menu-root';
    }
    return "\n<ul class=\"{$class}\">\n{$tree}\n</ul>\n";
  }
}

// panels 2 integration
function dhtml_menu_panels_block_info($module, $delta, &$info) {
  $info['icon'] = 'icon_core_block_menu.png';
  $info['category'] = array(
    t('Menus'),
    -2,
  );
}

// function dhtml_menu_item_link($item, $link_item) {
//   $add_links = _dhtml_get_add_links();
//   $title = ($link_item['path'] && $add_links[$link_item['path']] ? $add_links[$link_item['path']] : $item['title']);
//   return l($title, $link_item['path'], isset($item['description']) ? array('title' => $item['description']) : array());
// }

Functions

Namesort descending Description
dhtml_menu_block Implementation of hook_block(). Basically a copy of menu_block(). We're providing our own version of each menu.
dhtml_menu_menu Implementation of hook_menu().
dhtml_menu_panels_block_info
dhtml_menu_settings DHTML Menu Settings page.
dhtml_menu_tree Convert menus into DHTML menus
theme_dhtml_menu_item Format each menu item
theme_dhtml_menu_tree Format each menu tree
_dhtml_get_add_links Fetch duplicated menu items - this is a configurable setting.
_dhtml_menu_replace Replaces the navigation block in all themes by assigning its values to this module's block, then disabling the normal block.