PluginProbe
Responsive Menu – Create Mobile-Friendly Menu / 4.7.4
Responsive Menu – Create Mobile-Friendly Menu v4.7.4
4.7.4 4.7.3 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.10 4.1.11 4.1.12 4.1.2 4.1.3 4.1.4 4.1.5 4.1.6 4.1.7 4.1.8 4.1.9 All 91 releases
← All changes | v4.0.0/inc/classes/class-walker.php +162 -98 4.0.1 → 4.7.4 View file →
@@ -1,7 +1,7 @@
1 1 <?php
2 2 /**
3 - * This is core class file for responsive menu pro to design the menu
3 + * This is core class file for responsive menu pro to design the menu
4 4 * with custom walker with saved settings.
5 5 *
6 6 * @since 4.0.0
7 7 *
@@ -11,14 +11,14 @@
11 11 namespace RMP\Features\Inc;
12 12
13 13 /** Disable the direct access to this class */
14 14 if ( ! defined( 'ABSPATH' ) ) {
15 - exit;
15 + exit;
16 16 }
17 17
18 18 /**
19 19 * Class Walker prepare the menu as per loction and menu id.
20 - *
20 + *
21 21 * @since 4.0.0
22 22 * @package responsive_menu_pro
23 23 *
24 24 * @author Expresstech System
@@ -24,8 +24,9 @@
24 24 * @author Expresstech System
25 25 */
26 26 class Walker extends \Walker_Nav_Menu {
27 27
28 +
28 29 /**
29 30 * Hold the current menu item.
30 31 *
31 32 * @since 4.0.0
@@ -35,9 +36,9 @@
35 36 private $current_item;
36 37
37 38 /**
38 39 * Hold the top most item id
39 - *
40 + *
40 41 * @since 4.0.0
41 42 * @access protected
42 43 * @var object
43 44 */
@@ -44,9 +45,9 @@
44 45 protected $root_item_id;
45 46
46 47 /**
47 48 * This is Walker class constructor function.
48 - *
49 + *
49 50 * @access public
50 51 * @param array $option
51 52 */
52 53 public function __construct( $options ) {
@@ -53,13 +54,51 @@
53 54 $this->options = $options;
54 55 }
55 56
56 57 /**
58 + * Drop the "has children" class from items whose children are not in the list.
59 + *
60 + * WordPress stamps `menu-item-has-children` on before `wp_nav_menu_objects` runs, so
61 + * anything that removes items on that filter - the plugin's own logged-in/logged-out
62 + * visibility settings among them - can leave a parent claiming children it no longer
63 + * has. The walker would then emit a submenu toggle whose aria-controls points at a
64 + * <ul> that is never rendered: a control that announces a collapsed submenu, resolves
65 + * to nothing, and does nothing when activated.
66 + *
67 + * @since 4.7.4
68 + * @access public
69 + *
70 + * @param array $elements Menu item objects.
71 + * @param int $max_depth Maximum depth to walk.
72 + * @param mixed ...$args Arguments passed through to the parent walker.
73 + * @return string
74 + */
75 + public function walk( $elements, $max_depth, ...$args ) {
76 + $has_children = array();
77 +
78 + foreach ( (array) $elements as $element ) {
79 + if ( ! empty( $element->menu_item_parent ) ) {
80 + $has_children[ intval( $element->menu_item_parent ) ] = true;
81 + }
82 + }
83 +
84 + foreach ( (array) $elements as $element ) {
85 + if ( empty( $element->classes ) || ! empty( $has_children[ intval( $element->ID ) ] ) ) {
86 + continue;
87 + }
88 +
89 + $element->classes = array_diff( (array) $element->classes, array( 'menu-item-has-children' ) );
90 + }
91 +
92 + return parent::walk( $elements, $max_depth, ...$args );
93 + }
94 +
95 + /**
57 96 * Function to create element for menu items.
58 - *
97 + *
59 98 * @access public
60 99 * @version 4.0.0
61 - *
100 + *
62 101 * @param string/HTML $output
63 102 * @param object $item
64 103 * @param int $depth
65 104 * @param array $args
@@ -64,12 +103,11 @@
64 103 * @param int $depth
65 104 * @param array $args
66 105 * @param int $id
67 106 */
68 - public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0) {
107 + public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
108 + $this->set_current_item( $item );
69 109
70 - $this->set_current_item($item);
71 -
72 110 $classes = array();
73 111 if ( ! empty( $item->classes ) ) {
74 112 $classes = (array) $item->classes;
75 113 }
@@ -76,11 +114,11 @@
76 114
77 115 $rmp_menu_classes = $classes;
78 116
79 117 /** Add rmp menu classes as per item */
80 - foreach( $classes as $class ) {
81 - switch($class) {
82 - case 'menu-item' :
118 + foreach ( $classes as $class ) {
119 + switch ( $class ) {
120 + case 'menu-item':
83 121 $rmp_menu_classes[] = 'rmp-menu-item';
84 122 break;
85 123 case 'current-menu-item':
86 124 $rmp_menu_classes[] = 'rmp-menu-current-item';
@@ -87,9 +125,9 @@
87 125 break;
88 126 case 'menu-item-has-children':
89 127 $rmp_menu_classes[] = 'rmp-menu-item-has-children';
90 128 break;
91 - case 'current-menu-parent':
129 + case 'current-menu-parent':
92 130 $rmp_menu_classes[] = 'rmp-menu-item-current-parent';
93 131 break;
94 132 case 'current-menu-ancestor':
95 133 $rmp_menu_classes[] = 'rmp-menu-item-current-ancestor';
@@ -97,9 +135,9 @@
97 135 }
98 136 }
99 137
100 138 // Add top/sub level class as per item.
101 - if ( $item->menu_item_parent == 0 ) {
139 + if ( 0 === intval( $item->menu_item_parent ) ) {
102 140 $rmp_menu_classes[] = 'rmp-menu-top-level-item';
103 141 $this->root_item_id = $item->ID;
104 142 } else {
105 143 $rmp_menu_classes[] = 'rmp-menu-sub-level-item';
@@ -106,11 +144,11 @@
106 144 }
107 145
108 146 /* Clear child class if we are at the final depth level */
109 147 if ( isset( $rmp_menu_classes ) ) {
110 - $has_child = array_search( 'rmp-menu-item-has-children', $rmp_menu_classes );
111 - if ( ( $depth + 1 ) == $this->options['menu_depth'] && ( $has_child ) !== false) {
112 - unset( $rmp_menu_classes[$has_child] );
148 + $has_child = array_search( 'rmp-menu-item-has-children', $rmp_menu_classes, true );
149 + if ( ( intval( $depth ) + 1 ) === intval( $this->options['menu_depth'] ) && false !== $has_child ) {
150 + unset( $rmp_menu_classes[ $has_child ] );
113 151 }
114 152 }
115 153
116 154 $class_names = join( ' ', array_unique( $rmp_menu_classes ) );
@@ -115,36 +153,37 @@
115 153
116 154 $class_names = join( ' ', array_unique( $rmp_menu_classes ) );
117 155
118 156 /** Prepare classes for menu item. */
119 - if ( ! empty( $class_names ) ) {
120 - $class_names = sprintf( 'class="%s"', esc_attr( $class_names ) );
157 + if ( ! empty( $class_names ) ) {
158 + $class_names = sprintf( 'class="%s"', esc_attr( $class_names ) );
121 159 } else {
122 160 $class_names = '';
123 161 }
124 162
163 + $class_names = apply_filters( 'rmp_nav_item_class', $class_names, $item );
164 +
125 165 // Start menu item and set classes & ID.
126 - $output .= sprintf(
127 - '<li id="rmp-menu-item-%s" %s role="none">',
128 - esc_attr( $item->ID ) ,
166 + $output .= sprintf(
167 + '<li id="rmp-menu-item-%s" %s>',
168 + esc_attr( $item->ID ),
129 169 $class_names
130 170 );
131 171
132 172 // Set attributes on menu item link.
133 - $atts = array();
134 - $atts['title'] = ! empty( $item->attr_title ) ? $item->attr_title : '';
135 - $atts['target'] = ! empty( $item->target ) ? $item->target : '';
136 - $atts['rel'] = ! empty( $item->xfn ) ? $item->xfn : '';
137 - $atts['href'] = ! empty( $item->url ) ? $item->url : '';
138 - $atts['class'] = 'rmp-menu-item-link';
139 - $atts['role'] = 'menuitem';
140 - $atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args, $depth );
173 + $atts = array();
174 + $atts['title'] = ! empty( $item->attr_title ) ? $item->attr_title : '';
175 + $atts['target'] = ! empty( $item->target ) ? $item->target : '';
176 + $atts['rel'] = ! empty( $item->xfn ) ? $item->xfn : '';
177 + $atts['href'] = ! empty( $item->url ) ? $item->url : '';
178 + $atts['class'] = 'rmp-menu-item-link';
179 + $atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args, $depth );
141 180
142 181 $attributes = '';
143 - foreach( $atts as $key => $value ) {
182 + foreach ( $atts as $key => $value ) {
144 183 if ( ! empty( $value ) ) {
145 - $value = ('href' === $key ) ? esc_url( $value ) : esc_attr( $value );
146 - $attributes .= sprintf( ' %s = "%s" ' , $key , $value );
184 + $value = ( 'href' === $key ) ? esc_url( $value ) : esc_attr( $value );
185 + $attributes .= sprintf( ' %s="%s" ', $key, $value );
147 186 }
148 187 }
149 188
150 189 $title = apply_filters( 'the_title', $item->title, $item->ID );
@@ -149,48 +188,69 @@
149 188
150 189 $title = apply_filters( 'the_title', $item->title, $item->ID );
151 190 $title = apply_filters( 'rmp_menu_item_title', $title, $item, $args, $depth );
152 191
153 - // Activate the required menu item by default.
192 + /*
193 + * Build the submenu toggle. It is a real <button> that sits beside the link rather
194 + * than a <div> inside it: a control nested in an anchor is neither valid HTML nor
195 + * reachable by keyboard, so the submenu could only ever be opened with a mouse.
196 + */
154 197 $sub_menu_arrow = '';
155 - if ( in_array( 'rmp-menu-item-has-children', $rmp_menu_classes ) ) {
198 + $has_children = in_array( 'rmp-menu-item-has-children', $rmp_menu_classes, true );
156 199
157 - $inactive_arrow = sprintf(
158 - '<div class="rmp-menu-subarrow">%s</div>',
159 - $this->get_inactive_arrow()
160 - );
200 + /* No toggle if we are at the final depth level - there is nothing left to reveal. */
201 + if ( intval( $depth ) + 1 === intval( $this->options['menu_depth'] ) ) {
202 + $has_children = false;
203 + }
161 204
162 - $active_arrow = sprintf(
163 - '<div class="rmp-menu-subarrow rmp-menu-subarrow-active">%s</div>',
164 - $this->get_active_arrow()
165 - );
205 + if ( $has_children ) {
206 + $is_expanded = ( 'on' === $this->options['auto_expand_all_submenus'] )
207 + || (
208 + 'on' === $this->options['auto_expand_current_submenus'] &&
209 + (
210 + in_array( 'rmp-menu-item-current-parent', $rmp_menu_classes, true ) ||
211 + in_array( 'rmp-menu-item-current-ancestor', $rmp_menu_classes, true )
212 + )
213 + );
166 214
167 - if( 'on' == $this->options['auto_expand_all_submenus'] ) {
168 - $sub_menu_arrow = $active_arrow;
169 - } elseif (
170 - $this->options['auto_expand_current_submenus'] == 'on' &&
171 - ( in_array( 'rmp-menu-item-current-parent', $rmp_menu_classes ) ||
172 - in_array('rmp-menu-item-current-ancestor', $rmp_menu_classes ) ) ) {
173 - $sub_menu_arrow = $active_arrow;
174 - } else {
175 - $sub_menu_arrow = $inactive_arrow;
215 + $arrow_classes = 'rmp-menu-subarrow';
216 + if ( $is_expanded ) {
217 + $arrow_classes .= ' rmp-menu-subarrow-active';
176 218 }
177 - }
178 219
179 - /* Clear Arrow if we are at the final depth level */
180 - if ( $depth + 1 == $this->options['menu_depth'] ) {
181 - $sub_menu_arrow = '';
220 + /* translators: %s: Title of the menu item the submenu belongs to. */
221 + $toggle_label = sprintf( __( 'Toggle submenu of %s', 'responsive-menu' ), wp_strip_all_tags( $title ) );
222 +
223 + /**
224 + * Filters the accessible name of a submenu toggle button.
225 + *
226 + * @since 4.7.4
227 + *
228 + * @param string $toggle_label Accessible name announced by screen readers.
229 + * @param object $item Menu item object.
230 + * @param int $depth Depth of the menu item.
231 + */
232 + $toggle_label = apply_filters( 'rmp_submenu_toggle_label', $toggle_label, $item, $depth );
233 +
234 + $sub_menu_arrow = sprintf(
235 + '<button type="button" class="%1$s" aria-expanded="%2$s" aria-controls="rmp-submenu-%3$s" aria-label="%4$s">%5$s</button>',
236 + esc_attr( $arrow_classes ),
237 + $is_expanded ? 'true' : 'false',
238 + esc_attr( $item->ID ),
239 + esc_attr( $toggle_label ),
240 + rm_sanitize_html_tags( $is_expanded ? $this->get_active_arrow() : $this->get_inactive_arrow() )
241 + );
182 242 }
183 243
184 244 $item_output = '';
185 245 $item_output .= sprintf( '<a %s >', $attributes );
186 246 $item_output .= $title;
247 + $item_output .= '</a>';
187 248 $item_output .= $sub_menu_arrow;
188 - $item_output .= '</a>';
189 249
190 250 // If description is enable then add it below of menu item.
191 - if ( ! empty( $item->description ) && $this->options['submenu_descriptions_on'] == 'on' ) {
192 - $item_output .= sprintf( '<p class="rmp-menu-item-description"> %s </p>', esc_html( $item->description ) );
251 + if ( ! empty( $item->description ) && 'on' === $this->options['submenu_descriptions_on'] ) {
252 + $item_output .= sprintf( '<p class="rmp-menu-item-description"> %s </p>', esc_html( $item->description ) );
193 253 }
194 254
195 255 // Theme support for twenty twenty one.
196 256 if ( function_exists( 'twenty_twenty_one_add_sub_menu_toggle' ) ) {
@@ -198,18 +258,17 @@
198 258 remove_filter( 'walker_nav_menu_start_el', 'twenty_twenty_one_nav_menu_social_icons', 10 );
199 259 }
200 260
201 261 /* End Add Desktop Menu Widgets to Sub Items */
202 - $output .= apply_filters('walker_nav_menu_start_el', $item_output, $item, $depth, $args );
203 -
262 + $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
204 263 }
205 264
206 265 /**
207 266 * Function to build the sub-menu items.
208 - *
267 + *
209 268 * @since 4.0.0
210 - * @access public
211 - *
269 + * @access public
270 + *
212 271 * @param string|HTML $output
213 272 * @param int $depth
214 273 * @param array $args
215 274 */
@@ -214,24 +273,28 @@
214 273 * @param array $args
215 274 */
216 275 public function start_lvl( &$output, $depth = 0, $args = array() ) {
217 276
218 - // Add sub-menu item wrap.
219 - $output .= sprintf( '<ul aria-label="%s"
220 - role="menu" data-depth="%s"
221 - class="rmp-submenu rmp-submenu-depth-%s">',
222 - esc_attr( $this->current_item->title),
223 - ( $depth + 2 ),
224 - ($depth + 1) . $this->get_submenu_class_open_or_not()
277 + /*
278 + * Add sub-menu item wrap. The id is what the parent item's toggle button points at
279 + * with aria-controls, and role="menu" is deliberately absent: this is site
280 + * navigation, not an application menu with roving-tabindex semantics.
281 + */
282 + $output .= sprintf(
283 + '<ul id="rmp-submenu-%1$s" aria-label="%2$s" data-depth="%3$s" class="rmp-submenu rmp-submenu-depth-%4$s">',
284 + esc_attr( $this->current_item->ID ),
285 + esc_attr( $this->current_item->title ),
286 + ( $depth + 2 ),
287 + ( $depth + 1 ) . $this->get_submenu_class_open_or_not()
225 288 );
226 289 }
227 290
228 291 /**
229 292 * Function to close the menu item.
230 - *
293 + *
231 294 * @access public
232 295 * @version 4.0.0
233 - *
296 + *
234 297 * @param string/HTML $output
235 298 * @param object $item
236 299 * @param int $depth
237 300 * @param array $args
@@ -236,37 +299,38 @@
236 299 * @param int $depth
237 300 * @param array $args
238 301 */
239 302 public function end_el( &$output, $item, $depth = 0, $args = array() ) {
240 - $output .= "</li>";
303 + $output .= '</li>';
241 304 }
242 305
243 306 /**
244 307 * Function to close the sub-menu items.
245 - *
308 + *
246 309 * @since 4.0.0
247 - * @access public
248 - *
310 + * @access public
311 + *
249 312 * @param string|HTML $output
250 313 * @param int $depth
251 314 * @param array $args
252 315 */
253 316 public function end_lvl( &$output, $depth = 0, $args = array() ) {
254 - $output .= "</ul>";
317 + $output .= '</ul>';
255 318 }
256 319
257 320 /**
258 321 * Function get the active item toggle icon.
259 - *
322 + *
260 323 * @return HTML
261 324 */
262 325 public function get_active_arrow() {
263 326 if ( ! empty( $this->options['active_arrow_font_icon'] ) ) {
264 327 return $this->options['active_arrow_font_icon'];
265 - } elseif( ! empty( $this->options['active_arrow_image'] ) ) {
266 - return sprintf( '<img alt="%s" src="%s" />',
267 - rmp_image_alt_by_url( $this->options['active_arrow_image']),
268 - esc_url($this->options['active_arrow_image'])
328 + } elseif ( ! empty( $this->options['active_arrow_image'] ) ) {
329 + return sprintf(
330 + '<img alt="%s" src="%s" />',
331 + rmp_image_alt_by_url( $this->options['active_arrow_image'] ),
332 + esc_url( $this->options['active_arrow_image'] )
269 333 );
270 334 } else {
271 335 return $this->options['active_arrow_shape'];
272 336 }
@@ -273,19 +337,19 @@
273 337 }
274 338
275 339 /**
276 340 * Function get the inactive item toggle icon.
277 - *
341 + *
278 342 * @return HTML
279 343 */
280 344 public function get_inactive_arrow() {
281 -
282 345 if ( ! empty( $this->options['inactive_arrow_font_icon'] ) ) {
283 346 return $this->options['inactive_arrow_font_icon'];
284 - } elseif( ! empty( $this->options['inactive_arrow_image'] ) ) {
285 - return sprintf( '<img alt="%s" src="%s" />',
286 - rmp_image_alt_by_url( $this->options['inactive_arrow_image']),
287 - esc_url($this->options['inactive_arrow_image'])
347 + } elseif ( ! empty( $this->options['inactive_arrow_image'] ) ) {
348 + return sprintf(
349 + '<img alt="%s" src="%s" />',
350 + rmp_image_alt_by_url( $this->options['inactive_arrow_image'] ),
351 + esc_url( $this->options['inactive_arrow_image'] )
288 352 );
289 353 } else {
290 354 return $this->options['inactive_arrow_shape'];
291 355 }
@@ -292,9 +356,9 @@
292 356 }
293 357
294 358 /**
295 359 * Function to set the current item object.
296 - *
360 + *
297 361 * @param object $item Menu item object.
298 362 */
299 363 public function set_current_item( $item ) {
300 364 $this->current_item = $item;
@@ -301,9 +365,9 @@
301 365 }
302 366
303 367 /**
304 368 * Function to return the current item object.
305 - *
369 + *
306 370 * @return object $item Menu item object.
307 371 */
308 372 public function get_current_item() {
309 373 return $this->current_item;
@@ -310,9 +374,9 @@
310 374 }
311 375
312 376 /**
313 377 * Check submenu need to open or not.
314 - *
378 + *
315 379 * @return boolean
316 380 */
317 381 public function get_submenu_class_open_or_not() {
318 382 return $this->expand_all_submenu_options_is_on() || $this->expand_current_submenu_on_and_item_is_parent() ? ' rmp-submenu-open' : '';
@@ -319,21 +383,21 @@
319 383 }
320 384
321 385 /**
322 386 * Check all submenu need to open or not.
323 - *
387 + *
324 388 * @return boolean
325 389 */
326 390 public function expand_all_submenu_options_is_on() {
327 - return $this->options['auto_expand_all_submenus'] == 'on';
391 + return 'on' === $this->options['auto_expand_all_submenus'];
328 392 }
329 393
330 394 /**
331 395 * Check current submenu need to open or not.
332 - *
396 + *
333 397 * @return boolean
334 398 */
335 399 public function expand_current_submenu_on_and_item_is_parent() {
336 - return ($this->options['auto_expand_current_submenus'] == 'on')
337 - && ($this->get_current_item()->current_item_ancestor || $this->get_current_item()->current_item_parent);
400 + return ( 'on' === $this->options['auto_expand_current_submenus'] )
401 + && ( $this->get_current_item()->current_item_ancestor || $this->get_current_item()->current_item_parent );
338 402 }
339 403 }