mirror of
https://github.com/WordPress/WordPress.git
synced 2024-11-06 19:01:44 +01:00
6b775d4afe
See https://make.wordpress.org/core/2016/02/16/selective-refresh-in-the-customizer/. Props westonruter, valendesigns, DrewAPicture, ocean90. Fixes #27355. Built from https://develop.svn.wordpress.org/trunk@36586 git-svn-id: http://core.svn.wordpress.org/trunk@36553 1a063a9b-81f0-0310-95a4-ce76da25c4cd
218 lines
7.0 KiB
JavaScript
218 lines
7.0 KiB
JavaScript
wp.customize.navMenusPreview = wp.customize.MenusCustomizerPreview = ( function( $, _, wp, api ) {
|
|
'use strict';
|
|
|
|
var self = {};
|
|
|
|
/**
|
|
* Initialize nav menus preview.
|
|
*/
|
|
self.init = function() {
|
|
var self = this;
|
|
|
|
if ( api.selectiveRefresh ) {
|
|
self.watchNavMenuLocationChanges();
|
|
}
|
|
|
|
api.preview.bind( 'active', function() {
|
|
self.highlightControls();
|
|
} );
|
|
};
|
|
|
|
if ( api.selectiveRefresh ) {
|
|
|
|
/**
|
|
* Partial representing an invocation of wp_nav_menu().
|
|
*
|
|
* @class
|
|
* @augments wp.customize.selectiveRefresh.Partial
|
|
* @since 4.5.0
|
|
*/
|
|
self.NavMenuInstancePartial = api.selectiveRefresh.Partial.extend({
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @since 4.5.0
|
|
* @param {string} id - Partial ID.
|
|
* @param {Object} options
|
|
* @param {Object} options.params
|
|
* @param {Object} options.params.navMenuArgs
|
|
* @param {string} options.params.navMenuArgs.args_hmac
|
|
* @param {string} [options.params.navMenuArgs.theme_location]
|
|
* @param {number} [options.params.navMenuArgs.menu]
|
|
* @param {object} [options.constructingContainerContext]
|
|
*/
|
|
initialize: function( id, options ) {
|
|
var partial = this, matches, argsHmac;
|
|
matches = id.match( /^nav_menu_instance\[([0-9a-f]{32})]$/ );
|
|
if ( ! matches ) {
|
|
throw new Error( 'Illegal id for nav_menu_instance partial. The key corresponds with the args HMAC.' );
|
|
}
|
|
argsHmac = matches[1];
|
|
|
|
options = options || {};
|
|
options.params = _.extend(
|
|
{
|
|
selector: '[data-customize-partial-id="' + id + '"]',
|
|
navMenuArgs: options.constructingContainerContext || {},
|
|
containerInclusive: true
|
|
},
|
|
options.params || {}
|
|
);
|
|
api.selectiveRefresh.Partial.prototype.initialize.call( partial, id, options );
|
|
|
|
if ( ! _.isObject( partial.params.navMenuArgs ) ) {
|
|
throw new Error( 'Missing navMenuArgs' );
|
|
}
|
|
if ( partial.params.navMenuArgs.args_hmac !== argsHmac ) {
|
|
throw new Error( 'args_hmac mismatch with id' );
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Return whether the setting is related to this partial.
|
|
*
|
|
* @since 4.5.0
|
|
* @param {wp.customize.Value|string} setting - Object or ID.
|
|
* @param {number|object|false|null} newValue - New value, or null if the setting was just removed.
|
|
* @param {number|object|false|null} oldValue - Old value, or null if the setting was just added.
|
|
* @returns {boolean}
|
|
*/
|
|
isRelatedSetting: function( setting, newValue, oldValue ) {
|
|
var partial = this, navMenuLocationSetting, navMenuId, isNavMenuItemSetting;
|
|
if ( _.isString( setting ) ) {
|
|
setting = api( setting );
|
|
}
|
|
|
|
/*
|
|
* Prevent nav_menu_item changes only containing type_label differences triggering a refresh.
|
|
* These settings in the preview do not include type_label property, and so if one of these
|
|
* nav_menu_item settings is dirty, after a refresh the nav menu instance would do a selective
|
|
* refresh immediately because the setting from the pane would have the type_label whereas
|
|
* the setting in the preview would not, thus triggering a change event. The following
|
|
* condition short-circuits this unnecessary selective refresh and also prevents an infinite
|
|
* loop in the case where a nav_menu_instance partial had done a fallback refresh.
|
|
* @todo Nav menu item settings should not include a type_label property to begin with.
|
|
*/
|
|
isNavMenuItemSetting = /^nav_menu_item\[/.test( setting.id );
|
|
if ( isNavMenuItemSetting && _.isObject( newValue ) && _.isObject( oldValue ) ) {
|
|
delete newValue.type_label;
|
|
delete oldValue.type_label;
|
|
if ( _.isEqual( oldValue, newValue ) ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ( partial.params.navMenuArgs.theme_location ) {
|
|
if ( 'nav_menu_locations[' + partial.params.navMenuArgs.theme_location + ']' === setting.id ) {
|
|
return true;
|
|
}
|
|
navMenuLocationSetting = api( 'nav_menu_locations[' + partial.params.navMenuArgs.theme_location + ']' );
|
|
}
|
|
|
|
navMenuId = partial.params.navMenuArgs.menu;
|
|
if ( ! navMenuId && navMenuLocationSetting ) {
|
|
navMenuId = navMenuLocationSetting();
|
|
}
|
|
|
|
if ( ! navMenuId ) {
|
|
return false;
|
|
}
|
|
return (
|
|
( 'nav_menu[' + navMenuId + ']' === setting.id ) ||
|
|
( isNavMenuItemSetting && (
|
|
( newValue && newValue.nav_menu_term_id === navMenuId ) ||
|
|
( oldValue && oldValue.nav_menu_term_id === navMenuId )
|
|
) )
|
|
);
|
|
},
|
|
|
|
/**
|
|
* Render content.
|
|
*
|
|
* @inheritdoc
|
|
* @param {wp.customize.selectiveRefresh.Placement} placement
|
|
*/
|
|
renderContent: function( placement ) {
|
|
var partial = this, previousContainer = placement.container;
|
|
if ( api.selectiveRefresh.Partial.prototype.renderContent.call( partial, placement ) ) {
|
|
|
|
// Trigger deprecated event.
|
|
$( document ).trigger( 'customize-preview-menu-refreshed', [ {
|
|
instanceNumber: null, // @deprecated
|
|
wpNavArgs: placement.context, // @deprecated
|
|
wpNavMenuArgs: placement.context,
|
|
oldContainer: previousContainer,
|
|
newContainer: placement.container
|
|
} ] );
|
|
}
|
|
}
|
|
});
|
|
|
|
api.selectiveRefresh.partialConstructor.nav_menu_instance = self.NavMenuInstancePartial;
|
|
|
|
/**
|
|
* Watch for changes to nav_menu_locations[] settings.
|
|
*
|
|
* Refresh partials associated with the given nav_menu_locations[] setting,
|
|
* or request an entire preview refresh if there are no containers in the
|
|
* document for a partial associated with the theme location.
|
|
*
|
|
* @since 4.5.0
|
|
*/
|
|
self.watchNavMenuLocationChanges = function() {
|
|
api.bind( 'change', function( setting ) {
|
|
var themeLocation, themeLocationPartialFound = false, matches = setting.id.match( /^nav_menu_locations\[(.+)]$/ );
|
|
if ( ! matches ) {
|
|
return;
|
|
}
|
|
themeLocation = matches[1];
|
|
api.selectiveRefresh.partial.each( function( partial ) {
|
|
if ( partial.extended( self.NavMenuInstancePartial ) && partial.params.navMenuArgs.theme_location === themeLocation ) {
|
|
partial.refresh();
|
|
themeLocationPartialFound = true;
|
|
}
|
|
} );
|
|
|
|
if ( ! themeLocationPartialFound ) {
|
|
api.selectiveRefresh.requestFullRefresh();
|
|
}
|
|
} );
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Connect nav menu items with their corresponding controls in the pane.
|
|
*
|
|
* Setup shift-click on nav menu items which are more granular than the nav menu partial itself.
|
|
* Also this applies even if a nav menu is not partial-refreshable.
|
|
*
|
|
* @since 4.5.0
|
|
*/
|
|
self.highlightControls = function() {
|
|
var selector = '.menu-item';
|
|
|
|
// Focus on the menu item control when shift+clicking the menu item.
|
|
$( document ).on( 'click', selector, function( e ) {
|
|
var navMenuItemParts;
|
|
if ( ! e.shiftKey ) {
|
|
return;
|
|
}
|
|
|
|
navMenuItemParts = $( this ).attr( 'class' ).match( /(?:^|\s)menu-item-(\d+)(?:\s|$)/ );
|
|
if ( navMenuItemParts ) {
|
|
e.preventDefault();
|
|
e.stopPropagation(); // Make sure a sub-nav menu item will get focused instead of parent items.
|
|
api.preview.send( 'focus-nav-menu-item-control', parseInt( navMenuItemParts[1], 10 ) );
|
|
}
|
|
});
|
|
};
|
|
|
|
api.bind( 'preview-ready', function() {
|
|
self.init();
|
|
} );
|
|
|
|
return self;
|
|
|
|
}( jQuery, _, wp, wp.customize ) );
|