wp/wp-admin/js/customize-controls.js
changeset 16 a86126ab1dd4
parent 9 177826044cd9
child 18 be944660c56a
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
    23 		 * @augments   wp.customize.Notification
    23 		 * @augments   wp.customize.Notification
    24 		 *
    24 		 *
    25 		 * @since 4.9.0
    25 		 * @since 4.9.0
    26 		 *
    26 		 *
    27 		 * @param {string} code - Code.
    27 		 * @param {string} code - Code.
    28 		 * @param {object} params - Params.
    28 		 * @param {Object} params - Params.
    29 		 */
    29 		 */
    30 		initialize: function( code, params ) {
    30 		initialize: function( code, params ) {
    31 			var notification = this;
    31 			var notification = this;
    32 			api.Notification.prototype.initialize.call( notification, code, params );
    32 			api.Notification.prototype.initialize.call( notification, code, params );
    33 			notification.containerClasses += ' notification-overlay';
    33 			notification.containerClasses += ' notification-overlay';
    53 		 * Stop propagation on escape key presses, but also dismiss notification if it is dismissible.
    53 		 * Stop propagation on escape key presses, but also dismiss notification if it is dismissible.
    54 		 *
    54 		 *
    55 		 * @since 4.9.0
    55 		 * @since 4.9.0
    56 		 *
    56 		 *
    57 		 * @param {jQuery.Event} event - Event.
    57 		 * @param {jQuery.Event} event - Event.
    58 		 * @returns {void}
    58 		 * @return {void}
    59 		 */
    59 		 */
    60 		handleEscape: function( event ) {
    60 		handleEscape: function( event ) {
    61 			var notification = this;
    61 			var notification = this;
    62 			if ( 27 === event.which ) {
    62 			if ( 27 === event.which ) {
    63 				event.stopPropagation();
    63 				event.stopPropagation();
    92 		 * @since 4.9.0
    92 		 * @since 4.9.0
    93 		 *
    93 		 *
    94 		 * @constructs wp.customize.Notifications
    94 		 * @constructs wp.customize.Notifications
    95 		 * @augments   wp.customize.Values
    95 		 * @augments   wp.customize.Values
    96 		 *
    96 		 *
    97 		 * @param {object}  options - Options.
    97 		 * @param {Object}  options - Options.
    98 		 * @param {jQuery}  [options.container] - Container element for notifications. This can be injected later.
    98 		 * @param {jQuery}  [options.container] - Container element for notifications. This can be injected later.
    99 		 * @param {boolean} [options.alt] - Whether alternative style should be used when rendering notifications.
    99 		 * @param {boolean} [options.alt] - Whether alternative style should be used when rendering notifications.
   100 		 *
   100 		 *
   101 		 * @returns {void}
   101 		 * @return {void}
   102 		 */
   102 		 */
   103 		initialize: function( options ) {
   103 		initialize: function( options ) {
   104 			var collection = this;
   104 			var collection = this;
   105 
   105 
   106 			api.Values.prototype.initialize.call( collection, options );
   106 			api.Values.prototype.initialize.call( collection, options );
   135 		 *
   135 		 *
   136 		 * @since 4.9.0
   136 		 * @since 4.9.0
   137 		 *
   137 		 *
   138 		 * @param {string|wp.customize.Notification} notification - Notification object to add. Alternatively code may be supplied, and in that case the second notificationObject argument must be supplied.
   138 		 * @param {string|wp.customize.Notification} notification - Notification object to add. Alternatively code may be supplied, and in that case the second notificationObject argument must be supplied.
   139 		 * @param {wp.customize.Notification} [notificationObject] - Notification to add when first argument is the code string.
   139 		 * @param {wp.customize.Notification} [notificationObject] - Notification to add when first argument is the code string.
   140 		 * @returns {wp.customize.Notification} Added notification (or existing instance if it was already added).
   140 		 * @return {wp.customize.Notification} Added notification (or existing instance if it was already added).
   141 		 */
   141 		 */
   142 		add: function( notification, notificationObject ) {
   142 		add: function( notification, notificationObject ) {
   143 			var collection = this, code, instance;
   143 			var collection = this, code, instance;
   144 			if ( 'string' === typeof notification ) {
   144 			if ( 'string' === typeof notification ) {
   145 				code = notification;
   145 				code = notification;
   172 		 * Get list of notifications.
   172 		 * Get list of notifications.
   173 		 *
   173 		 *
   174 		 * Notifications may be sorted by type followed by added time.
   174 		 * Notifications may be sorted by type followed by added time.
   175 		 *
   175 		 *
   176 		 * @since 4.9.0
   176 		 * @since 4.9.0
   177 		 * @param {object}  args - Args.
   177 		 * @param {Object}  args - Args.
   178 		 * @param {boolean} [args.sort=false] - Whether to return the notifications sorted.
   178 		 * @param {boolean} [args.sort=false] - Whether to return the notifications sorted.
   179 		 * @return {Array.<wp.customize.Notification>} Notifications.
   179 		 * @return {Array.<wp.customize.Notification>} Notifications.
   180 		 */
   180 		 */
   181 		get: function( args ) {
   181 		get: function( args ) {
   182 			var collection = this, notifications, errorTypePriorities, params;
   182 			var collection = this, notifications, errorTypePriorities, params;
   209 
   209 
   210 		/**
   210 		/**
   211 		 * Render notifications area.
   211 		 * Render notifications area.
   212 		 *
   212 		 *
   213 		 * @since 4.9.0
   213 		 * @since 4.9.0
   214 		 * @returns {void}
   214 		 * @return {void}
   215 		 */
   215 		 */
   216 		render: function() {
   216 		render: function() {
   217 			var collection = this,
   217 			var collection = this,
   218 				notifications, hadOverlayNotification = false, hasOverlayNotification, overlayNotifications = [],
   218 				notifications, hadOverlayNotification = false, hasOverlayNotification, overlayNotifications = [],
   219 				previousNotificationsByCode = {},
   219 				previousNotificationsByCode = {},
   302 		 * Constrain focus on focus container.
   302 		 * Constrain focus on focus container.
   303 		 *
   303 		 *
   304 		 * @since 4.9.0
   304 		 * @since 4.9.0
   305 		 *
   305 		 *
   306 		 * @param {jQuery.Event} event - Event.
   306 		 * @param {jQuery.Event} event - Event.
   307 		 * @returns {void}
   307 		 * @return {void}
   308 		 */
   308 		 */
   309 		constrainFocus: function constrainFocus( event ) {
   309 		constrainFocus: function constrainFocus( event ) {
   310 			var collection = this, focusableElements;
   310 			var collection = this, focusableElements;
   311 
   311 
   312 			// Prevent keys from escaping.
   312 			// Prevent keys from escaping.
   360 		 *
   360 		 *
   361 		 * @since 3.4.0
   361 		 * @since 3.4.0
   362 		 *
   362 		 *
   363 		 * @param {string}  id                          - The setting ID.
   363 		 * @param {string}  id                          - The setting ID.
   364 		 * @param {*}       value                       - The initial value of the setting.
   364 		 * @param {*}       value                       - The initial value of the setting.
   365 		 * @param {object}  [options={}]                - Options.
   365 		 * @param {Object}  [options={}]                - Options.
   366 		 * @param {string}  [options.transport=refresh] - The transport to use for previewing. Supports 'refresh' and 'postMessage'.
   366 		 * @param {string}  [options.transport=refresh] - The transport to use for previewing. Supports 'refresh' and 'postMessage'.
   367 		 * @param {boolean} [options.dirty=false]       - Whether the setting should be considered initially dirty.
   367 		 * @param {boolean} [options.dirty=false]       - Whether the setting should be considered initially dirty.
   368 		 * @param {object}  [options.previewer]         - The Previewer instance to sync with. Defaults to wp.customize.previewer.
   368 		 * @param {Object}  [options.previewer]         - The Previewer instance to sync with. Defaults to wp.customize.previewer.
   369 		 */
   369 		 */
   370 		initialize: function( id, value, options ) {
   370 		initialize: function( id, value, options ) {
   371 			var setting = this, params;
   371 			var setting = this, params;
   372 			params = _.extend(
   372 			params = _.extend(
   373 				{ previewer: api.previewer },
   373 				{ previewer: api.previewer },
   396 		 * previewer.send() call to then fallback to refresh will not work.
   396 		 * previewer.send() call to then fallback to refresh will not work.
   397 		 *
   397 		 *
   398 		 * @since 3.4.0
   398 		 * @since 3.4.0
   399 		 * @access public
   399 		 * @access public
   400 		 *
   400 		 *
   401 		 * @returns {void}
   401 		 * @return {void}
   402 		 */
   402 		 */
   403 		preview: function() {
   403 		preview: function() {
   404 			var setting = this, transport;
   404 			var setting = this, transport;
   405 			transport = setting.transport;
   405 			transport = setting.transport;
   406 
   406 
   417 
   417 
   418 		/**
   418 		/**
   419 		 * Find controls associated with this setting.
   419 		 * Find controls associated with this setting.
   420 		 *
   420 		 *
   421 		 * @since 4.6.0
   421 		 * @since 4.6.0
   422 		 * @returns {wp.customize.Control[]} Controls associated with setting.
   422 		 * @return {wp.customize.Control[]} Controls associated with setting.
   423 		 */
   423 		 */
   424 		findControls: function() {
   424 		findControls: function() {
   425 			var setting = this, controls = [];
   425 			var setting = this, controls = [];
   426 			api.control.each( function( control ) {
   426 			api.control.each( function( control ) {
   427 				_.each( control.settings, function( controlSetting ) {
   427 				_.each( control.settings, function( controlSetting ) {
   493 	 * @alias wp.customize.dirtyValues
   493 	 * @alias wp.customize.dirtyValues
   494 	 *
   494 	 *
   495 	 * @since 4.7.0
   495 	 * @since 4.7.0
   496 	 * @access public
   496 	 * @access public
   497 	 *
   497 	 *
   498 	 * @param {object} [options] Options.
   498 	 * @param {Object} [options] Options.
   499 	 * @param {boolean} [options.unsaved=false] Whether only values not saved yet into a changeset will be returned (differential changes).
   499 	 * @param {boolean} [options.unsaved=false] Whether only values not saved yet into a changeset will be returned (differential changes).
   500 	 * @returns {object} Dirty setting values.
   500 	 * @return {Object} Dirty setting values.
   501 	 */
   501 	 */
   502 	api.dirtyValues = function dirtyValues( options ) {
   502 	api.dirtyValues = function dirtyValues( options ) {
   503 		var values = {};
   503 		var values = {};
   504 		api.each( function( setting ) {
   504 		api.each( function( setting ) {
   505 			var settingRevision;
   505 			var settingRevision;
   526 	 * @alias wp.customize.requestChangesetUpdate
   526 	 * @alias wp.customize.requestChangesetUpdate
   527 	 *
   527 	 *
   528 	 * @since 4.7.0
   528 	 * @since 4.7.0
   529 	 * @access public
   529 	 * @access public
   530 	 *
   530 	 *
   531 	 * @param {object}  [changes] - Mapping of setting IDs to setting params each normally including a value property, or mapping to null.
   531 	 * @param {Object}  [changes] - Mapping of setting IDs to setting params each normally including a value property, or mapping to null.
   532 	 *                             If not provided, then the changes will still be obtained from unsaved dirty settings.
   532 	 *                             If not provided, then the changes will still be obtained from unsaved dirty settings.
   533 	 * @param {object}  [args] - Additional options for the save request.
   533 	 * @param {Object}  [args] - Additional options for the save request.
   534 	 * @param {boolean} [args.autosave=false] - Whether changes will be stored in autosave revision if the changeset has been promoted from an auto-draft.
   534 	 * @param {boolean} [args.autosave=false] - Whether changes will be stored in autosave revision if the changeset has been promoted from an auto-draft.
   535 	 * @param {boolean} [args.force=false] - Send request to update even when there are no changes to submit. This can be used to request the latest status of the changeset on the server.
   535 	 * @param {boolean} [args.force=false] - Send request to update even when there are no changes to submit. This can be used to request the latest status of the changeset on the server.
   536 	 * @param {string}  [args.title] - Title to update in the changeset. Optional.
   536 	 * @param {string}  [args.title] - Title to update in the changeset. Optional.
   537 	 * @param {string}  [args.date] - Date to update in the changeset. Optional.
   537 	 * @param {string}  [args.date] - Date to update in the changeset. Optional.
   538 	 * @returns {jQuery.Promise} Promise resolving with the response data.
   538 	 * @return {jQuery.Promise} Promise resolving with the response data.
   539 	 */
   539 	 */
   540 	api.requestChangesetUpdate = function requestChangesetUpdate( changes, args ) {
   540 	api.requestChangesetUpdate = function requestChangesetUpdate( changes, args ) {
   541 		var deferred, request, submittedChanges = {}, data, submittedArgs;
   541 		var deferred, request, submittedChanges = {}, data, submittedArgs;
   542 		deferred = new $.Deferred();
   542 		deferred = new $.Deferred();
   543 
   543 
   576 		if ( ! submittedArgs.force && _.isEmpty( submittedChanges ) && null === submittedArgs.title && null === submittedArgs.date ) {
   576 		if ( ! submittedArgs.force && _.isEmpty( submittedChanges ) && null === submittedArgs.title && null === submittedArgs.date ) {
   577 			deferred.resolve( {} );
   577 			deferred.resolve( {} );
   578 			return deferred.promise();
   578 			return deferred.promise();
   579 		}
   579 		}
   580 
   580 
   581 		// A status would cause a revision to be made, and for this wp.customize.previewer.save() should be used. Status is also disallowed for revisions regardless.
   581 		// A status would cause a revision to be made, and for this wp.customize.previewer.save() should be used.
       
   582 		// Status is also disallowed for revisions regardless.
   582 		if ( submittedArgs.status ) {
   583 		if ( submittedArgs.status ) {
   583 			return deferred.reject( { code: 'illegal_status_in_changeset_update' } ).promise();
   584 			return deferred.reject( { code: 'illegal_status_in_changeset_update' } ).promise();
   584 		}
   585 		}
   585 
   586 
   586 		// Dates not beung allowed for revisions are is a technical limitation of post revisions.
   587 		// Dates not beung allowed for revisions are is a technical limitation of post revisions.
   733 	 *
   734 	 *
   734 	 * @since 4.1.0
   735 	 * @since 4.1.0
   735 	 *
   736 	 *
   736 	 * @param {(wp.customize.Panel|wp.customize.Section|wp.customize.Control)} a
   737 	 * @param {(wp.customize.Panel|wp.customize.Section|wp.customize.Control)} a
   737 	 * @param {(wp.customize.Panel|wp.customize.Section|wp.customize.Control)} b
   738 	 * @param {(wp.customize.Panel|wp.customize.Section|wp.customize.Control)} b
   738 	 * @returns {Number}
   739 	 * @return {number}
   739 	 */
   740 	 */
   740 	api.utils.prioritySort = function ( a, b ) {
   741 	api.utils.prioritySort = function ( a, b ) {
   741 		if ( a.priority() === b.priority() && typeof a.params.instanceNumber === 'number' && typeof b.params.instanceNumber === 'number' ) {
   742 		if ( a.priority() === b.priority() && typeof a.params.instanceNumber === 'number' && typeof b.params.instanceNumber === 'number' ) {
   742 			return a.params.instanceNumber - b.params.instanceNumber;
   743 			return a.params.instanceNumber - b.params.instanceNumber;
   743 		} else {
   744 		} else {
   751 	 * @alias wp.customize.utils.isKeydownButNotEnterEvent
   752 	 * @alias wp.customize.utils.isKeydownButNotEnterEvent
   752 	 *
   753 	 *
   753 	 * @since 4.1.0
   754 	 * @since 4.1.0
   754 	 *
   755 	 *
   755 	 * @param {jQuery.Event} event
   756 	 * @param {jQuery.Event} event
   756 	 * @returns {boolean}
   757 	 * @return {boolean}
   757 	 */
   758 	 */
   758 	api.utils.isKeydownButNotEnterEvent = function ( event ) {
   759 	api.utils.isKeydownButNotEnterEvent = function ( event ) {
   759 		return ( 'keydown' === event.type && 13 !== event.which );
   760 		return ( 'keydown' === event.type && 13 !== event.which );
   760 	};
   761 	};
   761 
   762 
   766 	 *
   767 	 *
   767 	 * @since 4.1.0
   768 	 * @since 4.1.0
   768 	 *
   769 	 *
   769 	 * @param {Array|jQuery} listA
   770 	 * @param {Array|jQuery} listA
   770 	 * @param {Array|jQuery} listB
   771 	 * @param {Array|jQuery} listB
   771 	 * @returns {boolean}
   772 	 * @return {boolean}
   772 	 */
   773 	 */
   773 	api.utils.areElementListsEqual = function ( listA, listB ) {
   774 	api.utils.areElementListsEqual = function ( listA, listB ) {
   774 		var equal = (
   775 		var equal = (
   775 			listA.length === listB.length && // if lists are different lengths, then naturally they are not equal
   776 			listA.length === listB.length && // If lists are different lengths, then naturally they are not equal.
   776 			-1 === _.indexOf( _.map( // are there any false values in the list returned by map?
   777 			-1 === _.indexOf( _.map(         // Are there any false values in the list returned by map?
   777 				_.zip( listA, listB ), // pair up each element between the two lists
   778 				_.zip( listA, listB ),       // Pair up each element between the two lists.
   778 				function ( pair ) {
   779 				function ( pair ) {
   779 					return $( pair[0] ).is( pair[1] ); // compare to see if each pair are equal
   780 					return $( pair[0] ).is( pair[1] ); // Compare to see if each pair is equal.
   780 				}
   781 				}
   781 			), false ) // check for presence of false in map's return value
   782 			), false ) // Check for presence of false in map's return value.
   782 		);
   783 		);
   783 		return equal;
   784 		return equal;
   784 	};
   785 	};
   785 
   786 
   786 	/**
   787 	/**
   793 	 * @alias wp.customize.utils.highlightButton
   794 	 * @alias wp.customize.utils.highlightButton
   794 	 *
   795 	 *
   795 	 * @since 4.9.0
   796 	 * @since 4.9.0
   796 	 *
   797 	 *
   797 	 * @param {jQuery} button - The element to highlight.
   798 	 * @param {jQuery} button - The element to highlight.
   798 	 * @param {object} [options] - Options.
   799 	 * @param {Object} [options] - Options.
   799 	 * @param {number} [options.delay=0] - Delay in milliseconds.
   800 	 * @param {number} [options.delay=0] - Delay in milliseconds.
   800 	 * @param {jQuery} [options.focusTarget] - A target for user focus that defaults to the highlighted element.
   801 	 * @param {jQuery} [options.focusTarget] - A target for user focus that defaults to the highlighted element.
   801 	 *                                         If the user focuses the target before the delay passes, the reminder
   802 	 *                                         If the user focuses the target before the delay passes, the reminder
   802 	 *                                         is canceled. This option exists to accommodate compound buttons
   803 	 *                                         is canceled. This option exists to accommodate compound buttons
   803 	 *                                         containing auxiliary UI, such as the Publish button augmented with a
   804 	 *                                         containing auxiliary UI, such as the Publish button augmented with a
   804 	 *                                         Settings button.
   805 	 *                                         Settings button.
   805 	 * @returns {Function} An idempotent function that cancels the reminder.
   806 	 * @return {Function} An idempotent function that cancels the reminder.
   806 	 */
   807 	 */
   807 	api.utils.highlightButton = function highlightButton( button, options ) {
   808 	api.utils.highlightButton = function highlightButton( button, options ) {
   808 		var animationClass = 'button-see-me',
   809 		var animationClass = 'button-see-me',
   809 			canceled = false,
   810 			canceled = false,
   810 			params;
   811 			params;
   847 	 *
   848 	 *
   848 	 * @alias wp.customize.utils.getCurrentTimestamp
   849 	 * @alias wp.customize.utils.getCurrentTimestamp
   849 	 *
   850 	 *
   850 	 * @since 4.9.0
   851 	 * @since 4.9.0
   851 	 *
   852 	 *
   852 	 * @returns {int} Current timestamp.
   853 	 * @return {number} Current timestamp.
   853 	 */
   854 	 */
   854 	api.utils.getCurrentTimestamp = function getCurrentTimestamp() {
   855 	api.utils.getCurrentTimestamp = function getCurrentTimestamp() {
   855 		var currentDate, currentClientTimestamp, timestampDifferential;
   856 		var currentDate, currentClientTimestamp, timestampDifferential;
   856 		currentClientTimestamp = _.now();
   857 		currentClientTimestamp = _.now();
   857 		currentDate = new Date( api.settings.initialServerDate.replace( /-/g, '/' ) );
   858 		currentDate = new Date( api.settings.initialServerDate.replace( /-/g, '/' ) );
   866 	 *
   867 	 *
   867 	 * @alias wp.customize.utils.getRemainingTime
   868 	 * @alias wp.customize.utils.getRemainingTime
   868 	 *
   869 	 *
   869 	 * @since 4.9.0
   870 	 * @since 4.9.0
   870 	 *
   871 	 *
   871 	 * @param {string|int|Date} datetime - Date time or timestamp of the future date.
   872 	 * @param {string|number|Date} datetime - Date time or timestamp of the future date.
   872 	 * @return {int} remainingTime - Remaining time in milliseconds.
   873 	 * @return {number} remainingTime - Remaining time in milliseconds.
   873 	 */
   874 	 */
   874 	api.utils.getRemainingTime = function getRemainingTime( datetime ) {
   875 	api.utils.getRemainingTime = function getRemainingTime( datetime ) {
   875 		var millisecondsDivider = 1000, remainingTime, timestamp;
   876 		var millisecondsDivider = 1000, remainingTime, timestamp;
   876 		if ( datetime instanceof Date ) {
   877 		if ( datetime instanceof Date ) {
   877 			timestamp = datetime.getTime();
   878 			timestamp = datetime.getTime();
   891 	 *
   892 	 *
   892 	 * @since 4.7.0
   893 	 * @since 4.7.0
   893 	 *
   894 	 *
   894 	 * @ignore
   895 	 * @ignore
   895 	 *
   896 	 *
   896 	 * @returns {string|null} Normalized `transitionend` event name or null if CSS transitions are not supported.
   897 	 * @return {string|null} Normalized `transitionend` event name or null if CSS transitions are not supported.
   897 	 */
   898 	 */
   898 	normalizedTransitionendEventName = (function () {
   899 	normalizedTransitionendEventName = (function () {
   899 		var el, transitions, prop;
   900 		var el, transitions, prop;
   900 		el = document.createElement( 'div' );
   901 		el = document.createElement( 'div' );
   901 		transitions = {
   902 		transitions = {
   937 		 * @since 4.1.0
   938 		 * @since 4.1.0
   938 		 *
   939 		 *
   939 		 * @borrows wp.customize~focus as focus
   940 		 * @borrows wp.customize~focus as focus
   940 		 *
   941 		 *
   941 		 * @param {string}  id - The ID for the container.
   942 		 * @param {string}  id - The ID for the container.
   942 		 * @param {object}  options - Object containing one property: params.
   943 		 * @param {Object}  options - Object containing one property: params.
   943 		 * @param {string}  options.title - Title shown when panel is collapsed and expanded.
   944 		 * @param {string}  options.title - Title shown when panel is collapsed and expanded.
   944 		 * @param {string}  [options.description] - Description shown at the top of the panel.
   945 		 * @param {string}  [options.description] - Description shown at the top of the panel.
   945 		 * @param {number}  [options.priority=100] - The sort priority for the panel.
   946 		 * @param {number}  [options.priority=100] - The sort priority for the panel.
   946 		 * @param {string}  [options.templateId] - Template selector for container.
   947 		 * @param {string}  [options.templateId] - Template selector for container.
   947 		 * @param {string}  [options.type=default] - The type of the panel. See wp.customize.panelConstructor.
   948 		 * @param {string}  [options.type=default] - The type of the panel. See wp.customize.panelConstructor.
   948 		 * @param {string}  [options.content] - The markup to be used for the panel container. If empty, a JS template is used.
   949 		 * @param {string}  [options.content] - The markup to be used for the panel container. If empty, a JS template is used.
   949 		 * @param {boolean} [options.active=true] - Whether the panel is active or not.
   950 		 * @param {boolean} [options.active=true] - Whether the panel is active or not.
   950 		 * @param {object}  [options.params] - Deprecated wrapper for the above properties.
   951 		 * @param {Object}  [options.params] - Deprecated wrapper for the above properties.
   951 		 */
   952 		 */
   952 		initialize: function ( id, options ) {
   953 		initialize: function ( id, options ) {
   953 			var container = this;
   954 			var container = this;
   954 			container.id = id;
   955 			container.id = id;
   955 
   956 
  1012 
  1013 
  1013 		/**
  1014 		/**
  1014 		 * Get the element that will contain the notifications.
  1015 		 * Get the element that will contain the notifications.
  1015 		 *
  1016 		 *
  1016 		 * @since 4.9.0
  1017 		 * @since 4.9.0
  1017 		 * @returns {jQuery} Notification container element.
  1018 		 * @return {jQuery} Notification container element.
  1018 		 */
  1019 		 */
  1019 		getNotificationsContainerElement: function() {
  1020 		getNotificationsContainerElement: function() {
  1020 			var container = this;
  1021 			var container = this;
  1021 			return container.contentContainer.find( '.customize-control-notifications-container:first' );
  1022 			return container.contentContainer.find( '.customize-control-notifications-container:first' );
  1022 		},
  1023 		},
  1023 
  1024 
  1024 		/**
  1025 		/**
  1025 		 * Set up notifications.
  1026 		 * Set up notifications.
  1026 		 *
  1027 		 *
  1027 		 * @since 4.9.0
  1028 		 * @since 4.9.0
  1028 		 * @returns {void}
  1029 		 * @return {void}
  1029 		 */
  1030 		 */
  1030 		setupNotifications: function() {
  1031 		setupNotifications: function() {
  1031 			var container = this, renderNotifications;
  1032 			var container = this, renderNotifications;
  1032 			container.notifications.container = container.getNotificationsContainerElement();
  1033 			container.notifications.container = container.getNotificationsContainerElement();
  1033 
  1034 
  1052 		/**
  1053 		/**
  1053 		 * Get the child models associated with this parent, sorting them by their priority Value.
  1054 		 * Get the child models associated with this parent, sorting them by their priority Value.
  1054 		 *
  1055 		 *
  1055 		 * @since 4.1.0
  1056 		 * @since 4.1.0
  1056 		 *
  1057 		 *
  1057 		 * @param {String} parentType
  1058 		 * @param {string} parentType
  1058 		 * @param {String} childType
  1059 		 * @param {string} childType
  1059 		 * @returns {Array}
  1060 		 * @return {Array}
  1060 		 */
  1061 		 */
  1061 		_children: function ( parentType, childType ) {
  1062 		_children: function ( parentType, childType ) {
  1062 			var parent = this,
  1063 			var parent = this,
  1063 				children = [];
  1064 				children = [];
  1064 			api[ childType ].each( function ( child ) {
  1065 			api[ childType ].each( function ( child ) {
  1126 					} );
  1127 					} );
  1127 				}
  1128 				}
  1128 			}
  1129 			}
  1129 
  1130 
  1130 			if ( ! $.contains( document, headContainer.get( 0 ) ) ) {
  1131 			if ( ! $.contains( document, headContainer.get( 0 ) ) ) {
  1131 				// If the element is not in the DOM, then jQuery.fn.slideUp() does nothing. In this case, a hard toggle is required instead.
  1132 				// If the element is not in the DOM, then jQuery.fn.slideUp() does nothing.
       
  1133 				// In this case, a hard toggle is required instead.
  1132 				headContainer.toggle( active );
  1134 				headContainer.toggle( active );
  1133 				if ( args.completeCallback ) {
  1135 				if ( args.completeCallback ) {
  1134 					args.completeCallback();
  1136 					args.completeCallback();
  1135 				}
  1137 				}
  1136 			} else if ( active ) {
  1138 			} else if ( active ) {
  1150 		},
  1152 		},
  1151 
  1153 
  1152 		/**
  1154 		/**
  1153 		 * @since 4.1.0
  1155 		 * @since 4.1.0
  1154 		 *
  1156 		 *
  1155 		 * @params {Boolean} active
  1157 		 * @param {boolean} active
  1156 		 * @param {Object}   [params]
  1158 		 * @param {Object}  [params]
  1157 		 * @returns {Boolean} false if state already applied
  1159 		 * @return {boolean} False if state already applied.
  1158 		 */
  1160 		 */
  1159 		_toggleActive: function ( active, params ) {
  1161 		_toggleActive: function ( active, params ) {
  1160 			var self = this;
  1162 			var self = this;
  1161 			params = params || {};
  1163 			params = params || {};
  1162 			if ( ( active && this.active.get() ) || ( ! active && ! this.active.get() ) ) {
  1164 			if ( ( active && this.active.get() ) || ( ! active && ! this.active.get() ) ) {
  1171 			}
  1173 			}
  1172 		},
  1174 		},
  1173 
  1175 
  1174 		/**
  1176 		/**
  1175 		 * @param {Object} [params]
  1177 		 * @param {Object} [params]
  1176 		 * @returns {Boolean} false if already active
  1178 		 * @return {boolean} False if already active.
  1177 		 */
  1179 		 */
  1178 		activate: function ( params ) {
  1180 		activate: function ( params ) {
  1179 			return this._toggleActive( true, params );
  1181 			return this._toggleActive( true, params );
  1180 		},
  1182 		},
  1181 
  1183 
  1182 		/**
  1184 		/**
  1183 		 * @param {Object} [params]
  1185 		 * @param {Object} [params]
  1184 		 * @returns {Boolean} false if already inactive
  1186 		 * @return {boolean} False if already inactive.
  1185 		 */
  1187 		 */
  1186 		deactivate: function ( params ) {
  1188 		deactivate: function ( params ) {
  1187 			return this._toggleActive( false, params );
  1189 			return this._toggleActive( false, params );
  1188 		},
  1190 		},
  1189 
  1191 
  1196 		},
  1198 		},
  1197 
  1199 
  1198 		/**
  1200 		/**
  1199 		 * Handle the toggle logic for expand/collapse.
  1201 		 * Handle the toggle logic for expand/collapse.
  1200 		 *
  1202 		 *
  1201 		 * @param {Boolean}  expanded - The new state to apply.
  1203 		 * @param {boolean}  expanded - The new state to apply.
  1202 		 * @param {Object}   [params] - Object containing options for expand/collapse.
  1204 		 * @param {Object}   [params] - Object containing options for expand/collapse.
  1203 		 * @param {Function} [params.completeCallback] - Function to call when expansion/collapse is complete.
  1205 		 * @param {Function} [params.completeCallback] - Function to call when expansion/collapse is complete.
  1204 		 * @returns {Boolean} false if state already applied or active state is false
  1206 		 * @return {boolean} False if state already applied or active state is false.
  1205 		 */
  1207 		 */
  1206 		_toggleExpanded: function( expanded, params ) {
  1208 		_toggleExpanded: function( expanded, params ) {
  1207 			var instance = this, previousCompleteCallback;
  1209 			var instance = this, previousCompleteCallback;
  1208 			params = params || {};
  1210 			params = params || {};
  1209 			previousCompleteCallback = params.completeCallback;
  1211 			previousCompleteCallback = params.completeCallback;
  1236 			}
  1238 			}
  1237 		},
  1239 		},
  1238 
  1240 
  1239 		/**
  1241 		/**
  1240 		 * @param {Object} [params]
  1242 		 * @param {Object} [params]
  1241 		 * @returns {Boolean} false if already expanded or if inactive.
  1243 		 * @return {boolean} False if already expanded or if inactive.
  1242 		 */
  1244 		 */
  1243 		expand: function ( params ) {
  1245 		expand: function ( params ) {
  1244 			return this._toggleExpanded( true, params );
  1246 			return this._toggleExpanded( true, params );
  1245 		},
  1247 		},
  1246 
  1248 
  1247 		/**
  1249 		/**
  1248 		 * @param {Object} [params]
  1250 		 * @param {Object} [params]
  1249 		 * @returns {Boolean} false if already collapsed.
  1251 		 * @return {boolean} False if already collapsed.
  1250 		 */
  1252 		 */
  1251 		collapse: function ( params ) {
  1253 		collapse: function ( params ) {
  1252 			return this._toggleExpanded( false, params );
  1254 			return this._toggleExpanded( false, params );
  1253 		},
  1255 		},
  1254 
  1256 
  1257 		 *
  1259 		 *
  1258 		 * @since 4.7.0
  1260 		 * @since 4.7.0
  1259 		 * @private
  1261 		 * @private
  1260 		 *
  1262 		 *
  1261 		 * @param {function} completeCallback Function to be called after transition is completed.
  1263 		 * @param {function} completeCallback Function to be called after transition is completed.
  1262 		 * @returns {void}
  1264 		 * @return {void}
  1263 		 */
  1265 		 */
  1264 		_animateChangeExpanded: function( completeCallback ) {
  1266 		_animateChangeExpanded: function( completeCallback ) {
  1265 			// Return if CSS transitions are not supported.
  1267 			// Return if CSS transitions are not supported.
  1266 			if ( ! normalizedTransitionendEventName ) {
  1268 			if ( ! normalizedTransitionendEventName ) {
  1267 				if ( completeCallback ) {
  1269 				if ( completeCallback ) {
  1360 		 * method to handle animating the panel/section into and out of view.
  1362 		 * method to handle animating the panel/section into and out of view.
  1361 		 *
  1363 		 *
  1362 		 * @since 4.7.0
  1364 		 * @since 4.7.0
  1363 		 * @access public
  1365 		 * @access public
  1364 		 *
  1366 		 *
  1365 		 * @returns {jQuery} Detached content element.
  1367 		 * @return {jQuery} Detached content element.
  1366 		 */
  1368 		 */
  1367 		getContent: function() {
  1369 		getContent: function() {
  1368 			var construct = this,
  1370 			var construct = this,
  1369 				container = construct.container,
  1371 				container = construct.container,
  1370 				content = container.find( '.accordion-section-content, .control-panel-content' ).first(),
  1372 				content = container.find( '.accordion-section-content, .control-panel-content' ).first(),
  1405 		 * @augments   wp.customize~Container
  1407 		 * @augments   wp.customize~Container
  1406 		 *
  1408 		 *
  1407 		 * @since 4.1.0
  1409 		 * @since 4.1.0
  1408 		 *
  1410 		 *
  1409 		 * @param {string}  id - The ID for the section.
  1411 		 * @param {string}  id - The ID for the section.
  1410 		 * @param {object}  options - Options.
  1412 		 * @param {Object}  options - Options.
  1411 		 * @param {string}  options.title - Title shown when section is collapsed and expanded.
  1413 		 * @param {string}  options.title - Title shown when section is collapsed and expanded.
  1412 		 * @param {string}  [options.description] - Description shown at the top of the section.
  1414 		 * @param {string}  [options.description] - Description shown at the top of the section.
  1413 		 * @param {number}  [options.priority=100] - The sort priority for the section.
  1415 		 * @param {number}  [options.priority=100] - The sort priority for the section.
  1414 		 * @param {string}  [options.type=default] - The type of the section. See wp.customize.sectionConstructor.
  1416 		 * @param {string}  [options.type=default] - The type of the section. See wp.customize.sectionConstructor.
  1415 		 * @param {string}  [options.content] - The markup to be used for the section container. If empty, a JS template is used.
  1417 		 * @param {string}  [options.content] - The markup to be used for the section container. If empty, a JS template is used.
  1416 		 * @param {boolean} [options.active=true] - Whether the section is active or not.
  1418 		 * @param {boolean} [options.active=true] - Whether the section is active or not.
  1417 		 * @param {string}  options.panel - The ID for the panel this section is associated with.
  1419 		 * @param {string}  options.panel - The ID for the panel this section is associated with.
  1418 		 * @param {string}  [options.customizeAction] - Additional context information shown before the section title when expanded.
  1420 		 * @param {string}  [options.customizeAction] - Additional context information shown before the section title when expanded.
  1419 		 * @param {object}  [options.params] - Deprecated wrapper for the above properties.
  1421 		 * @param {Object}  [options.params] - Deprecated wrapper for the above properties.
  1420 		 */
  1422 		 */
  1421 		initialize: function ( id, options ) {
  1423 		initialize: function ( id, options ) {
  1422 			var section = this, params;
  1424 			var section = this, params;
  1423 			params = options.params || options;
  1425 			params = options.params || options;
  1424 
  1426 
  1477 							}
  1479 							}
  1478 							section.deferred.embedded.resolve();
  1480 							section.deferred.embedded.resolve();
  1479 						});
  1481 						});
  1480 					} );
  1482 					} );
  1481 				} else {
  1483 				} else {
  1482 					// There is no panel, so embed the section in the root of the customizer
  1484 					// There is no panel, so embed the section in the root of the customizer.
  1483 					parentContainer = api.ensure( section.containerPaneParent );
  1485 					parentContainer = api.ensure( section.containerPaneParent );
  1484 					if ( ! section.headContainer.parent().is( parentContainer ) ) {
  1486 					if ( ! section.headContainer.parent().is( parentContainer ) ) {
  1485 						parentContainer.append( section.headContainer );
  1487 						parentContainer.append( section.headContainer );
  1486 					}
  1488 					}
  1487 					if ( ! section.contentContainer.parent().is( section.headContainer ) ) {
  1489 					if ( ! section.contentContainer.parent().is( section.headContainer ) ) {
  1509 			// Expand/Collapse accordion sections on click.
  1511 			// Expand/Collapse accordion sections on click.
  1510 			section.container.find( '.accordion-section-title, .customize-section-back' ).on( 'click keydown', function( event ) {
  1512 			section.container.find( '.accordion-section-title, .customize-section-back' ).on( 'click keydown', function( event ) {
  1511 				if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
  1513 				if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
  1512 					return;
  1514 					return;
  1513 				}
  1515 				}
  1514 				event.preventDefault(); // Keep this AFTER the key filter above
  1516 				event.preventDefault(); // Keep this AFTER the key filter above.
  1515 
  1517 
  1516 				if ( section.expanded() ) {
  1518 				if ( section.expanded() ) {
  1517 					section.collapse();
  1519 					section.collapse();
  1518 				} else {
  1520 				} else {
  1519 					section.expand();
  1521 					section.expand();
  1541 		/**
  1543 		/**
  1542 		 * Return whether this section has any active controls.
  1544 		 * Return whether this section has any active controls.
  1543 		 *
  1545 		 *
  1544 		 * @since 4.1.0
  1546 		 * @since 4.1.0
  1545 		 *
  1547 		 *
  1546 		 * @returns {Boolean}
  1548 		 * @return {boolean}
  1547 		 */
  1549 		 */
  1548 		isContextuallyActive: function () {
  1550 		isContextuallyActive: function () {
  1549 			var section = this,
  1551 			var section = this,
  1550 				controls = section.controls(),
  1552 				controls = section.controls(),
  1551 				activeCount = 0;
  1553 				activeCount = 0;
  1560 		/**
  1562 		/**
  1561 		 * Get the controls that are associated with this section, sorted by their priority Value.
  1563 		 * Get the controls that are associated with this section, sorted by their priority Value.
  1562 		 *
  1564 		 *
  1563 		 * @since 4.1.0
  1565 		 * @since 4.1.0
  1564 		 *
  1566 		 *
  1565 		 * @returns {Array}
  1567 		 * @return {Array}
  1566 		 */
  1568 		 */
  1567 		controls: function () {
  1569 		controls: function () {
  1568 			return this._children( 'section', 'control' );
  1570 			return this._children( 'section', 'control' );
  1569 		},
  1571 		},
  1570 
  1572 
  1571 		/**
  1573 		/**
  1572 		 * Update UI to reflect expanded state.
  1574 		 * Update UI to reflect expanded state.
  1573 		 *
  1575 		 *
  1574 		 * @since 4.1.0
  1576 		 * @since 4.1.0
  1575 		 *
  1577 		 *
  1576 		 * @param {Boolean} expanded
  1578 		 * @param {boolean} expanded
  1577 		 * @param {Object}  args
  1579 		 * @param {Object}  args
  1578 		 */
  1580 		 */
  1579 		onChangeExpanded: function ( expanded, args ) {
  1581 		onChangeExpanded: function ( expanded, args ) {
  1580 			var section = this,
  1582 			var section = this,
  1581 				container = section.headContainer.closest( '.wp-full-overlay-sidebar-content' ),
  1583 				container = section.headContainer.closest( '.wp-full-overlay-sidebar-content' ),
  1693 		 * @augments   wp.customize.Section
  1695 		 * @augments   wp.customize.Section
  1694 		 *
  1696 		 *
  1695 		 * @since 4.9.0
  1697 		 * @since 4.9.0
  1696 		 *
  1698 		 *
  1697 		 * @param {string} id - ID.
  1699 		 * @param {string} id - ID.
  1698 		 * @param {object} options - Options.
  1700 		 * @param {Object} options - Options.
  1699 		 * @returns {void}
  1701 		 * @return {void}
  1700 		 */
  1702 		 */
  1701 		initialize: function( id, options ) {
  1703 		initialize: function( id, options ) {
  1702 			var section = this;
  1704 			var section = this;
  1703 			section.headerContainer = $();
  1705 			section.headerContainer = $();
  1704 			section.$window = $( window );
  1706 			section.$window = $( window );
  1716 		 */
  1718 		 */
  1717 		embed: function() {
  1719 		embed: function() {
  1718 			var inject,
  1720 			var inject,
  1719 				section = this;
  1721 				section = this;
  1720 
  1722 
  1721 			// Watch for changes to the panel state
  1723 			// Watch for changes to the panel state.
  1722 			inject = function( panelId ) {
  1724 			inject = function( panelId ) {
  1723 				var parentContainer;
  1725 				var parentContainer;
  1724 				api.panel( panelId, function( panel ) {
  1726 				api.panel( panelId, function( panel ) {
  1725 
  1727 
  1726 					// The panel has been registered, wait for it to become ready/initialized
  1728 					// The panel has been registered, wait for it to become ready/initialized.
  1727 					panel.deferred.embedded.done( function() {
  1729 					panel.deferred.embedded.done( function() {
  1728 						parentContainer = panel.contentContainer;
  1730 						parentContainer = panel.contentContainer;
  1729 						if ( ! section.headContainer.parent().is( parentContainer ) ) {
  1731 						if ( ! section.headContainer.parent().is( parentContainer ) ) {
  1730 							parentContainer.find( '.customize-themes-full-container-container' ).before( section.headContainer );
  1732 							parentContainer.find( '.customize-themes-full-container-container' ).before( section.headContainer );
  1731 						}
  1733 						}
  1735 						section.deferred.embedded.resolve();
  1737 						section.deferred.embedded.resolve();
  1736 					});
  1738 					});
  1737 				} );
  1739 				} );
  1738 			};
  1740 			};
  1739 			section.panel.bind( inject );
  1741 			section.panel.bind( inject );
  1740 			inject( section.panel.get() ); // Since a section may never get a panel, assume that it won't ever get one
  1742 			inject( section.panel.get() ); // Since a section may never get a panel, assume that it won't ever get one.
  1741 		},
  1743 		},
  1742 
  1744 
  1743 		/**
  1745 		/**
  1744 		 * Set up.
  1746 		 * Set up.
  1745 		 *
  1747 		 *
  1746 		 * @since 4.2.0
  1748 		 * @since 4.2.0
  1747 		 *
  1749 		 *
  1748 		 * @returns {void}
  1750 		 * @return {void}
  1749 		 */
  1751 		 */
  1750 		ready: function() {
  1752 		ready: function() {
  1751 			var section = this;
  1753 			var section = this;
  1752 			section.overlay = section.container.find( '.theme-overlay' );
  1754 			section.overlay = section.container.find( '.theme-overlay' );
  1753 			section.template = wp.template( 'customize-themes-details-view' );
  1755 			section.template = wp.template( 'customize-themes-details-view' );
  1756 			section.container.on( 'keydown', function( event ) {
  1758 			section.container.on( 'keydown', function( event ) {
  1757 				if ( ! section.overlay.find( '.theme-wrap' ).is( ':visible' ) ) {
  1759 				if ( ! section.overlay.find( '.theme-wrap' ).is( ':visible' ) ) {
  1758 					return;
  1760 					return;
  1759 				}
  1761 				}
  1760 
  1762 
  1761 				// Pressing the right arrow key fires a theme:next event
  1763 				// Pressing the right arrow key fires a theme:next event.
  1762 				if ( 39 === event.keyCode ) {
  1764 				if ( 39 === event.keyCode ) {
  1763 					section.nextTheme();
  1765 					section.nextTheme();
  1764 				}
  1766 				}
  1765 
  1767 
  1766 				// Pressing the left arrow key fires a theme:previous event
  1768 				// Pressing the left arrow key fires a theme:previous event.
  1767 				if ( 37 === event.keyCode ) {
  1769 				if ( 37 === event.keyCode ) {
  1768 					section.previousTheme();
  1770 					section.previousTheme();
  1769 				}
  1771 				}
  1770 
  1772 
  1771 				// Pressing the escape key fires a theme:collapse event
  1773 				// Pressing the escape key fires a theme:collapse event.
  1772 				if ( 27 === event.keyCode ) {
  1774 				if ( 27 === event.keyCode ) {
  1773 					if ( section.$body.hasClass( 'modal-open' ) ) {
  1775 					if ( section.$body.hasClass( 'modal-open' ) ) {
  1774 
  1776 
  1775 						// Escape from the details modal.
  1777 						// Escape from the details modal.
  1776 						section.closeDetails();
  1778 						section.closeDetails();
  1795 		 * use the section's own active state instead. This prevents empty search
  1797 		 * use the section's own active state instead. This prevents empty search
  1796 		 * results for theme sections from causing the section to become inactive.
  1798 		 * results for theme sections from causing the section to become inactive.
  1797 		 *
  1799 		 *
  1798 		 * @since 4.2.0
  1800 		 * @since 4.2.0
  1799 		 *
  1801 		 *
  1800 		 * @returns {Boolean}
  1802 		 * @return {boolean}
  1801 		 */
  1803 		 */
  1802 		isContextuallyActive: function () {
  1804 		isContextuallyActive: function () {
  1803 			return this.active();
  1805 			return this.active();
  1804 		},
  1806 		},
  1805 
  1807 
  1806 		/**
  1808 		/**
  1807 		 * Attach events.
  1809 		 * Attach events.
  1808 		 *
  1810 		 *
  1809 		 * @since 4.2.0
  1811 		 * @since 4.2.0
  1810 		 *
  1812 		 *
  1811 		 * @returns {void}
  1813 		 * @return {void}
  1812 		 */
  1814 		 */
  1813 		attachEvents: function () {
  1815 		attachEvents: function () {
  1814 			var section = this, debounced;
  1816 			var section = this, debounced;
  1815 
  1817 
  1816 			// Expand/Collapse accordion sections on click.
  1818 			// Expand/Collapse accordion sections on click.
  1817 			section.container.find( '.customize-section-back' ).on( 'click keydown', function( event ) {
  1819 			section.container.find( '.customize-section-back' ).on( 'click keydown', function( event ) {
  1818 				if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
  1820 				if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
  1819 					return;
  1821 					return;
  1820 				}
  1822 				}
  1821 				event.preventDefault(); // Keep this AFTER the key filter above
  1823 				event.preventDefault(); // Keep this AFTER the key filter above.
  1822 				section.collapse();
  1824 				section.collapse();
  1823 			});
  1825 			});
  1824 
  1826 
  1825 			section.headerContainer = $( '#accordion-section-' + section.id );
  1827 			section.headerContainer = $( '#accordion-section-' + section.id );
  1826 
  1828 
  1947 		/**
  1949 		/**
  1948 		 * Update UI to reflect expanded state
  1950 		 * Update UI to reflect expanded state
  1949 		 *
  1951 		 *
  1950 		 * @since 4.2.0
  1952 		 * @since 4.2.0
  1951 		 *
  1953 		 *
  1952 		 * @param {Boolean}  expanded
  1954 		 * @param {boolean}  expanded
  1953 		 * @param {Object}   args
  1955 		 * @param {Object}   args
  1954 		 * @param {Boolean}  args.unchanged
  1956 		 * @param {boolean}  args.unchanged
  1955 		 * @param {Function} args.completeCallback
  1957 		 * @param {Function} args.completeCallback
  1956 		 * @returns {void}
  1958 		 * @return {void}
  1957 		 */
  1959 		 */
  1958 		onChangeExpanded: function ( expanded, args ) {
  1960 		onChangeExpanded: function ( expanded, args ) {
  1959 
  1961 
  1960 			// Note: there is a second argument 'args' passed
  1962 			// Note: there is a second argument 'args' passed.
  1961 			var section = this,
  1963 			var section = this,
  1962 				container = section.contentContainer.closest( '.customize-themes-full-container' );
  1964 				container = section.contentContainer.closest( '.customize-themes-full-container' );
  1963 
  1965 
  1964 			// Immediately call the complete callback if there were no changes
  1966 			// Immediately call the complete callback if there were no changes.
  1965 			if ( args.unchanged ) {
  1967 			if ( args.unchanged ) {
  1966 				if ( args.completeCallback ) {
  1968 				if ( args.completeCallback ) {
  1967 					args.completeCallback();
  1969 					args.completeCallback();
  1968 				}
  1970 				}
  1969 				return;
  1971 				return;
  1974 				// Try to load controls if none are loaded yet.
  1976 				// Try to load controls if none are loaded yet.
  1975 				if ( 0 === section.loaded ) {
  1977 				if ( 0 === section.loaded ) {
  1976 					section.loadThemes();
  1978 					section.loadThemes();
  1977 				}
  1979 				}
  1978 
  1980 
  1979 				// Collapse any sibling sections/panels
  1981 				// Collapse any sibling sections/panels.
  1980 				api.section.each( function ( otherSection ) {
  1982 				api.section.each( function ( otherSection ) {
  1981 					var searchTerm;
  1983 					var searchTerm;
  1982 
  1984 
  1983 					if ( otherSection !== section ) {
  1985 					if ( otherSection !== section ) {
  1984 
  1986 
  2041 		/**
  2043 		/**
  2042 		 * Return the section's content element without detaching from the parent.
  2044 		 * Return the section's content element without detaching from the parent.
  2043 		 *
  2045 		 *
  2044 		 * @since 4.9.0
  2046 		 * @since 4.9.0
  2045 		 *
  2047 		 *
  2046 		 * @returns {jQuery}
  2048 		 * @return {jQuery}
  2047 		 */
  2049 		 */
  2048 		getContent: function() {
  2050 		getContent: function() {
  2049 			return this.container.find( '.control-section-content' );
  2051 			return this.container.find( '.control-section-content' );
  2050 		},
  2052 		},
  2051 
  2053 
  2052 		/**
  2054 		/**
  2053 		 * Load theme data via Ajax and add themes to the section as controls.
  2055 		 * Load theme data via Ajax and add themes to the section as controls.
  2054 		 *
  2056 		 *
  2055 		 * @since 4.9.0
  2057 		 * @since 4.9.0
  2056 		 *
  2058 		 *
  2057 		 * @returns {void}
  2059 		 * @return {void}
  2058 		 */
  2060 		 */
  2059 		loadThemes: function() {
  2061 		loadThemes: function() {
  2060 			var section = this, params, page, request;
  2062 			var section = this, params, page, request;
  2061 
  2063 
  2062 			if ( section.loading ) {
  2064 			if ( section.loading ) {
  2121 						}
  2123 						}
  2122 					}
  2124 					}
  2123 
  2125 
  2124 					_.delay( section.renderScreenshots, 100 ); // Wait for the controls to become visible.
  2126 					_.delay( section.renderScreenshots, 100 ); // Wait for the controls to become visible.
  2125 
  2127 
  2126 					if ( 'local' === section.params.filter_type || 100 > themes.length ) { // If we have less than the requested 100 themes, it's the end of the list.
  2128 					if ( 'local' === section.params.filter_type || 100 > themes.length ) {
       
  2129 						// If we have less than the requested 100 themes, it's the end of the list.
  2127 						section.fullyLoaded = true;
  2130 						section.fullyLoaded = true;
  2128 					}
  2131 					}
  2129 				} else {
  2132 				} else {
  2130 					if ( 0 === section.loaded ) {
  2133 					if ( 0 === section.loaded ) {
  2131 						section.container.find( '.no-themes' ).show();
  2134 						section.container.find( '.no-themes' ).show();
  2161 
  2164 
  2162 		/**
  2165 		/**
  2163 		 * Loads controls into the section from data received from loadThemes().
  2166 		 * Loads controls into the section from data received from loadThemes().
  2164 		 *
  2167 		 *
  2165 		 * @since 4.9.0
  2168 		 * @since 4.9.0
  2166 		 * @param {Array} themes - Array of theme data to create controls with.
  2169 		 * @param {Array}  themes - Array of theme data to create controls with.
  2167 		 * @param {integer} page - Page of results being loaded.
  2170 		 * @param {number} page   - Page of results being loaded.
  2168 		 * @returns {void}
  2171 		 * @return {void}
  2169 		 */
  2172 		 */
  2170 		loadControls: function( themes, page ) {
  2173 		loadControls: function( themes, page ) {
  2171 			var newThemeControls = [],
  2174 			var newThemeControls = [],
  2172 				section = this;
  2175 				section = this;
  2173 
  2176 
  2192 
  2195 
  2193 		/**
  2196 		/**
  2194 		 * Determines whether more themes should be loaded, and loads them.
  2197 		 * Determines whether more themes should be loaded, and loads them.
  2195 		 *
  2198 		 *
  2196 		 * @since 4.9.0
  2199 		 * @since 4.9.0
  2197 		 * @returns {void}
  2200 		 * @return {void}
  2198 		 */
  2201 		 */
  2199 		loadMore: function() {
  2202 		loadMore: function() {
  2200 			var section = this, container, bottom, threshold;
  2203 			var section = this, container, bottom, threshold;
  2201 			if ( ! section.fullyLoaded && ! section.loading ) {
  2204 			if ( ! section.fullyLoaded && ! section.loading ) {
  2202 				container = section.container.closest( '.customize-themes-full-container' );
  2205 				container = section.container.closest( '.customize-themes-full-container' );
  2203 
  2206 
  2204 				bottom = container.scrollTop() + container.height();
  2207 				bottom = container.scrollTop() + container.height();
  2205 				threshold = container.prop( 'scrollHeight' ) - 3000; // Use a fixed distance to the bottom of loaded results to avoid unnecessarily loading results sooner when using a percentage of scroll distance.
  2208 				// Use a fixed distance to the bottom of loaded results to avoid unnecessarily
       
  2209 				// loading results sooner when using a percentage of scroll distance.
       
  2210 				threshold = container.prop( 'scrollHeight' ) - 3000;
  2206 
  2211 
  2207 				if ( bottom > threshold ) {
  2212 				if ( bottom > threshold ) {
  2208 					section.loadThemes();
  2213 					section.loadThemes();
  2209 				}
  2214 				}
  2210 			}
  2215 			}
  2214 		 * Event handler for search input that filters visible controls.
  2219 		 * Event handler for search input that filters visible controls.
  2215 		 *
  2220 		 *
  2216 		 * @since 4.9.0
  2221 		 * @since 4.9.0
  2217 		 *
  2222 		 *
  2218 		 * @param {string} term - The raw search input value.
  2223 		 * @param {string} term - The raw search input value.
  2219 		 * @returns {void}
  2224 		 * @return {void}
  2220 		 */
  2225 		 */
  2221 		filterSearch: function( term ) {
  2226 		filterSearch: function( term ) {
  2222 			var count = 0,
  2227 			var count = 0,
  2223 				visible = false,
  2228 				visible = false,
  2224 				section = this,
  2229 				section = this,
  2258 		 * Event handler for search input that determines if the terms have changed and loads new controls as needed.
  2263 		 * Event handler for search input that determines if the terms have changed and loads new controls as needed.
  2259 		 *
  2264 		 *
  2260 		 * @since 4.9.0
  2265 		 * @since 4.9.0
  2261 		 *
  2266 		 *
  2262 		 * @param {wp.customize.ThemesSection} section - The current theme section, passed through the debouncer.
  2267 		 * @param {wp.customize.ThemesSection} section - The current theme section, passed through the debouncer.
  2263 		 * @returns {void}
  2268 		 * @return {void}
  2264 		 */
  2269 		 */
  2265 		checkTerm: function( section ) {
  2270 		checkTerm: function( section ) {
  2266 			var newTerm;
  2271 			var newTerm;
  2267 			if ( 'remote' === section.params.filter_type ) {
  2272 			if ( 'remote' === section.params.filter_type ) {
  2268 				newTerm = section.contentContainer.find( '.wp-filter-search' ).val();
  2273 				newTerm = section.contentContainer.find( '.wp-filter-search' ).val();
  2275 		/**
  2280 		/**
  2276 		 * Check for filters checked in the feature filter list and initialize a new query.
  2281 		 * Check for filters checked in the feature filter list and initialize a new query.
  2277 		 *
  2282 		 *
  2278 		 * @since 4.9.0
  2283 		 * @since 4.9.0
  2279 		 *
  2284 		 *
  2280 		 * @returns {void}
  2285 		 * @return {void}
  2281 		 */
  2286 		 */
  2282 		filtersChecked: function() {
  2287 		filtersChecked: function() {
  2283 			var section = this,
  2288 			var section = this,
  2284 			    items = section.container.find( '.filter-group' ).find( ':checkbox' ),
  2289 			    items = section.container.find( '.filter-group' ).find( ':checkbox' ),
  2285 			    tags = [];
  2290 			    tags = [];
  2318 		 *
  2323 		 *
  2319 		 * @since 4.9.0
  2324 		 * @since 4.9.0
  2320 		 *
  2325 		 *
  2321 		 * @param {string} newTerm - New term.
  2326 		 * @param {string} newTerm - New term.
  2322 		 * @param {Array} newTags - New tags.
  2327 		 * @param {Array} newTags - New tags.
  2323 		 * @returns {void}
  2328 		 * @return {void}
  2324 		 */
  2329 		 */
  2325 		initializeNewQuery: function( newTerm, newTags ) {
  2330 		initializeNewQuery: function( newTerm, newTags ) {
  2326 			var section = this;
  2331 			var section = this;
  2327 
  2332 
  2328 			// Clear the controls in the section.
  2333 			// Clear the controls in the section.
  2351 		/**
  2356 		/**
  2352 		 * Render control's screenshot if the control comes into view.
  2357 		 * Render control's screenshot if the control comes into view.
  2353 		 *
  2358 		 *
  2354 		 * @since 4.2.0
  2359 		 * @since 4.2.0
  2355 		 *
  2360 		 *
  2356 		 * @returns {void}
  2361 		 * @return {void}
  2357 		 */
  2362 		 */
  2358 		renderScreenshots: function() {
  2363 		renderScreenshots: function() {
  2359 			var section = this;
  2364 			var section = this;
  2360 
  2365 
  2361 			// Fill queue initially, or check for more if empty.
  2366 			// Fill queue initially, or check for more if empty.
  2405 		/**
  2410 		/**
  2406 		 * Get visible count.
  2411 		 * Get visible count.
  2407 		 *
  2412 		 *
  2408 		 * @since 4.9.0
  2413 		 * @since 4.9.0
  2409 		 *
  2414 		 *
  2410 		 * @returns {int} Visible count.
  2415 		 * @return {number} Visible count.
  2411 		 */
  2416 		 */
  2412 		getVisibleCount: function() {
  2417 		getVisibleCount: function() {
  2413 			return this.contentContainer.find( 'li.customize-control:visible' ).length;
  2418 			return this.contentContainer.find( 'li.customize-control:visible' ).length;
  2414 		},
  2419 		},
  2415 
  2420 
  2416 		/**
  2421 		/**
  2417 		 * Update the number of themes in the section.
  2422 		 * Update the number of themes in the section.
  2418 		 *
  2423 		 *
  2419 		 * @since 4.9.0
  2424 		 * @since 4.9.0
  2420 		 *
  2425 		 *
  2421 		 * @returns {void}
  2426 		 * @return {void}
  2422 		 */
  2427 		 */
  2423 		updateCount: function( count ) {
  2428 		updateCount: function( count ) {
  2424 			var section = this, countEl, displayed;
  2429 			var section = this, countEl, displayed;
  2425 
  2430 
  2426 			if ( ! count && 0 !== count ) {
  2431 			if ( ! count && 0 !== count ) {
  2446 		/**
  2451 		/**
  2447 		 * Advance the modal to the next theme.
  2452 		 * Advance the modal to the next theme.
  2448 		 *
  2453 		 *
  2449 		 * @since 4.2.0
  2454 		 * @since 4.2.0
  2450 		 *
  2455 		 *
  2451 		 * @returns {void}
  2456 		 * @return {void}
  2452 		 */
  2457 		 */
  2453 		nextTheme: function () {
  2458 		nextTheme: function () {
  2454 			var section = this;
  2459 			var section = this;
  2455 			if ( section.getNextTheme() ) {
  2460 			if ( section.getNextTheme() ) {
  2456 				section.showDetails( section.getNextTheme(), function() {
  2461 				section.showDetails( section.getNextTheme(), function() {
  2462 		/**
  2467 		/**
  2463 		 * Get the next theme model.
  2468 		 * Get the next theme model.
  2464 		 *
  2469 		 *
  2465 		 * @since 4.2.0
  2470 		 * @since 4.2.0
  2466 		 *
  2471 		 *
  2467 		 * @returns {wp.customize.ThemeControl|boolean} Next theme.
  2472 		 * @return {wp.customize.ThemeControl|boolean} Next theme.
  2468 		 */
  2473 		 */
  2469 		getNextTheme: function () {
  2474 		getNextTheme: function () {
  2470 			var section = this, control, nextControl, sectionControls, i;
  2475 			var section = this, control, nextControl, sectionControls, i;
  2471 			control = api.control( section.params.action + '_theme_' + section.currentTheme );
  2476 			control = api.control( section.params.action + '_theme_' + section.currentTheme );
  2472 			sectionControls = section.controls();
  2477 			sectionControls = section.controls();
  2484 
  2489 
  2485 		/**
  2490 		/**
  2486 		 * Advance the modal to the previous theme.
  2491 		 * Advance the modal to the previous theme.
  2487 		 *
  2492 		 *
  2488 		 * @since 4.2.0
  2493 		 * @since 4.2.0
  2489 		 * @returns {void}
  2494 		 * @return {void}
  2490 		 */
  2495 		 */
  2491 		previousTheme: function () {
  2496 		previousTheme: function () {
  2492 			var section = this;
  2497 			var section = this;
  2493 			if ( section.getPreviousTheme() ) {
  2498 			if ( section.getPreviousTheme() ) {
  2494 				section.showDetails( section.getPreviousTheme(), function() {
  2499 				section.showDetails( section.getPreviousTheme(), function() {
  2499 
  2504 
  2500 		/**
  2505 		/**
  2501 		 * Get the previous theme model.
  2506 		 * Get the previous theme model.
  2502 		 *
  2507 		 *
  2503 		 * @since 4.2.0
  2508 		 * @since 4.2.0
  2504 		 * @returns {wp.customize.ThemeControl|boolean} Previous theme.
  2509 		 * @return {wp.customize.ThemeControl|boolean} Previous theme.
  2505 		 */
  2510 		 */
  2506 		getPreviousTheme: function () {
  2511 		getPreviousTheme: function () {
  2507 			var section = this, control, nextControl, sectionControls, i;
  2512 			var section = this, control, nextControl, sectionControls, i;
  2508 			control = api.control( section.params.action + '_theme_' + section.currentTheme );
  2513 			control = api.control( section.params.action + '_theme_' + section.currentTheme );
  2509 			sectionControls = section.controls();
  2514 			sectionControls = section.controls();
  2522 		/**
  2527 		/**
  2523 		 * Disable buttons when we're viewing the first or last theme.
  2528 		 * Disable buttons when we're viewing the first or last theme.
  2524 		 *
  2529 		 *
  2525 		 * @since 4.2.0
  2530 		 * @since 4.2.0
  2526 		 *
  2531 		 *
  2527 		 * @returns {void}
  2532 		 * @return {void}
  2528 		 */
  2533 		 */
  2529 		updateLimits: function () {
  2534 		updateLimits: function () {
  2530 			if ( ! this.getNextTheme() ) {
  2535 			if ( ! this.getNextTheme() ) {
  2531 				this.overlay.find( '.right' ).addClass( 'disabled' );
  2536 				this.overlay.find( '.right' ).addClass( 'disabled' );
  2532 			}
  2537 			}
  2541 		 * @since 4.7.0
  2546 		 * @since 4.7.0
  2542 		 * @access public
  2547 		 * @access public
  2543 		 *
  2548 		 *
  2544 		 * @deprecated
  2549 		 * @deprecated
  2545 		 * @param {string} themeId Theme ID.
  2550 		 * @param {string} themeId Theme ID.
  2546 		 * @returns {jQuery.promise} Promise.
  2551 		 * @return {jQuery.promise} Promise.
  2547 		 */
  2552 		 */
  2548 		loadThemePreview: function( themeId ) {
  2553 		loadThemePreview: function( themeId ) {
  2549 			return api.ThemesPanel.prototype.loadThemePreview.call( this, themeId );
  2554 			return api.ThemesPanel.prototype.loadThemePreview.call( this, themeId );
  2550 		},
  2555 		},
  2551 
  2556 
  2552 		/**
  2557 		/**
  2553 		 * Render & show the theme details for a given theme model.
  2558 		 * Render & show the theme details for a given theme model.
  2554 		 *
  2559 		 *
  2555 		 * @since 4.2.0
  2560 		 * @since 4.2.0
  2556 		 *
  2561 		 *
  2557 		 * @param {object} theme - Theme.
  2562 		 * @param {Object} theme - Theme.
  2558 		 * @param {Function} [callback] - Callback once the details have been shown.
  2563 		 * @param {Function} [callback] - Callback once the details have been shown.
  2559 		 * @returns {void}
  2564 		 * @return {void}
  2560 		 */
  2565 		 */
  2561 		showDetails: function ( theme, callback ) {
  2566 		showDetails: function ( theme, callback ) {
  2562 			var section = this, panel = api.panel( 'themes' );
  2567 			var section = this, panel = api.panel( 'themes' );
  2563 			section.currentTheme = theme.id;
  2568 			section.currentTheme = theme.id;
  2564 			section.overlay.html( section.template( theme ) )
  2569 			section.overlay.html( section.template( theme ) )
  2589 		/**
  2594 		/**
  2590 		 * Close the theme details modal.
  2595 		 * Close the theme details modal.
  2591 		 *
  2596 		 *
  2592 		 * @since 4.2.0
  2597 		 * @since 4.2.0
  2593 		 *
  2598 		 *
  2594 		 * @returns {void}
  2599 		 * @return {void}
  2595 		 */
  2600 		 */
  2596 		closeDetails: function () {
  2601 		closeDetails: function () {
  2597 			var section = this;
  2602 			var section = this;
  2598 			section.$body.removeClass( 'modal-open' );
  2603 			section.$body.removeClass( 'modal-open' );
  2599 			section.overlay.fadeOut( 'fast' );
  2604 			section.overlay.fadeOut( 'fast' );
  2604 		 * Keep tab focus within the theme details modal.
  2609 		 * Keep tab focus within the theme details modal.
  2605 		 *
  2610 		 *
  2606 		 * @since 4.2.0
  2611 		 * @since 4.2.0
  2607 		 *
  2612 		 *
  2608 		 * @param {jQuery} el - Element to contain focus.
  2613 		 * @param {jQuery} el - Element to contain focus.
  2609 		 * @returns {void}
  2614 		 * @return {void}
  2610 		 */
  2615 		 */
  2611 		containFocus: function( el ) {
  2616 		containFocus: function( el ) {
  2612 			var tabbables;
  2617 			var tabbables;
  2613 
  2618 
  2614 			el.on( 'keydown', function( event ) {
  2619 			el.on( 'keydown', function( event ) {
  2615 
  2620 
  2616 				// Return if it's not the tab key
  2621 				// Return if it's not the tab key
  2617 				// When navigating with prev/next focus is already handled
  2622 				// When navigating with prev/next focus is already handled.
  2618 				if ( 9 !== event.keyCode ) {
  2623 				if ( 9 !== event.keyCode ) {
  2619 					return;
  2624 					return;
  2620 				}
  2625 				}
  2621 
  2626 
  2622 				// uses jQuery UI to get the tabbable elements
  2627 				// Uses jQuery UI to get the tabbable elements.
  2623 				tabbables = $( ':tabbable', el );
  2628 				tabbables = $( ':tabbable', el );
  2624 
  2629 
  2625 				// Keep focus within the overlay
  2630 				// Keep focus within the overlay.
  2626 				if ( tabbables.last()[0] === event.target && ! event.shiftKey ) {
  2631 				if ( tabbables.last()[0] === event.target && ! event.shiftKey ) {
  2627 					tabbables.first().focus();
  2632 					tabbables.first().focus();
  2628 					return false;
  2633 					return false;
  2629 				} else if ( tabbables.first()[0] === event.target && event.shiftKey ) {
  2634 				} else if ( tabbables.first()[0] === event.target && event.shiftKey ) {
  2630 					tabbables.last().focus();
  2635 					tabbables.last().focus();
  2645 		 * @constructs wp.customize.OuterSection
  2650 		 * @constructs wp.customize.OuterSection
  2646 		 * @augments   wp.customize.Section
  2651 		 * @augments   wp.customize.Section
  2647 		 *
  2652 		 *
  2648 		 * @since 4.9.0
  2653 		 * @since 4.9.0
  2649 		 *
  2654 		 *
  2650 		 * @returns {void}
  2655 		 * @return {void}
  2651 		 */
  2656 		 */
  2652 		initialize: function() {
  2657 		initialize: function() {
  2653 			var section = this;
  2658 			var section = this;
  2654 			section.containerParent = '#customize-outer-theme-controls';
  2659 			section.containerParent = '#customize-outer-theme-controls';
  2655 			section.containerPaneParent = '.customize-outer-pane-parent';
  2660 			section.containerPaneParent = '.customize-outer-pane-parent';
  2660 		 * Overrides api.Section.prototype.onChangeExpanded to prevent collapse/expand effect
  2665 		 * Overrides api.Section.prototype.onChangeExpanded to prevent collapse/expand effect
  2661 		 * on other sections and panels.
  2666 		 * on other sections and panels.
  2662 		 *
  2667 		 *
  2663 		 * @since 4.9.0
  2668 		 * @since 4.9.0
  2664 		 *
  2669 		 *
  2665 		 * @param {Boolean}  expanded - The expanded state to transition to.
  2670 		 * @param {boolean}  expanded - The expanded state to transition to.
  2666 		 * @param {Object}   [args] - Args.
  2671 		 * @param {Object}   [args] - Args.
  2667 		 * @param {boolean}  [args.unchanged] - Whether the state is already known to not be changed, and so short-circuit with calling completeCallback early.
  2672 		 * @param {boolean}  [args.unchanged] - Whether the state is already known to not be changed, and so short-circuit with calling completeCallback early.
  2668 		 * @param {Function} [args.completeCallback] - Function to call when the slideUp/slideDown has completed.
  2673 		 * @param {Function} [args.completeCallback] - Function to call when the slideUp/slideDown has completed.
  2669 		 * @param {Object}   [args.duration] - The duration for the animation.
  2674 		 * @param {Object}   [args.duration] - The duration for the animation.
  2670 		 */
  2675 		 */
  2755 		 * @augments   wp.customize~Container
  2760 		 * @augments   wp.customize~Container
  2756 		 *
  2761 		 *
  2757 		 * @since 4.1.0
  2762 		 * @since 4.1.0
  2758 		 *
  2763 		 *
  2759 		 * @param {string}  id - The ID for the panel.
  2764 		 * @param {string}  id - The ID for the panel.
  2760 		 * @param {object}  options - Object containing one property: params.
  2765 		 * @param {Object}  options - Object containing one property: params.
  2761 		 * @param {string}  options.title - Title shown when panel is collapsed and expanded.
  2766 		 * @param {string}  options.title - Title shown when panel is collapsed and expanded.
  2762 		 * @param {string}  [options.description] - Description shown at the top of the panel.
  2767 		 * @param {string}  [options.description] - Description shown at the top of the panel.
  2763 		 * @param {number}  [options.priority=100] - The sort priority for the panel.
  2768 		 * @param {number}  [options.priority=100] - The sort priority for the panel.
  2764 		 * @param {string}  [options.type=default] - The type of the panel. See wp.customize.panelConstructor.
  2769 		 * @param {string}  [options.type=default] - The type of the panel. See wp.customize.panelConstructor.
  2765 		 * @param {string}  [options.content] - The markup to be used for the panel container. If empty, a JS template is used.
  2770 		 * @param {string}  [options.content] - The markup to be used for the panel container. If empty, a JS template is used.
  2766 		 * @param {boolean} [options.active=true] - Whether the panel is active or not.
  2771 		 * @param {boolean} [options.active=true] - Whether the panel is active or not.
  2767 		 * @param {object}  [options.params] - Deprecated wrapper for the above properties.
  2772 		 * @param {Object}  [options.params] - Deprecated wrapper for the above properties.
  2768 		 */
  2773 		 */
  2769 		initialize: function ( id, options ) {
  2774 		initialize: function ( id, options ) {
  2770 			var panel = this, params;
  2775 			var panel = this, params;
  2771 			params = options.params || options;
  2776 			params = options.params || options;
  2772 
  2777 
  2795 		 * @since 4.1.0
  2800 		 * @since 4.1.0
  2796 		 */
  2801 		 */
  2797 		embed: function () {
  2802 		embed: function () {
  2798 			var panel = this,
  2803 			var panel = this,
  2799 				container = $( '#customize-theme-controls' ),
  2804 				container = $( '#customize-theme-controls' ),
  2800 				parentContainer = $( '.customize-pane-parent' ); // @todo This should be defined elsewhere, and to be configurable
  2805 				parentContainer = $( '.customize-pane-parent' ); // @todo This should be defined elsewhere, and to be configurable.
  2801 
  2806 
  2802 			if ( ! panel.headContainer.parent().is( parentContainer ) ) {
  2807 			if ( ! panel.headContainer.parent().is( parentContainer ) ) {
  2803 				parentContainer.append( panel.headContainer );
  2808 				parentContainer.append( panel.headContainer );
  2804 			}
  2809 			}
  2805 			if ( ! panel.contentContainer.parent().is( panel.headContainer ) ) {
  2810 			if ( ! panel.contentContainer.parent().is( panel.headContainer ) ) {
  2819 			// Expand/Collapse accordion sections on click.
  2824 			// Expand/Collapse accordion sections on click.
  2820 			panel.headContainer.find( '.accordion-section-title' ).on( 'click keydown', function( event ) {
  2825 			panel.headContainer.find( '.accordion-section-title' ).on( 'click keydown', function( event ) {
  2821 				if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
  2826 				if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
  2822 					return;
  2827 					return;
  2823 				}
  2828 				}
  2824 				event.preventDefault(); // Keep this AFTER the key filter above
  2829 				event.preventDefault(); // Keep this AFTER the key filter above.
  2825 
  2830 
  2826 				if ( ! panel.expanded() ) {
  2831 				if ( ! panel.expanded() ) {
  2827 					panel.expand();
  2832 					panel.expand();
  2828 				}
  2833 				}
  2829 			});
  2834 			});
  2831 			// Close panel.
  2836 			// Close panel.
  2832 			panel.container.find( '.customize-panel-back' ).on( 'click keydown', function( event ) {
  2837 			panel.container.find( '.customize-panel-back' ).on( 'click keydown', function( event ) {
  2833 				if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
  2838 				if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
  2834 					return;
  2839 					return;
  2835 				}
  2840 				}
  2836 				event.preventDefault(); // Keep this AFTER the key filter above
  2841 				event.preventDefault(); // Keep this AFTER the key filter above.
  2837 
  2842 
  2838 				if ( panel.expanded() ) {
  2843 				if ( panel.expanded() ) {
  2839 					panel.collapse();
  2844 					panel.collapse();
  2840 				}
  2845 				}
  2841 			});
  2846 			});
  2868 		/**
  2873 		/**
  2869 		 * Get the sections that are associated with this panel, sorted by their priority Value.
  2874 		 * Get the sections that are associated with this panel, sorted by their priority Value.
  2870 		 *
  2875 		 *
  2871 		 * @since 4.1.0
  2876 		 * @since 4.1.0
  2872 		 *
  2877 		 *
  2873 		 * @returns {Array}
  2878 		 * @return {Array}
  2874 		 */
  2879 		 */
  2875 		sections: function () {
  2880 		sections: function () {
  2876 			return this._children( 'panel', 'section' );
  2881 			return this._children( 'panel', 'section' );
  2877 		},
  2882 		},
  2878 
  2883 
  2879 		/**
  2884 		/**
  2880 		 * Return whether this panel has any active sections.
  2885 		 * Return whether this panel has any active sections.
  2881 		 *
  2886 		 *
  2882 		 * @since 4.1.0
  2887 		 * @since 4.1.0
  2883 		 *
  2888 		 *
  2884 		 * @returns {boolean} Whether contextually active.
  2889 		 * @return {boolean} Whether contextually active.
  2885 		 */
  2890 		 */
  2886 		isContextuallyActive: function () {
  2891 		isContextuallyActive: function () {
  2887 			var panel = this,
  2892 			var panel = this,
  2888 				sections = panel.sections(),
  2893 				sections = panel.sections(),
  2889 				activeCount = 0;
  2894 				activeCount = 0;
  2898 		/**
  2903 		/**
  2899 		 * Update UI to reflect expanded state.
  2904 		 * Update UI to reflect expanded state.
  2900 		 *
  2905 		 *
  2901 		 * @since 4.1.0
  2906 		 * @since 4.1.0
  2902 		 *
  2907 		 *
  2903 		 * @param {Boolean}  expanded
  2908 		 * @param {boolean}  expanded
  2904 		 * @param {Object}   args
  2909 		 * @param {Object}   args
  2905 		 * @param {Boolean}  args.unchanged
  2910 		 * @param {boolean}  args.unchanged
  2906 		 * @param {Function} args.completeCallback
  2911 		 * @param {Function} args.completeCallback
  2907 		 * @returns {void}
  2912 		 * @return {void}
  2908 		 */
  2913 		 */
  2909 		onChangeExpanded: function ( expanded, args ) {
  2914 		onChangeExpanded: function ( expanded, args ) {
  2910 
  2915 
  2911 			// Immediately call the complete callback if there were no changes
  2916 			// Immediately call the complete callback if there were no changes.
  2912 			if ( args.unchanged ) {
  2917 			if ( args.unchanged ) {
  2913 				if ( args.completeCallback ) {
  2918 				if ( args.completeCallback ) {
  2914 					args.completeCallback();
  2919 					args.completeCallback();
  2915 				}
  2920 				}
  2916 				return;
  2921 				return;
  2917 			}
  2922 			}
  2918 
  2923 
  2919 			// Note: there is a second argument 'args' passed
  2924 			// Note: there is a second argument 'args' passed.
  2920 			var panel = this,
  2925 			var panel = this,
  2921 				accordionSection = panel.contentContainer,
  2926 				accordionSection = panel.contentContainer,
  2922 				overlay = accordionSection.closest( '.wp-full-overlay' ),
  2927 				overlay = accordionSection.closest( '.wp-full-overlay' ),
  2923 				container = accordionSection.closest( '.wp-full-overlay-sidebar-content' ),
  2928 				container = accordionSection.closest( '.wp-full-overlay-sidebar-content' ),
  2924 				topPanel = panel.headContainer.find( '.accordion-section-title' ),
  2929 				topPanel = panel.headContainer.find( '.accordion-section-title' ),
  2925 				backBtn = accordionSection.find( '.customize-panel-back' ),
  2930 				backBtn = accordionSection.find( '.customize-panel-back' ),
  2926 				childSections = panel.sections(),
  2931 				childSections = panel.sections(),
  2927 				skipTransition;
  2932 				skipTransition;
  2928 
  2933 
  2929 			if ( expanded && ! accordionSection.hasClass( 'current-panel' ) ) {
  2934 			if ( expanded && ! accordionSection.hasClass( 'current-panel' ) ) {
  2930 				// Collapse any sibling sections/panels
  2935 				// Collapse any sibling sections/panels.
  2931 				api.section.each( function ( section ) {
  2936 				api.section.each( function ( section ) {
  2932 					if ( panel.id !== section.panel() ) {
  2937 					if ( panel.id !== section.panel() ) {
  2933 						section.collapse( { duration: 0 } );
  2938 						section.collapse( { duration: 0 } );
  2934 					}
  2939 					}
  2935 				});
  2940 				});
  3029 		 * @augments   wp.customize.Panel
  3034 		 * @augments   wp.customize.Panel
  3030 		 *
  3035 		 *
  3031 		 * @since 4.9.0
  3036 		 * @since 4.9.0
  3032 		 *
  3037 		 *
  3033 		 * @param {string} id - The ID for the panel.
  3038 		 * @param {string} id - The ID for the panel.
  3034 		 * @param {object} options - Options.
  3039 		 * @param {Object} options - Options.
  3035 		 * @returns {void}
  3040 		 * @return {void}
  3036 		 */
  3041 		 */
  3037 		initialize: function( id, options ) {
  3042 		initialize: function( id, options ) {
  3038 			var panel = this;
  3043 			var panel = this;
  3039 			panel.installingThemes = [];
  3044 			panel.installingThemes = [];
  3040 			api.Panel.prototype.initialize.call( panel, id, options );
  3045 			api.Panel.prototype.initialize.call( panel, id, options );
  3044 		 * Determine whether a given theme can be switched to, or in general.
  3049 		 * Determine whether a given theme can be switched to, or in general.
  3045 		 *
  3050 		 *
  3046 		 * @since 4.9.0
  3051 		 * @since 4.9.0
  3047 		 *
  3052 		 *
  3048 		 * @param {string} [slug] - Theme slug.
  3053 		 * @param {string} [slug] - Theme slug.
  3049 		 * @returns {boolean} Whether the theme can be switched to.
  3054 		 * @return {boolean} Whether the theme can be switched to.
  3050 		 */
  3055 		 */
  3051 		canSwitchTheme: function canSwitchTheme( slug ) {
  3056 		canSwitchTheme: function canSwitchTheme( slug ) {
  3052 			if ( slug && slug === api.settings.theme.stylesheet ) {
  3057 			if ( slug && slug === api.settings.theme.stylesheet ) {
  3053 				return true;
  3058 				return true;
  3054 			}
  3059 			}
  3057 
  3062 
  3058 		/**
  3063 		/**
  3059 		 * Attach events.
  3064 		 * Attach events.
  3060 		 *
  3065 		 *
  3061 		 * @since 4.9.0
  3066 		 * @since 4.9.0
  3062 		 * @returns {void}
  3067 		 * @return {void}
  3063 		 */
  3068 		 */
  3064 		attachEvents: function() {
  3069 		attachEvents: function() {
  3065 			var panel = this;
  3070 			var panel = this;
  3066 
  3071 
  3067 			// Attach regular panel events.
  3072 			// Attach regular panel events.
  3068 			api.Panel.prototype.attachEvents.apply( panel );
  3073 			api.Panel.prototype.attachEvents.apply( panel );
  3069 
  3074 
  3070 			// Temporary since supplying SFTP credentials does not work yet. See #42184
  3075 			// Temporary since supplying SFTP credentials does not work yet. See #42184.
  3071 			if ( api.settings.theme._canInstall && api.settings.theme._filesystemCredentialsNeeded ) {
  3076 			if ( api.settings.theme._canInstall && api.settings.theme._filesystemCredentialsNeeded ) {
  3072 				panel.notifications.add( new api.Notification( 'theme_install_unavailable', {
  3077 				panel.notifications.add( new api.Notification( 'theme_install_unavailable', {
  3073 					message: api.l10n.themeInstallUnavailable,
  3078 					message: api.l10n.themeInstallUnavailable,
  3074 					type: 'info',
  3079 					type: 'info',
  3075 					dismissible: true
  3080 					dismissible: true
  3126 		/**
  3131 		/**
  3127 		 * Update UI to reflect expanded state
  3132 		 * Update UI to reflect expanded state
  3128 		 *
  3133 		 *
  3129 		 * @since 4.9.0
  3134 		 * @since 4.9.0
  3130 		 *
  3135 		 *
  3131 		 * @param {Boolean}  expanded - Expanded state.
  3136 		 * @param {boolean}  expanded - Expanded state.
  3132 		 * @param {Object}   args - Args.
  3137 		 * @param {Object}   args - Args.
  3133 		 * @param {Boolean}  args.unchanged - Whether or not the state changed.
  3138 		 * @param {boolean}  args.unchanged - Whether or not the state changed.
  3134 		 * @param {Function} args.completeCallback - Callback to execute when the animation completes.
  3139 		 * @param {Function} args.completeCallback - Callback to execute when the animation completes.
  3135 		 * @returns {void}
  3140 		 * @return {void}
  3136 		 */
  3141 		 */
  3137 		onChangeExpanded: function( expanded, args ) {
  3142 		onChangeExpanded: function( expanded, args ) {
  3138 			var panel = this, overlay, sections, hasExpandedSection = false;
  3143 			var panel = this, overlay, sections, hasExpandedSection = false;
  3139 
  3144 
  3140 			// Expand/collapse the panel normally.
  3145 			// Expand/collapse the panel normally.
  3141 			api.Panel.prototype.onChangeExpanded.apply( this, [ expanded, args ] );
  3146 			api.Panel.prototype.onChangeExpanded.apply( this, [ expanded, args ] );
  3142 
  3147 
  3143 			// Immediately call the complete callback if there were no changes
  3148 			// Immediately call the complete callback if there were no changes.
  3144 			if ( args.unchanged ) {
  3149 			if ( args.unchanged ) {
  3145 				if ( args.completeCallback ) {
  3150 				if ( args.completeCallback ) {
  3146 					args.completeCallback();
  3151 					args.completeCallback();
  3147 				}
  3152 				}
  3148 				return;
  3153 				return;
  3182 		 * Install a theme via wp.updates.
  3187 		 * Install a theme via wp.updates.
  3183 		 *
  3188 		 *
  3184 		 * @since 4.9.0
  3189 		 * @since 4.9.0
  3185 		 *
  3190 		 *
  3186 		 * @param {jQuery.Event} event - Event.
  3191 		 * @param {jQuery.Event} event - Event.
  3187 		 * @returns {jQuery.promise} Promise.
  3192 		 * @return {jQuery.promise} Promise.
  3188 		 */
  3193 		 */
  3189 		installTheme: function( event ) {
  3194 		installTheme: function( event ) {
  3190 			var panel = this, preview, onInstallSuccess, slug = $( event.target ).data( 'slug' ), deferred = $.Deferred(), request;
  3195 			var panel = this, preview, onInstallSuccess, slug = $( event.target ).data( 'slug' ), deferred = $.Deferred(), request;
  3191 			preview = $( event.target ).hasClass( 'preview' );
  3196 			preview = $( event.target ).hasClass( 'preview' );
  3192 
  3197 
  3287 		 * Load theme preview.
  3292 		 * Load theme preview.
  3288 		 *
  3293 		 *
  3289 		 * @since 4.9.0
  3294 		 * @since 4.9.0
  3290 		 *
  3295 		 *
  3291 		 * @param {string} themeId Theme ID.
  3296 		 * @param {string} themeId Theme ID.
  3292 		 * @returns {jQuery.promise} Promise.
  3297 		 * @return {jQuery.promise} Promise.
  3293 		 */
  3298 		 */
  3294 		loadThemePreview: function( themeId ) {
  3299 		loadThemePreview: function( themeId ) {
  3295 			var panel = this, deferred = $.Deferred(), onceProcessingComplete, urlParser, queryParams;
  3300 			var panel = this, deferred = $.Deferred(), onceProcessingComplete, urlParser, queryParams;
  3296 
  3301 
  3297 			// Prevent loading a non-active theme preview when there is a drafted/scheduled changeset.
  3302 			// Prevent loading a non-active theme preview when there is a drafted/scheduled changeset.
  3363 		 * Update a theme via wp.updates.
  3368 		 * Update a theme via wp.updates.
  3364 		 *
  3369 		 *
  3365 		 * @since 4.9.0
  3370 		 * @since 4.9.0
  3366 		 *
  3371 		 *
  3367 		 * @param {jQuery.Event} event - Event.
  3372 		 * @param {jQuery.Event} event - Event.
  3368 		 * @returns {void}
  3373 		 * @return {void}
  3369 		 */
  3374 		 */
  3370 		updateTheme: function( event ) {
  3375 		updateTheme: function( event ) {
  3371 			wp.updates.maybeRequestFilesystemCredentials( event );
  3376 			wp.updates.maybeRequestFilesystemCredentials( event );
  3372 
  3377 
  3373 			$( document ).one( 'wp-theme-update-success', function( e, response ) {
  3378 			$( document ).one( 'wp-theme-update-success', function( e, response ) {
  3393 		 * Delete a theme via wp.updates.
  3398 		 * Delete a theme via wp.updates.
  3394 		 *
  3399 		 *
  3395 		 * @since 4.9.0
  3400 		 * @since 4.9.0
  3396 		 *
  3401 		 *
  3397 		 * @param {jQuery.Event} event - Event.
  3402 		 * @param {jQuery.Event} event - Event.
  3398 		 * @returns {void}
  3403 		 * @return {void}
  3399 		 */
  3404 		 */
  3400 		deleteTheme: function( event ) {
  3405 		deleteTheme: function( event ) {
  3401 			var theme, section;
  3406 			var theme, section;
  3402 			theme = $( event.target ).data( 'slug' );
  3407 			theme = $( event.target ).data( 'slug' );
  3403 			section = api.section( 'installed_themes' );
  3408 			section = api.section( 'installed_themes' );
  3475 		 * @borrows wp.customize~Container#activate as this#activate
  3480 		 * @borrows wp.customize~Container#activate as this#activate
  3476 		 * @borrows wp.customize~Container#deactivate as this#deactivate
  3481 		 * @borrows wp.customize~Container#deactivate as this#deactivate
  3477 		 * @borrows wp.customize~Container#_toggleActive as this#_toggleActive
  3482 		 * @borrows wp.customize~Container#_toggleActive as this#_toggleActive
  3478 		 *
  3483 		 *
  3479 		 * @param {string} id                       - Unique identifier for the control instance.
  3484 		 * @param {string} id                       - Unique identifier for the control instance.
  3480 		 * @param {object} options                  - Options hash for the control instance.
  3485 		 * @param {Object} options                  - Options hash for the control instance.
  3481 		 * @param {object} options.type             - Type of control (e.g. text, radio, dropdown-pages, etc.)
  3486 		 * @param {Object} options.type             - Type of control (e.g. text, radio, dropdown-pages, etc.)
  3482 		 * @param {string} [options.content]        - The HTML content for the control or at least its container. This should normally be left blank and instead supplying a templateId.
  3487 		 * @param {string} [options.content]        - The HTML content for the control or at least its container. This should normally be left blank and instead supplying a templateId.
  3483 		 * @param {string} [options.templateId]     - Template ID for control's content.
  3488 		 * @param {string} [options.templateId]     - Template ID for control's content.
  3484 		 * @param {string} [options.priority=10]    - Order of priority to show the control within the section.
  3489 		 * @param {string} [options.priority=10]    - Order of priority to show the control within the section.
  3485 		 * @param {string} [options.active=true]    - Whether the control is active.
  3490 		 * @param {string} [options.active=true]    - Whether the control is active.
  3486 		 * @param {string} options.section          - The ID of the section the control belongs to.
  3491 		 * @param {string} options.section          - The ID of the section the control belongs to.
  3489 		 * @param {mixed}  options.settings.default - The ID of the setting the control relates to.
  3494 		 * @param {mixed}  options.settings.default - The ID of the setting the control relates to.
  3490 		 * @param {string} options.settings.data    - @todo Is this used?
  3495 		 * @param {string} options.settings.data    - @todo Is this used?
  3491 		 * @param {string} options.label            - Label.
  3496 		 * @param {string} options.label            - Label.
  3492 		 * @param {string} options.description      - Description.
  3497 		 * @param {string} options.description      - Description.
  3493 		 * @param {number} [options.instanceNumber] - Order in which this instance was created in relation to other instances.
  3498 		 * @param {number} [options.instanceNumber] - Order in which this instance was created in relation to other instances.
  3494 		 * @param {object} [options.params]         - Deprecated wrapper for the above properties.
  3499 		 * @param {Object} [options.params]         - Deprecated wrapper for the above properties.
  3495 		 * @returns {void}
  3500 		 * @return {void}
  3496 		 */
  3501 		 */
  3497 		initialize: function( id, options ) {
  3502 		initialize: function( id, options ) {
  3498 			var control = this, deferredSettingIds = [], settings, gatherSettings;
  3503 			var control = this, deferredSettingIds = [], settings, gatherSettings;
  3499 
  3504 
  3500 			control.params = _.extend(
  3505 			control.params = _.extend(
  3501 				{},
  3506 				{},
  3502 				control.defaults,
  3507 				control.defaults,
  3503 				control.params || {}, // In case sub-class already defines.
  3508 				control.params || {}, // In case subclass already defines.
  3504 				options.params || options || {} // The options.params property is deprecated, but it is checked first for back-compat.
  3509 				options.params || options || {} // The options.params property is deprecated, but it is checked first for back-compat.
  3505 			);
  3510 			);
  3506 
  3511 
  3507 			if ( ! api.Control.instanceCounter ) {
  3512 			if ( ! api.Control.instanceCounter ) {
  3508 				api.Control.instanceCounter = 0;
  3513 				api.Control.instanceCounter = 0;
  3631 		 * Link elements between settings and inputs.
  3636 		 * Link elements between settings and inputs.
  3632 		 *
  3637 		 *
  3633 		 * @since 4.7.0
  3638 		 * @since 4.7.0
  3634 		 * @access public
  3639 		 * @access public
  3635 		 *
  3640 		 *
  3636 		 * @returns {void}
  3641 		 * @return {void}
  3637 		 */
  3642 		 */
  3638 		linkElements: function () {
  3643 		linkElements: function () {
  3639 			var control = this, nodes, radios, element;
  3644 			var control = this, nodes, radios, element;
  3640 
  3645 
  3641 			nodes = control.container.find( '[data-customize-setting-link], [data-customize-setting-key-link]' );
  3646 			nodes = control.container.find( '[data-customize-setting-link], [data-customize-setting-key-link]' );
  3680 		 */
  3685 		 */
  3681 		embed: function () {
  3686 		embed: function () {
  3682 			var control = this,
  3687 			var control = this,
  3683 				inject;
  3688 				inject;
  3684 
  3689 
  3685 			// Watch for changes to the section state
  3690 			// Watch for changes to the section state.
  3686 			inject = function ( sectionId ) {
  3691 			inject = function ( sectionId ) {
  3687 				var parentContainer;
  3692 				var parentContainer;
  3688 				if ( ! sectionId ) { // @todo allow a control to be embedded without a section, for instance a control embedded in the front end.
  3693 				if ( ! sectionId ) { // @todo Allow a control to be embedded without a section, for instance a control embedded in the front end.
  3689 					return;
  3694 					return;
  3690 				}
  3695 				}
  3691 				// Wait for the section to be registered
  3696 				// Wait for the section to be registered.
  3692 				api.section( sectionId, function ( section ) {
  3697 				api.section( sectionId, function ( section ) {
  3693 					// Wait for the section to be ready/initialized
  3698 					// Wait for the section to be ready/initialized.
  3694 					section.deferred.embedded.done( function () {
  3699 					section.deferred.embedded.done( function () {
  3695 						parentContainer = ( section.contentContainer.is( 'ul' ) ) ? section.contentContainer : section.contentContainer.find( 'ul:first' );
  3700 						parentContainer = ( section.contentContainer.is( 'ul' ) ) ? section.contentContainer : section.contentContainer.find( 'ul:first' );
  3696 						if ( ! control.container.parent().is( parentContainer ) ) {
  3701 						if ( ! control.container.parent().is( parentContainer ) ) {
  3697 							parentContainer.append( control.container );
  3702 							parentContainer.append( control.container );
  3698 							control.renderContent();
  3703 							control.renderContent();
  3706 		},
  3711 		},
  3707 
  3712 
  3708 		/**
  3713 		/**
  3709 		 * Triggered when the control's markup has been injected into the DOM.
  3714 		 * Triggered when the control's markup has been injected into the DOM.
  3710 		 *
  3715 		 *
  3711 		 * @returns {void}
  3716 		 * @return {void}
  3712 		 */
  3717 		 */
  3713 		ready: function() {
  3718 		ready: function() {
  3714 			var control = this, newItem;
  3719 			var control = this, newItem;
  3715 			if ( 'dropdown-pages' === control.params.type && control.params.allow_addition ) {
  3720 			if ( 'dropdown-pages' === control.params.type && control.params.allow_addition ) {
  3716 				newItem = control.container.find( '.new-content-item' );
  3721 				newItem = control.container.find( '.new-content-item' );
  3722 				});
  3727 				});
  3723 				control.container.on( 'click', '.add-content', function() {
  3728 				control.container.on( 'click', '.add-content', function() {
  3724 					control.addNewPage();
  3729 					control.addNewPage();
  3725 				});
  3730 				});
  3726 				control.container.on( 'keydown', '.create-item-input', function( e ) {
  3731 				control.container.on( 'keydown', '.create-item-input', function( e ) {
  3727 					if ( 13 === e.which ) { // Enter
  3732 					if ( 13 === e.which ) { // Enter.
  3728 						control.addNewPage();
  3733 						control.addNewPage();
  3729 					}
  3734 					}
  3730 				});
  3735 				});
  3731 			}
  3736 			}
  3732 		},
  3737 		},
  3737 		 * Control subclasses may override this to return the proper container to render notifications into.
  3742 		 * Control subclasses may override this to return the proper container to render notifications into.
  3738 		 * Injects the notification container for existing controls that lack the necessary container,
  3743 		 * Injects the notification container for existing controls that lack the necessary container,
  3739 		 * including special handling for nav menu items and widgets.
  3744 		 * including special handling for nav menu items and widgets.
  3740 		 *
  3745 		 *
  3741 		 * @since 4.6.0
  3746 		 * @since 4.6.0
  3742 		 * @returns {jQuery} Setting validation message element.
  3747 		 * @return {jQuery} Setting validation message element.
  3743 		 */
  3748 		 */
  3744 		getNotificationsContainerElement: function() {
  3749 		getNotificationsContainerElement: function() {
  3745 			var control = this, controlTitle, notificationsContainer;
  3750 			var control = this, controlTitle, notificationsContainer;
  3746 
  3751 
  3747 			notificationsContainer = control.container.find( '.customize-control-notifications-container:first' );
  3752 			notificationsContainer = control.container.find( '.customize-control-notifications-container:first' );
  3768 
  3773 
  3769 		/**
  3774 		/**
  3770 		 * Set up notifications.
  3775 		 * Set up notifications.
  3771 		 *
  3776 		 *
  3772 		 * @since 4.9.0
  3777 		 * @since 4.9.0
  3773 		 * @returns {void}
  3778 		 * @return {void}
  3774 		 */
  3779 		 */
  3775 		setupNotifications: function() {
  3780 		setupNotifications: function() {
  3776 			var control = this, renderNotificationsIfVisible, onSectionAssigned;
  3781 			var control = this, renderNotificationsIfVisible, onSectionAssigned;
  3777 
  3782 
  3778 			// Add setting notifications to the control notification.
  3783 			// Add setting notifications to the control notification.
  3893 		 * This does not change the active state, it merely handles the behavior
  3898 		 * This does not change the active state, it merely handles the behavior
  3894 		 * for when it does change.
  3899 		 * for when it does change.
  3895 		 *
  3900 		 *
  3896 		 * @since 4.1.0
  3901 		 * @since 4.1.0
  3897 		 *
  3902 		 *
  3898 		 * @param {Boolean}  active
  3903 		 * @param {boolean}  active
  3899 		 * @param {Object}   args
  3904 		 * @param {Object}   args
  3900 		 * @param {Number}   args.duration
  3905 		 * @param {number}   args.duration
  3901 		 * @param {Function} args.completeCallback
  3906 		 * @param {Function} args.completeCallback
  3902 		 */
  3907 		 */
  3903 		onChangeActive: function ( active, args ) {
  3908 		onChangeActive: function ( active, args ) {
  3904 			if ( args.unchanged ) {
  3909 			if ( args.unchanged ) {
  3905 				if ( args.completeCallback ) {
  3910 				if ( args.completeCallback ) {
  3907 				}
  3912 				}
  3908 				return;
  3913 				return;
  3909 			}
  3914 			}
  3910 
  3915 
  3911 			if ( ! $.contains( document, this.container[0] ) ) {
  3916 			if ( ! $.contains( document, this.container[0] ) ) {
  3912 				// jQuery.fn.slideUp is not hiding an element if it is not in the DOM
  3917 				// jQuery.fn.slideUp is not hiding an element if it is not in the DOM.
  3913 				this.container.toggle( active );
  3918 				this.container.toggle( active );
  3914 				if ( args.completeCallback ) {
  3919 				if ( args.completeCallback ) {
  3915 					args.completeCallback();
  3920 					args.completeCallback();
  3916 				}
  3921 				}
  3917 			} else if ( active ) {
  3922 			} else if ( active ) {
  3955 					} else {
  3960 					} else {
  3956 						statuses.hide();
  3961 						statuses.hide();
  3957 					}
  3962 					}
  3958 				};
  3963 				};
  3959 
  3964 
  3960 			// Support the .dropdown class to open/close complex elements
  3965 			// Support the .dropdown class to open/close complex elements.
  3961 			this.container.on( 'click keydown', '.dropdown', function( event ) {
  3966 			this.container.on( 'click keydown', '.dropdown', function( event ) {
  3962 				if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
  3967 				if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
  3963 					return;
  3968 					return;
  3964 				}
  3969 				}
  3965 
  3970 
  3971 
  3976 
  3972 				if ( control.container.hasClass( 'open' ) ) {
  3977 				if ( control.container.hasClass( 'open' ) ) {
  3973 					control.container.parent().parent().find( 'li.library-selected' ).focus();
  3978 					control.container.parent().parent().find( 'li.library-selected' ).focus();
  3974 				}
  3979 				}
  3975 
  3980 
  3976 				// Don't want to fire focus and click at same time
  3981 				// Don't want to fire focus and click at same time.
  3977 				toggleFreeze = true;
  3982 				toggleFreeze = true;
  3978 				setTimeout(function () {
  3983 				setTimeout(function () {
  3979 					toggleFreeze = false;
  3984 					toggleFreeze = false;
  3980 				}, 400);
  3985 				}, 400);
  3981 			});
  3986 			});
  4015 				'url'
  4020 				'url'
  4016 			];
  4021 			];
  4017 
  4022 
  4018 			templateId = control.templateSelector;
  4023 			templateId = control.templateSelector;
  4019 
  4024 
  4020 			// Use default content template when a standard HTML type is used, there isn't a more specific template existing, and the control container is empty.
  4025 			// Use default content template when a standard HTML type is used,
       
  4026 			// there isn't a more specific template existing, and the control container is empty.
  4021 			if ( templateId === 'customize-control-' + control.params.type + '-content' &&
  4027 			if ( templateId === 'customize-control-' + control.params.type + '-content' &&
  4022 				_.contains( standardTypes, control.params.type ) &&
  4028 				_.contains( standardTypes, control.params.type ) &&
  4023 				! document.getElementById( 'tmpl-' + templateId ) &&
  4029 				! document.getElementById( 'tmpl-' + templateId ) &&
  4024 				0 === control.container.children().length )
  4030 				0 === control.container.children().length )
  4025 			{
  4031 			{
  4045 		/**
  4051 		/**
  4046 		 * Add a new page to a dropdown-pages control reusing menus code for this.
  4052 		 * Add a new page to a dropdown-pages control reusing menus code for this.
  4047 		 *
  4053 		 *
  4048 		 * @since 4.7.0
  4054 		 * @since 4.7.0
  4049 		 * @access private
  4055 		 * @access private
  4050 		 * @returns {void}
  4056 		 *
       
  4057 		 * @return {void}
  4051 		 */
  4058 		 */
  4052 		addNewPage: function () {
  4059 		addNewPage: function () {
  4053 			var control = this, promise, toggle, container, input, title, select;
  4060 			var control = this, promise, toggle, container, input, title, select;
  4054 
  4061 
  4055 			if ( 'dropdown-pages' !== control.params.type || ! control.params.allow_addition || ! api.Menus ) {
  4062 			if ( 'dropdown-pages' !== control.params.type || ! control.params.allow_addition || ! api.Menus ) {
  4068 			}
  4075 			}
  4069 
  4076 
  4070 			input.removeClass( 'invalid' );
  4077 			input.removeClass( 'invalid' );
  4071 			input.attr( 'disabled', 'disabled' );
  4078 			input.attr( 'disabled', 'disabled' );
  4072 
  4079 
  4073 			// The menus functions add the page, publish when appropriate, and also add the new page to the dropdown-pages controls.
  4080 			// The menus functions add the page, publish when appropriate,
       
  4081 			// and also add the new page to the dropdown-pages controls.
  4074 			promise = api.Menus.insertAutoDraftPost( {
  4082 			promise = api.Menus.insertAutoDraftPost( {
  4075 				post_title: title,
  4083 				post_title: title,
  4076 				post_type: 'page'
  4084 				post_type: 'page'
  4077 			} );
  4085 			} );
  4078 			promise.done( function( data ) {
  4086 			promise.done( function( data ) {
  4197 			control.container.on( 'click keydown', '.default-button', control.restoreDefault );
  4205 			control.container.on( 'click keydown', '.default-button', control.restoreDefault );
  4198 			control.container.on( 'click keydown', '.remove-button', control.pausePlayer );
  4206 			control.container.on( 'click keydown', '.remove-button', control.pausePlayer );
  4199 			control.container.on( 'click keydown', '.remove-button', control.removeFile );
  4207 			control.container.on( 'click keydown', '.remove-button', control.removeFile );
  4200 			control.container.on( 'click keydown', '.remove-button', control.cleanupPlayer );
  4208 			control.container.on( 'click keydown', '.remove-button', control.cleanupPlayer );
  4201 
  4209 
  4202 			// Resize the player controls when it becomes visible (ie when section is expanded)
  4210 			// Resize the player controls when it becomes visible (ie when section is expanded).
  4203 			api.section( control.section() ).container
  4211 			api.section( control.section() ).container
  4204 				.on( 'expanded', function() {
  4212 				.on( 'expanded', function() {
  4205 					if ( control.player ) {
  4213 					if ( control.player ) {
  4206 						control.player.setControlsSize();
  4214 						control.player.setControlsSize();
  4207 					}
  4215 					}
  4345 		},
  4353 		},
  4346 
  4354 
  4347 		/**
  4355 		/**
  4348 		 * Called when the "Remove" link is clicked. Empties the setting.
  4356 		 * Called when the "Remove" link is clicked. Empties the setting.
  4349 		 *
  4357 		 *
  4350 		 * @param {object} event jQuery Event object
  4358 		 * @param {Object} event jQuery Event object
  4351 		 */
  4359 		 */
  4352 		removeFile: function( event ) {
  4360 		removeFile: function( event ) {
  4353 			if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
  4361 			if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
  4354 				return;
  4362 				return;
  4355 			}
  4363 			}
  4430 			api.UploadControl.prototype.ready.apply( this, arguments );
  4438 			api.UploadControl.prototype.ready.apply( this, arguments );
  4431 		},
  4439 		},
  4432 
  4440 
  4433 		/**
  4441 		/**
  4434 		 * Callback handler for when an attachment is selected in the media modal.
  4442 		 * Callback handler for when an attachment is selected in the media modal.
  4435 		 * Does an additional AJAX request for setting the background context.
  4443 		 * Does an additional Ajax request for setting the background context.
  4436 		 */
  4444 		 */
  4437 		select: function() {
  4445 		select: function() {
  4438 			api.UploadControl.prototype.select.apply( this, arguments );
  4446 			api.UploadControl.prototype.select.apply( this, arguments );
  4439 
  4447 
  4440 			wp.ajax.post( 'custom-background-add', {
  4448 			wp.ajax.post( 'custom-background-add', {
  4555 		},
  4563 		},
  4556 
  4564 
  4557 		/**
  4565 		/**
  4558 		 * After the image has been cropped, apply the cropped image data to the setting.
  4566 		 * After the image has been cropped, apply the cropped image data to the setting.
  4559 		 *
  4567 		 *
  4560 		 * @param {object} croppedImage Cropped attachment data.
  4568 		 * @param {Object} croppedImage Cropped attachment data.
  4561 		 */
  4569 		 */
  4562 		onCropped: function( croppedImage ) {
  4570 		onCropped: function( croppedImage ) {
  4563 			this.setImageFromAttachment( croppedImage );
  4571 			this.setImageFromAttachment( croppedImage );
  4564 		},
  4572 		},
  4565 
  4573 
  4568 		 * control-specific data, to be fed to the imgAreaSelect plugin in
  4576 		 * control-specific data, to be fed to the imgAreaSelect plugin in
  4569 		 * wp.media.view.Cropper.
  4577 		 * wp.media.view.Cropper.
  4570 		 *
  4578 		 *
  4571 		 * @param {wp.media.model.Attachment} attachment
  4579 		 * @param {wp.media.model.Attachment} attachment
  4572 		 * @param {wp.media.controller.Cropper} controller
  4580 		 * @param {wp.media.controller.Cropper} controller
  4573 		 * @returns {Object} Options
  4581 		 * @return {Object} Options
  4574 		 */
  4582 		 */
  4575 		calculateImageSelectOptions: function( attachment, controller ) {
  4583 		calculateImageSelectOptions: function( attachment, controller ) {
  4576 			var control    = controller.get( 'control' ),
  4584 			var control    = controller.get( 'control' ),
  4577 				flexWidth  = !! parseInt( control.params.flex_width, 10 ),
  4585 				flexWidth  = !! parseInt( control.params.flex_width, 10 ),
  4578 				flexHeight = !! parseInt( control.params.flex_height, 10 ),
  4586 				flexHeight = !! parseInt( control.params.flex_height, 10 ),
  4631 		},
  4639 		},
  4632 
  4640 
  4633 		/**
  4641 		/**
  4634 		 * Return whether the image must be cropped, based on required dimensions.
  4642 		 * Return whether the image must be cropped, based on required dimensions.
  4635 		 *
  4643 		 *
  4636 		 * @param {bool} flexW
  4644 		 * @param {boolean} flexW
  4637 		 * @param {bool} flexH
  4645 		 * @param {boolean} flexH
  4638 		 * @param {int}  dstW
  4646 		 * @param {number}  dstW
  4639 		 * @param {int}  dstH
  4647 		 * @param {number}  dstH
  4640 		 * @param {int}  imgW
  4648 		 * @param {number}  imgW
  4641 		 * @param {int}  imgH
  4649 		 * @param {number}  imgH
  4642 		 * @return {bool}
  4650 		 * @return {boolean}
  4643 		 */
  4651 		 */
  4644 		mustBeCropped: function( flexW, flexH, dstW, dstH, imgW, imgH ) {
  4652 		mustBeCropped: function( flexW, flexH, dstW, dstH, imgW, imgH ) {
  4645 			if ( true === flexW && true === flexH ) {
  4653 			if ( true === flexW && true === flexH ) {
  4646 				return false;
  4654 				return false;
  4647 			}
  4655 			}
  4674 		},
  4682 		},
  4675 
  4683 
  4676 		/**
  4684 		/**
  4677 		 * Updates the setting and re-renders the control UI.
  4685 		 * Updates the setting and re-renders the control UI.
  4678 		 *
  4686 		 *
  4679 		 * @param {object} attachment
  4687 		 * @param {Object} attachment
  4680 		 */
  4688 		 */
  4681 		setImageFromAttachment: function( attachment ) {
  4689 		setImageFromAttachment: function( attachment ) {
  4682 			this.params.attachment = attachment;
  4690 			this.params.attachment = attachment;
  4683 
  4691 
  4684 			// Set the Customizer setting; the callback takes care of rendering.
  4692 			// Set the Customizer setting; the callback takes care of rendering.
  4760 		},
  4768 		},
  4761 
  4769 
  4762 		/**
  4770 		/**
  4763 		 * Updates the setting and re-renders the control UI.
  4771 		 * Updates the setting and re-renders the control UI.
  4764 		 *
  4772 		 *
  4765 		 * @param {object} attachment
  4773 		 * @param {Object} attachment
  4766 		 */
  4774 		 */
  4767 		setImageFromAttachment: function( attachment ) {
  4775 		setImageFromAttachment: function( attachment ) {
  4768 			var sizes = [ 'site_icon-32', 'thumbnail', 'full' ], link,
  4776 			var sizes = [ 'site_icon-32', 'thumbnail', 'full' ], link,
  4769 				icon;
  4777 				icon;
  4770 
  4778 
  4789 		},
  4797 		},
  4790 
  4798 
  4791 		/**
  4799 		/**
  4792 		 * Called when the "Remove" link is clicked. Empties the setting.
  4800 		 * Called when the "Remove" link is clicked. Empties the setting.
  4793 		 *
  4801 		 *
  4794 		 * @param {object} event jQuery Event object
  4802 		 * @param {Object} event jQuery Event object
  4795 		 */
  4803 		 */
  4796 		removeFile: function( event ) {
  4804 		removeFile: function( event ) {
  4797 			if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
  4805 			if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
  4798 				return;
  4806 				return;
  4799 			}
  4807 			}
  4851 		 * Returns a new instance of api.HeaderTool.ImageModel based on the currently
  4859 		 * Returns a new instance of api.HeaderTool.ImageModel based on the currently
  4852 		 * saved header image (if any).
  4860 		 * saved header image (if any).
  4853 		 *
  4861 		 *
  4854 		 * @since 4.2.0
  4862 		 * @since 4.2.0
  4855 		 *
  4863 		 *
  4856 		 * @returns {Object} Options
  4864 		 * @return {Object} Options
  4857 		 */
  4865 		 */
  4858 		getInitialHeaderImage: function() {
  4866 		getInitialHeaderImage: function() {
  4859 			if ( ! api.get().header_image || ! api.get().header_image_data || _.contains( [ 'remove-header', 'random-default-image', 'random-uploaded-image' ], api.get().header_image ) ) {
  4867 			if ( ! api.get().header_image || ! api.get().header_image_data || _.contains( [ 'remove-header', 'random-default-image', 'random-uploaded-image' ], api.get().header_image ) ) {
  4860 				return new api.HeaderTool.ImageModel();
  4868 				return new api.HeaderTool.ImageModel();
  4861 			}
  4869 			}
  4884 		 * theme-specific data, to be fed to the imgAreaSelect plugin in
  4892 		 * theme-specific data, to be fed to the imgAreaSelect plugin in
  4885 		 * wp.media.view.Cropper.
  4893 		 * wp.media.view.Cropper.
  4886 		 *
  4894 		 *
  4887 		 * @param {wp.media.model.Attachment} attachment
  4895 		 * @param {wp.media.model.Attachment} attachment
  4888 		 * @param {wp.media.controller.Cropper} controller
  4896 		 * @param {wp.media.controller.Cropper} controller
  4889 		 * @returns {Object} Options
  4897 		 * @return {Object} Options
  4890 		 */
  4898 		 */
  4891 		calculateImageSelectOptions: function(attachment, controller) {
  4899 		calculateImageSelectOptions: function(attachment, controller) {
  4892 			var xInit = parseInt(_wpCustomizeHeader.data.width, 10),
  4900 			var xInit = parseInt(_wpCustomizeHeader.data.width, 10),
  4893 				yInit = parseInt(_wpCustomizeHeader.data.height, 10),
  4901 				yInit = parseInt(_wpCustomizeHeader.data.height, 10),
  4894 				flexWidth = !! parseInt(_wpCustomizeHeader.data['flex-width'], 10),
  4902 				flexWidth = !! parseInt(_wpCustomizeHeader.data['flex-width'], 10),
  4999 		},
  5007 		},
  5000 
  5008 
  5001 		/**
  5009 		/**
  5002 		 * After the image has been cropped, apply the cropped image data to the setting.
  5010 		 * After the image has been cropped, apply the cropped image data to the setting.
  5003 		 *
  5011 		 *
  5004 		 * @param {object} croppedImage Cropped attachment data.
  5012 		 * @param {Object} croppedImage Cropped attachment data.
  5005 		 */
  5013 		 */
  5006 		onCropped: function(croppedImage) {
  5014 		onCropped: function(croppedImage) {
  5007 			var url = croppedImage.url,
  5015 			var url = croppedImage.url,
  5008 				attachmentId = croppedImage.attachment_id,
  5016 				attachmentId = croppedImage.attachment_id,
  5009 				w = croppedImage.width,
  5017 				w = croppedImage.width,
  5012 		},
  5020 		},
  5013 
  5021 
  5014 		/**
  5022 		/**
  5015 		 * If cropping was skipped, apply the image data directly to the setting.
  5023 		 * If cropping was skipped, apply the image data directly to the setting.
  5016 		 *
  5024 		 *
  5017 		 * @param {object} selection
  5025 		 * @param {Object} selection
  5018 		 */
  5026 		 */
  5019 		onSkippedCrop: function(selection) {
  5027 		onSkippedCrop: function(selection) {
  5020 			var url = selection.get('url'),
  5028 			var url = selection.get('url'),
  5021 				w = selection.get('width'),
  5029 				w = selection.get('width'),
  5022 				h = selection.get('height');
  5030 				h = selection.get('height');
  5026 		/**
  5034 		/**
  5027 		 * Creates a new wp.customize.HeaderTool.ImageModel from provided
  5035 		 * Creates a new wp.customize.HeaderTool.ImageModel from provided
  5028 		 * header image data and inserts it into the user-uploaded headers
  5036 		 * header image data and inserts it into the user-uploaded headers
  5029 		 * collection.
  5037 		 * collection.
  5030 		 *
  5038 		 *
  5031 		 * @param {String} url
  5039 		 * @param {string} url
  5032 		 * @param {Number} attachmentId
  5040 		 * @param {number} attachmentId
  5033 		 * @param {Number} width
  5041 		 * @param {number} width
  5034 		 * @param {Number} height
  5042 		 * @param {number} height
  5035 		 */
  5043 		 */
  5036 		setImageFromURL: function(url, attachmentId, width, height) {
  5044 		setImageFromURL: function(url, attachmentId, width, height) {
  5037 			var choice, data = {};
  5045 			var choice, data = {};
  5038 
  5046 
  5039 			data.url = url;
  5047 			data.url = url;
  5126 				// Prevent the modal from showing when the user clicks the action button.
  5134 				// Prevent the modal from showing when the user clicks the action button.
  5127 				if ( $( event.target ).is( '.theme-actions .button, .update-theme' ) ) {
  5135 				if ( $( event.target ).is( '.theme-actions .button, .update-theme' ) ) {
  5128 					return;
  5136 					return;
  5129 				}
  5137 				}
  5130 
  5138 
  5131 				event.preventDefault(); // Keep this AFTER the key filter above
  5139 				event.preventDefault(); // Keep this AFTER the key filter above.
  5132 				section = api.section( control.section() );
  5140 				section = api.section( control.section() );
  5133 				section.showDetails( control.params.theme, function() {
  5141 				section.showDetails( control.params.theme, function() {
  5134 
  5142 
  5135 					// Temporary special function since supplying SFTP credentials does not work yet. See #42184.
  5143 					// Temporary special function since supplying SFTP credentials does not work yet. See #42184.
  5136 					if ( api.settings.theme._filesystemCredentialsNeeded ) {
  5144 					if ( api.settings.theme._filesystemCredentialsNeeded ) {
  5153 		/**
  5161 		/**
  5154 		 * Show or hide the theme based on the presence of the term in the title, description, tags, and author.
  5162 		 * Show or hide the theme based on the presence of the term in the title, description, tags, and author.
  5155 		 *
  5163 		 *
  5156 		 * @since 4.2.0
  5164 		 * @since 4.2.0
  5157 		 * @param {Array} terms - An array of terms to search for.
  5165 		 * @param {Array} terms - An array of terms to search for.
  5158 		 * @returns {boolean} Whether a theme control was activated or not.
  5166 		 * @return {boolean} Whether a theme control was activated or not.
  5159 		 */
  5167 		 */
  5160 		filter: function( terms ) {
  5168 		filter: function( terms ) {
  5161 			var control = this,
  5169 			var control = this,
  5162 				matchCount = 0,
  5170 				matchCount = 0,
  5163 				haystack = control.params.theme.name + ' ' +
  5171 				haystack = control.params.theme.name + ' ' +
  5194 			if ( 0 !== matchCount ) {
  5202 			if ( 0 !== matchCount ) {
  5195 				control.activate();
  5203 				control.activate();
  5196 				control.params.priority = 101 - matchCount; // Sort results by match count.
  5204 				control.params.priority = 101 - matchCount; // Sort results by match count.
  5197 				return true;
  5205 				return true;
  5198 			} else {
  5206 			} else {
  5199 				control.deactivate(); // Hide control
  5207 				control.deactivate(); // Hide control.
  5200 				control.params.priority = 101;
  5208 				control.params.priority = 101;
  5201 				return false;
  5209 				return false;
  5202 			}
  5210 			}
  5203 		},
  5211 		},
  5204 
  5212 
  5205 		/**
  5213 		/**
  5206 		 * Rerender the theme from its JS template with the installed type.
  5214 		 * Rerender the theme from its JS template with the installed type.
  5207 		 *
  5215 		 *
  5208 		 * @since 4.9.0
  5216 		 * @since 4.9.0
  5209 		 *
  5217 		 *
  5210 		 * @returns {void}
  5218 		 * @return {void}
  5211 		 */
  5219 		 */
  5212 		rerenderAsInstalled: function( installed ) {
  5220 		rerenderAsInstalled: function( installed ) {
  5213 			var control = this, section;
  5221 			var control = this, section;
  5214 			if ( installed ) {
  5222 			if ( installed ) {
  5215 				control.params.theme.type = 'installed';
  5223 				control.params.theme.type = 'installed';
  5235 		/**
  5243 		/**
  5236 		 * Initialize.
  5244 		 * Initialize.
  5237 		 *
  5245 		 *
  5238 		 * @since 4.9.0
  5246 		 * @since 4.9.0
  5239 		 * @param {string} id      - Unique identifier for the control instance.
  5247 		 * @param {string} id      - Unique identifier for the control instance.
  5240 		 * @param {object} options - Options hash for the control instance.
  5248 		 * @param {Object} options - Options hash for the control instance.
  5241 		 * @returns {void}
  5249 		 * @return {void}
  5242 		 */
  5250 		 */
  5243 		initialize: function( id, options ) {
  5251 		initialize: function( id, options ) {
  5244 			var control = this;
  5252 			var control = this;
  5245 			control.deferred = _.extend( control.deferred || {}, {
  5253 			control.deferred = _.extend( control.deferred || {}, {
  5246 				codemirror: $.Deferred()
  5254 				codemirror: $.Deferred()
  5271 
  5279 
  5272 		/**
  5280 		/**
  5273 		 * Initialize the editor when the containing section is ready and expanded.
  5281 		 * Initialize the editor when the containing section is ready and expanded.
  5274 		 *
  5282 		 *
  5275 		 * @since 4.9.0
  5283 		 * @since 4.9.0
  5276 		 * @returns {void}
  5284 		 * @return {void}
  5277 		 */
  5285 		 */
  5278 		ready: function() {
  5286 		ready: function() {
  5279 			var control = this;
  5287 			var control = this;
  5280 			if ( ! control.section() ) {
  5288 			if ( ! control.section() ) {
  5281 				control.initEditor();
  5289 				control.initEditor();
  5303 
  5311 
  5304 		/**
  5312 		/**
  5305 		 * Initialize editor.
  5313 		 * Initialize editor.
  5306 		 *
  5314 		 *
  5307 		 * @since 4.9.0
  5315 		 * @since 4.9.0
  5308 		 * @returns {void}
  5316 		 * @return {void}
  5309 		 */
  5317 		 */
  5310 		initEditor: function() {
  5318 		initEditor: function() {
  5311 			var control = this, element, editorSettings = false;
  5319 			var control = this, element, editorSettings = false;
  5312 
  5320 
  5313 			// Obtain editorSettings for instantiation.
  5321 			// Obtain editorSettings for instantiation.
  5354 		 * Make sure editor gets focused when control is focused.
  5362 		 * Make sure editor gets focused when control is focused.
  5355 		 *
  5363 		 *
  5356 		 * @since 4.9.0
  5364 		 * @since 4.9.0
  5357 		 * @param {Object}   [params] - Focus params.
  5365 		 * @param {Object}   [params] - Focus params.
  5358 		 * @param {Function} [params.completeCallback] - Function to call when expansion is complete.
  5366 		 * @param {Function} [params.completeCallback] - Function to call when expansion is complete.
  5359 		 * @returns {void}
  5367 		 * @return {void}
  5360 		 */
  5368 		 */
  5361 		focus: function( params ) {
  5369 		focus: function( params ) {
  5362 			var control = this, extendedParams = _.extend( {}, params ), originalCompleteCallback;
  5370 			var control = this, extendedParams = _.extend( {}, params ), originalCompleteCallback;
  5363 			originalCompleteCallback = extendedParams.completeCallback;
  5371 			originalCompleteCallback = extendedParams.completeCallback;
  5364 			extendedParams.completeCallback = function() {
  5372 			extendedParams.completeCallback = function() {
  5374 
  5382 
  5375 		/**
  5383 		/**
  5376 		 * Initialize syntax-highlighting editor.
  5384 		 * Initialize syntax-highlighting editor.
  5377 		 *
  5385 		 *
  5378 		 * @since 4.9.0
  5386 		 * @since 4.9.0
  5379 		 * @param {object} codeEditorSettings - Code editor settings.
  5387 		 * @param {Object} codeEditorSettings - Code editor settings.
  5380 		 * @returns {void}
  5388 		 * @return {void}
  5381 		 */
  5389 		 */
  5382 		initSyntaxHighlightingEditor: function( codeEditorSettings ) {
  5390 		initSyntaxHighlightingEditor: function( codeEditorSettings ) {
  5383 			var control = this, $textarea = control.container.find( 'textarea' ), settings, suspendEditorUpdate = false;
  5391 			var control = this, $textarea = control.container.find( 'textarea' ), settings, suspendEditorUpdate = false;
  5384 
  5392 
  5385 			settings = _.extend( {}, codeEditorSettings, {
  5393 			settings = _.extend( {}, codeEditorSettings, {
  5434 
  5442 
  5435 		/**
  5443 		/**
  5436 		 * Handle tabbing to the field after the editor.
  5444 		 * Handle tabbing to the field after the editor.
  5437 		 *
  5445 		 *
  5438 		 * @since 4.9.0
  5446 		 * @since 4.9.0
  5439 		 * @returns {void}
  5447 		 * @return {void}
  5440 		 */
  5448 		 */
  5441 		onTabNext: function onTabNext() {
  5449 		onTabNext: function onTabNext() {
  5442 			var control = this, controls, controlIndex, section;
  5450 			var control = this, controls, controlIndex, section;
  5443 			section = api.section( control.section() );
  5451 			section = api.section( control.section() );
  5444 			controls = section.controls();
  5452 			controls = section.controls();
  5452 
  5460 
  5453 		/**
  5461 		/**
  5454 		 * Handle tabbing to the field before the editor.
  5462 		 * Handle tabbing to the field before the editor.
  5455 		 *
  5463 		 *
  5456 		 * @since 4.9.0
  5464 		 * @since 4.9.0
  5457 		 * @returns {void}
  5465 		 * @return {void}
  5458 		 */
  5466 		 */
  5459 		onTabPrevious: function onTabPrevious() {
  5467 		onTabPrevious: function onTabPrevious() {
  5460 			var control = this, controls, controlIndex, section;
  5468 			var control = this, controls, controlIndex, section;
  5461 			section = api.section( control.section() );
  5469 			section = api.section( control.section() );
  5462 			controls = section.controls();
  5470 			controls = section.controls();
  5471 		/**
  5479 		/**
  5472 		 * Update error notice.
  5480 		 * Update error notice.
  5473 		 *
  5481 		 *
  5474 		 * @since 4.9.0
  5482 		 * @since 4.9.0
  5475 		 * @param {Array} errorAnnotations - Error annotations.
  5483 		 * @param {Array} errorAnnotations - Error annotations.
  5476 		 * @returns {void}
  5484 		 * @return {void}
  5477 		 */
  5485 		 */
  5478 		onUpdateErrorNotice: function onUpdateErrorNotice( errorAnnotations ) {
  5486 		onUpdateErrorNotice: function onUpdateErrorNotice( errorAnnotations ) {
  5479 			var control = this, message;
  5487 			var control = this, message;
  5480 			control.setting.notifications.remove( 'csslint_error' );
  5488 			control.setting.notifications.remove( 'csslint_error' );
  5481 
  5489 
  5494 
  5502 
  5495 		/**
  5503 		/**
  5496 		 * Initialize plain-textarea editor when syntax highlighting is disabled.
  5504 		 * Initialize plain-textarea editor when syntax highlighting is disabled.
  5497 		 *
  5505 		 *
  5498 		 * @since 4.9.0
  5506 		 * @since 4.9.0
  5499 		 * @returns {void}
  5507 		 * @return {void}
  5500 		 */
  5508 		 */
  5501 		initPlainTextareaEditor: function() {
  5509 		initPlainTextareaEditor: function() {
  5502 			var control = this, $textarea = control.container.find( 'textarea' ), textarea = $textarea[0];
  5510 			var control = this, $textarea = control.container.find( 'textarea' ), textarea = $textarea[0];
  5503 
  5511 
  5504 			$textarea.on( 'blur', function onBlur() {
  5512 			$textarea.on( 'blur', function onBlur() {
  5554 
  5562 
  5555 		/**
  5563 		/**
  5556 		 * Initialize behaviors.
  5564 		 * Initialize behaviors.
  5557 		 *
  5565 		 *
  5558 		 * @since 4.9.0
  5566 		 * @since 4.9.0
  5559 		 * @returns {void}
  5567 		 * @return {void}
  5560 		 */
  5568 		 */
  5561 		ready: function ready() {
  5569 		ready: function ready() {
  5562 			var control = this;
  5570 			var control = this;
  5563 
  5571 
  5564 			control.inputElements = {};
  5572 			control.inputElements = {};
  5616 		 * Parse datetime string.
  5624 		 * Parse datetime string.
  5617 		 *
  5625 		 *
  5618 		 * @since 4.9.0
  5626 		 * @since 4.9.0
  5619 		 *
  5627 		 *
  5620 		 * @param {string} datetime - Date/Time string. Accepts Y-m-d[ H:i[:s]] format.
  5628 		 * @param {string} datetime - Date/Time string. Accepts Y-m-d[ H:i[:s]] format.
  5621 		 * @returns {object|null} Returns object containing date components or null if parse error.
  5629 		 * @return {Object|null} Returns object containing date components or null if parse error.
  5622 		 */
  5630 		 */
  5623 		parseDateTime: function parseDateTime( datetime ) {
  5631 		parseDateTime: function parseDateTime( datetime ) {
  5624 			var control = this, matches, date, midDayHour = 12;
  5632 			var control = this, matches, date, midDayHour = 12;
  5625 
  5633 
  5626 			if ( datetime ) {
  5634 			if ( datetime ) {
  5731 
  5739 
  5732 		/**
  5740 		/**
  5733 		 * Populate setting value from the inputs.
  5741 		 * Populate setting value from the inputs.
  5734 		 *
  5742 		 *
  5735 		 * @since 4.9.0
  5743 		 * @since 4.9.0
  5736 		 * @returns {boolean} If setting updated.
  5744 		 * @return {boolean} If setting updated.
  5737 		 */
  5745 		 */
  5738 		populateSetting: function populateSetting() {
  5746 		populateSetting: function populateSetting() {
  5739 			var control = this, date;
  5747 			var control = this, date;
  5740 
  5748 
  5741 			if ( control.validateInputs() || ! control.params.allowPastDate && ! control.isFutureDate() ) {
  5749 			if ( control.validateInputs() || ! control.params.allowPastDate && ! control.isFutureDate() ) {
  5792 
  5800 
  5793 		/**
  5801 		/**
  5794 		 * Check if the date is in the future.
  5802 		 * Check if the date is in the future.
  5795 		 *
  5803 		 *
  5796 		 * @since 4.9.0
  5804 		 * @since 4.9.0
  5797 		 * @returns {boolean} True if future date.
  5805 		 * @return {boolean} True if future date.
  5798 		 */
  5806 		 */
  5799 		isFutureDate: function isFutureDate() {
  5807 		isFutureDate: function isFutureDate() {
  5800 			var control = this;
  5808 			var control = this;
  5801 			return 0 < api.utils.getRemainingTime( control.convertInputDateToString() );
  5809 			return 0 < api.utils.getRemainingTime( control.convertInputDateToString() );
  5802 		},
  5810 		},
  5805 		 * Convert hour in twelve hour format to twenty four hour format.
  5813 		 * Convert hour in twelve hour format to twenty four hour format.
  5806 		 *
  5814 		 *
  5807 		 * @since 4.9.0
  5815 		 * @since 4.9.0
  5808 		 * @param {string} hourInTwelveHourFormat - Hour in twelve hour format.
  5816 		 * @param {string} hourInTwelveHourFormat - Hour in twelve hour format.
  5809 		 * @param {string} meridian - Either 'am' or 'pm'.
  5817 		 * @param {string} meridian - Either 'am' or 'pm'.
  5810 		 * @returns {string} Hour in twenty four hour format.
  5818 		 * @return {string} Hour in twenty four hour format.
  5811 		 */
  5819 		 */
  5812 		convertHourToTwentyFourHourFormat: function convertHour( hourInTwelveHourFormat, meridian ) {
  5820 		convertHourToTwentyFourHourFormat: function convertHour( hourInTwelveHourFormat, meridian ) {
  5813 			var hourInTwentyFourHourFormat, hour, midDayHour = 12;
  5821 			var hourInTwentyFourHourFormat, hour, midDayHour = 12;
  5814 
  5822 
  5815 			hour = parseInt( hourInTwelveHourFormat, 10 );
  5823 			hour = parseInt( hourInTwelveHourFormat, 10 );
  5830 
  5838 
  5831 		/**
  5839 		/**
  5832 		 * Populates date inputs in date fields.
  5840 		 * Populates date inputs in date fields.
  5833 		 *
  5841 		 *
  5834 		 * @since 4.9.0
  5842 		 * @since 4.9.0
  5835 		 * @returns {boolean} Whether the inputs were populated.
  5843 		 * @return {boolean} Whether the inputs were populated.
  5836 		 */
  5844 		 */
  5837 		populateDateInputs: function populateDateInputs() {
  5845 		populateDateInputs: function populateDateInputs() {
  5838 			var control = this, parsed;
  5846 			var control = this, parsed;
  5839 
  5847 
  5840 			parsed = control.parseDateTime( control.setting.get() );
  5848 			parsed = control.parseDateTime( control.setting.get() );
  5912 
  5920 
  5913 		/**
  5921 		/**
  5914 		 * Initialize behaviors.
  5922 		 * Initialize behaviors.
  5915 		 *
  5923 		 *
  5916 		 * @since 4.9.0
  5924 		 * @since 4.9.0
  5917 		 * @returns {void}
  5925 		 * @return {void}
  5918 		 */
  5926 		 */
  5919 		ready: function ready() {
  5927 		ready: function ready() {
  5920 			var control = this, element, component, node, url, input, button;
  5928 			var control = this, element, component, node, url, input, button;
  5921 
  5929 
  5922 			_.bindAll( control, 'updatePreviewLink' );
  5930 			_.bindAll( control, 'updatePreviewLink' );
  6040 	 * @since 3.4.0
  6048 	 * @since 3.4.0
  6041 	 *
  6049 	 *
  6042 	 * @type {Function}
  6050 	 * @type {Function}
  6043 	 * @param {...string} ids - One or more ids for controls to obtain.
  6051 	 * @param {...string} ids - One or more ids for controls to obtain.
  6044 	 * @param {deferredControlsCallback} [callback] - Function called when all supplied controls exist.
  6052 	 * @param {deferredControlsCallback} [callback] - Function called when all supplied controls exist.
  6045 	 * @returns {wp.customize.Control|undefined|jQuery.promise} Control instance or undefined (if function called with one id param), or promise resolving to requested controls.
  6053 	 * @return {wp.customize.Control|undefined|jQuery.promise} Control instance or undefined (if function called with one id param),
       
  6054 	 *                                                         or promise resolving to requested controls.
  6046 	 *
  6055 	 *
  6047 	 * @example <caption>Loop over all registered controls.</caption>
  6056 	 * @example <caption>Loop over all registered controls.</caption>
  6048 	 * wp.customize.control.each( function( control ) { ... } );
  6057 	 * wp.customize.control.each( function( control ) { ... } );
  6049 	 *
  6058 	 *
  6050 	 * @example <caption>Getting `background_color` control instance.</caption>
  6059 	 * @example <caption>Getting `background_color` control instance.</caption>
  6101 	 * @since 3.4.0
  6110 	 * @since 3.4.0
  6102 	 *
  6111 	 *
  6103 	 * @type {Function}
  6112 	 * @type {Function}
  6104 	 * @param {...string} ids - One or more ids for sections to obtain.
  6113 	 * @param {...string} ids - One or more ids for sections to obtain.
  6105 	 * @param {deferredSectionsCallback} [callback] - Function called when all supplied sections exist.
  6114 	 * @param {deferredSectionsCallback} [callback] - Function called when all supplied sections exist.
  6106 	 * @returns {wp.customize.Section|undefined|jQuery.promise} Section instance or undefined (if function called with one id param), or promise resolving to requested sections.
  6115 	 * @return {wp.customize.Section|undefined|jQuery.promise} Section instance or undefined (if function called with one id param),
       
  6116 	 *                                                         or promise resolving to requested sections.
  6107 	 *
  6117 	 *
  6108 	 * @example <caption>Loop over all registered sections.</caption>
  6118 	 * @example <caption>Loop over all registered sections.</caption>
  6109 	 * wp.customize.section.each( function( section ) { ... } )
  6119 	 * wp.customize.section.each( function( section ) { ... } )
  6110 	 *
  6120 	 *
  6111 	 * @example <caption>Getting `title_tagline` section instance.</caption>
  6121 	 * @example <caption>Getting `title_tagline` section instance.</caption>
  6135 	 * @since 4.0.0
  6145 	 * @since 4.0.0
  6136 	 *
  6146 	 *
  6137 	 * @type {Function}
  6147 	 * @type {Function}
  6138 	 * @param {...string} ids - One or more ids for panels to obtain.
  6148 	 * @param {...string} ids - One or more ids for panels to obtain.
  6139 	 * @param {deferredPanelsCallback} [callback] - Function called when all supplied panels exist.
  6149 	 * @param {deferredPanelsCallback} [callback] - Function called when all supplied panels exist.
  6140 	 * @returns {wp.customize.Panel|undefined|jQuery.promise} Panel instance or undefined (if function called with one id param), or promise resolving to requested panels.
  6150 	 * @return {wp.customize.Panel|undefined|jQuery.promise} Panel instance or undefined (if function called with one id param),
       
  6151 	 *                                                       or promise resolving to requested panels.
  6141 	 *
  6152 	 *
  6142 	 * @example <caption>Loop over all registered panels.</caption>
  6153 	 * @example <caption>Loop over all registered panels.</caption>
  6143 	 * wp.customize.panel.each( function( panel ) { ... } )
  6154 	 * wp.customize.panel.each( function( panel ) { ... } )
  6144 	 *
  6155 	 *
  6145 	 * @example <caption>Getting nav_menus panel instance.</caption>
  6156 	 * @example <caption>Getting nav_menus panel instance.</caption>
  6169 	 * @since 4.9.0
  6180 	 * @since 4.9.0
  6170 	 *
  6181 	 *
  6171 	 * @type {Function}
  6182 	 * @type {Function}
  6172 	 * @param {...string} codes - One or more codes for notifications to obtain.
  6183 	 * @param {...string} codes - One or more codes for notifications to obtain.
  6173 	 * @param {deferredNotificationsCallback} [callback] - Function called when all supplied notifications exist.
  6184 	 * @param {deferredNotificationsCallback} [callback] - Function called when all supplied notifications exist.
  6174 	 * @returns {wp.customize.Notification|undefined|jQuery.promise} notification instance or undefined (if function called with one code param), or promise resolving to requested notifications.
  6185 	 * @return {wp.customize.Notification|undefined|jQuery.promise} Notification instance or undefined (if function called with one code param),
       
  6186 	 *                                                              or promise resolving to requested notifications.
  6175 	 *
  6187 	 *
  6176 	 * @example <caption>Check if existing notification</caption>
  6188 	 * @example <caption>Check if existing notification</caption>
  6177 	 * exists = wp.customize.notifications.has( 'a_new_day_arrived' );
  6189 	 * exists = wp.customize.notifications.has( 'a_new_day_arrived' );
  6178 	 *
  6190 	 *
  6179 	 * @example <caption>Obtain existing notification</caption>
  6191 	 * @example <caption>Obtain existing notification</caption>
  6204 		 * allows for seamless replacement of an existing preview.
  6216 		 * allows for seamless replacement of an existing preview.
  6205 		 *
  6217 		 *
  6206 		 * @constructs wp.customize.PreviewFrame
  6218 		 * @constructs wp.customize.PreviewFrame
  6207 		 * @augments   wp.customize.Messenger
  6219 		 * @augments   wp.customize.Messenger
  6208 		 *
  6220 		 *
  6209 		 * @param {object} params.container
  6221 		 * @param {Object} params.container
  6210 		 * @param {object} params.previewUrl
  6222 		 * @param {Object} params.previewUrl
  6211 		 * @param {object} params.query
  6223 		 * @param {Object} params.query
  6212 		 * @param {object} options
  6224 		 * @param {Object} options
  6213 		 */
  6225 		 */
  6214 		initialize: function( params, options ) {
  6226 		initialize: function( params, options ) {
  6215 			var deferred = $.Deferred();
  6227 			var deferred = $.Deferred();
  6216 
  6228 
  6217 			/*
  6229 			/*
  6234 		},
  6246 		},
  6235 
  6247 
  6236 		/**
  6248 		/**
  6237 		 * Run the preview request.
  6249 		 * Run the preview request.
  6238 		 *
  6250 		 *
  6239 		 * @param {object} deferred jQuery Deferred object to be resolved with
  6251 		 * @param {Object} deferred jQuery Deferred object to be resolved with
  6240 		 *                          the request.
  6252 		 *                          the request.
  6241 		 */
  6253 		 */
  6242 		run: function( deferred ) {
  6254 		run: function( deferred ) {
  6243 			var previewFrame = this,
  6255 			var previewFrame = this,
  6244 				loaded = false,
  6256 				loaded = false,
  6442 
  6454 
  6443 		/**
  6455 		/**
  6444 		 * @constructs wp.customize.Previewer
  6456 		 * @constructs wp.customize.Previewer
  6445 		 * @augments   wp.customize.Messenger
  6457 		 * @augments   wp.customize.Messenger
  6446 		 *
  6458 		 *
  6447 		 * @param {array}  params.allowedUrls
  6459 		 * @param {Array}  params.allowedUrls
  6448 		 * @param {string} params.container   A selector or jQuery element for the preview
  6460 		 * @param {string} params.container   A selector or jQuery element for the preview
  6449 		 *                                    frame to be placed.
  6461 		 *                                    frame to be placed.
  6450 		 * @param {string} params.form
  6462 		 * @param {string} params.form
  6451 		 * @param {string} params.previewUrl  The URL to preview.
  6463 		 * @param {string} params.previewUrl  The URL to preview.
  6452 		 * @param {object} options
  6464 		 * @param {Object} options
  6453 		 */
  6465 		 */
  6454 		initialize: function( params, options ) {
  6466 		initialize: function( params, options ) {
  6455 			var previewer = this,
  6467 			var previewer = this,
  6456 				urlParser = document.createElement( 'a' );
  6468 				urlParser = document.createElement( 'a' );
  6457 
  6469 
  6492 			api.Messenger.prototype.initialize.call( previewer, params );
  6504 			api.Messenger.prototype.initialize.call( previewer, params );
  6493 
  6505 
  6494 			urlParser.href = previewer.origin();
  6506 			urlParser.href = previewer.origin();
  6495 			previewer.add( 'scheme', urlParser.protocol.replace( /:$/, '' ) );
  6507 			previewer.add( 'scheme', urlParser.protocol.replace( /:$/, '' ) );
  6496 
  6508 
  6497 			// Limit the URL to internal, front-end links.
  6509 			/*
  6498 			//
  6510 			 * Limit the URL to internal, front-end links.
  6499 			// If the front end and the admin are served from the same domain, load the
  6511 			 *
  6500 			// preview over ssl if the Customizer is being loaded over ssl. This avoids
  6512 			 * If the front end and the admin are served from the same domain, load the
  6501 			// insecure content warnings. This is not attempted if the admin and front end
  6513 			 * preview over ssl if the Customizer is being loaded over ssl. This avoids
  6502 			// are on different domains to avoid the case where the front end doesn't have
  6514 			 * insecure content warnings. This is not attempted if the admin and front end
  6503 			// ssl certs.
  6515 			 * are on different domains to avoid the case where the front end doesn't have
       
  6516 			 * ssl certs.
       
  6517 			 */
  6504 
  6518 
  6505 			previewer.add( 'previewUrl', params.previewUrl ).setter( function( to ) {
  6519 			previewer.add( 'previewUrl', params.previewUrl ).setter( function( to ) {
  6506 				var result = null, urlParser, queryParams, parsedAllowedUrl, parsedCandidateUrls = [];
  6520 				var result = null, urlParser, queryParams, parsedAllowedUrl, parsedCandidateUrls = [];
  6507 				urlParser = document.createElement( 'a' );
  6521 				urlParser = document.createElement( 'a' );
  6508 				urlParser.href = to;
  6522 				urlParser.href = to;
  6593 		 * Handle the preview receiving the ready message.
  6607 		 * Handle the preview receiving the ready message.
  6594 		 *
  6608 		 *
  6595 		 * @since 4.7.0
  6609 		 * @since 4.7.0
  6596 		 * @access public
  6610 		 * @access public
  6597 		 *
  6611 		 *
  6598 		 * @param {object} data - Data from preview.
  6612 		 * @param {Object} data - Data from preview.
  6599 		 * @param {string} data.currentUrl - Current URL.
  6613 		 * @param {string} data.currentUrl - Current URL.
  6600 		 * @param {object} data.activePanels - Active panels.
  6614 		 * @param {Object} data.activePanels - Active panels.
  6601 		 * @param {object} data.activeSections Active sections.
  6615 		 * @param {Object} data.activeSections Active sections.
  6602 		 * @param {object} data.activeControls Active controls.
  6616 		 * @param {Object} data.activeControls Active controls.
  6603 		 * @returns {void}
  6617 		 * @return {void}
  6604 		 */
  6618 		 */
  6605 		ready: function( data ) {
  6619 		ready: function( data ) {
  6606 			var previewer = this, synced = {}, constructs;
  6620 			var previewer = this, synced = {}, constructs;
  6607 
  6621 
  6608 			synced.settings = api.get();
  6622 			synced.settings = api.get();
  6668 		 * If a message is not received in the allotted time then the iframe will be set back to the last known valid URL.
  6682 		 * If a message is not received in the allotted time then the iframe will be set back to the last known valid URL.
  6669 		 *
  6683 		 *
  6670 		 * @since 4.7.0
  6684 		 * @since 4.7.0
  6671 		 * @access public
  6685 		 * @access public
  6672 		 *
  6686 		 *
  6673 		 * @returns {void}
  6687 		 * @return {void}
  6674 		 */
  6688 		 */
  6675 		keepPreviewAlive: function keepPreviewAlive() {
  6689 		keepPreviewAlive: function keepPreviewAlive() {
  6676 			var previewer = this, keepAliveTick, timeoutId, handleMissingKeepAlive, scheduleKeepAliveCheck;
  6690 			var previewer = this, keepAliveTick, timeoutId, handleMissingKeepAlive, scheduleKeepAliveCheck;
  6677 
  6691 
  6678 			/**
  6692 			/**
  6729 		/**
  6743 		/**
  6730 		 * Refresh the preview seamlessly.
  6744 		 * Refresh the preview seamlessly.
  6731 		 *
  6745 		 *
  6732 		 * @since 3.4.0
  6746 		 * @since 3.4.0
  6733 		 * @access public
  6747 		 * @access public
  6734 		 * @returns {void}
  6748 		 *
       
  6749 		 * @return {void}
  6735 		 */
  6750 		 */
  6736 		refresh: function() {
  6751 		refresh: function() {
  6737 			var previewer = this, onSettingChange;
  6752 			var previewer = this, onSettingChange;
  6738 
  6753 
  6739 			// Display loading indicator
  6754 			// Display loading indicator.
  6740 			previewer.send( 'loading-initiated' );
  6755 			previewer.send( 'loading-initiated' );
  6741 
  6756 
  6742 			previewer.abort();
  6757 			previewer.abort();
  6743 
  6758 
  6744 			previewer.loading = new api.PreviewFrame({
  6759 			previewer.loading = new api.PreviewFrame({
  6900 	 * @alias wp.customize._handleSettingValidities
  6915 	 * @alias wp.customize._handleSettingValidities
  6901 	 *
  6916 	 *
  6902 	 * @since 4.6.0
  6917 	 * @since 4.6.0
  6903 	 * @private
  6918 	 * @private
  6904 	 *
  6919 	 *
  6905 	 * @param {object}  args
  6920 	 * @param {Object}  args
  6906 	 * @param {object}  args.settingValidities
  6921 	 * @param {Object}  args.settingValidities
  6907 	 * @param {boolean} [args.focusInvalidControl=false]
  6922 	 * @param {boolean} [args.focusInvalidControl=false]
  6908 	 * @returns {void}
  6923 	 * @return {void}
  6909 	 */
  6924 	 */
  6910 	api._handleSettingValidities = function handleSettingValidities( args ) {
  6925 	api._handleSettingValidities = function handleSettingValidities( args ) {
  6911 		var invalidSettingControls, invalidSettings = [], wasFocused = false;
  6926 		var invalidSettingControls, invalidSettings = [], wasFocused = false;
  6912 
  6927 
  6913 		// Find the controls that correspond to each invalid setting.
  6928 		// Find the controls that correspond to each invalid setting.
  6976 	 *
  6991 	 *
  6977 	 * @alias wp.customize.findControlsForSettings
  6992 	 * @alias wp.customize.findControlsForSettings
  6978 	 *
  6993 	 *
  6979 	 * @since 4.6.0
  6994 	 * @since 4.6.0
  6980 	 * @param {string[]} settingIds Setting IDs.
  6995 	 * @param {string[]} settingIds Setting IDs.
  6981 	 * @returns {object<string, wp.customize.Control>} Mapping setting ids to arrays of controls.
  6996 	 * @return {Object<string, wp.customize.Control>} Mapping setting ids to arrays of controls.
  6982 	 */
  6997 	 */
  6983 	api.findControlsForSettings = function findControlsForSettings( settingIds ) {
  6998 	api.findControlsForSettings = function findControlsForSettings( settingIds ) {
  6984 		var controls = {}, settingControls;
  6999 		var controls = {}, settingControls;
  6985 		_.each( _.unique( settingIds ), function( settingId ) {
  7000 		_.each( _.unique( settingIds ), function( settingId ) {
  6986 			var setting = api( settingId );
  7001 			var setting = api( settingId );
  7007 
  7022 
  7008 		if ( document.activeElement ) {
  7023 		if ( document.activeElement ) {
  7009 			activeElement = $( document.activeElement );
  7024 			activeElement = $( document.activeElement );
  7010 		}
  7025 		}
  7011 
  7026 
  7012 		// Sort the sections within each panel
  7027 		// Sort the sections within each panel.
  7013 		api.panel.each( function ( panel ) {
  7028 		api.panel.each( function ( panel ) {
  7014 			if ( 'themes' === panel.id ) {
  7029 			if ( 'themes' === panel.id ) {
  7015 				return; // Don't reflow theme sections, as doing so moves them after the themes container.
  7030 				return; // Don't reflow theme sections, as doing so moves them after the themes container.
  7016 			}
  7031 			}
  7017 
  7032 
  7025 				} );
  7040 				} );
  7026 				wasReflowed = true;
  7041 				wasReflowed = true;
  7027 			}
  7042 			}
  7028 		} );
  7043 		} );
  7029 
  7044 
  7030 		// Sort the controls within each section
  7045 		// Sort the controls within each section.
  7031 		api.section.each( function ( section ) {
  7046 		api.section.each( function ( section ) {
  7032 			var controls = section.controls(),
  7047 			var controls = section.controls(),
  7033 				controlContainers = _.pluck( controls, 'container' );
  7048 				controlContainers = _.pluck( controls, 'container' );
  7034 			if ( ! section.panel() ) {
  7049 			if ( ! section.panel() ) {
  7035 				rootNodes.push( section );
  7050 				rootNodes.push( section );
  7041 				} );
  7056 				} );
  7042 				wasReflowed = true;
  7057 				wasReflowed = true;
  7043 			}
  7058 			}
  7044 		} );
  7059 		} );
  7045 
  7060 
  7046 		// Sort the root panels and sections
  7061 		// Sort the root panels and sections.
  7047 		rootNodes.sort( api.utils.prioritySort );
  7062 		rootNodes.sort( api.utils.prioritySort );
  7048 		rootHeadContainers = _.pluck( rootNodes, 'headContainer' );
  7063 		rootHeadContainers = _.pluck( rootNodes, 'headContainer' );
  7049 		appendContainer = $( '#customize-theme-controls .customize-pane-parent' ); // @todo This should be defined elsewhere, and to be configurable
  7064 		appendContainer = $( '#customize-theme-controls .customize-pane-parent' ); // @todo This should be defined elsewhere, and to be configurable.
  7050 		if ( ! api.utils.areElementListsEqual( rootHeadContainers, appendContainer.children() ) ) {
  7065 		if ( ! api.utils.areElementListsEqual( rootHeadContainers, appendContainer.children() ) ) {
  7051 			_( rootNodes ).each( function ( rootNode ) {
  7066 			_( rootNodes ).each( function ( rootNode ) {
  7052 				appendContainer.append( rootNode.headContainer );
  7067 				appendContainer.append( rootNode.headContainer );
  7053 			} );
  7068 			} );
  7054 			wasReflowed = true;
  7069 			wasReflowed = true;
  7055 		}
  7070 		}
  7056 
  7071 
  7057 		// Now re-trigger the active Value callbacks to that the panels and sections can decide whether they can be rendered
  7072 		// Now re-trigger the active Value callbacks so that the panels and sections can decide whether they can be rendered.
  7058 		api.panel.each( function ( panel ) {
  7073 		api.panel.each( function ( panel ) {
  7059 			var value = panel.active();
  7074 			var value = panel.active();
  7060 			panel.active.callbacks.fireWith( panel.active, [ value, value ] );
  7075 			panel.active.callbacks.fireWith( panel.active, [ value, value ] );
  7061 		} );
  7076 		} );
  7062 		api.section.each( function ( section ) {
  7077 		api.section.each( function ( section ) {
  7063 			var value = section.active();
  7078 			var value = section.active();
  7064 			section.active.callbacks.fireWith( section.active, [ value, value ] );
  7079 			section.active.callbacks.fireWith( section.active, [ value, value ] );
  7065 		} );
  7080 		} );
  7066 
  7081 
  7067 		// Restore focus if there was a reflow and there was an active (focused) element
  7082 		// Restore focus if there was a reflow and there was an active (focused) element.
  7068 		if ( wasReflowed && activeElement ) {
  7083 		if ( wasReflowed && activeElement ) {
  7069 			activeElement.focus();
  7084 			activeElement.focus();
  7070 		}
  7085 		}
  7071 		api.trigger( 'pane-contents-reflowed' );
  7086 		api.trigger( 'pane-contents-reflowed' );
  7072 	}, api );
  7087 	}, api );
  7202 			function highlightScheduleButton() {
  7217 			function highlightScheduleButton() {
  7203 				if ( ! cancelScheduleButtonReminder ) {
  7218 				if ( ! cancelScheduleButtonReminder ) {
  7204 					cancelScheduleButtonReminder = api.utils.highlightButton( btnWrapper, {
  7219 					cancelScheduleButtonReminder = api.utils.highlightButton( btnWrapper, {
  7205 						delay: 1000,
  7220 						delay: 1000,
  7206 
  7221 
  7207 						// Only abort the reminder when the save button is focused.
  7222 						/*
  7208 						// If the user clicks the settings button to toggle the
  7223 						 * Only abort the reminder when the save button is focused.
  7209 						// settings closed, we'll still remind them.
  7224 						 * If the user clicks the settings button to toggle the
       
  7225 						 * settings closed, we'll still remind them.
       
  7226 						 */
  7210 						focusTarget: saveBtn
  7227 						focusTarget: saveBtn
  7211 					} );
  7228 					} );
  7212 				}
  7229 				}
  7213 			}
  7230 			}
  7214 			function cancelHighlightScheduleButton() {
  7231 			function cancelHighlightScheduleButton() {
  7382 			 *
  7399 			 *
  7383 			 * @since 3.4.0
  7400 			 * @since 3.4.0
  7384 			 * @since 4.7.0 Added options param.
  7401 			 * @since 4.7.0 Added options param.
  7385 			 * @access public
  7402 			 * @access public
  7386 			 *
  7403 			 *
  7387 			 * @param {object}  [options] Options.
  7404 			 * @param {Object}  [options] Options.
  7388 			 * @param {boolean} [options.excludeCustomizedSaved=false] Exclude saved settings in customized response (values pending writing to changeset).
  7405 			 * @param {boolean} [options.excludeCustomizedSaved=false] Exclude saved settings in customized response (values pending writing to changeset).
  7389 			 * @return {object} Query vars.
  7406 			 * @return {Object} Query vars.
  7390 			 */
  7407 			 */
  7391 			query: function( options ) {
  7408 			query: function( options ) {
  7392 				var queryVars = {
  7409 				var queryVars = {
  7393 					wp_customize: 'on',
  7410 					wp_customize: 'on',
  7394 					customize_theme: api.settings.theme.stylesheet,
  7411 					customize_theme: api.settings.theme.stylesheet,
  7420 			 * has been added to the post type.
  7437 			 * has been added to the post type.
  7421 			 *
  7438 			 *
  7422 			 * @since 3.4.0
  7439 			 * @since 3.4.0
  7423 			 * @since 4.7.0 Added args param and return value.
  7440 			 * @since 4.7.0 Added args param and return value.
  7424 			 *
  7441 			 *
  7425 			 * @param {object} [args] Args.
  7442 			 * @param {Object} [args] Args.
  7426 			 * @param {string} [args.status=publish] Status.
  7443 			 * @param {string} [args.status=publish] Status.
  7427 			 * @param {string} [args.date] Date, in local time in MySQL format.
  7444 			 * @param {string} [args.date] Date, in local time in MySQL format.
  7428 			 * @param {string} [args.title] Title
  7445 			 * @param {string} [args.title] Title
  7429 			 * @returns {jQuery.promise} Promise.
  7446 			 * @return {jQuery.promise} Promise.
  7430 			 */
  7447 			 */
  7431 			save: function( args ) {
  7448 			save: function( args ) {
  7432 				var previewer = this,
  7449 				var previewer = this,
  7433 					deferred = $.Deferred(),
  7450 					deferred = $.Deferred(),
  7434 					changesetStatus = api.state( 'selectedChangesetStatus' ).get(),
  7451 					changesetStatus = api.state( 'selectedChangesetStatus' ).get(),
  7573 						};
  7590 						};
  7574 
  7591 
  7575 						if ( '0' === response ) {
  7592 						if ( '0' === response ) {
  7576 							response = 'not_logged_in';
  7593 							response = 'not_logged_in';
  7577 						} else if ( '-1' === response ) {
  7594 						} else if ( '-1' === response ) {
  7578 							// Back-compat in case any other check_ajax_referer() call is dying
  7595 							// Back-compat in case any other check_ajax_referer() call is dying.
  7579 							response = 'invalid_nonce';
  7596 							response = 'invalid_nonce';
  7580 						}
  7597 						}
  7581 
  7598 
  7582 						if ( 'invalid_nonce' === response ) {
  7599 						if ( 'invalid_nonce' === response ) {
  7583 							previewer.cheatin();
  7600 							previewer.cheatin();
  7697 			 *
  7714 			 *
  7698 			 * Revert the Customizer to it's previously-published state.
  7715 			 * Revert the Customizer to it's previously-published state.
  7699 			 *
  7716 			 *
  7700 			 * @since 4.9.0
  7717 			 * @since 4.9.0
  7701 			 *
  7718 			 *
  7702 			 * @returns {jQuery.promise} Promise.
  7719 			 * @return {jQuery.promise} Promise.
  7703 			 */
  7720 			 */
  7704 			trash: function trash() {
  7721 			trash: function trash() {
  7705 				var request, success, fail;
  7722 				var request, success, fail;
  7706 
  7723 
  7707 				api.state( 'trashing' ).set( true );
  7724 				api.state( 'trashing' ).set( true );
  7806 			$.extend( api.settings.nonce, nonce );
  7823 			$.extend( api.settings.nonce, nonce );
  7807 			$.extend( api.previewer.nonce, nonce );
  7824 			$.extend( api.previewer.nonce, nonce );
  7808 			api.previewer.send( 'nonce-refresh', nonce );
  7825 			api.previewer.send( 'nonce-refresh', nonce );
  7809 		});
  7826 		});
  7810 
  7827 
  7811 		// Create Settings
  7828 		// Create Settings.
  7812 		$.each( api.settings.settings, function( id, data ) {
  7829 		$.each( api.settings.settings, function( id, data ) {
  7813 			var Constructor = api.settingConstructor[ data.type ] || api.Setting;
  7830 			var Constructor = api.settingConstructor[ data.type ] || api.Setting;
  7814 			api.add( new Constructor( id, data.value, {
  7831 			api.add( new Constructor( id, data.value, {
  7815 				transport: data.transport,
  7832 				transport: data.transport,
  7816 				previewer: api.previewer,
  7833 				previewer: api.previewer,
  7817 				dirty: !! data.dirty
  7834 				dirty: !! data.dirty
  7818 			} ) );
  7835 			} ) );
  7819 		});
  7836 		});
  7820 
  7837 
  7821 		// Create Panels
  7838 		// Create Panels.
  7822 		$.each( api.settings.panels, function ( id, data ) {
  7839 		$.each( api.settings.panels, function ( id, data ) {
  7823 			var Constructor = api.panelConstructor[ data.type ] || api.Panel, options;
  7840 			var Constructor = api.panelConstructor[ data.type ] || api.Panel, options;
  7824 			options = _.extend( { params: data }, data ); // Inclusion of params alias is for back-compat for custom panels that expect to augment this property.
  7841 			// Inclusion of params alias is for back-compat for custom panels that expect to augment this property.
       
  7842 			options = _.extend( { params: data }, data );
  7825 			api.panel.add( new Constructor( id, options ) );
  7843 			api.panel.add( new Constructor( id, options ) );
  7826 		});
  7844 		});
  7827 
  7845 
  7828 		// Create Sections
  7846 		// Create Sections.
  7829 		$.each( api.settings.sections, function ( id, data ) {
  7847 		$.each( api.settings.sections, function ( id, data ) {
  7830 			var Constructor = api.sectionConstructor[ data.type ] || api.Section, options;
  7848 			var Constructor = api.sectionConstructor[ data.type ] || api.Section, options;
  7831 			options = _.extend( { params: data }, data ); // Inclusion of params alias is for back-compat for custom sections that expect to augment this property.
  7849 			// Inclusion of params alias is for back-compat for custom sections that expect to augment this property.
       
  7850 			options = _.extend( { params: data }, data );
  7832 			api.section.add( new Constructor( id, options ) );
  7851 			api.section.add( new Constructor( id, options ) );
  7833 		});
  7852 		});
  7834 
  7853 
  7835 		// Create Controls
  7854 		// Create Controls.
  7836 		$.each( api.settings.controls, function( id, data ) {
  7855 		$.each( api.settings.controls, function( id, data ) {
  7837 			var Constructor = api.controlConstructor[ data.type ] || api.Control, options;
  7856 			var Constructor = api.controlConstructor[ data.type ] || api.Control, options;
  7838 			options = _.extend( { params: data }, data ); // Inclusion of params alias is for back-compat for custom controls that expect to augment this property.
  7857 			// Inclusion of params alias is for back-compat for custom controls that expect to augment this property.
       
  7858 			options = _.extend( { params: data }, data );
  7839 			api.control.add( new Constructor( id, options ) );
  7859 			api.control.add( new Constructor( id, options ) );
  7840 		});
  7860 		});
  7841 
  7861 
  7842 		// Focus the autofocused element
  7862 		// Focus the autofocused element.
  7843 		_.each( [ 'panel', 'section', 'control' ], function( type ) {
  7863 		_.each( [ 'panel', 'section', 'control' ], function( type ) {
  7844 			var id = api.settings.autofocus[ type ];
  7864 			var id = api.settings.autofocus[ type ];
  7845 			if ( ! id ) {
  7865 			if ( ! id ) {
  7846 				return;
  7866 				return;
  7847 			}
  7867 			}
  7890 			});
  7910 			});
  7891 
  7911 
  7892 			api.notifications.render();
  7912 			api.notifications.render();
  7893 		});
  7913 		});
  7894 
  7914 
  7895 		// Save and activated states
  7915 		// Save and activated states.
  7896 		(function( state ) {
  7916 		(function( state ) {
  7897 			var saved = state.instance( 'saved' ),
  7917 			var saved = state.instance( 'saved' ),
  7898 				saving = state.instance( 'saving' ),
  7918 				saving = state.instance( 'saving' ),
  7899 				trashing = state.instance( 'trashing' ),
  7919 				trashing = state.instance( 'trashing' ),
  7900 				activated = state.instance( 'activated' ),
  7920 				activated = state.instance( 'activated' ),
  8031 			 *
  8051 			 *
  8032 			 * @since 4.7.0
  8052 			 * @since 4.7.0
  8033 			 * @access private
  8053 			 * @access private
  8034 			 *
  8054 			 *
  8035 			 * @param {boolean} isIncluded Is UUID included.
  8055 			 * @param {boolean} isIncluded Is UUID included.
  8036 			 * @returns {void}
  8056 			 * @return {void}
  8037 			 */
  8057 			 */
  8038 			populateChangesetUuidParam = function( isIncluded ) {
  8058 			populateChangesetUuidParam = function( isIncluded ) {
  8039 				var urlParser, queryParams;
  8059 				var urlParser, queryParams;
  8040 
  8060 
  8041 				// Abort on IE9 which doesn't support history management.
  8061 				// Abort on IE9 which doesn't support history management.
  8099 				 * @augments   wp.customize.OverlayNotification
  8119 				 * @augments   wp.customize.OverlayNotification
  8100 				 *
  8120 				 *
  8101 				 * @since 4.9.0
  8121 				 * @since 4.9.0
  8102 				 *
  8122 				 *
  8103 				 * @param {string} [code] - Code.
  8123 				 * @param {string} [code] - Code.
  8104 				 * @param {object} [params] - Params.
  8124 				 * @param {Object} [params] - Params.
  8105 				 */
  8125 				 */
  8106 				initialize: function( code, params ) {
  8126 				initialize: function( code, params ) {
  8107 					var notification = this, _code, _params;
  8127 					var notification = this, _code, _params;
  8108 					_code = code || 'changeset_locked';
  8128 					_code = code || 'changeset_locked';
  8109 					_params = _.extend(
  8129 					_params = _.extend(
  8110 						{
  8130 						{
       
  8131 							message: '',
  8111 							type: 'warning',
  8132 							type: 'warning',
  8112 							containerClasses: '',
  8133 							containerClasses: '',
  8113 							lockUser: {}
  8134 							lockUser: {}
  8114 						},
  8135 						},
  8115 						params
  8136 						params
  8187 			/**
  8208 			/**
  8188 			 * Start lock.
  8209 			 * Start lock.
  8189 			 *
  8210 			 *
  8190 			 * @since 4.9.0
  8211 			 * @since 4.9.0
  8191 			 *
  8212 			 *
  8192 			 * @param {object} [args] - Args.
  8213 			 * @param {Object} [args] - Args.
  8193 			 * @param {object} [args.lockUser] - Lock user data.
  8214 			 * @param {Object} [args.lockUser] - Lock user data.
  8194 			 * @param {boolean} [args.allowOverride=false] - Whether override is allowed.
  8215 			 * @param {boolean} [args.allowOverride=false] - Whether override is allowed.
  8195 			 * @returns {void}
  8216 			 * @return {void}
  8196 			 */
  8217 			 */
  8197 			function startLock( args ) {
  8218 			function startLock( args ) {
  8198 				if ( args && args.lockUser ) {
  8219 				if ( args && args.lockUser ) {
  8199 					api.settings.changeset.lockUser = args.lockUser;
  8220 					api.settings.changeset.lockUser = args.lockUser;
  8200 				}
  8221 				}
  8249 			var removedQueryParams = [], autosaveDismissed = false;
  8270 			var removedQueryParams = [], autosaveDismissed = false;
  8250 
  8271 
  8251 			/**
  8272 			/**
  8252 			 * Obtain the URL to restore the autosave.
  8273 			 * Obtain the URL to restore the autosave.
  8253 			 *
  8274 			 *
  8254 			 * @returns {string} Customizer URL.
  8275 			 * @return {string} Customizer URL.
  8255 			 */
  8276 			 */
  8256 			function getAutosaveRestorationUrl() {
  8277 			function getAutosaveRestorationUrl() {
  8257 				var urlParser, queryParams;
  8278 				var urlParser, queryParams;
  8258 				urlParser = document.createElement( 'a' );
  8279 				urlParser = document.createElement( 'a' );
  8259 				urlParser.href = location.href;
  8280 				urlParser.href = location.href;
  8270 
  8291 
  8271 			/**
  8292 			/**
  8272 			 * Remove parameter from the URL.
  8293 			 * Remove parameter from the URL.
  8273 			 *
  8294 			 *
  8274 			 * @param {Array} params - Parameter names to remove.
  8295 			 * @param {Array} params - Parameter names to remove.
  8275 			 * @returns {void}
  8296 			 * @return {void}
  8276 			 */
  8297 			 */
  8277 			function stripParamsFromLocation( params ) {
  8298 			function stripParamsFromLocation( params ) {
  8278 				var urlParser = document.createElement( 'a' ), queryParams, strippedParams = 0;
  8299 				var urlParser = document.createElement( 'a' ), queryParams, strippedParams = 0;
  8279 				urlParser.href = location.href;
  8300 				urlParser.href = location.href;
  8280 				queryParams = api.utils.parseQueryString( urlParser.search.substr( 1 ) );
  8301 				queryParams = api.utils.parseQueryString( urlParser.search.substr( 1 ) );
  8293 			}
  8314 			}
  8294 
  8315 
  8295 			/**
  8316 			/**
  8296 			 * Dismiss autosave.
  8317 			 * Dismiss autosave.
  8297 			 *
  8318 			 *
  8298 			 * @returns {void}
  8319 			 * @return {void}
  8299 			 */
  8320 			 */
  8300 			function dismissAutosave() {
  8321 			function dismissAutosave() {
  8301 				if ( autosaveDismissed ) {
  8322 				if ( autosaveDismissed ) {
  8302 					return;
  8323 					return;
  8303 				}
  8324 				}
  8312 			}
  8333 			}
  8313 
  8334 
  8314 			/**
  8335 			/**
  8315 			 * Add notification regarding the availability of an autosave to restore.
  8336 			 * Add notification regarding the availability of an autosave to restore.
  8316 			 *
  8337 			 *
  8317 			 * @returns {void}
  8338 			 * @return {void}
  8318 			 */
  8339 			 */
  8319 			function addAutosaveRestoreNotification() {
  8340 			function addAutosaveRestoreNotification() {
  8320 				var code = 'autosave_available', onStateChange;
  8341 				var code = 'autosave_available', onStateChange;
  8321 
  8342 
  8322 				// Since there is an autosave revision and the user hasn't loaded with autosaved, add notification to prompt to load autosaved version.
  8343 				// Since there is an autosave revision and the user hasn't loaded with autosaved, add notification to prompt to load autosaved version.
  8490 			 *
  8511 			 *
  8491 			 * @since 4.7.0
  8512 			 * @since 4.7.0
  8492 			 * @access private
  8513 			 * @access private
  8493 			 *
  8514 			 *
  8494 			 * @param {wp.customize.Panel|wp.customize.Section} container Construct.
  8515 			 * @param {wp.customize.Panel|wp.customize.Section} container Construct.
  8495 			 * @returns {void}
  8516 			 * @return {void}
  8496 			 */
  8517 			 */
  8497 			changeContainer = function( container ) {
  8518 			changeContainer = function( container ) {
  8498 				var newInstance = container,
  8519 				var newInstance = container,
  8499 					expandedSection = api.state( 'expandedSection' ).get(),
  8520 					expandedSection = api.state( 'expandedSection' ).get(),
  8500 					expandedPanel = api.state( 'expandedPanel' ).get(),
  8521 					expandedPanel = api.state( 'expandedPanel' ).get(),
  8602 			 * Update active header height.
  8623 			 * Update active header height.
  8603 			 *
  8624 			 *
  8604 			 * @since 4.7.0
  8625 			 * @since 4.7.0
  8605 			 * @access private
  8626 			 * @access private
  8606 			 *
  8627 			 *
  8607 			 * @returns {void}
  8628 			 * @return {void}
  8608 			 */
  8629 			 */
  8609 			updateHeaderHeight = function() {
  8630 			updateHeaderHeight = function() {
  8610 				activeHeader.height = activeHeader.element.outerHeight();
  8631 				activeHeader.height = activeHeader.element.outerHeight();
  8611 			};
  8632 			};
  8612 
  8633 
  8614 			 * Reposition header on throttled `scroll` event.
  8635 			 * Reposition header on throttled `scroll` event.
  8615 			 *
  8636 			 *
  8616 			 * @since 4.7.0
  8637 			 * @since 4.7.0
  8617 			 * @access private
  8638 			 * @access private
  8618 			 *
  8639 			 *
  8619 			 * @param {object} header - Header.
  8640 			 * @param {Object} header - Header.
  8620 			 * @param {number} scrollTop - Scroll top.
  8641 			 * @param {number} scrollTop - Scroll top.
  8621 			 * @param {number} scrollDirection - Scroll direction, negative number being up and positive being down.
  8642 			 * @param {number} scrollDirection - Scroll direction, negative number being up and positive being down.
  8622 			 * @returns {void}
  8643 			 * @return {void}
  8623 			 */
  8644 			 */
  8624 			positionStickyHeader = function( header, scrollTop, scrollDirection ) {
  8645 			positionStickyHeader = function( header, scrollTop, scrollDirection ) {
  8625 				var headerElement = header.element,
  8646 				var headerElement = header.element,
  8626 					headerParent = header.parent,
  8647 					headerParent = header.parent,
  8627 					headerHeight = header.height,
  8648 					headerHeight = header.height,
  8684 					headerParent.css( 'padding-top', headerHeight + 'px' );
  8705 					headerParent.css( 'padding-top', headerHeight + 'px' );
  8685 				}
  8706 				}
  8686 			};
  8707 			};
  8687 		}());
  8708 		}());
  8688 
  8709 
  8689 		// Previewed device bindings. (The api.previewedDevice property is how this Value was first introduced, but since it has moved to api.state.)
  8710 		// Previewed device bindings. (The api.previewedDevice property
       
  8711 		// is how this Value was first introduced, but since it has moved to api.state.)
  8690 		api.previewedDevice = api.state( 'previewedDevice' );
  8712 		api.previewedDevice = api.state( 'previewedDevice' );
  8691 
  8713 
  8692 		// Set the default device.
  8714 		// Set the default device.
  8693 		api.bind( 'ready', function() {
  8715 		api.bind( 'ready', function() {
  8694 			_.find( api.settings.previewableDevices, function( value, key ) {
  8716 			_.find( api.settings.previewableDevices, function( value, key ) {
  8791 			function startPromptingBeforeUnload() {
  8813 			function startPromptingBeforeUnload() {
  8792 				api.unbind( 'change', startPromptingBeforeUnload );
  8814 				api.unbind( 'change', startPromptingBeforeUnload );
  8793 				api.state( 'selectedChangesetStatus' ).unbind( startPromptingBeforeUnload );
  8815 				api.state( 'selectedChangesetStatus' ).unbind( startPromptingBeforeUnload );
  8794 				api.state( 'selectedChangesetDate' ).unbind( startPromptingBeforeUnload );
  8816 				api.state( 'selectedChangesetDate' ).unbind( startPromptingBeforeUnload );
  8795 
  8817 
  8796 				// Prompt user with AYS dialog if leaving the Customizer with unsaved changes
  8818 				// Prompt user with AYS dialog if leaving the Customizer with unsaved changes.
  8797 				$( window ).on( 'beforeunload.customize-confirm', function() {
  8819 				$( window ).on( 'beforeunload.customize-confirm', function() {
  8798 					if ( ! isCleanState() && ! api.state( 'changesetLocked' ).get() ) {
  8820 					if ( ! isCleanState() && ! api.state( 'changesetLocked' ).get() ) {
  8799 						setTimeout( function() {
  8821 						setTimeout( function() {
  8800 							overlay.removeClass( 'customize-loading' );
  8822 							overlay.removeClass( 'customize-loading' );
  8801 						}, 1 );
  8823 						}, 1 );
  8876 			api.bind( event, function() {
  8898 			api.bind( event, function() {
  8877 				parent.send( event );
  8899 				parent.send( event );
  8878 			});
  8900 			});
  8879 		} );
  8901 		} );
  8880 
  8902 
  8881 		// Pass titles to the parent
  8903 		// Pass titles to the parent.
  8882 		api.bind( 'title', function( newTitle ) {
  8904 		api.bind( 'title', function( newTitle ) {
  8883 			parent.send( 'title', newTitle );
  8905 			parent.send( 'title', newTitle );
  8884 		});
  8906 		});
  8885 
  8907 
  8886 		if ( api.settings.changeset.branching ) {
  8908 		if ( api.settings.changeset.branching ) {
  8888 		}
  8910 		}
  8889 
  8911 
  8890 		// Initialize the connection with the parent frame.
  8912 		// Initialize the connection with the parent frame.
  8891 		parent.send( 'ready' );
  8913 		parent.send( 'ready' );
  8892 
  8914 
  8893 		// Control visibility for default controls
  8915 		// Control visibility for default controls.
  8894 		$.each({
  8916 		$.each({
  8895 			'background_image': {
  8917 			'background_image': {
  8896 				controls: [ 'background_preset', 'background_position', 'background_size', 'background_repeat', 'background_attachment' ],
  8918 				controls: [ 'background_preset', 'background_position', 'background_size', 'background_repeat', 'background_attachment' ],
  8897 				callback: function( to ) { return !! to; }
  8919 				callback: function( to ) { return !! to; }
  8898 			},
  8920 			},
  8920 		});
  8942 		});
  8921 
  8943 
  8922 		api.control( 'background_preset', function( control ) {
  8944 		api.control( 'background_preset', function( control ) {
  8923 			var visibility, defaultValues, values, toggleVisibility, updateSettings, preset;
  8945 			var visibility, defaultValues, values, toggleVisibility, updateSettings, preset;
  8924 
  8946 
  8925 			visibility = { // position, size, repeat, attachment
  8947 			visibility = { // position, size, repeat, attachment.
  8926 				'default': [ false, false, false, false ],
  8948 				'default': [ false, false, false, false ],
  8927 				'fill': [ true, false, false, false ],
  8949 				'fill': [ true, false, false, false ],
  8928 				'fit': [ true, false, true, false ],
  8950 				'fit': [ true, false, true, false ],
  8929 				'repeat': [ true, false, false, true ],
  8951 				'repeat': [ true, false, false, true ],
  8930 				'custom': [ true, true, true, true ]
  8952 				'custom': [ true, true, true, true ]
  8936 				_wpCustomizeBackground.defaults['default-size'],
  8958 				_wpCustomizeBackground.defaults['default-size'],
  8937 				_wpCustomizeBackground.defaults['default-repeat'],
  8959 				_wpCustomizeBackground.defaults['default-repeat'],
  8938 				_wpCustomizeBackground.defaults['default-attachment']
  8960 				_wpCustomizeBackground.defaults['default-attachment']
  8939 			];
  8961 			];
  8940 
  8962 
  8941 			values = { // position_x, position_y, size, repeat, attachment
  8963 			values = { // position_x, position_y, size, repeat, attachment.
  8942 				'default': defaultValues,
  8964 				'default': defaultValues,
  8943 				'fill': [ 'left', 'top', 'cover', 'no-repeat', 'fixed' ],
  8965 				'fill': [ 'left', 'top', 'cover', 'no-repeat', 'fixed' ],
  8944 				'fit': [ 'left', 'top', 'contain', 'no-repeat', 'fixed' ],
  8966 				'fit': [ 'left', 'top', 'contain', 'no-repeat', 'fixed' ],
  8945 				'repeat': [ 'left', 'top', 'auto', 'repeat', 'scroll' ]
  8967 				'repeat': [ 'left', 'top', 'auto', 'repeat', 'scroll' ]
  8946 			};
  8968 			};
  8947 
  8969 
  8948 			// @todo These should actually toggle the active state, but without the preview overriding the state in data.activeControls.
  8970 			// @todo These should actually toggle the active state,
       
  8971 			// but without the preview overriding the state in data.activeControls.
  8949 			toggleVisibility = function( preset ) {
  8972 			toggleVisibility = function( preset ) {
  8950 				_.each( [ 'background_position', 'background_size', 'background_repeat', 'background_attachment' ], function( controlId, i ) {
  8973 				_.each( [ 'background_position', 'background_size', 'background_repeat', 'background_attachment' ], function( controlId, i ) {
  8951 					var control = api.control( controlId );
  8974 					var control = api.control( controlId );
  8952 					if ( control ) {
  8975 					if ( control ) {
  8953 						control.container.toggle( visibility[ preset ][ i ] );
  8976 						control.container.toggle( visibility[ preset ][ i ] );
  9003 			control.setting.bind( function( to ) {
  9026 			control.setting.bind( function( to ) {
  9004 				control.element.set( 'fixed' !== to );
  9027 				control.element.set( 'fixed' !== to );
  9005 			} );
  9028 			} );
  9006 		} );
  9029 		} );
  9007 
  9030 
  9008 		// Juggle the two controls that use header_textcolor
  9031 		// Juggle the two controls that use header_textcolor.
  9009 		api.control( 'display_header_text', function( control ) {
  9032 		api.control( 'display_header_text', function( control ) {
  9010 			var last = '';
  9033 			var last = '';
  9011 
  9034 
  9012 			control.elements[0].unsync( api( 'header_textcolor' ) );
  9035 			control.elements[0].unsync( api( 'header_textcolor' ) );
  9013 
  9036