Skip to content

Add Customizer context #399

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 86 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
25f95bf
Enable autocomplete when focusing an autocomplete-ready field
dlh01 Oct 17, 2015
370d191
Allow init_sortable() and init_label_macros() to be triggered externally
dlh01 Nov 15, 2015
490f7a8
Add the basics of a Customizer context
dlh01 Nov 15, 2015
c5fc06e
Include jQuery Datepicker as Datepicker dependency
dlh01 Nov 22, 2015
be02e65
Replace per-field JS handlers with Customizer event
dlh01 Nov 22, 2015
be00ddf
Fix Datepicker initialization in the Customizer
dlh01 Nov 22, 2015
a79fe3e
Initialize display-if triggers in the Customizer
dlh01 Nov 22, 2015
c1c66d8
Remove unnecessary variable
dlh01 Nov 22, 2015
8f39b3f
Fix unnecessarily debounced autocomplete events
dlh01 Nov 22, 2015
980e2b5
Remove extra space in string
dlh01 Dec 6, 2015
0795c83
Alert users to failed validation during saving
dlh01 Dec 6, 2015
e017a20
Bind FM event to Sections as they're added
dlh01 Dec 7, 2015
bd39fe8
Offer suggestion after saving in Customizer fails
dlh01 Dec 7, 2015
fbd2dbd
Merge branch 'master' into customizer-context
dlh01 Dec 7, 2015
077968b
Merge branch 'master' into customizer-context
dlh01 Jan 2, 2016
d6330b4
Don't reserialize all controls on changes in value
dlh01 Jan 2, 2016
466ce6e
Fix some saving callbacks after d6330b4
dlh01 Jan 26, 2016
01893e7
Serialize FM Control values into JS objects
dlh01 Jan 26, 2016
2080bc1
Merge branch 'master' into customizer-context
dlh01 Jan 26, 2016
96a9041
stripslashes_deep(), use context API when saving
dlh01 Jan 30, 2016
1155bf5
Pass context, not field, to Customize control
dlh01 Jan 30, 2016
176460c
Ensure Customize settings accept null default value
dlh01 Jan 30, 2016
43b320a
Trigger an event after an FM RTE loads TinyMCE
dlh01 Jan 30, 2016
3c13c56
Check for getUserSetting() before calling it
dlh01 Jan 30, 2016
8066242
Add support for RichTextAreas in the Customizer
dlh01 Jan 30, 2016
591ae7b
Don't reserialize each control on 'ready'
dlh01 Jan 30, 2016
1d6148c
Expose setting functions in the global fm object
dlh01 Jan 31, 2016
5d51f6e
Include fm-customize in script tests
dlh01 Feb 4, 2016
7c991b8
Add support for Colorpickers in the Customizer
dlh01 Feb 7, 2016
9e98884
Introduce Fieldmanager_Customize_Setting
dlh01 Feb 11, 2016
9b2a851
Document more of Fieldmanager_Context_Customizer tests
dlh01 Feb 11, 2016
ac92831
Expose the target selector in fm.customize
dlh01 Feb 19, 2016
738d1ed
Fix reference to 'this' in setEachControl()
dlh01 Feb 19, 2016
fa1c350
Check for serializeJSON() on found element itself
dlh01 Feb 19, 2016
4005cf3
Return the control, not nothing, after setControl()
dlh01 Feb 19, 2016
affc2ce
Update to QUnit 1.21.0
dlh01 Feb 19, 2016
c9cb7e5
Add Customizer JS unit tests, save one event
dlh01 Feb 19, 2016
d71c9aa
Fix default RichTextArea 'teeny' setting
dlh01 Feb 19, 2016
75e5544
Update WordPress versions QUnit tests againsts
dlh01 Feb 19, 2016
d4e0093
Provide 'settings' in test control params
dlh01 Feb 19, 2016
5484a07
Merge branch 'master' into customizer-context
dlh01 Feb 20, 2016
8bb2f5d
Add explanatory comment around creating field Setting
dlh01 Feb 20, 2016
24c04e4
Merge branch 'master' into customizer-context
dlh01 Feb 20, 2016
31c7740
Merge branch 'master' into customizer-context
dlh01 Feb 20, 2016
0b13e64
Remove autocomplete shim
dlh01 Feb 20, 2016
cdf92e0
Merge branch 'master' into customizer-context
dlh01 Feb 28, 2016
d72e3dc
Update test control constructors after r36689
dlh01 Mar 3, 2016
7065196
Update autocomplete event test after 0b13e64
dlh01 Mar 3, 2016
c24c240
Check control has setting before using it
dlh01 Mar 5, 2016
45bff86
Remove debouncing from changes after 'keyup'
dlh01 Mar 5, 2016
25c8fb0
Remove the other half of RichTextArea debouncing
dlh01 Mar 5, 2016
82aaf30
Merge redundant keyup events
dlh01 Mar 5, 2016
163d44b
Hook into 'customize_register' at priority 100
dlh01 Mar 18, 2016
2396297
Don't require each Customizer context to create a section
dlh01 Mar 18, 2016
2571c9d
Revert "Update test control constructors after r36689"
dlh01 Mar 18, 2016
85f90e7
Correctly instantiate controls in JS tests
dlh01 Mar 18, 2016
af2ed0f
Remove extra line
dlh01 Mar 18, 2016
42b1b5a
Merge branch 'master' into customizer-context
dlh01 May 15, 2016
9d94adb
Render Fieldmanager_Customize_Control::label and ::description
dlh01 May 15, 2016
85f1efb
Support core's Customizer setting validation
dlh01 Jun 3, 2016
c8b87f7
Clean up comments
dlh01 Jun 3, 2016
e27ddda
Revert "Support core's Customizer setting validation"
dlh01 Jul 2, 2016
a855da6
Merge branch 'master' into customizer-context
dlh01 Aug 19, 2016
c6ec5b3
Restore Customizer calculated context
dlh01 Aug 19, 2016
7cacfaa
Restore 'customize_controls_enqueue_scripts' hook
dlh01 Aug 19, 2016
59ad758
Update `init_display_if()` on control section expansion
dlh01 Aug 19, 2016
1ed2d45
Test JS against 4.6 and 4.5
dlh01 Aug 19, 2016
2637ef2
Remove outdated script-loading preparation
dlh01 Aug 19, 2016
e44f903
Improve 'X' placement after FM 1.0 CSS refresh
dlh01 Aug 19, 2016
fb490c0
Revert custom `alert()` on validation failure
dlh01 Sep 10, 2016
a34aee2
Add default method for a Setting's $validate_callback
dlh01 Sep 10, 2016
669eec6
Rearrange validate_callback() tests to match class
dlh01 Sep 10, 2016
f0be9ff
Use context's validation callback with FM settings
dlh01 Sep 18, 2016
93b5061
Parse query-string values before validating
dlh01 Sep 18, 2016
de1ab07
Reject invalid values from sanitize callback
dlh01 Sep 18, 2016
451935b
Test catching invalid values while sanitizing
dlh01 Sep 18, 2016
9a94d30
Test validating value submitted as a query string
dlh01 Sep 18, 2016
804f8e1
Handle calls to `wp_die()` on validation error
dlh01 Sep 25, 2016
38e6884
Remove outdated test
dlh01 Sep 25, 2016
f154b9f
Update `Fieldmanager_RichTextArea::customize_controls_print_footer_sc…
dlh01 Sep 25, 2016
dfcefd7
Merge branch 'master' into customizer-context
dlh01 Sep 25, 2016
1de94b3
Fix docs standards violations
dlh01 Sep 25, 2016
afca523
Handle all exceptions in `validate_callback()`
dlh01 Sep 25, 2016
32c61b6
Standardize on 'Customize' for objects
dlh01 Oct 10, 2016
11037a4
Fix `Fieldmanager_Datepicker` styles
dlh01 Oct 12, 2016
0053c3e
Fix inconsistent check for Customize context
dlh01 Oct 14, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions css/fieldmanager.css
Original file line number Diff line number Diff line change
Expand Up @@ -379,3 +379,34 @@ a.fm-delete:hover {
.form-field .fm-checkbox label {
display: inline;
}

.wp-customizer .ui-autocomplete,
.wp-customizer .ui-datepicker {
/* Hoist jQuery UI popups over who-knows-what in the Customizer. */
z-index: 500000 !important;
}

.customize-control input.fm-datepicker-popup {
/* Override Customizer defaults; datepickers have multiple inputs on one line. */
width: 100px;
}

.customize-control input.fm-datepicker-time {
/* Override Customizer defaults; datepickers have multiple inputs on one line. */
width: 30px;
}

.fm-datepicker-time-wrapper select {
/* Override Customizer defaults; datepickers have multiple inputs on one line. */
min-width: 0;
}

.wp-customizer .fmjs-removable .fmjs-drag-icon {
/* Nudge for the Customizer. */
margin-top: 7px;
}

.wp-customizer .fmjs-removable .fmjs-drag-icon + .fmjs-removable-element {
/* Nudge for the Customizer. */
max-width: 80%;
}
19 changes: 18 additions & 1 deletion fieldmanager.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@ function fieldmanager_load_class( $class ) {
return fieldmanager_load_file( 'datasource/class-fieldmanager-datasource-' . $class_id . '.php' );
}

if ( 'Fieldmanager_Customize_Control' === $class ) {
return fieldmanager_load_file( 'class-fieldmanager-customize-control.php' );
}

if ( 'Fieldmanager_Customize_Setting' === $class ) {
return fieldmanager_load_file( 'class-fieldmanager-customize-setting.php' );
}

if ( 0 === strpos( $class, 'Fieldmanager_Util' ) ) {
return fieldmanager_load_file( 'util/class-fieldmanager-util-' . $class_id . '.php' );
}
Expand Down Expand Up @@ -250,8 +258,12 @@ function fm_get_context( $recalculate = false ) {
function fm_calculate_context() {
$calculated_context = array( null, null );

if ( is_customize_preview() ) {
$calculated_context = array( 'customize', null );
}

// Safe to use at any point in the load process, and better than URL matching.
if ( is_admin() ) {
if ( empty( $calculated_context[0] ) && is_admin() ) {
$script = substr( $_SERVER['PHP_SELF'], strrpos( $_SERVER['PHP_SELF'], '/' ) + 1 );

/*
Expand Down Expand Up @@ -284,7 +296,12 @@ function fm_calculate_context() {
}
}

if ( 'customize.php' === $script || is_customize_preview() ) {
$calculated_context = array( 'customize', null );
}

if ( empty( $calculated_context[0] ) ) {

switch ( $script ) {
// Context = "post".
case 'post.php':
Expand Down
8 changes: 6 additions & 2 deletions js/fieldmanager-autocomplete.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
( function( $ ) {
(function( $ ) {

fm.autocomplete = {

Expand Down Expand Up @@ -84,5 +84,9 @@ fm.autocomplete = {

$( document ).ready( fm.autocomplete.enable_autocomplete );
$( document ).on( 'fm_collapsible_toggle fm_added_element fm_displayif_toggle fm_activate_tab', fm.autocomplete.enable_autocomplete );
$( document ).on( 'focus',
'input[class*="fm-autocomplete"]:not(.fm-autocomplete-enabled)',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need the prior to implementation approaches in addition to this approach?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. But the existing JS predates my involvement with FM, so I'd appreciate a review from you or someone else to confirm.

fm.autocomplete.enable_autocomplete
);

} ) ( jQuery );
})( jQuery );
23 changes: 21 additions & 2 deletions js/fieldmanager-colorpicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,28 @@

fm.colorpicker = {
init: function() {
$( '.fm-colorpicker-popup:visible' ).wpColorPicker();
$( '.fm-colorpicker-popup:visible' ).wpColorPicker({
change: function ( e, ui ) {
// Make sure the input's value attribute also changes.
$( this ).attr( 'value', ui.color.toString() );
fm.colorpicker.triggerUpdateEvent( this );
},
clear: function () {
// Make sure the input's value attribute also changes.
$( this ).attr( 'value', '' );
fm.colorpicker.triggerUpdateEvent( this );
},
});
},
triggerUpdateEvent: function ( el ) {
/**
* Trigger a common event for a value 'change' or 'clear'.
*
* @var {Element} Colorpicker element.
*/
$( document ).trigger( 'fm_colorpicker_update', el );
}
}
};

$( document ).ready( fm.colorpicker.init );
$( document ).on( 'fm_collapsible_toggle fm_added_element fm_displayif_toggle fm_activate_tab', fm.colorpicker.init );
Expand Down
256 changes: 256 additions & 0 deletions js/fieldmanager-customize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
/* global document, jQuery, wp, _, fm */
/**
* Integrate Fieldmanager and the Customizer.
*
* @param {function} $ jQuery
* @param {function} api Customizer API.
* @param {function} _ Underscore
* @param {Object} fm Fieldmanager API.
*/
(function( $, api, _, fm ) {
'use strict';

fm.customize = {
/**
* jQuery selector targeting all elements to include in a Fieldmanager setting value.
*
* @type {String}
*/
targetSelector: '.fm-element',

/**
* Set the values of all Fieldmanager controls.
*/
setEachControl: function () {
var that = this;

api.control.each(function( control ) {
that.setControl( control );
});
},

/**
* Set the value of any Fieldmanager control with a given element in its container.
*
* @param {Element} el Element to look for.
*/
setControlsContainingElement: function ( el ) {
var that = this;

api.control.each(function( control ) {
if ( control.container.find( el ).length ) {
that.setControl( control );
}
});
},

/**
* Set a Fieldmanager setting to its control's form values.
*
* @param {Object} control Customizer Control object.
* @return {Object} The updated Control.
*/
setControl: function ( control ) {
var $element;
var serialized;
var value;

if ( 'fieldmanager' !== control.params.type ) {
return;
}

if ( ! control.setting ) {
return;
}

$element = control.container.find( this.targetSelector );

if ( $element.serializeJSON ) {
serialized = $element.serializeJSON();
value = serialized[ control.id ];
} else {
value = $element.serialize();
}

return control.setting.set( value );
},
};

/**
* Fires when an .fm-element input triggers a 'change' event.
*
* @param {Event} e Event object.
*/
var onFmElementChange = function( e ) {
fm.customize.setControlsContainingElement( e.target );
};

/**
* Fires when an .fm-element input triggers a 'keyup' event.
*
* @param {Event} e Event object.
*/
var onFmElementKeyup = function( e ) {
var $target = $( e.target );

// Ignore [Escape] and [Enter].
if ( 27 === e.keyCode || 13 === e.keyCode ) {
return;
}

if ( $target.hasClass( 'fm-autocomplete' ) ) {
/*
* Don't update when typing into the autocomplete input. The hidden
* field actually contains the value and is handled onFmElementChange().
*/
return;
}

fm.customize.setControlsContainingElement( e.target );
};

/**
* Fires when a Fieldmanager object is dropped while sorting.
*
* @param {Event} e Event object.
* @param {Element} el The sorted element.
*/
var onFmSortableDrop = function ( e, el ) {
fm.customize.setControlsContainingElement( el );
};

/**
* Fires when Fieldmanager adds a new element in a repeatable field.
*
* @param {Event} e Event object.
*/
var onFmAddedElement = function( e ) {
fm.customize.setControlsContainingElement( e.target );
};

/**
* Fires when an item is selected and previewed in a Fieldmanager media field.
*
* @param {Event} e Event object.
* @param {jQuery} $wrapper .media-wrapper jQuery object.
* @param {Object} attachment Attachment attributes.
* @param {Object} wp Global WordPress JS API.
*/
var onFieldmanagerMediaPreview = function( e, $wrapper, attachment, wp ) {
fm.customize.setControlsContainingElement( e.target );
};

/**
* Fires after TinyMCE initializes in a Fieldmanager richtext field.
*
* @param {Event} e Event object.
* @param {Object} ed TinyMCE instance.
*/
var onFmRichtextInit = function( e, ed ) {
ed.on( 'keyup AddUndo', function () {
ed.save();
fm.customize.setControlsContainingElement( document.getElementById( ed.id ) );
} );
};

/**
* Fires after a Fieldmanager colorpicker field updates.
*
* @param {Event} e Event object.
* @param {Element} el Colorpicker element.
*/
var onFmColorpickerUpdate = function( e, el ) {
fm.customize.setControlsContainingElement( el );
};

/**
* Fires after clicking the "Remove" link of a Fieldmanager media field.
*
* @param {Event} e Event object.
*/
var onFmMediaRemoveClick = function ( e ) {
// The control no longer contains the element, so set all of them.
fm.customize.setEachControl();
};

/**
* Fires after clicking the "Remove" link of a Fieldmanager repeatable field.
*
* @param {Event} e Event object.
*/
var onFmjsRemoveClick = function ( e ) {
// The control no longer contains the element, so set all of them.
fm.customize.setEachControl();
};

/**
* Fires when a Customizer Section expands.
*
* @param {Object} section Customizer Section object.
*/
var onSectionExpanded = function( section ) {
/*
* Trigger a Fieldmanager event when a Customizer section expands.
*
* We bind to sections whether or not they have FM controls in case a
* control is added dynamically.
*/
$( document ).trigger( 'fm_customize_control_section_expanded' );

if ( fm.richtextarea ) {
fm.richtextarea.add_rte_to_visible_textareas();
}

if ( fm.colorpicker ) {
fm.colorpicker.init();
}

/*
* Reserialize any Fieldmanager controls in this section with null
* values. We assume null indicates nothing has been saved to the
* database, so we want to make sure default values take effect in the
* preview and are submitted on save as they would be in other contexts.
*/
_.each( section.controls(), function ( control ) {
if (
control.settings.default &&
null === control.settings.default.get()
) {
fm.customize.setControl( control );
}
});
};

/**
* Fires when the Customizer is loaded.
*/
var ready = function() {
var $document = $( document );

$document.on( 'keyup', '.fm-element', onFmElementKeyup );
$document.on( 'change', '.fm-element', onFmElementChange );
$document.on( 'click', '.fm-media-remove', onFmMediaRemoveClick );
$document.on( 'click', '.fmjs-remove', onFmjsRemoveClick );
$document.on( 'fm_sortable_drop', onFmSortableDrop );
$document.on( 'fieldmanager_media_preview', onFieldmanagerMediaPreview );
$document.on( 'fm_richtext_init', onFmRichtextInit );
$document.on( 'fm_colorpicker_update', onFmColorpickerUpdate );
};

/**
* Fires when a Customizer Section is added.
*
* @param {Object} section Customizer Section object.
*/
var addSection = function( section ) {
// It would be more efficient to do this only when adding an FM control to a section.
section.container.bind( 'expanded', function () {
onSectionExpanded( section );
} );
};

if ( typeof api !== 'undefined' ) {
api.bind( 'ready', ready );
api.section.bind( 'add', addSection );
}
})( jQuery, wp.customize, _, fm );
7 changes: 5 additions & 2 deletions js/fieldmanager-datepicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,8 @@
}

$( document ).ready( fm.datepicker.add_datepicker );
$( document ).on( 'fm_collapsible_toggle fm_added_element fm_displayif_toggle fm_activate_tab', fm.datepicker.add_datepicker );
} ) ( jQuery );
$( document ).on(
'fm_collapsible_toggle fm_added_element fm_displayif_toggle fm_activate_tab fm_customize_control_section_expanded',
fm.datepicker.add_datepicker
);
} ) ( jQuery );
Loading