wp/wp-includes/nav-menu-template.php
changeset 0 d970ebf37754
child 5 5e2f62d02dcd
equal deleted inserted replaced
-1:000000000000 0:d970ebf37754
       
     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 	 * What the class handles.
       
    20 	 *
       
    21 	 * @see Walker::$tree_type
       
    22 	 * @since 3.0.0
       
    23 	 * @var string
       
    24 	 */
       
    25 	var $tree_type = array( 'post_type', 'taxonomy', 'custom' );
       
    26 
       
    27 	/**
       
    28 	 * Database fields to use.
       
    29 	 *
       
    30 	 * @see Walker::$db_fields
       
    31 	 * @since 3.0.0
       
    32 	 * @todo Decouple this.
       
    33 	 * @var array
       
    34 	 */
       
    35 	var $db_fields = array( 'parent' => 'menu_item_parent', 'id' => 'db_id' );
       
    36 
       
    37 	/**
       
    38 	 * Starts the list before the elements are added.
       
    39 	 *
       
    40 	 * @see Walker::start_lvl()
       
    41 	 *
       
    42 	 * @since 3.0.0
       
    43 	 *
       
    44 	 * @param string $output Passed by reference. Used to append additional content.
       
    45 	 * @param int    $depth  Depth of menu item. Used for padding.
       
    46 	 * @param array  $args   An array of arguments. @see wp_nav_menu()
       
    47 	 */
       
    48 	function start_lvl( &$output, $depth = 0, $args = array() ) {
       
    49 		$indent = str_repeat("\t", $depth);
       
    50 		$output .= "\n$indent<ul class=\"sub-menu\">\n";
       
    51 	}
       
    52 
       
    53 	/**
       
    54 	 * Ends the list of after the elements are added.
       
    55 	 *
       
    56 	 * @see Walker::end_lvl()
       
    57 	 *
       
    58 	 * @since 3.0.0
       
    59 	 *
       
    60 	 * @param string $output Passed by reference. Used to append additional content.
       
    61 	 * @param int    $depth  Depth of menu item. Used for padding.
       
    62 	 * @param array  $args   An array of arguments. @see wp_nav_menu()
       
    63 	 */
       
    64 	function end_lvl( &$output, $depth = 0, $args = array() ) {
       
    65 		$indent = str_repeat("\t", $depth);
       
    66 		$output .= "$indent</ul>\n";
       
    67 	}
       
    68 
       
    69 	/**
       
    70 	 * Start the element output.
       
    71 	 *
       
    72 	 * @see Walker::start_el()
       
    73 	 *
       
    74 	 * @since 3.0.0
       
    75 	 *
       
    76 	 * @param string $output Passed by reference. Used to append additional content.
       
    77 	 * @param object $item   Menu item data object.
       
    78 	 * @param int    $depth  Depth of menu item. Used for padding.
       
    79 	 * @param array  $args   An array of arguments. @see wp_nav_menu()
       
    80 	 * @param int    $id     Current item ID.
       
    81 	 */
       
    82 	function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
       
    83 		$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
       
    84 
       
    85 		$class_names = $value = '';
       
    86 
       
    87 		$classes = empty( $item->classes ) ? array() : (array) $item->classes;
       
    88 		$classes[] = 'menu-item-' . $item->ID;
       
    89 
       
    90 		/**
       
    91 		 * Filter the CSS class(es) applied to a menu item's <li>.
       
    92 		 *
       
    93 		 * @since 3.0.0
       
    94 		 *
       
    95 		 * @param array  $classes The CSS classes that are applied to the menu item's <li>.
       
    96 		 * @param object $item    The current menu item.
       
    97 		 * @param array  $args    An array of arguments. @see wp_nav_menu()
       
    98 		 */
       
    99 		$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args ) );
       
   100 		$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
       
   101 
       
   102 		/**
       
   103 		 * Filter the ID applied to a menu item's <li>.
       
   104 		 *
       
   105 		 * @since 3.0.1
       
   106 		 *
       
   107 		 * @param string The ID that is applied to the menu item's <li>.
       
   108 		 * @param object $item The current menu item.
       
   109 		 * @param array $args An array of arguments. @see wp_nav_menu()
       
   110 		 */
       
   111 		$id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args );
       
   112 		$id = $id ? ' id="' . esc_attr( $id ) . '"' : '';
       
   113 
       
   114 		$output .= $indent . '<li' . $id . $value . $class_names .'>';
       
   115 
       
   116 		$atts = array();
       
   117 		$atts['title']  = ! empty( $item->attr_title ) ? $item->attr_title : '';
       
   118 		$atts['target'] = ! empty( $item->target )     ? $item->target     : '';
       
   119 		$atts['rel']    = ! empty( $item->xfn )        ? $item->xfn        : '';
       
   120 		$atts['href']   = ! empty( $item->url )        ? $item->url        : '';
       
   121 
       
   122 		/**
       
   123 		 * Filter the HTML attributes applied to a menu item's <a>.
       
   124 		 *
       
   125 		 * @since 3.6.0
       
   126 		 *
       
   127 		 * @param array $atts {
       
   128 		 *     The HTML attributes applied to the menu item's <a>, empty strings are ignored.
       
   129 		 *
       
   130 		 *     @type string $title  The title attribute.
       
   131 		 *     @type string $target The target attribute.
       
   132 		 *     @type string $rel    The rel attribute.
       
   133 		 *     @type string $href   The href attribute.
       
   134 		 * }
       
   135 		 * @param object $item The current menu item.
       
   136 		 * @param array  $args An array of arguments. @see wp_nav_menu()
       
   137 		 */
       
   138 		$atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args );
       
   139 
       
   140 		$attributes = '';
       
   141 		foreach ( $atts as $attr => $value ) {
       
   142 			if ( ! empty( $value ) ) {
       
   143 				$value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
       
   144 				$attributes .= ' ' . $attr . '="' . $value . '"';
       
   145 			}
       
   146 		}
       
   147 
       
   148 		$item_output = $args->before;
       
   149 		$item_output .= '<a'. $attributes .'>';
       
   150 		/** This filter is documented in wp-includes/post-template.php */
       
   151 		$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
       
   152 		$item_output .= '</a>';
       
   153 		$item_output .= $args->after;
       
   154 
       
   155 		/**
       
   156 		 * Filter a menu item's starting output.
       
   157 		 *
       
   158 		 * The menu item's starting output only includes $args->before, the opening <a>,
       
   159 		 * the menu item's title, the closing </a>, and $args->after. Currently, there is
       
   160 		 * no filter for modifying the opening and closing <li> for a menu item.
       
   161 		 *
       
   162 		 * @since 3.0.0
       
   163 		 *
       
   164 		 * @param string $item_output The menu item's starting HTML output.
       
   165 		 * @param object $item        Menu item data object.
       
   166 		 * @param int    $depth       Depth of menu item. Used for padding.
       
   167 		 * @param array  $args        An array of arguments. @see wp_nav_menu()
       
   168 		 */
       
   169 		$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
       
   170 	}
       
   171 
       
   172 	/**
       
   173 	 * Ends the element output, if needed.
       
   174 	 *
       
   175 	 * @see Walker::end_el()
       
   176 	 *
       
   177 	 * @since 3.0.0
       
   178 	 *
       
   179 	 * @param string $output Passed by reference. Used to append additional content.
       
   180 	 * @param object $item   Page data object. Not used.
       
   181 	 * @param int    $depth  Depth of page. Not Used.
       
   182 	 * @param array  $args   An array of arguments. @see wp_nav_menu()
       
   183 	 */
       
   184 	function end_el( &$output, $item, $depth = 0, $args = array() ) {
       
   185 		$output .= "</li>\n";
       
   186 	}
       
   187 
       
   188 } // Walker_Nav_Menu
       
   189 
       
   190 /**
       
   191  * Displays a navigation menu.
       
   192  *
       
   193  * Optional $args contents:
       
   194  *
       
   195  * menu - The menu that is desired. Accepts (matching in order) id, slug, name. Defaults to blank.
       
   196  * menu_class - CSS class to use for the ul element which forms the menu. Defaults to 'menu'.
       
   197  * menu_id - The ID that is applied to the ul element which forms the menu. Defaults to the menu slug, incremented.
       
   198  * container - Whether to wrap the ul, and what to wrap it with. Defaults to 'div'.
       
   199  * container_class - the class that is applied to the container. Defaults to 'menu-{menu slug}-container'.
       
   200  * container_id - The ID that is applied to the container. Defaults to blank.
       
   201  * fallback_cb - If the menu doesn't exists, a callback function will fire. Defaults to 'wp_page_menu'. Set to false for no fallback.
       
   202  * before - Text before the link text.
       
   203  * after - Text after the link text.
       
   204  * link_before - Text before the link.
       
   205  * link_after - Text after the link.
       
   206  * echo - Whether to echo the menu or return it. Defaults to echo.
       
   207  * depth - how many levels of the hierarchy are to be included. 0 means all. Defaults to 0.
       
   208  * walker - allows a custom walker to be specified.
       
   209  * 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.
       
   210  * items_wrap - How the list items should be wrapped. Defaults to a ul with an id and class. Uses printf() format with numbered placeholders.
       
   211  *
       
   212  * @since 3.0.0
       
   213  *
       
   214  * @param array $args Arguments
       
   215  */
       
   216 function wp_nav_menu( $args = array() ) {
       
   217 	static $menu_id_slugs = array();
       
   218 
       
   219 	$defaults = array( 'menu' => '', 'container' => 'div', 'container_class' => '', 'container_id' => '', 'menu_class' => 'menu', 'menu_id' => '',
       
   220 	'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>',
       
   221 	'depth' => 0, 'walker' => '', 'theme_location' => '' );
       
   222 
       
   223 	$args = wp_parse_args( $args, $defaults );
       
   224 	/**
       
   225 	 * Filter the arguments used to display a navigation menu.
       
   226 	 *
       
   227 	 * @since 3.0.0
       
   228 	 *
       
   229 	 * @param array $args Arguments from {@see wp_nav_menu()}.
       
   230 	 */
       
   231 	$args = apply_filters( 'wp_nav_menu_args', $args );
       
   232 	$args = (object) $args;
       
   233 
       
   234 	// Get the nav menu based on the requested menu
       
   235 	$menu = wp_get_nav_menu_object( $args->menu );
       
   236 
       
   237 	// Get the nav menu based on the theme_location
       
   238 	if ( ! $menu && $args->theme_location && ( $locations = get_nav_menu_locations() ) && isset( $locations[ $args->theme_location ] ) )
       
   239 		$menu = wp_get_nav_menu_object( $locations[ $args->theme_location ] );
       
   240 
       
   241 	// get the first menu that has items if we still can't find a menu
       
   242 	if ( ! $menu && !$args->theme_location ) {
       
   243 		$menus = wp_get_nav_menus();
       
   244 		foreach ( $menus as $menu_maybe ) {
       
   245 			if ( $menu_items = wp_get_nav_menu_items( $menu_maybe->term_id, array( 'update_post_term_cache' => false ) ) ) {
       
   246 				$menu = $menu_maybe;
       
   247 				break;
       
   248 			}
       
   249 		}
       
   250 	}
       
   251 
       
   252 	// If the menu exists, get its items.
       
   253 	if ( $menu && ! is_wp_error($menu) && !isset($menu_items) )
       
   254 		$menu_items = wp_get_nav_menu_items( $menu->term_id, array( 'update_post_term_cache' => false ) );
       
   255 
       
   256 	/*
       
   257 	 * If no menu was found:
       
   258 	 *  - Fall back (if one was specified), or bail.
       
   259 	 *
       
   260 	 * If no menu items were found:
       
   261 	 *  - Fall back, but only if no theme location was specified.
       
   262 	 *  - Otherwise, bail.
       
   263 	 */
       
   264 	if ( ( !$menu || is_wp_error($menu) || ( isset($menu_items) && empty($menu_items) && !$args->theme_location ) )
       
   265 		&& $args->fallback_cb && is_callable( $args->fallback_cb ) )
       
   266 			return call_user_func( $args->fallback_cb, (array) $args );
       
   267 
       
   268 	if ( ! $menu || is_wp_error( $menu ) )
       
   269 		return false;
       
   270 
       
   271 	$nav_menu = $items = '';
       
   272 
       
   273 	$show_container = false;
       
   274 	if ( $args->container ) {
       
   275 		/**
       
   276 		 * Filter the list of HTML tags that are valid for use as menu containers.
       
   277 		 *
       
   278 		 * @since 3.0.0
       
   279 		 *
       
   280 		 * @param array The acceptable HTML tags for use as menu containers, defaults as 'div' and 'nav'.
       
   281 		 */
       
   282 		$allowed_tags = apply_filters( 'wp_nav_menu_container_allowedtags', array( 'div', 'nav' ) );
       
   283 		if ( in_array( $args->container, $allowed_tags ) ) {
       
   284 			$show_container = true;
       
   285 			$class = $args->container_class ? ' class="' . esc_attr( $args->container_class ) . '"' : ' class="menu-'. $menu->slug .'-container"';
       
   286 			$id = $args->container_id ? ' id="' . esc_attr( $args->container_id ) . '"' : '';
       
   287 			$nav_menu .= '<'. $args->container . $id . $class . '>';
       
   288 		}
       
   289 	}
       
   290 
       
   291 	// Set up the $menu_item variables
       
   292 	_wp_menu_item_classes_by_context( $menu_items );
       
   293 
       
   294 	$sorted_menu_items = $menu_items_with_children = array();
       
   295 	foreach ( (array) $menu_items as $menu_item ) {
       
   296 		$sorted_menu_items[ $menu_item->menu_order ] = $menu_item;
       
   297 		if ( $menu_item->menu_item_parent )
       
   298 			$menu_items_with_children[ $menu_item->menu_item_parent ] = true;
       
   299 	}
       
   300 
       
   301 	// Add the menu-item-has-children class where applicable
       
   302 	if ( $menu_items_with_children ) {
       
   303 		foreach ( $sorted_menu_items as &$menu_item ) {
       
   304 			if ( isset( $menu_items_with_children[ $menu_item->ID ] ) )
       
   305 				$menu_item->classes[] = 'menu-item-has-children';
       
   306 		}
       
   307 	}
       
   308 
       
   309 	unset( $menu_items, $menu_item );
       
   310 
       
   311 	/**
       
   312 	 * Filter the sorted list of menu item objects before generating the menu's HTML.
       
   313 	 *
       
   314 	 * @since 3.1.0
       
   315 	 *
       
   316 	 * @param array $sorted_menu_items The menu items, sorted by each menu item's menu order.
       
   317 	 */
       
   318 	$sorted_menu_items = apply_filters( 'wp_nav_menu_objects', $sorted_menu_items, $args );
       
   319 
       
   320 	$items .= walk_nav_menu_tree( $sorted_menu_items, $args->depth, $args );
       
   321 	unset($sorted_menu_items);
       
   322 
       
   323 	// Attributes
       
   324 	if ( ! empty( $args->menu_id ) ) {
       
   325 		$wrap_id = $args->menu_id;
       
   326 	} else {
       
   327 		$wrap_id = 'menu-' . $menu->slug;
       
   328 		while ( in_array( $wrap_id, $menu_id_slugs ) ) {
       
   329 			if ( preg_match( '#-(\d+)$#', $wrap_id, $matches ) )
       
   330 				$wrap_id = preg_replace('#-(\d+)$#', '-' . ++$matches[1], $wrap_id );
       
   331 			else
       
   332 				$wrap_id = $wrap_id . '-1';
       
   333 		}
       
   334 	}
       
   335 	$menu_id_slugs[] = $wrap_id;
       
   336 
       
   337 	$wrap_class = $args->menu_class ? $args->menu_class : '';
       
   338 
       
   339 	/**
       
   340 	 * Filter the HTML list content for navigation menus.
       
   341 	 *
       
   342 	 * @since 3.0.0
       
   343 	 *
       
   344 	 * @param string $items The HTML list content for the menu items.
       
   345 	 * @param array $args Arguments from {@see wp_nav_menu()}.
       
   346 	 */
       
   347 	$items = apply_filters( 'wp_nav_menu_items', $items, $args );
       
   348 	/**
       
   349 	 * Filter the HTML list content for a specific navigation menu.
       
   350 	 *
       
   351 	 * @since 3.0.0
       
   352 	 *
       
   353 	 * @param string $items The HTML list content for the menu items.
       
   354 	 * @param array $args Arguments from {@see wp_nav_menu()}.
       
   355 	 */
       
   356 	$items = apply_filters( "wp_nav_menu_{$menu->slug}_items", $items, $args );
       
   357 
       
   358 	// Don't print any markup if there are no items at this point.
       
   359 	if ( empty( $items ) )
       
   360 		return false;
       
   361 
       
   362 	$nav_menu .= sprintf( $args->items_wrap, esc_attr( $wrap_id ), esc_attr( $wrap_class ), $items );
       
   363 	unset( $items );
       
   364 
       
   365 	if ( $show_container )
       
   366 		$nav_menu .= '</' . $args->container . '>';
       
   367 
       
   368 	/**
       
   369 	 * Filter the HTML content for navigation menus.
       
   370 	 *
       
   371 	 * @since 3.0.0
       
   372 	 *
       
   373 	 * @param string $nav_menu The HTML content for the navigation menu.
       
   374 	 * @param array $args Arguments from {@see wp_nav_menu()}.
       
   375 	 */
       
   376 	$nav_menu = apply_filters( 'wp_nav_menu', $nav_menu, $args );
       
   377 
       
   378 	if ( $args->echo )
       
   379 		echo $nav_menu;
       
   380 	else
       
   381 		return $nav_menu;
       
   382 }
       
   383 
       
   384 /**
       
   385  * Add the class property classes for the current context, if applicable.
       
   386  *
       
   387  * @access private
       
   388  * @since 3.0
       
   389  *
       
   390  * @param array $menu_items The current menu item objects to which to add the class property information.
       
   391  */
       
   392 function _wp_menu_item_classes_by_context( &$menu_items ) {
       
   393 	global $wp_query, $wp_rewrite;
       
   394 
       
   395 	$queried_object = $wp_query->get_queried_object();
       
   396 	$queried_object_id = (int) $wp_query->queried_object_id;
       
   397 
       
   398 	$active_object = '';
       
   399 	$active_ancestor_item_ids = array();
       
   400 	$active_parent_item_ids = array();
       
   401 	$active_parent_object_ids = array();
       
   402 	$possible_taxonomy_ancestors = array();
       
   403 	$possible_object_parents = array();
       
   404 	$home_page_id = (int) get_option( 'page_for_posts' );
       
   405 
       
   406 	if ( $wp_query->is_singular && ! empty( $queried_object->post_type ) && ! is_post_type_hierarchical( $queried_object->post_type ) ) {
       
   407 		foreach ( (array) get_object_taxonomies( $queried_object->post_type ) as $taxonomy ) {
       
   408 			if ( is_taxonomy_hierarchical( $taxonomy ) ) {
       
   409 				$term_hierarchy = _get_term_hierarchy( $taxonomy );
       
   410 				$terms = wp_get_object_terms( $queried_object_id, $taxonomy, array( 'fields' => 'ids' ) );
       
   411 				if ( is_array( $terms ) ) {
       
   412 					$possible_object_parents = array_merge( $possible_object_parents, $terms );
       
   413 					$term_to_ancestor = array();
       
   414 					foreach ( (array) $term_hierarchy as $anc => $descs ) {
       
   415 						foreach ( (array) $descs as $desc )
       
   416 							$term_to_ancestor[ $desc ] = $anc;
       
   417 					}
       
   418 
       
   419 					foreach ( $terms as $desc ) {
       
   420 						do {
       
   421 							$possible_taxonomy_ancestors[ $taxonomy ][] = $desc;
       
   422 							if ( isset( $term_to_ancestor[ $desc ] ) ) {
       
   423 								$_desc = $term_to_ancestor[ $desc ];
       
   424 								unset( $term_to_ancestor[ $desc ] );
       
   425 								$desc = $_desc;
       
   426 							} else {
       
   427 								$desc = 0;
       
   428 							}
       
   429 						} while ( ! empty( $desc ) );
       
   430 					}
       
   431 				}
       
   432 			}
       
   433 		}
       
   434 	} elseif ( ! empty( $queried_object->taxonomy ) && is_taxonomy_hierarchical( $queried_object->taxonomy ) ) {
       
   435 		$term_hierarchy = _get_term_hierarchy( $queried_object->taxonomy );
       
   436 		$term_to_ancestor = array();
       
   437 		foreach ( (array) $term_hierarchy as $anc => $descs ) {
       
   438 			foreach ( (array) $descs as $desc )
       
   439 				$term_to_ancestor[ $desc ] = $anc;
       
   440 		}
       
   441 		$desc = $queried_object->term_id;
       
   442 		do {
       
   443 			$possible_taxonomy_ancestors[ $queried_object->taxonomy ][] = $desc;
       
   444 			if ( isset( $term_to_ancestor[ $desc ] ) ) {
       
   445 				$_desc = $term_to_ancestor[ $desc ];
       
   446 				unset( $term_to_ancestor[ $desc ] );
       
   447 				$desc = $_desc;
       
   448 			} else {
       
   449 				$desc = 0;
       
   450 			}
       
   451 		} while ( ! empty( $desc ) );
       
   452 	}
       
   453 
       
   454 	$possible_object_parents = array_filter( $possible_object_parents );
       
   455 
       
   456 	$front_page_url = home_url();
       
   457 
       
   458 	foreach ( (array) $menu_items as $key => $menu_item ) {
       
   459 
       
   460 		$menu_items[$key]->current = false;
       
   461 
       
   462 		$classes = (array) $menu_item->classes;
       
   463 		$classes[] = 'menu-item';
       
   464 		$classes[] = 'menu-item-type-' . $menu_item->type;
       
   465 		$classes[] = 'menu-item-object-' . $menu_item->object;
       
   466 
       
   467 		// if the menu item corresponds to a taxonomy term for the currently-queried non-hierarchical post object
       
   468 		if ( $wp_query->is_singular && 'taxonomy' == $menu_item->type && in_array( $menu_item->object_id, $possible_object_parents ) ) {
       
   469 			$active_parent_object_ids[] = (int) $menu_item->object_id;
       
   470 			$active_parent_item_ids[] = (int) $menu_item->db_id;
       
   471 			$active_object = $queried_object->post_type;
       
   472 
       
   473 		// if the menu item corresponds to the currently-queried post or taxonomy object
       
   474 		} elseif (
       
   475 			$menu_item->object_id == $queried_object_id &&
       
   476 			(
       
   477 				( ! empty( $home_page_id ) && 'post_type' == $menu_item->type && $wp_query->is_home && $home_page_id == $menu_item->object_id ) ||
       
   478 				( 'post_type' == $menu_item->type && $wp_query->is_singular ) ||
       
   479 				( 'taxonomy' == $menu_item->type && ( $wp_query->is_category || $wp_query->is_tag || $wp_query->is_tax ) && $queried_object->taxonomy == $menu_item->object )
       
   480 			)
       
   481 		) {
       
   482 			$classes[] = 'current-menu-item';
       
   483 			$menu_items[$key]->current = true;
       
   484 			$_anc_id = (int) $menu_item->db_id;
       
   485 
       
   486 			while(
       
   487 				( $_anc_id = get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) ) &&
       
   488 				! in_array( $_anc_id, $active_ancestor_item_ids )
       
   489 			) {
       
   490 				$active_ancestor_item_ids[] = $_anc_id;
       
   491 			}
       
   492 
       
   493 			if ( 'post_type' == $menu_item->type && 'page' == $menu_item->object ) {
       
   494 				// Back compat classes for pages to match wp_page_menu()
       
   495 				$classes[] = 'page_item';
       
   496 				$classes[] = 'page-item-' . $menu_item->object_id;
       
   497 				$classes[] = 'current_page_item';
       
   498 			}
       
   499 			$active_parent_item_ids[] = (int) $menu_item->menu_item_parent;
       
   500 			$active_parent_object_ids[] = (int) $menu_item->post_parent;
       
   501 			$active_object = $menu_item->object;
       
   502 
       
   503 		// if the menu item corresponds to the currently-requested URL
       
   504 		} elseif ( 'custom' == $menu_item->object ) {
       
   505 			$_root_relative_current = untrailingslashit( $_SERVER['REQUEST_URI'] );
       
   506 			$current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_root_relative_current );
       
   507 			$raw_item_url = strpos( $menu_item->url, '#' ) ? substr( $menu_item->url, 0, strpos( $menu_item->url, '#' ) ) : $menu_item->url;
       
   508 			$item_url = untrailingslashit( $raw_item_url );
       
   509 			$_indexless_current = untrailingslashit( preg_replace( '/' . preg_quote( $wp_rewrite->index, '/' ) . '$/', '', $current_url ) );
       
   510 
       
   511 			if ( $raw_item_url && in_array( $item_url, array( $current_url, $_indexless_current, $_root_relative_current ) ) ) {
       
   512 				$classes[] = 'current-menu-item';
       
   513 				$menu_items[$key]->current = true;
       
   514 				$_anc_id = (int) $menu_item->db_id;
       
   515 
       
   516 				while(
       
   517 					( $_anc_id = get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) ) &&
       
   518 					! in_array( $_anc_id, $active_ancestor_item_ids )
       
   519 				) {
       
   520 					$active_ancestor_item_ids[] = $_anc_id;
       
   521 				}
       
   522 
       
   523 				if ( in_array( home_url(), array( untrailingslashit( $current_url ), untrailingslashit( $_indexless_current ) ) ) ) {
       
   524 					// Back compat for home link to match wp_page_menu()
       
   525 					$classes[] = 'current_page_item';
       
   526 				}
       
   527 				$active_parent_item_ids[] = (int) $menu_item->menu_item_parent;
       
   528 				$active_parent_object_ids[] = (int) $menu_item->post_parent;
       
   529 				$active_object = $menu_item->object;
       
   530 
       
   531 			// give front page item current-menu-item class when extra query arguments involved
       
   532 			} elseif ( $item_url == $front_page_url && is_front_page() ) {
       
   533 				$classes[] = 'current-menu-item';
       
   534 			}
       
   535 
       
   536 			if ( untrailingslashit($item_url) == home_url() )
       
   537 				$classes[] = 'menu-item-home';
       
   538 		}
       
   539 
       
   540 		// back-compat with wp_page_menu: add "current_page_parent" to static home page link for any non-page query
       
   541 		if ( ! empty( $home_page_id ) && 'post_type' == $menu_item->type && empty( $wp_query->is_page ) && $home_page_id == $menu_item->object_id )
       
   542 			$classes[] = 'current_page_parent';
       
   543 
       
   544 		$menu_items[$key]->classes = array_unique( $classes );
       
   545 	}
       
   546 	$active_ancestor_item_ids = array_filter( array_unique( $active_ancestor_item_ids ) );
       
   547 	$active_parent_item_ids = array_filter( array_unique( $active_parent_item_ids ) );
       
   548 	$active_parent_object_ids = array_filter( array_unique( $active_parent_object_ids ) );
       
   549 
       
   550 	// set parent's class
       
   551 	foreach ( (array) $menu_items as $key => $parent_item ) {
       
   552 		$classes = (array) $parent_item->classes;
       
   553 		$menu_items[$key]->current_item_ancestor = false;
       
   554 		$menu_items[$key]->current_item_parent = false;
       
   555 
       
   556 		if (
       
   557 			isset( $parent_item->type ) &&
       
   558 			(
       
   559 				// ancestral post object
       
   560 				(
       
   561 					'post_type' == $parent_item->type &&
       
   562 					! empty( $queried_object->post_type ) &&
       
   563 					is_post_type_hierarchical( $queried_object->post_type ) &&
       
   564 					in_array( $parent_item->object_id, $queried_object->ancestors ) &&
       
   565 					$parent_item->object != $queried_object->ID
       
   566 				) ||
       
   567 
       
   568 				// ancestral term
       
   569 				(
       
   570 					'taxonomy' == $parent_item->type &&
       
   571 					isset( $possible_taxonomy_ancestors[ $parent_item->object ] ) &&
       
   572 					in_array( $parent_item->object_id, $possible_taxonomy_ancestors[ $parent_item->object ] ) &&
       
   573 					(
       
   574 						! isset( $queried_object->term_id ) ||
       
   575 						$parent_item->object_id != $queried_object->term_id
       
   576 					)
       
   577 				)
       
   578 			)
       
   579 		) {
       
   580 			$classes[] = empty( $queried_object->taxonomy ) ? 'current-' . $queried_object->post_type . '-ancestor' : 'current-' . $queried_object->taxonomy . '-ancestor';
       
   581 		}
       
   582 
       
   583 		if ( in_array(  intval( $parent_item->db_id ), $active_ancestor_item_ids ) ) {
       
   584 			$classes[] = 'current-menu-ancestor';
       
   585 			$menu_items[$key]->current_item_ancestor = true;
       
   586 		}
       
   587 		if ( in_array( $parent_item->db_id, $active_parent_item_ids ) ) {
       
   588 			$classes[] = 'current-menu-parent';
       
   589 			$menu_items[$key]->current_item_parent = true;
       
   590 		}
       
   591 		if ( in_array( $parent_item->object_id, $active_parent_object_ids ) )
       
   592 			$classes[] = 'current-' . $active_object . '-parent';
       
   593 
       
   594 		if ( 'post_type' == $parent_item->type && 'page' == $parent_item->object ) {
       
   595 			// Back compat classes for pages to match wp_page_menu()
       
   596 			if ( in_array('current-menu-parent', $classes) )
       
   597 				$classes[] = 'current_page_parent';
       
   598 			if ( in_array('current-menu-ancestor', $classes) )
       
   599 				$classes[] = 'current_page_ancestor';
       
   600 		}
       
   601 
       
   602 		$menu_items[$key]->classes = array_unique( $classes );
       
   603 	}
       
   604 }
       
   605 
       
   606 /**
       
   607  * Retrieve the HTML list content for nav menu items.
       
   608  *
       
   609  * @uses Walker_Nav_Menu to create HTML list content.
       
   610  * @since 3.0.0
       
   611  * @see Walker::walk() for parameters and return description.
       
   612  */
       
   613 function walk_nav_menu_tree( $items, $depth, $r ) {
       
   614 	$walker = ( empty($r->walker) ) ? new Walker_Nav_Menu : $r->walker;
       
   615 	$args = array( $items, $depth, $r );
       
   616 
       
   617 	return call_user_func_array( array($walker, 'walk'), $args );
       
   618 }
       
   619 
       
   620 /**
       
   621  * Prevents a menu item ID from being used more than once.
       
   622  *
       
   623  * @since 3.0.1
       
   624  * @access private
       
   625  */
       
   626 function _nav_menu_item_id_use_once( $id, $item ) {
       
   627 	static $_used_ids = array();
       
   628 	if ( in_array( $item->ID, $_used_ids ) )
       
   629 		return '';
       
   630 	$_used_ids[] = $item->ID;
       
   631 	return $id;
       
   632 }
       
   633 add_filter( 'nav_menu_item_id', '_nav_menu_item_id_use_once', 10, 2 );