/* global _wpCustomizeNavMenusSettings, wpNavMenu, console */
( function( api, wp, $ ) {
'use strict';
/**
* Set up wpNavMenu for drag and drop.
*/
wpNavMenu.originalInit = wpNavMenu.init;
wpNavMenu.options.menuItemDepthPerLevel = 20;
wpNavMenu.options.sortableItems = '> .customize-control-nav_menu_item';
wpNavMenu.options.targetTolerance = 10;
wpNavMenu.init = function() {
this.jQueryExtensions();
};
api.Menus = api.Menus || {};
// Link settings.
api.Menus.data = {
itemTypes: [],
l10n: {},
settingTransport: 'refresh',
phpIntMax: 0,
defaultSettingValues: {
nav_menu: {},
nav_menu_item: {}
},
locationSlugMappedToName: {}
};
if ( 'undefined' !== typeof _wpCustomizeNavMenusSettings ) {
$.extend( api.Menus.data, _wpCustomizeNavMenusSettings );
}
/**
* Newly-created Nav Menus and Nav Menu Items have negative integer IDs which
* serve as placeholders until Save & Publish happens.
*
* @return {number}
*/
api.Menus.generatePlaceholderAutoIncrementId = function() {
return -Math.ceil( api.Menus.data.phpIntMax * Math.random() );
};
/**
* wp.customize.Menus.AvailableItemModel
*
* A single available menu item model. See PHP's WP_Customize_Nav_Menu_Item_Setting class.
*
* @constructor
* @augments Backbone.Model
*/
api.Menus.AvailableItemModel = Backbone.Model.extend( $.extend(
{
id: null // This is only used by Backbone.
},
api.Menus.data.defaultSettingValues.nav_menu_item
) );
/**
* wp.customize.Menus.AvailableItemCollection
*
* Collection for available menu item models.
*
* @constructor
* @augments Backbone.Model
*/
api.Menus.AvailableItemCollection = Backbone.Collection.extend({
model: api.Menus.AvailableItemModel,
sort_key: 'order',
comparator: function( item ) {
return -item.get( this.sort_key );
},
sortByField: function( fieldName ) {
this.sort_key = fieldName;
this.sort();
}
});
api.Menus.availableMenuItems = new api.Menus.AvailableItemCollection( api.Menus.data.availableMenuItems );
api.Menus.insertedAutoDrafts = [];
/**
* Insert a new `auto-draft` post.
*
* @param {object} params - Parameters for the draft post to create.
* @param {string} params.post_type - Post type to add.
* @param {string} params.post_title - Post title to use.
* @return {jQuery.promise} Promise resolved with the added post.
*/
api.Menus.insertAutoDraftPost = function insertAutoDraftPost( params ) {
var request, deferred = $.Deferred();
request = wp.ajax.post( 'customize-nav-menus-insert-auto-draft', {
'customize-menus-nonce': api.settings.nonce['customize-menus'],
'wp_customize': 'on',
'params': params
} );
request.done( function( response ) {
if ( response.post_id ) {
deferred.resolve( response );
api.Menus.insertedAutoDrafts.push( response.post_id );
api( 'nav_menus_created_posts' ).set( _.clone( api.Menus.insertedAutoDrafts ) );
if ( 'page' === params.post_type ) {
// Activate static front page controls as this could be the first page created.
if ( api.section.has( 'static_front_page' ) ) {
api.section( 'static_front_page' ).activate();
}
// Add new page to dropdown-pages controls.
api.control.each( function( control ) {
var select;
if ( 'dropdown-pages' === control.params.type ) {
select = control.container.find( 'select[name^="_customize-dropdown-pages-"]' );
select.append( new Option( params.post_title, response.post_id ) );
}
} );
}
}
} );
request.fail( function( response ) {
var error = response || '';
if ( 'undefined' !== typeof response.message ) {
error = response.message;
}
console.error( error );
deferred.rejectWith( error );
} );
return deferred.promise();
};
/**
* wp.customize.Menus.AvailableMenuItemsPanelView
*
* View class for the available menu items panel.
*
* @constructor
* @augments wp.Backbone.View
* @augments Backbone.View
*/
api.Menus.AvailableMenuItemsPanelView = wp.Backbone.View.extend({
el: '#available-menu-items',
events: {
'input #menu-items-search': 'debounceSearch',
'keyup #menu-items-search': 'debounceSearch',
'focus .menu-item-tpl': 'focus',
'click .menu-item-tpl': '_submit',
'click #custom-menu-item-submit': '_submitLink',
'keypress #custom-menu-item-name': '_submitLink',
'click .new-content-item .add-content': '_submitNew',
'keypress .create-item-input': '_submitNew',
'keydown': 'keyboardAccessible'
},
// Cache current selected menu item.
selected: null,
// Cache menu control that opened the panel.
currentMenuControl: null,
debounceSearch: null,
$search: null,
searchTerm: '',
rendered: false,
pages: {},
sectionContent: '',
loading: false,
addingNew: false,
initialize: function() {
var self = this;
if ( ! api.panel.has( 'nav_menus' ) ) {
return;
}
this.$search = $( '#menu-items-search' );
this.sectionContent = this.$el.find( '.available-menu-items-list' );
this.debounceSearch = _.debounce( self.search, 500 );
_.bindAll( this, 'close' );
// If the available menu items panel is open and the customize controls are
// interacted with (other than an item being deleted), then close the
// available menu items panel. Also close on back button click.
$( '#customize-controls, .customize-section-back' ).on( 'click keydown', function( e ) {
var isDeleteBtn = $( e.target ).is( '.item-delete, .item-delete *' ),
isAddNewBtn = $( e.target ).is( '.add-new-menu-item, .add-new-menu-item *' );
if ( $( 'body' ).hasClass( 'adding-menu-items' ) && ! isDeleteBtn && ! isAddNewBtn ) {
self.close();
}
} );
// Clear the search results.
$( '.clear-results' ).on( 'click', function() {
self.$search.val( '' ).focus().trigger( 'keyup' );
} );
this.$el.on( 'input', '#custom-menu-item-name.invalid, #custom-menu-item-url.invalid', function() {
$( this ).removeClass( 'invalid' );
});
// Load available items if it looks like we'll need them.
api.panel( 'nav_menus' ).container.bind( 'expanded', function() {
if ( ! self.rendered ) {
self.initList();
self.rendered = true;
}
});
// Load more items.
this.sectionContent.scroll( function() {
var totalHeight = self.$el.find( '.accordion-section.open .available-menu-items-list' ).prop( 'scrollHeight' ),
visibleHeight = self.$el.find( '.accordion-section.open' ).height();
if ( ! self.loading && $( this ).scrollTop() > 3 / 4 * totalHeight - visibleHeight ) {
var type = $( this ).data( 'type' ),
object = $( this ).data( 'object' );
if ( 'search' === type ) {
if ( self.searchTerm ) {
self.doSearch( self.pages.search );
}
} else {
self.loadItems( type, object );
}
}
});
// Close the panel if the URL in the preview changes
api.previewer.bind( 'url', this.close );
self.delegateEvents();
},
// Search input change handler.
search: function( event ) {
var $searchSection = $( '#available-menu-items-search' ),
$otherSections = $( '#available-menu-items .accordion-section' ).not( $searchSection );
if ( ! event ) {
return;
}
if ( this.searchTerm === event.target.value ) {
return;
}
if ( '' !== event.target.value && ! $searchSection.hasClass( 'open' ) ) {
$otherSections.fadeOut( 100 );
$searchSection.find( '.accordion-section-content' ).slideDown( 'fast' );
$searchSection.addClass( 'open' );
$searchSection.find( '.clear-results' ).addClass( 'is-visible' );
} else if ( '' === event.target.value ) {
$searchSection.removeClass( 'open' );
$otherSections.show();
$searchSection.find( '.clear-results' ).removeClass( 'is-visible' );
}
this.searchTerm = event.target.value;
this.pages.search = 1;
this.doSearch( 1 );
},
// Get search results.
doSearch: function( page ) {
var self = this, params,
$section = $( '#available-menu-items-search' ),
$content = $section.find( '.accordion-section-content' ),
itemTemplate = wp.template( 'available-menu-item' );
if ( self.currentRequest ) {
self.currentRequest.abort();
}
if ( page < 0 ) {
return;
} else if ( page > 1 ) {
$section.addClass( 'loading-more' );
$content.attr( 'aria-busy', 'true' );
wp.a11y.speak( api.Menus.data.l10n.itemsLoadingMore );
} else if ( '' === self.searchTerm ) {
$content.html( '' );
wp.a11y.speak( '' );
return;
}
$section.addClass( 'loading' );
self.loading = true;
params = {
'customize-menus-nonce': api.settings.nonce['customize-menus'],
'wp_customize': 'on',
'search': self.searchTerm,
'page': page
};
self.currentRequest = wp.ajax.post( 'search-available-menu-items-customizer', params );
self.currentRequest.done(function( data ) {
var items;
if ( 1 === page ) {
// Clear previous results as it's a new search.
$content.empty();
}
$section.removeClass( 'loading loading-more' );
$content.attr( 'aria-busy', 'false' );
$section.addClass( 'open' );
self.loading = false;
items = new api.Menus.AvailableItemCollection( data.items );
self.collection.add( items.models );
items.each( function( menuItem ) {
$content.append( itemTemplate( menuItem.attributes ) );
} );
if ( 20 > items.length ) {
self.pages.search = -1; // Up to 20 posts and 20 terms in results, if <20, no more results for either.
} else {
self.pages.search = self.pages.search + 1;
}
if ( items && page > 1 ) {
wp.a11y.speak( api.Menus.data.l10n.itemsFoundMore.replace( '%d', items.length ) );
} else if ( items && page === 1 ) {
wp.a11y.speak( api.Menus.data.l10n.itemsFound.replace( '%d', items.length ) );
}
});
self.currentRequest.fail(function( data ) {
// data.message may be undefined, for example when typing slow and the request is aborted.
if ( data.message ) {
$content.empty().append( $( '
' ).text( data.message ) );
wp.a11y.speak( data.message );
}
self.pages.search = -1;
});
self.currentRequest.always(function() {
$section.removeClass( 'loading loading-more' );
$content.attr( 'aria-busy', 'false' );
self.loading = false;
self.currentRequest = null;
});
},
// Render the individual items.
initList: function() {
var self = this;
// Render the template for each item by type.
_.each( api.Menus.data.itemTypes, function( itemType ) {
self.pages[ itemType.type + ':' + itemType.object ] = 0;
self.loadItems( itemType.type, itemType.object ); // @todo we need to combine these Ajax requests.
} );
},
// Load available menu items.
loadItems: function( type, object ) {
var self = this, params, request, itemTemplate, availableMenuItemContainer;
itemTemplate = wp.template( 'available-menu-item' );
if ( -1 === self.pages[ type + ':' + object ] ) {
return;
}
availableMenuItemContainer = $( '#available-menu-items-' + type + '-' + object );
availableMenuItemContainer.find( '.accordion-section-title' ).addClass( 'loading' );
self.loading = true;
params = {
'customize-menus-nonce': api.settings.nonce['customize-menus'],
'wp_customize': 'on',
'type': type,
'object': object,
'page': self.pages[ type + ':' + object ]
};
request = wp.ajax.post( 'load-available-menu-items-customizer', params );
request.done(function( data ) {
var items, typeInner;
items = data.items;
if ( 0 === items.length ) {
if ( 0 === self.pages[ type + ':' + object ] ) {
availableMenuItemContainer
.addClass( 'cannot-expand' )
.removeClass( 'loading' )
.find( '.accordion-section-title > button' )
.prop( 'tabIndex', -1 );
}
self.pages[ type + ':' + object ] = -1;
return;
}
items = new api.Menus.AvailableItemCollection( items ); // @todo Why is this collection created and then thrown away?
self.collection.add( items.models );
typeInner = availableMenuItemContainer.find( '.available-menu-items-list' );
items.each(function( menuItem ) {
typeInner.append( itemTemplate( menuItem.attributes ) );
});
self.pages[ type + ':' + object ] += 1;
});
request.fail(function( data ) {
if ( typeof console !== 'undefined' && console.error ) {
console.error( data );
}
});
request.always(function() {
availableMenuItemContainer.find( '.accordion-section-title' ).removeClass( 'loading' );
self.loading = false;
});
},
// Adjust the height of each section of items to fit the screen.
itemSectionHeight: function() {
var sections, lists, totalHeight, accordionHeight, diff;
totalHeight = window.innerHeight;
sections = this.$el.find( '.accordion-section:not( #available-menu-items-search ) .accordion-section-content' );
lists = this.$el.find( '.accordion-section:not( #available-menu-items-search ) .available-menu-items-list:not(":only-child")' );
accordionHeight = 46 * ( 1 + sections.length ) + 14; // Magic numbers.
diff = totalHeight - accordionHeight;
if ( 120 < diff && 290 > diff ) {
sections.css( 'max-height', diff );
lists.css( 'max-height', ( diff - 60 ) );
}
},
// Highlights a menu item.
select: function( menuitemTpl ) {
this.selected = $( menuitemTpl );
this.selected.siblings( '.menu-item-tpl' ).removeClass( 'selected' );
this.selected.addClass( 'selected' );
},
// Highlights a menu item on focus.
focus: function( event ) {
this.select( $( event.currentTarget ) );
},
// Submit handler for keypress and click on menu item.
_submit: function( event ) {
// Only proceed with keypress if it is Enter or Spacebar
if ( 'keypress' === event.type && ( 13 !== event.which && 32 !== event.which ) ) {
return;
}
this.submit( $( event.currentTarget ) );
},
// Adds a selected menu item to the menu.
submit: function( menuitemTpl ) {
var menuitemId, menu_item;
if ( ! menuitemTpl ) {
menuitemTpl = this.selected;
}
if ( ! menuitemTpl || ! this.currentMenuControl ) {
return;
}
this.select( menuitemTpl );
menuitemId = $( this.selected ).data( 'menu-item-id' );
menu_item = this.collection.findWhere( { id: menuitemId } );
if ( ! menu_item ) {
return;
}
this.currentMenuControl.addItemToMenu( menu_item.attributes );
$( 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 ) {
return;
}
this.submitLink();
},
// Adds the custom menu item to the menu.
submitLink: function() {
var menuItem,
itemName = $( '#custom-menu-item-name' ),
itemUrl = $( '#custom-menu-item-url' );
if ( ! this.currentMenuControl ) {
return;
}
if ( '' === itemName.val() ) {
itemName.addClass( 'invalid' );
return;
} else if ( '' === itemUrl.val() || 'http://' === itemUrl.val() ) {
itemUrl.addClass( 'invalid' );
return;
}
menuItem = {
'title': itemName.val(),
'url': itemUrl.val(),
'type': 'custom',
'type_label': api.Menus.data.l10n.custom_label,
'object': 'custom'
};
this.currentMenuControl.addItemToMenu( menuItem );
// Reset the custom link form.
itemUrl.val( 'http://' );
itemName.val( '' );
},
// Submit handler for keypress (enter) on field and click on button.
_submitNew: function( event ) {
var container;
// Only proceed with keypress if it is Enter.
if ( 'keypress' === event.type && 13 !== event.which ) {
return;
}
if ( this.addingNew ) {
return;
}
container = $( event.target ).closest( '.accordion-section' );
this.submitNew( container );
},
// Creates a new object and adds an associated menu item to the menu.
submitNew: function( container ) {
var panel = this,
itemName = container.find( '.create-item-input' ),
title = itemName.val(),
dataContainer = container.find( '.available-menu-items-list' ),
itemType = dataContainer.data( 'type' ),
itemObject = dataContainer.data( 'object' ),
itemTypeLabel = dataContainer.data( 'type_label' ),
promise;
if ( ! this.currentMenuControl ) {
return;
}
// Only posts are supported currently.
if ( 'post_type' !== itemType ) {
return;
}
if ( '' === $.trim( itemName.val() ) ) {
itemName.addClass( 'invalid' );
itemName.focus();
return;
} else {
itemName.removeClass( 'invalid' );
container.find( '.accordion-section-title' ).addClass( 'loading' );
}
panel.addingNew = true;
itemName.attr( 'disabled', 'disabled' );
promise = api.Menus.insertAutoDraftPost( {
post_title: title,
post_type: itemObject
} );
promise.done( function( data ) {
var availableItem, $content, itemTemplate;
availableItem = new api.Menus.AvailableItemModel( {
'id': 'post-' + data.post_id, // Used for available menu item Backbone models.
'title': itemName.val(),
'type': itemType,
'type_label': itemTypeLabel,
'object': itemObject,
'object_id': data.post_id,
'url': data.url
} );
// Add new item to menu.
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' );
itemTemplate = wp.template( 'available-menu-item' );
$content.prepend( itemTemplate( availableItem.attributes ) );
$content.scrollTop();
// Reset the create content form.
itemName.val( '' ).removeAttr( 'disabled' );
panel.addingNew = false;
container.find( '.accordion-section-title' ).removeClass( 'loading' );
} );
},
// Opens the panel.
open: function( menuControl ) {
this.currentMenuControl = menuControl;
this.itemSectionHeight();
$( 'body' ).addClass( 'adding-menu-items' );
// Collapse all controls.
_( this.currentMenuControl.getMenuItemControls() ).each( function( control ) {
control.collapseForm();
} );
this.$el.find( '.selected' ).removeClass( 'selected' );
this.$search.focus();
},
// Closes the panel
close: function( options ) {
options = options || {};
if ( options.returnFocus && this.currentMenuControl ) {
this.currentMenuControl.container.find( '.add-new-menu-item' ).focus();
}
this.currentMenuControl = null;
this.selected = null;
$( 'body' ).removeClass( 'adding-menu-items' );
$( '#available-menu-items .menu-item-handle.item-added' ).removeClass( 'item-added' );
this.$search.val( '' );
},
// 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() ) {
return;
}
if ( isSearchFocused && isBackTab ) {
this.currentMenuControl.container.find( '.add-new-menu-item' ).focus();
event.preventDefault(); // Avoid additional back-tab.
} else if ( isEsc ) {
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.
*
* @constructor
* @augments wp.customize.Panel
*/
api.Menus.MenusPanel = api.Panel.extend({
attachEvents: function() {
api.Panel.prototype.attachEvents.call( this );
var panel = 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 ) ) {
return;
}
event.preventDefault();
// Hide description
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' );
} else {
button.attr( 'aria-expanded', 'true' );
panelMeta.addClass( 'open' );
panelMeta.addClass( 'active-menu-screen-options' );
options.slideDown( 'fast' );
}
return false;
} );
// Help toggle
help.on( 'click keydown', function( event ) {
if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
return;
}
event.preventDefault();
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' );
}
} );
},
/**
* Show/hide/save screen options (columns). From common.js.
*/
ready: function() {
var panel = this;
this.container.find( '.hide-column-tog' ).click( function() {
var $t = $( this ), column = $t.val();
if ( $t.prop( 'checked' ) ) {
panel.checked( column );
} else {
panel.unchecked( column );
}
panel.saveManageColumnsState();
});
this.container.find( '.hide-column-tog' ).each( function() {
var $t = $( this ), column = $t.val();
if ( $t.prop( 'checked' ) ) {
panel.checked( column );
} else {
panel.unchecked( column );
}
});
},
saveManageColumnsState: _.debounce( function() {
var panel = this;
if ( panel._updateHiddenColumnsRequest ) {
panel._updateHiddenColumnsRequest.abort();
}
panel._updateHiddenColumnsRequest = wp.ajax.post( 'hidden-columns', {
hidden: panel.hidden(),
screenoptionnonce: $( '#screenoptionnonce' ).val(),
page: 'nav-menus'
} );
panel._updateHiddenColumnsRequest.always( function() {
panel._updateHiddenColumnsRequest = null;
} );
}, 2000 ),
checked: function( column ) {
this.container.addClass( 'field-' + column + '-active' );
},
unchecked: function( column ) {
this.container.removeClass( 'field-' + column + '-active' );
},
hidden: function() {
return $( '.hide-column-tog' ).not( ':checked' ).map( function() {
var id = this.id;
return id.substring( 0, id.length - 5 );
}).get().join( ',' );
}
} );
/**
* 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.
*
* @constructor
* @augments wp.customize.Section
*/
api.Menus.MenuSection = api.Section.extend({
/**
* Initialize.
*
* @since 4.3.0
*
* @param {String} id
* @param {Object} options
*/
initialize: function( id, options ) {
var section = this;
api.Section.prototype.initialize.call( section, id, options );
section.deferred.initSortables = $.Deferred();
},
/**
* Ready.
*/
ready: function() {
var section = this;
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 false;
}
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\[(.+?)]/ );
if ( matches ) {
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 ) {
return;
}
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' });
} );
},
populateControls: function() {
var section = this, menuNameControlId, menuAutoAddControlId, menuControl, menuNameControl, menuAutoAddControl;
// Add the control for managing the menu name.
menuNameControlId = section.id + '[name]';
menuNameControl = api.control( menuNameControlId );
if ( ! menuNameControl ) {
menuNameControl = new api.controlConstructor.nav_menu_name( menuNameControlId, {
params: {
type: 'nav_menu_name',
content: '', // @todo core should do this for us; see #30741
label: api.Menus.data.l10n.menuNameLabel,
active: true,
section: section.id,
priority: 0,
settings: {
'default': section.id
}
}
} );
api.control.add( menuNameControl.id, menuNameControl );
menuNameControl.active.set( true );
}
// Add the menu control.
menuControl = api.control( section.id );
if ( ! menuControl ) {
menuControl = new api.controlConstructor.nav_menu( section.id, {
params: {
type: 'nav_menu',
content: '', // @todo core should do this for us; see #30741
section: section.id,
priority: 998,
active: true,
settings: {
'default': section.id
},
menu_id: section.params.menu_id
}
} );
api.control.add( menuControl.id, menuControl );
menuControl.active.set( true );
}
// Add the control for managing the menu auto_add.
menuAutoAddControlId = section.id + '[auto_add]';
menuAutoAddControl = api.control( menuAutoAddControlId );
if ( ! menuAutoAddControl ) {
menuAutoAddControl = new api.controlConstructor.nav_menu_auto_add( menuAutoAddControlId, {
params: {
type: 'nav_menu_auto_add',
content: '', // @todo core should do this for us
label: '',
active: true,
section: section.id,
priority: 999,
settings: {
'default': section.id
}
}
} );
api.control.add( menuAutoAddControl.id, menuAutoAddControl );
menuAutoAddControl.active.set( true );
}
},
/**
*
*/
refreshAssignedLocations: function() {
var section = this,
menuTermId = section.params.menu_id,
currentAssignedLocations = [];
_.each( section.navMenuLocationSettings, function( setting, themeLocation ) {
if ( setting() === menuTermId ) {
currentAssignedLocations.push( themeLocation );
}
});
section.assignedLocations.set( currentAssignedLocations );
},
/**
* @param {Array} themeLocationSlugs Theme location slugs.
*/
updateAssignedLocationsInSectionTitle: function( themeLocationSlugs ) {
var section = this,
$title;
$title = section.container.find( '.accordion-section-title:first' );
$title.find( '.menu-in-location' ).remove();
_.each( themeLocationSlugs, function( themeLocationSlug ) {
var $label, locationName;
$label = $( '' );
locationName = api.Menus.data.locationSlugMappedToName[ themeLocationSlug ];
$label.text( api.Menus.data.l10n.menuLocation.replace( '%s', locationName ) );
$title.append( $label );
});
section.container.toggleClass( 'assigned-to-menu-location', 0 !== themeLocationSlugs.length );
},
onChangeExpanded: function( expanded, args ) {
var section = this;
if ( expanded ) {
wpNavMenu.menuList = section.contentContainer;
wpNavMenu.targetList = wpNavMenu.menuList;
// Add attributes needed by wpNavMenu
$( '#menu-to-edit' ).removeAttr( 'id' );
wpNavMenu.menuList.attr( 'id', 'menu-to-edit' ).addClass( 'menu' );
_.each( api.section( section.id ).controls(), function( control ) {
if ( 'nav_menu_item' === control.params.type ) {
control.actuallyEmbed();
}
} );
if ( 'resolved' !== section.deferred.initSortables.state() ) {
wpNavMenu.initSortables(); // Depends on menu-to-edit ID being set above.
section.deferred.initSortables.resolve( wpNavMenu.menuList ); // Now MenuControl can extend the sortable.
// @todo Note that wp.customize.reflowPaneContents() is debounced, so this immediate change will show a slight flicker while priorities get updated.
api.control( 'nav_menu[' + String( section.params.menu_id ) + ']' ).reflowMenuItems();
}
}
api.Section.prototype.onChangeExpanded.call( section, expanded, args );
}
});
/**
* wp.customize.Menus.NewMenuSection
*
* Customizer section for new menus.
* Note that 'new_menu' must match the WP_Customize_New_Menu_Section::$type.
*
* @constructor
* @augments wp.customize.Section
*/
api.Menus.NewMenuSection = api.Section.extend({
/**
* Add behaviors for the accordion section.
*
* @since 4.3.0
*/
attachEvents: function() {
var section = this;
this.container.on( 'click', '.add-menu-toggle', function() {
if ( section.expanded() ) {
section.collapse();
} else {
section.expand();
}
});
},
/**
* Update UI to reflect expanded state.
*
* @since 4.1.0
*
* @param {Boolean} expanded
*/
onChangeExpanded: function( expanded ) {
var section = this,
button = section.container.find( '.add-menu-toggle' ),
content = section.contentContainer,
customizer = section.headContainer.closest( '.wp-full-overlay-sidebar-content' );
if ( expanded ) {
button.addClass( 'open' );
button.attr( 'aria-expanded', 'true' );
content.slideDown( 'fast', function() {
customizer.scrollTop( customizer.height() );
});
} else {
button.removeClass( 'open' );
button.attr( 'aria-expanded', 'false' );
content.slideUp( 'fast' );
content.find( '.menu-name-field' ).removeClass( 'invalid' );
}
},
/**
* Find the content element.
*
* @since 4.7.0
*
* @returns {jQuery} Content UL element.
*/
getContent: function() {
return this.container.find( 'ul:first' );
}
});
/**
* wp.customize.Menus.MenuLocationControl
*
* Customizer control for menu locations (rendered as a