-
Notifications
You must be signed in to change notification settings - Fork 99
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
dlh01
wants to merge
86
commits into
main
Choose a base branch
from
customizer-context
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
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 370d191
Allow init_sortable() and init_label_macros() to be triggered externally
dlh01 490f7a8
Add the basics of a Customizer context
dlh01 c5fc06e
Include jQuery Datepicker as Datepicker dependency
dlh01 be02e65
Replace per-field JS handlers with Customizer event
dlh01 be00ddf
Fix Datepicker initialization in the Customizer
dlh01 a79fe3e
Initialize display-if triggers in the Customizer
dlh01 c1c66d8
Remove unnecessary variable
dlh01 8f39b3f
Fix unnecessarily debounced autocomplete events
dlh01 980e2b5
Remove extra space in string
dlh01 0795c83
Alert users to failed validation during saving
dlh01 e017a20
Bind FM event to Sections as they're added
dlh01 bd39fe8
Offer suggestion after saving in Customizer fails
dlh01 fbd2dbd
Merge branch 'master' into customizer-context
dlh01 077968b
Merge branch 'master' into customizer-context
dlh01 d6330b4
Don't reserialize all controls on changes in value
dlh01 466ce6e
Fix some saving callbacks after d6330b4
dlh01 01893e7
Serialize FM Control values into JS objects
dlh01 2080bc1
Merge branch 'master' into customizer-context
dlh01 96a9041
stripslashes_deep(), use context API when saving
dlh01 1155bf5
Pass context, not field, to Customize control
dlh01 176460c
Ensure Customize settings accept null default value
dlh01 43b320a
Trigger an event after an FM RTE loads TinyMCE
dlh01 3c13c56
Check for getUserSetting() before calling it
dlh01 8066242
Add support for RichTextAreas in the Customizer
dlh01 591ae7b
Don't reserialize each control on 'ready'
dlh01 1d6148c
Expose setting functions in the global fm object
dlh01 5d51f6e
Include fm-customize in script tests
dlh01 7c991b8
Add support for Colorpickers in the Customizer
dlh01 9e98884
Introduce Fieldmanager_Customize_Setting
dlh01 9b2a851
Document more of Fieldmanager_Context_Customizer tests
dlh01 ac92831
Expose the target selector in fm.customize
dlh01 738d1ed
Fix reference to 'this' in setEachControl()
dlh01 fa1c350
Check for serializeJSON() on found element itself
dlh01 4005cf3
Return the control, not nothing, after setControl()
dlh01 affc2ce
Update to QUnit 1.21.0
dlh01 c9cb7e5
Add Customizer JS unit tests, save one event
dlh01 d71c9aa
Fix default RichTextArea 'teeny' setting
dlh01 75e5544
Update WordPress versions QUnit tests againsts
dlh01 d4e0093
Provide 'settings' in test control params
dlh01 5484a07
Merge branch 'master' into customizer-context
dlh01 8bb2f5d
Add explanatory comment around creating field Setting
dlh01 24c04e4
Merge branch 'master' into customizer-context
dlh01 31c7740
Merge branch 'master' into customizer-context
dlh01 0b13e64
Remove autocomplete shim
dlh01 cdf92e0
Merge branch 'master' into customizer-context
dlh01 d72e3dc
Update test control constructors after r36689
dlh01 7065196
Update autocomplete event test after 0b13e64
dlh01 c24c240
Check control has setting before using it
dlh01 45bff86
Remove debouncing from changes after 'keyup'
dlh01 25c8fb0
Remove the other half of RichTextArea debouncing
dlh01 82aaf30
Merge redundant keyup events
dlh01 163d44b
Hook into 'customize_register' at priority 100
dlh01 2396297
Don't require each Customizer context to create a section
dlh01 2571c9d
Revert "Update test control constructors after r36689"
dlh01 85f90e7
Correctly instantiate controls in JS tests
dlh01 af2ed0f
Remove extra line
dlh01 42b1b5a
Merge branch 'master' into customizer-context
dlh01 9d94adb
Render Fieldmanager_Customize_Control::label and ::description
dlh01 85f1efb
Support core's Customizer setting validation
dlh01 c8b87f7
Clean up comments
dlh01 e27ddda
Revert "Support core's Customizer setting validation"
dlh01 a855da6
Merge branch 'master' into customizer-context
dlh01 c6ec5b3
Restore Customizer calculated context
dlh01 7cacfaa
Restore 'customize_controls_enqueue_scripts' hook
dlh01 59ad758
Update `init_display_if()` on control section expansion
dlh01 1ed2d45
Test JS against 4.6 and 4.5
dlh01 2637ef2
Remove outdated script-loading preparation
dlh01 e44f903
Improve 'X' placement after FM 1.0 CSS refresh
dlh01 fb490c0
Revert custom `alert()` on validation failure
dlh01 a34aee2
Add default method for a Setting's $validate_callback
dlh01 669eec6
Rearrange validate_callback() tests to match class
dlh01 f0be9ff
Use context's validation callback with FM settings
dlh01 93b5061
Parse query-string values before validating
dlh01 de1ab07
Reject invalid values from sanitize callback
dlh01 451935b
Test catching invalid values while sanitizing
dlh01 9a94d30
Test validating value submitted as a query string
dlh01 804f8e1
Handle calls to `wp_die()` on validation error
dlh01 38e6884
Remove outdated test
dlh01 f154b9f
Update `Fieldmanager_RichTextArea::customize_controls_print_footer_sc…
dlh01 dfcefd7
Merge branch 'master' into customizer-context
dlh01 1de94b3
Fix docs standards violations
dlh01 afca523
Handle all exceptions in `validate_callback()`
dlh01 32c61b6
Standardize on 'Customize' for objects
dlh01 11037a4
Fix `Fieldmanager_Datepicker` styles
dlh01 0053c3e
Fix inconsistent check for Customize context
dlh01 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 ); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.