web/wp-includes/nav-menu-template.php
changeset 194 32102edaa81b
child 204 09a1c134465b
equal deleted inserted replaced
193:2f6f6f7551ca 194:32102edaa81b
       
     1 <?php
       
     2 /**
       
     3  * Navigation Menu template functions
       
     4  *
       
     5  * @package WordPress
       
     6  * @subpackage Nav_Menus
       
     7  * @since 3.0.0
       
     8  */
       
     9 
       
    10 /**
       
    11  * Create HTML list of nav menu items.
       
    12  *
       
    13  * @package WordPress
       
    14  * @since 3.0.0
       
    15  * @uses Walker
       
    16  */
       
    17 class Walker_Nav_Menu extends Walker {
       
    18 	/**
       
    19 	 * @see Walker::$tree_type
       
    20 	 * @since 3.0.0
       
    21 	 * @var string
       
    22 	 */
       
    23 	var $tree_type = array( 'post_type', 'taxonomy', 'custom' );
       
    24 
       
    25 	/**
       
    26 	 * @see Walker::$db_fields
       
    27 	 * @since 3.0.0
       
    28 	 * @todo Decouple this.
       
    29 	 * @var array
       
    30 	 */
       
    31 	var $db_fields = array( 'parent' => 'menu_item_parent', 'id' => 'db_id' );
       
    32 
       
    33 	/**
       
    34 	 * @see Walker::start_lvl()
       
    35 	 * @since 3.0.0
       
    36 	 *
       
    37 	 * @param string $output Passed by reference. Used to append additional content.
       
    38 	 * @param int $depth Depth of page. Used for padding.
       
    39 	 */
       
    40 	function start_lvl( &$output, $depth = 0, $args = array() ) {
       
    41 		$indent = str_repeat("\t", $depth);
       
    42 		$output .= "\n$indent<ul class=\"sub-menu\">\n";
       
    43 	}
       
    44 
       
    45 	/**
       
    46 	 * @see Walker::end_lvl()
       
    47 	 * @since 3.0.0
       
    48 	 *
       
    49 	 * @param string $output Passed by reference. Used to append additional content.
       
    50 	 * @param int $depth Depth of page. Used for padding.
       
    51 	 */
       
    52 	function end_lvl( &$output, $depth = 0, $args = array() ) {
       
    53 		$indent = str_repeat("\t", $depth);
       
    54 		$output .= "$indent</ul>\n";
       
    55 	}
       
    56 
       
    57 	/**
       
    58 	 * @see Walker::start_el()
       
    59 	 * @since 3.0.0
       
    60 	 *
       
    61 	 * @param string $output Passed by reference. Used to append additional content.
       
    62 	 * @param object $item Menu item data object.
       
    63 	 * @param int $depth Depth of menu item. Used for padding.
       
    64 	 * @param int $current_page Menu item ID.
       
    65 	 * @param object $args
       
    66 	 */
       
    67 	function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
       
    68 		global $wp_query;
       
    69 		$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
       
    70 
       
    71 		$class_names = $value = '';
       
    72 
       
    73 		$classes = empty( $item->classes ) ? array() : (array) $item->classes;
       
    74 		$classes[] = 'menu-item-' . $item->ID;
       
    75 
       
    76 		$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args ) );
       
    77 		$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
       
    78 
       
    79 		$id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args );
       
    80 		$id = $id ? ' id="' . esc_attr( $id ) . '"' : '';
       
    81 
       
    82 		$output .= $indent . '<li' . $id . $value . $class_names .'>';
       
    83 
       
    84 		$attributes  = ! empty( $item->attr_title ) ? ' title="'  . esc_attr( $item->attr_title ) .'"' : '';
       
    85 		$attributes .= ! empty( $item->target )     ? ' target="' . esc_attr( $item->target     ) .'"' : '';
       
    86 		$attributes .= ! empty( $item->xfn )        ? ' rel="'    . esc_attr( $item->xfn        ) .'"' : '';
       
    87 		$attributes .= ! empty( $item->url )        ? ' href="'   . esc_attr( $item->url        ) .'"' : '';
       
    88 
       
    89 		$item_output = $args->before;
       
    90 		$item_output .= '<a'. $attributes .'>';
       
    91 		$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
       
    92 		$item_output .= '</a>';
       
    93 		$item_output .= $args->after;
       
    94 
       
    95 		$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
       
    96 	}
       
    97 
       
    98 	/**
       
    99 	 * @see Walker::end_el()
       
   100 	 * @since 3.0.0
       
   101 	 *
       
   102 	 * @param string $output Passed by reference. Used to append additional content.
       
   103 	 * @param object $item Page data object. Not used.
       
   104 	 * @param int $depth Depth of page. Not Used.
       
   105 	 */
       
   106 	function end_el( &$output, $item, $depth = 0, $args = array() ) {
       
   107 		$output .= "</li>\n";
       
   108 	}
       
   109 }
       
   110 
       
   111 /**
       
   112  * Displays a navigation menu.
       
   113  *
       
   114  * Optional $args contents:
       
   115  *
       
   116  * menu - The menu that is desired. Accepts (matching in order) id, slug, name. Defaults to blank.
       
   117  * menu_class - CSS class to use for the ul element which forms the menu. Defaults to 'menu'.
       
   118  * menu_id - The ID that is applied to the ul element which forms the menu. Defaults to the menu slug, incremented.
       
   119  * container - Whether to wrap the ul, and what to wrap it with. Defaults to 'div'.
       
   120  * container_class - the class that is applied to the container. Defaults to 'menu-{menu slug}-container'.
       
   121  * container_id - The ID that is applied to the container. Defaults to blank.
       
   122  * fallback_cb - If the menu doesn't exists, a callback function will fire. Defaults to 'wp_page_menu'. Set to false for no fallback.
       
   123  * before - Text before the link text.
       
   124  * after - Text after the link text.
       
   125  * link_before - Text before the link.
       
   126  * link_after - Text after the link.
       
   127  * echo - Whether to echo the menu or return it. Defaults to echo.
       
   128  * depth - how many levels of the hierarchy are to be included. 0 means all. Defaults to 0.
       
   129  * walker - allows a custom walker to be specified.
       
   130  * theme_location - the location in the theme to be used. Must be registered with register_nav_menu() in order to be selectable by the user.
       
   131  * items_wrap - How the list items should be wrapped. Defaults to a ul with an id and class. Uses printf() format with numbered placeholders.
       
   132  *
       
   133  * @since 3.0.0
       
   134  *
       
   135  * @param array $args Arguments
       
   136  */
       
   137 function wp_nav_menu( $args = array() ) {
       
   138 	static $menu_id_slugs = array();
       
   139 
       
   140 	$defaults = array( 'menu' => '', 'container' => 'div', 'container_class' => '', 'container_id' => '', 'menu_class' => 'menu', 'menu_id' => '',
       
   141 	'echo' => true, 'fallback_cb' => 'wp_page_menu', 'before' => '', 'after' => '', 'link_before' => '', 'link_after' => '', 'items_wrap' => '<ul id="%1$s" class="%2$s">%3$s</ul>',
       
   142 	'depth' => 0, 'walker' => '', 'theme_location' => '' );
       
   143 
       
   144 	$args = wp_parse_args( $args, $defaults );
       
   145 	$args = apply_filters( 'wp_nav_menu_args', $args );
       
   146 	$args = (object) $args;
       
   147 
       
   148 	// Get the nav menu based on the requested menu
       
   149 	$menu = wp_get_nav_menu_object( $args->menu );
       
   150 
       
   151 	// Get the nav menu based on the theme_location
       
   152 	if ( ! $menu && $args->theme_location && ( $locations = get_nav_menu_locations() ) && isset( $locations[ $args->theme_location ] ) )
       
   153 		$menu = wp_get_nav_menu_object( $locations[ $args->theme_location ] );
       
   154 
       
   155 	// get the first menu that has items if we still can't find a menu
       
   156 	if ( ! $menu && !$args->theme_location ) {
       
   157 		$menus = wp_get_nav_menus();
       
   158 		foreach ( $menus as $menu_maybe ) {
       
   159 			if ( $menu_items = wp_get_nav_menu_items($menu_maybe->term_id) ) {
       
   160 				$menu = $menu_maybe;
       
   161 				break;
       
   162 			}
       
   163 		}
       
   164 	}
       
   165 
       
   166 	// If the menu exists, get its items.
       
   167 	if ( $menu && ! is_wp_error($menu) && !isset($menu_items) )
       
   168 		$menu_items = wp_get_nav_menu_items( $menu->term_id );
       
   169 
       
   170 	// If no menu was found or if the menu has no items and no location was requested, call the fallback_cb if it exists
       
   171 	if ( ( !$menu || is_wp_error($menu) || ( isset($menu_items) && empty($menu_items) && !$args->theme_location ) )
       
   172 		&& $args->fallback_cb && is_callable( $args->fallback_cb ) )
       
   173 			return call_user_func( $args->fallback_cb, (array) $args );
       
   174 
       
   175 	// If no fallback function was specified and the menu doesn't exists, bail.
       
   176 	if ( !$menu || is_wp_error($menu) )
       
   177 		return false;
       
   178 
       
   179 	$nav_menu = $items = '';
       
   180 
       
   181 	$show_container = false;
       
   182 	if ( $args->container ) {
       
   183 		$allowed_tags = apply_filters( 'wp_nav_menu_container_allowedtags', array( 'div', 'nav' ) );
       
   184 		if ( in_array( $args->container, $allowed_tags ) ) {
       
   185 			$show_container = true;
       
   186 			$class = $args->container_class ? ' class="' . esc_attr( $args->container_class ) . '"' : ' class="menu-'. $menu->slug .'-container"';
       
   187 			$id = $args->container_id ? ' id="' . esc_attr( $args->container_id ) . '"' : '';
       
   188 			$nav_menu .= '<'. $args->container . $id . $class . '>';
       
   189 		}
       
   190 	}
       
   191 
       
   192 	// Set up the $menu_item variables
       
   193 	_wp_menu_item_classes_by_context( $menu_items );
       
   194 
       
   195 	$sorted_menu_items = array();
       
   196 	foreach ( (array) $menu_items as $key => $menu_item )
       
   197 		$sorted_menu_items[$menu_item->menu_order] = $menu_item;
       
   198 
       
   199 	unset($menu_items);
       
   200 
       
   201 	$sorted_menu_items = apply_filters( 'wp_nav_menu_objects', $sorted_menu_items, $args );
       
   202 
       
   203 	$items .= walk_nav_menu_tree( $sorted_menu_items, $args->depth, $args );
       
   204 	unset($sorted_menu_items);
       
   205 
       
   206 	// Attributes
       
   207 	if ( ! empty( $args->menu_id ) ) {
       
   208 		$wrap_id = $args->menu_id;
       
   209 	} else {
       
   210 		$wrap_id = 'menu-' . $menu->slug;
       
   211 		while ( in_array( $wrap_id, $menu_id_slugs ) ) {
       
   212 			if ( preg_match( '#-(\d+)$#', $wrap_id, $matches ) )
       
   213 				$wrap_id = preg_replace('#-(\d+)$#', '-' . ++$matches[1], $wrap_id );
       
   214 			else
       
   215 				$wrap_id = $wrap_id . '-1';
       
   216 		}
       
   217 	}
       
   218 	$menu_id_slugs[] = $wrap_id;
       
   219 
       
   220 	$wrap_class = $args->menu_class ? $args->menu_class : '';
       
   221 
       
   222 	// Allow plugins to hook into the menu to add their own <li>'s
       
   223 	$items = apply_filters( 'wp_nav_menu_items', $items, $args );
       
   224 	$items = apply_filters( "wp_nav_menu_{$menu->slug}_items", $items, $args );
       
   225 
       
   226 	$nav_menu .= sprintf( $args->items_wrap, esc_attr( $wrap_id ), esc_attr( $wrap_class ), $items );
       
   227 	unset( $items );
       
   228 
       
   229 	if ( $show_container )
       
   230 		$nav_menu .= '</' . $args->container . '>';
       
   231 
       
   232 	$nav_menu = apply_filters( 'wp_nav_menu', $nav_menu, $args );
       
   233 
       
   234 	if ( $args->echo )
       
   235 		echo $nav_menu;
       
   236 	else
       
   237 		return $nav_menu;
       
   238 }
       
   239 
       
   240 /**
       
   241  * Add the class property classes for the current context, if applicable.
       
   242  *
       
   243  * @access private
       
   244  * @since 3.0
       
   245  *
       
   246  * @param array $menu_items The current menu item objects to which to add the class property information.
       
   247  */
       
   248 function _wp_menu_item_classes_by_context( &$menu_items ) {
       
   249 	global $wp_query;
       
   250 
       
   251 	$queried_object = $wp_query->get_queried_object();
       
   252 	$queried_object_id = (int) $wp_query->queried_object_id;
       
   253 
       
   254 	$active_object = '';
       
   255 	$active_ancestor_item_ids = array();
       
   256 	$active_parent_item_ids = array();
       
   257 	$active_parent_object_ids = array();
       
   258 	$possible_taxonomy_ancestors = array();
       
   259 	$possible_object_parents = array();
       
   260 	$home_page_id = (int) get_option( 'page_for_posts' );
       
   261 
       
   262 	if ( $wp_query->is_singular && ! empty( $queried_object->post_type ) && ! is_post_type_hierarchical( $queried_object->post_type ) ) {
       
   263 		foreach ( (array) get_object_taxonomies( $queried_object->post_type ) as $taxonomy ) {
       
   264 			if ( is_taxonomy_hierarchical( $taxonomy ) ) {
       
   265 				$term_hierarchy = _get_term_hierarchy( $taxonomy );
       
   266 				$terms = wp_get_object_terms( $queried_object_id, $taxonomy, array( 'fields' => 'ids' ) );
       
   267 				if ( is_array( $terms ) ) {
       
   268 					$possible_object_parents = array_merge( $possible_object_parents, $terms );
       
   269 					$term_to_ancestor = array();
       
   270 					foreach ( (array) $term_hierarchy as $anc => $descs ) {
       
   271 						foreach ( (array) $descs as $desc )
       
   272 							$term_to_ancestor[ $desc ] = $anc;
       
   273 					}
       
   274 
       
   275 					foreach ( $terms as $desc ) {
       
   276 						do {
       
   277 							$possible_taxonomy_ancestors[ $taxonomy ][] = $desc;
       
   278 							if ( isset( $term_to_ancestor[ $desc ] ) ) {
       
   279 								$_desc = $term_to_ancestor[ $desc ];
       
   280 								unset( $term_to_ancestor[ $desc ] );
       
   281 								$desc = $_desc;
       
   282 							} else {
       
   283 								$desc = 0;
       
   284 							}
       
   285 						} while ( ! empty( $desc ) );
       
   286 					}
       
   287 				}
       
   288 			}
       
   289 		}
       
   290 	} elseif ( ! empty( $queried_object->post_type ) && is_post_type_hierarchical( $queried_object->post_type ) ) {
       
   291 		_get_post_ancestors( $queried_object );
       
   292 	} elseif ( ! empty( $queried_object->taxonomy ) && is_taxonomy_hierarchical( $queried_object->taxonomy ) ) {
       
   293 		$term_hierarchy = _get_term_hierarchy( $queried_object->taxonomy );
       
   294 		$term_to_ancestor = array();
       
   295 		foreach ( (array) $term_hierarchy as $anc => $descs ) {
       
   296 			foreach ( (array) $descs as $desc )
       
   297 				$term_to_ancestor[ $desc ] = $anc;
       
   298 		}
       
   299 		$desc = $queried_object->term_id;
       
   300 		do {
       
   301 			$possible_taxonomy_ancestors[ $queried_object->taxonomy ][] = $desc;
       
   302 			if ( isset( $term_to_ancestor[ $desc ] ) ) {
       
   303 				$_desc = $term_to_ancestor[ $desc ];
       
   304 				unset( $term_to_ancestor[ $desc ] );
       
   305 				$desc = $_desc;
       
   306 			} else {
       
   307 				$desc = 0;
       
   308 			}
       
   309 		} while ( ! empty( $desc ) );
       
   310 	}
       
   311 
       
   312 	$possible_object_parents = array_filter( $possible_object_parents );
       
   313 
       
   314 	$front_page_url = home_url();
       
   315 
       
   316 	foreach ( (array) $menu_items as $key => $menu_item ) {
       
   317 
       
   318 		$menu_items[$key]->current = false;
       
   319 
       
   320 		$classes = (array) $menu_item->classes;
       
   321 		$classes[] = 'menu-item';
       
   322 		$classes[] = 'menu-item-type-' . $menu_item->type;
       
   323 		$classes[] = 'menu-item-object-' . $menu_item->object;
       
   324 
       
   325 		// if the menu item corresponds to a taxonomy term for the currently-queried non-hierarchical post object
       
   326 		if ( $wp_query->is_singular && 'taxonomy' == $menu_item->type && in_array( $menu_item->object_id, $possible_object_parents ) ) {
       
   327 			$active_parent_object_ids[] = (int) $menu_item->object_id;
       
   328 			$active_parent_item_ids[] = (int) $menu_item->db_id;
       
   329 			$active_object = $queried_object->post_type;
       
   330 
       
   331 		// if the menu item corresponds to the currently-queried post or taxonomy object
       
   332 		} elseif (
       
   333 			$menu_item->object_id == $queried_object_id &&
       
   334 			(
       
   335 				( ! empty( $home_page_id ) && 'post_type' == $menu_item->type && $wp_query->is_home && $home_page_id == $menu_item->object_id ) ||
       
   336 				( 'post_type' == $menu_item->type && $wp_query->is_singular ) ||
       
   337 				( 'taxonomy' == $menu_item->type && ( $wp_query->is_category || $wp_query->is_tag || $wp_query->is_tax ) )
       
   338 			)
       
   339 		) {
       
   340 			$classes[] = 'current-menu-item';
       
   341 			$menu_items[$key]->current = true;
       
   342 			$_anc_id = (int) $menu_item->db_id;
       
   343 
       
   344 			while(
       
   345 				( $_anc_id = get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) ) &&
       
   346 				! in_array( $_anc_id, $active_ancestor_item_ids )
       
   347 			) {
       
   348 				$active_ancestor_item_ids[] = $_anc_id;
       
   349 			}
       
   350 
       
   351 			if ( 'post_type' == $menu_item->type && 'page' == $menu_item->object ) {
       
   352 				// Back compat classes for pages to match wp_page_menu()
       
   353 				$classes[] = 'page_item';
       
   354 				$classes[] = 'page-item-' . $menu_item->object_id;
       
   355 				$classes[] = 'current_page_item';
       
   356 			}
       
   357 			$active_parent_item_ids[] = (int) $menu_item->menu_item_parent;
       
   358 			$active_parent_object_ids[] = (int) $menu_item->post_parent;
       
   359 			$active_object = $menu_item->object;
       
   360 
       
   361 		// if the menu item corresponds to the currently-requested URL
       
   362 		} elseif ( 'custom' == $menu_item->object ) {
       
   363 			$_root_relative_current = untrailingslashit( $_SERVER['REQUEST_URI'] );
       
   364 			$current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_root_relative_current;
       
   365 			$raw_item_url = strpos( $menu_item->url, '#' ) ? substr( $menu_item->url, 0, strpos( $menu_item->url, '#' ) ) : $menu_item->url;
       
   366 			$item_url = untrailingslashit( $raw_item_url );
       
   367 			$_indexless_current = untrailingslashit( preg_replace( '/index.php$/', '', $current_url ) );
       
   368 
       
   369 			if ( $raw_item_url && in_array( $item_url, array( $current_url, $_indexless_current, $_root_relative_current ) ) ) {
       
   370 				$classes[] = 'current-menu-item';
       
   371 				$menu_items[$key]->current = true;
       
   372 				$_anc_id = (int) $menu_item->db_id;
       
   373 
       
   374 				while(
       
   375 					( $_anc_id = get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) ) &&
       
   376 					! in_array( $_anc_id, $active_ancestor_item_ids )
       
   377 				) {
       
   378 					$active_ancestor_item_ids[] = $_anc_id;
       
   379 				}
       
   380 
       
   381 				if ( in_array( home_url(), array( untrailingslashit( $current_url ), untrailingslashit( $_indexless_current ) ) ) ) {
       
   382 					// Back compat for home link to match wp_page_menu()
       
   383 					$classes[] = 'current_page_item';
       
   384 				}
       
   385 				$active_parent_item_ids[] = (int) $menu_item->menu_item_parent;
       
   386 				$active_parent_object_ids[] = (int) $menu_item->post_parent;
       
   387 				$active_object = $menu_item->object;
       
   388 
       
   389 			// give front page item current-menu-item class when extra query arguments involved
       
   390 			} elseif ( $item_url == $front_page_url && is_front_page() ) {
       
   391 				$classes[] = 'current-menu-item';
       
   392 			}
       
   393 
       
   394 			if ( untrailingslashit($item_url) == home_url() )
       
   395 				$classes[] = 'menu-item-home';
       
   396 		}
       
   397 
       
   398 		// back-compat with wp_page_menu: add "current_page_parent" to static home page link for any non-page query
       
   399 		if ( ! empty( $home_page_id ) && 'post_type' == $menu_item->type && empty( $wp_query->is_page ) && $home_page_id == $menu_item->object_id )
       
   400 			$classes[] = 'current_page_parent';
       
   401 
       
   402 		$menu_items[$key]->classes = array_unique( $classes );
       
   403 	}
       
   404 	$active_ancestor_item_ids = array_filter( array_unique( $active_ancestor_item_ids ) );
       
   405 	$active_parent_item_ids = array_filter( array_unique( $active_parent_item_ids ) );
       
   406 	$active_parent_object_ids = array_filter( array_unique( $active_parent_object_ids ) );
       
   407 
       
   408 	// set parent's class
       
   409 	foreach ( (array) $menu_items as $key => $parent_item ) {
       
   410 		$classes = (array) $parent_item->classes;
       
   411 		$menu_items[$key]->current_item_ancestor = false;
       
   412 		$menu_items[$key]->current_item_parent = false;
       
   413 
       
   414 		if (
       
   415 			isset( $parent_item->type ) &&
       
   416 			(
       
   417 				// ancestral post object
       
   418 				(
       
   419 					'post_type' == $parent_item->type &&
       
   420 					! empty( $queried_object->post_type ) &&
       
   421 					is_post_type_hierarchical( $queried_object->post_type ) &&
       
   422 					in_array( $parent_item->object_id, $queried_object->ancestors ) &&
       
   423 					$parent_item->object != $queried_object->ID
       
   424 				) ||
       
   425 
       
   426 				// ancestral term
       
   427 				(
       
   428 					'taxonomy' == $parent_item->type &&
       
   429 					isset( $possible_taxonomy_ancestors[ $parent_item->object ] ) &&
       
   430 					in_array( $parent_item->object_id, $possible_taxonomy_ancestors[ $parent_item->object ] ) &&
       
   431 					(
       
   432 						! isset( $queried_object->term_id ) ||
       
   433 						$parent_item->object_id != $queried_object->term_id
       
   434 					)
       
   435 				)
       
   436 			)
       
   437 		) {
       
   438 			$classes[] = empty( $queried_object->taxonomy ) ? 'current-' . $queried_object->post_type . '-ancestor' : 'current-' . $queried_object->taxonomy . '-ancestor';
       
   439 		}
       
   440 
       
   441 		if ( in_array(  intval( $parent_item->db_id ), $active_ancestor_item_ids ) ) {
       
   442 			$classes[] = 'current-menu-ancestor';
       
   443 			$menu_items[$key]->current_item_ancestor = true;
       
   444 		}
       
   445 		if ( in_array( $parent_item->db_id, $active_parent_item_ids ) ) {
       
   446 			$classes[] = 'current-menu-parent';
       
   447 			$menu_items[$key]->current_item_parent = true;
       
   448 		}
       
   449 		if ( in_array( $parent_item->object_id, $active_parent_object_ids ) )
       
   450 			$classes[] = 'current-' . $active_object . '-parent';
       
   451 
       
   452 		if ( 'post_type' == $parent_item->type && 'page' == $parent_item->object ) {
       
   453 			// Back compat classes for pages to match wp_page_menu()
       
   454 			if ( in_array('current-menu-parent', $classes) )
       
   455 				$classes[] = 'current_page_parent';
       
   456 			if ( in_array('current-menu-ancestor', $classes) )
       
   457 				$classes[] = 'current_page_ancestor';
       
   458 		}
       
   459 
       
   460 		$menu_items[$key]->classes = array_unique( $classes );
       
   461 	}
       
   462 }
       
   463 
       
   464 /**
       
   465  * Retrieve the HTML list content for nav menu items.
       
   466  *
       
   467  * @uses Walker_Nav_Menu to create HTML list content.
       
   468  * @since 3.0.0
       
   469  * @see Walker::walk() for parameters and return description.
       
   470  */
       
   471 function walk_nav_menu_tree( $items, $depth, $r ) {
       
   472 	$walker = ( empty($r->walker) ) ? new Walker_Nav_Menu : $r->walker;
       
   473 	$args = array( $items, $depth, $r );
       
   474 
       
   475 	return call_user_func_array( array(&$walker, 'walk'), $args );
       
   476 }
       
   477 
       
   478 /**
       
   479  * Prevents a menu item ID from being used more than once.
       
   480  *
       
   481  * @since 3.0.1
       
   482  * @access private
       
   483  */
       
   484 function _nav_menu_item_id_use_once( $id, $item ) {
       
   485 	static $_used_ids = array();
       
   486 	if ( in_array( $item->ID, $_used_ids ) )
       
   487 		return '';
       
   488 	$_used_ids[] = $item->ID;
       
   489 	return $id;
       
   490 }
       
   491 add_filter( 'nav_menu_item_id', '_nav_menu_item_id_use_once', 10, 2 );