~canonical-sysadmins/wordpress/4.5.2

« back to all changes in this revision

Viewing changes to wp-includes/nav-menu-template.php

  • Committer: Jacek Nykis
  • Date: 2015-01-05 16:17:05 UTC
  • Revision ID: jacek.nykis@canonical.com-20150105161705-w544l1h5mcg7u4w9
Initial commit

Show diffs side-by-side

added added

removed removed

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