at path:ROOT / wp-admin / js / nav-menu.js
run:R W Run
DIR
2026-03-11 16:18:51
R W Run
2.86 KB
2026-03-11 16:18:51
R W Run
758 By
2026-03-11 16:18:51
R W Run
6.24 KB
2026-03-11 16:18:51
R W Run
2.95 KB
2026-03-11 16:18:51
R W Run
5.66 KB
2026-03-11 16:18:51
R W Run
2.04 KB
2026-03-11 16:18:51
R W Run
11.32 KB
2026-03-11 16:18:51
R W Run
3.01 KB
2026-03-11 16:18:51
R W Run
9.54 KB
2026-03-11 16:18:51
R W Run
3.4 KB
2026-03-11 16:18:51
R W Run
2.85 KB
2026-03-11 16:18:51
R W Run
1.28 KB
2026-03-11 16:18:51
R W Run
61.15 KB
2026-03-11 16:18:51
R W Run
23.12 KB
2026-03-11 16:18:51
R W Run
3.35 KB
2026-03-11 16:18:51
R W Run
1.18 KB
2026-03-11 16:18:51
R W Run
1.98 KB
2026-03-11 16:18:51
R W Run
288.41 KB
2026-03-11 16:18:51
R W Run
109.69 KB
2026-03-11 16:18:51
R W Run
111.46 KB
2026-03-11 16:18:51
R W Run
47.14 KB
2026-03-11 16:18:51
R W Run
70.05 KB
2026-03-11 16:18:51
R W Run
27.41 KB
2026-03-11 16:18:51
R W Run
27.02 KB
2026-03-11 16:18:51
R W Run
8.65 KB
2026-03-11 16:18:51
R W Run
37.12 KB
2026-03-11 16:18:51
R W Run
15.13 KB
2026-03-11 16:18:51
R W Run
41.61 KB
2026-03-11 16:18:51
R W Run
13.14 KB
2026-03-11 16:18:51
R W Run
44 KB
2026-03-11 16:18:51
R W Run
12.78 KB
2026-03-11 16:18:51
R W Run
7.67 KB
2026-03-11 16:18:51
R W Run
5.41 KB
2026-03-11 16:18:51
R W Run
3.65 KB
2026-03-11 16:18:51
R W Run
39.98 KB
2026-03-11 16:18:51
R W Run
15.15 KB
2026-03-11 16:18:51
R W Run
20.17 KB
2026-03-11 16:18:51
R W Run
9.41 KB
2026-03-11 16:18:51
R W Run
7.61 KB
2026-03-11 16:18:51
R W Run
2.93 KB
2026-03-11 16:18:51
R W Run
23.09 KB
2026-03-11 16:18:51
R W Run
890 By
2026-03-11 16:18:51
R W Run
423 By
2026-03-11 16:18:51
R W Run
3.89 KB
2026-03-11 16:18:51
R W Run
1.7 KB
2026-03-11 16:18:51
R W Run
1.27 KB
2026-03-11 16:18:51
R W Run
611 By
2026-03-11 16:18:51
R W Run
3.38 KB
2026-03-11 16:18:51
R W Run
1.13 KB
2026-03-11 16:18:51
R W Run
6.61 KB
2026-03-11 16:18:51
R W Run
2.38 KB
2026-03-11 16:18:51
R W Run
61.15 KB
2026-03-11 16:18:51
R W Run
30.06 KB
2026-03-11 16:18:51
R W Run
4.14 KB
2026-03-11 16:18:51
R W Run
1.1 KB
2026-03-11 16:18:51
R W Run
1.31 KB
2026-03-11 16:18:51
R W Run
847 By
2026-03-11 16:18:51
R W Run
6.92 KB
2026-03-11 16:18:51
R W Run
2.35 KB
2026-03-11 16:18:51
R W Run
38.68 KB
2026-03-11 16:18:51
R W Run
18.4 KB
2026-03-11 16:18:51
R W Run
18.49 KB
2026-03-11 16:18:51
R W Run
6.6 KB
2026-03-11 16:18:51
R W Run
10.67 KB
2026-03-11 16:18:51
R W Run
5.03 KB
2026-03-11 16:18:51
R W Run
33.92 KB
2026-03-11 16:18:51
R W Run
17.97 KB
2026-03-11 16:18:51
R W Run
876 By
2026-03-11 16:18:51
R W Run
620 By
2026-03-11 16:18:51
R W Run
13.15 KB
2026-03-11 16:18:51
R W Run
6.13 KB
2026-03-11 16:18:51
R W Run
6.1 KB
2026-03-11 16:18:51
R W Run
2.2 KB
2026-03-11 16:18:51
R W Run
3.2 KB
2026-03-11 16:18:51
R W Run
1.53 KB
2026-03-11 16:18:51
R W Run
10.88 KB
2026-03-11 16:18:51
R W Run
3 KB
2026-03-11 16:18:51
R W Run
5.64 KB
2026-03-11 16:18:51
R W Run
2.22 KB
2026-03-11 16:18:51
R W Run
5.96 KB
2026-03-11 16:18:51
R W Run
2.41 KB
2026-03-11 16:18:51
R W Run
24.77 KB
2026-03-11 16:18:51
R W Run
11.43 KB
2026-03-11 16:18:51
R W Run
54.94 KB
2026-03-11 16:18:51
R W Run
26.51 KB
2026-03-11 16:18:51
R W Run
109.37 KB
2026-03-11 16:18:51
R W Run
47.31 KB
2026-03-11 16:18:51
R W Run
17.91 KB
2026-03-11 16:18:51
R W Run
7.81 KB
2026-03-11 16:18:51
R W Run
2.25 KB
2026-03-11 16:18:51
R W Run
676 By
2026-03-11 16:18:51
R W Run
22.56 KB
2026-03-11 16:18:51
R W Run
12.31 KB
2026-03-11 16:18:51
R W Run
7.52 KB
2026-03-11 16:18:51
R W Run
1.49 KB
2026-03-11 16:18:51
R W Run
740 By
2026-03-11 16:18:51
R W Run
458 By
2026-03-11 16:18:51
R W Run
error_log
📄nav-menu.js
1/**
2 * WordPress Administration Navigation Menu
3 * Interface JS functions
4 *
5 * @version 2.0.0
6 *
7 * @package WordPress
8 * @subpackage Administration
9 * @output wp-admin/js/nav-menu.js
10 */
11
12/* global menus, postboxes, columns, isRtl, ajaxurl, wpNavMenu */
13
14(function($) {
15
16 var api;
17
18 /**
19 * Contains all the functions to handle WordPress navigation menus administration.
20 *
21 * @namespace wpNavMenu
22 */
23 api = window.wpNavMenu = {
24
25 options : {
26 menuItemDepthPerLevel : 30, // Do not use directly. Use depthToPx and pxToDepth instead.
27 globalMaxDepth: 11,
28 sortableItems: '> *',
29 targetTolerance: 0
30 },
31
32 menuList : undefined, // Set in init.
33 targetList : undefined, // Set in init.
34 menusChanged : false,
35 isRTL: !! ( 'undefined' != typeof isRtl && isRtl ),
36 negateIfRTL: ( 'undefined' != typeof isRtl && isRtl ) ? -1 : 1,
37 lastSearch: '',
38
39 // Functions that run on init.
40 init : function() {
41 api.menuList = $('#menu-to-edit');
42 api.targetList = api.menuList;
43
44 this.jQueryExtensions();
45
46 this.attachMenuEditListeners();
47
48 this.attachBulkSelectButtonListeners();
49 this.attachMenuCheckBoxListeners();
50 this.attachMenuItemDeleteButton();
51 this.attachPendingMenuItemsListForDeletion();
52
53 this.attachQuickSearchListeners();
54 this.attachThemeLocationsListeners();
55 this.attachMenuSaveSubmitListeners();
56
57 this.attachTabsPanelListeners();
58
59 this.attachUnsavedChangesListener();
60
61 if ( api.menuList.length )
62 this.initSortables();
63
64 if ( menus.oneThemeLocationNoMenus )
65 $( '#posttype-page' ).addSelectedToMenu( api.addMenuItemToBottom );
66
67 this.initManageLocations();
68
69 this.initAccessibility();
70
71 this.initToggles();
72
73 this.initPreviewing();
74 },
75
76 jQueryExtensions : function() {
77 // jQuery extensions.
78 $.fn.extend({
79 menuItemDepth : function() {
80 var margin = api.isRTL ? this.eq(0).css('margin-right') : this.eq(0).css('margin-left');
81 return api.pxToDepth( margin && -1 != margin.indexOf('px') ? margin.slice(0, -2) : 0 );
82 },
83 updateDepthClass : function(current, prev) {
84 return this.each(function(){
85 var t = $(this);
86 prev = prev || t.menuItemDepth();
87 $(this).removeClass('menu-item-depth-'+ prev )
88 .addClass('menu-item-depth-'+ current );
89 });
90 },
91 shiftDepthClass : function(change) {
92 return this.each(function(){
93 var t = $(this),
94 depth = t.menuItemDepth(),
95 newDepth = depth + change;
96
97 t.removeClass( 'menu-item-depth-'+ depth )
98 .addClass( 'menu-item-depth-'+ ( newDepth ) );
99
100 if ( 0 === newDepth ) {
101 t.find( '.is-submenu' ).hide();
102 }
103 });
104 },
105 childMenuItems : function() {
106 var result = $();
107 this.each(function(){
108 var t = $(this), depth = t.menuItemDepth(), next = t.next( '.menu-item' );
109 while( next.length && next.menuItemDepth() > depth ) {
110 result = result.add( next );
111 next = next.next( '.menu-item' );
112 }
113 });
114 return result;
115 },
116 shiftHorizontally : function( dir ) {
117 return this.each(function(){
118 var t = $(this),
119 depth = t.menuItemDepth(),
120 newDepth = depth + dir;
121
122 // Change .menu-item-depth-n class.
123 t.moveHorizontally( newDepth, depth );
124 });
125 },
126 moveHorizontally : function( newDepth, depth ) {
127 return this.each(function(){
128 var t = $(this),
129 children = t.childMenuItems(),
130 diff = newDepth - depth,
131 subItemText = t.find('.is-submenu');
132
133 // Change .menu-item-depth-n class.
134 t.updateDepthClass( newDepth, depth ).updateParentMenuItemDBId();
135
136 // If it has children, move those too.
137 if ( children ) {
138 children.each(function() {
139 var t = $(this),
140 thisDepth = t.menuItemDepth(),
141 newDepth = thisDepth + diff;
142 t.updateDepthClass(newDepth, thisDepth).updateParentMenuItemDBId();
143 });
144 }
145
146 // Show "Sub item" helper text.
147 if (0 === newDepth)
148 subItemText.hide();
149 else
150 subItemText.show();
151 });
152 },
153 updateParentMenuItemDBId : function() {
154 return this.each(function(){
155 var item = $(this),
156 input = item.find( '.menu-item-data-parent-id' ),
157 depth = parseInt( item.menuItemDepth(), 10 ),
158 parentDepth = depth - 1,
159 parent = item.prevAll( '.menu-item-depth-' + parentDepth ).first();
160
161 if ( 0 === depth ) { // Item is on the top level, has no parent.
162 input.val(0);
163 } else { // Find the parent item, and retrieve its object id.
164 input.val( parent.find( '.menu-item-data-db-id' ).val() );
165 }
166 });
167 },
168 hideAdvancedMenuItemFields : function() {
169 return this.each(function(){
170 var that = $(this);
171 $('.hide-column-tog').not(':checked').each(function(){
172 that.find('.field-' + $(this).val() ).addClass('hidden-field');
173 });
174 });
175 },
176 /**
177 * Adds selected menu items to the menu.
178 *
179 * @ignore
180 *
181 * @param jQuery metabox The metabox jQuery object.
182 */
183 addSelectedToMenu : function(processMethod) {
184 if ( 0 === $('#menu-to-edit').length ) {
185 return false;
186 }
187
188 return this.each(function() {
189 var t = $(this), menuItems = {},
190 checkboxes = ( menus.oneThemeLocationNoMenus && 0 === t.find( '.tabs-panel-active .categorychecklist li input:checked' ).length ) ? t.find( '#page-all li input[type="checkbox"]' ) : t.find( '.tabs-panel-active .categorychecklist li input:checked' ),
191 re = /menu-item\[([^\]]*)/;
192
193 processMethod = processMethod || api.addMenuItemToBottom;
194
195 // If no items are checked, bail.
196 if ( !checkboxes.length )
197 return false;
198
199 // Show the Ajax spinner.
200 t.find( '.button-controls .spinner' ).addClass( 'is-active' );
201
202 // Retrieve menu item data.
203 $(checkboxes).each(function(){
204 var t = $(this),
205 listItemDBIDMatch = re.exec( t.attr('name') ),
206 listItemDBID = 'undefined' == typeof listItemDBIDMatch[1] ? 0 : parseInt(listItemDBIDMatch[1], 10);
207
208 if ( this.className && -1 != this.className.indexOf('add-to-top') )
209 processMethod = api.addMenuItemToTop;
210 menuItems[listItemDBID] = t.closest('li').getItemData( 'add-menu-item', listItemDBID );
211 });
212
213 // Add the items.
214 api.addItemToMenu(menuItems, processMethod, function(){
215 // Deselect the items and hide the Ajax spinner.
216 checkboxes.prop( 'checked', false );
217 t.find( '.button-controls .select-all' ).prop( 'checked', false );
218 t.find( '.button-controls .spinner' ).removeClass( 'is-active' );
219 t.updateParentDropdown();
220 t.updateOrderDropdown();
221 });
222 });
223 },
224 getItemData : function( itemType, id ) {
225 itemType = itemType || 'menu-item';
226
227 var itemData = {}, i,
228 fields = [
229 'menu-item-db-id',
230 'menu-item-object-id',
231 'menu-item-object',
232 'menu-item-parent-id',
233 'menu-item-position',
234 'menu-item-type',
235 'menu-item-title',
236 'menu-item-url',
237 'menu-item-description',
238 'menu-item-attr-title',
239 'menu-item-target',
240 'menu-item-classes',
241 'menu-item-xfn'
242 ];
243
244 if( !id && itemType == 'menu-item' ) {
245 id = this.find('.menu-item-data-db-id').val();
246 }
247
248 if( !id ) return itemData;
249
250 this.find('input').each(function() {
251 var field;
252 i = fields.length;
253 while ( i-- ) {
254 if( itemType == 'menu-item' )
255 field = fields[i] + '[' + id + ']';
256 else if( itemType == 'add-menu-item' )
257 field = 'menu-item[' + id + '][' + fields[i] + ']';
258
259 if (
260 this.name &&
261 field == this.name
262 ) {
263 itemData[fields[i]] = this.value;
264 }
265 }
266 });
267
268 return itemData;
269 },
270 setItemData : function( itemData, itemType, id ) { // Can take a type, such as 'menu-item', or an id.
271 itemType = itemType || 'menu-item';
272
273 if( !id && itemType == 'menu-item' ) {
274 id = $('.menu-item-data-db-id', this).val();
275 }
276
277 if( !id ) return this;
278
279 this.find('input').each(function() {
280 var t = $(this), field;
281 $.each( itemData, function( attr, val ) {
282 if( itemType == 'menu-item' )
283 field = attr + '[' + id + ']';
284 else if( itemType == 'add-menu-item' )
285 field = 'menu-item[' + id + '][' + attr + ']';
286
287 if ( field == t.attr('name') ) {
288 t.val( val );
289 }
290 });
291 });
292 return this;
293 },
294 updateParentDropdown : function() {
295 return this.each(function(){
296 var menuItems = $( '#menu-to-edit li' ),
297 parentDropdowns = $( '.edit-menu-item-parent' );
298
299 $.each( parentDropdowns, function() {
300 var parentDropdown = $( this ),
301 currentItemID = parseInt( parentDropdown.closest( 'li.menu-item' ).find( '.menu-item-data-db-id' ).val() ),
302 currentParentID = parseInt( parentDropdown.closest( 'li.menu-item' ).find( '.menu-item-data-parent-id' ).val() ),
303 currentItem = parentDropdown.closest( 'li.menu-item' ),
304 currentMenuItemChild = currentItem.childMenuItems(),
305 excludeMenuItem = /** @type {number[]} */ [ currentItemID ];
306
307 parentDropdown.empty();
308
309 if ( currentMenuItemChild.length > 0 ) {
310 $.each( currentMenuItemChild, function(){
311 var childItem = $(this),
312 childID = parseInt( childItem.find( '.menu-item-data-db-id' ).val() );
313
314 excludeMenuItem.push( childID );
315 });
316 }
317
318 parentDropdown.append(
319 $( '<option>', {
320 value: '0',
321 selected: currentParentID === 0,
322 text: wp.i18n._x( 'No Parent', 'menu item without a parent in navigation menu' ),
323 } )
324 );
325
326 $.each( menuItems, function() {
327 var menuItem = $(this),
328 menuID = parseInt( menuItem.find( '.menu-item-data-db-id' ).val() ),
329 menuTitle = menuItem.find( '.edit-menu-item-title' ).val();
330
331 if ( ! excludeMenuItem.includes( menuID ) ) {
332 parentDropdown.append(
333 $( '<option>', {
334 value: menuID.toString(),
335 selected: currentParentID === menuID,
336 text: menuTitle,
337 } )
338 );
339 }
340 });
341 });
342
343 });
344 },
345 updateOrderDropdown : function() {
346 return this.each( function() {
347 var itemPosition,
348 orderDropdowns = $( '.edit-menu-item-order' );
349
350 $.each( orderDropdowns, function() {
351 var orderDropdown = $( this ),
352 menuItem = orderDropdown.closest( 'li.menu-item' ).first(),
353 depth = menuItem.menuItemDepth(),
354 isPrimaryMenuItem = ( 0 === depth );
355
356 orderDropdown.empty();
357
358 if ( isPrimaryMenuItem ) {
359 var primaryItems = $( '.menu-item-depth-0' ),
360 totalMenuItems = primaryItems.length;
361
362 itemPosition = primaryItems.index( menuItem ) + 1;
363
364 for ( let i = 1; i < totalMenuItems + 1; i++ ) {
365 var itemString = wp.i18n.sprintf(
366 /* translators: 1: The current menu item number, 2: The total number of menu items. */
367 wp.i18n._x( '%1$s of %2$s', 'part of a total number of menu items' ),
368 i,
369 totalMenuItems
370 );
371 orderDropdown.append(
372 $( '<option>', {
373 selected: i === itemPosition,
374 value: i.toString(),
375 text: itemString,
376 } )
377 );
378 }
379
380 } else {
381 var parentItem = menuItem.prevAll( '.menu-item-depth-' + parseInt( depth - 1, 10 ) ).first(),
382 parentItemId = parentItem.find( '.menu-item-data-db-id' ).val(),
383 subItems = $( '.menu-item .menu-item-data-parent-id[value="' + parentItemId + '"]' ),
384 totalSubMenuItems = subItems.length;
385
386 itemPosition = $( subItems.parents('.menu-item').get().reverse() ).index( menuItem ) + 1;
387
388 for ( let i = 1; i < totalSubMenuItems + 1; i++ ) {
389 var submenuString = wp.i18n.sprintf(
390 /* translators: 1: The current submenu item number, 2: The total number of submenu items. */
391 wp.i18n._x( '%1$s of %2$s', 'part of a total number of menu items' ),
392 i,
393 totalSubMenuItems
394 );
395 orderDropdown.append(
396 $( '<option>', {
397 selected: i === itemPosition,
398 value: i.toString(),
399 text: submenuString,
400 } )
401 );
402 }
403
404 }
405 });
406
407 });
408 }
409 });
410 },
411
412 countMenuItems : function( depth ) {
413 return $( '.menu-item-depth-' + depth ).length;
414 },
415
416 moveMenuItem : function( $this, dir ) {
417 var items, newItemPosition, newDepth,
418 menuItems = $( '#menu-to-edit li' ),
419 menuItemsCount = menuItems.length,
420 thisItem = $this.parents( 'li.menu-item' ),
421 thisItemChildren = thisItem.childMenuItems(),
422 thisItemData = thisItem.getItemData(),
423 thisItemDepth = parseInt( thisItem.menuItemDepth(), 10 ),
424 thisItemPosition = parseInt( thisItem.index(), 10 ),
425 nextItem = thisItem.next(),
426 nextItemChildren = nextItem.childMenuItems(),
427 nextItemDepth = parseInt( nextItem.menuItemDepth(), 10 ) + 1,
428 prevItem = thisItem.prev(),
429 prevItemDepth = parseInt( prevItem.menuItemDepth(), 10 ),
430 prevItemId = prevItem.getItemData()['menu-item-db-id'],
431 a11ySpeech = menus[ 'moved' + dir.charAt(0).toUpperCase() + dir.slice(1) ];
432
433 switch ( dir ) {
434 case 'up':
435 newItemPosition = thisItemPosition - 1;
436
437 // Already at top.
438 if ( 0 === thisItemPosition )
439 break;
440
441 // If a sub item is moved to top, shift it to 0 depth.
442 if ( 0 === newItemPosition && 0 !== thisItemDepth )
443 thisItem.moveHorizontally( 0, thisItemDepth );
444
445 // If prev item is sub item, shift to match depth.
446 if ( 0 !== prevItemDepth )
447 thisItem.moveHorizontally( prevItemDepth, thisItemDepth );
448
449 // Does this item have sub items?
450 if ( thisItemChildren ) {
451 items = thisItem.add( thisItemChildren );
452 // Move the entire block.
453 items.detach().insertBefore( menuItems.eq( newItemPosition ) ).updateParentMenuItemDBId();
454 } else {
455 thisItem.detach().insertBefore( menuItems.eq( newItemPosition ) ).updateParentMenuItemDBId();
456 }
457 break;
458 case 'down':
459 // Does this item have sub items?
460 if ( thisItemChildren ) {
461 items = thisItem.add( thisItemChildren ),
462 nextItem = menuItems.eq( items.length + thisItemPosition ),
463 nextItemChildren = 0 !== nextItem.childMenuItems().length;
464
465 if ( nextItemChildren ) {
466 newDepth = parseInt( nextItem.menuItemDepth(), 10 ) + 1;
467 thisItem.moveHorizontally( newDepth, thisItemDepth );
468 }
469
470 // Have we reached the bottom?
471 if ( menuItemsCount === thisItemPosition + items.length )
472 break;
473
474 items.detach().insertAfter( menuItems.eq( thisItemPosition + items.length ) ).updateParentMenuItemDBId();
475 } else {
476 // If next item has sub items, shift depth.
477 if ( 0 !== nextItemChildren.length )
478 thisItem.moveHorizontally( nextItemDepth, thisItemDepth );
479
480 // Have we reached the bottom?
481 if ( menuItemsCount === thisItemPosition + 1 )
482 break;
483 thisItem.detach().insertAfter( menuItems.eq( thisItemPosition + 1 ) ).updateParentMenuItemDBId();
484 }
485 break;
486 case 'top':
487 // Already at top.
488 if ( 0 === thisItemPosition )
489 break;
490 // Does this item have sub items?
491 if ( thisItemChildren ) {
492 items = thisItem.add( thisItemChildren );
493 // Move the entire block.
494 items.detach().insertBefore( menuItems.eq( 0 ) ).updateParentMenuItemDBId();
495 } else {
496 thisItem.detach().insertBefore( menuItems.eq( 0 ) ).updateParentMenuItemDBId();
497 }
498 break;
499 case 'left':
500 // As far left as possible.
501 if ( 0 === thisItemDepth )
502 break;
503 thisItem.shiftHorizontally( -1 );
504 break;
505 case 'right':
506 // Can't be sub item at top.
507 if ( 0 === thisItemPosition )
508 break;
509 // Already sub item of prevItem.
510 if ( thisItemData['menu-item-parent-id'] === prevItemId )
511 break;
512 thisItem.shiftHorizontally( 1 );
513 break;
514 }
515 $this.trigger( 'focus' );
516 api.registerChange();
517 api.refreshKeyboardAccessibility();
518 api.refreshAdvancedAccessibility();
519 thisItem.updateParentDropdown();
520 thisItem.updateOrderDropdown();
521
522 if ( a11ySpeech ) {
523 wp.a11y.speak( a11ySpeech );
524 }
525 },
526
527 initAccessibility : function() {
528 var menu = $( '#menu-to-edit' );
529
530 api.refreshKeyboardAccessibility();
531 api.refreshAdvancedAccessibility();
532
533 // Refresh the accessibility when the user comes close to the item in any way.
534 menu.on( 'mouseenter.refreshAccessibility focus.refreshAccessibility touchstart.refreshAccessibility' , '.menu-item' , function(){
535 api.refreshAdvancedAccessibilityOfItem( $( this ).find( 'a.item-edit' ) );
536 } );
537
538 // We have to update on click as well because we might hover first, change the item, and then click.
539 menu.on( 'click', 'a.item-edit', function() {
540 api.refreshAdvancedAccessibilityOfItem( $( this ) );
541 } );
542
543 // Links for moving items.
544 menu.on( 'click', '.menus-move', function () {
545 var $this = $( this ),
546 dir = $this.data( 'dir' );
547
548 if ( 'undefined' !== typeof dir ) {
549 api.moveMenuItem( $( this ).parents( 'li.menu-item' ).find( 'a.item-edit' ), dir );
550 }
551 });
552
553 // Set menu parents data for all menu items.
554 menu.updateParentDropdown();
555
556 // Set menu order data for all menu items.
557 menu.updateOrderDropdown();
558
559 // Update menu item parent when value is changed.
560 menu.on( 'change', '.edit-menu-item-parent', function() {
561 api.changeMenuParent( $( this ) );
562 });
563
564 // Update menu item order when value is changed.
565 menu.on( 'change', '.edit-menu-item-order', function() {
566 api.changeMenuOrder( $( this ) );
567 });
568 },
569
570 /**
571 * changeMenuParent( [parentDropdown] )
572 *
573 * @since 6.7.0
574 *
575 * @param {object} parentDropdown select field
576 */
577 changeMenuParent : function( parentDropdown ) {
578 var menuItemNewPosition,
579 menuItems = $( '#menu-to-edit li' ),
580 $this = $( parentDropdown ),
581 newParentID = $this.val(),
582 menuItem = $this.closest( 'li.menu-item' ).first(),
583 menuItemOldDepth = menuItem.menuItemDepth(),
584 menuItemChildren = menuItem.childMenuItems(),
585 menuItemNoChildren = parseInt( menuItem.childMenuItems().length, 10 ),
586 parentItem = $( '#menu-item-' + newParentID ),
587 parentItemDepth = parentItem.menuItemDepth(),
588 menuItemNewDepth = parseInt( parentItemDepth ) + 1;
589
590 if ( newParentID == 0 ) {
591 menuItemNewDepth = 0;
592 }
593
594 menuItem.find( '.menu-item-data-parent-id' ).val( newParentID );
595 menuItem.moveHorizontally( menuItemNewDepth, menuItemOldDepth );
596
597 if ( menuItemNoChildren > 0 ) {
598 menuItem = menuItem.add( menuItemChildren );
599 }
600 menuItem.detach();
601
602 menuItems = $( '#menu-to-edit li' );
603
604 var parentItemPosition = parseInt( parentItem.index(), 10 ),
605 parentItemNoChild = parseInt( parentItem.childMenuItems().length, 10 );
606
607 if ( parentItemNoChild > 0 ){
608 menuItemNewPosition = parentItemPosition + parentItemNoChild;
609 } else {
610 menuItemNewPosition = parentItemPosition;
611 }
612
613 if ( newParentID == 0 ) {
614 menuItemNewPosition = menuItems.length - 1;
615 }
616
617 menuItem.insertAfter( menuItems.eq( menuItemNewPosition ) ).updateParentMenuItemDBId().updateParentDropdown().updateOrderDropdown();
618
619 api.registerChange();
620 api.refreshKeyboardAccessibility();
621 api.refreshAdvancedAccessibility();
622 $this.trigger( 'focus' );
623 wp.a11y.speak( menus.parentUpdated, 'polite' );
624 },
625
626 /**
627 * changeMenuOrder( [OrderDropdown] )
628 *
629 * @since 6.7.0
630 *
631 * @param {object} orderDropdown select field
632 */
633 changeMenuOrder : function( orderDropdown ) {
634 var menuItems = $( '#menu-to-edit li' ),
635 $this = $( orderDropdown ),
636 newOrderID = parseInt( $this.val(), 10),
637 menuItem = $this.closest( 'li.menu-item' ).first(),
638 menuItemChildren = menuItem.childMenuItems(),
639 menuItemNoChildren = menuItemChildren.length,
640 menuItemCurrentPosition = parseInt( menuItem.index(), 10 ),
641 parentItemID = menuItem.find( '.menu-item-data-parent-id' ).val(),
642 subItems = $( '.menu-item .menu-item-data-parent-id[value="' + parentItemID + '"]' ),
643 currentItemAtPosition = $(subItems[newOrderID - 1]).closest( 'li.menu-item' );
644
645 if ( menuItemNoChildren > 0 ) {
646 menuItem = menuItem.add( menuItemChildren );
647 }
648
649 var currentItemNoChildren = currentItemAtPosition.childMenuItems().length,
650 currentItemPosition = parseInt( currentItemAtPosition.index(), 10 );
651
652 menuItems = $( '#menu-to-edit li' );
653
654 var menuItemNewPosition = currentItemPosition;
655
656 if(menuItemCurrentPosition > menuItemNewPosition){
657 menuItemNewPosition = currentItemPosition;
658 menuItem.detach().insertBefore( menuItems.eq( menuItemNewPosition ) ).updateOrderDropdown();
659 } else {
660 menuItemNewPosition = menuItemNewPosition + currentItemNoChildren;
661 menuItem.detach().insertAfter( menuItems.eq( menuItemNewPosition ) ).updateOrderDropdown();
662 }
663
664 api.registerChange();
665 api.refreshKeyboardAccessibility();
666 api.refreshAdvancedAccessibility();
667 $this.trigger( 'focus' );
668 wp.a11y.speak( menus.orderUpdated, 'polite' );
669 },
670
671 /**
672 * refreshAdvancedAccessibilityOfItem( [itemToRefresh] )
673 *
674 * Refreshes advanced accessibility buttons for one menu item.
675 * Shows or hides buttons based on the location of the menu item.
676 *
677 * @param {Object} itemToRefresh The menu item that might need its advanced accessibility buttons refreshed
678 */
679 refreshAdvancedAccessibilityOfItem : function( itemToRefresh ) {
680
681 // Only refresh accessibility when necessary.
682 if ( true !== $( itemToRefresh ).data( 'needs_accessibility_refresh' ) ) {
683 return;
684 }
685
686 var thisLink, thisLinkText, primaryItems, itemPosition, title,
687 parentItem, parentItemId, parentItemName, subItems, totalSubItems,
688 $this = $( itemToRefresh ),
689 menuItem = $this.closest( 'li.menu-item' ).first(),
690 depth = menuItem.menuItemDepth(),
691 isPrimaryMenuItem = ( 0 === depth ),
692 itemName = $this.closest( '.menu-item-handle' ).find( '.menu-item-title' ).text(),
693 menuItemType = $this.closest( '.menu-item-handle' ).find( '.item-controls' ).find( '.item-type' ).text(),
694 position = parseInt( menuItem.index(), 10 ),
695 prevItemDepth = ( isPrimaryMenuItem ) ? depth : parseInt( depth - 1, 10 ),
696 prevItemNameLeft = menuItem.prevAll('.menu-item-depth-' + prevItemDepth).first().find( '.menu-item-title' ).text(),
697 prevItemNameRight = menuItem.prevAll('.menu-item-depth-' + depth).first().find( '.menu-item-title' ).text(),
698 totalMenuItems = $('#menu-to-edit li').length,
699 hasSameDepthSibling = menuItem.nextAll( '.menu-item-depth-' + depth ).length;
700
701 menuItem.find( '.field-move' ).toggle( totalMenuItems > 1 );
702
703 // Where can they move this menu item?
704 if ( 0 !== position ) {
705 thisLink = menuItem.find( '.menus-move-up' );
706 thisLink.attr( 'aria-label', menus.moveUp ).css( 'display', 'inline' );
707 }
708
709 if ( 0 !== position && isPrimaryMenuItem ) {
710 thisLink = menuItem.find( '.menus-move-top' );
711 thisLink.attr( 'aria-label', menus.moveToTop ).css( 'display', 'inline' );
712 }
713
714 if ( position + 1 !== totalMenuItems && 0 !== position ) {
715 thisLink = menuItem.find( '.menus-move-down' );
716 thisLink.attr( 'aria-label', menus.moveDown ).css( 'display', 'inline' );
717 }
718
719 if ( 0 === position && 0 !== hasSameDepthSibling ) {
720 thisLink = menuItem.find( '.menus-move-down' );
721 thisLink.attr( 'aria-label', menus.moveDown ).css( 'display', 'inline' );
722 }
723
724 if ( ! isPrimaryMenuItem ) {
725 thisLink = menuItem.find( '.menus-move-left' ),
726 thisLinkText = menus.outFrom.replace( '%s', prevItemNameLeft );
727 thisLink.attr( 'aria-label', menus.moveOutFrom.replace( '%s', prevItemNameLeft ) ).text( thisLinkText ).css( 'display', 'inline' );
728 }
729
730 if ( 0 !== position ) {
731 if ( menuItem.find( '.menu-item-data-parent-id' ).val() !== menuItem.prev().find( '.menu-item-data-db-id' ).val() ) {
732 thisLink = menuItem.find( '.menus-move-right' ),
733 thisLinkText = menus.under.replace( '%s', prevItemNameRight );
734 thisLink.attr( 'aria-label', menus.moveUnder.replace( '%s', prevItemNameRight ) ).text( thisLinkText ).css( 'display', 'inline' );
735 }
736 }
737
738 if ( isPrimaryMenuItem ) {
739 primaryItems = $( '.menu-item-depth-0' ),
740 itemPosition = primaryItems.index( menuItem ) + 1,
741 totalMenuItems = primaryItems.length,
742 // String together help text for primary menu items.
743 title = menus.menuFocus.replace( '%1$s', itemName ).replace( '%2$s', menuItemType ).replace( '%3$d', itemPosition ).replace( '%4$d', totalMenuItems );
744 } else {
745 parentItem = menuItem.prevAll( '.menu-item-depth-' + parseInt( depth - 1, 10 ) ).first(),
746 parentItemId = parentItem.find( '.menu-item-data-db-id' ).val(),
747 parentItemName = parentItem.find( '.menu-item-title' ).text(),
748 subItems = $( '.menu-item .menu-item-data-parent-id[value="' + parentItemId + '"]' ),
749 totalSubItems = subItems.length,
750 itemPosition = $( subItems.parents('.menu-item').get().reverse() ).index( menuItem ) + 1;
751
752 // String together help text for sub menu items.
753 if ( depth < 2 ) {
754 title = menus.subMenuFocus.replace( '%1$s', itemName ).replace( '%2$s', menuItemType ).replace( '%3$d', itemPosition ).replace( '%4$d', totalSubItems ).replace( '%5$s', parentItemName );
755 } else {
756 title = menus.subMenuMoreDepthFocus.replace( '%1$s', itemName ).replace( '%2$s', menuItemType ).replace( '%3$d', itemPosition ).replace( '%4$d', totalSubItems ).replace( '%5$s', parentItemName ).replace( '%6$d', depth );
757 }
758 }
759
760 $this.attr( 'aria-label', title );
761
762 // Mark this item's accessibility as refreshed.
763 $this.data( 'needs_accessibility_refresh', false );
764 },
765
766 /**
767 * refreshAdvancedAccessibility
768 *
769 * Hides all advanced accessibility buttons and marks them for refreshing.
770 */
771 refreshAdvancedAccessibility : function() {
772
773 // Hide all the move buttons by default.
774 $( '.menu-item-settings .field-move .menus-move' ).hide();
775
776 // Mark all menu items as unprocessed.
777 $( 'a.item-edit' ).data( 'needs_accessibility_refresh', true );
778
779 // All open items have to be refreshed or they will show no links.
780 $( '.menu-item-edit-active a.item-edit' ).each( function() {
781 api.refreshAdvancedAccessibilityOfItem( this );
782 } );
783 },
784
785 refreshKeyboardAccessibility : function() {
786 $( 'a.item-edit' ).off( 'focus' ).on( 'focus', function(){
787 $(this).off( 'keydown' ).on( 'keydown', function(e){
788
789 var arrows,
790 $this = $( this ),
791 thisItem = $this.parents( 'li.menu-item' ),
792 thisItemData = thisItem.getItemData();
793
794 // Bail if it's not an arrow key.
795 if ( 37 != e.which && 38 != e.which && 39 != e.which && 40 != e.which )
796 return;
797
798 // Avoid multiple keydown events.
799 $this.off('keydown');
800
801 // Bail if there is only one menu item.
802 if ( 1 === $('#menu-to-edit li').length )
803 return;
804
805 // If RTL, swap left/right arrows.
806 arrows = { '38': 'up', '40': 'down', '37': 'left', '39': 'right' };
807 if ( $('body').hasClass('rtl') )
808 arrows = { '38' : 'up', '40' : 'down', '39' : 'left', '37' : 'right' };
809
810 switch ( arrows[e.which] ) {
811 case 'up':
812 api.moveMenuItem( $this, 'up' );
813 break;
814 case 'down':
815 api.moveMenuItem( $this, 'down' );
816 break;
817 case 'left':
818 api.moveMenuItem( $this, 'left' );
819 break;
820 case 'right':
821 api.moveMenuItem( $this, 'right' );
822 break;
823 }
824 // Put focus back on same menu item.
825 $( '#edit-' + thisItemData['menu-item-db-id'] ).trigger( 'focus' );
826 return false;
827 });
828 });
829 },
830
831 initPreviewing : function() {
832 // Update the item handle title when the navigation label is changed.
833 $( '#menu-to-edit' ).on( 'change input', '.edit-menu-item-title', function(e) {
834 var input = $( e.currentTarget ), title, titleEl;
835 title = input.val();
836 titleEl = input.closest( '.menu-item' ).find( '.menu-item-title' );
837 // Don't update to empty title.
838 if ( title ) {
839 titleEl.text( title ).removeClass( 'no-title' );
840 } else {
841 titleEl.text( wp.i18n._x( '(no label)', 'missing menu item navigation label' ) ).addClass( 'no-title' );
842 }
843 } );
844 },
845
846 initToggles : function() {
847 // Init postboxes.
848 postboxes.add_postbox_toggles('nav-menus');
849
850 // Adjust columns functions for menus UI.
851 columns.useCheckboxesForHidden();
852 columns.checked = function(field) {
853 $('.field-' + field).removeClass('hidden-field');
854 };
855 columns.unchecked = function(field) {
856 $('.field-' + field).addClass('hidden-field');
857 };
858 // Hide fields.
859 api.menuList.hideAdvancedMenuItemFields();
860
861 $('.hide-postbox-tog').on( 'click', function () {
862 var hidden = $( '.accordion-container li.accordion-section' ).filter(':hidden').map(function() { return this.id; }).get().join(',');
863 $.post(ajaxurl, {
864 action: 'closed-postboxes',
865 hidden: hidden,
866 closedpostboxesnonce: jQuery('#closedpostboxesnonce').val(),
867 page: 'nav-menus'
868 });
869 });
870 },
871
872 initSortables : function() {
873 var currentDepth = 0, originalDepth, minDepth, maxDepth,
874 prev, next, prevBottom, nextThreshold, helperHeight, transport,
875 menuEdge = api.menuList.offset().left,
876 body = $('body'), maxChildDepth,
877 menuMaxDepth = initialMenuMaxDepth();
878
879 if( 0 !== $( '#menu-to-edit li' ).length )
880 $( '.drag-instructions' ).show();
881
882 // Use the right edge if RTL.
883 menuEdge += api.isRTL ? api.menuList.width() : 0;
884
885 api.menuList.sortable({
886 handle: '.menu-item-handle',
887 placeholder: 'sortable-placeholder',
888 items: api.options.sortableItems,
889 start: function(e, ui) {
890 var height, width, parent, children, tempHolder;
891
892 // Handle placement for RTL orientation.
893 if ( api.isRTL )
894 ui.item[0].style.right = 'auto';
895
896 transport = ui.item.children('.menu-item-transport');
897
898 // Set depths. currentDepth must be set before children are located.
899 originalDepth = ui.item.menuItemDepth();
900 updateCurrentDepth(ui, originalDepth);
901
902 // Attach child elements to parent.
903 // Skip the placeholder.
904 parent = ( ui.item.next()[0] == ui.placeholder[0] ) ? ui.item.next() : ui.item;
905 children = parent.childMenuItems();
906 transport.append( children );
907
908 // Update the height of the placeholder to match the moving item.
909 height = transport.outerHeight();
910 // If there are children, account for distance between top of children and parent.
911 height += ( height > 0 ) ? (ui.placeholder.css('margin-top').slice(0, -2) * 1) : 0;
912 height += ui.helper.outerHeight();
913 helperHeight = height;
914 height -= 2; // Subtract 2 for borders.
915 ui.placeholder.height(height);
916
917 // Update the width of the placeholder to match the moving item.
918 maxChildDepth = originalDepth;
919 children.each(function(){
920 var depth = $(this).menuItemDepth();
921 maxChildDepth = (depth > maxChildDepth) ? depth : maxChildDepth;
922 });
923 width = ui.helper.find('.menu-item-handle').outerWidth(); // Get original width.
924 width += api.depthToPx(maxChildDepth - originalDepth); // Account for children.
925 width -= 2; // Subtract 2 for borders.
926 ui.placeholder.width(width);
927
928 // Update the list of menu items.
929 tempHolder = ui.placeholder.next( '.menu-item' );
930 tempHolder.css( 'margin-top', helperHeight + 'px' ); // Set the margin to absorb the placeholder.
931 ui.placeholder.detach(); // Detach or jQuery UI will think the placeholder is a menu item.
932 $(this).sortable( 'refresh' ); // The children aren't sortable. We should let jQuery UI know.
933 ui.item.after( ui.placeholder ); // Reattach the placeholder.
934 tempHolder.css('margin-top', 0); // Reset the margin.
935
936 // Now that the element is complete, we can update...
937 updateSharedVars(ui);
938 },
939 stop: function(e, ui) {
940 var children, subMenuTitle,
941 depthChange = currentDepth - originalDepth;
942
943 // Return child elements to the list.
944 children = transport.children().insertAfter(ui.item);
945
946 // Add "sub menu" description.
947 subMenuTitle = ui.item.find( '.item-title .is-submenu' );
948 if ( 0 < currentDepth )
949 subMenuTitle.show();
950 else
951 subMenuTitle.hide();
952
953 // Update depth classes.
954 if ( 0 !== depthChange ) {
955 ui.item.updateDepthClass( currentDepth );
956 children.shiftDepthClass( depthChange );
957 updateMenuMaxDepth( depthChange );
958 }
959 // Register a change.
960 api.registerChange();
961 // Update the item data.
962 ui.item.updateParentMenuItemDBId();
963
964 // Address sortable's incorrectly-calculated top in Opera.
965 ui.item[0].style.top = 0;
966
967 // Handle drop placement for rtl orientation.
968 if ( api.isRTL ) {
969 ui.item[0].style.left = 'auto';
970 ui.item[0].style.right = 0;
971 }
972
973 api.refreshKeyboardAccessibility();
974 api.refreshAdvancedAccessibility();
975 ui.item.updateParentDropdown();
976 ui.item.updateOrderDropdown();
977 api.refreshAdvancedAccessibilityOfItem( ui.item.find( 'a.item-edit' ) );
978 },
979 change: function(e, ui) {
980 // Make sure the placeholder is inside the menu.
981 // Otherwise fix it, or we're in trouble.
982 if( ! ui.placeholder.parent().hasClass('menu') )
983 (prev.length) ? prev.after( ui.placeholder ) : api.menuList.prepend( ui.placeholder );
984
985 updateSharedVars(ui);
986 },
987 sort: function(e, ui) {
988 var offset = ui.helper.offset(),
989 edge = api.isRTL ? offset.left + ui.helper.width() : offset.left,
990 depth = api.negateIfRTL * api.pxToDepth( edge - menuEdge );
991
992 /*
993 * Check and correct if depth is not within range.
994 * Also, if the dragged element is dragged upwards over an item,
995 * shift the placeholder to a child position.
996 */
997 if ( depth > maxDepth || offset.top < ( prevBottom - api.options.targetTolerance ) ) {
998 depth = maxDepth;
999 } else if ( depth < minDepth ) {
1000 depth = minDepth;
1001 }
1002
1003 if( depth != currentDepth )
1004 updateCurrentDepth(ui, depth);
1005
1006 // If we overlap the next element, manually shift downwards.
1007 if( nextThreshold && offset.top + helperHeight > nextThreshold ) {
1008 next.after( ui.placeholder );
1009 updateSharedVars( ui );
1010 $( this ).sortable( 'refreshPositions' );
1011 }
1012 }
1013 });
1014
1015 function updateSharedVars(ui) {
1016 var depth;
1017
1018 prev = ui.placeholder.prev( '.menu-item' );
1019 next = ui.placeholder.next( '.menu-item' );
1020
1021 // Make sure we don't select the moving item.
1022 if( prev[0] == ui.item[0] ) prev = prev.prev( '.menu-item' );
1023 if( next[0] == ui.item[0] ) next = next.next( '.menu-item' );
1024
1025 prevBottom = (prev.length) ? prev.offset().top + prev.height() : 0;
1026 nextThreshold = (next.length) ? next.offset().top + next.height() / 3 : 0;
1027 minDepth = (next.length) ? next.menuItemDepth() : 0;
1028
1029 if( prev.length )
1030 maxDepth = ( (depth = prev.menuItemDepth() + 1) > api.options.globalMaxDepth ) ? api.options.globalMaxDepth : depth;
1031 else
1032 maxDepth = 0;
1033 }
1034
1035 function updateCurrentDepth(ui, depth) {
1036 ui.placeholder.updateDepthClass( depth, currentDepth );
1037 currentDepth = depth;
1038 }
1039
1040 function initialMenuMaxDepth() {
1041 if( ! body[0].className ) return 0;
1042 var match = body[0].className.match(/menu-max-depth-(\d+)/);
1043 return match && match[1] ? parseInt( match[1], 10 ) : 0;
1044 }
1045
1046 function updateMenuMaxDepth( depthChange ) {
1047 var depth, newDepth = menuMaxDepth;
1048 if ( depthChange === 0 ) {
1049 return;
1050 } else if ( depthChange > 0 ) {
1051 depth = maxChildDepth + depthChange;
1052 if( depth > menuMaxDepth )
1053 newDepth = depth;
1054 } else if ( depthChange < 0 && maxChildDepth == menuMaxDepth ) {
1055 while( ! $('.menu-item-depth-' + newDepth, api.menuList).length && newDepth > 0 )
1056 newDepth--;
1057 }
1058 // Update the depth class.
1059 body.removeClass( 'menu-max-depth-' + menuMaxDepth ).addClass( 'menu-max-depth-' + newDepth );
1060 menuMaxDepth = newDepth;
1061 }
1062 },
1063
1064 initManageLocations : function () {
1065 $('#menu-locations-wrap form').on( 'submit', function(){
1066 window.onbeforeunload = null;
1067 });
1068 $('.menu-location-menus select').on('change', function () {
1069 var editLink = $(this).closest('tr').find('.locations-edit-menu-link');
1070 if ($(this).find('option:selected').data('orig'))
1071 editLink.show();
1072 else
1073 editLink.hide();
1074 });
1075 },
1076
1077 attachMenuEditListeners : function() {
1078 var that = this;
1079 $('#update-nav-menu').on('click', function(e) {
1080 if ( e.target && e.target.className ) {
1081 if ( -1 != e.target.className.indexOf('item-edit') ) {
1082 return that.eventOnClickEditLink(e.target);
1083 } else if ( -1 != e.target.className.indexOf('menu-save') ) {
1084 return that.eventOnClickMenuSave(e.target);
1085 } else if ( -1 != e.target.className.indexOf('menu-delete') ) {
1086 return that.eventOnClickMenuDelete(e.target);
1087 } else if ( -1 != e.target.className.indexOf('item-delete') ) {
1088 return that.eventOnClickMenuItemDelete(e.target);
1089 } else if ( -1 != e.target.className.indexOf('item-cancel') ) {
1090 return that.eventOnClickCancelLink(e.target);
1091 }
1092 }
1093 });
1094
1095 $( '#menu-name' ).on( 'input', _.debounce( function () {
1096 var menuName = $( document.getElementById( 'menu-name' ) ),
1097 menuNameVal = menuName.val();
1098
1099 if ( ! menuNameVal || ! menuNameVal.replace( /\s+/, '' ) ) {
1100 // Add warning for invalid menu name.
1101 menuName.parent().addClass( 'form-invalid' );
1102 } else {
1103 // Remove warning for valid menu name.
1104 menuName.parent().removeClass( 'form-invalid' );
1105 }
1106 }, 500 ) );
1107
1108 $('#add-custom-links input[type="text"]').on( 'keypress', function(e){
1109 $( '#customlinkdiv' ).removeClass( 'form-invalid' );
1110 $( '#custom-menu-item-url' ).removeAttr( 'aria-invalid' ).removeAttr( 'aria-describedby' );
1111 $( '#custom-url-error' ).hide();
1112
1113 if ( e.keyCode === 13 ) {
1114 e.preventDefault();
1115 $( '#submit-customlinkdiv' ).trigger( 'click' );
1116 }
1117 });
1118
1119 $( '#submit-customlinkdiv' ).on( 'click', function (e) {
1120 var urlInput = $( '#custom-menu-item-url' ),
1121 url = urlInput.val().trim(),
1122 errorMessage = $( '#custom-url-error' ),
1123 urlWrap = $( '#menu-item-url-wrap' ),
1124 urlRegex;
1125
1126 // Hide the error message initially
1127 errorMessage.hide();
1128 urlWrap.removeClass( 'has-error' );
1129
1130 /*
1131 * Allow URLs including:
1132 * - http://example.com/
1133 * - //example.com
1134 * - /directory/
1135 * - ?query-param
1136 * - #target
1137 * - mailto:foo@example.com
1138 *
1139 * Any further validation will be handled on the server when the setting is attempted to be saved,
1140 * so this pattern does not need to be complete.
1141 */
1142 urlRegex = /^((\w+:)?\/\/\w.*|\w+:(?!\/\/$)|\/|\?|#)/;
1143 if ( ! urlRegex.test( url ) ) {
1144 e.preventDefault();
1145 urlInput.addClass( 'form-invalid' )
1146 .attr( 'aria-invalid', 'true' )
1147 .attr( 'aria-describedby', 'custom-url-error' );
1148
1149 errorMessage.show();
1150 var errorText = errorMessage.text();
1151 urlWrap.addClass( 'has-error' );
1152 // Announce error message via screen reader
1153 wp.a11y.speak( errorText, 'assertive' );
1154 }
1155 });
1156 },
1157
1158 /**
1159 * Handle toggling bulk selection checkboxes for menu items.
1160 *
1161 * @since 5.8.0
1162 */
1163 attachBulkSelectButtonListeners : function() {
1164 var that = this;
1165
1166 $( '.bulk-select-switcher' ).on( 'change', function() {
1167 if ( this.checked ) {
1168 $( '.bulk-select-switcher' ).prop( 'checked', true );
1169 that.enableBulkSelection();
1170 } else {
1171 $( '.bulk-select-switcher' ).prop( 'checked', false );
1172 that.disableBulkSelection();
1173 }
1174 });
1175 },
1176
1177 /**
1178 * Enable bulk selection checkboxes for menu items.
1179 *
1180 * @since 5.8.0
1181 */
1182 enableBulkSelection : function() {
1183 var checkbox = $( '#menu-to-edit .menu-item-checkbox' );
1184
1185 $( '#menu-to-edit' ).addClass( 'bulk-selection' );
1186 $( '#nav-menu-bulk-actions-top' ).addClass( 'bulk-selection' );
1187 $( '#nav-menu-bulk-actions-bottom' ).addClass( 'bulk-selection' );
1188
1189 $.each( checkbox, function() {
1190 $(this).prop( 'disabled', false );
1191 });
1192 },
1193
1194 /**
1195 * Disable bulk selection checkboxes for menu items.
1196 *
1197 * @since 5.8.0
1198 */
1199 disableBulkSelection : function() {
1200 var checkbox = $( '#menu-to-edit .menu-item-checkbox' );
1201
1202 $( '#menu-to-edit' ).removeClass( 'bulk-selection' );
1203 $( '#nav-menu-bulk-actions-top' ).removeClass( 'bulk-selection' );
1204 $( '#nav-menu-bulk-actions-bottom' ).removeClass( 'bulk-selection' );
1205
1206 if ( $( '.menu-items-delete' ).is( '[aria-describedby="pending-menu-items-to-delete"]' ) ) {
1207 $( '.menu-items-delete' ).removeAttr( 'aria-describedby' );
1208 }
1209
1210 $.each( checkbox, function() {
1211 $(this).prop( 'disabled', true ).prop( 'checked', false );
1212 });
1213
1214 $( '.menu-items-delete' ).addClass( 'disabled' );
1215 $( '#pending-menu-items-to-delete ul' ).empty();
1216 },
1217
1218 /**
1219 * Listen for state changes on bulk action checkboxes.
1220 *
1221 * @since 5.8.0
1222 */
1223 attachMenuCheckBoxListeners : function() {
1224 var that = this;
1225
1226 $( '#menu-to-edit' ).on( 'change', '.menu-item-checkbox', function() {
1227 that.setRemoveSelectedButtonStatus();
1228 });
1229 },
1230
1231 /**
1232 * Create delete button to remove menu items from collection.
1233 *
1234 * @since 5.8.0
1235 */
1236 attachMenuItemDeleteButton : function() {
1237 var that = this;
1238
1239 $( document ).on( 'click', '.menu-items-delete', function( e ) {
1240 var itemsPendingDeletion, itemsPendingDeletionList, deletionSpeech;
1241
1242 e.preventDefault();
1243
1244 if ( ! $(this).hasClass( 'disabled' ) ) {
1245 $.each( $( '.menu-item-checkbox:checked' ), function( index, element ) {
1246 $( element ).parents( 'li' ).find( 'a.item-delete' ).trigger( 'click' );
1247 });
1248
1249 $( '.menu-items-delete' ).addClass( 'disabled' );
1250 $( '.bulk-select-switcher' ).prop( 'checked', false );
1251
1252 itemsPendingDeletion = '';
1253 itemsPendingDeletionList = $( '#pending-menu-items-to-delete ul li' );
1254
1255 $.each( itemsPendingDeletionList, function( index, element ) {
1256 var itemName = $( element ).find( '.pending-menu-item-name' ).text();
1257 var itemSpeech = menus.menuItemDeletion.replace( '%s', itemName );
1258
1259 itemsPendingDeletion += itemSpeech;
1260 if ( ( index + 1 ) < itemsPendingDeletionList.length ) {
1261 itemsPendingDeletion += ', ';
1262 }
1263 });
1264
1265 deletionSpeech = menus.itemsDeleted.replace( '%s', itemsPendingDeletion );
1266 wp.a11y.speak( deletionSpeech, 'polite' );
1267 that.disableBulkSelection();
1268 $( '#menu-to-edit' ).updateParentDropdown();
1269 $( '#menu-to-edit' ).updateOrderDropdown();
1270 }
1271 });
1272 },
1273
1274 /**
1275 * List menu items awaiting deletion.
1276 *
1277 * @since 5.8.0
1278 */
1279 attachPendingMenuItemsListForDeletion : function() {
1280 $( '#post-body-content' ).on( 'change', '.menu-item-checkbox', function() {
1281 var menuItemName, menuItemType, menuItemID, listedMenuItem;
1282
1283 if ( ! $( '.menu-items-delete' ).is( '[aria-describedby="pending-menu-items-to-delete"]' ) ) {
1284 $( '.menu-items-delete' ).attr( 'aria-describedby', 'pending-menu-items-to-delete' );
1285 }
1286
1287 menuItemName = $(this).next().text();
1288 menuItemType = $(this).parent().next( '.item-controls' ).find( '.item-type' ).text();
1289 menuItemID = $(this).attr( 'data-menu-item-id' );
1290
1291 listedMenuItem = $( '#pending-menu-items-to-delete ul' ).find( '[data-menu-item-id=' + menuItemID + ']' );
1292 if ( listedMenuItem.length > 0 ) {
1293 listedMenuItem.remove();
1294 }
1295
1296 if ( this.checked === true ) {
1297 const $li = $( '<li>', { 'data-menu-item-id': menuItemID } );
1298 $li.append( $( '<span>', {
1299 'class': 'pending-menu-item-name',
1300 text: menuItemName
1301 } ) );
1302 $li.append( ' ' );
1303 $li.append( $( '<span>', {
1304 'class': 'pending-menu-item-type',
1305 text: '(' + menuItemType + ')',
1306 } ) );
1307 $li.append( $( '<span>', { 'class': 'separator' } ) );
1308 $( '#pending-menu-items-to-delete ul' ).append( $li );
1309 }
1310
1311 $( '#pending-menu-items-to-delete li .separator' ).html( ', ' );
1312 $( '#pending-menu-items-to-delete li .separator' ).last().html( '.' );
1313 });
1314 },
1315
1316 /**
1317 * Set status of bulk delete checkbox.
1318 *
1319 * @since 5.8.0
1320 */
1321 setBulkDeleteCheckboxStatus : function() {
1322 var that = this;
1323 var checkbox = $( '#menu-to-edit .menu-item-checkbox' );
1324
1325 $.each( checkbox, function() {
1326 if ( $(this).prop( 'disabled' ) ) {
1327 $(this).prop( 'disabled', false );
1328 } else {
1329 $(this).prop( 'disabled', true );
1330 }
1331
1332 if ( $(this).is( ':checked' ) ) {
1333 $(this).prop( 'checked', false );
1334 }
1335 });
1336
1337 that.setRemoveSelectedButtonStatus();
1338 },
1339
1340 /**
1341 * Set status of menu items removal button.
1342 *
1343 * @since 5.8.0
1344 */
1345 setRemoveSelectedButtonStatus : function() {
1346 var button = $( '.menu-items-delete' );
1347
1348 if ( $( '.menu-item-checkbox:checked' ).length > 0 ) {
1349 button.removeClass( 'disabled' );
1350 } else {
1351 button.addClass( 'disabled' );
1352 }
1353 },
1354
1355 attachMenuSaveSubmitListeners : function() {
1356 /*
1357 * When a navigation menu is saved, store a JSON representation of all form data
1358 * in a single input to avoid PHP `max_input_vars` limitations. See #14134.
1359 */
1360 $( '#update-nav-menu' ).on( 'submit', function() {
1361 var navMenuData = $( '#update-nav-menu' ).serializeArray();
1362 $( '[name="nav-menu-data"]' ).val( JSON.stringify( navMenuData ) );
1363 });
1364 },
1365
1366 attachThemeLocationsListeners : function() {
1367 var loc = $('#nav-menu-theme-locations'), params = {};
1368 params.action = 'menu-locations-save';
1369 params['menu-settings-column-nonce'] = $('#menu-settings-column-nonce').val();
1370 loc.find('input[type="submit"]').on( 'click', function() {
1371 loc.find('select').each(function() {
1372 params[this.name] = $(this).val();
1373 });
1374 loc.find( '.spinner' ).addClass( 'is-active' );
1375 $.post( ajaxurl, params, function() {
1376 loc.find( '.spinner' ).removeClass( 'is-active' );
1377 });
1378 return false;
1379 });
1380 },
1381
1382 attachQuickSearchListeners : function() {
1383 var searchTimer;
1384
1385 // Prevent form submission.
1386 $( '#nav-menu-meta' ).on( 'submit', function( event ) {
1387 event.preventDefault();
1388 });
1389
1390 $( '#nav-menu-meta' ).on( 'input', '.quick-search', function() {
1391 var $this = $( this );
1392
1393 $this.attr( 'autocomplete', 'off' );
1394
1395 if ( searchTimer ) {
1396 clearTimeout( searchTimer );
1397 }
1398
1399 searchTimer = setTimeout( function() {
1400 api.updateQuickSearchResults( $this );
1401 }, 500 );
1402 }).on( 'blur', '.quick-search', function() {
1403 api.lastSearch = '';
1404 });
1405 },
1406
1407 updateQuickSearchResults : function(input) {
1408 var panel, params,
1409 minSearchLength = 1,
1410 q = input.val(),
1411 pageSearchChecklist = $( '#page-search-checklist' );
1412
1413 /*
1414 * Avoid a new Ajax search when the pressed key (e.g. arrows)
1415 * doesn't change the searched term.
1416 */
1417 if ( api.lastSearch == q ) {
1418 return;
1419 }
1420
1421 /*
1422 * Reset results when search is less than or equal to
1423 * minimum characters for searched term.
1424 */
1425 if ( q.length <= minSearchLength ) {
1426 pageSearchChecklist.empty();
1427 wp.a11y.speak( wp.i18n.__( 'Search results cleared' ) );
1428 return;
1429 }
1430
1431 api.lastSearch = q;
1432
1433 panel = input.parents('.tabs-panel');
1434 params = {
1435 'action': 'menu-quick-search',
1436 'response-format': 'markup',
1437 'menu': $('#menu').val(),
1438 'menu-settings-column-nonce': $('#menu-settings-column-nonce').val(),
1439 'q': q,
1440 'type': input.attr('name')
1441 };
1442
1443 $( '.spinner', panel ).addClass( 'is-active' );
1444
1445 $.post( ajaxurl, params, function(menuMarkup) {
1446 api.processQuickSearchQueryResponse(menuMarkup, params, panel);
1447 });
1448 },
1449
1450 addCustomLink : function( processMethod ) {
1451 var url = $('#custom-menu-item-url').val().toString(),
1452 label = $('#custom-menu-item-name').val(),
1453 urlRegex;
1454
1455 if ( '' !== url ) {
1456 url = url.trim();
1457 }
1458
1459 processMethod = processMethod || api.addMenuItemToBottom;
1460
1461 /*
1462 * Allow URLs including:
1463 * - http://example.com/
1464 * - //example.com
1465 * - /directory/
1466 * - ?query-param
1467 * - #target
1468 * - mailto:foo@example.com
1469 *
1470 * Any further validation will be handled on the server when the setting is attempted to be saved,
1471 * so this pattern does not need to be complete.
1472 */
1473 urlRegex = /^((\w+:)?\/\/\w.*|\w+:(?!\/\/$)|\/|\?|#)/;
1474 if ( ! urlRegex.test( url ) ) {
1475 $('#customlinkdiv').addClass('form-invalid');
1476 return false;
1477 }
1478
1479 // Show the Ajax spinner.
1480 $( '.customlinkdiv .spinner' ).addClass( 'is-active' );
1481 this.addLinkToMenu( url, label, processMethod, function() {
1482 // Remove the Ajax spinner.
1483 $( '.customlinkdiv .spinner' ).removeClass( 'is-active' );
1484 // Set custom link form back to defaults.
1485 $('#custom-menu-item-name').val('').trigger( 'blur' );
1486 $( '#custom-menu-item-url' ).val( '' ).attr( 'placeholder', 'https://' );
1487 });
1488 },
1489
1490 addLinkToMenu : function(url, label, processMethod, callback) {
1491 processMethod = processMethod || api.addMenuItemToBottom;
1492 callback = callback || function(){};
1493
1494 api.addItemToMenu({
1495 '-1': {
1496 'menu-item-type': 'custom',
1497 'menu-item-url': url,
1498 'menu-item-title': label
1499 }
1500 }, processMethod, callback);
1501 },
1502
1503 addItemToMenu : function(menuItem, processMethod, callback) {
1504 var menu = $('#menu').val(),
1505 nonce = $('#menu-settings-column-nonce').val(),
1506 params;
1507
1508 processMethod = processMethod || function(){};
1509 callback = callback || function(){};
1510
1511 params = {
1512 'action': 'add-menu-item',
1513 'menu': menu,
1514 'menu-settings-column-nonce': nonce,
1515 'menu-item': menuItem
1516 };
1517
1518 $.post( ajaxurl, params, function(menuMarkup) {
1519 var ins = $('#menu-instructions');
1520
1521 menuMarkup = menuMarkup || '';
1522 menuMarkup = menuMarkup.toString().trim(); // Trim leading whitespaces.
1523 processMethod(menuMarkup, params);
1524
1525 // Make it stand out a bit more visually, by adding a fadeIn.
1526 $( 'li.pending' ).hide().fadeIn('slow');
1527 $( '.drag-instructions' ).show();
1528 if( ! ins.hasClass( 'menu-instructions-inactive' ) && ins.siblings().length )
1529 ins.addClass( 'menu-instructions-inactive' );
1530
1531 callback();
1532 });
1533 },
1534
1535 /**
1536 * Process the add menu item request response into menu list item. Appends to menu.
1537 *
1538 * @param {string} menuMarkup The text server response of menu item markup.
1539 *
1540 * @fires document#menu-item-added Passes menuMarkup as a jQuery object.
1541 */
1542 addMenuItemToBottom : function( menuMarkup ) {
1543 var $menuMarkup = $( menuMarkup );
1544 $menuMarkup.hideAdvancedMenuItemFields().appendTo( api.targetList );
1545 api.refreshKeyboardAccessibility();
1546 api.refreshAdvancedAccessibility();
1547 wp.a11y.speak( menus.itemAdded );
1548 $( document ).trigger( 'menu-item-added', [ $menuMarkup ] );
1549 },
1550
1551 /**
1552 * Process the add menu item request response into menu list item. Prepends to menu.
1553 *
1554 * @param {string} menuMarkup The text server response of menu item markup.
1555 *
1556 * @fires document#menu-item-added Passes menuMarkup as a jQuery object.
1557 */
1558 addMenuItemToTop : function( menuMarkup ) {
1559 var $menuMarkup = $( menuMarkup );
1560 $menuMarkup.hideAdvancedMenuItemFields().prependTo( api.targetList );
1561 api.refreshKeyboardAccessibility();
1562 api.refreshAdvancedAccessibility();
1563 wp.a11y.speak( menus.itemAdded );
1564 $( document ).trigger( 'menu-item-added', [ $menuMarkup ] );
1565 },
1566
1567 attachUnsavedChangesListener : function() {
1568 $('#menu-management input, #menu-management select, #menu-management, #menu-management textarea, .menu-location-menus select').on( 'change', function(){
1569 api.registerChange();
1570 });
1571
1572 if ( 0 !== $('#menu-to-edit').length || 0 !== $('.menu-location-menus select').length ) {
1573 window.onbeforeunload = function(){
1574 if ( api.menusChanged )
1575 return wp.i18n.__( 'The changes you made will be lost if you navigate away from this page.' );
1576 };
1577 } else {
1578 // Make the post boxes read-only, as they can't be used yet.
1579 $( '#menu-settings-column' ).find( 'input,select' ).end().find( 'a' ).attr( 'href', '#' ).off( 'click' );
1580 }
1581 },
1582
1583 registerChange : function() {
1584 api.menusChanged = true;
1585 },
1586
1587 attachTabsPanelListeners : function() {
1588 $('#menu-settings-column').on('click', function(e) {
1589 var selectAreaMatch, selectAll, panelId, wrapper, items,
1590 target = $(e.target);
1591
1592 if ( target.hasClass('nav-tab-link') ) {
1593
1594 panelId = target.data( 'type' );
1595
1596 wrapper = target.parents('.accordion-section-content').first();
1597
1598 // Upon changing tabs, we want to uncheck all checkboxes.
1599 $( 'input', wrapper ).prop( 'checked', false );
1600
1601 $('.tabs-panel-active', wrapper).removeClass('tabs-panel-active').addClass('tabs-panel-inactive');
1602 $('#' + panelId, wrapper).removeClass('tabs-panel-inactive').addClass('tabs-panel-active');
1603
1604 $('.tabs', wrapper).removeClass('tabs');
1605 target.parent().addClass('tabs');
1606
1607 // Select the search bar.
1608 $('.quick-search', wrapper).trigger( 'focus' );
1609
1610 // Hide controls in the search tab if no items found.
1611 if ( ! wrapper.find( '.tabs-panel-active .menu-item-title' ).length ) {
1612 wrapper.addClass( 'has-no-menu-item' );
1613 } else {
1614 wrapper.removeClass( 'has-no-menu-item' );
1615 }
1616
1617 e.preventDefault();
1618 } else if ( target.hasClass( 'select-all' ) ) {
1619 selectAreaMatch = target.closest( '.button-controls' ).data( 'items-type' );
1620 if ( selectAreaMatch ) {
1621 items = $( '#' + selectAreaMatch + ' .tabs-panel-active .menu-item-title input' );
1622
1623 if ( items.length === items.filter( ':checked' ).length && ! target.is( ':checked' ) ) {
1624 items.prop( 'checked', false );
1625 } else if ( target.is( ':checked' ) ) {
1626 items.prop( 'checked', true );
1627 }
1628 }
1629 } else if ( target.hasClass( 'menu-item-checkbox' ) ) {
1630 selectAreaMatch = target.closest( '.tabs-panel-active' ).parent().attr( 'id' );
1631 if ( selectAreaMatch ) {
1632 items = $( '#' + selectAreaMatch + ' .tabs-panel-active .menu-item-title input' );
1633 selectAll = $( '.button-controls[data-items-type="' + selectAreaMatch + '"] .select-all' );
1634
1635 if ( items.length === items.filter( ':checked' ).length && ! selectAll.is( ':checked' ) ) {
1636 selectAll.prop( 'checked', true );
1637 } else if ( selectAll.is( ':checked' ) ) {
1638 selectAll.prop( 'checked', false );
1639 }
1640 }
1641 } else if ( target.hasClass('submit-add-to-menu') ) {
1642 api.registerChange();
1643
1644 if ( e.target.id && 'submit-customlinkdiv' == e.target.id )
1645 api.addCustomLink( api.addMenuItemToBottom );
1646 else if ( e.target.id && -1 != e.target.id.indexOf('submit-') )
1647 $('#' + e.target.id.replace(/submit-/, '')).addSelectedToMenu( api.addMenuItemToBottom );
1648 return false;
1649 }
1650 });
1651
1652 /*
1653 * Delegate the `click` event and attach it just to the pagination
1654 * links thus excluding the current page `<span>`. See ticket #35577.
1655 */
1656 $( '#nav-menu-meta' ).on( 'click', 'a.page-numbers', function() {
1657 var $container = $( this ).closest( '.inside' );
1658
1659 $.post( ajaxurl, this.href.replace( /.*\?/, '' ).replace( /action=([^&]*)/, '' ) + '&action=menu-get-metabox',
1660 function( resp ) {
1661 var metaBoxData = JSON.parse( resp ),
1662 toReplace;
1663
1664 if ( -1 === resp.indexOf( 'replace-id' ) ) {
1665 return;
1666 }
1667
1668 // Get the post type menu meta box to update.
1669 toReplace = document.getElementById( metaBoxData['replace-id'] );
1670
1671 if ( ! metaBoxData.markup || ! toReplace ) {
1672 return;
1673 }
1674
1675 // Update the post type menu meta box with new content from the response.
1676 $container.html( metaBoxData.markup );
1677 }
1678 );
1679
1680 return false;
1681 });
1682 },
1683
1684 eventOnClickEditLink : function(clickedEl) {
1685 var settings, item,
1686 matchedSection = /#(.*)$/.exec(clickedEl.href);
1687
1688 if ( matchedSection && matchedSection[1] ) {
1689 settings = $('#'+matchedSection[1]);
1690 item = settings.parent();
1691 if( 0 !== item.length ) {
1692 if( item.hasClass('menu-item-edit-inactive') ) {
1693 if( ! settings.data('menu-item-data') ) {
1694 settings.data( 'menu-item-data', settings.getItemData() );
1695 }
1696 settings.slideDown('fast');
1697 item.removeClass('menu-item-edit-inactive')
1698 .addClass('menu-item-edit-active');
1699 } else {
1700 settings.slideUp('fast');
1701 item.removeClass('menu-item-edit-active')
1702 .addClass('menu-item-edit-inactive');
1703 }
1704 return false;
1705 }
1706 }
1707 },
1708
1709 eventOnClickCancelLink : function(clickedEl) {
1710 var settings = $( clickedEl ).closest( '.menu-item-settings' ),
1711 thisMenuItem = $( clickedEl ).closest( '.menu-item' );
1712
1713 thisMenuItem.removeClass( 'menu-item-edit-active' ).addClass( 'menu-item-edit-inactive' );
1714 settings.setItemData( settings.data( 'menu-item-data' ) ).hide();
1715 // Restore the title of the currently active/expanded menu item.
1716 thisMenuItem.find( '.menu-item-title' ).text( settings.data( 'menu-item-data' )['menu-item-title'] );
1717
1718 return false;
1719 },
1720
1721 eventOnClickMenuSave : function() {
1722 var menuName = $('#menu-name'),
1723 menuNameVal = menuName.val();
1724
1725 // Cancel and warn if invalid menu name.
1726 if ( ! menuNameVal || ! menuNameVal.replace( /\s+/, '' ) ) {
1727 menuName.parent().addClass( 'form-invalid' );
1728 return false;
1729 }
1730 // Copy menu theme locations.
1731 // Note: This appears to be dead code since #nav-menu-theme-locations no longer exists, perhaps removed in r32842.
1732 var $updateNavMenu = $('#update-nav-menu');
1733 $('#nav-menu-theme-locations select').each(function() {
1734 $updateNavMenu.append(
1735 $( '<input>', {
1736 type: 'hidden',
1737 name: this.name,
1738 value: $( this ).val(),
1739 } )
1740 );
1741 });
1742 // Update menu item position data.
1743 api.menuList.find('.menu-item-data-position').val( function(index) { return index + 1; } );
1744 window.onbeforeunload = null;
1745
1746 return true;
1747 },
1748
1749 eventOnClickMenuDelete : function() {
1750 // Delete warning AYS.
1751 if ( window.confirm( wp.i18n.__( 'You are about to permanently delete this menu.\n\'Cancel\' to stop, \'OK\' to delete.' ) ) ) {
1752 window.onbeforeunload = null;
1753 return true;
1754 }
1755 return false;
1756 },
1757
1758 eventOnClickMenuItemDelete : function(clickedEl) {
1759 var itemID = parseInt(clickedEl.id.replace('delete-', ''), 10);
1760
1761 api.removeMenuItem( $('#menu-item-' + itemID) );
1762 api.registerChange();
1763 return false;
1764 },
1765
1766 /**
1767 * Process the quick search response into a search result
1768 *
1769 * @param string resp The server response to the query.
1770 * @param object req The request arguments.
1771 * @param jQuery panel The tabs panel we're searching in.
1772 */
1773 processQuickSearchQueryResponse : function(resp, req, panel) {
1774 var matched, newID,
1775 takenIDs = {},
1776 form = document.getElementById('nav-menu-meta'),
1777 pattern = /menu-item[(\[^]\]*/,
1778 $items = $('<div>').html(resp).find('li'),
1779 wrapper = panel.closest( '.accordion-section-content' ),
1780 selectAll = wrapper.find( '.button-controls .select-all' ),
1781 $item;
1782
1783 if( ! $items.length ) {
1784 let noResults = wp.i18n.__( 'No results found.' );
1785 const li = $( '<li>' );
1786 const p = $( '<p>', { text: noResults } );
1787 li.append( p );
1788 $('.categorychecklist', panel).empty().append( li );
1789 $( '.spinner', panel ).removeClass( 'is-active' );
1790 wrapper.addClass( 'has-no-menu-item' );
1791 wp.a11y.speak( noResults, 'assertive' );
1792 return;
1793 }
1794
1795 $items.each(function(){
1796 $item = $(this);
1797
1798 // Make a unique DB ID number.
1799 matched = pattern.exec($item.html());
1800
1801 if ( matched && matched[1] ) {
1802 newID = matched[1];
1803 while( form.elements['menu-item[' + newID + '][menu-item-type]'] || takenIDs[ newID ] ) {
1804 newID--;
1805 }
1806
1807 takenIDs[newID] = true;
1808 if ( newID != matched[1] ) {
1809 $item.html( $item.html().replace(new RegExp(
1810 'menu-item\\[' + matched[1] + '\\]', 'g'),
1811 'menu-item[' + newID + ']'
1812 ) );
1813 }
1814 }
1815 });
1816
1817 $('.categorychecklist', panel).html( $items );
1818 wp.a11y.speak( wp.i18n.sprintf( wp.i18n.__( '%d Search Results Found' ), $items.length ), 'assertive' );
1819 $( '.spinner', panel ).removeClass( 'is-active' );
1820 wrapper.removeClass( 'has-no-menu-item' );
1821
1822 if ( selectAll.is( ':checked' ) ) {
1823 selectAll.prop( 'checked', false );
1824 }
1825 },
1826
1827 /**
1828 * Remove a menu item.
1829 *
1830 * @param {Object} el The element to be removed as a jQuery object.
1831 *
1832 * @fires document#menu-removing-item Passes the element to be removed.
1833 */
1834 removeMenuItem : function(el) {
1835 var children = el.childMenuItems();
1836
1837 $( document ).trigger( 'menu-removing-item', [ el ] );
1838 el.addClass('deleting').animate({
1839 opacity : 0,
1840 height: 0
1841 }, 350, function() {
1842 var ins = $('#menu-instructions');
1843 el.remove();
1844 children.shiftDepthClass( -1 ).updateParentMenuItemDBId();
1845 if ( 0 === $( '#menu-to-edit li' ).length ) {
1846 $( '.drag-instructions' ).hide();
1847 ins.removeClass( 'menu-instructions-inactive' );
1848 }
1849 api.refreshAdvancedAccessibility();
1850 wp.a11y.speak( menus.itemRemoved );
1851 $( '#menu-to-edit' ).updateParentDropdown();
1852 $( '#menu-to-edit' ).updateOrderDropdown();
1853 });
1854 },
1855
1856 depthToPx : function(depth) {
1857 return depth * api.options.menuItemDepthPerLevel;
1858 },
1859
1860 pxToDepth : function(px) {
1861 return Math.floor(px / api.options.menuItemDepthPerLevel);
1862 }
1863
1864 };
1865
1866 $( function() {
1867
1868 wpNavMenu.init();
1869
1870 // Prevent focused element from being hidden by the sticky footer.
1871 $( '.menu-edit a, .menu-edit button, .menu-edit input, .menu-edit textarea, .menu-edit select' ).on('focus', function() {
1872 if ( window.innerWidth >= 783 ) {
1873 var navMenuHeight = $( '#nav-menu-footer' ).height() + 20;
1874 var bottomOffset = $(this).offset().top - ( $(window).scrollTop() + $(window).height() - $(this).height() );
1875
1876 if ( bottomOffset > 0 ) {
1877 bottomOffset = 0;
1878 }
1879 bottomOffset = bottomOffset * -1;
1880
1881 if( bottomOffset < navMenuHeight ) {
1882 var scrollTop = $(document).scrollTop();
1883 $(document).scrollTop( scrollTop + ( navMenuHeight - bottomOffset ) );
1884 }
1885 }
1886 });
1887 });
1888
1889 // Show bulk action.
1890 $( document ).on( 'menu-item-added', function() {
1891 if ( ! $( '.bulk-actions' ).is( ':visible' ) ) {
1892 $( '.bulk-actions' ).show();
1893 }
1894 } );
1895
1896 // Hide bulk action.
1897 $( document ).on( 'menu-removing-item', function( e, el ) {
1898 var menuElement = $( el ).parents( '#menu-to-edit' );
1899 if ( menuElement.find( 'li' ).length === 1 && $( '.bulk-actions' ).is( ':visible' ) ) {
1900 $( '.bulk-actions' ).hide();
1901 }
1902 } );
1903
1904})(jQuery);
1905
Ui Ux Design – Teachers Night Out https://cardgames4educators.com Wed, 16 Oct 2024 22:24:18 +0000 en-US hourly 1 https://wordpress.org/?v=6.9.4 https://cardgames4educators.com/wp-content/uploads/2024/06/cropped-Card-4-Educators-logo-32x32.png Ui Ux Design – Teachers Night Out https://cardgames4educators.com 32 32 Masters In English How English Speaker https://cardgames4educators.com/masters-in-english-how-english-speaker/ https://cardgames4educators.com/masters-in-english-how-english-speaker/#comments Mon, 27 May 2024 08:54:45 +0000 https://themexriver.com/wp/kadu/?p=1

Erat himenaeos neque id sagittis massa. Hac suscipit pulvinar dignissim platea magnis eu. Don tellus a pharetra inceptos efficitur dui pulvinar. Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent pulvinar odio volutpat parturient. Quisque risus finibus suspendisse mus purus magnis facilisi condimentum consectetur dui. Curae elit suspendisse cursus vehicula.

Turpis taciti class non vel pretium quis pulvinar tempor lobortis nunc. Libero phasellus parturient sapien volutpat malesuada ornare. Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae. Porta est tempor ex eget feugiat vulputate ipsum. Justo nec iaculis habitant diam arcu fermentum.

We offer comprehen sive emplo ment services such as assistance wit employer compliance.Our company is your strategic HR partner as instead of HR. john smithson

Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae.

Exploring Learning Landscapes in Academic

Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent.

]]>
https://cardgames4educators.com/masters-in-english-how-english-speaker/feed/ 1