run:R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
10.3 KB
2026-03-11 16:18:51
R W Run
3.41 KB
2026-03-11 16:18:51
R W Run
3.25 KB
2026-03-11 16:18:51
R W Run
1023 By
2026-03-11 16:18:51
R W Run
21.95 KB
2026-03-11 16:18:51
R W Run
5.67 KB
2026-03-11 16:18:51
R W Run
78.51 KB
2026-03-11 16:18:51
R W Run
23.73 KB
2026-03-11 16:18:51
R W Run
26.18 KB
2026-03-11 16:18:51
R W Run
8.8 KB
2026-03-11 16:18:51
R W Run
28.4 KB
2026-03-11 16:18:51
R W Run
16.11 KB
2026-03-11 16:18:51
R W Run
12.22 KB
2026-03-11 16:18:51
R W Run
2.96 KB
2026-03-11 16:18:51
R W Run
25.22 KB
2026-03-11 16:18:51
R W Run
7.67 KB
2026-03-11 16:18:51
R W Run
7.72 KB
2026-03-11 16:18:51
R W Run
3.47 KB
2026-03-11 16:18:51
R W Run
6.66 KB
2026-03-11 16:18:51
R W Run
3.59 KB
2026-03-11 16:18:51
R W Run
14.67 KB
2026-03-11 16:18:51
R W Run
4.92 KB
2026-03-11 16:18:51
R W Run
22.71 KB
2026-03-11 16:18:51
R W Run
7.64 KB
2026-03-11 16:18:51
R W Run
27.93 KB
2026-03-11 16:18:51
R W Run
10.75 KB
2026-03-11 16:18:51
R W Run
32.55 KB
2026-03-11 16:18:51
R W Run
10.44 KB
2026-03-11 16:18:51
R W Run
5.1 KB
2026-03-11 16:18:51
R W Run
2.51 KB
2026-03-11 16:18:51
R W Run
23.49 KB
2026-03-11 16:18:51
R W Run
5.81 KB
2026-03-11 16:18:51
R W Run
7.06 KB
2026-03-11 16:18:51
R W Run
1.46 KB
2026-03-11 16:18:51
R W Run
1.68 KB
2026-03-11 16:18:51
R W Run
5.39 KB
2026-03-11 16:18:51
R W Run
31 By
2026-03-11 16:18:51
R W Run
35 By
2026-03-11 16:18:51
R W Run
23.57 KB
2026-03-11 16:18:51
R W Run
25.24 KB
2026-03-11 16:18:51
R W Run
9.54 KB
2026-03-11 16:18:51
R W Run
24.24 KB
2026-03-11 16:18:51
R W Run
11.77 KB
2026-03-11 16:18:51
R W Run
28.44 KB
2026-03-11 16:18:51
R W Run
10.63 KB
2026-03-11 16:18:51
R W Run
26.15 KB
2026-03-11 16:18:51
R W Run
12.98 KB
2026-03-11 16:18:51
R W Run
42.58 KB
2026-03-11 16:18:51
R W Run
12.97 KB
2026-03-11 16:18:51
R W Run
266.99 KB
2026-03-11 16:18:51
R W Run
108.18 KB
2026-03-11 16:18:51
R W Run
22.07 KB
2026-03-11 16:18:51
R W Run
10.87 KB
2026-03-11 16:18:51
R W Run
10.51 KB
2026-03-11 16:18:51
R W Run
2.58 KB
2026-03-11 16:18:51
R W Run
0 By
2026-03-11 16:18:51
R W Run
35 By
2026-03-11 16:18:51
R W Run
4.85 KB
2026-03-11 16:18:51
R W Run
3.21 KB
2026-03-11 16:18:51
R W Run
36.32 KB
2026-03-11 16:18:51
R W Run
19.39 KB
2026-03-11 16:18:51
R W Run
67.12 KB
2026-03-11 16:18:51
R W Run
18.46 KB
2026-03-11 16:18:51
R W Run
4.56 KB
2026-03-11 16:18:51
R W Run
1.82 KB
2026-03-11 16:18:51
R W Run
3.81 KB
2026-03-11 16:18:51
R W Run
2.51 KB
2026-03-11 16:18:51
R W Run
45.88 KB
2026-03-11 16:18:51
R W Run
14.34 KB
2026-03-11 16:18:51
R W Run
4.11 KB
2026-03-11 16:18:51
R W Run
1.62 KB
2026-03-11 16:18:51
R W Run
14.88 KB
2026-03-11 16:18:51
R W Run
2.97 KB
2026-03-11 16:18:51
R W Run
10.22 KB
2026-03-11 16:18:51
R W Run
4.34 KB
2026-03-11 16:18:51
R W Run
6.62 KB
2026-03-11 16:18:51
R W Run
3.1 KB
2026-03-11 16:18:51
R W Run
3.14 KB
2026-03-11 16:18:51
R W Run
1.22 KB
2026-03-11 16:18:51
R W Run
12.89 KB
2026-03-11 16:18:51
R W Run
2.82 KB
2026-03-11 16:18:51
R W Run
22.23 KB
2026-03-11 16:18:51
R W Run
8.59 KB
2026-03-11 16:18:51
R W Run
2.79 KB
2026-03-11 16:18:51
R W Run
970 By
2026-03-11 16:18:51
R W Run
597 By
2026-03-11 16:18:51
R W Run
24.72 KB
2026-03-11 16:18:51
R W Run
7.34 KB
2026-03-11 16:18:51
R W Run
9.99 KB
2026-03-11 16:18:51
R W Run
3.54 KB
2026-03-11 16:18:51
R W Run
1.3 KB
2026-03-11 16:18:51
R W Run
444 By
2026-03-11 16:18:51
R W Run
4.58 KB
2026-03-11 16:18:51
R W Run
1.4 KB
2026-03-11 16:18:51
R W Run
569 By
2026-03-11 16:18:51
R W Run
281 By
2026-03-11 16:18:51
R W Run
20.74 KB
2026-03-11 16:18:51
R W Run
11.05 KB
2026-03-11 16:18:51
R W Run
821 By
2026-03-11 16:18:51
R W Run
351 By
2026-03-11 16:18:51
R W Run
802.97 KB
2026-03-11 16:18:51
R W Run
error_log
📄customize-preview-widgets.js
1/**
2 * @output wp-includes/js/customize-preview-widgets.js
3 */
4
5/* global _wpWidgetCustomizerPreviewSettings */
6
7/**
8 * Handles the initialization, refreshing and rendering of widget partials and sidebar widgets.
9 *
10 * @since 4.5.0
11 *
12 * @namespace wp.customize.widgetsPreview
13 *
14 * @param {jQuery} $ The jQuery object.
15 * @param {Object} _ The utilities library.
16 * @param {Object} wp Current WordPress environment instance.
17 * @param {Object} api Information from the API.
18 *
19 * @return {Object} Widget-related variables.
20 */
21wp.customize.widgetsPreview = wp.customize.WidgetCustomizerPreview = (function( $, _, wp, api ) {
22
23 var self;
24
25 self = {
26 renderedSidebars: {},
27 renderedWidgets: {},
28 registeredSidebars: [],
29 registeredWidgets: {},
30 widgetSelectors: [],
31 preview: null,
32 l10n: {
33 widgetTooltip: ''
34 },
35 selectiveRefreshableWidgets: {}
36 };
37
38 /**
39 * Initializes the widgets preview.
40 *
41 * @since 4.5.0
42 *
43 * @memberOf wp.customize.widgetsPreview
44 *
45 * @return {void}
46 */
47 self.init = function() {
48 var self = this;
49
50 self.preview = api.preview;
51 if ( ! _.isEmpty( self.selectiveRefreshableWidgets ) ) {
52 self.addPartials();
53 }
54
55 self.buildWidgetSelectors();
56 self.highlightControls();
57
58 self.preview.bind( 'highlight-widget', self.highlightWidget );
59
60 api.preview.bind( 'active', function() {
61 self.highlightControls();
62 } );
63
64 /*
65 * Refresh a partial when the controls pane requests it. This is used currently just by the
66 * Gallery widget so that when an attachment's caption is updated in the media modal,
67 * the widget in the preview will then be refreshed to show the change. Normally doing this
68 * would not be necessary because all of the state should be contained inside the changeset,
69 * as everything done in the Customizer should not make a change to the site unless the
70 * changeset itself is published. Attachments are a current exception to this rule.
71 * For a proposal to include attachments in the customized state, see #37887.
72 */
73 api.preview.bind( 'refresh-widget-partial', function( widgetId ) {
74 var partialId = 'widget[' + widgetId + ']';
75 if ( api.selectiveRefresh.partial.has( partialId ) ) {
76 api.selectiveRefresh.partial( partialId ).refresh();
77 } else if ( self.renderedWidgets[ widgetId ] ) {
78 api.preview.send( 'refresh' ); // Fallback in case theme does not support 'customize-selective-refresh-widgets'.
79 }
80 } );
81 };
82
83 self.WidgetPartial = api.selectiveRefresh.Partial.extend(/** @lends wp.customize.widgetsPreview.WidgetPartial.prototype */{
84
85 /**
86 * Represents a partial widget instance.
87 *
88 * @since 4.5.0
89 *
90 * @constructs
91 * @augments wp.customize.selectiveRefresh.Partial
92 *
93 * @alias wp.customize.widgetsPreview.WidgetPartial
94 * @memberOf wp.customize.widgetsPreview
95 *
96 * @param {string} id The partial's ID.
97 * @param {Object} options Options used to initialize the partial's
98 * instance.
99 * @param {Object} options.params The options parameters.
100 */
101 initialize: function( id, options ) {
102 var partial = this, matches;
103 matches = id.match( /^widget\[(.+)]$/ );
104 if ( ! matches ) {
105 throw new Error( 'Illegal id for widget partial.' );
106 }
107
108 partial.widgetId = matches[1];
109 partial.widgetIdParts = self.parseWidgetId( partial.widgetId );
110 options = options || {};
111 options.params = _.extend(
112 {
113 settings: [ self.getWidgetSettingId( partial.widgetId ) ],
114 containerInclusive: true
115 },
116 options.params || {}
117 );
118
119 api.selectiveRefresh.Partial.prototype.initialize.call( partial, id, options );
120 },
121
122 /**
123 * Refreshes the widget partial.
124 *
125 * @since 4.5.0
126 *
127 * @return {Promise|void} Either a promise postponing the refresh, or void.
128 */
129 refresh: function() {
130 var partial = this, refreshDeferred;
131 if ( ! self.selectiveRefreshableWidgets[ partial.widgetIdParts.idBase ] ) {
132 refreshDeferred = $.Deferred();
133 refreshDeferred.reject();
134 partial.fallback();
135 return refreshDeferred.promise();
136 } else {
137 return api.selectiveRefresh.Partial.prototype.refresh.call( partial );
138 }
139 },
140
141 /**
142 * Sends the widget-updated message to the parent so the spinner will get
143 * removed from the widget control.
144 *
145 * @inheritDoc
146 * @param {wp.customize.selectiveRefresh.Placement} placement The placement
147 * function.
148 *
149 * @return {void}
150 */
151 renderContent: function( placement ) {
152 var partial = this;
153 if ( api.selectiveRefresh.Partial.prototype.renderContent.call( partial, placement ) ) {
154 api.preview.send( 'widget-updated', partial.widgetId );
155 api.selectiveRefresh.trigger( 'widget-updated', partial );
156 }
157 }
158 });
159
160 self.SidebarPartial = api.selectiveRefresh.Partial.extend(/** @lends wp.customize.widgetsPreview.SidebarPartial.prototype */{
161
162 /**
163 * Represents a partial widget area.
164 *
165 * @since 4.5.0
166 *
167 * @class
168 * @augments wp.customize.selectiveRefresh.Partial
169 *
170 * @memberOf wp.customize.widgetsPreview
171 * @alias wp.customize.widgetsPreview.SidebarPartial
172 *
173 * @param {string} id The partial's ID.
174 * @param {Object} options Options used to initialize the partial's instance.
175 * @param {Object} options.params The options parameters.
176 */
177 initialize: function( id, options ) {
178 var partial = this, matches;
179 matches = id.match( /^sidebar\[(.+)]$/ );
180 if ( ! matches ) {
181 throw new Error( 'Illegal id for sidebar partial.' );
182 }
183 partial.sidebarId = matches[1];
184
185 options = options || {};
186 options.params = _.extend(
187 {
188 settings: [ 'sidebars_widgets[' + partial.sidebarId + ']' ]
189 },
190 options.params || {}
191 );
192
193 api.selectiveRefresh.Partial.prototype.initialize.call( partial, id, options );
194
195 if ( ! partial.params.sidebarArgs ) {
196 throw new Error( 'The sidebarArgs param was not provided.' );
197 }
198 if ( partial.params.settings.length > 1 ) {
199 throw new Error( 'Expected SidebarPartial to only have one associated setting' );
200 }
201 },
202
203 /**
204 * Sets up the partial.
205 *
206 * @since 4.5.0
207 *
208 * @return {void}
209 */
210 ready: function() {
211 var sidebarPartial = this;
212
213 // Watch for changes to the sidebar_widgets setting.
214 _.each( sidebarPartial.settings(), function( settingId ) {
215 api( settingId ).bind( _.bind( sidebarPartial.handleSettingChange, sidebarPartial ) );
216 } );
217
218 // Trigger an event for this sidebar being updated whenever a widget inside is rendered.
219 api.selectiveRefresh.bind( 'partial-content-rendered', function( placement ) {
220 var isAssignedWidgetPartial = (
221 placement.partial.extended( self.WidgetPartial ) &&
222 ( -1 !== _.indexOf( sidebarPartial.getWidgetIds(), placement.partial.widgetId ) )
223 );
224 if ( isAssignedWidgetPartial ) {
225 api.selectiveRefresh.trigger( 'sidebar-updated', sidebarPartial );
226 }
227 } );
228
229 // Make sure that a widget partial has a container in the DOM prior to a refresh.
230 api.bind( 'change', function( widgetSetting ) {
231 var widgetId, parsedId;
232 parsedId = self.parseWidgetSettingId( widgetSetting.id );
233 if ( ! parsedId ) {
234 return;
235 }
236 widgetId = parsedId.idBase;
237 if ( parsedId.number ) {
238 widgetId += '-' + String( parsedId.number );
239 }
240 if ( -1 !== _.indexOf( sidebarPartial.getWidgetIds(), widgetId ) ) {
241 sidebarPartial.ensureWidgetPlacementContainers( widgetId );
242 }
243 } );
244 },
245
246 /**
247 * Gets the before/after boundary nodes for all instances of this sidebar
248 * (usually one).
249 *
250 * Note that TreeWalker is not implemented in IE8.
251 *
252 * @since 4.5.0
253 *
254 * @return {Array.<{before: Comment, after: Comment, instanceNumber: number}>}
255 * An array with an object for each sidebar instance, containing the
256 * node before and after the sidebar instance and its instance number.
257 */
258 findDynamicSidebarBoundaryNodes: function() {
259 var partial = this, regExp, boundaryNodes = {}, recursiveCommentTraversal;
260 regExp = /^(dynamic_sidebar_before|dynamic_sidebar_after):(.+):(\d+)$/;
261 recursiveCommentTraversal = function( childNodes ) {
262 _.each( childNodes, function( node ) {
263 var matches;
264 if ( 8 === node.nodeType ) {
265 matches = node.nodeValue.match( regExp );
266 if ( ! matches || matches[2] !== partial.sidebarId ) {
267 return;
268 }
269 if ( _.isUndefined( boundaryNodes[ matches[3] ] ) ) {
270 boundaryNodes[ matches[3] ] = {
271 before: null,
272 after: null,
273 instanceNumber: parseInt( matches[3], 10 )
274 };
275 }
276 if ( 'dynamic_sidebar_before' === matches[1] ) {
277 boundaryNodes[ matches[3] ].before = node;
278 } else {
279 boundaryNodes[ matches[3] ].after = node;
280 }
281 } else if ( 1 === node.nodeType ) {
282 recursiveCommentTraversal( node.childNodes );
283 }
284 } );
285 };
286
287 recursiveCommentTraversal( document.body.childNodes );
288 return _.values( boundaryNodes );
289 },
290
291 /**
292 * Gets the placements for this partial.
293 *
294 * @since 4.5.0
295 *
296 * @return {Array} An array containing placement objects for each of the
297 * dynamic sidebar boundary nodes.
298 */
299 placements: function() {
300 var partial = this;
301 return _.map( partial.findDynamicSidebarBoundaryNodes(), function( boundaryNodes ) {
302 return new api.selectiveRefresh.Placement( {
303 partial: partial,
304 container: null,
305 startNode: boundaryNodes.before,
306 endNode: boundaryNodes.after,
307 context: {
308 instanceNumber: boundaryNodes.instanceNumber
309 }
310 } );
311 } );
312 },
313
314 /**
315 * Get the list of widget IDs associated with this widget area.
316 *
317 * @since 4.5.0
318 *
319 * @throws {Error} If there's no settingId.
320 * @throws {Error} If the setting doesn't exist in the API.
321 * @throws {Error} If the API doesn't pass an array of widget IDs.
322 *
323 * @return {Array} A shallow copy of the array containing widget IDs.
324 */
325 getWidgetIds: function() {
326 var sidebarPartial = this, settingId, widgetIds;
327 settingId = sidebarPartial.settings()[0];
328 if ( ! settingId ) {
329 throw new Error( 'Missing associated setting.' );
330 }
331 if ( ! api.has( settingId ) ) {
332 throw new Error( 'Setting does not exist.' );
333 }
334 widgetIds = api( settingId ).get();
335 if ( ! _.isArray( widgetIds ) ) {
336 throw new Error( 'Expected setting to be array of widget IDs' );
337 }
338 return widgetIds.slice( 0 );
339 },
340
341 /**
342 * Reflows widgets in the sidebar, ensuring they have the proper position in the
343 * DOM.
344 *
345 * @since 4.5.0
346 *
347 * @return {Array.<wp.customize.selectiveRefresh.Placement>} List of placements
348 * that were reflowed.
349 */
350 reflowWidgets: function() {
351 var sidebarPartial = this, sidebarPlacements, widgetIds, widgetPartials, sortedSidebarContainers = [];
352 widgetIds = sidebarPartial.getWidgetIds();
353 sidebarPlacements = sidebarPartial.placements();
354
355 widgetPartials = {};
356 _.each( widgetIds, function( widgetId ) {
357 var widgetPartial = api.selectiveRefresh.partial( 'widget[' + widgetId + ']' );
358 if ( widgetPartial ) {
359 widgetPartials[ widgetId ] = widgetPartial;
360 }
361 } );
362
363 _.each( sidebarPlacements, function( sidebarPlacement ) {
364 var sidebarWidgets = [], needsSort = false, thisPosition, lastPosition = -1;
365
366 // Gather list of widget partial containers in this sidebar, and determine if a sort is needed.
367 _.each( widgetPartials, function( widgetPartial ) {
368 _.each( widgetPartial.placements(), function( widgetPlacement ) {
369
370 if ( sidebarPlacement.context.instanceNumber === widgetPlacement.context.sidebar_instance_number ) {
371 thisPosition = widgetPlacement.container.index();
372 sidebarWidgets.push( {
373 partial: widgetPartial,
374 placement: widgetPlacement,
375 position: thisPosition
376 } );
377 if ( thisPosition < lastPosition ) {
378 needsSort = true;
379 }
380 lastPosition = thisPosition;
381 }
382 } );
383 } );
384
385 if ( needsSort ) {
386 _.each( sidebarWidgets, function( sidebarWidget ) {
387 sidebarPlacement.endNode.parentNode.insertBefore(
388 sidebarWidget.placement.container[0],
389 sidebarPlacement.endNode
390 );
391
392 // @todo Rename partial-placement-moved?
393 api.selectiveRefresh.trigger( 'partial-content-moved', sidebarWidget.placement );
394 } );
395
396 sortedSidebarContainers.push( sidebarPlacement );
397 }
398 } );
399
400 if ( sortedSidebarContainers.length > 0 ) {
401 api.selectiveRefresh.trigger( 'sidebar-updated', sidebarPartial );
402 }
403
404 return sortedSidebarContainers;
405 },
406
407 /**
408 * Makes sure there is a widget instance container in this sidebar for the given
409 * widget ID.
410 *
411 * @since 4.5.0
412 *
413 * @param {string} widgetId The widget ID.
414 *
415 * @return {wp.customize.selectiveRefresh.Partial} The widget instance partial.
416 */
417 ensureWidgetPlacementContainers: function( widgetId ) {
418 var sidebarPartial = this, widgetPartial, wasInserted = false, partialId = 'widget[' + widgetId + ']';
419 widgetPartial = api.selectiveRefresh.partial( partialId );
420 if ( ! widgetPartial ) {
421 widgetPartial = new self.WidgetPartial( partialId, {
422 params: {}
423 } );
424 }
425
426 // Make sure that there is a container element for the widget in the sidebar, if at least a placeholder.
427 _.each( sidebarPartial.placements(), function( sidebarPlacement ) {
428 var foundWidgetPlacement, widgetContainerElement;
429
430 foundWidgetPlacement = _.find( widgetPartial.placements(), function( widgetPlacement ) {
431 return ( widgetPlacement.context.sidebar_instance_number === sidebarPlacement.context.instanceNumber );
432 } );
433 if ( foundWidgetPlacement ) {
434 return;
435 }
436
437 widgetContainerElement = $(
438 sidebarPartial.params.sidebarArgs.before_widget.replace( /%1\$s/g, widgetId ).replace( /%2\$s/g, 'widget' ) +
439 sidebarPartial.params.sidebarArgs.after_widget
440 );
441
442 // Handle rare case where before_widget and after_widget are empty.
443 if ( ! widgetContainerElement[0] ) {
444 return;
445 }
446
447 widgetContainerElement.attr( 'data-customize-partial-id', widgetPartial.id );
448 widgetContainerElement.attr( 'data-customize-partial-type', 'widget' );
449 widgetContainerElement.attr( 'data-customize-widget-id', widgetId );
450
451 /*
452 * Make sure the widget container element has the customize-container context data.
453 * The sidebar_instance_number is used to disambiguate multiple instances of the
454 * same sidebar are rendered onto the template, and so the same widget is embedded
455 * multiple times.
456 */
457 widgetContainerElement.data( 'customize-partial-placement-context', {
458 'sidebar_id': sidebarPartial.sidebarId,
459 'sidebar_instance_number': sidebarPlacement.context.instanceNumber
460 } );
461
462 sidebarPlacement.endNode.parentNode.insertBefore( widgetContainerElement[0], sidebarPlacement.endNode );
463 wasInserted = true;
464 } );
465
466 api.selectiveRefresh.partial.add( widgetPartial );
467
468 if ( wasInserted ) {
469 sidebarPartial.reflowWidgets();
470 }
471
472 return widgetPartial;
473 },
474
475 /**
476 * Handles changes to the sidebars_widgets[] setting.
477 *
478 * @since 4.5.0
479 *
480 * @param {Array} newWidgetIds New widget IDs.
481 * @param {Array} oldWidgetIds Old widget IDs.
482 *
483 * @return {void}
484 */
485 handleSettingChange: function( newWidgetIds, oldWidgetIds ) {
486 var sidebarPartial = this, needsRefresh, widgetsRemoved, widgetsAdded, addedWidgetPartials = [];
487
488 needsRefresh = (
489 ( oldWidgetIds.length > 0 && 0 === newWidgetIds.length ) ||
490 ( newWidgetIds.length > 0 && 0 === oldWidgetIds.length )
491 );
492 if ( needsRefresh ) {
493 sidebarPartial.fallback();
494 return;
495 }
496
497 // Handle removal of widgets.
498 widgetsRemoved = _.difference( oldWidgetIds, newWidgetIds );
499 _.each( widgetsRemoved, function( removedWidgetId ) {
500 var widgetPartial = api.selectiveRefresh.partial( 'widget[' + removedWidgetId + ']' );
501 if ( widgetPartial ) {
502 _.each( widgetPartial.placements(), function( placement ) {
503 var isRemoved = (
504 placement.context.sidebar_id === sidebarPartial.sidebarId ||
505 ( placement.context.sidebar_args && placement.context.sidebar_args.id === sidebarPartial.sidebarId )
506 );
507 if ( isRemoved ) {
508 placement.container.remove();
509 }
510 } );
511 }
512 delete self.renderedWidgets[ removedWidgetId ];
513 } );
514
515 // Handle insertion of widgets.
516 widgetsAdded = _.difference( newWidgetIds, oldWidgetIds );
517 _.each( widgetsAdded, function( addedWidgetId ) {
518 var widgetPartial = sidebarPartial.ensureWidgetPlacementContainers( addedWidgetId );
519 addedWidgetPartials.push( widgetPartial );
520 self.renderedWidgets[ addedWidgetId ] = true;
521 } );
522
523 _.each( addedWidgetPartials, function( widgetPartial ) {
524 widgetPartial.refresh();
525 } );
526
527 api.selectiveRefresh.trigger( 'sidebar-updated', sidebarPartial );
528 },
529
530 /**
531 * Refreshes the sidebar partial.
532 *
533 * Note that the meat is handled in handleSettingChange because it has the
534 * context of which widgets were removed.
535 *
536 * @since 4.5.0
537 *
538 * @return {Promise} A promise postponing the refresh.
539 */
540 refresh: function() {
541 var partial = this, deferred = $.Deferred();
542
543 deferred.fail( function() {
544 partial.fallback();
545 } );
546
547 if ( 0 === partial.placements().length ) {
548 deferred.reject();
549 } else {
550 _.each( partial.reflowWidgets(), function( sidebarPlacement ) {
551 api.selectiveRefresh.trigger( 'partial-content-rendered', sidebarPlacement );
552 } );
553 deferred.resolve();
554 }
555
556 return deferred.promise();
557 }
558 });
559
560 api.selectiveRefresh.partialConstructor.sidebar = self.SidebarPartial;
561 api.selectiveRefresh.partialConstructor.widget = self.WidgetPartial;
562
563 /**
564 * Adds partials for the registered widget areas (sidebars).
565 *
566 * @since 4.5.0
567 *
568 * @return {void}
569 */
570 self.addPartials = function() {
571 _.each( self.registeredSidebars, function( registeredSidebar ) {
572 var partial, partialId = 'sidebar[' + registeredSidebar.id + ']';
573 partial = api.selectiveRefresh.partial( partialId );
574 if ( ! partial ) {
575 partial = new self.SidebarPartial( partialId, {
576 params: {
577 sidebarArgs: registeredSidebar
578 }
579 } );
580 api.selectiveRefresh.partial.add( partial );
581 }
582 } );
583 };
584
585 /**
586 * Calculates the selector for the sidebar's widgets based on the registered
587 * sidebar's info.
588 *
589 * @memberOf wp.customize.widgetsPreview
590 *
591 * @since 3.9.0
592 *
593 * @return {void}
594 */
595 self.buildWidgetSelectors = function() {
596 var self = this;
597
598 $.each( self.registeredSidebars, function( i, sidebar ) {
599 var widgetTpl = [
600 sidebar.before_widget,
601 sidebar.before_title,
602 sidebar.after_title,
603 sidebar.after_widget
604 ].join( '' ),
605 emptyWidget,
606 widgetSelector,
607 widgetClasses;
608
609 emptyWidget = $( widgetTpl );
610 widgetSelector = emptyWidget.prop( 'tagName' ) || '';
611 widgetClasses = emptyWidget.prop( 'className' ) || '';
612
613 // Prevent a rare case when before_widget, before_title, after_title and after_widget is empty.
614 if ( ! widgetClasses ) {
615 return;
616 }
617
618 // Remove class names that incorporate the string formatting placeholders %1$s and %2$s.
619 widgetClasses = widgetClasses.replace( /\S*%[12]\$s\S*/g, '' );
620 widgetClasses = widgetClasses.replace( /^\s+|\s+$/g, '' );
621 if ( widgetClasses ) {
622 widgetSelector += '.' + widgetClasses.split( /\s+/ ).join( '.' );
623 }
624 self.widgetSelectors.push( widgetSelector );
625 });
626 };
627
628 /**
629 * Highlights the widget on widget updates or widget control mouse overs.
630 *
631 * @memberOf wp.customize.widgetsPreview
632 *
633 * @since 3.9.0
634 * @param {string} widgetId ID of the widget.
635 *
636 * @return {void}
637 */
638 self.highlightWidget = function( widgetId ) {
639 var $body = $( document.body ),
640 $widget = $( '#' + widgetId );
641
642 $body.find( '.widget-customizer-highlighted-widget' ).removeClass( 'widget-customizer-highlighted-widget' );
643
644 $widget.addClass( 'widget-customizer-highlighted-widget' );
645 setTimeout( function() {
646 $widget.removeClass( 'widget-customizer-highlighted-widget' );
647 }, 500 );
648 };
649
650 /**
651 * Shows a title and highlights widgets on hover. On shift+clicking focuses the
652 * widget control.
653 *
654 * @memberOf wp.customize.widgetsPreview
655 *
656 * @since 3.9.0
657 *
658 * @return {void}
659 */
660 self.highlightControls = function() {
661 var self = this,
662 selector = this.widgetSelectors.join( ',' );
663
664 // Skip adding highlights if not in the customizer preview iframe.
665 if ( ! api.settings.channel ) {
666 return;
667 }
668
669 $( selector ).attr( 'title', this.l10n.widgetTooltip );
670 // Highlights widget when entering the widget editor.
671 $( document ).on( 'mouseenter', selector, function() {
672 self.preview.send( 'highlight-widget-control', $( this ).prop( 'id' ) );
673 });
674
675 // Open expand the widget control when shift+clicking the widget element.
676 $( document ).on( 'click', selector, function( e ) {
677 if ( ! e.shiftKey ) {
678 return;
679 }
680 e.preventDefault();
681
682 self.preview.send( 'focus-widget-control', $( this ).prop( 'id' ) );
683 });
684 };
685
686 /**
687 * Parses a widget ID.
688 *
689 * @memberOf wp.customize.widgetsPreview
690 *
691 * @since 4.5.0
692 *
693 * @param {string} widgetId The widget ID.
694 *
695 * @return {{idBase: string, number: number|null}} An object containing the idBase
696 * and number of the parsed widget ID.
697 */
698 self.parseWidgetId = function( widgetId ) {
699 var matches, parsed = {
700 idBase: '',
701 number: null
702 };
703
704 matches = widgetId.match( /^(.+)-(\d+)$/ );
705 if ( matches ) {
706 parsed.idBase = matches[1];
707 parsed.number = parseInt( matches[2], 10 );
708 } else {
709 parsed.idBase = widgetId; // Likely an old single widget.
710 }
711
712 return parsed;
713 };
714
715 /**
716 * Parses a widget setting ID.
717 *
718 * @memberOf wp.customize.widgetsPreview
719 *
720 * @since 4.5.0
721 *
722 * @param {string} settingId Widget setting ID.
723 *
724 * @return {{idBase: string, number: number|null}|null} Either an object containing the idBase
725 * and number of the parsed widget setting ID,
726 * or null.
727 */
728 self.parseWidgetSettingId = function( settingId ) {
729 var matches, parsed = {
730 idBase: '',
731 number: null
732 };
733
734 matches = settingId.match( /^widget_([^\[]+?)(?:\[(\d+)])?$/ );
735 if ( ! matches ) {
736 return null;
737 }
738 parsed.idBase = matches[1];
739 if ( matches[2] ) {
740 parsed.number = parseInt( matches[2], 10 );
741 }
742 return parsed;
743 };
744
745 /**
746 * Converts a widget ID into a Customizer setting ID.
747 *
748 * @memberOf wp.customize.widgetsPreview
749 *
750 * @since 4.5.0
751 *
752 * @param {string} widgetId The widget ID.
753 *
754 * @return {string} The setting ID.
755 */
756 self.getWidgetSettingId = function( widgetId ) {
757 var parsed = this.parseWidgetId( widgetId ), settingId;
758
759 settingId = 'widget_' + parsed.idBase;
760 if ( parsed.number ) {
761 settingId += '[' + String( parsed.number ) + ']';
762 }
763
764 return settingId;
765 };
766
767 api.bind( 'preview-ready', function() {
768 $.extend( self, _wpWidgetCustomizerPreviewSettings );
769 self.init();
770 });
771
772 return self;
773})( jQuery, _, wp, wp.customize );
774
Ui Ux Design – Teachers Night Out

Get in Touch

© 2024 Teachers Night Out. All Rights Reserved.