home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress / wp-includes / nav-menu-template.php < prev    next >
Encoding:
PHP Script  |  2017-07-05  |  20.4 KB  |  553 lines

  1. <?php
  2. /**
  3.  * Nav Menu API: Template functions
  4.  *
  5.  * @package WordPress
  6.  * @subpackage Nav_Menus
  7.  * @since 3.0.0
  8.  */
  9.  
  10. /** Walker_Nav_Menu class */
  11. require_once ABSPATH . WPINC . '/class-walker-nav-menu.php';
  12.  
  13. /**
  14.  * Displays a navigation menu.
  15.  *
  16.  * @since 3.0.0
  17.  * @since 4.7.0 Added the `item_spacing` argument.
  18.  *
  19.  * @staticvar array $menu_id_slugs
  20.  *
  21.  * @param array $args {
  22.  *     Optional. Array of nav menu arguments.
  23.  *
  24.  *     @type int|string|WP_Term $menu            Desired menu. Accepts a menu ID, slug, name, or object. Default empty.
  25.  *     @type string             $menu_class      CSS class to use for the ul element which forms the menu. Default 'menu'.
  26.  *     @type string             $menu_id         The ID that is applied to the ul element which forms the menu.
  27.  *                                               Default is the menu slug, incremented.
  28.  *     @type string             $container       Whether to wrap the ul, and what to wrap it with. Default 'div'.
  29.  *     @type string             $container_class Class that is applied to the container. Default 'menu-{menu slug}-container'.
  30.  *     @type string             $container_id    The ID that is applied to the container. Default empty.
  31.  *     @type callable|bool      $fallback_cb     If the menu doesn't exists, a callback function will fire.
  32.  *                                               Default is 'wp_page_menu'. Set to false for no fallback.
  33.  *     @type string             $before          Text before the link markup. Default empty.
  34.  *     @type string             $after           Text after the link markup. Default empty.
  35.  *     @type string             $link_before     Text before the link text. Default empty.
  36.  *     @type string             $link_after      Text after the link text. Default empty.
  37.  *     @type bool               $echo            Whether to echo the menu or return it. Default true.
  38.  *     @type int                $depth           How many levels of the hierarchy are to be included. 0 means all. Default 0.
  39.  *     @type object             $walker          Instance of a custom walker class. Default empty.
  40.  *     @type string             $theme_location  Theme location to be used. Must be registered with register_nav_menu()
  41.  *                                               in order to be selectable by the user.
  42.  *     @type string             $items_wrap      How the list items should be wrapped. Default is a ul with an id and class.
  43.  *                                               Uses printf() format with numbered placeholders.
  44.  *     @type string             $item_spacing    Whether to preserve whitespace within the menu's HTML. Accepts 'preserve' or 'discard'. Default 'preserve'.
  45.  * }
  46.  * @return string|false|void Menu output if $echo is false, false if there are no items or no menu was found.
  47.  */
  48. function wp_nav_menu( $args = array() ) {
  49.     static $menu_id_slugs = array();
  50.  
  51.     $defaults = array( 'menu' => '', 'container' => 'div', 'container_class' => '', 'container_id' => '', 'menu_class' => 'menu', 'menu_id' => '',
  52.     '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>', 'item_spacing' => 'preserve',
  53.     'depth' => 0, 'walker' => '', 'theme_location' => '' );
  54.  
  55.     $args = wp_parse_args( $args, $defaults );
  56.  
  57.     if ( ! in_array( $args['item_spacing'], array( 'preserve', 'discard' ), true ) ) {
  58.         // invalid value, fall back to default.
  59.         $args['item_spacing'] = $defaults['item_spacing'];
  60.     }
  61.  
  62.     /**
  63.      * Filters the arguments used to display a navigation menu.
  64.      *
  65.      * @since 3.0.0
  66.      *
  67.      * @see wp_nav_menu()
  68.      *
  69.      * @param array $args Array of wp_nav_menu() arguments.
  70.      */
  71.     $args = apply_filters( 'wp_nav_menu_args', $args );
  72.     $args = (object) $args;
  73.  
  74.     /**
  75.      * Filters whether to short-circuit the wp_nav_menu() output.
  76.      *
  77.      * Returning a non-null value to the filter will short-circuit
  78.      * wp_nav_menu(), echoing that value if $args->echo is true,
  79.      * returning that value otherwise.
  80.      *
  81.      * @since 3.9.0
  82.      *
  83.      * @see wp_nav_menu()
  84.      *
  85.      * @param string|null $output Nav menu output to short-circuit with. Default null.
  86.      * @param stdClass    $args   An object containing wp_nav_menu() arguments.
  87.      */
  88.     $nav_menu = apply_filters( 'pre_wp_nav_menu', null, $args );
  89.  
  90.     if ( null !== $nav_menu ) {
  91.         if ( $args->echo ) {
  92.             echo $nav_menu;
  93.             return;
  94.         }
  95.  
  96.         return $nav_menu;
  97.     }
  98.  
  99.     // Get the nav menu based on the requested menu
  100.     $menu = wp_get_nav_menu_object( $args->menu );
  101.  
  102.     // Get the nav menu based on the theme_location
  103.     if ( ! $menu && $args->theme_location && ( $locations = get_nav_menu_locations() ) && isset( $locations[ $args->theme_location ] ) )
  104.         $menu = wp_get_nav_menu_object( $locations[ $args->theme_location ] );
  105.  
  106.     // get the first menu that has items if we still can't find a menu
  107.     if ( ! $menu && !$args->theme_location ) {
  108.         $menus = wp_get_nav_menus();
  109.         foreach ( $menus as $menu_maybe ) {
  110.             if ( $menu_items = wp_get_nav_menu_items( $menu_maybe->term_id, array( 'update_post_term_cache' => false ) ) ) {
  111.                 $menu = $menu_maybe;
  112.                 break;
  113.             }
  114.         }
  115.     }
  116.  
  117.     if ( empty( $args->menu ) ) {
  118.         $args->menu = $menu;
  119.     }
  120.  
  121.     // If the menu exists, get its items.
  122.     if ( $menu && ! is_wp_error($menu) && !isset($menu_items) )
  123.         $menu_items = wp_get_nav_menu_items( $menu->term_id, array( 'update_post_term_cache' => false ) );
  124.  
  125.     /*
  126.      * If no menu was found:
  127.      *  - Fall back (if one was specified), or bail.
  128.      *
  129.      * If no menu items were found:
  130.      *  - Fall back, but only if no theme location was specified.
  131.      *  - Otherwise, bail.
  132.      */
  133.     if ( ( !$menu || is_wp_error($menu) || ( isset($menu_items) && empty($menu_items) && !$args->theme_location ) )
  134.         && isset( $args->fallback_cb ) && $args->fallback_cb && is_callable( $args->fallback_cb ) )
  135.             return call_user_func( $args->fallback_cb, (array) $args );
  136.  
  137.     if ( ! $menu || is_wp_error( $menu ) )
  138.         return false;
  139.  
  140.     $nav_menu = $items = '';
  141.  
  142.     $show_container = false;
  143.     if ( $args->container ) {
  144.         /**
  145.          * Filters the list of HTML tags that are valid for use as menu containers.
  146.          *
  147.          * @since 3.0.0
  148.          *
  149.          * @param array $tags The acceptable HTML tags for use as menu containers.
  150.          *                    Default is array containing 'div' and 'nav'.
  151.          */
  152.         $allowed_tags = apply_filters( 'wp_nav_menu_container_allowedtags', array( 'div', 'nav' ) );
  153.         if ( is_string( $args->container ) && in_array( $args->container, $allowed_tags ) ) {
  154.             $show_container = true;
  155.             $class = $args->container_class ? ' class="' . esc_attr( $args->container_class ) . '"' : ' class="menu-'. $menu->slug .'-container"';
  156.             $id = $args->container_id ? ' id="' . esc_attr( $args->container_id ) . '"' : '';
  157.             $nav_menu .= '<'. $args->container . $id . $class . '>';
  158.         }
  159.     }
  160.  
  161.     // Set up the $menu_item variables
  162.     _wp_menu_item_classes_by_context( $menu_items );
  163.  
  164.     $sorted_menu_items = $menu_items_with_children = array();
  165.     foreach ( (array) $menu_items as $menu_item ) {
  166.         $sorted_menu_items[ $menu_item->menu_order ] = $menu_item;
  167.         if ( $menu_item->menu_item_parent )
  168.             $menu_items_with_children[ $menu_item->menu_item_parent ] = true;
  169.     }
  170.  
  171.     // Add the menu-item-has-children class where applicable
  172.     if ( $menu_items_with_children ) {
  173.         foreach ( $sorted_menu_items as &$menu_item ) {
  174.             if ( isset( $menu_items_with_children[ $menu_item->ID ] ) )
  175.                 $menu_item->classes[] = 'menu-item-has-children';
  176.         }
  177.     }
  178.  
  179.     unset( $menu_items, $menu_item );
  180.  
  181.     /**
  182.      * Filters the sorted list of menu item objects before generating the menu's HTML.
  183.      *
  184.      * @since 3.1.0
  185.      *
  186.      * @param array    $sorted_menu_items The menu items, sorted by each menu item's menu order.
  187.      * @param stdClass $args              An object containing wp_nav_menu() arguments.
  188.      */
  189.     $sorted_menu_items = apply_filters( 'wp_nav_menu_objects', $sorted_menu_items, $args );
  190.  
  191.     $items .= walk_nav_menu_tree( $sorted_menu_items, $args->depth, $args );
  192.     unset($sorted_menu_items);
  193.  
  194.     // Attributes
  195.     if ( ! empty( $args->menu_id ) ) {
  196.         $wrap_id = $args->menu_id;
  197.     } else {
  198.         $wrap_id = 'menu-' . $menu->slug;
  199.         while ( in_array( $wrap_id, $menu_id_slugs ) ) {
  200.             if ( preg_match( '#-(\d+)$#', $wrap_id, $matches ) )
  201.                 $wrap_id = preg_replace('#-(\d+)$#', '-' . ++$matches[1], $wrap_id );
  202.             else
  203.                 $wrap_id = $wrap_id . '-1';
  204.         }
  205.     }
  206.     $menu_id_slugs[] = $wrap_id;
  207.  
  208.     $wrap_class = $args->menu_class ? $args->menu_class : '';
  209.  
  210.     /**
  211.      * Filters the HTML list content for navigation menus.
  212.      *
  213.      * @since 3.0.0
  214.      *
  215.      * @see wp_nav_menu()
  216.      *
  217.      * @param string   $items The HTML list content for the menu items.
  218.      * @param stdClass $args  An object containing wp_nav_menu() arguments.
  219.      */
  220.     $items = apply_filters( 'wp_nav_menu_items', $items, $args );
  221.     /**
  222.      * Filters the HTML list content for a specific navigation menu.
  223.      *
  224.      * @since 3.0.0
  225.      *
  226.      * @see wp_nav_menu()
  227.      *
  228.      * @param string   $items The HTML list content for the menu items.
  229.      * @param stdClass $args  An object containing wp_nav_menu() arguments.
  230.      */
  231.     $items = apply_filters( "wp_nav_menu_{$menu->slug}_items", $items, $args );
  232.  
  233.     // Don't print any markup if there are no items at this point.
  234.     if ( empty( $items ) )
  235.         return false;
  236.  
  237.     $nav_menu .= sprintf( $args->items_wrap, esc_attr( $wrap_id ), esc_attr( $wrap_class ), $items );
  238.     unset( $items );
  239.  
  240.     if ( $show_container )
  241.         $nav_menu .= '</' . $args->container . '>';
  242.  
  243.     /**
  244.      * Filters the HTML content for navigation menus.
  245.      *
  246.      * @since 3.0.0
  247.      *
  248.      * @see wp_nav_menu()
  249.      *
  250.      * @param string   $nav_menu The HTML content for the navigation menu.
  251.      * @param stdClass $args     An object containing wp_nav_menu() arguments.
  252.      */
  253.     $nav_menu = apply_filters( 'wp_nav_menu', $nav_menu, $args );
  254.  
  255.     if ( $args->echo )
  256.         echo $nav_menu;
  257.     else
  258.         return $nav_menu;
  259. }
  260.  
  261. /**
  262.  * Add the class property classes for the current context, if applicable.
  263.  *
  264.  * @access private
  265.  * @since 3.0.0
  266.  *
  267.  * @global WP_Query   $wp_query
  268.  * @global WP_Rewrite $wp_rewrite
  269.  *
  270.  * @param array $menu_items The current menu item objects to which to add the class property information.
  271.  */
  272. function _wp_menu_item_classes_by_context( &$menu_items ) {
  273.     global $wp_query, $wp_rewrite;
  274.  
  275.     $queried_object = $wp_query->get_queried_object();
  276.     $queried_object_id = (int) $wp_query->queried_object_id;
  277.  
  278.     $active_object = '';
  279.     $active_ancestor_item_ids = array();
  280.     $active_parent_item_ids = array();
  281.     $active_parent_object_ids = array();
  282.     $possible_taxonomy_ancestors = array();
  283.     $possible_object_parents = array();
  284.     $home_page_id = (int) get_option( 'page_for_posts' );
  285.  
  286.     if ( $wp_query->is_singular && ! empty( $queried_object->post_type ) && ! is_post_type_hierarchical( $queried_object->post_type ) ) {
  287.         foreach ( (array) get_object_taxonomies( $queried_object->post_type ) as $taxonomy ) {
  288.             if ( is_taxonomy_hierarchical( $taxonomy ) ) {
  289.                 $term_hierarchy = _get_term_hierarchy( $taxonomy );
  290.                 $terms = wp_get_object_terms( $queried_object_id, $taxonomy, array( 'fields' => 'ids' ) );
  291.                 if ( is_array( $terms ) ) {
  292.                     $possible_object_parents = array_merge( $possible_object_parents, $terms );
  293.                     $term_to_ancestor = array();
  294.                     foreach ( (array) $term_hierarchy as $anc => $descs ) {
  295.                         foreach ( (array) $descs as $desc )
  296.                             $term_to_ancestor[ $desc ] = $anc;
  297.                     }
  298.  
  299.                     foreach ( $terms as $desc ) {
  300.                         do {
  301.                             $possible_taxonomy_ancestors[ $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.             }
  313.         }
  314.     } elseif ( ! empty( $queried_object->taxonomy ) && is_taxonomy_hierarchical( $queried_object->taxonomy ) ) {
  315.         $term_hierarchy = _get_term_hierarchy( $queried_object->taxonomy );
  316.         $term_to_ancestor = array();
  317.         foreach ( (array) $term_hierarchy as $anc => $descs ) {
  318.             foreach ( (array) $descs as $desc )
  319.                 $term_to_ancestor[ $desc ] = $anc;
  320.         }
  321.         $desc = $queried_object->term_id;
  322.         do {
  323.             $possible_taxonomy_ancestors[ $queried_object->taxonomy ][] = $desc;
  324.             if ( isset( $term_to_ancestor[ $desc ] ) ) {
  325.                 $_desc = $term_to_ancestor[ $desc ];
  326.                 unset( $term_to_ancestor[ $desc ] );
  327.                 $desc = $_desc;
  328.             } else {
  329.                 $desc = 0;
  330.             }
  331.         } while ( ! empty( $desc ) );
  332.     }
  333.  
  334.     $possible_object_parents = array_filter( $possible_object_parents );
  335.  
  336.     $front_page_url = home_url();
  337.     $front_page_id  = (int) get_option( 'page_on_front' );
  338.  
  339.     foreach ( (array) $menu_items as $key => $menu_item ) {
  340.  
  341.         $menu_items[$key]->current = false;
  342.  
  343.         $classes = (array) $menu_item->classes;
  344.         $classes[] = 'menu-item';
  345.         $classes[] = 'menu-item-type-' . $menu_item->type;
  346.         $classes[] = 'menu-item-object-' . $menu_item->object;
  347.  
  348.         // This menu item is set as the 'Front Page'.
  349.         if ( 'post_type' === $menu_item->type && $front_page_id === (int) $menu_item->object_id ) {
  350.             $classes[] = 'menu-item-home';
  351.         }
  352.  
  353.         // if the menu item corresponds to a taxonomy term for the currently-queried non-hierarchical post object
  354.         if ( $wp_query->is_singular && 'taxonomy' == $menu_item->type && in_array( $menu_item->object_id, $possible_object_parents ) ) {
  355.             $active_parent_object_ids[] = (int) $menu_item->object_id;
  356.             $active_parent_item_ids[] = (int) $menu_item->db_id;
  357.             $active_object = $queried_object->post_type;
  358.  
  359.         // if the menu item corresponds to the currently-queried post or taxonomy object
  360.         } elseif (
  361.             $menu_item->object_id == $queried_object_id &&
  362.             (
  363.                 ( ! empty( $home_page_id ) && 'post_type' == $menu_item->type && $wp_query->is_home && $home_page_id == $menu_item->object_id ) ||
  364.                 ( 'post_type' == $menu_item->type && $wp_query->is_singular ) ||
  365.                 ( 'taxonomy' == $menu_item->type && ( $wp_query->is_category || $wp_query->is_tag || $wp_query->is_tax ) && $queried_object->taxonomy == $menu_item->object )
  366.             )
  367.         ) {
  368.             $classes[] = 'current-menu-item';
  369.             $menu_items[$key]->current = true;
  370.             $_anc_id = (int) $menu_item->db_id;
  371.  
  372.             while(
  373.                 ( $_anc_id = get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) ) &&
  374.                 ! in_array( $_anc_id, $active_ancestor_item_ids )
  375.             ) {
  376.                 $active_ancestor_item_ids[] = $_anc_id;
  377.             }
  378.  
  379.             if ( 'post_type' == $menu_item->type && 'page' == $menu_item->object ) {
  380.                 // Back compat classes for pages to match wp_page_menu()
  381.                 $classes[] = 'page_item';
  382.                 $classes[] = 'page-item-' . $menu_item->object_id;
  383.                 $classes[] = 'current_page_item';
  384.             }
  385.  
  386.             $active_parent_item_ids[] = (int) $menu_item->menu_item_parent;
  387.             $active_parent_object_ids[] = (int) $menu_item->post_parent;
  388.             $active_object = $menu_item->object;
  389.  
  390.         // if the menu item corresponds to the currently-queried post type archive
  391.         } elseif (
  392.             'post_type_archive' == $menu_item->type &&
  393.             is_post_type_archive( array( $menu_item->object ) )
  394.         ) {
  395.             $classes[] = 'current-menu-item';
  396.             $menu_items[$key]->current = true;
  397.             $_anc_id = (int) $menu_item->db_id;
  398.  
  399.             while(
  400.                 ( $_anc_id = get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) ) &&
  401.                 ! in_array( $_anc_id, $active_ancestor_item_ids )
  402.             ) {
  403.                 $active_ancestor_item_ids[] = $_anc_id;
  404.             }
  405.  
  406.             $active_parent_item_ids[] = (int) $menu_item->menu_item_parent;
  407.  
  408.         // if the menu item corresponds to the currently-requested URL
  409.         } elseif ( 'custom' == $menu_item->object && isset( $_SERVER['HTTP_HOST'] ) ) {
  410.             $_root_relative_current = untrailingslashit( $_SERVER['REQUEST_URI'] );
  411.  
  412.             //if it is the customize page then it will strips the query var off the url before entering the comparison block.
  413.             if ( is_customize_preview() ) {
  414.                 $_root_relative_current = strtok( untrailingslashit( $_SERVER['REQUEST_URI'] ), '?' );
  415.             }
  416.             $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_root_relative_current );
  417.             $raw_item_url = strpos( $menu_item->url, '#' ) ? substr( $menu_item->url, 0, strpos( $menu_item->url, '#' ) ) : $menu_item->url;
  418.             $item_url = set_url_scheme( untrailingslashit( $raw_item_url ) );
  419.             $_indexless_current = untrailingslashit( preg_replace( '/' . preg_quote( $wp_rewrite->index, '/' ) . '$/', '', $current_url ) );
  420.  
  421.             if ( $raw_item_url && in_array( $item_url, array( $current_url, $_indexless_current, $_root_relative_current ) ) ) {
  422.                 $classes[] = 'current-menu-item';
  423.                 $menu_items[$key]->current = true;
  424.                 $_anc_id = (int) $menu_item->db_id;
  425.  
  426.                 while(
  427.                     ( $_anc_id = get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) ) &&
  428.                     ! in_array( $_anc_id, $active_ancestor_item_ids )
  429.                 ) {
  430.                     $active_ancestor_item_ids[] = $_anc_id;
  431.                 }
  432.  
  433.                 if ( in_array( home_url(), array( untrailingslashit( $current_url ), untrailingslashit( $_indexless_current ) ) ) ) {
  434.                     // Back compat for home link to match wp_page_menu()
  435.                     $classes[] = 'current_page_item';
  436.                 }
  437.                 $active_parent_item_ids[] = (int) $menu_item->menu_item_parent;
  438.                 $active_parent_object_ids[] = (int) $menu_item->post_parent;
  439.                 $active_object = $menu_item->object;
  440.  
  441.             // give front page item current-menu-item class when extra query arguments involved
  442.             } elseif ( $item_url == $front_page_url && is_front_page() ) {
  443.                 $classes[] = 'current-menu-item';
  444.             }
  445.  
  446.             if ( untrailingslashit($item_url) == home_url() )
  447.                 $classes[] = 'menu-item-home';
  448.         }
  449.  
  450.         // back-compat with wp_page_menu: add "current_page_parent" to static home page link for any non-page query
  451.         if ( ! empty( $home_page_id ) && 'post_type' == $menu_item->type && empty( $wp_query->is_page ) && $home_page_id == $menu_item->object_id )
  452.             $classes[] = 'current_page_parent';
  453.  
  454.         $menu_items[$key]->classes = array_unique( $classes );
  455.     }
  456.     $active_ancestor_item_ids = array_filter( array_unique( $active_ancestor_item_ids ) );
  457.     $active_parent_item_ids = array_filter( array_unique( $active_parent_item_ids ) );
  458.     $active_parent_object_ids = array_filter( array_unique( $active_parent_object_ids ) );
  459.  
  460.     // set parent's class
  461.     foreach ( (array) $menu_items as $key => $parent_item ) {
  462.         $classes = (array) $parent_item->classes;
  463.         $menu_items[$key]->current_item_ancestor = false;
  464.         $menu_items[$key]->current_item_parent = false;
  465.  
  466.         if (
  467.             isset( $parent_item->type ) &&
  468.             (
  469.                 // ancestral post object
  470.                 (
  471.                     'post_type' == $parent_item->type &&
  472.                     ! empty( $queried_object->post_type ) &&
  473.                     is_post_type_hierarchical( $queried_object->post_type ) &&
  474.                     in_array( $parent_item->object_id, $queried_object->ancestors ) &&
  475.                     $parent_item->object != $queried_object->ID
  476.                 ) ||
  477.  
  478.                 // ancestral term
  479.                 (
  480.                     'taxonomy' == $parent_item->type &&
  481.                     isset( $possible_taxonomy_ancestors[ $parent_item->object ] ) &&
  482.                     in_array( $parent_item->object_id, $possible_taxonomy_ancestors[ $parent_item->object ] ) &&
  483.                     (
  484.                         ! isset( $queried_object->term_id ) ||
  485.                         $parent_item->object_id != $queried_object->term_id
  486.                     )
  487.                 )
  488.             )
  489.         ) {
  490.             $classes[] = empty( $queried_object->taxonomy ) ? 'current-' . $queried_object->post_type . '-ancestor' : 'current-' . $queried_object->taxonomy . '-ancestor';
  491.         }
  492.  
  493.         if ( in_array(  intval( $parent_item->db_id ), $active_ancestor_item_ids ) ) {
  494.             $classes[] = 'current-menu-ancestor';
  495.             $menu_items[$key]->current_item_ancestor = true;
  496.         }
  497.         if ( in_array( $parent_item->db_id, $active_parent_item_ids ) ) {
  498.             $classes[] = 'current-menu-parent';
  499.             $menu_items[$key]->current_item_parent = true;
  500.         }
  501.         if ( in_array( $parent_item->object_id, $active_parent_object_ids ) )
  502.             $classes[] = 'current-' . $active_object . '-parent';
  503.  
  504.         if ( 'post_type' == $parent_item->type && 'page' == $parent_item->object ) {
  505.             // Back compat classes for pages to match wp_page_menu()
  506.             if ( in_array('current-menu-parent', $classes) )
  507.                 $classes[] = 'current_page_parent';
  508.             if ( in_array('current-menu-ancestor', $classes) )
  509.                 $classes[] = 'current_page_ancestor';
  510.         }
  511.  
  512.         $menu_items[$key]->classes = array_unique( $classes );
  513.     }
  514. }
  515.  
  516. /**
  517.  * Retrieve the HTML list content for nav menu items.
  518.  *
  519.  * @uses Walker_Nav_Menu to create HTML list content.
  520.  * @since 3.0.0
  521.  *
  522.  * @param array    $items The menu items, sorted by each menu item's menu order.
  523.  * @param int      $depth Depth of the item in reference to parents.
  524.  * @param stdClass $r     An object containing wp_nav_menu() arguments.
  525.  * @return string The HTML list content for the menu items.
  526.  */
  527. function walk_nav_menu_tree( $items, $depth, $r ) {
  528.     $walker = ( empty($r->walker) ) ? new Walker_Nav_Menu : $r->walker;
  529.     $args = array( $items, $depth, $r );
  530.  
  531.     return call_user_func_array( array( $walker, 'walk' ), $args );
  532. }
  533.  
  534. /**
  535.  * Prevents a menu item ID from being used more than once.
  536.  *
  537.  * @since 3.0.1
  538.  * @access private
  539.  *
  540.  * @staticvar array $used_ids
  541.  * @param string $id
  542.  * @param object $item
  543.  * @return string
  544.  */
  545. function _nav_menu_item_id_use_once( $id, $item ) {
  546.     static $_used_ids = array();
  547.     if ( in_array( $item->ID, $_used_ids ) ) {
  548.         return '';
  549.     }
  550.     $_used_ids[] = $item->ID;
  551.     return $id;
  552. }
  553.