// Link settings.
api.Menus.data = {
- nonce: '',
itemTypes: [],
l10n: {},
- menuItemTransport: 'postMessage',
+ settingTransport: 'refresh',
phpIntMax: 0,
defaultSettingValues: {
nav_menu: {},
nav_menu_item: {}
- }
+ },
+ locationSlugMappedToName: {}
};
if ( 'undefined' !== typeof _wpCustomizeNavMenusSettings ) {
$.extend( api.Menus.data, _wpCustomizeNavMenusSettings );
initialize: function() {
var self = this;
+ if ( ! api.panel.has( 'nav_menus' ) ) {
+ return;
+ }
+
this.$search = $( '#menu-items-search' );
this.sectionContent = this.$el.find( '.accordion-section-content' );
// Close the panel if the URL in the preview changes
api.previewer.bind( 'url', this.close );
+
+ self.delegateEvents();
},
// Search input change handler.
.prop( 'tabIndex', -1 )
.removeClass( 'is-visible' );
}
-
+
this.searchTerm = event.target.value;
this.pages.search = 1;
this.doSearch( 1 );
$section.addClass( 'loading' );
self.loading = true;
params = {
- 'customize-menus-nonce': api.Menus.data.nonce,
+ 'customize-menus-nonce': api.settings.nonce['customize-menus'],
'wp_customize': 'on',
'search': self.searchTerm,
'page': page
availableMenuItemContainer.find( '.accordion-section-title' ).addClass( 'loading' );
self.loading = true;
params = {
- 'customize-menus-nonce': api.Menus.data.nonce,
+ 'customize-menus-nonce': api.settings.nonce['customize-menus'],
'wp_customize': 'on',
'type': type,
'object': object,
});
},
- saveManageColumnsState: function() {
- var hidden = this.hidden();
- $.post( wp.ajax.settings.url, {
- action: 'hidden-columns',
- hidden: hidden,
+ 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' );
},
hidden: function() {
- this.hidden = function() {
- return $( '.hide-column-tog' ).not( ':checked' ).map( function() {
- var id = this.id;
- return id.substring( id, id.length - 5 );
- }).get().join( ',' );
- };
+ return $( '.hide-column-tog' ).not( ':checked' ).map( function() {
+ var id = this.id;
+ return id.substring( 0, id.length - 5 );
+ }).get().join( ',' );
}
} );
/**
* @param {array} themeLocations
*/
- updateAssignedLocationsInSectionTitle: function( themeLocations ) {
+ updateAssignedLocationsInSectionTitle: function( themeLocationSlugs ) {
var section = this,
$title;
$title = section.container.find( '.accordion-section-title:first' );
$title.find( '.menu-in-location' ).remove();
- _.each( themeLocations, function( themeLocation ) {
- var $label = $( '<span class="menu-in-location"></span>' );
- $label.text( api.Menus.data.l10n.menuLocation.replace( '%s', themeLocation ) );
+ _.each( themeLocationSlugs, function( themeLocationSlug ) {
+ var $label, locationName;
+ $label = $( '<span class="menu-in-location"></span>' );
+ 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 !== themeLocations.length );
+ section.container.toggleClass( 'assigned-to-menu-location', 0 !== themeLocationSlugs.length );
},
return;
}
section = api.section( sectionId );
- if ( section && section.expanded() ) {
+ if ( ( section && section.expanded() ) || api.settings.autofocus.control === control.id ) {
control.actuallyEmbed();
}
},
}
});
if ( settingValue ) {
- element.set( settingValue[ property ] );
+ if ( ( property === 'classes' || property === 'xfn' ) && _.isArray( settingValue[ property ] ) ) {
+ element.set( settingValue[ property ].join( ' ' ) );
+ } else {
+ element.set( settingValue[ property ] );
+ }
}
});
return;
}
- var titleEl = control.container.find( '.menu-item-title' );
+ var titleEl = control.container.find( '.menu-item-title' ),
+ titleText = item.title || api.Menus.data.l10n.untitled;
+
+ if ( item._invalid ) {
+ titleText = api.Menus.data.l10n.invalidTitleTpl.replace( '%s', titleText );
+ }
// Don't update to an empty title.
if ( item.title ) {
titleEl
- .text( item.title )
+ .text( titleText )
.removeClass( 'no-title' );
} else {
titleEl
- .text( api.Menus.data.l10n.untitled )
+ .text( titleText )
.addClass( 'no-title' );
}
} );
'menu-item-edit-inactive'
];
- if ( settingValue.invalid ) {
- containerClasses.push( 'invalid' );
- control.params.title = api.Menus.data.invalidTitleTpl.replace( '%s', control.params.title );
+ if ( settingValue._invalid ) {
+ containerClasses.push( 'menu-item-invalid' );
+ control.params.title = api.Menus.data.l10n.invalidTitleTpl.replace( '%s', control.params.title );
} else if ( 'draft' === settingValue.status ) {
containerClasses.push( 'pending' );
control.params.title = api.Menus.data.pendingTitleTpl.replace( '%s', control.params.title );
/**
* Expand the menu item form control.
+ *
+ * @since 4.5.0 Added params.completeCallback.
+ *
+ * @param {Object} [params] - Optional params.
+ * @param {Function} [params.completeCallback] - Function to call when the form toggle has finished animating.
*/
- expandForm: function() {
- this.toggleForm( true );
+ expandForm: function( params ) {
+ this.toggleForm( true, params );
},
/**
* Collapse the menu item form control.
+ *
+ * @since 4.5.0 Added params.completeCallback.
+ *
+ * @param {Object} [params] - Optional params.
+ * @param {Function} [params.completeCallback] - Function to call when the form toggle has finished animating.
*/
- collapseForm: function() {
- this.toggleForm( false );
+ collapseForm: function( params ) {
+ this.toggleForm( false, params );
},
/**
* Expand or collapse the menu item control.
*
- * @param {boolean|undefined} [showOrHide] If not supplied, will be inverse of current visibility
+ * @since 4.5.0 Added params.completeCallback.
+ *
+ * @param {boolean} [showOrHide] - If not supplied, will be inverse of current visibility
+ * @param {Object} [params] - Optional params.
+ * @param {Function} [params.completeCallback] - Function to call when the form toggle has finished animating.
*/
- toggleForm: function( showOrHide ) {
+ toggleForm: function( showOrHide, params ) {
var self = this, $menuitem, $inside, complete;
$menuitem = this.container;
// Already expanded or collapsed.
if ( $inside.is( ':visible' ) === showOrHide ) {
+ if ( params && params.completeCallback ) {
+ params.completeCallback();
+ }
return;
}
.removeClass( 'menu-item-edit-inactive' )
.addClass( 'menu-item-edit-active' );
self.container.trigger( 'expanded' );
+
+ if ( params && params.completeCallback ) {
+ params.completeCallback();
+ }
};
$menuitem.find( '.item-edit' ).attr( 'aria-expanded', 'true' );
.addClass( 'menu-item-edit-inactive' )
.removeClass( 'menu-item-edit-active' );
self.container.trigger( 'collapsed' );
+
+ if ( params && params.completeCallback ) {
+ params.completeCallback();
+ }
};
self.container.trigger( 'collapse' );
/**
* Expand the containing menu section, expand the form, and focus on
* the first input in the control.
+ *
+ * @since 4.5.0 Added params.completeCallback.
+ *
+ * @param {Object} [params] - Params object.
+ * @param {Function} [params.completeCallback] - Optional callback function when focus has completed.
*/
- focus: function() {
- this.expandControlSection();
- this.expandForm();
- this.container.find( '.menu-item-settings :focusable:first' ).focus();
+ focus: function( params ) {
+ params = params || {};
+ var control = this, originalCompleteCallback = params.completeCallback;
+
+ control.expandControlSection();
+
+ params.completeCallback = function() {
+ var focusable;
+
+ // Note that we can't use :focusable due to a jQuery UI issue. See: https://github.com/jquery/jquery-ui/pull/1583
+ focusable = control.container.find( '.menu-item-settings' ).find( 'input, select, textarea, button, object, a[href], [tabindex]' ).filter( ':visible' );
+ focusable.first().focus();
+
+ if ( originalCompleteCallback ) {
+ originalCompleteCallback();
+ }
+ };
+
+ control.expandForm( params );
},
/**
control.isSorting = false;
+ // Reset horizontal scroll position when done dragging.
+ control.$sectionContent.scrollLeft( 0 );
+
_.each( menuItemContainerIds, function( menuItemContainerId ) {
var menuItemId, menuItemControl, matches;
matches = menuItemContainerId.match( /^customize-control-nav_menu_item-(-?\d+)$/, '' );
customizeId = 'nav_menu_item[' + String( placeholderId ) + ']';
settingArgs = {
type: 'nav_menu_item',
- transport: 'postMessage',
+ transport: api.Menus.data.settingTransport,
previewer: api.previewer
};
setting = api.create( customizeId, customizeId, {}, settingArgs );
// Register the menu control setting.
api.create( customizeId, customizeId, {}, {
type: 'nav_menu',
- transport: 'postMessage',
+ transport: api.Menus.data.settingTransport,
previewer: api.previewer
} );
api( customizeId ).set( $.extend(
}
} );
- api.previewer.bind( 'refresh', function() {
- api.previewer.refresh();
- });
+ // Open and focus menu control.
+ api.previewer.bind( 'focus-nav-menu-item-control', api.Menus.focusMenuItemControl );
} );
/**
*/
api.Menus.applySavedData = function( data ) {
- var insertedMenuIdMapping = {};
+ var insertedMenuIdMapping = {}, insertedMenuItemIdMapping = {};
_( data.nav_menu_updates ).each(function( update ) {
var oldCustomizeId, newCustomizeId, customizeId, oldSetting, newSetting, setting, settingValue, oldSection, newSection, wasSaved, widgetTemplate, navMenuCount;
newCustomizeId = 'nav_menu[' + String( update.term_id ) + ']';
newSetting = api.create( newCustomizeId, newCustomizeId, settingValue, {
type: 'nav_menu',
- transport: 'postMessage',
+ transport: api.Menus.data.settingTransport,
previewer: api.previewer
} );
}
} );
+ // Build up mapping of nav_menu_item placeholder IDs to inserted IDs.
+ _( data.nav_menu_item_updates ).each(function( update ) {
+ if ( update.previous_post_id ) {
+ insertedMenuItemIdMapping[ update.previous_post_id ] = update.post_id;
+ }
+ });
+
_( data.nav_menu_item_updates ).each(function( update ) {
var oldCustomizeId, newCustomizeId, oldSetting, newSetting, settingValue, oldControl, newControl;
if ( 'inserted' === update.status ) {
}
settingValue = _.clone( settingValue );
+ // If the parent menu item was also inserted, update the menu_item_parent to the new ID.
+ if ( settingValue.menu_item_parent < 0 ) {
+ if ( ! insertedMenuItemIdMapping[ settingValue.menu_item_parent ] ) {
+ throw new Error( 'inserted ID for menu_item_parent not available' );
+ }
+ settingValue.menu_item_parent = insertedMenuItemIdMapping[ settingValue.menu_item_parent ];
+ }
+
// If the menu was also inserted, then make sure it uses the new menu ID for nav_menu_term_id.
if ( insertedMenuIdMapping[ settingValue.nav_menu_term_id ] ) {
settingValue.nav_menu_term_id = insertedMenuIdMapping[ settingValue.nav_menu_term_id ];
newCustomizeId = 'nav_menu_item[' + String( update.post_id ) + ']';
newSetting = api.create( newCustomizeId, newCustomizeId, settingValue, {
type: 'nav_menu_item',
- transport: 'postMessage',
+ transport: api.Menus.data.settingTransport,
previewer: api.previewer
} );