diff --git a/src/wp-admin/js/widgets/media-widgets.js b/src/wp-admin/js/widgets/media-widgets.js
index 1c4d6fecaf..598d94d8b1 100644
--- a/src/wp-admin/js/widgets/media-widgets.js
+++ b/src/wp-admin/js/widgets/media-widgets.js
@@ -435,7 +435,8 @@ wp.mediaWidgets = ( function( $ ) {
*
* @param {Object} options - Options.
* @param {Backbone.Model} options.model - Model.
- * @param {jQuery} options.el - Control container element.
+ * @param {jQuery} options.el - Control field container element.
+ * @param {jQuery} options.syncContainer - Container element where fields are synced for the server.
* @returns {void}
*/
initialize: function initialize( options ) {
@@ -443,12 +444,19 @@ wp.mediaWidgets = ( function( $ ) {
Backbone.View.prototype.initialize.call( control, options );
- if ( ! control.el ) {
- throw new Error( 'Missing options.el' );
- }
if ( ! ( control.model instanceof component.MediaWidgetModel ) ) {
throw new Error( 'Missing options.model' );
}
+ if ( ! options.el ) {
+ throw new Error( 'Missing options.el' );
+ }
+ if ( ! options.syncContainer ) {
+ throw new Error( 'Missing options.syncContainer' );
+ }
+
+ control.syncContainer = options.syncContainer;
+
+ control.$el.addClass( 'media-widget-control' );
// Allow methods to be passed in with control context preserved.
_.bindAll( control, 'syncModelToInputs', 'render', 'updateSelectedAttachment', 'renderPreview' );
@@ -553,7 +561,7 @@ wp.mediaWidgets = ( function( $ ) {
*/
syncModelToInputs: function syncModelToInputs() {
var control = this;
- control.$el.next( '.widget-content' ).find( '.media-widget-instance-property' ).each( function() {
+ control.syncContainer.find( '.media-widget-instance-property' ).each( function() {
var input = $( this ), value;
value = control.model.get( input.data( 'property' ) );
if ( _.isUndefined( value ) ) {
@@ -1009,9 +1017,8 @@ wp.mediaWidgets = ( function( $ ) {
* @returns {void}
*/
component.handleWidgetAdded = function handleWidgetAdded( event, widgetContainer ) {
- var widgetContent, controlContainer, widgetForm, idBase, ControlConstructor, ModelConstructor, modelAttributes, widgetControl, widgetModel, widgetId, widgetInside, animatedCheckDelay = 50, renderWhenAnimationDone;
+ var fieldContainer, syncContainer, widgetForm, idBase, ControlConstructor, ModelConstructor, modelAttributes, widgetControl, widgetModel, widgetId, widgetInside, animatedCheckDelay = 50, renderWhenAnimationDone;
widgetForm = widgetContainer.find( '> .widget-inside > .form, > .widget-inside > form' ); // Note: '.form' appears in the customizer, whereas 'form' on the widgets admin screen.
- widgetContent = widgetForm.find( '> .widget-content' );
idBase = widgetForm.find( '> .id_base' ).val();
widgetId = widgetForm.find( '> .widget-id' ).val();
@@ -1038,8 +1045,9 @@ wp.mediaWidgets = ( function( $ ) {
* components", the JS template is rendered outside of the normal form
* container.
*/
- controlContainer = $( '
' );
- widgetContent.before( controlContainer );
+ fieldContainer = $( '' );
+ syncContainer = widgetContainer.find( '.widget-content:first' );
+ syncContainer.before( fieldContainer );
/*
* Sync the widget instance model attributes onto the hidden inputs that widgets currently use to store the state.
@@ -1047,7 +1055,7 @@ wp.mediaWidgets = ( function( $ ) {
* from the start, without having to sync with hidden fields. See .
*/
modelAttributes = {};
- widgetContent.find( '.media-widget-instance-property' ).each( function() {
+ syncContainer.find( '.media-widget-instance-property' ).each( function() {
var input = $( this );
modelAttributes[ input.data( 'property' ) ] = input.val();
});
@@ -1056,7 +1064,8 @@ wp.mediaWidgets = ( function( $ ) {
widgetModel = new ModelConstructor( modelAttributes );
widgetControl = new ControlConstructor({
- el: controlContainer,
+ el: fieldContainer,
+ syncContainer: syncContainer,
model: widgetModel
});
@@ -1084,6 +1093,51 @@ wp.mediaWidgets = ( function( $ ) {
component.widgetControls[ widgetModel.get( 'widget_id' ) ] = widgetControl;
};
+ /**
+ * Setup widget in accessibility mode.
+ *
+ * @returns {void}
+ */
+ component.setupAccessibleMode = function setupAccessibleMode() {
+ var widgetForm, widgetId, idBase, widgetControl, ControlConstructor, ModelConstructor, modelAttributes, fieldContainer, syncContainer;
+ widgetForm = $( '.editwidget > form' );
+ if ( 0 === widgetForm.length ) {
+ return;
+ }
+
+ idBase = widgetForm.find( '> .widget-control-actions > .id_base' ).val();
+
+ ControlConstructor = component.controlConstructors[ idBase ];
+ if ( ! ControlConstructor ) {
+ return;
+ }
+
+ widgetId = widgetForm.find( '> .widget-control-actions > .widget-id' ).val();
+
+ ModelConstructor = component.modelConstructors[ idBase ] || component.MediaWidgetModel;
+ fieldContainer = $( '' );
+ syncContainer = widgetForm.find( '> .widget-inside' );
+ syncContainer.before( fieldContainer );
+
+ modelAttributes = {};
+ syncContainer.find( '.media-widget-instance-property' ).each( function() {
+ var input = $( this );
+ modelAttributes[ input.data( 'property' ) ] = input.val();
+ });
+ modelAttributes.widget_id = widgetId;
+
+ widgetControl = new ControlConstructor({
+ el: fieldContainer,
+ syncContainer: syncContainer,
+ model: new ModelConstructor( modelAttributes )
+ });
+
+ component.modelCollection.add( [ widgetControl.model ] );
+ component.widgetControls[ widgetControl.model.get( 'widget_id' ) ] = widgetControl;
+
+ widgetControl.render();
+ };
+
/**
* Sync widget instance data sanitized from server back onto widget model.
*
@@ -1152,6 +1206,11 @@ wp.mediaWidgets = ( function( $ ) {
var widgetContainer = $( this );
component.handleWidgetAdded( new jQuery.Event( 'widget-added' ), widgetContainer );
});
+
+ // Accessibility mode.
+ $( window ).on( 'load', function() {
+ component.setupAccessibleMode();
+ });
});
};
diff --git a/src/wp-admin/js/widgets/text-widgets.js b/src/wp-admin/js/widgets/text-widgets.js
index 2108b65548..6f057044cf 100644
--- a/src/wp-admin/js/widgets/text-widgets.js
+++ b/src/wp-admin/js/widgets/text-widgets.js
@@ -24,9 +24,9 @@ wp.textWidgets = ( function( $ ) {
/**
* Initialize.
*
- * @param {Object} options - Options.
- * @param {Backbone.Model} options.model - Model.
- * @param {jQuery} options.el - Control container element.
+ * @param {Object} options - Options.
+ * @param {jQuery} options.el - Control field container element.
+ * @param {jQuery} options.syncContainer - Container element where fields are synced for the server.
* @returns {void}
*/
initialize: function initialize( options ) {
@@ -35,34 +35,25 @@ wp.textWidgets = ( function( $ ) {
if ( ! options.el ) {
throw new Error( 'Missing options.el' );
}
+ if ( ! options.syncContainer ) {
+ throw new Error( 'Missing options.syncContainer' );
+ }
Backbone.View.prototype.initialize.call( control, options );
+ control.syncContainer = options.syncContainer;
- /*
- * Create a container element for the widget control fields.
- * This is inserted into the DOM immediately before the the .widget-content
- * element because the contents of this element are essentially "managed"
- * by PHP, where each widget update cause the entire element to be emptied
- * and replaced with the rendered output of WP_Widget::form() which is
- * sent back in Ajax request made to save/update the widget instance.
- * To prevent a "flash of replaced DOM elements and re-initialized JS
- * components", the JS template is rendered outside of the normal form
- * container.
- */
- control.fieldContainer = $( '' );
- control.fieldContainer.html( wp.template( 'widget-text-control-fields' ) );
- control.widgetContentContainer = control.$el.find( '.widget-content:first' );
- control.widgetContentContainer.before( control.fieldContainer );
+ control.$el.addClass( 'text-widget-fields' );
+ control.$el.html( wp.template( 'widget-text-control-fields' ) );
control.fields = {
- title: control.fieldContainer.find( '.title' ),
- text: control.fieldContainer.find( '.text' )
+ title: control.$el.find( '.title' ),
+ text: control.$el.find( '.text' )
};
// Sync input fields to hidden sync fields which actually get sent to the server.
_.each( control.fields, function( fieldInput, fieldName ) {
fieldInput.on( 'input change', function updateSyncField() {
- var syncInput = control.widgetContentContainer.find( 'input[type=hidden].' + fieldName );
+ var syncInput = control.syncContainer.find( 'input[type=hidden].' + fieldName );
if ( syncInput.val() !== $( this ).val() ) {
syncInput.val( $( this ).val() );
syncInput.trigger( 'change' );
@@ -70,7 +61,7 @@ wp.textWidgets = ( function( $ ) {
});
// Note that syncInput cannot be re-used because it will be destroyed with each widget-updated event.
- fieldInput.val( control.widgetContentContainer.find( 'input[type=hidden].' + fieldName ).val() );
+ fieldInput.val( control.syncContainer.find( 'input[type=hidden].' + fieldName ).val() );
});
},
@@ -87,11 +78,11 @@ wp.textWidgets = ( function( $ ) {
var control = this, syncInput;
if ( ! control.fields.title.is( document.activeElement ) ) {
- syncInput = control.widgetContentContainer.find( 'input[type=hidden].title' );
+ syncInput = control.syncContainer.find( 'input[type=hidden].title' );
control.fields.title.val( syncInput.val() );
}
- syncInput = control.widgetContentContainer.find( 'input[type=hidden].text' );
+ syncInput = control.syncContainer.find( 'input[type=hidden].text' );
if ( control.fields.text.is( ':visible' ) ) {
if ( ! control.fields.text.is( document.activeElement ) ) {
control.fields.text.val( syncInput.val() );
@@ -219,7 +210,7 @@ wp.textWidgets = ( function( $ ) {
* @returns {void}
*/
component.handleWidgetAdded = function handleWidgetAdded( event, widgetContainer ) {
- var widgetForm, idBase, widgetControl, widgetId, animatedCheckDelay = 50, widgetInside, renderWhenAnimationDone;
+ var widgetForm, idBase, widgetControl, widgetId, animatedCheckDelay = 50, widgetInside, renderWhenAnimationDone, fieldContainer, syncContainer;
widgetForm = widgetContainer.find( '> .widget-inside > .form, > .widget-inside > form' ); // Note: '.form' appears in the customizer, whereas 'form' on the widgets admin screen.
idBase = widgetForm.find( '> .id_base' ).val();
@@ -228,13 +219,29 @@ wp.textWidgets = ( function( $ ) {
}
// Prevent initializing already-added widgets.
- widgetId = widgetForm.find( '> .widget-id' ).val();
+ widgetId = widgetForm.find( '.widget-id' ).val();
if ( component.widgetControls[ widgetId ] ) {
return;
}
+ /*
+ * Create a container element for the widget control fields.
+ * This is inserted into the DOM immediately before the the .widget-content
+ * element because the contents of this element are essentially "managed"
+ * by PHP, where each widget update cause the entire element to be emptied
+ * and replaced with the rendered output of WP_Widget::form() which is
+ * sent back in Ajax request made to save/update the widget instance.
+ * To prevent a "flash of replaced DOM elements and re-initialized JS
+ * components", the JS template is rendered outside of the normal form
+ * container.
+ */
+ fieldContainer = $( '' );
+ syncContainer = widgetContainer.find( '.widget-content:first' );
+ syncContainer.before( fieldContainer );
+
widgetControl = new component.TextWidgetControl({
- el: widgetContainer
+ el: fieldContainer,
+ syncContainer: syncContainer
});
component.widgetControls[ widgetId ] = widgetControl;
@@ -256,6 +263,35 @@ wp.textWidgets = ( function( $ ) {
renderWhenAnimationDone();
};
+ /**
+ * Setup widget in accessibility mode.
+ *
+ * @returns {void}
+ */
+ component.setupAccessibleMode = function setupAccessibleMode() {
+ var widgetForm, idBase, widgetControl, fieldContainer, syncContainer;
+ widgetForm = $( '.editwidget > form' );
+ if ( 0 === widgetForm.length ) {
+ return;
+ }
+
+ idBase = widgetForm.find( '> .widget-control-actions > .id_base' ).val();
+ if ( 'text' !== idBase ) {
+ return;
+ }
+
+ fieldContainer = $( '' );
+ syncContainer = widgetForm.find( '> .widget-inside' );
+ syncContainer.before( fieldContainer );
+
+ widgetControl = new component.TextWidgetControl({
+ el: fieldContainer,
+ syncContainer: syncContainer
+ });
+
+ widgetControl.initializeEditor();
+ };
+
/**
* Sync widget instance data sanitized from server back onto widget model.
*
@@ -319,6 +355,11 @@ wp.textWidgets = ( function( $ ) {
var widgetContainer = $( this );
component.handleWidgetAdded( new jQuery.Event( 'widget-added' ), widgetContainer );
});
+
+ // Accessibility mode.
+ $( window ).on( 'load', function() {
+ component.setupAccessibleMode();
+ });
});
};
diff --git a/tests/qunit/wp-admin/js/widgets/test-media-image-widget.js b/tests/qunit/wp-admin/js/widgets/test-media-image-widget.js
index 4c2c8a4378..212726e641 100644
--- a/tests/qunit/wp-admin/js/widgets/test-media-image-widget.js
+++ b/tests/qunit/wp-admin/js/widgets/test-media-image-widget.js
@@ -17,6 +17,8 @@
imageWidgetModelInstance = new wp.mediaWidgets.modelConstructors.media_image();
imageWidgetControlInstance = new ImageWidgetControl({
+ el: jQuery( '' ),
+ syncContainer: jQuery( '' ),
model: imageWidgetModelInstance
});
@@ -84,6 +86,8 @@
imageWidgetModelInstance = new wp.mediaWidgets.modelConstructors.media_image();
imageWidgetControlInstance = new wp.mediaWidgets.controlConstructors.media_image({
+ el: jQuery( '' ),
+ syncContainer: jQuery( '' ),
model: imageWidgetModelInstance
});
equal( imageWidgetControlInstance.$el.find( 'img' ).length, 0, 'No images should be rendered' );
diff --git a/tests/qunit/wp-admin/js/widgets/test-media-video-widget.js b/tests/qunit/wp-admin/js/widgets/test-media-video-widget.js
index bef61c5317..0abd1c0289 100644
--- a/tests/qunit/wp-admin/js/widgets/test-media-video-widget.js
+++ b/tests/qunit/wp-admin/js/widgets/test-media-video-widget.js
@@ -17,6 +17,8 @@
videoWidgetModelInstance = new wp.mediaWidgets.modelConstructors.media_video();
videoWidgetControlInstance = new VideoWidgetControl({
+ el: jQuery( '' ),
+ syncContainer: jQuery( '' ),
model: videoWidgetModelInstance
});
@@ -46,6 +48,8 @@
videoWidgetModelInstance = new wp.mediaWidgets.modelConstructors.media_video();
videoWidgetControlInstance = new wp.mediaWidgets.controlConstructors.media_video({
+ el: jQuery( '' ),
+ syncContainer: jQuery( '' ),
model: videoWidgetModelInstance
});
equal( videoWidgetControlInstance.$el.find( 'a' ).length, 0, 'No video links should be rendered' );