wp/wp-admin/js/nav-menu.js
changeset 22 8c2e4d02f4ef
parent 21 48c4eec2b7e6
equal deleted inserted replaced
21:48c4eec2b7e6 22:8c2e4d02f4ef
   214 						api.addItemToMenu(menuItems, processMethod, function(){
   214 						api.addItemToMenu(menuItems, processMethod, function(){
   215 							// Deselect the items and hide the Ajax spinner.
   215 							// Deselect the items and hide the Ajax spinner.
   216 							checkboxes.prop( 'checked', false );
   216 							checkboxes.prop( 'checked', false );
   217 							t.find( '.button-controls .select-all' ).prop( 'checked', false );
   217 							t.find( '.button-controls .select-all' ).prop( 'checked', false );
   218 							t.find( '.button-controls .spinner' ).removeClass( 'is-active' );
   218 							t.find( '.button-controls .spinner' ).removeClass( 'is-active' );
       
   219 							t.updateParentDropdown();
       
   220 							t.updateOrderDropdown();
   219 						});
   221 						});
   220 					});
   222 					});
   221 				},
   223 				},
   222 				getItemData : function( itemType, id ) {
   224 				getItemData : function( itemType, id ) {
   223 					itemType = itemType || 'menu-item';
   225 					itemType = itemType || 'menu-item';
   286 								t.val( val );
   288 								t.val( val );
   287 							}
   289 							}
   288 						});
   290 						});
   289 					});
   291 					});
   290 					return this;
   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 								$html = '',
       
   302 								$selected = '',
       
   303 								currentItemID = parentDropdown.closest( 'li.menu-item' ).find( '.menu-item-data-db-id' ).val(),
       
   304 								currentparentID = parentDropdown.closest( 'li.menu-item' ).find( '.menu-item-data-parent-id' ).val(),
       
   305 								currentItem = parentDropdown.closest( 'li.menu-item' ),
       
   306 								currentMenuItemChild = currentItem.childMenuItems(),
       
   307 								excludeMenuItem = [ currentItemID ];
       
   308 
       
   309 							if ( currentMenuItemChild.length > 0 ) {
       
   310 								$.each( currentMenuItemChild, function(){
       
   311 									var childItem = $(this),
       
   312 										childID = childItem.find( '.menu-item-data-db-id' ).val();
       
   313 
       
   314 									excludeMenuItem.push( childID );
       
   315 								});
       
   316 							}
       
   317 
       
   318 							if ( currentparentID == 0 ) {
       
   319 								$selected = 'selected';
       
   320 							}
       
   321 
       
   322 							$html += '<option ' + $selected + ' value="0">' + wp.i18n._x( 'No Parent', 'menu item without a parent in navigation menu' ) + '</option>';
       
   323 
       
   324 							$.each( menuItems, function() {
       
   325 								var menuItem = $(this),
       
   326 								$selected = '',
       
   327 								menuID = menuItem.find( '.menu-item-data-db-id' ).val(),
       
   328 								menuTitle = menuItem.find( '.edit-menu-item-title' ).val();
       
   329 
       
   330 								if ( ! excludeMenuItem.includes( menuID ) ) {
       
   331 									if ( currentparentID == menuID ) {
       
   332 										$selected = 'selected';
       
   333 									}
       
   334 									$html += '<option ' + $selected + ' value="' + menuID + '">' + menuTitle + '</option>';
       
   335 								}
       
   336 							});
       
   337 
       
   338 							parentDropdown.html( $html );
       
   339 						});
       
   340 						
       
   341 					});
       
   342 				},
       
   343 				updateOrderDropdown : function() {
       
   344 					return this.each( function() {
       
   345 						var itemPosition,
       
   346 							orderDropdowns = $( '.edit-menu-item-order' );
       
   347 
       
   348 						$.each( orderDropdowns, function() {
       
   349 							var orderDropdown = $( this ),
       
   350 								menuItem = orderDropdown.closest( 'li.menu-item' ).first(),
       
   351 								depth = menuItem.menuItemDepth(),
       
   352 								isPrimaryMenuItem = ( 0 === depth ),
       
   353 								$html = '',
       
   354 								$selected = '';
       
   355 
       
   356 							if ( isPrimaryMenuItem ) {
       
   357 								var primaryItems = $( '.menu-item-depth-0' ),
       
   358 									totalMenuItems = primaryItems.length;
       
   359 
       
   360 								itemPosition = primaryItems.index( menuItem ) + 1;
       
   361 
       
   362 								for ( let i = 1; i < totalMenuItems + 1; i++ ) {
       
   363 									$selected = '';
       
   364 									if ( i == itemPosition ) { 
       
   365 										$selected = 'selected';
       
   366 									}
       
   367 									var itemString = wp.i18n.sprintf( 
       
   368 										/* translators: 1: The current menu item number, 2: The total number of menu items. */
       
   369 										wp.i18n._x( '%1$s of %2$s', 'part of a total number of menu items' ),
       
   370 										i,
       
   371 										totalMenuItems
       
   372 									);
       
   373 									$html += '<option ' + $selected + ' value="' + i + '">' + itemString + '</option>';
       
   374 								}
       
   375 
       
   376 							} else {
       
   377 								var parentItem = menuItem.prevAll( '.menu-item-depth-' + parseInt( depth - 1, 10 ) ).first(),
       
   378 									parentItemId = parentItem.find( '.menu-item-data-db-id' ).val(),
       
   379 									subItems = $( '.menu-item .menu-item-data-parent-id[value="' + parentItemId + '"]' ),
       
   380 									totalSubMenuItems = subItems.length;
       
   381 
       
   382 								itemPosition = $( subItems.parents('.menu-item').get().reverse() ).index( menuItem ) + 1;
       
   383 
       
   384 								for ( let i = 1; i < totalSubMenuItems + 1; i++ ) {
       
   385 									$selected = '';
       
   386 									if ( i == itemPosition ) {
       
   387 										$selected = 'selected';
       
   388 									}
       
   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 									$html += '<option ' + $selected + ' value="' + i + '">' + submenuString + '</option>';
       
   396 								}
       
   397 
       
   398 							}
       
   399 
       
   400 							orderDropdown.html( $html );
       
   401 						});
       
   402 						
       
   403 					});
   291 				}
   404 				}
   292 			});
   405 			});
   293 		},
   406 		},
   294 
   407 
   295 		countMenuItems : function( depth ) {
   408 		countMenuItems : function( depth ) {
   296 			return $( '.menu-item-depth-' + depth ).length;
   409 			return $( '.menu-item-depth-' + depth ).length;
   297 		},
   410 		},
   298 
   411 
   299 		moveMenuItem : function( $this, dir ) {
   412 		moveMenuItem : function( $this, dir ) {
   300 
       
   301 			var items, newItemPosition, newDepth,
   413 			var items, newItemPosition, newDepth,
   302 				menuItems = $( '#menu-to-edit li' ),
   414 				menuItems = $( '#menu-to-edit li' ),
   303 				menuItemsCount = menuItems.length,
   415 				menuItemsCount = menuItems.length,
   304 				thisItem = $this.parents( 'li.menu-item' ),
   416 				thisItem = $this.parents( 'li.menu-item' ),
   305 				thisItemChildren = thisItem.childMenuItems(),
   417 				thisItemChildren = thisItem.childMenuItems(),
   398 			}
   510 			}
   399 			$this.trigger( 'focus' );
   511 			$this.trigger( 'focus' );
   400 			api.registerChange();
   512 			api.registerChange();
   401 			api.refreshKeyboardAccessibility();
   513 			api.refreshKeyboardAccessibility();
   402 			api.refreshAdvancedAccessibility();
   514 			api.refreshAdvancedAccessibility();
       
   515 			thisItem.updateParentDropdown();
       
   516 			thisItem.updateOrderDropdown();
   403 
   517 
   404 			if ( a11ySpeech ) {
   518 			if ( a11ySpeech ) {
   405 				wp.a11y.speak( a11ySpeech );
   519 				wp.a11y.speak( a11ySpeech );
   406 			}
   520 			}
   407 		},
   521 		},
   429 
   543 
   430 				if ( 'undefined' !== typeof dir ) {
   544 				if ( 'undefined' !== typeof dir ) {
   431 					api.moveMenuItem( $( this ).parents( 'li.menu-item' ).find( 'a.item-edit' ), dir );
   545 					api.moveMenuItem( $( this ).parents( 'li.menu-item' ).find( 'a.item-edit' ), dir );
   432 				}
   546 				}
   433 			});
   547 			});
       
   548 
       
   549 			// Set menu parents data for all menu items.
       
   550 			menu.updateParentDropdown();
       
   551 
       
   552 			// Set menu order data for all menu items.
       
   553 			menu.updateOrderDropdown();
       
   554 
       
   555 			// Update menu item parent when value is changed.
       
   556 			menu.on( 'change', '.edit-menu-item-parent', function() {
       
   557 				api.changeMenuParent( $( this ) );
       
   558 			});
       
   559 			
       
   560 			// Update menu item order when value is changed.
       
   561 			menu.on( 'change', '.edit-menu-item-order', function() {
       
   562 				api.changeMenuOrder( $( this ) );
       
   563 			});
       
   564 		},
       
   565 
       
   566 		/**
       
   567 		 * changeMenuParent( [parentDropdown] )
       
   568 		 * 
       
   569 		 * @since 6.7.0
       
   570 		 *
       
   571 		 * @param {object} parentDropdown select field
       
   572 		 */
       
   573 		changeMenuParent : function( parentDropdown ) {
       
   574 			var menuItemNewPosition,
       
   575 				menuItems = $( '#menu-to-edit li' ),
       
   576 				$this = $( parentDropdown ),
       
   577 				newParentID = $this.val(),
       
   578 				menuItem = $this.closest( 'li.menu-item' ).first(),
       
   579 				menuItemOldDepth = menuItem.menuItemDepth(),
       
   580 				menuItemChildren = menuItem.childMenuItems(),
       
   581 				menuItemNoChildren = parseInt( menuItem.childMenuItems().length, 10 ),
       
   582 				parentItem = $( '#menu-item-' + newParentID ),
       
   583 				parentItemDepth = parentItem.menuItemDepth(),
       
   584 				menuItemNewDepth = parseInt( parentItemDepth ) + 1;
       
   585 
       
   586 			if ( newParentID == 0 ) {
       
   587 				menuItemNewDepth = 0;
       
   588 			}
       
   589 
       
   590 			menuItem.find( '.menu-item-data-parent-id' ).val( newParentID );
       
   591 			menuItem.moveHorizontally( menuItemNewDepth, menuItemOldDepth );
       
   592 
       
   593 			if ( menuItemNoChildren > 0 ) {
       
   594 				menuItem = menuItem.add( menuItemChildren );
       
   595 			}
       
   596 			menuItem.detach();
       
   597 
       
   598 			menuItems = $( '#menu-to-edit li' );
       
   599 
       
   600 			var	parentItemPosition = parseInt( parentItem.index(), 10 ),
       
   601 				parentItemNoChild = parseInt( parentItem.childMenuItems().length, 10 );
       
   602 
       
   603 			if ( parentItemNoChild > 0 ){
       
   604 				menuItemNewPosition = parentItemPosition + parentItemNoChild;
       
   605 			} else {
       
   606 				menuItemNewPosition = parentItemPosition;
       
   607 			}
       
   608 
       
   609 			if ( newParentID == 0 ) {
       
   610 				menuItemNewPosition = menuItems.length - 1;
       
   611 			}
       
   612 
       
   613 			menuItem.insertAfter( menuItems.eq( menuItemNewPosition ) ).updateParentMenuItemDBId().updateParentDropdown().updateOrderDropdown();
       
   614 
       
   615 			api.registerChange();
       
   616 			api.refreshKeyboardAccessibility();
       
   617 			api.refreshAdvancedAccessibility();
       
   618 			$this.trigger( 'focus' );
       
   619 			wp.a11y.speak( menus.parentUpdated, 'polite' );
       
   620 		},
       
   621 
       
   622 		/**
       
   623 		 * changeMenuOrder( [OrderDropdown] )
       
   624 		 * 
       
   625 		 * @since 6.7.0
       
   626 		 *
       
   627 		 * @param {object} orderDropdown select field
       
   628 		 */
       
   629 		changeMenuOrder : function( orderDropdown ) {
       
   630 			var menuItems = $( '#menu-to-edit li' ),
       
   631 				$this = $( orderDropdown ),
       
   632 				newOrderID = parseInt( $this.val(), 10),
       
   633 				menuItem = $this.closest( 'li.menu-item' ).first(),
       
   634 				menuItemChildren = menuItem.childMenuItems(),
       
   635 				menuItemNoChildren = menuItemChildren.length,
       
   636 				menuItemCurrentPosition = parseInt( menuItem.index(), 10 ),
       
   637 				parentItemID = menuItem.find( '.menu-item-data-parent-id' ).val(),
       
   638 				subItems = $( '.menu-item .menu-item-data-parent-id[value="' + parentItemID + '"]' ),
       
   639 				currentItemAtPosition = $(subItems[newOrderID - 1]).closest( 'li.menu-item' );
       
   640 
       
   641 			if ( menuItemNoChildren > 0 ) {
       
   642 				menuItem = menuItem.add( menuItemChildren );
       
   643 			}
       
   644 
       
   645 			var currentItemNoChildren = currentItemAtPosition.childMenuItems().length,
       
   646 				currentItemPosition = parseInt( currentItemAtPosition.index(), 10 );
       
   647 
       
   648 			menuItems = $( '#menu-to-edit li' );
       
   649 
       
   650 			var	menuItemNewPosition = currentItemPosition;
       
   651 
       
   652 			if(menuItemCurrentPosition > menuItemNewPosition){
       
   653 				menuItemNewPosition = currentItemPosition;
       
   654 				menuItem.detach().insertBefore( menuItems.eq( menuItemNewPosition ) ).updateOrderDropdown();
       
   655 			} else {
       
   656 				menuItemNewPosition = menuItemNewPosition + currentItemNoChildren;
       
   657 				menuItem.detach().insertAfter( menuItems.eq( menuItemNewPosition ) ).updateOrderDropdown();
       
   658 			}
       
   659 
       
   660 			api.registerChange();
       
   661 			api.refreshKeyboardAccessibility();
       
   662 			api.refreshAdvancedAccessibility();
       
   663 			$this.trigger( 'focus' );
       
   664 			wp.a11y.speak( menus.orderUpdated, 'polite' );
   434 		},
   665 		},
   435 
   666 
   436 		/**
   667 		/**
   437 		 * refreshAdvancedAccessibilityOfItem( [itemToRefresh] )
   668 		 * refreshAdvancedAccessibilityOfItem( [itemToRefresh] )
   438 		 *
   669 		 *
   735 						ui.item[0].style.right = 0;
   966 						ui.item[0].style.right = 0;
   736 					}
   967 					}
   737 
   968 
   738 					api.refreshKeyboardAccessibility();
   969 					api.refreshKeyboardAccessibility();
   739 					api.refreshAdvancedAccessibility();
   970 					api.refreshAdvancedAccessibility();
       
   971 					ui.item.updateParentDropdown();
       
   972 					ui.item.updateOrderDropdown();
   740 					api.refreshAdvancedAccessibilityOfItem( ui.item.find( 'a.item-edit' ) );
   973 					api.refreshAdvancedAccessibilityOfItem( ui.item.find( 'a.item-edit' ) );
   741 				},
   974 				},
   742 				change: function(e, ui) {
   975 				change: function(e, ui) {
   743 					// Make sure the placeholder is inside the menu.
   976 					// Make sure the placeholder is inside the menu.
   744 					// Otherwise fix it, or we're in trouble.
   977 					// Otherwise fix it, or we're in trouble.
   867 					menuName.parent().removeClass( 'form-invalid' );
  1100 					menuName.parent().removeClass( 'form-invalid' );
   868 				}
  1101 				}
   869 			}, 500 ) );
  1102 			}, 500 ) );
   870 
  1103 
   871 			$('#add-custom-links input[type="text"]').on( 'keypress', function(e){
  1104 			$('#add-custom-links input[type="text"]').on( 'keypress', function(e){
   872 				$('#customlinkdiv').removeClass('form-invalid');
  1105 				$( '#customlinkdiv' ).removeClass( 'form-invalid' );
       
  1106 				$( '#custom-menu-item-url' ).removeAttr( 'aria-invalid' ).removeAttr( 'aria-describedby' );
       
  1107 				$( '#custom-url-error' ).hide();
   873 
  1108 
   874 				if ( e.keyCode === 13 ) {
  1109 				if ( e.keyCode === 13 ) {
   875 					e.preventDefault();
  1110 					e.preventDefault();
   876 					$( '#submit-customlinkdiv' ).trigger( 'click' );
  1111 					$( '#submit-customlinkdiv' ).trigger( 'click' );
       
  1112 				}
       
  1113 			});
       
  1114 
       
  1115 			$( '#submit-customlinkdiv' ).on( 'click', function (e) {
       
  1116 				var urlInput = $( '#custom-menu-item-url' ),
       
  1117 					url = urlInput.val().trim(),
       
  1118 					errorMessage = $( '#custom-url-error' ),
       
  1119 					urlWrap = $( '#menu-item-url-wrap' ),
       
  1120 					urlRegex;
       
  1121 
       
  1122 				// Hide the error message initially
       
  1123 				errorMessage.hide();
       
  1124 				urlWrap.removeClass( 'has-error' );
       
  1125 
       
  1126 				/*
       
  1127 				 * Allow URLs including:
       
  1128 				 * - http://example.com/
       
  1129 				 * - //example.com
       
  1130 				 * - /directory/
       
  1131 				 * - ?query-param
       
  1132 				 * - #target
       
  1133 				 * - mailto:foo@example.com
       
  1134 				 *
       
  1135 				 * Any further validation will be handled on the server when the setting is attempted to be saved,
       
  1136 				 * so this pattern does not need to be complete.
       
  1137 				 */
       
  1138 				urlRegex = /^((\w+:)?\/\/\w.*|\w+:(?!\/\/$)|\/|\?|#)/;
       
  1139 				if ( ! urlRegex.test( url ) ) {
       
  1140 					e.preventDefault();
       
  1141 					urlInput.addClass( 'form-invalid' )
       
  1142 						.attr( 'aria-invalid', 'true' )
       
  1143 						.attr( 'aria-describedby', 'custom-url-error' );
       
  1144 
       
  1145 					errorMessage.show();
       
  1146 					var errorText = errorMessage.text();
       
  1147 					urlWrap.addClass( 'has-error' );
       
  1148 					// Announce error message via screen reader
       
  1149 					wp.a11y.speak( errorText, 'assertive' );
   877 				}
  1150 				}
   878 			});
  1151 			});
   879 		},
  1152 		},
   880 
  1153 
   881 		/**
  1154 		/**
   986 					});
  1259 					});
   987 
  1260 
   988 					deletionSpeech = menus.itemsDeleted.replace( '%s', itemsPendingDeletion );
  1261 					deletionSpeech = menus.itemsDeleted.replace( '%s', itemsPendingDeletion );
   989 					wp.a11y.speak( deletionSpeech, 'polite' );
  1262 					wp.a11y.speak( deletionSpeech, 'polite' );
   990 					that.disableBulkSelection();
  1263 					that.disableBulkSelection();
       
  1264 					$( '#menu-to-edit' ).updateParentDropdown();
       
  1265 					$( '#menu-to-edit' ).updateOrderDropdown();
   991 				}
  1266 				}
   992 			});
  1267 			});
   993 		},
  1268 		},
   994 
  1269 
   995 		/**
  1270 		/**
  1152 			});
  1427 			});
  1153 		},
  1428 		},
  1154 
  1429 
  1155 		addCustomLink : function( processMethod ) {
  1430 		addCustomLink : function( processMethod ) {
  1156 			var url = $('#custom-menu-item-url').val().toString(),
  1431 			var url = $('#custom-menu-item-url').val().toString(),
  1157 				label = $('#custom-menu-item-name').val();
  1432 				label = $('#custom-menu-item-name').val(),
       
  1433 				urlRegex;
  1158 
  1434 
  1159 			if ( '' !== url ) {
  1435 			if ( '' !== url ) {
  1160 				url = url.trim();
  1436 				url = url.trim();
  1161 			}
  1437 			}
  1162 
  1438 
  1163 			processMethod = processMethod || api.addMenuItemToBottom;
  1439 			processMethod = processMethod || api.addMenuItemToBottom;
  1164 
  1440 
  1165 			if ( '' === url || 'https://' == url || 'http://' == url ) {
  1441 			/*
       
  1442 			 * Allow URLs including:
       
  1443 			 * - http://example.com/
       
  1444 			 * - //example.com
       
  1445 			 * - /directory/
       
  1446 			 * - ?query-param
       
  1447 			 * - #target
       
  1448 			 * - mailto:foo@example.com
       
  1449 			 *
       
  1450 			 * Any further validation will be handled on the server when the setting is attempted to be saved,
       
  1451 			 * so this pattern does not need to be complete.
       
  1452 			 */
       
  1453 			urlRegex = /^((\w+:)?\/\/\w.*|\w+:(?!\/\/$)|\/|\?|#)/;
       
  1454 			if ( ! urlRegex.test( url ) ) {
  1166 				$('#customlinkdiv').addClass('form-invalid');
  1455 				$('#customlinkdiv').addClass('form-invalid');
  1167 				return false;
  1456 				return false;
  1168 			}
  1457 			}
  1169 
  1458 
  1170 			// Show the Ajax spinner.
  1459 			// Show the Ajax spinner.
  1525 						$( '.drag-instructions' ).hide();
  1814 						$( '.drag-instructions' ).hide();
  1526 						ins.removeClass( 'menu-instructions-inactive' );
  1815 						ins.removeClass( 'menu-instructions-inactive' );
  1527 					}
  1816 					}
  1528 					api.refreshAdvancedAccessibility();
  1817 					api.refreshAdvancedAccessibility();
  1529 					wp.a11y.speak( menus.itemRemoved );
  1818 					wp.a11y.speak( menus.itemRemoved );
       
  1819 					$( '#menu-to-edit' ).updateParentDropdown();
       
  1820 					$( '#menu-to-edit' ).updateOrderDropdown();
  1530 				});
  1821 				});
  1531 		},
  1822 		},
  1532 
  1823 
  1533 		depthToPx : function(depth) {
  1824 		depthToPx : function(depth) {
  1534 			return depth * api.options.menuItemDepthPerLevel;
  1825 			return depth * api.options.menuItemDepthPerLevel;