: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
// Only proceed with keypress if it is Enter or Spacebar.
if ( 'keypress' === event.type && ( 13 !== event.which && 32 !== event.which ) ) {
this.submit( $( event.currentTarget ) );
// Adds a selected menu item to the menu.
submit: function( menuitemTpl ) {
var menuitemId, menu_item;
menuitemTpl = this.selected;
if ( ! menuitemTpl || ! this.currentMenuControl ) {
this.select( menuitemTpl );
menuitemId = $( this.selected ).data( 'menu-item-id' );
menu_item = this.collection.findWhere( { id: menuitemId } );
// Leave the title as empty to reuse the original title as a placeholder if set.
var nav_menu_item = Object.assign( {}, menu_item.attributes );
if ( nav_menu_item.title === nav_menu_item.original_title ) {
nav_menu_item.title = '';
this.currentMenuControl.addItemToMenu( nav_menu_item );
$( menuitemTpl ).find( '.menu-item-handle' ).addClass( 'item-added' );
// Submit handler for keypress and click on custom menu item.
_submitLink: function( event ) {
// Only proceed with keypress if it is Enter.
if ( 'keypress' === event.type && 13 !== event.which ) {
// Adds the custom menu item to the menu.
itemName = $( '#custom-menu-item-name' ),
itemUrl = $( '#custom-menu-item-url' ),
url = itemUrl.val().trim(),
if ( ! this.currentMenuControl ) {
* - mailto:foo@example.com
* Any further validation will be handled on the server when the setting is attempted to be saved,
* so this pattern does not need to be complete.
urlRegex = /^((\w+:)?\/\/\w.*|\w+:(?!\/\/$)|\/|\?|#)/;
if ( '' === itemName.val() ) {
itemName.addClass( 'invalid' );
} else if ( ! urlRegex.test( url ) ) {
itemUrl.addClass( 'invalid' );
'type_label': api.Menus.data.l10n.custom_label,
this.currentMenuControl.addItemToMenu( menuItem );
// Reset the custom link form.
itemUrl.val( '' ).attr( 'placeholder', 'https://' );
* Submit handler for keypress (enter) on field and click on button.
* @param {jQuery.Event} event Event.
_submitNew: function( event ) {
// Only proceed with keypress if it is Enter.
if ( 'keypress' === event.type && 13 !== event.which ) {
container = $( event.target ).closest( '.accordion-section' );
this.submitNew( container );
* Creates a new object and adds an associated menu item to the menu.
* @param {jQuery} container
submitNew: function( container ) {
itemName = container.find( '.create-item-input' ),
dataContainer = container.find( '.available-menu-items-list' ),
itemType = dataContainer.data( 'type' ),
itemObject = dataContainer.data( 'object' ),
itemTypeLabel = dataContainer.data( 'type_label' ),
if ( ! this.currentMenuControl ) {
// Only posts are supported currently.
if ( 'post_type' !== itemType ) {
if ( '' === itemName.val().trim() ) {
itemName.addClass( 'invalid' );
itemName.removeClass( 'invalid' );
container.find( '.accordion-section-title' ).addClass( 'loading' );
itemName.attr( 'disabled', 'disabled' );
promise = api.Menus.insertAutoDraftPost( {
promise.done( function( data ) {
var availableItem, $content, itemElement;
availableItem = new api.Menus.AvailableItemModel( {
'id': 'post-' + data.post_id, // Used for available menu item Backbone models.
'type_label': itemTypeLabel,
'object_id': data.post_id,
panel.currentMenuControl.addItemToMenu( availableItem.attributes );
// Add the new item to the list of available items.
api.Menus.availableMenuItemsPanel.collection.add( availableItem );
$content = container.find( '.available-menu-items-list' );
itemElement = $( wp.template( 'available-menu-item' )( availableItem.attributes ) );
itemElement.find( '.menu-item-handle:first' ).addClass( 'item-added' );
$content.prepend( itemElement );
// Reset the create content form.
itemName.val( '' ).removeAttr( 'disabled' );
container.find( '.accordion-section-title' ).removeClass( 'loading' );
open: function( menuControl ) {
this.currentMenuControl = menuControl;
this.itemSectionHeight();
if ( api.section.has( 'publish_settings' ) ) {
api.section( 'publish_settings' ).collapse();
$( 'body' ).addClass( 'adding-menu-items' );
$( this ).off( 'click', close );
$( '#customize-preview' ).on( 'click', close );
// Collapse all controls.
_( this.currentMenuControl.getMenuItemControls() ).each( function( control ) {
this.$el.find( '.selected' ).removeClass( 'selected' );
this.$search.trigger( 'focus' );
close: function( options ) {
if ( options.returnFocus && this.currentMenuControl ) {
this.currentMenuControl.container.find( '.add-new-menu-item' ).focus();
this.currentMenuControl = null;
$( 'body' ).removeClass( 'adding-menu-items' );
$( '#available-menu-items .menu-item-handle.item-added' ).removeClass( 'item-added' );
this.$search.val( '' ).trigger( 'input' );
// Add a few keyboard enhancements to the panel.
keyboardAccessible: function( event ) {
var isEnter = ( 13 === event.which ),
isEsc = ( 27 === event.which ),
isBackTab = ( 9 === event.which && event.shiftKey ),
isSearchFocused = $( event.target ).is( this.$search );
// If enter pressed but nothing entered, don't do anything.
if ( isEnter && ! this.$search.val() ) {
if ( isSearchFocused && isBackTab ) {
this.currentMenuControl.container.find( '.add-new-menu-item' ).focus();
event.preventDefault(); // Avoid additional back-tab.
this.close( { returnFocus: true } );
* wp.customize.Menus.MenusPanel
* Customizer panel for menus. This is used only for screen options management.
* Note that 'menus' must match the WP_Customize_Menu_Panel::$type.
* @class wp.customize.Menus.MenusPanel
* @augments wp.customize.Panel
api.Menus.MenusPanel = api.Panel.extend(/** @lends wp.customize.Menus.MenusPanel.prototype */{
attachEvents: function() {
api.Panel.prototype.attachEvents.call( this );
panelMeta = panel.container.find( '.panel-meta' ),
help = panelMeta.find( '.customize-help-toggle' ),
content = panelMeta.find( '.customize-panel-description' ),
options = $( '#screen-options-wrap' ),
button = panelMeta.find( '.customize-screen-options-toggle' );
button.on( 'click keydown', function( event ) {
if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
if ( content.not( ':hidden' ) ) {
content.slideUp( 'fast' );
help.attr( 'aria-expanded', 'false' );
if ( 'true' === button.attr( 'aria-expanded' ) ) {
button.attr( 'aria-expanded', 'false' );
panelMeta.removeClass( 'open' );
panelMeta.removeClass( 'active-menu-screen-options' );
options.slideUp( 'fast' );
button.attr( 'aria-expanded', 'true' );
panelMeta.addClass( 'open' );
panelMeta.addClass( 'active-menu-screen-options' );
options.slideDown( 'fast' );
help.on( 'click keydown', function( event ) {
if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
if ( 'true' === button.attr( 'aria-expanded' ) ) {
button.attr( 'aria-expanded', 'false' );
help.attr( 'aria-expanded', 'true' );
panelMeta.addClass( 'open' );
panelMeta.removeClass( 'active-menu-screen-options' );
options.slideUp( 'fast' );
content.slideDown( 'fast' );
* Update field visibility when clicking on the field toggles.
panel.container.find( '.hide-column-tog' ).on( 'click', function() {
panel.saveManageColumnsState();
// Inject additional heading into the menu locations section's head container.
api.section( 'menu_locations', function( section ) {
section.headContainer.prepend(
wp.template( 'nav-menu-locations-header' )( api.Menus.data )
* Save hidden column states.
saveManageColumnsState: _.debounce( function() {
if ( panel._updateHiddenColumnsRequest ) {
panel._updateHiddenColumnsRequest.abort();
panel._updateHiddenColumnsRequest = wp.ajax.post( 'hidden-columns', {
screenoptionnonce: $( '#screenoptionnonce' ).val(),
panel._updateHiddenColumnsRequest.always( function() {
panel._updateHiddenColumnsRequest = null;
* @deprecated Since 4.7.0 now that the nav_menu sections are responsible for toggling the classes on their own containers.
* @deprecated Since 4.7.0 now that the nav_menu sections are responsible for toggling the classes on their own containers.
unchecked: function() {},
* @return {Array} Fields (columns) that are hidden.
return $( '.hide-column-tog' ).not( ':checked' ).map( function() {
return id.substring( 0, id.length - 5 );
* wp.customize.Menus.MenuSection
* Customizer section for menus. This is used only for lazy-loading child controls.
* Note that 'nav_menu' must match the WP_Customize_Menu_Section::$type.
* @class wp.customize.Menus.MenuSection
* @augments wp.customize.Section
api.Menus.MenuSection = api.Section.extend(/** @lends wp.customize.Menus.MenuSection.prototype */{
* @param {Object} options
initialize: function( id, options ) {
api.Section.prototype.initialize.call( section, id, options );
section.deferred.initSortables = $.Deferred();
var section = this, fieldActiveToggles, handleFieldActiveToggle;
if ( 'undefined' === typeof section.params.menu_id ) {
throw new Error( 'params.menu_id was not defined' );
* Since newly created sections won't be registered in PHP, we need to prevent the
* preview's sending of the activeSections to result in this control
* being deactivated when the preview refreshes. So we can hook onto
* the setting that has the same ID and its presence can dictate
* whether the section is active.
section.active.validate = function() {
if ( ! api.has( section.id ) ) {
return !! api( section.id ).get();
section.populateControls();
section.navMenuLocationSettings = {};
section.assignedLocations = new api.Value( [] );
api.each(function( setting, id ) {
var matches = id.match( /^nav_menu_locations\[(.+?)]/ );
section.navMenuLocationSettings[ matches[1] ] = setting;
setting.bind( function() {
section.refreshAssignedLocations();
section.assignedLocations.bind(function( to ) {
section.updateAssignedLocationsInSectionTitle( to );
section.refreshAssignedLocations();
api.bind( 'pane-contents-reflowed', function() {
// Skip menus that have been removed.
if ( ! section.contentContainer.parent().length ) {
section.container.find( '.menu-item .menu-item-reorder-nav button' ).attr({ 'tabindex': '0', 'aria-hidden': 'false' });
section.container.find( '.menu-item.move-up-disabled .menus-move-up' ).attr({ 'tabindex': '-1', 'aria-hidden': 'true' });
section.container.find( '.menu-item.move-down-disabled .menus-move-down' ).attr({ 'tabindex': '-1', 'aria-hidden': 'true' });
section.container.find( '.menu-item.move-left-disabled .menus-move-left' ).attr({ 'tabindex': '-1', 'aria-hidden': 'true' });
section.container.find( '.menu-item.move-right-disabled .menus-move-right' ).attr({ 'tabindex': '-1', 'aria-hidden': 'true' });
* Update the active field class for the content container for a given checkbox toggle.
handleFieldActiveToggle = function() {
var className = 'field-' + $( this ).val() + '-active';
section.contentContainer.toggleClass( className, $( this ).prop( 'checked' ) );
fieldActiveToggles = api.panel( 'nav_menus' ).contentContainer.find( '.metabox-prefs:first' ).find( '.hide-column-tog' );
fieldActiveToggles.each( handleFieldActiveToggle );
fieldActiveToggles.on( 'click', handleFieldActiveToggle );
populateControls: function() {