1 /* global JSON, _wpCustomizePreviewNavMenusExports */
3 ( function( $, _, wp ) {
6 if ( ! wp || ! wp.customize ) { return; }
8 var api = wp.customize,
9 currentRefreshDebounced = {},
10 refreshDebounceDelay = 200,
14 renderNonceValue: null,
15 renderNoncePostKey: null,
16 previewCustomizeNonce: null,
22 navMenuInstanceArgs: {}
25 api.MenusCustomizerPreview = {
27 * Bootstrap functionality.
30 var self = this, initializedSettings = {};
32 settings = _.extend( {}, defaultSettings );
33 if ( 'undefined' !== typeof _wpCustomizePreviewNavMenusExports ) {
34 _.extend( settings, _wpCustomizePreviewNavMenusExports );
37 api.each( function( setting, id ) {
39 initializedSettings[ setting.id ] = true;
40 self.bindListener( setting );
43 api.preview.bind( 'setting', function( args ) {
44 var id, value, setting;
51 // Currently customize-preview.js is not creating settings for dynamically-created settings in the pane, so we have to do it.
52 setting = api.create( id, value ); // @todo This should be in core
55 // Currently customize-preview.js doesn't set the id property for each setting, like customize-controls.js does.
59 if ( ! initializedSettings[ setting.id ] ) {
60 initializedSettings[ setting.id ] = true;
61 if ( self.bindListener( setting ) ) {
62 setting.callbacks.fireWith( setting, [ setting(), null ] );
70 * @param {wp.customize.Value} setting
71 * @returns {boolean} Whether the setting was bound.
73 bindListener : function( setting ) {
74 var matches, themeLocation;
76 matches = setting.id.match( /^nav_menu\[(-?\d+)]$/ );
78 setting.navMenuId = parseInt( matches[1], 10 );
79 setting.bind( this.onChangeNavMenuSetting );
83 matches = setting.id.match( /^nav_menu_item\[(-?\d+)]$/ );
85 setting.navMenuItemId = parseInt( matches[1], 10 );
86 setting.bind( this.onChangeNavMenuItemSetting );
90 matches = setting.id.match( /^nav_menu_locations\[(.+?)]/ );
92 themeLocation = matches[1];
93 setting.bind( _.bind( function() {
94 this.refreshMenuLocation( themeLocation );
103 * Handle changing of a nav_menu setting.
105 * @this {wp.customize.Setting}
107 onChangeNavMenuSetting : function() {
109 if ( ! setting.navMenuId ) {
110 throw new Error( 'Expected navMenuId property to be set.' );
112 api.MenusCustomizerPreview.refreshMenu( setting.navMenuId );
116 * Handle changing of a nav_menu_item setting.
118 * @this {wp.customize.Setting}
120 * @param {object} from
122 onChangeNavMenuItemSetting : function( to, from ) {
123 if ( from && from.nav_menu_term_id && ( ! to || from.nav_menu_term_id !== to.nav_menu_term_id ) ) {
124 api.MenusCustomizerPreview.refreshMenu( from.nav_menu_term_id );
126 if ( to && to.nav_menu_term_id ) {
127 api.MenusCustomizerPreview.refreshMenu( to.nav_menu_term_id );
132 * Update a given menu rendered in the preview.
134 * @param {int} menuId
136 refreshMenu : function( menuId ) {
137 var assignedLocations = [];
139 api.each(function( setting, id ) {
140 var matches = id.match( /^nav_menu_locations\[(.+?)]/ );
141 if ( matches && menuId === setting() ) {
142 assignedLocations.push( matches[1] );
146 _.each( settings.navMenuInstanceArgs, function( navMenuArgs, instanceNumber ) {
147 if ( menuId === navMenuArgs.menu || -1 !== _.indexOf( assignedLocations, navMenuArgs.theme_location ) ) {
148 this.refreshMenuInstanceDebounced( instanceNumber );
154 * Refresh the menu(s) associated with a given nav menu location.
156 * @param {string} location
158 refreshMenuLocation : function( location ) {
159 var foundInstance = false;
160 _.each( settings.navMenuInstanceArgs, function( navMenuArgs, instanceNumber ) {
161 if ( location === navMenuArgs.theme_location ) {
162 this.refreshMenuInstanceDebounced( instanceNumber );
163 foundInstance = true;
166 if ( ! foundInstance ) {
167 api.preview.send( 'refresh' );
172 * Update a specific instance of a given menu on the page.
174 * @param {int} instanceNumber
176 refreshMenuInstance : function( instanceNumber ) {
177 var data, menuId, customized, container, request, wpNavMenuArgs, instance, containerInstanceClassName;
179 if ( ! settings.navMenuInstanceArgs[ instanceNumber ] ) {
180 throw new Error( 'unknown_instance_number' );
182 instance = settings.navMenuInstanceArgs[ instanceNumber ];
184 containerInstanceClassName = 'partial-refreshable-nav-menu-' + String( instanceNumber );
185 container = $( '.' + containerInstanceClassName );
187 if ( _.isNumber( instance.menu ) ) {
188 menuId = instance.menu;
189 } else if ( instance.theme_location && api.has( 'nav_menu_locations[' + instance.theme_location + ']' ) ) {
190 menuId = api( 'nav_menu_locations[' + instance.theme_location + ']' ).get();
193 if ( ! menuId || ! instance.can_partial_refresh || 0 === container.length ) {
194 api.preview.send( 'refresh' );
197 menuId = parseInt( menuId, 10 );
200 nonce: settings.previewCustomizeNonce, // for Customize Preview
203 if ( ! settings.theme.active ) {
204 data.theme = settings.theme.stylesheet;
206 data[ settings.renderQueryVar ] = '1';
208 // Gather settings to send in partial refresh request.
210 api.each( function( setting, id ) {
211 var value = setting.get(), shouldSend = false;
212 // @todo Core should propagate the dirty state into the Preview as well so we can use that here.
214 // Send setting if it is a nav_menu_locations[] setting.
215 shouldSend = shouldSend || /^nav_menu_locations\[/.test( id );
217 // Send setting if it is the setting for this menu.
218 shouldSend = shouldSend || id === 'nav_menu[' + String( menuId ) + ']';
220 // Send setting if it is one that is associated with this menu, or it is deleted.
221 shouldSend = shouldSend || ( /^nav_menu_item\[/.test( id ) && ( false === value || menuId === value.nav_menu_term_id ) );
224 customized[ id ] = value;
227 data.customized = JSON.stringify( customized );
228 data[ settings.renderNoncePostKey ] = settings.renderNonceValue;
230 wpNavMenuArgs = $.extend( {}, instance );
231 data.wp_nav_menu_args_hash = wpNavMenuArgs.args_hash;
232 delete wpNavMenuArgs.args_hash;
233 data.wp_nav_menu_args = JSON.stringify( wpNavMenuArgs );
235 container.addClass( 'customize-partial-refreshing' );
237 request = wp.ajax.send( null, {
239 url: settings.requestUri
241 request.done( function( data ) {
242 // If the menu is now not visible, refresh since the page layout may have changed.
243 if ( false === data ) {
244 api.preview.send( 'refresh' );
248 var eventParam, previousContainer = container;
249 container = $( data );
250 container.addClass( containerInstanceClassName );
251 container.addClass( 'partial-refreshable-nav-menu customize-partial-refreshing' );
252 previousContainer.replaceWith( container );
254 instanceNumber: instanceNumber,
255 wpNavArgs: wpNavMenuArgs, // @deprecated
256 wpNavMenuArgs: wpNavMenuArgs,
257 oldContainer: previousContainer,
258 newContainer: container
260 container.removeClass( 'customize-partial-refreshing' );
261 $( document ).trigger( 'customize-preview-menu-refreshed', [ eventParam ] );
265 refreshMenuInstanceDebounced : function( instanceNumber ) {
266 if ( currentRefreshDebounced[ instanceNumber ] ) {
267 clearTimeout( currentRefreshDebounced[ instanceNumber ] );
269 currentRefreshDebounced[ instanceNumber ] = setTimeout(
271 this.refreshMenuInstance( instanceNumber );
278 api.bind( 'preview-ready', function() {
279 api.preview.bind( 'active', function() {
280 api.MenusCustomizerPreview.init();
284 }( jQuery, _, wp ) );