wp/wp-admin/js/customize-nav-menus.js
changeset 16 a86126ab1dd4
parent 9 177826044cd9
child 18 be944660c56a
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
    93 	 * Insert a new `auto-draft` post.
    93 	 * Insert a new `auto-draft` post.
    94 	 *
    94 	 *
    95 	 * @since 4.7.0
    95 	 * @since 4.7.0
    96 	 * @alias wp.customize.Menus.insertAutoDraftPost
    96 	 * @alias wp.customize.Menus.insertAutoDraftPost
    97 	 *
    97 	 *
    98 	 * @param {object} params - Parameters for the draft post to create.
    98 	 * @param {Object} params - Parameters for the draft post to create.
    99 	 * @param {string} params.post_type - Post type to add.
    99 	 * @param {string} params.post_type - Post type to add.
   100 	 * @param {string} params.post_title - Post title to use.
   100 	 * @param {string} params.post_title - Post title to use.
   101 	 * @return {jQuery.promise} Promise resolved with the added post.
   101 	 * @return {jQuery.promise} Promise resolved with the added post.
   102 	 */
   102 	 */
   103 	api.Menus.insertAutoDraftPost = function insertAutoDraftPost( params ) {
   103 	api.Menus.insertAutoDraftPost = function insertAutoDraftPost( params ) {
   201 
   201 
   202 			this.debounceSearch = _.debounce( self.search, 500 );
   202 			this.debounceSearch = _.debounce( self.search, 500 );
   203 
   203 
   204 			_.bindAll( this, 'close' );
   204 			_.bindAll( this, 'close' );
   205 
   205 
   206 			// If the available menu items panel is open and the customize controls are
   206 			/*
   207 			// interacted with (other than an item being deleted), then close the
   207 			 * If the available menu items panel is open and the customize controls
   208 			// available menu items panel. Also close on back button click.
   208 			 * are interacted with (other than an item being deleted), then close
       
   209 			 * the available menu items panel. Also close on back button click.
       
   210 			 */
   209 			$( '#customize-controls, .customize-section-back' ).on( 'click keydown', function( e ) {
   211 			$( '#customize-controls, .customize-section-back' ).on( 'click keydown', function( e ) {
   210 				var isDeleteBtn = $( e.target ).is( '.item-delete, .item-delete *' ),
   212 				var isDeleteBtn = $( e.target ).is( '.item-delete, .item-delete *' ),
   211 					isAddNewBtn = $( e.target ).is( '.add-new-menu-item, .add-new-menu-item *' );
   213 					isAddNewBtn = $( e.target ).is( '.add-new-menu-item, .add-new-menu-item *' );
   212 				if ( $( 'body' ).hasClass( 'adding-menu-items' ) && ! isDeleteBtn && ! isAddNewBtn ) {
   214 				if ( $( 'body' ).hasClass( 'adding-menu-items' ) && ! isDeleteBtn && ! isAddNewBtn ) {
   213 					self.close();
   215 					self.close();
   214 				}
   216 				}
   215 			} );
   217 			} );
   216 
   218 
   217 			// Clear the search results and trigger a `keyup` event to fire a new search.
   219 			// Clear the search results and trigger an `input` event to fire a new search.
   218 			this.$clearResults.on( 'click', function() {
   220 			this.$clearResults.on( 'click', function() {
   219 				self.$search.val( '' ).focus().trigger( 'keyup' );
   221 				self.$search.val( '' ).focus().trigger( 'input' );
   220 			} );
   222 			} );
   221 
   223 
   222 			this.$el.on( 'input', '#custom-menu-item-name.invalid, #custom-menu-item-url.invalid', function() {
   224 			this.$el.on( 'input', '#custom-menu-item-name.invalid, #custom-menu-item-url.invalid', function() {
   223 				$( this ).removeClass( 'invalid' );
   225 				$( this ).removeClass( 'invalid' );
   224 			});
   226 			});
   250 						] );
   252 						] );
   251 					}
   253 					}
   252 				}
   254 				}
   253 			});
   255 			});
   254 
   256 
   255 			// Close the panel if the URL in the preview changes
   257 			// Close the panel if the URL in the preview changes.
   256 			api.previewer.bind( 'url', this.close );
   258 			api.previewer.bind( 'url', this.close );
   257 
   259 
   258 			self.delegateEvents();
   260 			self.delegateEvents();
   259 		},
   261 		},
   260 
   262 
   383 		 *
   385 		 *
   384 		 * @since 4.3.0
   386 		 * @since 4.3.0
   385 		 * @since 4.7.0 Changed function signature to take list of item types instead of single type/object.
   387 		 * @since 4.7.0 Changed function signature to take list of item types instead of single type/object.
   386 		 * @access private
   388 		 * @access private
   387 		 *
   389 		 *
   388 		 * @param {Array.<object>} itemTypes List of objects containing type and key.
   390 		 * @param {Array.<Object>} itemTypes List of objects containing type and key.
   389 		 * @param {string} deprecated Formerly the object parameter.
   391 		 * @param {string} deprecated Formerly the object parameter.
   390 		 * @returns {void}
   392 		 * @return {void}
   391 		 */
   393 		 */
   392 		loadItems: function( itemTypes, deprecated ) {
   394 		loadItems: function( itemTypes, deprecated ) {
   393 			var self = this, _itemTypes, requestItemTypes = [], params, request, itemTemplate, availableMenuItemContainers = {};
   395 			var self = this, _itemTypes, requestItemTypes = [], params, request, itemTemplate, availableMenuItemContainers = {};
   394 			itemTemplate = wp.template( 'available-menu-item' );
   396 			itemTemplate = wp.template( 'available-menu-item' );
   395 
   397 
   494 			this.select( $( event.currentTarget ) );
   496 			this.select( $( event.currentTarget ) );
   495 		},
   497 		},
   496 
   498 
   497 		// Submit handler for keypress and click on menu item.
   499 		// Submit handler for keypress and click on menu item.
   498 		_submit: function( event ) {
   500 		_submit: function( event ) {
   499 			// Only proceed with keypress if it is Enter or Spacebar
   501 			// Only proceed with keypress if it is Enter or Spacebar.
   500 			if ( 'keypress' === event.type && ( 13 !== event.which && 32 !== event.which ) ) {
   502 			if ( 'keypress' === event.type && ( 13 !== event.which && 32 !== event.which ) ) {
   501 				return;
   503 				return;
   502 			}
   504 			}
   503 
   505 
   504 			this.submit( $( event.currentTarget ) );
   506 			this.submit( $( event.currentTarget ) );
   582 			};
   584 			};
   583 
   585 
   584 			this.currentMenuControl.addItemToMenu( menuItem );
   586 			this.currentMenuControl.addItemToMenu( menuItem );
   585 
   587 
   586 			// Reset the custom link form.
   588 			// Reset the custom link form.
   587 			itemUrl.val( 'http://' );
   589 			itemUrl.val( '' ).attr( 'placeholder', 'https://' );
   588 			itemName.val( '' );
   590 			itemName.val( '' );
   589 		},
   591 		},
   590 
   592 
   591 		/**
   593 		/**
   592 		 * Submit handler for keypress (enter) on field and click on button.
   594 		 * Submit handler for keypress (enter) on field and click on button.
   593 		 *
   595 		 *
   594 		 * @since 4.7.0
   596 		 * @since 4.7.0
   595 		 * @private
   597 		 * @private
   596 		 *
   598 		 *
   597 		 * @param {jQuery.Event} event Event.
   599 		 * @param {jQuery.Event} event Event.
   598 		 * @returns {void}
   600 		 * @return {void}
   599 		 */
   601 		 */
   600 		_submitNew: function( event ) {
   602 		_submitNew: function( event ) {
   601 			var container;
   603 			var container;
   602 
   604 
   603 			// Only proceed with keypress if it is Enter.
   605 			// Only proceed with keypress if it is Enter.
   619 		 *
   621 		 *
   620 		 * @since 4.7.0
   622 		 * @since 4.7.0
   621 		 * @private
   623 		 * @private
   622 		 *
   624 		 *
   623 		 * @param {jQuery} container
   625 		 * @param {jQuery} container
   624 		 * @returns {void}
   626 		 * @return {void}
   625 		 */
   627 		 */
   626 		submitNew: function( container ) {
   628 		submitNew: function( container ) {
   627 			var panel = this,
   629 			var panel = this,
   628 				itemName = container.find( '.create-item-input' ),
   630 				itemName = container.find( '.create-item-input' ),
   629 				title = itemName.val(),
   631 				title = itemName.val(),
   715 			this.$el.find( '.selected' ).removeClass( 'selected' );
   717 			this.$el.find( '.selected' ).removeClass( 'selected' );
   716 
   718 
   717 			this.$search.focus();
   719 			this.$search.focus();
   718 		},
   720 		},
   719 
   721 
   720 		// Closes the panel
   722 		// Closes the panel.
   721 		close: function( options ) {
   723 		close: function( options ) {
   722 			options = options || {};
   724 			options = options || {};
   723 
   725 
   724 			if ( options.returnFocus && this.currentMenuControl ) {
   726 			if ( options.returnFocus && this.currentMenuControl ) {
   725 				this.currentMenuControl.container.find( '.add-new-menu-item' ).focus();
   727 				this.currentMenuControl.container.find( '.add-new-menu-item' ).focus();
   729 			this.selected = null;
   731 			this.selected = null;
   730 
   732 
   731 			$( 'body' ).removeClass( 'adding-menu-items' );
   733 			$( 'body' ).removeClass( 'adding-menu-items' );
   732 			$( '#available-menu-items .menu-item-handle.item-added' ).removeClass( 'item-added' );
   734 			$( '#available-menu-items .menu-item-handle.item-added' ).removeClass( 'item-added' );
   733 
   735 
   734 			this.$search.val( '' ).trigger( 'keyup' );
   736 			this.$search.val( '' ).trigger( 'input' );
   735 		},
   737 		},
   736 
   738 
   737 		// Add a few keyboard enhancements to the panel.
   739 		// Add a few keyboard enhancements to the panel.
   738 		keyboardAccessible: function( event ) {
   740 		keyboardAccessible: function( event ) {
   739 			var isEnter = ( 13 === event.which ),
   741 			var isEnter = ( 13 === event.which ),
   740 				isEsc = ( 27 === event.which ),
   742 				isEsc = ( 27 === event.which ),
   741 				isBackTab = ( 9 === event.which && event.shiftKey ),
   743 				isBackTab = ( 9 === event.which && event.shiftKey ),
   742 				isSearchFocused = $( event.target ).is( this.$search );
   744 				isSearchFocused = $( event.target ).is( this.$search );
   743 
   745 
   744 			// If enter pressed but nothing entered, don't do anything
   746 			// If enter pressed but nothing entered, don't do anything.
   745 			if ( isEnter && ! this.$search.val() ) {
   747 			if ( isEnter && ! this.$search.val() ) {
   746 				return;
   748 				return;
   747 			}
   749 			}
   748 
   750 
   749 			if ( isSearchFocused && isBackTab ) {
   751 			if ( isSearchFocused && isBackTab ) {
   779 				if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
   781 				if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
   780 					return;
   782 					return;
   781 				}
   783 				}
   782 				event.preventDefault();
   784 				event.preventDefault();
   783 
   785 
   784 				// Hide description
   786 				// Hide description.
   785 				if ( content.not( ':hidden' ) ) {
   787 				if ( content.not( ':hidden' ) ) {
   786 					content.slideUp( 'fast' );
   788 					content.slideUp( 'fast' );
   787 					help.attr( 'aria-expanded', 'false' );
   789 					help.attr( 'aria-expanded', 'false' );
   788 				}
   790 				}
   789 
   791 
   800 				}
   802 				}
   801 
   803 
   802 				return false;
   804 				return false;
   803 			} );
   805 			} );
   804 
   806 
   805 			// Help toggle
   807 			// Help toggle.
   806 			help.on( 'click keydown', function( event ) {
   808 			help.on( 'click keydown', function( event ) {
   807 				if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
   809 				if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
   808 					return;
   810 					return;
   809 				}
   811 				}
   810 				event.preventDefault();
   812 				event.preventDefault();
   841 		 * Save hidden column states.
   843 		 * Save hidden column states.
   842 		 *
   844 		 *
   843 		 * @since 4.3.0
   845 		 * @since 4.3.0
   844 		 * @private
   846 		 * @private
   845 		 *
   847 		 *
   846 		 * @returns {void}
   848 		 * @return {void}
   847 		 */
   849 		 */
   848 		saveManageColumnsState: _.debounce( function() {
   850 		saveManageColumnsState: _.debounce( function() {
   849 			var panel = this;
   851 			var panel = this;
   850 			if ( panel._updateHiddenColumnsRequest ) {
   852 			if ( panel._updateHiddenColumnsRequest ) {
   851 				panel._updateHiddenColumnsRequest.abort();
   853 				panel._updateHiddenColumnsRequest.abort();
   875 		 * Get hidden fields.
   877 		 * Get hidden fields.
   876 		 *
   878 		 *
   877 		 * @since 4.3.0
   879 		 * @since 4.3.0
   878 		 * @private
   880 		 * @private
   879 		 *
   881 		 *
   880 		 * @returns {Array} Fields (columns) that are hidden.
   882 		 * @return {Array} Fields (columns) that are hidden.
   881 		 */
   883 		 */
   882 		hidden: function() {
   884 		hidden: function() {
   883 			return $( '.hide-column-tog' ).not( ':checked' ).map( function() {
   885 			return $( '.hide-column-tog' ).not( ':checked' ).map( function() {
   884 				var id = this.id;
   886 				var id = this.id;
   885 				return id.substring( 0, id.length - 5 );
   887 				return id.substring( 0, id.length - 5 );
   901 		/**
   903 		/**
   902 		 * Initialize.
   904 		 * Initialize.
   903 		 *
   905 		 *
   904 		 * @since 4.3.0
   906 		 * @since 4.3.0
   905 		 *
   907 		 *
   906 		 * @param {String} id
   908 		 * @param {string} id
   907 		 * @param {Object} options
   909 		 * @param {Object} options
   908 		 */
   910 		 */
   909 		initialize: function( id, options ) {
   911 		initialize: function( id, options ) {
   910 			var section = this;
   912 			var section = this;
   911 			api.Section.prototype.initialize.call( section, id, options );
   913 			api.Section.prototype.initialize.call( section, id, options );
   971 
   973 
   972 			/**
   974 			/**
   973 			 * Update the active field class for the content container for a given checkbox toggle.
   975 			 * Update the active field class for the content container for a given checkbox toggle.
   974 			 *
   976 			 *
   975 			 * @this {jQuery}
   977 			 * @this {jQuery}
   976 			 * @returns {void}
   978 			 * @return {void}
   977 			 */
   979 			 */
   978 			handleFieldActiveToggle = function() {
   980 			handleFieldActiveToggle = function() {
   979 				var className = 'field-' + $( this ).val() + '-active';
   981 				var className = 'field-' + $( this ).val() + '-active';
   980 				section.contentContainer.toggleClass( className, $( this ).prop( 'checked' ) );
   982 				section.contentContainer.toggleClass( className, $( this ).prop( 'checked' ) );
   981 			};
   983 			};
  1060 				} );
  1062 				} );
  1061 				api.control.add( menuAutoAddControl );
  1063 				api.control.add( menuAutoAddControl );
  1062 				menuAutoAddControl.active.set( true );
  1064 				menuAutoAddControl.active.set( true );
  1063 			}
  1065 			}
  1064 
  1066 
  1065 			// Add the control for deleting the menu
  1067 			// Add the control for deleting the menu.
  1066 			menuDeleteControlId = section.id + '[delete]';
  1068 			menuDeleteControlId = section.id + '[delete]';
  1067 			menuDeleteControl = api.control( menuDeleteControlId );
  1069 			menuDeleteControl = api.control( menuDeleteControlId );
  1068 			if ( ! menuDeleteControl ) {
  1070 			if ( ! menuDeleteControl ) {
  1069 				menuDeleteControl = new api.Control( menuDeleteControlId, {
  1071 				menuDeleteControl = new api.Control( menuDeleteControlId, {
  1070 					section: section.id,
  1072 					section: section.id,
  1124 
  1126 
  1125 			if ( expanded ) {
  1127 			if ( expanded ) {
  1126 				wpNavMenu.menuList = section.contentContainer;
  1128 				wpNavMenu.menuList = section.contentContainer;
  1127 				wpNavMenu.targetList = wpNavMenu.menuList;
  1129 				wpNavMenu.targetList = wpNavMenu.menuList;
  1128 
  1130 
  1129 				// Add attributes needed by wpNavMenu
  1131 				// Add attributes needed by wpNavMenu.
  1130 				$( '#menu-to-edit' ).removeAttr( 'id' );
  1132 				$( '#menu-to-edit' ).removeAttr( 'id' );
  1131 				wpNavMenu.menuList.attr( 'id', 'menu-to-edit' ).addClass( 'menu' );
  1133 				wpNavMenu.menuList.attr( 'id', 'menu-to-edit' ).addClass( 'menu' );
  1132 
  1134 
  1133 				_.each( api.section( section.id ).controls(), function( control ) {
  1135 				_.each( api.section( section.id ).controls(), function( control ) {
  1134 					if ( 'nav_menu_item' === control.params.type ) {
  1136 					if ( 'nav_menu_item' === control.params.type ) {
  1143 				args.completeCallback = function() {
  1145 				args.completeCallback = function() {
  1144 					if ( 'resolved' !== section.deferred.initSortables.state() ) {
  1146 					if ( 'resolved' !== section.deferred.initSortables.state() ) {
  1145 						wpNavMenu.initSortables(); // Depends on menu-to-edit ID being set above.
  1147 						wpNavMenu.initSortables(); // Depends on menu-to-edit ID being set above.
  1146 						section.deferred.initSortables.resolve( wpNavMenu.menuList ); // Now MenuControl can extend the sortable.
  1148 						section.deferred.initSortables.resolve( wpNavMenu.menuList ); // Now MenuControl can extend the sortable.
  1147 
  1149 
  1148 						// @todo Note that wp.customize.reflowPaneContents() is debounced, so this immediate change will show a slight flicker while priorities get updated.
  1150 						// @todo Note that wp.customize.reflowPaneContents() is debounced,
       
  1151 						// so this immediate change will show a slight flicker while priorities get updated.
  1149 						api.control( 'nav_menu[' + String( section.params.menu_id ) + ']' ).reflowMenuItems();
  1152 						api.control( 'nav_menu[' + String( section.params.menu_id ) + ']' ).reflowMenuItems();
  1150 					}
  1153 					}
  1151 					if ( _.isFunction( completeCallback ) ) {
  1154 					if ( _.isFunction( completeCallback ) ) {
  1152 						completeCallback();
  1155 						completeCallback();
  1153 					}
  1156 					}
  1164 		 * highlighted but those expanding this section know more about why and
  1167 		 * highlighted but those expanding this section know more about why and
  1165 		 * when the affordance should be highlighted.
  1168 		 * when the affordance should be highlighted.
  1166 		 *
  1169 		 *
  1167 		 * @since 4.9.0
  1170 		 * @since 4.9.0
  1168 		 *
  1171 		 *
  1169 		 * @returns {void}
  1172 		 * @return {void}
  1170 		 */
  1173 		 */
  1171 		highlightNewItemButton: function() {
  1174 		highlightNewItemButton: function() {
  1172 			api.utils.highlightButton( this.contentContainer.find( '.add-new-menu-item' ), { delay: 2000 } );
  1175 			api.utils.highlightButton( this.contentContainer.find( '.add-new-menu-item' ), { delay: 2000 } );
  1173 		}
  1176 		}
  1174 	});
  1177 	});
  1177 	 * Create a nav menu setting and section.
  1180 	 * Create a nav menu setting and section.
  1178 	 *
  1181 	 *
  1179 	 * @since 4.9.0
  1182 	 * @since 4.9.0
  1180 	 *
  1183 	 *
  1181 	 * @param {string} [name=''] Nav menu name.
  1184 	 * @param {string} [name=''] Nav menu name.
  1182 	 * @returns {wp.customize.Menus.MenuSection} Added nav menu.
  1185 	 * @return {wp.customize.Menus.MenuSection} Added nav menu.
  1183 	 */
  1186 	 */
  1184 	api.Menus.createNavMenu = function createNavMenu( name ) {
  1187 	api.Menus.createNavMenu = function createNavMenu( name ) {
  1185 		var customizeId, placeholderId, setting;
  1188 		var customizeId, placeholderId, setting;
  1186 		placeholderId = api.Menus.generatePlaceholderAutoIncrementId();
  1189 		placeholderId = api.Menus.generatePlaceholderAutoIncrementId();
  1187 
  1190 
  1261 
  1264 
  1262 			/**
  1265 			/**
  1263 			 * Get number of non-deleted nav menus.
  1266 			 * Get number of non-deleted nav menus.
  1264 			 *
  1267 			 *
  1265 			 * @since 4.9.0
  1268 			 * @since 4.9.0
  1266 			 * @returns {number} Count.
  1269 			 * @return {number} Count.
  1267 			 */
  1270 			 */
  1268 			function getNavMenuCount() {
  1271 			function getNavMenuCount() {
  1269 				var count = 0;
  1272 				var count = 0;
  1270 				api.each( function( setting ) {
  1273 				api.each( function( setting ) {
  1271 					if ( navMenuSettingPattern.test( setting.id ) && false !== setting.get() ) {
  1274 					if ( navMenuSettingPattern.test( setting.id ) && false !== setting.get() ) {
  1277 
  1280 
  1278 			/**
  1281 			/**
  1279 			 * Update visibility of notice to prompt users to create menus.
  1282 			 * Update visibility of notice to prompt users to create menus.
  1280 			 *
  1283 			 *
  1281 			 * @since 4.9.0
  1284 			 * @since 4.9.0
  1282 			 * @returns {void}
  1285 			 * @return {void}
  1283 			 */
  1286 			 */
  1284 			function updateNoticeVisibility() {
  1287 			function updateNoticeVisibility() {
  1285 				container.find( '.add-new-menu-notice' ).prop( 'hidden', getNavMenuCount() > 0 );
  1288 				container.find( '.add-new-menu-notice' ).prop( 'hidden', getNavMenuCount() > 0 );
  1286 			}
  1289 			}
  1287 
  1290 
  1288 			/**
  1291 			/**
  1289 			 * Handle setting addition.
  1292 			 * Handle setting addition.
  1290 			 *
  1293 			 *
  1291 			 * @since 4.9.0
  1294 			 * @since 4.9.0
  1292 			 * @param {wp.customize.Setting} setting - Added setting.
  1295 			 * @param {wp.customize.Setting} setting - Added setting.
  1293 			 * @returns {void}
  1296 			 * @return {void}
  1294 			 */
  1297 			 */
  1295 			function addChangeEventListener( setting ) {
  1298 			function addChangeEventListener( setting ) {
  1296 				if ( navMenuSettingPattern.test( setting.id ) ) {
  1299 				if ( navMenuSettingPattern.test( setting.id ) ) {
  1297 					setting.bind( updateNoticeVisibility );
  1300 					setting.bind( updateNoticeVisibility );
  1298 					updateNoticeVisibility();
  1301 					updateNoticeVisibility();
  1302 			/**
  1305 			/**
  1303 			 * Handle setting removal.
  1306 			 * Handle setting removal.
  1304 			 *
  1307 			 *
  1305 			 * @since 4.9.0
  1308 			 * @since 4.9.0
  1306 			 * @param {wp.customize.Setting} setting - Removed setting.
  1309 			 * @param {wp.customize.Setting} setting - Removed setting.
  1307 			 * @returns {void}
  1310 			 * @return {void}
  1308 			 */
  1311 			 */
  1309 			function removeChangeEventListener( setting ) {
  1312 			function removeChangeEventListener( setting ) {
  1310 				if ( navMenuSettingPattern.test( setting.id ) ) {
  1313 				if ( navMenuSettingPattern.test( setting.id ) ) {
  1311 					setting.unbind( updateNoticeVisibility );
  1314 					setting.unbind( updateNoticeVisibility );
  1312 					updateNoticeVisibility();
  1315 					updateNoticeVisibility();
  1413 
  1416 
  1414 				if ( checkbox.prop( 'checked' ) ) {
  1417 				if ( checkbox.prop( 'checked' ) ) {
  1415 					navMenuLocationSetting = api( 'nav_menu_locations[' + checkbox.data( 'location-id' ) + ']' );
  1418 					navMenuLocationSetting = api( 'nav_menu_locations[' + checkbox.data( 'location-id' ) + ']' );
  1416 					navMenuLocationSetting.set( menuSection.params.menu_id );
  1419 					navMenuLocationSetting.set( menuSection.params.menu_id );
  1417 
  1420 
  1418 					// Reset state for next new menu
  1421 					// Reset state for next new menu.
  1419 					checkbox.prop( 'checked', false );
  1422 					checkbox.prop( 'checked', false );
  1420 				}
  1423 				}
  1421 			} );
  1424 			} );
  1422 
  1425 
  1423 			wp.a11y.speak( api.Menus.data.l10n.menuAdded );
  1426 			wp.a11y.speak( api.Menus.data.l10n.menuAdded );
  1437 		 * creating a menu for a specific menu location.
  1440 		 * creating a menu for a specific menu location.
  1438 		 *
  1441 		 *
  1439 		 * @since 4.9.0
  1442 		 * @since 4.9.0
  1440 		 *
  1443 		 *
  1441 		 * @param {string|null} locationId - The ID of the location to select. `null` clears all selections.
  1444 		 * @param {string|null} locationId - The ID of the location to select. `null` clears all selections.
  1442 		 * @returns {void}
  1445 		 * @return {void}
  1443 		 */
  1446 		 */
  1444 		selectDefaultLocation: function( locationId ) {
  1447 		selectDefaultLocation: function( locationId ) {
  1445 			var locationControl = api.control( this.id + '[locations]' ),
  1448 			var locationControl = api.control( this.id + '[locations]' ),
  1446 				locationSelections = {};
  1449 				locationSelections = {};
  1447 
  1450 
  1696 			control.elements.attr_title = new api.Element( control.container.find( '.edit-menu-item-attr-title' ) );
  1699 			control.elements.attr_title = new api.Element( control.container.find( '.edit-menu-item-attr-title' ) );
  1697 			control.elements.target = new api.Element( control.container.find( '.edit-menu-item-target' ) );
  1700 			control.elements.target = new api.Element( control.container.find( '.edit-menu-item-target' ) );
  1698 			control.elements.classes = new api.Element( control.container.find( '.edit-menu-item-classes' ) );
  1701 			control.elements.classes = new api.Element( control.container.find( '.edit-menu-item-classes' ) );
  1699 			control.elements.xfn = new api.Element( control.container.find( '.edit-menu-item-xfn' ) );
  1702 			control.elements.xfn = new api.Element( control.container.find( '.edit-menu-item-xfn' ) );
  1700 			control.elements.description = new api.Element( control.container.find( '.edit-menu-item-description' ) );
  1703 			control.elements.description = new api.Element( control.container.find( '.edit-menu-item-description' ) );
  1701 			// @todo allow other elements, added by plugins, to be automatically picked up here; allow additional values to be added to setting array.
  1704 			// @todo Allow other elements, added by plugins, to be automatically picked up here;
       
  1705 			// allow additional values to be added to setting array.
  1702 
  1706 
  1703 			_.each( control.elements, function( element, property ) {
  1707 			_.each( control.elements, function( element, property ) {
  1704 				element.bind(function( value ) {
  1708 				element.bind(function( value ) {
  1705 					if ( element.element.is( 'input[type=checkbox]' ) ) {
  1709 					if ( element.element.is( 'input[type=checkbox]' ) ) {
  1706 						value = ( value ) ? element.element.val() : '';
  1710 						value = ( value ) ? element.element.val() : '';
  1788 
  1792 
  1789 			// Configure delete button.
  1793 			// Configure delete button.
  1790 			$removeBtn = control.container.find( '.item-delete' );
  1794 			$removeBtn = control.container.find( '.item-delete' );
  1791 
  1795 
  1792 			$removeBtn.on( 'click', function() {
  1796 			$removeBtn.on( 'click', function() {
  1793 				// Find an adjacent element to add focus to when this menu item goes away
  1797 				// Find an adjacent element to add focus to when this menu item goes away.
  1794 				var addingItems = true, $adjacentFocusTarget, $next, $prev;
  1798 				var addingItems = true, $adjacentFocusTarget, $next, $prev,
       
  1799 					instanceCounter = 0, // Instance count of the menu item deleted.
       
  1800 					deleteItemOriginalItemId = control.params.original_item_id,
       
  1801 					addedItems = control.getMenuControl().$sectionContent.find( '.menu-item' ),
       
  1802 					availableMenuItem;
  1795 
  1803 
  1796 				if ( ! $( 'body' ).hasClass( 'adding-menu-items' ) ) {
  1804 				if ( ! $( 'body' ).hasClass( 'adding-menu-items' ) ) {
  1797 					addingItems = false;
  1805 					addingItems = false;
  1798 				}
  1806 				}
  1799 
  1807 
  1806 					$adjacentFocusTarget = $prev.find( false === addingItems ? '.item-edit' : '.item-delete' ).first();
  1814 					$adjacentFocusTarget = $prev.find( false === addingItems ? '.item-edit' : '.item-delete' ).first();
  1807 				} else {
  1815 				} else {
  1808 					$adjacentFocusTarget = control.container.nextAll( '.customize-control-nav_menu' ).find( '.add-new-menu-item' ).first();
  1816 					$adjacentFocusTarget = control.container.nextAll( '.customize-control-nav_menu' ).find( '.add-new-menu-item' ).first();
  1809 				}
  1817 				}
  1810 
  1818 
       
  1819 				/*
       
  1820 				 * If the menu item deleted is the only of its instance left,
       
  1821 				 * remove the check icon of this menu item in the right panel.
       
  1822 				 */
       
  1823 				_.each( addedItems, function( addedItem ) {
       
  1824 					var menuItemId, menuItemControl, matches;
       
  1825 
       
  1826 					// This is because menu item that's deleted is just hidden.
       
  1827 					if ( ! $( addedItem ).is( ':visible' ) ) {
       
  1828 						return;
       
  1829 					}
       
  1830 
       
  1831 					matches = addedItem.getAttribute( 'id' ).match( /^customize-control-nav_menu_item-(-?\d+)$/, '' );
       
  1832 					if ( ! matches ) {
       
  1833 						return;
       
  1834 					}
       
  1835 
       
  1836 					menuItemId      = parseInt( matches[1], 10 );
       
  1837 					menuItemControl = api.control( 'nav_menu_item[' + String( menuItemId ) + ']' );
       
  1838 
       
  1839 					// Check for duplicate menu items.
       
  1840 					if ( menuItemControl && deleteItemOriginalItemId == menuItemControl.params.original_item_id ) {
       
  1841 						instanceCounter++;
       
  1842 					}
       
  1843 				} );
       
  1844 
       
  1845 				if ( instanceCounter <= 1 ) {
       
  1846 					// Revert the check icon to add icon.
       
  1847 					availableMenuItem = $( '#menu-item-tpl-' + control.params.original_item_id );
       
  1848 					availableMenuItem.removeClass( 'selected' );
       
  1849 					availableMenuItem.find( '.menu-item-handle' ).removeClass( 'item-added' );
       
  1850 				}
       
  1851 
  1811 				control.container.slideUp( function() {
  1852 				control.container.slideUp( function() {
  1812 					control.setting.set( false );
  1853 					control.setting.set( false );
  1813 					wp.a11y.speak( api.Menus.data.l10n.itemDeleted );
  1854 					wp.a11y.speak( api.Menus.data.l10n.itemDeleted );
  1814 					$adjacentFocusTarget.focus(); // keyboard accessibility
  1855 					$adjacentFocusTarget.focus(); // Keyboard accessibility.
  1815 				} );
  1856 				} );
  1816 
  1857 
  1817 				control.setting.set( false );
  1858 				control.setting.set( false );
  1818 			} );
  1859 			} );
  1819 		},
  1860 		},
  1868 			} );
  1909 			} );
  1869 		},
  1910 		},
  1870 
  1911 
  1871 		/**
  1912 		/**
  1872 		 *
  1913 		 *
  1873 		 * @returns {number}
  1914 		 * @return {number}
  1874 		 */
  1915 		 */
  1875 		getDepth: function() {
  1916 		getDepth: function() {
  1876 			var control = this, setting = control.setting(), depth = 0;
  1917 			var control = this, setting = control.setting(), depth = 0;
  1877 			if ( ! setting ) {
  1918 			if ( ! setting ) {
  1878 				return 0;
  1919 				return 0;
  1919 			control.params.item_type = settingValue.type;
  1960 			control.params.item_type = settingValue.type;
  1920 			control.params.url = settingValue.url;
  1961 			control.params.url = settingValue.url;
  1921 			control.params.target = settingValue.target;
  1962 			control.params.target = settingValue.target;
  1922 			control.params.attr_title = settingValue.attr_title;
  1963 			control.params.attr_title = settingValue.attr_title;
  1923 			control.params.classes = _.isArray( settingValue.classes ) ? settingValue.classes.join( ' ' ) : settingValue.classes;
  1964 			control.params.classes = _.isArray( settingValue.classes ) ? settingValue.classes.join( ' ' ) : settingValue.classes;
  1924 			control.params.attr_title = settingValue.attr_title;
       
  1925 			control.params.xfn = settingValue.xfn;
  1965 			control.params.xfn = settingValue.xfn;
  1926 			control.params.description = settingValue.description;
  1966 			control.params.description = settingValue.description;
  1927 			control.params.parent = settingValue.menu_item_parent;
  1967 			control.params.parent = settingValue.menu_item_parent;
  1928 			control.params.original_title = settingValue.original_title || '';
  1968 			control.params.original_title = settingValue.original_title || '';
  1929 
  1969 
  1961 		/**
  2001 		/**
  1962 		 * @since 4.6.0
  2002 		 * @since 4.6.0
  1963 		 *
  2003 		 *
  1964 		 * @param {Boolean} expanded
  2004 		 * @param {Boolean} expanded
  1965 		 * @param {Object} [params]
  2005 		 * @param {Object} [params]
  1966 		 * @returns {Boolean} false if state already applied
  2006 		 * @return {Boolean} False if state already applied.
  1967 		 */
  2007 		 */
  1968 		_toggleExpanded: api.Section.prototype._toggleExpanded,
  2008 		_toggleExpanded: api.Section.prototype._toggleExpanded,
  1969 
  2009 
  1970 		/**
  2010 		/**
  1971 		 * @since 4.6.0
  2011 		 * @since 4.6.0
  1972 		 *
  2012 		 *
  1973 		 * @param {Object} [params]
  2013 		 * @param {Object} [params]
  1974 		 * @returns {Boolean} false if already expanded
  2014 		 * @return {Boolean} False if already expanded.
  1975 		 */
  2015 		 */
  1976 		expand: api.Section.prototype.expand,
  2016 		expand: api.Section.prototype.expand,
  1977 
  2017 
  1978 		/**
  2018 		/**
  1979 		 * Expand the menu item form control.
  2019 		 * Expand the menu item form control.
  1989 
  2029 
  1990 		/**
  2030 		/**
  1991 		 * @since 4.6.0
  2031 		 * @since 4.6.0
  1992 		 *
  2032 		 *
  1993 		 * @param {Object} [params]
  2033 		 * @param {Object} [params]
  1994 		 * @returns {Boolean} false if already collapsed
  2034 		 * @return {Boolean} False if already collapsed.
  1995 		 */
  2035 		 */
  1996 		collapse: api.Section.prototype.collapse,
  2036 		collapse: api.Section.prototype.collapse,
  1997 
  2037 
  1998 		/**
  2038 		/**
  1999 		 * Collapse the menu item form control.
  2039 		 * Collapse the menu item form control.
  2170 		 * Note that this will trigger a UI update, causing child items to
  2210 		 * Note that this will trigger a UI update, causing child items to
  2171 		 * move as well and cardinal order class names to be updated.
  2211 		 * move as well and cardinal order class names to be updated.
  2172 		 *
  2212 		 *
  2173 		 * @private
  2213 		 * @private
  2174 		 *
  2214 		 *
  2175 		 * @param {Number} offset 1|-1
  2215 		 * @param {number} offset 1|-1
  2176 		 */
  2216 		 */
  2177 		_changePosition: function( offset ) {
  2217 		_changePosition: function( offset ) {
  2178 			var control = this,
  2218 			var control = this,
  2179 				adjacentSetting,
  2219 				adjacentSetting,
  2180 				settingValue = _.clone( control.setting() ),
  2220 				settingValue = _.clone( control.setting() ),
  2230 		 * Note that this will trigger a UI update, causing child items to
  2270 		 * Note that this will trigger a UI update, causing child items to
  2231 		 * move as well and cardinal order class names to be updated.
  2271 		 * move as well and cardinal order class names to be updated.
  2232 		 *
  2272 		 *
  2233 		 * @private
  2273 		 * @private
  2234 		 *
  2274 		 *
  2235 		 * @param {Number} offset 1|-1
  2275 		 * @param {number} offset 1|-1
  2236 		 */
  2276 		 */
  2237 		_changeDepth: function( offset ) {
  2277 		_changeDepth: function( offset ) {
  2238 			if ( 1 !== offset && -1 !== offset ) {
  2278 			if ( 1 !== offset && -1 !== offset ) {
  2239 				throw new Error( 'Offset changes by 1 are only supported.' );
  2279 				throw new Error( 'Offset changes by 1 are only supported.' );
  2240 			}
  2280 			}
  2427 		 * set the default location for a new menu.
  2467 		 * set the default location for a new menu.
  2428 		 *
  2468 		 *
  2429 		 * @since 4.9.0
  2469 		 * @since 4.9.0
  2430 		 *
  2470 		 *
  2431 		 * @param {Object.<string,boolean>} selections - A map of location selections.
  2471 		 * @param {Object.<string,boolean>} selections - A map of location selections.
  2432 		 * @returns {void}
  2472 		 * @return {void}
  2433 		 */
  2473 		 */
  2434 		setSelections: function( selections ) {
  2474 		setSelections: function( selections ) {
  2435 			this.container.find( '.menu-location' ).each( function( i, checkboxNode ) {
  2475 			this.container.find( '.menu-location' ).each( function( i, checkboxNode ) {
  2436 				var locationId = checkboxNode.dataset.locationId;
  2476 				var locationId = checkboxNode.dataset.locationId;
  2437 				checkboxNode.checked = locationId in selections ? selections[ locationId ] : false;
  2477 				checkboxNode.checked = locationId in selections ? selections[ locationId ] : false;
  2613 		 * Allow items in each menu to be re-ordered, and for the order to be previewed.
  2653 		 * Allow items in each menu to be re-ordered, and for the order to be previewed.
  2614 		 *
  2654 		 *
  2615 		 * Notice that the UI aspects here are handled by wpNavMenu.initSortables()
  2655 		 * Notice that the UI aspects here are handled by wpNavMenu.initSortables()
  2616 		 * which is called in MenuSection.onChangeExpanded()
  2656 		 * which is called in MenuSection.onChangeExpanded()
  2617 		 *
  2657 		 *
  2618 		 * @param {object} menuList - The element that has sortable().
  2658 		 * @param {Object} menuList - The element that has sortable().
  2619 		 */
  2659 		 */
  2620 		_setupSortable: function( menuList ) {
  2660 		_setupSortable: function( menuList ) {
  2621 			var control = this;
  2661 			var control = this;
  2622 
  2662 
  2623 			if ( ! menuList.is( control.$sectionContent ) ) {
  2663 			if ( ! menuList.is( control.$sectionContent ) ) {
  2777 					sectionTitle = section.contentContainer.find( '.customize-section-title h3' ),
  2817 					sectionTitle = section.contentContainer.find( '.customize-section-title h3' ),
  2778 					location = section.headContainer.find( '.menu-in-location' ),
  2818 					location = section.headContainer.find( '.menu-in-location' ),
  2779 					action = sectionTitle.find( '.customize-action' ),
  2819 					action = sectionTitle.find( '.customize-action' ),
  2780 					name = displayNavMenuName( menu.name );
  2820 					name = displayNavMenuName( menu.name );
  2781 
  2821 
  2782 				// Update the control title
  2822 				// Update the control title.
  2783 				controlTitle.text( name );
  2823 				controlTitle.text( name );
  2784 				if ( location.length ) {
  2824 				if ( location.length ) {
  2785 					location.appendTo( controlTitle );
  2825 					location.appendTo( controlTitle );
  2786 				}
  2826 				}
  2787 
  2827 
  2788 				// Update the section title
  2828 				// Update the section title.
  2789 				sectionTitle.text( name );
  2829 				sectionTitle.text( name );
  2790 				if ( action.length ) {
  2830 				if ( action.length ) {
  2791 					action.prependTo( sectionTitle );
  2831 					action.prependTo( sectionTitle );
  2792 				}
  2832 				}
  2793 
  2833 
  2812 		 **********************************************************************/
  2852 		 **********************************************************************/
  2813 
  2853 
  2814 		/**
  2854 		/**
  2815 		 * Enable/disable the reordering UI
  2855 		 * Enable/disable the reordering UI
  2816 		 *
  2856 		 *
  2817 		 * @param {Boolean} showOrHide to enable/disable reordering
  2857 		 * @param {boolean} showOrHide to enable/disable reordering
  2818 		 */
  2858 		 */
  2819 		toggleReordering: function( showOrHide ) {
  2859 		toggleReordering: function( showOrHide ) {
  2820 			var addNewItemBtn = this.container.find( '.add-new-menu-item' ),
  2860 			var addNewItemBtn = this.container.find( '.add-new-menu-item' ),
  2821 				reorderBtn = this.container.find( '.reorder-toggle' ),
  2861 				reorderBtn = this.container.find( '.reorder-toggle' ),
  2822 				itemsTitle = this.$sectionContent.find( '.item-title' );
  2862 				itemsTitle = this.$sectionContent.find( '.item-title' );
  2952 		}, 0 ),
  2992 		}, 0 ),
  2953 
  2993 
  2954 		/**
  2994 		/**
  2955 		 * Add a new item to this menu.
  2995 		 * Add a new item to this menu.
  2956 		 *
  2996 		 *
  2957 		 * @param {object} item - Value for the nav_menu_item setting to be created.
  2997 		 * @param {Object} item - Value for the nav_menu_item setting to be created.
  2958 		 * @returns {wp.customize.Menus.controlConstructor.nav_menu_item} The newly-created nav_menu_item control instance.
  2998 		 * @return {wp.customize.Menus.controlConstructor.nav_menu_item} The newly-created nav_menu_item control instance.
  2959 		 */
  2999 		 */
  2960 		addItemToMenu: function( item ) {
  3000 		addItemToMenu: function( item ) {
  2961 			var menuControl = this, customizeId, settingArgs, setting, menuItemControl, placeholderId, position = 0, priority = 10;
  3001 			var menuControl = this, customizeId, settingArgs, setting, menuItemControl, placeholderId, position = 0, priority = 10,
       
  3002 				originalItemId = item.id || '';
  2962 
  3003 
  2963 			_.each( menuControl.getMenuItemControls(), function( control ) {
  3004 			_.each( menuControl.getMenuItemControls(), function( control ) {
  2964 				if ( false === control.setting() ) {
  3005 				if ( false === control.setting() ) {
  2965 					return;
  3006 					return;
  2966 				}
  3007 				}
  2980 					nav_menu_term_id: menuControl.params.menu_id,
  3021 					nav_menu_term_id: menuControl.params.menu_id,
  2981 					original_title: item.title,
  3022 					original_title: item.title,
  2982 					position: position
  3023 					position: position
  2983 				}
  3024 				}
  2984 			);
  3025 			);
  2985 			delete item.id; // only used by Backbone
  3026 			delete item.id; // Only used by Backbone.
  2986 
  3027 
  2987 			placeholderId = api.Menus.generatePlaceholderAutoIncrementId();
  3028 			placeholderId = api.Menus.generatePlaceholderAutoIncrementId();
  2988 			customizeId = 'nav_menu_item[' + String( placeholderId ) + ']';
  3029 			customizeId = 'nav_menu_item[' + String( placeholderId ) + ']';
  2989 			settingArgs = {
  3030 			settingArgs = {
  2990 				type: 'nav_menu_item',
  3031 				type: 'nav_menu_item',
  3000 				section: menuControl.id,
  3041 				section: menuControl.id,
  3001 				priority: priority,
  3042 				priority: priority,
  3002 				settings: {
  3043 				settings: {
  3003 					'default': customizeId
  3044 					'default': customizeId
  3004 				},
  3045 				},
  3005 				menu_item_id: placeholderId
  3046 				menu_item_id: placeholderId,
       
  3047 				original_item_id: originalItemId
  3006 			} );
  3048 			} );
  3007 
  3049 
  3008 			api.control.add( menuItemControl );
  3050 			api.control.add( menuItemControl );
  3009 			setting.preview();
  3051 			setting.preview();
  3010 			menuControl.debouncedReflowMenuItems();
  3052 			menuControl.debouncedReflowMenuItems();
  3025 			var menuItemControls = optionalMenuItemControls || this.getMenuItemControls();
  3067 			var menuItemControls = optionalMenuItemControls || this.getMenuItemControls();
  3026 
  3068 
  3027 			this.container.find( '.new-menu-item-invitation' ).toggle( menuItemControls.length === 0 );
  3069 			this.container.find( '.new-menu-item-invitation' ).toggle( menuItemControls.length === 0 );
  3028 		}
  3070 		}
  3029 	} );
  3071 	} );
  3030 
       
  3031 	api.Menus.NewMenuControl = api.Control.extend(/** @lends wp.customize.Menus.NewMenuControl.prototype */{
       
  3032 
       
  3033 		/**
       
  3034 		 * wp.customize.Menus.NewMenuControl
       
  3035 		 *
       
  3036 		 * Customizer control for creating new menus and handling deletion of existing menus.
       
  3037 		 * Note that 'new_menu' must match the WP_Customize_New_Menu_Control::$type.
       
  3038 		 *
       
  3039 		 * @constructs wp.customize.Menus.NewMenuControl
       
  3040 		 * @augments   wp.customize.Control
       
  3041 		 *
       
  3042 		 * @deprecated 4.9.0 This class is no longer used due to new menu creation UX.
       
  3043 		 */
       
  3044 		initialize: function() {
       
  3045 			if ( 'undefined' !== typeof console && console.warn ) {
       
  3046 				console.warn( '[DEPRECATED] wp.customize.NewMenuControl will be removed. Please use wp.customize.Menus.createNavMenu() instead.' );
       
  3047 			}
       
  3048 			api.Control.prototype.initialize.apply( this, arguments );
       
  3049 		},
       
  3050 
       
  3051 		/**
       
  3052 		 * Set up the control.
       
  3053 		 *
       
  3054 		 * @deprecated 4.9.0
       
  3055 		 */
       
  3056 		ready: function() {
       
  3057 			this._bindHandlers();
       
  3058 		},
       
  3059 
       
  3060 		_bindHandlers: function() {
       
  3061 			var self = this,
       
  3062 				name = $( '#customize-control-new_menu_name input' ),
       
  3063 				submit = $( '#create-new-menu-submit' );
       
  3064 			name.on( 'keydown', function( event ) {
       
  3065 				if ( 13 === event.which ) { // Enter.
       
  3066 					self.submit();
       
  3067 				}
       
  3068 			} );
       
  3069 			submit.on( 'click', function( event ) {
       
  3070 				self.submit();
       
  3071 				event.stopPropagation();
       
  3072 				event.preventDefault();
       
  3073 			} );
       
  3074 		},
       
  3075 
       
  3076 		/**
       
  3077 		 * Create the new menu with the name supplied.
       
  3078 		 *
       
  3079 		 * @deprecated 4.9.0
       
  3080 		 */
       
  3081 		submit: function() {
       
  3082 
       
  3083 			var control = this,
       
  3084 				container = control.container.closest( '.accordion-section-new-menu' ),
       
  3085 				nameInput = container.find( '.menu-name-field' ).first(),
       
  3086 				name = nameInput.val(),
       
  3087 				menuSection;
       
  3088 
       
  3089 			if ( ! name ) {
       
  3090 				nameInput.addClass( 'invalid' );
       
  3091 				nameInput.focus();
       
  3092 				return;
       
  3093 			}
       
  3094 
       
  3095 			menuSection = api.Menus.createNavMenu( name );
       
  3096 
       
  3097 			// Clear name field.
       
  3098 			nameInput.val( '' );
       
  3099 			nameInput.removeClass( 'invalid' );
       
  3100 
       
  3101 			wp.a11y.speak( api.Menus.data.l10n.menuAdded );
       
  3102 
       
  3103 			// Focus on the new menu section.
       
  3104 			menuSection.focus();
       
  3105 		}
       
  3106 	});
       
  3107 
  3072 
  3108 	/**
  3073 	/**
  3109 	 * Extends wp.customize.controlConstructor with control constructor for
  3074 	 * Extends wp.customize.controlConstructor with control constructor for
  3110 	 * menu_location, menu_item, nav_menu, and new_menu.
  3075 	 * menu_location, menu_item, nav_menu, and new_menu.
  3111 	 */
  3076 	 */
  3112 	$.extend( api.controlConstructor, {
  3077 	$.extend( api.controlConstructor, {
  3113 		nav_menu_location: api.Menus.MenuLocationControl,
  3078 		nav_menu_location: api.Menus.MenuLocationControl,
  3114 		nav_menu_item: api.Menus.MenuItemControl,
  3079 		nav_menu_item: api.Menus.MenuItemControl,
  3115 		nav_menu: api.Menus.MenuControl,
  3080 		nav_menu: api.Menus.MenuControl,
  3116 		nav_menu_name: api.Menus.MenuNameControl,
  3081 		nav_menu_name: api.Menus.MenuNameControl,
  3117 		new_menu: api.Menus.NewMenuControl, // @todo Remove in a future release. See #42364.
       
  3118 		nav_menu_locations: api.Menus.MenuLocationsControl,
  3082 		nav_menu_locations: api.Menus.MenuLocationsControl,
  3119 		nav_menu_auto_add: api.Menus.MenuAutoAddControl
  3083 		nav_menu_auto_add: api.Menus.MenuAutoAddControl
  3120 	});
  3084 	});
  3121 
  3085 
  3122 	/**
  3086 	/**
  3169 	 * When customize_save comes back with a success, make sure any inserted
  3133 	 * When customize_save comes back with a success, make sure any inserted
  3170 	 * nav menus and items are properly re-added with their newly-assigned IDs.
  3134 	 * nav menus and items are properly re-added with their newly-assigned IDs.
  3171 	 *
  3135 	 *
  3172 	 * @alias wp.customize.Menus.applySavedData
  3136 	 * @alias wp.customize.Menus.applySavedData
  3173 	 *
  3137 	 *
  3174 	 * @param {object} data
  3138 	 * @param {Object} data
  3175 	 * @param {array} data.nav_menu_updates
  3139 	 * @param {Array} data.nav_menu_updates
  3176 	 * @param {array} data.nav_menu_item_updates
  3140 	 * @param {Array} data.nav_menu_item_updates
  3177 	 */
  3141 	 */
  3178 	api.Menus.applySavedData = function( data ) {
  3142 	api.Menus.applySavedData = function( data ) {
  3179 
  3143 
  3180 		var insertedMenuIdMapping = {}, insertedMenuItemIdMapping = {};
  3144 		var insertedMenuIdMapping = {}, insertedMenuItemIdMapping = {};
  3181 
  3145 
  3428 	 * Given a menu item ID, get the control associated with it.
  3392 	 * Given a menu item ID, get the control associated with it.
  3429 	 *
  3393 	 *
  3430 	 * @alias wp.customize.Menus.getMenuItemControl
  3394 	 * @alias wp.customize.Menus.getMenuItemControl
  3431 	 *
  3395 	 *
  3432 	 * @param {string} menuItemId
  3396 	 * @param {string} menuItemId
  3433 	 * @return {object|null}
  3397 	 * @return {Object|null}
  3434 	 */
  3398 	 */
  3435 	api.Menus.getMenuItemControl = function( menuItemId ) {
  3399 	api.Menus.getMenuItemControl = function( menuItemId ) {
  3436 		return api.control( menuItemIdToSettingId( menuItemId ) );
  3400 		return api.control( menuItemIdToSettingId( menuItemId ) );
  3437 	};
  3401 	};
  3438 
  3402 
  3439 	/**
  3403 	/**
  3440 	 * @alias wp.customize.Menus~menuItemIdToSettingId
  3404 	 * @alias wp.customize.Menus~menuItemIdToSettingId
  3441 	 *
  3405 	 *
  3442 	 * @param {String} menuItemId
  3406 	 * @param {string} menuItemId
  3443 	 */
  3407 	 */
  3444 	function menuItemIdToSettingId( menuItemId ) {
  3408 	function menuItemIdToSettingId( menuItemId ) {
  3445 		return 'nav_menu_item[' + menuItemId + ']';
  3409 		return 'nav_menu_item[' + menuItemId + ']';
  3446 	}
  3410 	}
  3447 
  3411 
  3450 	 * "unnammed" fallback string if the name is then empty.
  3414 	 * "unnammed" fallback string if the name is then empty.
  3451 	 *
  3415 	 *
  3452 	 * @alias wp.customize.Menus~displayNavMenuName
  3416 	 * @alias wp.customize.Menus~displayNavMenuName
  3453 	 *
  3417 	 *
  3454 	 * @param {string} name
  3418 	 * @param {string} name
  3455 	 * @returns {string}
  3419 	 * @return {string}
  3456 	 */
  3420 	 */
  3457 	function displayNavMenuName( name ) {
  3421 	function displayNavMenuName( name ) {
  3458 		name = name || '';
  3422 		name = name || '';
  3459 		name = wp.sanitize.stripTagsAndEncodeText( name ); // Remove any potential tags from name.
  3423 		name = wp.sanitize.stripTagsAndEncodeText( name ); // Remove any potential tags from name.
  3460 		name = $.trim( name );
  3424 		name = $.trim( name );