]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-includes/js/customize-preview-nav-menus.js
WordPress 4.4
[autoinstalls/wordpress.git] / wp-includes / js / customize-preview-nav-menus.js
1 /* global JSON, _wpCustomizePreviewNavMenusExports */
2
3 ( function( $, _, wp ) {
4         'use strict';
5
6         if ( ! wp || ! wp.customize ) { return; }
7
8         var api = wp.customize,
9                 currentRefreshDebounced = {},
10                 refreshDebounceDelay = 200,
11                 settings = {},
12                 defaultSettings = {
13                         renderQueryVar: null,
14                         renderNonceValue: null,
15                         renderNoncePostKey: null,
16                         previewCustomizeNonce: null,
17                         requestUri: '/',
18                         theme: {
19                                 active: false,
20                                 stylesheet: ''
21                         },
22                         navMenuInstanceArgs: {}
23                 };
24
25         api.MenusCustomizerPreview = {
26                 /**
27                  * Bootstrap functionality.
28                  */
29                 init : function() {
30                         var self = this, initializedSettings = {};
31
32                         settings = _.extend( {}, defaultSettings );
33                         if ( 'undefined' !== typeof _wpCustomizePreviewNavMenusExports ) {
34                                 _.extend( settings, _wpCustomizePreviewNavMenusExports );
35                         }
36
37                         api.each( function( setting, id ) {
38                                 setting.id = id;
39                                 initializedSettings[ setting.id ] = true;
40                                 self.bindListener( setting );
41                         } );
42
43                         api.preview.bind( 'setting', function( args ) {
44                                 var id, value, setting;
45                                 args = args.slice();
46                                 id = args.shift();
47                                 value = args.shift();
48
49                                 setting = api( id );
50                                 if ( ! 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
53                                 }
54                                 if ( ! setting.id ) {
55                                         // Currently customize-preview.js doesn't set the id property for each setting, like customize-controls.js does.
56                                         setting.id = id;
57                                 }
58
59                                 if ( ! initializedSettings[ setting.id ] ) {
60                                         initializedSettings[ setting.id ] = true;
61                                         if ( self.bindListener( setting ) ) {
62                                                 setting.callbacks.fireWith( setting, [ setting(), null ] );
63                                         }
64                                 }
65                         } );
66                 },
67
68                 /**
69                  *
70                  * @param {wp.customize.Value} setting
71                  * @returns {boolean} Whether the setting was bound.
72                  */
73                 bindListener : function( setting ) {
74                         var matches, themeLocation;
75
76                         matches = setting.id.match( /^nav_menu\[(-?\d+)]$/ );
77                         if ( matches ) {
78                                 setting.navMenuId = parseInt( matches[1], 10 );
79                                 setting.bind( this.onChangeNavMenuSetting );
80                                 return true;
81                         }
82
83                         matches = setting.id.match( /^nav_menu_item\[(-?\d+)]$/ );
84                         if ( matches ) {
85                                 setting.navMenuItemId = parseInt( matches[1], 10 );
86                                 setting.bind( this.onChangeNavMenuItemSetting );
87                                 return true;
88                         }
89
90                         matches = setting.id.match( /^nav_menu_locations\[(.+?)]/ );
91                         if ( matches ) {
92                                 themeLocation = matches[1];
93                                 setting.bind( _.bind( function() {
94                                         this.refreshMenuLocation( themeLocation );
95                                 }, this ) );
96                                 return true;
97                         }
98
99                         return false;
100                 },
101
102                 /**
103                  * Handle changing of a nav_menu setting.
104                  *
105                  * @this {wp.customize.Setting}
106                  */
107                 onChangeNavMenuSetting : function() {
108                         var setting = this;
109                         if ( ! setting.navMenuId ) {
110                                 throw new Error( 'Expected navMenuId property to be set.' );
111                         }
112                         api.MenusCustomizerPreview.refreshMenu( setting.navMenuId );
113                 },
114
115                 /**
116                  * Handle changing of a nav_menu_item setting.
117                  *
118                  * @this {wp.customize.Setting}
119                  * @param {object} to
120                  * @param {object} from
121                  */
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 );
125                         }
126                         if ( to && to.nav_menu_term_id ) {
127                                 api.MenusCustomizerPreview.refreshMenu( to.nav_menu_term_id );
128                         }
129                 },
130
131                 /**
132                  * Update a given menu rendered in the preview.
133                  *
134                  * @param {int} menuId
135                  */
136                 refreshMenu : function( menuId ) {
137                         var assignedLocations = [];
138
139                         api.each(function( setting, id ) {
140                                 var matches = id.match( /^nav_menu_locations\[(.+?)]/ );
141                                 if ( matches && menuId === setting() ) {
142                                         assignedLocations.push( matches[1] );
143                                 }
144                         });
145
146                         _.each( settings.navMenuInstanceArgs, function( navMenuArgs, instanceNumber ) {
147                                 if ( menuId === navMenuArgs.menu || -1 !== _.indexOf( assignedLocations, navMenuArgs.theme_location ) ) {
148                                         this.refreshMenuInstanceDebounced( instanceNumber );
149                                 }
150                         }, this );
151                 },
152
153                 /**
154                  * Refresh the menu(s) associated with a given nav menu location.
155                  *
156                  * @param {string} location
157                  */
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;
164                                 }
165                         }, this );
166                         if ( ! foundInstance ) {
167                                 api.preview.send( 'refresh' );
168                         }
169                 },
170
171                 /**
172                  * Update a specific instance of a given menu on the page.
173                  *
174                  * @param {int} instanceNumber
175                  */
176                 refreshMenuInstance : function( instanceNumber ) {
177                         var data, menuId, customized, container, request, wpNavMenuArgs, instance, containerInstanceClassName;
178
179                         if ( ! settings.navMenuInstanceArgs[ instanceNumber ] ) {
180                                 throw new Error( 'unknown_instance_number' );
181                         }
182                         instance = settings.navMenuInstanceArgs[ instanceNumber ];
183
184                         containerInstanceClassName = 'partial-refreshable-nav-menu-' + String( instanceNumber );
185                         container = $( '.' + containerInstanceClassName );
186
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();
191                         }
192
193                         if ( ! menuId || ! instance.can_partial_refresh || 0 === container.length ) {
194                                 api.preview.send( 'refresh' );
195                                 return;
196                         }
197                         menuId = parseInt( menuId, 10 );
198
199                         data = {
200                                 nonce: settings.previewCustomizeNonce, // for Customize Preview
201                                 wp_customize: 'on'
202                         };
203                         if ( ! settings.theme.active ) {
204                                 data.theme = settings.theme.stylesheet;
205                         }
206                         data[ settings.renderQueryVar ] = '1';
207
208                         // Gather settings to send in partial refresh request.
209                         customized = {};
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.
213
214                                 // Send setting if it is a nav_menu_locations[] setting.
215                                 shouldSend = shouldSend || /^nav_menu_locations\[/.test( id );
216
217                                 // Send setting if it is the setting for this menu.
218                                 shouldSend = shouldSend || id === 'nav_menu[' + String( menuId ) + ']';
219
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 ) );
222
223                                 if ( shouldSend ) {
224                                         customized[ id ] = value;
225                                 }
226                         } );
227                         data.customized = JSON.stringify( customized );
228                         data[ settings.renderNoncePostKey ] = settings.renderNonceValue;
229
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 );
234
235                         container.addClass( 'customize-partial-refreshing' );
236
237                         request = wp.ajax.send( null, {
238                                 data: data,
239                                 url: settings.requestUri
240                         } );
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' );
245                                         return;
246                                 }
247
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 );
253                                 eventParam = {
254                                         instanceNumber: instanceNumber,
255                                         wpNavArgs: wpNavMenuArgs, // @deprecated
256                                         wpNavMenuArgs: wpNavMenuArgs,
257                                         oldContainer: previousContainer,
258                                         newContainer: container
259                                 };
260                                 container.removeClass( 'customize-partial-refreshing' );
261                                 $( document ).trigger( 'customize-preview-menu-refreshed', [ eventParam ] );
262                         } );
263                 },
264
265                 refreshMenuInstanceDebounced : function( instanceNumber ) {
266                         if ( currentRefreshDebounced[ instanceNumber ] ) {
267                                 clearTimeout( currentRefreshDebounced[ instanceNumber ] );
268                         }
269                         currentRefreshDebounced[ instanceNumber ] = setTimeout(
270                                 _.bind( function() {
271                                         this.refreshMenuInstance( instanceNumber );
272                                 }, this ),
273                                 refreshDebounceDelay
274                         );
275                 }
276         };
277
278         api.bind( 'preview-ready', function() {
279                 api.preview.bind( 'active', function() {
280                         api.MenusCustomizerPreview.init();
281                 } );
282         } );
283
284 }( jQuery, _, wp ) );