wp/wp-includes/js/customize-preview-widgets.js
changeset 9 177826044cd9
parent 7 cf61fcea0001
child 16 a86126ab1dd4
equal deleted inserted replaced
8:c7c34916027a 9:177826044cd9
       
     1 /**
       
     2  * @output wp-includes/js/customize-preview-widgets.js
       
     3  */
       
     4 
     1 /* global _wpWidgetCustomizerPreviewSettings */
     5 /* global _wpWidgetCustomizerPreviewSettings */
     2 
     6 
     3 /** @namespace wp.customize.widgetsPreview */
     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  * @returns {Object} Widget-related variables.
       
    20  */
     4 wp.customize.widgetsPreview = wp.customize.WidgetCustomizerPreview = (function( $, _, wp, api ) {
    21 wp.customize.widgetsPreview = wp.customize.WidgetCustomizerPreview = (function( $, _, wp, api ) {
     5 
    22 
     6 	var self;
    23 	var self;
     7 
    24 
     8 	self = {
    25 	self = {
    17 		},
    34 		},
    18 		selectiveRefreshableWidgets: {}
    35 		selectiveRefreshableWidgets: {}
    19 	};
    36 	};
    20 
    37 
    21 	/**
    38 	/**
    22 	 * Init widgets preview.
    39 	 * Initializes the widgets preview.
    23 	 *
    40 	 *
    24 	 * @since 4.5.0
    41 	 * @since 4.5.0
       
    42 	 *
       
    43 	 * @memberOf wp.customize.widgetsPreview
       
    44 	 *
       
    45 	 * @returns {void}
    25 	 */
    46 	 */
    26 	self.init = function() {
    47 	self.init = function() {
    27 		var self = this;
    48 		var self = this;
    28 
    49 
    29 		self.preview = api.preview;
    50 		self.preview = api.preview;
    57 				api.preview.send( 'refresh' ); // Fallback in case theme does not support 'customize-selective-refresh-widgets'.
    78 				api.preview.send( 'refresh' ); // Fallback in case theme does not support 'customize-selective-refresh-widgets'.
    58 			}
    79 			}
    59 		} );
    80 		} );
    60 	};
    81 	};
    61 
    82 
    62 	/**
       
    63 	 * Partial representing a widget instance.
       
    64 	 *
       
    65 	 * @memberOf wp.customize.widgetsPreview
       
    66 	 * @alias wp.customize.widgetsPreview.WidgetPartial
       
    67 	 *
       
    68 	 * @class
       
    69 	 * @augments wp.customize.selectiveRefresh.Partial
       
    70 	 * @since 4.5.0
       
    71 	 */
       
    72 	self.WidgetPartial = api.selectiveRefresh.Partial.extend(/** @lends wp.customize.widgetsPreview.WidgetPartial.prototype */{
    83 	self.WidgetPartial = api.selectiveRefresh.Partial.extend(/** @lends wp.customize.widgetsPreview.WidgetPartial.prototype */{
    73 
    84 
    74 		/**
    85 		/**
    75 		 * Constructor.
    86 		 * Represents a partial widget instance.
    76 		 *
    87 		 *
    77 		 * @since 4.5.0
    88 		 * @since 4.5.0
    78 		 * @param {string} id - Partial ID.
    89 		 *
    79 		 * @param {Object} options
    90 		 * @constructs
    80 		 * @param {Object} options.params
    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.
    81 		 */
   100 		 */
    82 		initialize: function( id, options ) {
   101 		initialize: function( id, options ) {
    83 			var partial = this, matches;
   102 			var partial = this, matches;
    84 			matches = id.match( /^widget\[(.+)]$/ );
   103 			matches = id.match( /^widget\[(.+)]$/ );
    85 			if ( ! matches ) {
   104 			if ( ! matches ) {
    99 
   118 
   100 			api.selectiveRefresh.Partial.prototype.initialize.call( partial, id, options );
   119 			api.selectiveRefresh.Partial.prototype.initialize.call( partial, id, options );
   101 		},
   120 		},
   102 
   121 
   103 		/**
   122 		/**
   104 		 * Refresh widget partial.
   123 		 * Refreshes the widget partial.
   105 		 *
   124 		 *
   106 		 * @returns {Promise}
   125 		 * @since 4.5.0
       
   126 		 *
       
   127 		 * @returns {Promise|void} Either a promise postponing the refresh, or void.
   107 		 */
   128 		 */
   108 		refresh: function() {
   129 		refresh: function() {
   109 			var partial = this, refreshDeferred;
   130 			var partial = this, refreshDeferred;
   110 			if ( ! self.selectiveRefreshableWidgets[ partial.widgetIdParts.idBase ] ) {
   131 			if ( ! self.selectiveRefreshableWidgets[ partial.widgetIdParts.idBase ] ) {
   111 				refreshDeferred = $.Deferred();
   132 				refreshDeferred = $.Deferred();
   116 				return api.selectiveRefresh.Partial.prototype.refresh.call( partial );
   137 				return api.selectiveRefresh.Partial.prototype.refresh.call( partial );
   117 			}
   138 			}
   118 		},
   139 		},
   119 
   140 
   120 		/**
   141 		/**
   121 		 * Send widget-updated message to parent so spinner will get removed from widget control.
   142 		 * Sends the widget-updated message to the parent so the spinner will get
   122 		 *
   143 		 * removed from the widget control.
   123 		 * @inheritdoc
   144 		 *
   124 		 * @param {wp.customize.selectiveRefresh.Placement} placement
   145 		 * @inheritDoc
       
   146 		 * @param {wp.customize.selectiveRefresh.Placement} placement The placement
       
   147 		 *                                                            function.
       
   148 		 *
       
   149 		 * @returns {void}
   125 		 */
   150 		 */
   126 		renderContent: function( placement ) {
   151 		renderContent: function( placement ) {
   127 			var partial = this;
   152 			var partial = this;
   128 			if ( api.selectiveRefresh.Partial.prototype.renderContent.call( partial, placement ) ) {
   153 			if ( api.selectiveRefresh.Partial.prototype.renderContent.call( partial, placement ) ) {
   129 				api.preview.send( 'widget-updated', partial.widgetId );
   154 				api.preview.send( 'widget-updated', partial.widgetId );
   130 				api.selectiveRefresh.trigger( 'widget-updated', partial );
   155 				api.selectiveRefresh.trigger( 'widget-updated', partial );
   131 			}
   156 			}
   132 		}
   157 		}
   133 	});
   158 	});
   134 
   159 
   135 	/**
       
   136 	 * Partial representing a widget area.
       
   137 	 *
       
   138 	 * @memberOf wp.customize.widgetsPreview
       
   139 	 * @alias wp.customize.widgetsPreview.SidebarPartial
       
   140 	 *
       
   141 	 * @class
       
   142 	 * @augments wp.customize.selectiveRefresh.Partial
       
   143 	 * @since 4.5.0
       
   144 	 */
       
   145 	self.SidebarPartial = api.selectiveRefresh.Partial.extend(/** @lends wp.customize.widgetsPreview.SidebarPartial.prototype */{
   160 	self.SidebarPartial = api.selectiveRefresh.Partial.extend(/** @lends wp.customize.widgetsPreview.SidebarPartial.prototype */{
   146 
   161 
   147 		/**
   162 		/**
   148 		 * Constructor.
   163 		 * Represents a partial widget area.
   149 		 *
   164 		 *
   150 		 * @since 4.5.0
   165 		 * @since 4.5.0
   151 		 * @param {string} id - Partial ID.
   166 		 *
   152 		 * @param {Object} options
   167 		 * @class
   153 		 * @param {Object} options.params
   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.
   154 		 */
   176 		 */
   155 		initialize: function( id, options ) {
   177 		initialize: function( id, options ) {
   156 			var partial = this, matches;
   178 			var partial = this, matches;
   157 			matches = id.match( /^sidebar\[(.+)]$/ );
   179 			matches = id.match( /^sidebar\[(.+)]$/ );
   158 			if ( ! matches ) {
   180 			if ( ! matches ) {
   177 				throw new Error( 'Expected SidebarPartial to only have one associated setting' );
   199 				throw new Error( 'Expected SidebarPartial to only have one associated setting' );
   178 			}
   200 			}
   179 		},
   201 		},
   180 
   202 
   181 		/**
   203 		/**
   182 		 * Set up the partial.
   204 		 * Sets up the partial.
   183 		 *
   205 		 *
   184 		 * @since 4.5.0
   206 		 * @since 4.5.0
       
   207 		 *
       
   208 		 * @returns {void}
   185 		 */
   209 		 */
   186 		ready: function() {
   210 		ready: function() {
   187 			var sidebarPartial = this;
   211 			var sidebarPartial = this;
   188 
   212 
   189 			// Watch for changes to the sidebar_widgets setting.
   213 			// Watch for changes to the sidebar_widgets setting.
   218 				}
   242 				}
   219 			} );
   243 			} );
   220 		},
   244 		},
   221 
   245 
   222 		/**
   246 		/**
   223 		 * Get the before/after boundary nodes for all instances of this sidebar (usually one).
   247 		 * Gets the before/after boundary nodes for all instances of this sidebar
       
   248 		 * (usually one).
   224 		 *
   249 		 *
   225 		 * Note that TreeWalker is not implemented in IE8.
   250 		 * Note that TreeWalker is not implemented in IE8.
   226 		 *
   251 		 *
   227 		 * @since 4.5.0
   252 		 * @since 4.5.0
       
   253 		 *
   228 		 * @returns {Array.<{before: Comment, after: Comment, instanceNumber: number}>}
   254 		 * @returns {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.
   229 		 */
   257 		 */
   230 		findDynamicSidebarBoundaryNodes: function() {
   258 		findDynamicSidebarBoundaryNodes: function() {
   231 			var partial = this, regExp, boundaryNodes = {}, recursiveCommentTraversal;
   259 			var partial = this, regExp, boundaryNodes = {}, recursiveCommentTraversal;
   232 			regExp = /^(dynamic_sidebar_before|dynamic_sidebar_after):(.+):(\d+)$/;
   260 			regExp = /^(dynamic_sidebar_before|dynamic_sidebar_after):(.+):(\d+)$/;
   233 			recursiveCommentTraversal = function( childNodes ) {
   261 			recursiveCommentTraversal = function( childNodes ) {
   259 			recursiveCommentTraversal( document.body.childNodes );
   287 			recursiveCommentTraversal( document.body.childNodes );
   260 			return _.values( boundaryNodes );
   288 			return _.values( boundaryNodes );
   261 		},
   289 		},
   262 
   290 
   263 		/**
   291 		/**
   264 		 * Get the placements for this partial.
   292 		 * Gets the placements for this partial.
   265 		 *
   293 		 *
   266 		 * @since 4.5.0
   294 		 * @since 4.5.0
   267 		 * @returns {Array}
   295 		 *
       
   296 		 * @returns {Array} An array containing placement objects for each of the
       
   297 		 *                  dynamic sidebar boundary nodes.
   268 		 */
   298 		 */
   269 		placements: function() {
   299 		placements: function() {
   270 			var partial = this;
   300 			var partial = this;
   271 			return _.map( partial.findDynamicSidebarBoundaryNodes(), function( boundaryNodes ) {
   301 			return _.map( partial.findDynamicSidebarBoundaryNodes(), function( boundaryNodes ) {
   272 				return new api.selectiveRefresh.Placement( {
   302 				return new api.selectiveRefresh.Placement( {
   284 		/**
   314 		/**
   285 		 * Get the list of widget IDs associated with this widget area.
   315 		 * Get the list of widget IDs associated with this widget area.
   286 		 *
   316 		 *
   287 		 * @since 4.5.0
   317 		 * @since 4.5.0
   288 		 *
   318 		 *
   289 		 * @returns {Array}
   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 		 * @returns {Array} A shallow copy of the array containing widget IDs.
   290 		 */
   324 		 */
   291 		getWidgetIds: function() {
   325 		getWidgetIds: function() {
   292 			var sidebarPartial = this, settingId, widgetIds;
   326 			var sidebarPartial = this, settingId, widgetIds;
   293 			settingId = sidebarPartial.settings()[0];
   327 			settingId = sidebarPartial.settings()[0];
   294 			if ( ! settingId ) {
   328 			if ( ! settingId ) {
   303 			}
   337 			}
   304 			return widgetIds.slice( 0 );
   338 			return widgetIds.slice( 0 );
   305 		},
   339 		},
   306 
   340 
   307 		/**
   341 		/**
   308 		 * Reflow widgets in the sidebar, ensuring they have the proper position in the DOM.
   342 		 * Reflows widgets in the sidebar, ensuring they have the proper position in the
   309 		 *
   343 		 * DOM.
   310 		 * @since 4.5.0
   344 		 *
   311 		 *
   345 		 * @since 4.5.0
   312 		 * @return {Array.<wp.customize.selectiveRefresh.Placement>} List of placements that were reflowed.
   346 		 *
       
   347 		 * @returns {Array.<wp.customize.selectiveRefresh.Placement>} List of placements
       
   348 		 *                                                            that were reflowed.
   313 		 */
   349 		 */
   314 		reflowWidgets: function() {
   350 		reflowWidgets: function() {
   315 			var sidebarPartial = this, sidebarPlacements, widgetIds, widgetPartials, sortedSidebarContainers = [];
   351 			var sidebarPartial = this, sidebarPlacements, widgetIds, widgetPartials, sortedSidebarContainers = [];
   316 			widgetIds = sidebarPartial.getWidgetIds();
   352 			widgetIds = sidebarPartial.getWidgetIds();
   317 			sidebarPlacements = sidebarPartial.placements();
   353 			sidebarPlacements = sidebarPartial.placements();
   367 
   403 
   368 			return sortedSidebarContainers;
   404 			return sortedSidebarContainers;
   369 		},
   405 		},
   370 
   406 
   371 		/**
   407 		/**
   372 		 * Make sure there is a widget instance container in this sidebar for the given widget ID.
   408 		 * Makes sure there is a widget instance container in this sidebar for the given
   373 		 *
   409 		 * widget ID.
   374 		 * @since 4.5.0
   410 		 *
   375 		 *
   411 		 * @since 4.5.0
   376 		 * @param {string} widgetId
   412 		 *
   377 		 * @returns {wp.customize.selectiveRefresh.Partial} Widget instance partial.
   413 		 * @param {string} widgetId The widget ID.
       
   414 		 *
       
   415 		 * @returns {wp.customize.selectiveRefresh.Partial} The widget instance partial.
   378 		 */
   416 		 */
   379 		ensureWidgetPlacementContainers: function( widgetId ) {
   417 		ensureWidgetPlacementContainers: function( widgetId ) {
   380 			var sidebarPartial = this, widgetPartial, wasInserted = false, partialId = 'widget[' + widgetId + ']';
   418 			var sidebarPartial = this, widgetPartial, wasInserted = false, partialId = 'widget[' + widgetId + ']';
   381 			widgetPartial = api.selectiveRefresh.partial( partialId );
   419 			widgetPartial = api.selectiveRefresh.partial( partialId );
   382 			if ( ! widgetPartial ) {
   420 			if ( ! widgetPartial ) {
   433 
   471 
   434 			return widgetPartial;
   472 			return widgetPartial;
   435 		},
   473 		},
   436 
   474 
   437 		/**
   475 		/**
   438 		 * Handle change to the sidebars_widgets[] setting.
   476 		 * Handles changes to the sidebars_widgets[] setting.
   439 		 *
   477 		 *
   440 		 * @since 4.5.0
   478 		 * @since 4.5.0
   441 		 *
   479 		 *
   442 		 * @param {Array} newWidgetIds New widget ids.
   480 		 * @param {Array} newWidgetIds New widget IDs.
   443 		 * @param {Array} oldWidgetIds Old widget ids.
   481 		 * @param {Array} oldWidgetIds Old widget IDs.
       
   482 		 *
       
   483 		 * @returns {void}
   444 		 */
   484 		 */
   445 		handleSettingChange: function( newWidgetIds, oldWidgetIds ) {
   485 		handleSettingChange: function( newWidgetIds, oldWidgetIds ) {
   446 			var sidebarPartial = this, needsRefresh, widgetsRemoved, widgetsAdded, addedWidgetPartials = [];
   486 			var sidebarPartial = this, needsRefresh, widgetsRemoved, widgetsAdded, addedWidgetPartials = [];
   447 
   487 
   448 			needsRefresh = (
   488 			needsRefresh = (
   486 
   526 
   487 			api.selectiveRefresh.trigger( 'sidebar-updated', sidebarPartial );
   527 			api.selectiveRefresh.trigger( 'sidebar-updated', sidebarPartial );
   488 		},
   528 		},
   489 
   529 
   490 		/**
   530 		/**
   491 		 * Note that the meat is handled in handleSettingChange because it has the context of which widgets were removed.
   531 		 * Refreshes the sidebar partial.
   492 		 *
   532 		 *
   493 		 * @since 4.5.0
   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 		 * @returns {Promise} A promise postponing the refresh.
   494 		 */
   539 		 */
   495 		refresh: function() {
   540 		refresh: function() {
   496 			var partial = this, deferred = $.Deferred();
   541 			var partial = this, deferred = $.Deferred();
   497 
   542 
   498 			deferred.fail( function() {
   543 			deferred.fail( function() {
   514 
   559 
   515 	api.selectiveRefresh.partialConstructor.sidebar = self.SidebarPartial;
   560 	api.selectiveRefresh.partialConstructor.sidebar = self.SidebarPartial;
   516 	api.selectiveRefresh.partialConstructor.widget = self.WidgetPartial;
   561 	api.selectiveRefresh.partialConstructor.widget = self.WidgetPartial;
   517 
   562 
   518 	/**
   563 	/**
   519 	 * Add partials for the registered widget areas (sidebars).
   564 	 * Adds partials for the registered widget areas (sidebars).
   520 	 *
   565 	 *
   521 	 * @since 4.5.0
   566 	 * @since 4.5.0
       
   567 	 *
       
   568 	 * @returns {void}
   522 	 */
   569 	 */
   523 	self.addPartials = function() {
   570 	self.addPartials = function() {
   524 		_.each( self.registeredSidebars, function( registeredSidebar ) {
   571 		_.each( self.registeredSidebars, function( registeredSidebar ) {
   525 			var partial, partialId = 'sidebar[' + registeredSidebar.id + ']';
   572 			var partial, partialId = 'sidebar[' + registeredSidebar.id + ']';
   526 			partial = api.selectiveRefresh.partial( partialId );
   573 			partial = api.selectiveRefresh.partial( partialId );
   534 			}
   581 			}
   535 		} );
   582 		} );
   536 	};
   583 	};
   537 
   584 
   538 	/**
   585 	/**
   539 	 * Calculate the selector for the sidebar's widgets based on the registered sidebar's info.
   586 	 * Calculates the selector for the sidebar's widgets based on the registered
       
   587 	 * sidebar's info.
   540 	 *
   588 	 *
   541 	 * @memberOf wp.customize.widgetsPreview
   589 	 * @memberOf wp.customize.widgetsPreview
   542 	 *
   590 	 *
   543 	 * @since 3.9.0
   591 	 * @since 3.9.0
       
   592 	 *
       
   593 	 * @returns {void}
   544 	 */
   594 	 */
   545 	self.buildWidgetSelectors = function() {
   595 	self.buildWidgetSelectors = function() {
   546 		var self = this;
   596 		var self = this;
   547 
   597 
   548 		$.each( self.registeredSidebars, function( i, sidebar ) {
   598 		$.each( self.registeredSidebars, function( i, sidebar ) {
   574 			self.widgetSelectors.push( widgetSelector );
   624 			self.widgetSelectors.push( widgetSelector );
   575 		});
   625 		});
   576 	};
   626 	};
   577 
   627 
   578 	/**
   628 	/**
   579 	 * Highlight the widget on widget updates or widget control mouse overs.
   629 	 * Highlights the widget on widget updates or widget control mouse overs.
   580 	 *
   630 	 *
   581 	 * @memberOf wp.customize.widgetsPreview
   631 	 * @memberOf wp.customize.widgetsPreview
   582 	 *
   632 	 *
   583 	 * @since 3.9.0
   633 	 * @since 3.9.0
   584 	 * @param  {string} widgetId ID of the widget.
   634 	 * @param  {string} widgetId ID of the widget.
       
   635 	 *
       
   636 	 * @returns {void}
   585 	 */
   637 	 */
   586 	self.highlightWidget = function( widgetId ) {
   638 	self.highlightWidget = function( widgetId ) {
   587 		var $body = $( document.body ),
   639 		var $body = $( document.body ),
   588 			$widget = $( '#' + widgetId );
   640 			$widget = $( '#' + widgetId );
   589 
   641 
   594 			$widget.removeClass( 'widget-customizer-highlighted-widget' );
   646 			$widget.removeClass( 'widget-customizer-highlighted-widget' );
   595 		}, 500 );
   647 		}, 500 );
   596 	};
   648 	};
   597 
   649 
   598 	/**
   650 	/**
   599 	 * Show a title and highlight widgets on hover. On shift+clicking
   651 	 * Shows a title and highlights widgets on hover. On shift+clicking focuses the
   600 	 * focus the widget control.
   652 	 * widget control.
   601 	 *
   653 	 *
   602 	 * @memberOf wp.customize.widgetsPreview
   654 	 * @memberOf wp.customize.widgetsPreview
   603 	 *
   655 	 *
   604 	 * @since 3.9.0
   656 	 * @since 3.9.0
       
   657 	 *
       
   658 	 * @returns {void}
   605 	 */
   659 	 */
   606 	self.highlightControls = function() {
   660 	self.highlightControls = function() {
   607 		var self = this,
   661 		var self = this,
   608 			selector = this.widgetSelectors.join( ',' );
   662 			selector = this.widgetSelectors.join( ',' );
   609 
   663 
   611 		if ( ! api.settings.channel ) {
   665 		if ( ! api.settings.channel ) {
   612 			return;
   666 			return;
   613 		}
   667 		}
   614 
   668 
   615 		$( selector ).attr( 'title', this.l10n.widgetTooltip );
   669 		$( selector ).attr( 'title', this.l10n.widgetTooltip );
   616 
   670 		// Highlights widget when entering the widget editor.
   617 		$( document ).on( 'mouseenter', selector, function() {
   671 		$( document ).on( 'mouseenter', selector, function() {
   618 			self.preview.send( 'highlight-widget-control', $( this ).prop( 'id' ) );
   672 			self.preview.send( 'highlight-widget-control', $( this ).prop( 'id' ) );
   619 		});
   673 		});
   620 
   674 
   621 		// Open expand the widget control when shift+clicking the widget element
   675 		// Open expand the widget control when shift+clicking the widget element
   628 			self.preview.send( 'focus-widget-control', $( this ).prop( 'id' ) );
   682 			self.preview.send( 'focus-widget-control', $( this ).prop( 'id' ) );
   629 		});
   683 		});
   630 	};
   684 	};
   631 
   685 
   632 	/**
   686 	/**
   633 	 * Parse a widget ID.
   687 	 * Parses a widget ID.
   634 	 *
   688 	 *
   635 	 * @memberOf wp.customize.widgetsPreview
   689 	 * @memberOf wp.customize.widgetsPreview
   636 	 *
   690 	 *
   637 	 * @since 4.5.0
   691 	 * @since 4.5.0
   638 	 *
   692 	 *
   639 	 * @param {string} widgetId Widget ID.
   693 	 * @param {string} widgetId The widget ID.
   640 	 * @returns {{idBase: string, number: number|null}}
   694 	 *
       
   695 	 * @returns {{idBase: string, number: number|null}} An object containing the
       
   696 	 *          idBase and number of the parsed widget ID.
   641 	 */
   697 	 */
   642 	self.parseWidgetId = function( widgetId ) {
   698 	self.parseWidgetId = function( widgetId ) {
   643 		var matches, parsed = {
   699 		var matches, parsed = {
   644 			idBase: '',
   700 			idBase: '',
   645 			number: null
   701 			number: null
   655 
   711 
   656 		return parsed;
   712 		return parsed;
   657 	};
   713 	};
   658 
   714 
   659 	/**
   715 	/**
   660 	 * Parse a widget setting ID.
   716 	 * Parses a widget setting ID.
   661 	 *
   717 	 *
   662 	 * @memberOf wp.customize.widgetsPreview
   718 	 * @memberOf wp.customize.widgetsPreview
   663 	 *
   719 	 *
   664 	 * @since 4.5.0
   720 	 * @since 4.5.0
   665 	 *
   721 	 *
   666 	 * @param {string} settingId Widget setting ID.
   722 	 * @param {string} settingId Widget setting ID.
   667 	 * @returns {{idBase: string, number: number|null}|null}
   723 	 *
       
   724 	 * @returns {{idBase: string, number: number|null}|null} Either an object
       
   725 	 *          containing the idBase and number of the parsed widget setting ID, or
       
   726 	 *          null.
   668 	 */
   727 	 */
   669 	self.parseWidgetSettingId = function( settingId ) {
   728 	self.parseWidgetSettingId = function( settingId ) {
   670 		var matches, parsed = {
   729 		var matches, parsed = {
   671 			idBase: '',
   730 			idBase: '',
   672 			number: null
   731 			number: null
   682 		}
   741 		}
   683 		return parsed;
   742 		return parsed;
   684 	};
   743 	};
   685 
   744 
   686 	/**
   745 	/**
   687 	 * Convert a widget ID into a Customizer setting ID.
   746 	 * Converts a widget ID into a Customizer setting ID.
   688 	 *
   747 	 *
   689 	 * @memberOf wp.customize.widgetsPreview
   748 	 * @memberOf wp.customize.widgetsPreview
   690 	 *
   749 	 *
   691 	 * @since 4.5.0
   750 	 * @since 4.5.0
   692 	 *
   751 	 *
   693 	 * @param {string} widgetId Widget ID.
   752 	 * @param {string} widgetId The widget ID.
   694 	 * @returns {string} settingId Setting ID.
   753 	 *
       
   754 	 * @returns {string} The setting ID.
   695 	 */
   755 	 */
   696 	self.getWidgetSettingId = function( widgetId ) {
   756 	self.getWidgetSettingId = function( widgetId ) {
   697 		var parsed = this.parseWidgetId( widgetId ), settingId;
   757 		var parsed = this.parseWidgetId( widgetId ), settingId;
   698 
   758 
   699 		settingId = 'widget_' + parsed.idBase;
   759 		settingId = 'widget_' + parsed.idBase;