2018-06-27 22:30:15 -04:00
|
|
|
/**
|
|
|
|
* @output wp-includes/js/customize-selective-refresh.js
|
|
|
|
*/
|
|
|
|
|
2016-02-19 13:41:28 -05:00
|
|
|
/* global jQuery, JSON, _customizePartialRefreshExports, console */
|
|
|
|
|
2017-09-08 14:42:49 -04:00
|
|
|
/** @namespace wp.customize.selectiveRefresh */
|
2016-02-19 13:41:28 -05:00
|
|
|
wp.customize.selectiveRefresh = ( function( $, api ) {
|
|
|
|
'use strict';
|
|
|
|
var self, Partial, Placement;
|
|
|
|
|
|
|
|
self = {
|
|
|
|
ready: $.Deferred(),
|
2016-10-26 16:03:32 -04:00
|
|
|
editShortcutVisibility: new api.Value(),
|
2016-02-19 13:41:28 -05:00
|
|
|
data: {
|
|
|
|
partials: {},
|
|
|
|
renderQueryVar: '',
|
|
|
|
l10n: {
|
|
|
|
shiftClickToEdit: ''
|
Customize: Implement customized state persistence with changesets.
Includes infrastructure developed in the Customize Snapshots feature plugin.
See https://make.wordpress.org/core/2016/10/12/customize-changesets-technical-design-decisions/
Props westonruter, valendesigns, utkarshpatel, stubgo, lgedeon, ocean90, ryankienstra, mihai2u, dlh, aaroncampbell, jonathanbardo, jorbin.
See #28721.
See #31089.
Fixes #30937.
Fixes #31517.
Fixes #30028.
Fixes #23225.
Fixes #34142.
Fixes #36485.
Built from https://develop.svn.wordpress.org/trunk@38810
git-svn-id: http://core.svn.wordpress.org/trunk@38753 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2016-10-18 16:05:31 -04:00
|
|
|
}
|
2016-02-19 13:41:28 -05:00
|
|
|
},
|
|
|
|
currentRequest: null
|
|
|
|
};
|
|
|
|
|
|
|
|
_.extend( self, api.Events );
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A Customizer Partial.
|
|
|
|
*
|
|
|
|
* A partial provides a rendering of one or more settings according to a template.
|
|
|
|
*
|
2017-09-08 14:42:49 -04:00
|
|
|
* @memberOf wp.customize.selectiveRefresh
|
|
|
|
*
|
2016-02-19 13:41:28 -05:00
|
|
|
* @see PHP class WP_Customize_Partial.
|
|
|
|
*
|
|
|
|
* @class
|
|
|
|
* @augments wp.customize.Class
|
|
|
|
* @since 4.5.0
|
|
|
|
*/
|
2017-09-08 14:42:49 -04:00
|
|
|
Partial = self.Partial = api.Class.extend(/** @lends wp.customize.SelectiveRefresh.Partial.prototype */{
|
2016-02-19 13:41:28 -05:00
|
|
|
|
|
|
|
id: null,
|
|
|
|
|
2017-10-28 20:15:53 -04:00
|
|
|
/**
|
|
|
|
* Default params.
|
|
|
|
*
|
|
|
|
* @since 4.9.0
|
|
|
|
* @var {object}
|
|
|
|
*/
|
|
|
|
defaults: {
|
|
|
|
selector: null,
|
|
|
|
primarySetting: null,
|
|
|
|
containerInclusive: false,
|
|
|
|
fallbackRefresh: true // Note this needs to be false in a front-end editing context.
|
|
|
|
},
|
|
|
|
|
2016-10-26 16:03:32 -04:00
|
|
|
/**
|
2016-02-19 13:41:28 -05:00
|
|
|
* Constructor.
|
|
|
|
*
|
|
|
|
* @since 4.5.0
|
|
|
|
*
|
2020-07-27 20:07:03 -04:00
|
|
|
* @param {string} id - Unique identifier for the partial instance.
|
|
|
|
* @param {Object} options - Options hash for the partial instance.
|
|
|
|
* @param {string} options.type - Type of partial (e.g. nav_menu, widget, etc)
|
|
|
|
* @param {string} options.selector - jQuery selector to find the container element in the page.
|
|
|
|
* @param {Array} options.settings - The IDs for the settings the partial relates to.
|
|
|
|
* @param {string} options.primarySetting - The ID for the primary setting the partial renders.
|
|
|
|
* @param {boolean} options.fallbackRefresh - Whether to refresh the entire preview in case of a partial refresh failure.
|
|
|
|
* @param {Object} [options.params] - Deprecated wrapper for the above properties.
|
2016-02-19 13:41:28 -05:00
|
|
|
*/
|
|
|
|
initialize: function( id, options ) {
|
|
|
|
var partial = this;
|
|
|
|
options = options || {};
|
|
|
|
partial.id = id;
|
|
|
|
|
|
|
|
partial.params = _.extend(
|
|
|
|
{
|
2017-10-28 20:15:53 -04:00
|
|
|
settings: []
|
2016-02-19 13:41:28 -05:00
|
|
|
},
|
2017-10-28 20:15:53 -04:00
|
|
|
partial.defaults,
|
|
|
|
options.params || options
|
2016-02-19 13:41:28 -05:00
|
|
|
);
|
|
|
|
|
|
|
|
partial.deferred = {};
|
|
|
|
partial.deferred.ready = $.Deferred();
|
|
|
|
|
|
|
|
partial.deferred.ready.done( function() {
|
|
|
|
partial.ready();
|
|
|
|
} );
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set up the partial.
|
|
|
|
*
|
|
|
|
* @since 4.5.0
|
|
|
|
*/
|
|
|
|
ready: function() {
|
|
|
|
var partial = this;
|
2016-10-26 16:03:32 -04:00
|
|
|
_.each( partial.placements(), function( placement ) {
|
|
|
|
$( placement.container ).attr( 'title', self.data.l10n.shiftClickToEdit );
|
|
|
|
partial.createEditShortcutForPlacement( placement );
|
2016-02-19 13:41:28 -05:00
|
|
|
} );
|
|
|
|
$( document ).on( 'click', partial.params.selector, function( e ) {
|
|
|
|
if ( ! e.shiftKey ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
e.preventDefault();
|
|
|
|
_.each( partial.placements(), function( placement ) {
|
|
|
|
if ( $( placement.container ).is( e.currentTarget ) ) {
|
|
|
|
partial.showControl();
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
},
|
|
|
|
|
2016-10-26 16:03:32 -04:00
|
|
|
/**
|
|
|
|
* Create and show the edit shortcut for a given partial placement container.
|
|
|
|
*
|
2016-11-17 13:28:30 -05:00
|
|
|
* @since 4.7.0
|
2016-11-23 01:05:32 -05:00
|
|
|
* @access public
|
2016-10-26 16:03:32 -04:00
|
|
|
*
|
|
|
|
* @param {Placement} placement The placement container element.
|
2019-11-29 13:01:03 -05:00
|
|
|
* @return {void}
|
2016-10-26 16:03:32 -04:00
|
|
|
*/
|
|
|
|
createEditShortcutForPlacement: function( placement ) {
|
2016-11-16 15:37:30 -05:00
|
|
|
var partial = this, $shortcut, $placementContainer, illegalAncestorSelector, illegalContainerSelector;
|
2016-10-26 16:03:32 -04:00
|
|
|
if ( ! placement.container ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
$placementContainer = $( placement.container );
|
2016-11-16 15:37:30 -05:00
|
|
|
illegalAncestorSelector = 'head';
|
|
|
|
illegalContainerSelector = 'area, audio, base, bdi, bdo, br, button, canvas, col, colgroup, command, datalist, embed, head, hr, html, iframe, img, input, keygen, label, link, map, math, menu, meta, noscript, object, optgroup, option, param, progress, rp, rt, ruby, script, select, source, style, svg, table, tbody, textarea, tfoot, thead, title, tr, track, video, wbr';
|
|
|
|
if ( ! $placementContainer.length || $placementContainer.is( illegalContainerSelector ) || $placementContainer.closest( illegalAncestorSelector ).length ) {
|
2016-10-26 16:03:32 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
$shortcut = partial.createEditShortcut();
|
|
|
|
$shortcut.on( 'click', function( event ) {
|
|
|
|
event.preventDefault();
|
|
|
|
event.stopPropagation();
|
|
|
|
partial.showControl();
|
|
|
|
} );
|
2016-12-12 02:52:43 -05:00
|
|
|
partial.addEditShortcutToPlacement( placement, $shortcut );
|
2016-10-26 16:03:32 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2016-11-11 04:47:43 -05:00
|
|
|
* Add an edit shortcut to the placement container.
|
2016-10-26 16:03:32 -04:00
|
|
|
*
|
2016-11-17 13:28:30 -05:00
|
|
|
* @since 4.7.0
|
2016-11-23 01:05:32 -05:00
|
|
|
* @access public
|
2016-10-26 16:03:32 -04:00
|
|
|
*
|
|
|
|
* @param {Placement} placement The placement for the partial.
|
|
|
|
* @param {jQuery} $editShortcut The shortcut element as a jQuery object.
|
2019-11-29 13:01:03 -05:00
|
|
|
* @return {void}
|
2016-10-26 16:03:32 -04:00
|
|
|
*/
|
2016-11-11 04:47:43 -05:00
|
|
|
addEditShortcutToPlacement: function( placement, $editShortcut ) {
|
|
|
|
var $placementContainer = $( placement.container );
|
2016-10-26 16:03:32 -04:00
|
|
|
$placementContainer.prepend( $editShortcut );
|
|
|
|
if ( ! $placementContainer.is( ':visible' ) || 'none' === $placementContainer.css( 'display' ) ) {
|
|
|
|
$editShortcut.addClass( 'customize-partial-edit-shortcut-hidden' );
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the unique class name for the edit shortcut button for this partial.
|
|
|
|
*
|
2016-11-17 13:28:30 -05:00
|
|
|
* @since 4.7.0
|
2016-11-23 01:05:32 -05:00
|
|
|
* @access public
|
2016-10-26 16:03:32 -04:00
|
|
|
*
|
|
|
|
* @return {string} Partial ID converted into a class name for use in shortcut.
|
|
|
|
*/
|
|
|
|
getEditShortcutClassName: function() {
|
|
|
|
var partial = this, cleanId;
|
|
|
|
cleanId = partial.id.replace( /]/g, '' ).replace( /\[/g, '-' );
|
|
|
|
return 'customize-partial-edit-shortcut-' + cleanId;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the appropriate translated string for the edit shortcut button.
|
|
|
|
*
|
2016-11-17 13:28:30 -05:00
|
|
|
* @since 4.7.0
|
2016-11-23 01:05:32 -05:00
|
|
|
* @access public
|
2016-10-26 16:03:32 -04:00
|
|
|
*
|
|
|
|
* @return {string} Tooltip for edit shortcut.
|
|
|
|
*/
|
|
|
|
getEditShortcutTitle: function() {
|
|
|
|
var partial = this, l10n = self.data.l10n;
|
|
|
|
switch ( partial.getType() ) {
|
|
|
|
case 'widget':
|
|
|
|
return l10n.clickEditWidget;
|
|
|
|
case 'blogname':
|
|
|
|
return l10n.clickEditTitle;
|
|
|
|
case 'blogdescription':
|
|
|
|
return l10n.clickEditTitle;
|
|
|
|
case 'nav_menu':
|
|
|
|
return l10n.clickEditMenu;
|
|
|
|
default:
|
|
|
|
return l10n.clickEditMisc;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the type of this partial
|
|
|
|
*
|
|
|
|
* Will use `params.type` if set, but otherwise will try to infer type from settingId.
|
|
|
|
*
|
2016-11-17 13:28:30 -05:00
|
|
|
* @since 4.7.0
|
2016-11-23 01:05:32 -05:00
|
|
|
* @access public
|
2016-10-26 16:03:32 -04:00
|
|
|
*
|
|
|
|
* @return {string} Type of partial derived from type param or the related setting ID.
|
|
|
|
*/
|
|
|
|
getType: function() {
|
|
|
|
var partial = this, settingId;
|
|
|
|
settingId = partial.params.primarySetting || _.first( partial.settings() ) || 'unknown';
|
|
|
|
if ( partial.params.type ) {
|
|
|
|
return partial.params.type;
|
|
|
|
}
|
|
|
|
if ( settingId.match( /^nav_menu_instance\[/ ) ) {
|
|
|
|
return 'nav_menu';
|
|
|
|
}
|
|
|
|
if ( settingId.match( /^widget_.+\[\d+]$/ ) ) {
|
|
|
|
return 'widget';
|
|
|
|
}
|
|
|
|
return settingId;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create an edit shortcut button for this partial.
|
|
|
|
*
|
2016-11-17 13:28:30 -05:00
|
|
|
* @since 4.7.0
|
2016-11-23 01:05:32 -05:00
|
|
|
* @access public
|
2016-10-26 16:03:32 -04:00
|
|
|
*
|
|
|
|
* @return {jQuery} The edit shortcut button element.
|
|
|
|
*/
|
|
|
|
createEditShortcut: function() {
|
2016-11-04 13:16:26 -04:00
|
|
|
var partial = this, shortcutTitle, $buttonContainer, $button, $image;
|
2016-10-26 16:03:32 -04:00
|
|
|
shortcutTitle = partial.getEditShortcutTitle();
|
2016-11-04 13:16:26 -04:00
|
|
|
$buttonContainer = $( '<span>', {
|
|
|
|
'class': 'customize-partial-edit-shortcut ' + partial.getEditShortcutClassName()
|
|
|
|
} );
|
|
|
|
$button = $( '<button>', {
|
2016-10-26 16:03:32 -04:00
|
|
|
'aria-label': shortcutTitle,
|
|
|
|
'title': shortcutTitle,
|
2016-11-04 13:16:26 -04:00
|
|
|
'class': 'customize-partial-edit-shortcut-button'
|
2016-10-26 16:03:32 -04:00
|
|
|
} );
|
2016-11-04 13:16:26 -04:00
|
|
|
$image = $( '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M13.89 3.39l2.71 2.72c.46.46.42 1.24.03 1.64l-8.01 8.02-5.56 1.16 1.16-5.58s7.6-7.63 7.99-8.03c.39-.39 1.22-.39 1.68.07zm-2.73 2.79l-5.59 5.61 1.11 1.11 5.54-5.65zm-2.97 8.23l5.58-5.6-1.07-1.08-5.59 5.6z"/></svg>' );
|
|
|
|
$button.append( $image );
|
|
|
|
$buttonContainer.append( $button );
|
|
|
|
return $buttonContainer;
|
2016-10-26 16:03:32 -04:00
|
|
|
},
|
|
|
|
|
2016-02-19 13:41:28 -05:00
|
|
|
/**
|
2018-02-26 12:52:31 -05:00
|
|
|
* Find all placements for this partial in the document.
|
2016-02-19 13:41:28 -05:00
|
|
|
*
|
|
|
|
* @since 4.5.0
|
|
|
|
*
|
|
|
|
* @return {Array.<Placement>}
|
|
|
|
*/
|
|
|
|
placements: function() {
|
|
|
|
var partial = this, selector;
|
|
|
|
|
2016-03-21 17:59:29 -04:00
|
|
|
selector = partial.params.selector || '';
|
2016-02-19 13:41:28 -05:00
|
|
|
if ( selector ) {
|
|
|
|
selector += ', ';
|
|
|
|
}
|
|
|
|
selector += '[data-customize-partial-id="' + partial.id + '"]'; // @todo Consider injecting customize-partial-id-${id} classnames instead.
|
|
|
|
|
|
|
|
return $( selector ).map( function() {
|
|
|
|
var container = $( this ), context;
|
|
|
|
|
|
|
|
context = container.data( 'customize-partial-placement-context' );
|
|
|
|
if ( _.isString( context ) && '{' === context.substr( 0, 1 ) ) {
|
|
|
|
throw new Error( 'context JSON parse error' );
|
|
|
|
}
|
|
|
|
|
|
|
|
return new Placement( {
|
|
|
|
partial: partial,
|
|
|
|
container: container,
|
|
|
|
context: context
|
|
|
|
} );
|
|
|
|
} ).get();
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get list of setting IDs related to this partial.
|
|
|
|
*
|
|
|
|
* @since 4.5.0
|
|
|
|
*
|
2020-07-27 19:35:02 -04:00
|
|
|
* @return {string[]}
|
2016-02-19 13:41:28 -05:00
|
|
|
*/
|
|
|
|
settings: function() {
|
|
|
|
var partial = this;
|
|
|
|
if ( partial.params.settings && 0 !== partial.params.settings.length ) {
|
|
|
|
return partial.params.settings;
|
|
|
|
} else if ( partial.params.primarySetting ) {
|
|
|
|
return [ partial.params.primarySetting ];
|
|
|
|
} else {
|
|
|
|
return [ partial.id ];
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return whether the setting is related to the partial.
|
|
|
|
*
|
|
|
|
* @since 4.5.0
|
|
|
|
*
|
|
|
|
* @param {wp.customize.Value|string} setting ID or object for setting.
|
|
|
|
* @return {boolean} Whether the setting is related to the partial.
|
|
|
|
*/
|
|
|
|
isRelatedSetting: function( setting /*... newValue, oldValue */ ) {
|
|
|
|
var partial = this;
|
|
|
|
if ( _.isString( setting ) ) {
|
|
|
|
setting = api( setting );
|
|
|
|
}
|
|
|
|
if ( ! setting ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return -1 !== _.indexOf( partial.settings(), setting.id );
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Show the control to modify this partial's setting(s).
|
|
|
|
*
|
|
|
|
* This may be overridden for inline editing.
|
|
|
|
*
|
|
|
|
* @since 4.5.0
|
|
|
|
*/
|
|
|
|
showControl: function() {
|
2016-12-20 17:05:43 -05:00
|
|
|
var partial = this, settingId = partial.params.primarySetting;
|
2016-02-19 13:41:28 -05:00
|
|
|
if ( ! settingId ) {
|
|
|
|
settingId = _.first( partial.settings() );
|
|
|
|
}
|
2016-10-26 16:03:32 -04:00
|
|
|
if ( partial.getType() === 'nav_menu' ) {
|
2016-12-20 17:05:43 -05:00
|
|
|
if ( partial.params.navMenuArgs.theme_location ) {
|
|
|
|
settingId = 'nav_menu_locations[' + partial.params.navMenuArgs.theme_location + ']';
|
|
|
|
} else if ( partial.params.navMenuArgs.menu ) {
|
|
|
|
settingId = 'nav_menu[' + String( partial.params.navMenuArgs.menu ) + ']';
|
2016-10-26 16:03:32 -04:00
|
|
|
}
|
|
|
|
}
|
2016-02-19 13:41:28 -05:00
|
|
|
api.preview.send( 'focus-control-for-setting', settingId );
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prepare container for selective refresh.
|
|
|
|
*
|
|
|
|
* @since 4.5.0
|
|
|
|
*
|
|
|
|
* @param {Placement} placement
|
|
|
|
*/
|
|
|
|
preparePlacement: function( placement ) {
|
|
|
|
$( placement.container ).addClass( 'customize-partial-refreshing' );
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reference to the pending promise returned from self.requestPartial().
|
|
|
|
*
|
|
|
|
* @since 4.5.0
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
_pendingRefreshPromise: null,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Request the new partial and render it into the placements.
|
|
|
|
*
|
|
|
|
* @since 4.5.0
|
|
|
|
*
|
|
|
|
* @this {wp.customize.selectiveRefresh.Partial}
|
|
|
|
* @return {jQuery.Promise}
|
|
|
|
*/
|
|
|
|
refresh: function() {
|
|
|
|
var partial = this, refreshPromise;
|
|
|
|
|
|
|
|
refreshPromise = self.requestPartial( partial );
|
|
|
|
|
|
|
|
if ( ! partial._pendingRefreshPromise ) {
|
|
|
|
_.each( partial.placements(), function( placement ) {
|
|
|
|
partial.preparePlacement( placement );
|
|
|
|
} );
|
|
|
|
|
|
|
|
refreshPromise.done( function( placements ) {
|
|
|
|
_.each( placements, function( placement ) {
|
|
|
|
partial.renderContent( placement );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
refreshPromise.fail( function( data, placements ) {
|
|
|
|
partial.fallback( data, placements );
|
|
|
|
} );
|
|
|
|
|
|
|
|
// Allow new request when this one finishes.
|
|
|
|
partial._pendingRefreshPromise = refreshPromise;
|
|
|
|
refreshPromise.always( function() {
|
|
|
|
partial._pendingRefreshPromise = null;
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
|
|
|
return refreshPromise;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Apply the addedContent in the placement to the document.
|
|
|
|
*
|
|
|
|
* Note the placement object will have its container and removedNodes
|
|
|
|
* properties updated.
|
|
|
|
*
|
|
|
|
* @since 4.5.0
|
|
|
|
*
|
|
|
|
* @param {Placement} placement
|
|
|
|
* @param {Element|jQuery} [placement.container] - This param will be empty if there was no element matching the selector.
|
2020-07-27 19:35:02 -04:00
|
|
|
* @param {string|Object|boolean} placement.addedContent - Rendered HTML content, a data object for JS templates to render, or false if no render.
|
|
|
|
* @param {Object} [placement.context] - Optional context information about the container.
|
2019-11-29 13:01:03 -05:00
|
|
|
* @return {boolean} Whether the rendering was successful and the fallback was not invoked.
|
2016-02-19 13:41:28 -05:00
|
|
|
*/
|
|
|
|
renderContent: function( placement ) {
|
2016-03-09 00:05:27 -05:00
|
|
|
var partial = this, content, newContainerElement;
|
2016-02-19 13:41:28 -05:00
|
|
|
if ( ! placement.container ) {
|
|
|
|
partial.fallback( new Error( 'no_container' ), [ placement ] );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
placement.container = $( placement.container );
|
|
|
|
if ( false === placement.addedContent ) {
|
|
|
|
partial.fallback( new Error( 'missing_render' ), [ placement ] );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Currently a subclass needs to override renderContent to handle partials returning data object.
|
|
|
|
if ( ! _.isString( placement.addedContent ) ) {
|
|
|
|
partial.fallback( new Error( 'non_string_content' ), [ placement ] );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* jshint ignore:start */
|
2024-04-12 13:47:13 -04:00
|
|
|
self.originalDocumentWrite = document.write;
|
2016-02-19 13:41:28 -05:00
|
|
|
document.write = function() {
|
|
|
|
throw new Error( self.data.l10n.badDocumentWrite );
|
|
|
|
};
|
|
|
|
/* jshint ignore:end */
|
|
|
|
try {
|
|
|
|
content = placement.addedContent;
|
|
|
|
if ( wp.emoji && wp.emoji.parse && ! $.contains( document.head, placement.container[0] ) ) {
|
|
|
|
content = wp.emoji.parse( content );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( partial.params.containerInclusive ) {
|
|
|
|
|
2020-01-28 19:45:18 -05:00
|
|
|
// Note that content may be an empty string, and in this case jQuery will just remove the oldContainer.
|
2016-02-19 13:41:28 -05:00
|
|
|
newContainerElement = $( content );
|
|
|
|
|
|
|
|
// Merge the new context on top of the old context.
|
|
|
|
placement.context = _.extend(
|
|
|
|
placement.context,
|
|
|
|
newContainerElement.data( 'customize-partial-placement-context' ) || {}
|
|
|
|
);
|
|
|
|
newContainerElement.data( 'customize-partial-placement-context', placement.context );
|
|
|
|
|
|
|
|
placement.removedNodes = placement.container;
|
|
|
|
placement.container = newContainerElement;
|
|
|
|
placement.removedNodes.replaceWith( placement.container );
|
|
|
|
placement.container.attr( 'title', self.data.l10n.shiftClickToEdit );
|
|
|
|
} else {
|
|
|
|
placement.removedNodes = document.createDocumentFragment();
|
|
|
|
while ( placement.container[0].firstChild ) {
|
|
|
|
placement.removedNodes.appendChild( placement.container[0].firstChild );
|
|
|
|
}
|
|
|
|
|
|
|
|
placement.container.html( content );
|
|
|
|
}
|
|
|
|
|
|
|
|
placement.container.removeClass( 'customize-render-content-error' );
|
|
|
|
} catch ( error ) {
|
|
|
|
if ( 'undefined' !== typeof console && console.error ) {
|
|
|
|
console.error( partial.id, error );
|
|
|
|
}
|
2017-05-16 20:01:40 -04:00
|
|
|
partial.fallback( error, [ placement ] );
|
2016-02-19 13:41:28 -05:00
|
|
|
}
|
|
|
|
/* jshint ignore:start */
|
2024-04-12 13:47:13 -04:00
|
|
|
document.write = self.originalDocumentWrite;
|
|
|
|
self.originalDocumentWrite = null;
|
2016-02-19 13:41:28 -05:00
|
|
|
/* jshint ignore:end */
|
|
|
|
|
2016-10-26 16:03:32 -04:00
|
|
|
partial.createEditShortcutForPlacement( placement );
|
2016-02-19 13:41:28 -05:00
|
|
|
placement.container.removeClass( 'customize-partial-refreshing' );
|
|
|
|
|
2017-09-09 10:49:44 -04:00
|
|
|
// Prevent placement container from being re-triggered as being rendered among nested partials.
|
2016-02-19 13:41:28 -05:00
|
|
|
placement.container.data( 'customize-partial-content-rendered', true );
|
|
|
|
|
Widgets: Introduce media widgets for images, audio, and video with extensible base for additional media widgets in the future.
The last time a new widget was introduced, Vuvuzelas were a thing, Angry Birds started taking over phones, and WordPress stopped shipping with Kubrick. Seven years and 17 releases without new widgets have been enough, time to spice up your sidebar!
Props westonruter, melchoyce, obenland, timmydcrawford, adamsilverstein, gonom9, wonderboymusic, Fab1en, DrewAPicture, sirbrillig, joen, matias, samikeijonen, afercia, celloexpressions, designsimply, michelleweber, ranh, kjellr, karmatosed.
Fixes #32417, #39993, #39994, #39995.
Built from https://develop.svn.wordpress.org/trunk@40640
git-svn-id: http://core.svn.wordpress.org/trunk@40501 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2017-05-11 17:11:44 -04:00
|
|
|
/*
|
|
|
|
* Note that the 'wp_audio_shortcode_library' and 'wp_video_shortcode_library' filters
|
|
|
|
* will determine whether or not wp.mediaelement is loaded and whether it will
|
|
|
|
* initialize audio and video respectively. See also https://core.trac.wordpress.org/ticket/40144
|
|
|
|
*/
|
|
|
|
if ( wp.mediaelement ) {
|
|
|
|
wp.mediaelement.initialize();
|
|
|
|
}
|
|
|
|
|
2018-01-29 18:56:29 -05:00
|
|
|
if ( wp.playlist ) {
|
|
|
|
wp.playlist.initialize();
|
|
|
|
}
|
|
|
|
|
2016-02-19 13:41:28 -05:00
|
|
|
/**
|
|
|
|
* Announce when a partial's placement has been rendered so that dynamic elements can be re-built.
|
|
|
|
*/
|
|
|
|
self.trigger( 'partial-content-rendered', placement );
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle fail to render partial.
|
|
|
|
*
|
|
|
|
* The first argument is either the failing jqXHR or an Error object, and the second argument is the array of containers.
|
|
|
|
*
|
|
|
|
* @since 4.5.0
|
|
|
|
*/
|
|
|
|
fallback: function() {
|
|
|
|
var partial = this;
|
|
|
|
if ( partial.params.fallbackRefresh ) {
|
|
|
|
self.requestFullRefresh();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A Placement for a Partial.
|
|
|
|
*
|
|
|
|
* A partial placement is the actual physical representation of a partial for a given context.
|
|
|
|
* It also may have information in relation to how a placement may have just changed.
|
|
|
|
* The placement is conceptually similar to a DOM Range or MutationRecord.
|
|
|
|
*
|
2017-09-08 14:42:49 -04:00
|
|
|
* @memberOf wp.customize.selectiveRefresh
|
|
|
|
*
|
|
|
|
* @class Placement
|
2016-02-19 13:41:28 -05:00
|
|
|
* @augments wp.customize.Class
|
|
|
|
* @since 4.5.0
|
|
|
|
*/
|
2017-09-08 14:42:49 -04:00
|
|
|
self.Placement = Placement = api.Class.extend(/** @lends wp.customize.selectiveRefresh.prototype */{
|
2016-02-19 13:41:28 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The partial with which the container is associated.
|
|
|
|
*
|
|
|
|
* @param {wp.customize.selectiveRefresh.Partial}
|
|
|
|
*/
|
|
|
|
partial: null,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* DOM element which contains the placement's contents.
|
|
|
|
*
|
|
|
|
* This will be null if the startNode and endNode do not point to the same
|
|
|
|
* DOM element, such as in the case of a sidebar partial.
|
|
|
|
* This container element itself will be replaced for partials that
|
|
|
|
* have containerInclusive param defined as true.
|
|
|
|
*/
|
|
|
|
container: null,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* DOM node for the initial boundary of the placement.
|
|
|
|
*
|
|
|
|
* This will normally be the same as endNode since most placements appear as elements.
|
|
|
|
* This is primarily useful for widget sidebars which do not have intrinsic containers, but
|
|
|
|
* for which an HTML comment is output before to mark the starting position.
|
|
|
|
*/
|
|
|
|
startNode: null,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* DOM node for the terminal boundary of the placement.
|
|
|
|
*
|
|
|
|
* This will normally be the same as startNode since most placements appear as elements.
|
|
|
|
* This is primarily useful for widget sidebars which do not have intrinsic containers, but
|
|
|
|
* for which an HTML comment is output before to mark the ending position.
|
|
|
|
*/
|
|
|
|
endNode: null,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Context data.
|
|
|
|
*
|
|
|
|
* This provides information about the placement which is included in the request
|
|
|
|
* in order to render the partial properly.
|
|
|
|
*
|
|
|
|
* @param {object}
|
|
|
|
*/
|
|
|
|
context: null,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The content for the partial when refreshed.
|
|
|
|
*
|
|
|
|
* @param {string}
|
|
|
|
*/
|
|
|
|
addedContent: null,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* DOM node(s) removed when the partial is refreshed.
|
|
|
|
*
|
|
|
|
* If the partial is containerInclusive, then the removedNodes will be
|
|
|
|
* the single Element that was the partial's former placement. If the
|
|
|
|
* partial is not containerInclusive, then the removedNodes will be a
|
|
|
|
* documentFragment containing the nodes removed.
|
|
|
|
*
|
|
|
|
* @param {Element|DocumentFragment}
|
|
|
|
*/
|
|
|
|
removedNodes: null,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor.
|
|
|
|
*
|
|
|
|
* @since 4.5.0
|
|
|
|
*
|
2020-07-27 19:35:02 -04:00
|
|
|
* @param {Object} args
|
2016-02-19 13:41:28 -05:00
|
|
|
* @param {Partial} args.partial
|
|
|
|
* @param {jQuery|Element} [args.container]
|
|
|
|
* @param {Node} [args.startNode]
|
|
|
|
* @param {Node} [args.endNode]
|
2020-07-27 19:35:02 -04:00
|
|
|
* @param {Object} [args.context]
|
2016-02-19 13:41:28 -05:00
|
|
|
* @param {string} [args.addedContent]
|
|
|
|
* @param {jQuery|DocumentFragment} [args.removedNodes]
|
|
|
|
*/
|
|
|
|
initialize: function( args ) {
|
|
|
|
var placement = this;
|
|
|
|
|
|
|
|
args = _.extend( {}, args || {} );
|
|
|
|
if ( ! args.partial || ! args.partial.extended( Partial ) ) {
|
|
|
|
throw new Error( 'Missing partial' );
|
|
|
|
}
|
|
|
|
args.context = args.context || {};
|
|
|
|
if ( args.container ) {
|
|
|
|
args.container = $( args.container );
|
|
|
|
}
|
|
|
|
|
|
|
|
_.extend( placement, args );
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Mapping of type names to Partial constructor subclasses.
|
|
|
|
*
|
|
|
|
* @since 4.5.0
|
|
|
|
*
|
|
|
|
* @type {Object.<string, wp.customize.selectiveRefresh.Partial>}
|
|
|
|
*/
|
|
|
|
self.partialConstructor = {};
|
|
|
|
|
|
|
|
self.partial = new api.Values({ defaultConstructor: Partial });
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the POST vars for a Customizer preview request.
|
|
|
|
*
|
|
|
|
* @since 4.5.0
|
|
|
|
* @see wp.customize.previewer.query()
|
|
|
|
*
|
2020-07-27 19:35:02 -04:00
|
|
|
* @return {Object}
|
2016-02-19 13:41:28 -05:00
|
|
|
*/
|
|
|
|
self.getCustomizeQuery = function() {
|
|
|
|
var dirtyCustomized = {};
|
|
|
|
api.each( function( value, key ) {
|
|
|
|
if ( value._dirty ) {
|
|
|
|
dirtyCustomized[ key ] = value();
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
|
|
|
|
return {
|
|
|
|
wp_customize: 'on',
|
|
|
|
nonce: api.settings.nonce.preview,
|
Customize: Implement customized state persistence with changesets.
Includes infrastructure developed in the Customize Snapshots feature plugin.
See https://make.wordpress.org/core/2016/10/12/customize-changesets-technical-design-decisions/
Props westonruter, valendesigns, utkarshpatel, stubgo, lgedeon, ocean90, ryankienstra, mihai2u, dlh, aaroncampbell, jonathanbardo, jorbin.
See #28721.
See #31089.
Fixes #30937.
Fixes #31517.
Fixes #30028.
Fixes #23225.
Fixes #34142.
Fixes #36485.
Built from https://develop.svn.wordpress.org/trunk@38810
git-svn-id: http://core.svn.wordpress.org/trunk@38753 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2016-10-18 16:05:31 -04:00
|
|
|
customize_theme: api.settings.theme.stylesheet,
|
|
|
|
customized: JSON.stringify( dirtyCustomized ),
|
|
|
|
customize_changeset_uuid: api.settings.changeset.uuid
|
2016-02-19 13:41:28 -05:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Currently-requested partials and their associated deferreds.
|
|
|
|
*
|
|
|
|
* @since 4.5.0
|
|
|
|
* @type {Object<string, { deferred: jQuery.Promise, partial: wp.customize.selectiveRefresh.Partial }>}
|
|
|
|
*/
|
|
|
|
self._pendingPartialRequests = {};
|
|
|
|
|
|
|
|
/**
|
2024-04-12 13:47:13 -04:00
|
|
|
* Timeout ID for the current request, or null if no request is current.
|
2016-02-19 13:41:28 -05:00
|
|
|
*
|
|
|
|
* @since 4.5.0
|
|
|
|
* @type {number|null}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
self._debouncedTimeoutId = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Current jqXHR for the request to the partials.
|
|
|
|
*
|
|
|
|
* @since 4.5.0
|
|
|
|
* @type {jQuery.jqXHR|null}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
self._currentRequest = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Request full page refresh.
|
|
|
|
*
|
2016-02-25 07:53:27 -05:00
|
|
|
* When selective refresh is embedded in the context of front-end editing, this request
|
2016-02-19 13:41:28 -05:00
|
|
|
* must fail or else changes will be lost, unless transactions are implemented.
|
|
|
|
*
|
|
|
|
* @since 4.5.0
|
|
|
|
*/
|
|
|
|
self.requestFullRefresh = function() {
|
|
|
|
api.preview.send( 'refresh' );
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Request a re-rendering of a partial.
|
|
|
|
*
|
|
|
|
* @since 4.5.0
|
|
|
|
*
|
|
|
|
* @param {wp.customize.selectiveRefresh.Partial} partial
|
|
|
|
* @return {jQuery.Promise}
|
|
|
|
*/
|
|
|
|
self.requestPartial = function( partial ) {
|
|
|
|
var partialRequest;
|
|
|
|
|
|
|
|
if ( self._debouncedTimeoutId ) {
|
|
|
|
clearTimeout( self._debouncedTimeoutId );
|
|
|
|
self._debouncedTimeoutId = null;
|
|
|
|
}
|
|
|
|
if ( self._currentRequest ) {
|
|
|
|
self._currentRequest.abort();
|
|
|
|
self._currentRequest = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
partialRequest = self._pendingPartialRequests[ partial.id ];
|
|
|
|
if ( ! partialRequest || 'pending' !== partialRequest.deferred.state() ) {
|
|
|
|
partialRequest = {
|
|
|
|
deferred: $.Deferred(),
|
|
|
|
partial: partial
|
|
|
|
};
|
|
|
|
self._pendingPartialRequests[ partial.id ] = partialRequest;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prevent leaking partial into debounced timeout callback.
|
|
|
|
partial = null;
|
|
|
|
|
|
|
|
self._debouncedTimeoutId = setTimeout(
|
|
|
|
function() {
|
|
|
|
var data, partialPlacementContexts, partialsPlacements, request;
|
|
|
|
|
|
|
|
self._debouncedTimeoutId = null;
|
|
|
|
data = self.getCustomizeQuery();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It is key that the containers be fetched exactly at the point of the request being
|
|
|
|
* made, because the containers need to be mapped to responses by array indices.
|
|
|
|
*/
|
|
|
|
partialsPlacements = {};
|
|
|
|
|
|
|
|
partialPlacementContexts = {};
|
|
|
|
|
|
|
|
_.each( self._pendingPartialRequests, function( pending, partialId ) {
|
|
|
|
partialsPlacements[ partialId ] = pending.partial.placements();
|
|
|
|
if ( ! self.partial.has( partialId ) ) {
|
|
|
|
pending.deferred.rejectWith( pending.partial, [ new Error( 'partial_removed' ), partialsPlacements[ partialId ] ] );
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Note that this may in fact be an empty array. In that case, it is the responsibility
|
|
|
|
* of the Partial subclass instance to know where to inject the response, or else to
|
|
|
|
* just issue a refresh (default behavior). The data being returned with each container
|
|
|
|
* is the context information that may be needed to render certain partials, such as
|
|
|
|
* the contained sidebar for rendering widgets or what the nav menu args are for a menu.
|
|
|
|
*/
|
|
|
|
partialPlacementContexts[ partialId ] = _.map( partialsPlacements[ partialId ], function( placement ) {
|
|
|
|
return placement.context || {};
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
|
|
|
|
data.partials = JSON.stringify( partialPlacementContexts );
|
|
|
|
data[ self.data.renderQueryVar ] = '1';
|
|
|
|
|
|
|
|
request = self._currentRequest = wp.ajax.send( null, {
|
|
|
|
data: data,
|
|
|
|
url: api.settings.url.self
|
|
|
|
} );
|
|
|
|
|
|
|
|
request.done( function( data ) {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Announce the data returned from a request to render partials.
|
|
|
|
*
|
|
|
|
* The data is filtered on the server via customize_render_partials_response
|
|
|
|
* so plugins can inject data from the server to be utilized
|
|
|
|
* on the client via this event. Plugins may use this filter
|
|
|
|
* to communicate script and style dependencies that need to get
|
|
|
|
* injected into the page to support the rendered partials.
|
|
|
|
* This is similar to the 'saved' event.
|
|
|
|
*/
|
|
|
|
self.trigger( 'render-partials-response', data );
|
|
|
|
|
|
|
|
// Relay errors (warnings) captured during rendering and relay to console.
|
|
|
|
if ( data.errors && 'undefined' !== typeof console && console.warn ) {
|
|
|
|
_.each( data.errors, function( error ) {
|
|
|
|
console.warn( error );
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note that data is an array of items that correspond to the array of
|
|
|
|
* containers that were submitted in the request. So we zip up the
|
|
|
|
* array of containers with the array of contents for those containers,
|
|
|
|
* and send them into .
|
|
|
|
*/
|
|
|
|
_.each( self._pendingPartialRequests, function( pending, partialId ) {
|
|
|
|
var placementsContents;
|
|
|
|
if ( ! _.isArray( data.contents[ partialId ] ) ) {
|
|
|
|
pending.deferred.rejectWith( pending.partial, [ new Error( 'unrecognized_partial' ), partialsPlacements[ partialId ] ] );
|
|
|
|
} else {
|
|
|
|
placementsContents = _.map( data.contents[ partialId ], function( content, i ) {
|
|
|
|
var partialPlacement = partialsPlacements[ partialId ][ i ];
|
|
|
|
if ( partialPlacement ) {
|
|
|
|
partialPlacement.addedContent = content;
|
|
|
|
} else {
|
|
|
|
partialPlacement = new Placement( {
|
|
|
|
partial: pending.partial,
|
|
|
|
addedContent: content
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
return partialPlacement;
|
|
|
|
} );
|
|
|
|
pending.deferred.resolveWith( pending.partial, [ placementsContents ] );
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
self._pendingPartialRequests = {};
|
|
|
|
} );
|
|
|
|
|
|
|
|
request.fail( function( data, statusText ) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ignore failures caused by partial.currentRequest.abort()
|
|
|
|
* The pending deferreds will remain in self._pendingPartialRequests
|
|
|
|
* for re-use with the next request.
|
|
|
|
*/
|
|
|
|
if ( 'abort' === statusText ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_.each( self._pendingPartialRequests, function( pending, partialId ) {
|
|
|
|
pending.deferred.rejectWith( pending.partial, [ data, partialsPlacements[ partialId ] ] );
|
|
|
|
} );
|
|
|
|
self._pendingPartialRequests = {};
|
|
|
|
} );
|
|
|
|
},
|
Customize: Implement customized state persistence with changesets.
Includes infrastructure developed in the Customize Snapshots feature plugin.
See https://make.wordpress.org/core/2016/10/12/customize-changesets-technical-design-decisions/
Props westonruter, valendesigns, utkarshpatel, stubgo, lgedeon, ocean90, ryankienstra, mihai2u, dlh, aaroncampbell, jonathanbardo, jorbin.
See #28721.
See #31089.
Fixes #30937.
Fixes #31517.
Fixes #30028.
Fixes #23225.
Fixes #34142.
Fixes #36485.
Built from https://develop.svn.wordpress.org/trunk@38810
git-svn-id: http://core.svn.wordpress.org/trunk@38753 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2016-10-18 16:05:31 -04:00
|
|
|
api.settings.timeouts.selectiveRefresh
|
2016-02-19 13:41:28 -05:00
|
|
|
);
|
|
|
|
|
|
|
|
return partialRequest.deferred.promise();
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add partials for any nav menu container elements in the document.
|
|
|
|
*
|
|
|
|
* This method may be called multiple times. Containers that already have been
|
|
|
|
* seen will be skipped.
|
|
|
|
*
|
|
|
|
* @since 4.5.0
|
|
|
|
*
|
|
|
|
* @param {jQuery|HTMLElement} [rootElement]
|
|
|
|
* @param {object} [options]
|
|
|
|
* @param {boolean=true} [options.triggerRendered]
|
|
|
|
*/
|
|
|
|
self.addPartials = function( rootElement, options ) {
|
|
|
|
var containerElements;
|
|
|
|
if ( ! rootElement ) {
|
|
|
|
rootElement = document.documentElement;
|
|
|
|
}
|
|
|
|
rootElement = $( rootElement );
|
|
|
|
options = _.extend(
|
|
|
|
{
|
|
|
|
triggerRendered: true
|
|
|
|
},
|
|
|
|
options || {}
|
|
|
|
);
|
|
|
|
|
|
|
|
containerElements = rootElement.find( '[data-customize-partial-id]' );
|
|
|
|
if ( rootElement.is( '[data-customize-partial-id]' ) ) {
|
|
|
|
containerElements = containerElements.add( rootElement );
|
|
|
|
}
|
|
|
|
containerElements.each( function() {
|
2017-02-10 22:35:43 -05:00
|
|
|
var containerElement = $( this ), partial, placement, id, Constructor, partialOptions, containerContext;
|
2016-02-19 13:41:28 -05:00
|
|
|
id = containerElement.data( 'customize-partial-id' );
|
|
|
|
if ( ! id ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
containerContext = containerElement.data( 'customize-partial-placement-context' ) || {};
|
|
|
|
|
|
|
|
partial = self.partial( id );
|
|
|
|
if ( ! partial ) {
|
|
|
|
partialOptions = containerElement.data( 'customize-partial-options' ) || {};
|
|
|
|
partialOptions.constructingContainerContext = containerElement.data( 'customize-partial-placement-context' ) || {};
|
|
|
|
Constructor = self.partialConstructor[ containerElement.data( 'customize-partial-type' ) ] || self.Partial;
|
|
|
|
partial = new Constructor( id, partialOptions );
|
2017-10-04 02:48:46 -04:00
|
|
|
self.partial.add( partial );
|
2016-02-19 13:41:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Only trigger renders on (nested) partials that have been not been
|
|
|
|
* handled yet. An example where this would apply is a nav menu
|
2017-10-15 18:23:47 -04:00
|
|
|
* embedded inside of a navigation menu widget. When the widget's title
|
2016-02-19 13:41:28 -05:00
|
|
|
* is updated, the entire widget will re-render and then the event
|
|
|
|
* will be triggered for the nested nav menu to do any initialization.
|
|
|
|
*/
|
|
|
|
if ( options.triggerRendered && ! containerElement.data( 'customize-partial-content-rendered' ) ) {
|
|
|
|
|
2017-02-10 22:35:43 -05:00
|
|
|
placement = new Placement( {
|
2016-02-19 13:41:28 -05:00
|
|
|
partial: partial,
|
|
|
|
context: containerContext,
|
|
|
|
container: containerElement
|
2017-02-10 22:35:43 -05:00
|
|
|
} );
|
|
|
|
|
|
|
|
$( placement.container ).attr( 'title', self.data.l10n.shiftClickToEdit );
|
|
|
|
partial.createEditShortcutForPlacement( placement );
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Announce when a partial's nested placement has been re-rendered.
|
|
|
|
*/
|
|
|
|
self.trigger( 'partial-content-rendered', placement );
|
2016-02-19 13:41:28 -05:00
|
|
|
}
|
|
|
|
containerElement.data( 'customize-partial-content-rendered', true );
|
|
|
|
} );
|
|
|
|
};
|
|
|
|
|
|
|
|
api.bind( 'preview-ready', function() {
|
|
|
|
var handleSettingChange, watchSettingChange, unwatchSettingChange;
|
|
|
|
|
|
|
|
_.extend( self.data, _customizePartialRefreshExports );
|
|
|
|
|
|
|
|
// Create the partial JS models.
|
|
|
|
_.each( self.data.partials, function( data, id ) {
|
|
|
|
var Constructor, partial = self.partial( id );
|
|
|
|
if ( ! partial ) {
|
|
|
|
Constructor = self.partialConstructor[ data.type ] || self.Partial;
|
2017-10-28 20:15:53 -04:00
|
|
|
partial = new Constructor(
|
|
|
|
id,
|
|
|
|
_.extend( { params: data }, data ) // Inclusion of params alias is for back-compat for custom partials that expect to augment this property.
|
|
|
|
);
|
2017-10-04 02:48:46 -04:00
|
|
|
self.partial.add( partial );
|
2016-02-19 13:41:28 -05:00
|
|
|
} else {
|
|
|
|
_.extend( partial.params, data );
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle change to a setting.
|
|
|
|
*
|
|
|
|
* Note this is largely needed because adding a 'change' event handler to wp.customize
|
|
|
|
* will only include the changed setting object as an argument, not including the
|
|
|
|
* new value or the old value.
|
|
|
|
*
|
|
|
|
* @since 4.5.0
|
|
|
|
* @this {wp.customize.Setting}
|
|
|
|
*
|
|
|
|
* @param {*|null} newValue New value, or null if the setting was just removed.
|
|
|
|
* @param {*|null} oldValue Old value, or null if the setting was just added.
|
|
|
|
*/
|
|
|
|
handleSettingChange = function( newValue, oldValue ) {
|
|
|
|
var setting = this;
|
|
|
|
self.partial.each( function( partial ) {
|
|
|
|
if ( partial.isRelatedSetting( setting, newValue, oldValue ) ) {
|
|
|
|
partial.refresh();
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Trigger the initial change for the added setting, and watch for changes.
|
|
|
|
*
|
|
|
|
* @since 4.5.0
|
|
|
|
* @this {wp.customize.Values}
|
|
|
|
*
|
|
|
|
* @param {wp.customize.Setting} setting
|
|
|
|
*/
|
|
|
|
watchSettingChange = function( setting ) {
|
|
|
|
handleSettingChange.call( setting, setting(), null );
|
|
|
|
setting.bind( handleSettingChange );
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Trigger the final change for the removed setting, and unwatch for changes.
|
|
|
|
*
|
|
|
|
* @since 4.5.0
|
|
|
|
* @this {wp.customize.Values}
|
|
|
|
*
|
|
|
|
* @param {wp.customize.Setting} setting
|
|
|
|
*/
|
|
|
|
unwatchSettingChange = function( setting ) {
|
|
|
|
handleSettingChange.call( setting, null, setting() );
|
|
|
|
setting.unbind( handleSettingChange );
|
|
|
|
};
|
|
|
|
|
|
|
|
api.bind( 'add', watchSettingChange );
|
|
|
|
api.bind( 'remove', unwatchSettingChange );
|
|
|
|
api.each( function( setting ) {
|
|
|
|
setting.bind( handleSettingChange );
|
|
|
|
} );
|
|
|
|
|
|
|
|
// Add (dynamic) initial partials that are declared via data-* attributes.
|
|
|
|
self.addPartials( document.documentElement, {
|
|
|
|
triggerRendered: false
|
|
|
|
} );
|
|
|
|
|
|
|
|
// Add new dynamic partials when the document changes.
|
|
|
|
if ( 'undefined' !== typeof MutationObserver ) {
|
|
|
|
self.mutationObserver = new MutationObserver( function( mutations ) {
|
|
|
|
_.each( mutations, function( mutation ) {
|
|
|
|
self.addPartials( $( mutation.target ) );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
self.mutationObserver.observe( document.documentElement, {
|
|
|
|
childList: true,
|
|
|
|
subtree: true
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle rendering of partials.
|
|
|
|
*
|
|
|
|
* @param {api.selectiveRefresh.Placement} placement
|
|
|
|
*/
|
|
|
|
api.selectiveRefresh.bind( 'partial-content-rendered', function( placement ) {
|
|
|
|
if ( placement.container ) {
|
|
|
|
self.addPartials( placement.container );
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
|
2016-06-14 15:17:28 -04:00
|
|
|
/**
|
|
|
|
* Handle setting validities in partial refresh response.
|
|
|
|
*
|
|
|
|
* @param {object} data Response data.
|
|
|
|
* @param {object} data.setting_validities Setting validities.
|
|
|
|
*/
|
|
|
|
api.selectiveRefresh.bind( 'render-partials-response', function handleSettingValiditiesResponse( data ) {
|
|
|
|
if ( data.setting_validities ) {
|
|
|
|
api.preview.send( 'selective-refresh-setting-validities', data.setting_validities );
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
|
2016-10-26 16:03:32 -04:00
|
|
|
api.preview.bind( 'edit-shortcut-visibility', function( visibility ) {
|
|
|
|
api.selectiveRefresh.editShortcutVisibility.set( visibility );
|
|
|
|
} );
|
|
|
|
api.selectiveRefresh.editShortcutVisibility.bind( function( visibility ) {
|
|
|
|
var body = $( document.body ), shouldAnimateHide;
|
|
|
|
|
|
|
|
shouldAnimateHide = ( 'hidden' === visibility && body.hasClass( 'customize-partial-edit-shortcuts-shown' ) && ! body.hasClass( 'customize-partial-edit-shortcuts-hidden' ) );
|
|
|
|
body.toggleClass( 'customize-partial-edit-shortcuts-hidden', shouldAnimateHide );
|
|
|
|
body.toggleClass( 'customize-partial-edit-shortcuts-shown', 'visible' === visibility );
|
|
|
|
} );
|
|
|
|
|
2016-02-19 13:41:28 -05:00
|
|
|
api.preview.bind( 'active', function() {
|
|
|
|
|
|
|
|
// Make all partials ready.
|
|
|
|
self.partial.each( function( partial ) {
|
|
|
|
partial.deferred.ready.resolve();
|
|
|
|
} );
|
|
|
|
|
|
|
|
// Make all partials added henceforth as ready upon add.
|
|
|
|
self.partial.bind( 'add', function( partial ) {
|
|
|
|
partial.deferred.ready.resolve();
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
} );
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}( jQuery, wp.customize ) );
|