PDF rausgenommen
This commit is contained in:
456
msd2/wordpress/wp-admin/js/widgets/custom-html-widgets.js
Normal file
456
msd2/wordpress/wp-admin/js/widgets/custom-html-widgets.js
Normal file
@ -0,0 +1,456 @@
|
||||
/**
|
||||
* @output wp-admin/js/widgets/custom-html-widgets.js
|
||||
*/
|
||||
|
||||
/* global wp */
|
||||
/* eslint consistent-this: [ "error", "control" ] */
|
||||
/* eslint no-magic-numbers: ["error", { "ignore": [0,1,-1] }] */
|
||||
|
||||
/**
|
||||
* @namespace wp.customHtmlWidget
|
||||
* @memberOf wp
|
||||
*/
|
||||
wp.customHtmlWidgets = ( function( $ ) {
|
||||
'use strict';
|
||||
|
||||
var component = {
|
||||
idBases: [ 'custom_html' ],
|
||||
codeEditorSettings: {},
|
||||
l10n: {
|
||||
errorNotice: {
|
||||
singular: '',
|
||||
plural: ''
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
component.CustomHtmlWidgetControl = Backbone.View.extend(/** @lends wp.customHtmlWidgets.CustomHtmlWidgetControl.prototype */{
|
||||
|
||||
/**
|
||||
* View events.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
events: {},
|
||||
|
||||
/**
|
||||
* Text widget control.
|
||||
*
|
||||
* @constructs wp.customHtmlWidgets.CustomHtmlWidgetControl
|
||||
* @augments Backbone.View
|
||||
* @abstract
|
||||
*
|
||||
* @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 ) {
|
||||
var control = this;
|
||||
|
||||
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;
|
||||
control.widgetIdBase = control.syncContainer.parent().find( '.id_base' ).val();
|
||||
control.widgetNumber = control.syncContainer.parent().find( '.widget_number' ).val();
|
||||
control.customizeSettingId = 'widget_' + control.widgetIdBase + '[' + String( control.widgetNumber ) + ']';
|
||||
|
||||
control.$el.addClass( 'custom-html-widget-fields' );
|
||||
control.$el.html( wp.template( 'widget-custom-html-control-fields' )( { codeEditorDisabled: component.codeEditorSettings.disabled } ) );
|
||||
|
||||
control.errorNoticeContainer = control.$el.find( '.code-editor-error-container' );
|
||||
control.currentErrorAnnotations = [];
|
||||
control.saveButton = control.syncContainer.add( control.syncContainer.parent().find( '.widget-control-actions' ) ).find( '.widget-control-save, #savewidget' );
|
||||
control.saveButton.addClass( 'custom-html-widget-save-button' ); // To facilitate style targeting.
|
||||
|
||||
control.fields = {
|
||||
title: control.$el.find( '.title' ),
|
||||
content: control.$el.find( '.content' )
|
||||
};
|
||||
|
||||
// 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.syncContainer.find( '.sync-input.' + fieldName );
|
||||
if ( syncInput.val() !== fieldInput.val() ) {
|
||||
syncInput.val( fieldInput.val() );
|
||||
syncInput.trigger( 'change' );
|
||||
}
|
||||
});
|
||||
|
||||
// Note that syncInput cannot be re-used because it will be destroyed with each widget-updated event.
|
||||
fieldInput.val( control.syncContainer.find( '.sync-input.' + fieldName ).val() );
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Update input fields from the sync fields.
|
||||
*
|
||||
* This function is called at the widget-updated and widget-synced events.
|
||||
* A field will only be updated if it is not currently focused, to avoid
|
||||
* overwriting content that the user is entering.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
updateFields: function updateFields() {
|
||||
var control = this, syncInput;
|
||||
|
||||
if ( ! control.fields.title.is( document.activeElement ) ) {
|
||||
syncInput = control.syncContainer.find( '.sync-input.title' );
|
||||
control.fields.title.val( syncInput.val() );
|
||||
}
|
||||
|
||||
/*
|
||||
* Prevent updating content when the editor is focused or if there are current error annotations,
|
||||
* to prevent the editor's contents from getting sanitized as soon as a user removes focus from
|
||||
* the editor. This is particularly important for users who cannot unfiltered_html.
|
||||
*/
|
||||
control.contentUpdateBypassed = control.fields.content.is( document.activeElement ) || control.editor && control.editor.codemirror.state.focused || 0 !== control.currentErrorAnnotations.length;
|
||||
if ( ! control.contentUpdateBypassed ) {
|
||||
syncInput = control.syncContainer.find( '.sync-input.content' );
|
||||
control.fields.content.val( syncInput.val() );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Show linting error notice.
|
||||
*
|
||||
* @param {Array} errorAnnotations - Error annotations.
|
||||
* @returns {void}
|
||||
*/
|
||||
updateErrorNotice: function( errorAnnotations ) {
|
||||
var control = this, errorNotice, message = '', customizeSetting;
|
||||
|
||||
if ( 1 === errorAnnotations.length ) {
|
||||
message = component.l10n.errorNotice.singular.replace( '%d', '1' );
|
||||
} else if ( errorAnnotations.length > 1 ) {
|
||||
message = component.l10n.errorNotice.plural.replace( '%d', String( errorAnnotations.length ) );
|
||||
}
|
||||
|
||||
if ( control.fields.content[0].setCustomValidity ) {
|
||||
control.fields.content[0].setCustomValidity( message );
|
||||
}
|
||||
|
||||
if ( wp.customize && wp.customize.has( control.customizeSettingId ) ) {
|
||||
customizeSetting = wp.customize( control.customizeSettingId );
|
||||
customizeSetting.notifications.remove( 'htmlhint_error' );
|
||||
if ( 0 !== errorAnnotations.length ) {
|
||||
customizeSetting.notifications.add( 'htmlhint_error', new wp.customize.Notification( 'htmlhint_error', {
|
||||
message: message,
|
||||
type: 'error'
|
||||
} ) );
|
||||
}
|
||||
} else if ( 0 !== errorAnnotations.length ) {
|
||||
errorNotice = $( '<div class="inline notice notice-error notice-alt"></div>' );
|
||||
errorNotice.append( $( '<p></p>', {
|
||||
text: message
|
||||
} ) );
|
||||
control.errorNoticeContainer.empty();
|
||||
control.errorNoticeContainer.append( errorNotice );
|
||||
control.errorNoticeContainer.slideDown( 'fast' );
|
||||
wp.a11y.speak( message );
|
||||
} else {
|
||||
control.errorNoticeContainer.slideUp( 'fast' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize editor.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
initializeEditor: function initializeEditor() {
|
||||
var control = this, settings;
|
||||
|
||||
if ( component.codeEditorSettings.disabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
settings = _.extend( {}, component.codeEditorSettings, {
|
||||
|
||||
/**
|
||||
* Handle tabbing to the field before the editor.
|
||||
*
|
||||
* @ignore
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
onTabPrevious: function onTabPrevious() {
|
||||
control.fields.title.focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle tabbing to the field after the editor.
|
||||
*
|
||||
* @ignore
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
onTabNext: function onTabNext() {
|
||||
var tabbables = control.syncContainer.add( control.syncContainer.parent().find( '.widget-position, .widget-control-actions' ) ).find( ':tabbable' );
|
||||
tabbables.first().focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable save button and store linting errors for use in updateFields.
|
||||
*
|
||||
* @ignore
|
||||
*
|
||||
* @param {Array} errorAnnotations - Error notifications.
|
||||
* @returns {void}
|
||||
*/
|
||||
onChangeLintingErrors: function onChangeLintingErrors( errorAnnotations ) {
|
||||
control.currentErrorAnnotations = errorAnnotations;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update error notice.
|
||||
*
|
||||
* @ignore
|
||||
*
|
||||
* @param {Array} errorAnnotations - Error annotations.
|
||||
* @returns {void}
|
||||
*/
|
||||
onUpdateErrorNotice: function onUpdateErrorNotice( errorAnnotations ) {
|
||||
control.saveButton.toggleClass( 'validation-blocked disabled', errorAnnotations.length > 0 );
|
||||
control.updateErrorNotice( errorAnnotations );
|
||||
}
|
||||
});
|
||||
|
||||
control.editor = wp.codeEditor.initialize( control.fields.content, settings );
|
||||
|
||||
// Improve the editor accessibility.
|
||||
$( control.editor.codemirror.display.lineDiv )
|
||||
.attr({
|
||||
role: 'textbox',
|
||||
'aria-multiline': 'true',
|
||||
'aria-labelledby': control.fields.content[0].id + '-label',
|
||||
'aria-describedby': 'editor-keyboard-trap-help-1 editor-keyboard-trap-help-2 editor-keyboard-trap-help-3 editor-keyboard-trap-help-4'
|
||||
});
|
||||
|
||||
// Focus the editor when clicking on its label.
|
||||
$( '#' + control.fields.content[0].id + '-label' ).on( 'click', function() {
|
||||
control.editor.codemirror.focus();
|
||||
});
|
||||
|
||||
control.fields.content.on( 'change', function() {
|
||||
if ( this.value !== control.editor.codemirror.getValue() ) {
|
||||
control.editor.codemirror.setValue( this.value );
|
||||
}
|
||||
});
|
||||
control.editor.codemirror.on( 'change', function() {
|
||||
var value = control.editor.codemirror.getValue();
|
||||
if ( value !== control.fields.content.val() ) {
|
||||
control.fields.content.val( value ).trigger( 'change' );
|
||||
}
|
||||
});
|
||||
|
||||
// Make sure the editor gets updated if the content was updated on the server (sanitization) but not updated in the editor since it was focused.
|
||||
control.editor.codemirror.on( 'blur', function() {
|
||||
if ( control.contentUpdateBypassed ) {
|
||||
control.syncContainer.find( '.sync-input.content' ).trigger( 'change' );
|
||||
}
|
||||
});
|
||||
|
||||
// Prevent hitting Esc from collapsing the widget control.
|
||||
if ( wp.customize ) {
|
||||
control.editor.codemirror.on( 'keydown', function onKeydown( codemirror, event ) {
|
||||
var escKeyCode = 27;
|
||||
if ( escKeyCode === event.keyCode ) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Mapping of widget ID to instances of CustomHtmlWidgetControl subclasses.
|
||||
*
|
||||
* @alias wp.customHtmlWidgets.widgetControls
|
||||
*
|
||||
* @type {Object.<string, wp.textWidgets.CustomHtmlWidgetControl>}
|
||||
*/
|
||||
component.widgetControls = {};
|
||||
|
||||
/**
|
||||
* Handle widget being added or initialized for the first time at the widget-added event.
|
||||
*
|
||||
* @alias wp.customHtmlWidgets.handleWidgetAdded
|
||||
*
|
||||
* @param {jQuery.Event} event - Event.
|
||||
* @param {jQuery} widgetContainer - Widget container element.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
component.handleWidgetAdded = function handleWidgetAdded( event, widgetContainer ) {
|
||||
var widgetForm, idBase, widgetControl, widgetId, animatedCheckDelay = 50, 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();
|
||||
if ( -1 === component.idBases.indexOf( idBase ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent initializing already-added widgets.
|
||||
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 = $( '<div></div>' );
|
||||
syncContainer = widgetContainer.find( '.widget-content:first' );
|
||||
syncContainer.before( fieldContainer );
|
||||
|
||||
widgetControl = new component.CustomHtmlWidgetControl({
|
||||
el: fieldContainer,
|
||||
syncContainer: syncContainer
|
||||
});
|
||||
|
||||
component.widgetControls[ widgetId ] = widgetControl;
|
||||
|
||||
/*
|
||||
* Render the widget once the widget parent's container finishes animating,
|
||||
* as the widget-added event fires with a slideDown of the container.
|
||||
* This ensures that the textarea is visible and the editor can be initialized.
|
||||
*/
|
||||
renderWhenAnimationDone = function() {
|
||||
if ( ! ( wp.customize ? widgetContainer.parent().hasClass( 'expanded' ) : widgetContainer.hasClass( 'open' ) ) ) { // Core merge: The wp.customize condition can be eliminated with this change being in core: https://github.com/xwp/wordpress-develop/pull/247/commits/5322387d
|
||||
setTimeout( renderWhenAnimationDone, animatedCheckDelay );
|
||||
} else {
|
||||
widgetControl.initializeEditor();
|
||||
}
|
||||
};
|
||||
renderWhenAnimationDone();
|
||||
};
|
||||
|
||||
/**
|
||||
* Setup widget in accessibility mode.
|
||||
*
|
||||
* @alias wp.customHtmlWidgets.setupAccessibleMode
|
||||
*
|
||||
* @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 ( -1 === component.idBases.indexOf( idBase ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
fieldContainer = $( '<div></div>' );
|
||||
syncContainer = widgetForm.find( '> .widget-inside' );
|
||||
syncContainer.before( fieldContainer );
|
||||
|
||||
widgetControl = new component.CustomHtmlWidgetControl({
|
||||
el: fieldContainer,
|
||||
syncContainer: syncContainer
|
||||
});
|
||||
|
||||
widgetControl.initializeEditor();
|
||||
};
|
||||
|
||||
/**
|
||||
* Sync widget instance data sanitized from server back onto widget model.
|
||||
*
|
||||
* This gets called via the 'widget-updated' event when saving a widget from
|
||||
* the widgets admin screen and also via the 'widget-synced' event when making
|
||||
* a change to a widget in the customizer.
|
||||
*
|
||||
* @alias wp.customHtmlWidgets.handleWidgetUpdated
|
||||
*
|
||||
* @param {jQuery.Event} event - Event.
|
||||
* @param {jQuery} widgetContainer - Widget container element.
|
||||
* @returns {void}
|
||||
*/
|
||||
component.handleWidgetUpdated = function handleWidgetUpdated( event, widgetContainer ) {
|
||||
var widgetForm, widgetId, widgetControl, idBase;
|
||||
widgetForm = widgetContainer.find( '> .widget-inside > .form, > .widget-inside > form' );
|
||||
|
||||
idBase = widgetForm.find( '> .id_base' ).val();
|
||||
if ( -1 === component.idBases.indexOf( idBase ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
widgetId = widgetForm.find( '> .widget-id' ).val();
|
||||
widgetControl = component.widgetControls[ widgetId ];
|
||||
if ( ! widgetControl ) {
|
||||
return;
|
||||
}
|
||||
|
||||
widgetControl.updateFields();
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize functionality.
|
||||
*
|
||||
* This function exists to prevent the JS file from having to boot itself.
|
||||
* When WordPress enqueues this script, it should have an inline script
|
||||
* attached which calls wp.textWidgets.init().
|
||||
*
|
||||
* @alias wp.customHtmlWidgets.init
|
||||
*
|
||||
* @param {object} settings - Options for code editor, exported from PHP.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
component.init = function init( settings ) {
|
||||
var $document = $( document );
|
||||
_.extend( component.codeEditorSettings, settings );
|
||||
|
||||
$document.on( 'widget-added', component.handleWidgetAdded );
|
||||
$document.on( 'widget-synced widget-updated', component.handleWidgetUpdated );
|
||||
|
||||
/*
|
||||
* Manually trigger widget-added events for media widgets on the admin
|
||||
* screen once they are expanded. The widget-added event is not triggered
|
||||
* for each pre-existing widget on the widgets admin screen like it is
|
||||
* on the customizer. Likewise, the customizer only triggers widget-added
|
||||
* when the widget is expanded to just-in-time construct the widget form
|
||||
* when it is actually going to be displayed. So the following implements
|
||||
* the same for the widgets admin screen, to invoke the widget-added
|
||||
* handler when a pre-existing media widget is expanded.
|
||||
*/
|
||||
$( function initializeExistingWidgetContainers() {
|
||||
var widgetContainers;
|
||||
if ( 'widgets' !== window.pagenow ) {
|
||||
return;
|
||||
}
|
||||
widgetContainers = $( '.widgets-holder-wrap:not(#available-widgets)' ).find( 'div.widget' );
|
||||
widgetContainers.one( 'click.toggle-widget-expanded', function toggleWidgetExpanded() {
|
||||
var widgetContainer = $( this );
|
||||
component.handleWidgetAdded( new jQuery.Event( 'widget-added' ), widgetContainer );
|
||||
});
|
||||
|
||||
// Accessibility mode.
|
||||
$( window ).on( 'load', function() {
|
||||
component.setupAccessibleMode();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return component;
|
||||
})( jQuery );
|
1
msd2/wordpress/wp-admin/js/widgets/custom-html-widgets.min.js
vendored
Normal file
1
msd2/wordpress/wp-admin/js/widgets/custom-html-widgets.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
154
msd2/wordpress/wp-admin/js/widgets/media-audio-widget.js
Normal file
154
msd2/wordpress/wp-admin/js/widgets/media-audio-widget.js
Normal file
@ -0,0 +1,154 @@
|
||||
/**
|
||||
* @output wp-admin/js/widgets/media-audio-widget.js
|
||||
*/
|
||||
|
||||
/* eslint consistent-this: [ "error", "control" ] */
|
||||
(function( component ) {
|
||||
'use strict';
|
||||
|
||||
var AudioWidgetModel, AudioWidgetControl, AudioDetailsMediaFrame;
|
||||
|
||||
/**
|
||||
* Custom audio details frame that removes the replace-audio state.
|
||||
*
|
||||
* @class wp.mediaWidgets.controlConstructors~AudioDetailsMediaFrame
|
||||
* @augments wp.media.view.MediaFrame.AudioDetails
|
||||
*/
|
||||
AudioDetailsMediaFrame = wp.media.view.MediaFrame.AudioDetails.extend(/** @lends wp.mediaWidgets.controlConstructors~AudioDetailsMediaFrame.prototype */{
|
||||
|
||||
/**
|
||||
* Create the default states.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
createStates: function createStates() {
|
||||
this.states.add([
|
||||
new wp.media.controller.AudioDetails({
|
||||
media: this.media
|
||||
}),
|
||||
|
||||
new wp.media.controller.MediaLibrary({
|
||||
type: 'audio',
|
||||
id: 'add-audio-source',
|
||||
title: wp.media.view.l10n.audioAddSourceTitle,
|
||||
toolbar: 'add-audio-source',
|
||||
media: this.media,
|
||||
menu: false
|
||||
})
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Audio widget model.
|
||||
*
|
||||
* See WP_Widget_Audio::enqueue_admin_scripts() for amending prototype from PHP exports.
|
||||
*
|
||||
* @class wp.mediaWidgets.modelConstructors.media_audio
|
||||
* @augments wp.mediaWidgets.MediaWidgetModel
|
||||
*/
|
||||
AudioWidgetModel = component.MediaWidgetModel.extend({});
|
||||
|
||||
/**
|
||||
* Audio widget control.
|
||||
*
|
||||
* See WP_Widget_Audio::enqueue_admin_scripts() for amending prototype from PHP exports.
|
||||
*
|
||||
* @class wp.mediaWidgets.controlConstructors.media_audio
|
||||
* @augments wp.mediaWidgets.MediaWidgetControl
|
||||
*/
|
||||
AudioWidgetControl = component.MediaWidgetControl.extend(/** @lends wp.mediaWidgets.controlConstructors.media_audio.prototype */{
|
||||
|
||||
/**
|
||||
* Show display settings.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
showDisplaySettings: false,
|
||||
|
||||
/**
|
||||
* Map model props to media frame props.
|
||||
*
|
||||
* @param {Object} modelProps - Model props.
|
||||
* @returns {Object} Media frame props.
|
||||
*/
|
||||
mapModelToMediaFrameProps: function mapModelToMediaFrameProps( modelProps ) {
|
||||
var control = this, mediaFrameProps;
|
||||
mediaFrameProps = component.MediaWidgetControl.prototype.mapModelToMediaFrameProps.call( control, modelProps );
|
||||
mediaFrameProps.link = 'embed';
|
||||
return mediaFrameProps;
|
||||
},
|
||||
|
||||
/**
|
||||
* Render preview.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
renderPreview: function renderPreview() {
|
||||
var control = this, previewContainer, previewTemplate, attachmentId, attachmentUrl;
|
||||
attachmentId = control.model.get( 'attachment_id' );
|
||||
attachmentUrl = control.model.get( 'url' );
|
||||
|
||||
if ( ! attachmentId && ! attachmentUrl ) {
|
||||
return;
|
||||
}
|
||||
|
||||
previewContainer = control.$el.find( '.media-widget-preview' );
|
||||
previewTemplate = wp.template( 'wp-media-widget-audio-preview' );
|
||||
|
||||
previewContainer.html( previewTemplate({
|
||||
model: {
|
||||
attachment_id: control.model.get( 'attachment_id' ),
|
||||
src: attachmentUrl
|
||||
},
|
||||
error: control.model.get( 'error' )
|
||||
}));
|
||||
wp.mediaelement.initialize();
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the media audio-edit frame to modify the selected item.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
editMedia: function editMedia() {
|
||||
var control = this, mediaFrame, metadata, updateCallback;
|
||||
|
||||
metadata = control.mapModelToMediaFrameProps( control.model.toJSON() );
|
||||
|
||||
// Set up the media frame.
|
||||
mediaFrame = new AudioDetailsMediaFrame({
|
||||
frame: 'audio',
|
||||
state: 'audio-details',
|
||||
metadata: metadata
|
||||
});
|
||||
wp.media.frame = mediaFrame;
|
||||
mediaFrame.$el.addClass( 'media-widget' );
|
||||
|
||||
updateCallback = function( mediaFrameProps ) {
|
||||
|
||||
// Update cached attachment object to avoid having to re-fetch. This also triggers re-rendering of preview.
|
||||
control.selectedAttachment.set( mediaFrameProps );
|
||||
|
||||
control.model.set( _.extend(
|
||||
control.model.defaults(),
|
||||
control.mapMediaToModelProps( mediaFrameProps ),
|
||||
{ error: false }
|
||||
) );
|
||||
};
|
||||
|
||||
mediaFrame.state( 'audio-details' ).on( 'update', updateCallback );
|
||||
mediaFrame.state( 'replace-audio' ).on( 'replace', updateCallback );
|
||||
mediaFrame.on( 'close', function() {
|
||||
mediaFrame.detach();
|
||||
});
|
||||
|
||||
mediaFrame.open();
|
||||
}
|
||||
});
|
||||
|
||||
// Exports.
|
||||
component.controlConstructors.media_audio = AudioWidgetControl;
|
||||
component.modelConstructors.media_audio = AudioWidgetModel;
|
||||
|
||||
})( wp.mediaWidgets );
|
1
msd2/wordpress/wp-admin/js/widgets/media-audio-widget.min.js
vendored
Normal file
1
msd2/wordpress/wp-admin/js/widgets/media-audio-widget.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
!function(a){"use strict";var b,c,d;d=wp.media.view.MediaFrame.AudioDetails.extend({createStates:function(){this.states.add([new wp.media.controller.AudioDetails({media:this.media}),new wp.media.controller.MediaLibrary({type:"audio",id:"add-audio-source",title:wp.media.view.l10n.audioAddSourceTitle,toolbar:"add-audio-source",media:this.media,menu:!1})])}}),b=a.MediaWidgetModel.extend({}),c=a.MediaWidgetControl.extend({showDisplaySettings:!1,mapModelToMediaFrameProps:function(b){var c,d=this;return c=a.MediaWidgetControl.prototype.mapModelToMediaFrameProps.call(d,b),c.link="embed",c},renderPreview:function(){var a,b,c,d,e=this;c=e.model.get("attachment_id"),d=e.model.get("url"),(c||d)&&(a=e.$el.find(".media-widget-preview"),b=wp.template("wp-media-widget-audio-preview"),a.html(b({model:{attachment_id:e.model.get("attachment_id"),src:d},error:e.model.get("error")})),wp.mediaelement.initialize())},editMedia:function(){var a,b,c,e=this;b=e.mapModelToMediaFrameProps(e.model.toJSON()),a=new d({frame:"audio",state:"audio-details",metadata:b}),wp.media.frame=a,a.$el.addClass("media-widget"),c=function(a){e.selectedAttachment.set(a),e.model.set(_.extend(e.model.defaults(),e.mapMediaToModelProps(a),{error:!1}))},a.state("audio-details").on("update",c),a.state("replace-audio").on("replace",c),a.on("close",function(){a.detach()}),a.open()}}),a.controlConstructors.media_audio=c,a.modelConstructors.media_audio=b}(wp.mediaWidgets);
|
341
msd2/wordpress/wp-admin/js/widgets/media-gallery-widget.js
Normal file
341
msd2/wordpress/wp-admin/js/widgets/media-gallery-widget.js
Normal file
@ -0,0 +1,341 @@
|
||||
/**
|
||||
* @output wp-admin/js/widgets/media-gallery-widget.js
|
||||
*/
|
||||
|
||||
/* eslint consistent-this: [ "error", "control" ] */
|
||||
(function( component ) {
|
||||
'use strict';
|
||||
|
||||
var GalleryWidgetModel, GalleryWidgetControl, GalleryDetailsMediaFrame;
|
||||
|
||||
/**
|
||||
* Custom gallery details frame.
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @class wp.mediaWidgets~GalleryDetailsMediaFrame
|
||||
* @augments wp.media.view.MediaFrame.Post
|
||||
*/
|
||||
GalleryDetailsMediaFrame = wp.media.view.MediaFrame.Post.extend(/** @lends wp.mediaWidgets~GalleryDetailsMediaFrame.prototype */{
|
||||
|
||||
/**
|
||||
* Create the default states.
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @returns {void}
|
||||
*/
|
||||
createStates: function createStates() {
|
||||
this.states.add([
|
||||
new wp.media.controller.Library({
|
||||
id: 'gallery',
|
||||
title: wp.media.view.l10n.createGalleryTitle,
|
||||
priority: 40,
|
||||
toolbar: 'main-gallery',
|
||||
filterable: 'uploaded',
|
||||
multiple: 'add',
|
||||
editable: true,
|
||||
|
||||
library: wp.media.query( _.defaults({
|
||||
type: 'image'
|
||||
}, this.options.library ) )
|
||||
}),
|
||||
|
||||
// Gallery states.
|
||||
new wp.media.controller.GalleryEdit({
|
||||
library: this.options.selection,
|
||||
editing: this.options.editing,
|
||||
menu: 'gallery'
|
||||
}),
|
||||
|
||||
new wp.media.controller.GalleryAdd()
|
||||
]);
|
||||
}
|
||||
} );
|
||||
|
||||
/**
|
||||
* Gallery widget model.
|
||||
*
|
||||
* See WP_Widget_Gallery::enqueue_admin_scripts() for amending prototype from PHP exports.
|
||||
*
|
||||
* @since 4.9.0
|
||||
*
|
||||
* @class wp.mediaWidgets.modelConstructors.media_gallery
|
||||
* @augments wp.mediaWidgets.MediaWidgetModel
|
||||
*/
|
||||
GalleryWidgetModel = component.MediaWidgetModel.extend(/** @lends wp.mediaWidgets.modelConstructors.media_gallery.prototype */{} );
|
||||
|
||||
GalleryWidgetControl = component.MediaWidgetControl.extend(/** @lends wp.mediaWidgets.controlConstructors.media_gallery.prototype */{
|
||||
|
||||
/**
|
||||
* View events.
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @type {object}
|
||||
*/
|
||||
events: _.extend( {}, component.MediaWidgetControl.prototype.events, {
|
||||
'click .media-widget-gallery-preview': 'editMedia'
|
||||
} ),
|
||||
|
||||
/**
|
||||
* Gallery widget control.
|
||||
*
|
||||
* See WP_Widget_Gallery::enqueue_admin_scripts() for amending prototype from PHP exports.
|
||||
*
|
||||
* @constructs wp.mediaWidgets.controlConstructors.media_gallery
|
||||
* @augments wp.mediaWidgets.MediaWidgetControl
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @param {Object} options - Options.
|
||||
* @param {Backbone.Model} options.model - Model.
|
||||
* @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 ) {
|
||||
var control = this;
|
||||
|
||||
component.MediaWidgetControl.prototype.initialize.call( control, options );
|
||||
|
||||
_.bindAll( control, 'updateSelectedAttachments', 'handleAttachmentDestroy' );
|
||||
control.selectedAttachments = new wp.media.model.Attachments();
|
||||
control.model.on( 'change:ids', control.updateSelectedAttachments );
|
||||
control.selectedAttachments.on( 'change', control.renderPreview );
|
||||
control.selectedAttachments.on( 'reset', control.renderPreview );
|
||||
control.updateSelectedAttachments();
|
||||
|
||||
/*
|
||||
* Refresh a Gallery widget partial when the user modifies one of the selected attachments.
|
||||
* This ensures that when an attachment's caption is updated in the media modal the Gallery
|
||||
* widget in the preview will then be refreshed to show the change. Normally doing this
|
||||
* would not be necessary because all of the state should be contained inside the changeset,
|
||||
* as everything done in the Customizer should not make a change to the site unless the
|
||||
* changeset itself is published. Attachments are a current exception to this rule.
|
||||
* For a proposal to include attachments in the customized state, see #37887.
|
||||
*/
|
||||
if ( wp.customize && wp.customize.previewer ) {
|
||||
control.selectedAttachments.on( 'change', function() {
|
||||
wp.customize.previewer.send( 'refresh-widget-partial', control.model.get( 'widget_id' ) );
|
||||
} );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the selected attachments if necessary.
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @returns {void}
|
||||
*/
|
||||
updateSelectedAttachments: function updateSelectedAttachments() {
|
||||
var control = this, newIds, oldIds, removedIds, addedIds, addedQuery;
|
||||
|
||||
newIds = control.model.get( 'ids' );
|
||||
oldIds = _.pluck( control.selectedAttachments.models, 'id' );
|
||||
|
||||
removedIds = _.difference( oldIds, newIds );
|
||||
_.each( removedIds, function( removedId ) {
|
||||
control.selectedAttachments.remove( control.selectedAttachments.get( removedId ) );
|
||||
});
|
||||
|
||||
addedIds = _.difference( newIds, oldIds );
|
||||
if ( addedIds.length ) {
|
||||
addedQuery = wp.media.query({
|
||||
order: 'ASC',
|
||||
orderby: 'post__in',
|
||||
perPage: -1,
|
||||
post__in: newIds,
|
||||
query: true,
|
||||
type: 'image'
|
||||
});
|
||||
addedQuery.more().done( function() {
|
||||
control.selectedAttachments.reset( addedQuery.models );
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Render preview.
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @returns {void}
|
||||
*/
|
||||
renderPreview: function renderPreview() {
|
||||
var control = this, previewContainer, previewTemplate, data;
|
||||
|
||||
previewContainer = control.$el.find( '.media-widget-preview' );
|
||||
previewTemplate = wp.template( 'wp-media-widget-gallery-preview' );
|
||||
|
||||
data = control.previewTemplateProps.toJSON();
|
||||
data.attachments = {};
|
||||
control.selectedAttachments.each( function( attachment ) {
|
||||
data.attachments[ attachment.id ] = attachment.toJSON();
|
||||
} );
|
||||
|
||||
previewContainer.html( previewTemplate( data ) );
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine whether there are selected attachments.
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @returns {boolean} Selected.
|
||||
*/
|
||||
isSelected: function isSelected() {
|
||||
var control = this;
|
||||
|
||||
if ( control.model.get( 'error' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return control.model.get( 'ids' ).length > 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the media select frame to edit images.
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @returns {void}
|
||||
*/
|
||||
editMedia: function editMedia() {
|
||||
var control = this, selection, mediaFrame, mediaFrameProps;
|
||||
|
||||
selection = new wp.media.model.Selection( control.selectedAttachments.models, {
|
||||
multiple: true
|
||||
});
|
||||
|
||||
mediaFrameProps = control.mapModelToMediaFrameProps( control.model.toJSON() );
|
||||
selection.gallery = new Backbone.Model( mediaFrameProps );
|
||||
if ( mediaFrameProps.size ) {
|
||||
control.displaySettings.set( 'size', mediaFrameProps.size );
|
||||
}
|
||||
mediaFrame = new GalleryDetailsMediaFrame({
|
||||
frame: 'manage',
|
||||
text: control.l10n.add_to_widget,
|
||||
selection: selection,
|
||||
mimeType: control.mime_type,
|
||||
selectedDisplaySettings: control.displaySettings,
|
||||
showDisplaySettings: control.showDisplaySettings,
|
||||
metadata: mediaFrameProps,
|
||||
editing: true,
|
||||
multiple: true,
|
||||
state: 'gallery-edit'
|
||||
});
|
||||
wp.media.frame = mediaFrame; // See wp.media().
|
||||
|
||||
// Handle selection of a media item.
|
||||
mediaFrame.on( 'update', function onUpdate( newSelection ) {
|
||||
var state = mediaFrame.state(), resultSelection;
|
||||
|
||||
resultSelection = newSelection || state.get( 'selection' );
|
||||
if ( ! resultSelection ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy orderby_random from gallery state.
|
||||
if ( resultSelection.gallery ) {
|
||||
control.model.set( control.mapMediaToModelProps( resultSelection.gallery.toJSON() ) );
|
||||
}
|
||||
|
||||
// Directly update selectedAttachments to prevent needing to do additional request.
|
||||
control.selectedAttachments.reset( resultSelection.models );
|
||||
|
||||
// Update models in the widget instance.
|
||||
control.model.set( {
|
||||
ids: _.pluck( resultSelection.models, 'id' )
|
||||
} );
|
||||
} );
|
||||
|
||||
mediaFrame.$el.addClass( 'media-widget' );
|
||||
mediaFrame.open();
|
||||
|
||||
if ( selection ) {
|
||||
selection.on( 'destroy', control.handleAttachmentDestroy );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the media select frame to chose an item.
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @returns {void}
|
||||
*/
|
||||
selectMedia: function selectMedia() {
|
||||
var control = this, selection, mediaFrame, mediaFrameProps;
|
||||
selection = new wp.media.model.Selection( control.selectedAttachments.models, {
|
||||
multiple: true
|
||||
});
|
||||
|
||||
mediaFrameProps = control.mapModelToMediaFrameProps( control.model.toJSON() );
|
||||
if ( mediaFrameProps.size ) {
|
||||
control.displaySettings.set( 'size', mediaFrameProps.size );
|
||||
}
|
||||
mediaFrame = new GalleryDetailsMediaFrame({
|
||||
frame: 'select',
|
||||
text: control.l10n.add_to_widget,
|
||||
selection: selection,
|
||||
mimeType: control.mime_type,
|
||||
selectedDisplaySettings: control.displaySettings,
|
||||
showDisplaySettings: control.showDisplaySettings,
|
||||
metadata: mediaFrameProps,
|
||||
state: 'gallery'
|
||||
});
|
||||
wp.media.frame = mediaFrame; // See wp.media().
|
||||
|
||||
// Handle selection of a media item.
|
||||
mediaFrame.on( 'update', function onUpdate( newSelection ) {
|
||||
var state = mediaFrame.state(), resultSelection;
|
||||
|
||||
resultSelection = newSelection || state.get( 'selection' );
|
||||
if ( ! resultSelection ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy orderby_random from gallery state.
|
||||
if ( resultSelection.gallery ) {
|
||||
control.model.set( control.mapMediaToModelProps( resultSelection.gallery.toJSON() ) );
|
||||
}
|
||||
|
||||
// Directly update selectedAttachments to prevent needing to do additional request.
|
||||
control.selectedAttachments.reset( resultSelection.models );
|
||||
|
||||
// Update widget instance.
|
||||
control.model.set( {
|
||||
ids: _.pluck( resultSelection.models, 'id' )
|
||||
} );
|
||||
} );
|
||||
|
||||
mediaFrame.$el.addClass( 'media-widget' );
|
||||
mediaFrame.open();
|
||||
|
||||
if ( selection ) {
|
||||
selection.on( 'destroy', control.handleAttachmentDestroy );
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure focus is set inside of modal so that hitting Esc will close
|
||||
* the modal and not inadvertently cause the widget to collapse in the customizer.
|
||||
*/
|
||||
mediaFrame.$el.find( ':focusable:first' ).focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear the selected attachment when it is deleted in the media select frame.
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @param {wp.media.models.Attachment} attachment - Attachment.
|
||||
* @returns {void}
|
||||
*/
|
||||
handleAttachmentDestroy: function handleAttachmentDestroy( attachment ) {
|
||||
var control = this;
|
||||
control.model.set( {
|
||||
ids: _.difference(
|
||||
control.model.get( 'ids' ),
|
||||
[ attachment.id ]
|
||||
)
|
||||
} );
|
||||
}
|
||||
} );
|
||||
|
||||
// Exports.
|
||||
component.controlConstructors.media_gallery = GalleryWidgetControl;
|
||||
component.modelConstructors.media_gallery = GalleryWidgetModel;
|
||||
|
||||
})( wp.mediaWidgets );
|
1
msd2/wordpress/wp-admin/js/widgets/media-gallery-widget.min.js
vendored
Normal file
1
msd2/wordpress/wp-admin/js/widgets/media-gallery-widget.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
!function(a){"use strict";var b,c,d;d=wp.media.view.MediaFrame.Post.extend({createStates:function(){this.states.add([new wp.media.controller.Library({id:"gallery",title:wp.media.view.l10n.createGalleryTitle,priority:40,toolbar:"main-gallery",filterable:"uploaded",multiple:"add",editable:!0,library:wp.media.query(_.defaults({type:"image"},this.options.library))}),new wp.media.controller.GalleryEdit({library:this.options.selection,editing:this.options.editing,menu:"gallery"}),new wp.media.controller.GalleryAdd])}}),b=a.MediaWidgetModel.extend({}),c=a.MediaWidgetControl.extend({events:_.extend({},a.MediaWidgetControl.prototype.events,{"click .media-widget-gallery-preview":"editMedia"}),initialize:function(b){var c=this;a.MediaWidgetControl.prototype.initialize.call(c,b),_.bindAll(c,"updateSelectedAttachments","handleAttachmentDestroy"),c.selectedAttachments=new wp.media.model.Attachments,c.model.on("change:ids",c.updateSelectedAttachments),c.selectedAttachments.on("change",c.renderPreview),c.selectedAttachments.on("reset",c.renderPreview),c.updateSelectedAttachments(),wp.customize&&wp.customize.previewer&&c.selectedAttachments.on("change",function(){wp.customize.previewer.send("refresh-widget-partial",c.model.get("widget_id"))})},updateSelectedAttachments:function(){var a,b,c,d,e,f=this;a=f.model.get("ids"),b=_.pluck(f.selectedAttachments.models,"id"),c=_.difference(b,a),_.each(c,function(a){f.selectedAttachments.remove(f.selectedAttachments.get(a))}),d=_.difference(a,b),d.length&&(e=wp.media.query({order:"ASC",orderby:"post__in",perPage:-1,post__in:a,query:!0,type:"image"}),e.more().done(function(){f.selectedAttachments.reset(e.models)}))},renderPreview:function(){var a,b,c,d=this;a=d.$el.find(".media-widget-preview"),b=wp.template("wp-media-widget-gallery-preview"),c=d.previewTemplateProps.toJSON(),c.attachments={},d.selectedAttachments.each(function(a){c.attachments[a.id]=a.toJSON()}),a.html(b(c))},isSelected:function(){var a=this;return!a.model.get("error")&&a.model.get("ids").length>0},editMedia:function(){var a,b,c,e=this;a=new wp.media.model.Selection(e.selectedAttachments.models,{multiple:!0}),c=e.mapModelToMediaFrameProps(e.model.toJSON()),a.gallery=new Backbone.Model(c),c.size&&e.displaySettings.set("size",c.size),b=new d({frame:"manage",text:e.l10n.add_to_widget,selection:a,mimeType:e.mime_type,selectedDisplaySettings:e.displaySettings,showDisplaySettings:e.showDisplaySettings,metadata:c,editing:!0,multiple:!0,state:"gallery-edit"}),wp.media.frame=b,b.on("update",function(a){var c,d=b.state();c=a||d.get("selection"),c&&(c.gallery&&e.model.set(e.mapMediaToModelProps(c.gallery.toJSON())),e.selectedAttachments.reset(c.models),e.model.set({ids:_.pluck(c.models,"id")}))}),b.$el.addClass("media-widget"),b.open(),a&&a.on("destroy",e.handleAttachmentDestroy)},selectMedia:function(){var a,b,c,e=this;a=new wp.media.model.Selection(e.selectedAttachments.models,{multiple:!0}),c=e.mapModelToMediaFrameProps(e.model.toJSON()),c.size&&e.displaySettings.set("size",c.size),b=new d({frame:"select",text:e.l10n.add_to_widget,selection:a,mimeType:e.mime_type,selectedDisplaySettings:e.displaySettings,showDisplaySettings:e.showDisplaySettings,metadata:c,state:"gallery"}),wp.media.frame=b,b.on("update",function(a){var c,d=b.state();c=a||d.get("selection"),c&&(c.gallery&&e.model.set(e.mapMediaToModelProps(c.gallery.toJSON())),e.selectedAttachments.reset(c.models),e.model.set({ids:_.pluck(c.models,"id")}))}),b.$el.addClass("media-widget"),b.open(),a&&a.on("destroy",e.handleAttachmentDestroy),b.$el.find(":focusable:first").focus()},handleAttachmentDestroy:function(a){var b=this;b.model.set({ids:_.difference(b.model.get("ids"),[a.id])})}}),a.controlConstructors.media_gallery=c,a.modelConstructors.media_gallery=b}(wp.mediaWidgets);
|
170
msd2/wordpress/wp-admin/js/widgets/media-image-widget.js
Normal file
170
msd2/wordpress/wp-admin/js/widgets/media-image-widget.js
Normal file
@ -0,0 +1,170 @@
|
||||
/**
|
||||
* @output wp-admin/js/widgets/media-image-widget.js
|
||||
*/
|
||||
|
||||
/* eslint consistent-this: [ "error", "control" ] */
|
||||
(function( component, $ ) {
|
||||
'use strict';
|
||||
|
||||
var ImageWidgetModel, ImageWidgetControl;
|
||||
|
||||
/**
|
||||
* Image widget model.
|
||||
*
|
||||
* See WP_Widget_Media_Image::enqueue_admin_scripts() for amending prototype from PHP exports.
|
||||
*
|
||||
* @class wp.mediaWidgets.modelConstructors.media_image
|
||||
* @augments wp.mediaWidgets.MediaWidgetModel
|
||||
*/
|
||||
ImageWidgetModel = component.MediaWidgetModel.extend({});
|
||||
|
||||
/**
|
||||
* Image widget control.
|
||||
*
|
||||
* See WP_Widget_Media_Image::enqueue_admin_scripts() for amending prototype from PHP exports.
|
||||
*
|
||||
* @class wp.mediaWidgets.controlConstructors.media_audio
|
||||
* @augments wp.mediaWidgets.MediaWidgetControl
|
||||
*/
|
||||
ImageWidgetControl = component.MediaWidgetControl.extend(/** @lends wp.mediaWidgets.controlConstructors.media_image.prototype */{
|
||||
|
||||
/**
|
||||
* View events.
|
||||
*
|
||||
* @type {object}
|
||||
*/
|
||||
events: _.extend( {}, component.MediaWidgetControl.prototype.events, {
|
||||
'click .media-widget-preview.populated': 'editMedia'
|
||||
} ),
|
||||
|
||||
/**
|
||||
* Render preview.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
renderPreview: function renderPreview() {
|
||||
var control = this, previewContainer, previewTemplate, fieldsContainer, fieldsTemplate, linkInput;
|
||||
if ( ! control.model.get( 'attachment_id' ) && ! control.model.get( 'url' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
previewContainer = control.$el.find( '.media-widget-preview' );
|
||||
previewTemplate = wp.template( 'wp-media-widget-image-preview' );
|
||||
previewContainer.html( previewTemplate( control.previewTemplateProps.toJSON() ) );
|
||||
previewContainer.addClass( 'populated' );
|
||||
|
||||
linkInput = control.$el.find( '.link' );
|
||||
if ( ! linkInput.is( document.activeElement ) ) {
|
||||
fieldsContainer = control.$el.find( '.media-widget-fields' );
|
||||
fieldsTemplate = wp.template( 'wp-media-widget-image-fields' );
|
||||
fieldsContainer.html( fieldsTemplate( control.previewTemplateProps.toJSON() ) );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the media image-edit frame to modify the selected item.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
editMedia: function editMedia() {
|
||||
var control = this, mediaFrame, updateCallback, defaultSync, metadata;
|
||||
|
||||
metadata = control.mapModelToMediaFrameProps( control.model.toJSON() );
|
||||
|
||||
// Needed or else none will not be selected if linkUrl is not also empty.
|
||||
if ( 'none' === metadata.link ) {
|
||||
metadata.linkUrl = '';
|
||||
}
|
||||
|
||||
// Set up the media frame.
|
||||
mediaFrame = wp.media({
|
||||
frame: 'image',
|
||||
state: 'image-details',
|
||||
metadata: metadata
|
||||
});
|
||||
mediaFrame.$el.addClass( 'media-widget' );
|
||||
|
||||
updateCallback = function() {
|
||||
var mediaProps, linkType;
|
||||
|
||||
// Update cached attachment object to avoid having to re-fetch. This also triggers re-rendering of preview.
|
||||
mediaProps = mediaFrame.state().attributes.image.toJSON();
|
||||
linkType = mediaProps.link;
|
||||
mediaProps.link = mediaProps.linkUrl;
|
||||
control.selectedAttachment.set( mediaProps );
|
||||
control.displaySettings.set( 'link', linkType );
|
||||
|
||||
control.model.set( _.extend(
|
||||
control.mapMediaToModelProps( mediaProps ),
|
||||
{ error: false }
|
||||
) );
|
||||
};
|
||||
|
||||
mediaFrame.state( 'image-details' ).on( 'update', updateCallback );
|
||||
mediaFrame.state( 'replace-image' ).on( 'replace', updateCallback );
|
||||
|
||||
// Disable syncing of attachment changes back to server. See <https://core.trac.wordpress.org/ticket/40403>.
|
||||
defaultSync = wp.media.model.Attachment.prototype.sync;
|
||||
wp.media.model.Attachment.prototype.sync = function rejectedSync() {
|
||||
return $.Deferred().rejectWith( this ).promise();
|
||||
};
|
||||
mediaFrame.on( 'close', function onClose() {
|
||||
mediaFrame.detach();
|
||||
wp.media.model.Attachment.prototype.sync = defaultSync;
|
||||
});
|
||||
|
||||
mediaFrame.open();
|
||||
},
|
||||
|
||||
/**
|
||||
* Get props which are merged on top of the model when an embed is chosen (as opposed to an attachment).
|
||||
*
|
||||
* @returns {Object} Reset/override props.
|
||||
*/
|
||||
getEmbedResetProps: function getEmbedResetProps() {
|
||||
return _.extend(
|
||||
component.MediaWidgetControl.prototype.getEmbedResetProps.call( this ),
|
||||
{
|
||||
size: 'full',
|
||||
width: 0,
|
||||
height: 0
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the instance props from the media selection frame.
|
||||
*
|
||||
* Prevent the image_title attribute from being initially set when adding an image from the media library.
|
||||
*
|
||||
* @param {wp.media.view.MediaFrame.Select} mediaFrame - Select frame.
|
||||
* @returns {Object} Props.
|
||||
*/
|
||||
getModelPropsFromMediaFrame: function getModelPropsFromMediaFrame( mediaFrame ) {
|
||||
var control = this;
|
||||
return _.omit(
|
||||
component.MediaWidgetControl.prototype.getModelPropsFromMediaFrame.call( control, mediaFrame ),
|
||||
'image_title'
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Map model props to preview template props.
|
||||
*
|
||||
* @returns {Object} Preview template props.
|
||||
*/
|
||||
mapModelToPreviewTemplateProps: function mapModelToPreviewTemplateProps() {
|
||||
var control = this, previewTemplateProps, url;
|
||||
url = control.model.get( 'url' );
|
||||
previewTemplateProps = component.MediaWidgetControl.prototype.mapModelToPreviewTemplateProps.call( control );
|
||||
previewTemplateProps.currentFilename = url ? url.replace( /\?.*$/, '' ).replace( /^.+\//, '' ) : '';
|
||||
previewTemplateProps.link_url = control.model.get( 'link_url' );
|
||||
return previewTemplateProps;
|
||||
}
|
||||
});
|
||||
|
||||
// Exports.
|
||||
component.controlConstructors.media_image = ImageWidgetControl;
|
||||
component.modelConstructors.media_image = ImageWidgetModel;
|
||||
|
||||
})( wp.mediaWidgets, jQuery );
|
1
msd2/wordpress/wp-admin/js/widgets/media-image-widget.min.js
vendored
Normal file
1
msd2/wordpress/wp-admin/js/widgets/media-image-widget.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
!function(a,b){"use strict";var c,d;c=a.MediaWidgetModel.extend({}),d=a.MediaWidgetControl.extend({events:_.extend({},a.MediaWidgetControl.prototype.events,{"click .media-widget-preview.populated":"editMedia"}),renderPreview:function(){var a,b,c,d,e,f=this;(f.model.get("attachment_id")||f.model.get("url"))&&(a=f.$el.find(".media-widget-preview"),b=wp.template("wp-media-widget-image-preview"),a.html(b(f.previewTemplateProps.toJSON())),a.addClass("populated"),e=f.$el.find(".link"),e.is(document.activeElement)||(c=f.$el.find(".media-widget-fields"),d=wp.template("wp-media-widget-image-fields"),c.html(d(f.previewTemplateProps.toJSON()))))},editMedia:function(){var a,c,d,e,f=this;e=f.mapModelToMediaFrameProps(f.model.toJSON()),"none"===e.link&&(e.linkUrl=""),a=wp.media({frame:"image",state:"image-details",metadata:e}),a.$el.addClass("media-widget"),c=function(){var b,c;b=a.state().attributes.image.toJSON(),c=b.link,b.link=b.linkUrl,f.selectedAttachment.set(b),f.displaySettings.set("link",c),f.model.set(_.extend(f.mapMediaToModelProps(b),{error:!1}))},a.state("image-details").on("update",c),a.state("replace-image").on("replace",c),d=wp.media.model.Attachment.prototype.sync,wp.media.model.Attachment.prototype.sync=function(){return b.Deferred().rejectWith(this).promise()},a.on("close",function(){a.detach(),wp.media.model.Attachment.prototype.sync=d}),a.open()},getEmbedResetProps:function(){return _.extend(a.MediaWidgetControl.prototype.getEmbedResetProps.call(this),{size:"full",width:0,height:0})},getModelPropsFromMediaFrame:function(b){var c=this;return _.omit(a.MediaWidgetControl.prototype.getModelPropsFromMediaFrame.call(c,b),"image_title")},mapModelToPreviewTemplateProps:function(){var b,c,d=this;return c=d.model.get("url"),b=a.MediaWidgetControl.prototype.mapModelToPreviewTemplateProps.call(d),b.currentFilename=c?c.replace(/\?.*$/,"").replace(/^.+\//,""):"",b.link_url=d.model.get("link_url"),b}}),a.controlConstructors.media_image=d,a.modelConstructors.media_image=c}(wp.mediaWidgets,jQuery);
|
256
msd2/wordpress/wp-admin/js/widgets/media-video-widget.js
Normal file
256
msd2/wordpress/wp-admin/js/widgets/media-video-widget.js
Normal file
@ -0,0 +1,256 @@
|
||||
/**
|
||||
* @output wp-admin/js/widgets/media-video-widget.js
|
||||
*/
|
||||
|
||||
/* eslint consistent-this: [ "error", "control" ] */
|
||||
(function( component ) {
|
||||
'use strict';
|
||||
|
||||
var VideoWidgetModel, VideoWidgetControl, VideoDetailsMediaFrame;
|
||||
|
||||
/**
|
||||
* Custom video details frame that removes the replace-video state.
|
||||
*
|
||||
* @class wp.mediaWidgets.controlConstructors~VideoDetailsMediaFrame
|
||||
* @augments wp.media.view.MediaFrame.VideoDetails
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
VideoDetailsMediaFrame = wp.media.view.MediaFrame.VideoDetails.extend(/** @lends wp.mediaWidgets.controlConstructors~VideoDetailsMediaFrame.prototype */{
|
||||
|
||||
/**
|
||||
* Create the default states.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
createStates: function createStates() {
|
||||
this.states.add([
|
||||
new wp.media.controller.VideoDetails({
|
||||
media: this.media
|
||||
}),
|
||||
|
||||
new wp.media.controller.MediaLibrary({
|
||||
type: 'video',
|
||||
id: 'add-video-source',
|
||||
title: wp.media.view.l10n.videoAddSourceTitle,
|
||||
toolbar: 'add-video-source',
|
||||
media: this.media,
|
||||
menu: false
|
||||
}),
|
||||
|
||||
new wp.media.controller.MediaLibrary({
|
||||
type: 'text',
|
||||
id: 'add-track',
|
||||
title: wp.media.view.l10n.videoAddTrackTitle,
|
||||
toolbar: 'add-track',
|
||||
media: this.media,
|
||||
menu: 'video-details'
|
||||
})
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Video widget model.
|
||||
*
|
||||
* See WP_Widget_Video::enqueue_admin_scripts() for amending prototype from PHP exports.
|
||||
*
|
||||
* @class wp.mediaWidgets.modelConstructors.media_video
|
||||
* @augments wp.mediaWidgets.MediaWidgetModel
|
||||
*/
|
||||
VideoWidgetModel = component.MediaWidgetModel.extend({});
|
||||
|
||||
/**
|
||||
* Video widget control.
|
||||
*
|
||||
* See WP_Widget_Video::enqueue_admin_scripts() for amending prototype from PHP exports.
|
||||
*
|
||||
* @class wp.mediaWidgets.controlConstructors.media_video
|
||||
* @augments wp.mediaWidgets.MediaWidgetControl
|
||||
*/
|
||||
VideoWidgetControl = component.MediaWidgetControl.extend(/** @lends wp.mediaWidgets.controlConstructors.media_video.prototype */{
|
||||
|
||||
/**
|
||||
* Show display settings.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
showDisplaySettings: false,
|
||||
|
||||
/**
|
||||
* Cache of oembed responses.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
oembedResponses: {},
|
||||
|
||||
/**
|
||||
* Map model props to media frame props.
|
||||
*
|
||||
* @param {Object} modelProps - Model props.
|
||||
* @returns {Object} Media frame props.
|
||||
*/
|
||||
mapModelToMediaFrameProps: function mapModelToMediaFrameProps( modelProps ) {
|
||||
var control = this, mediaFrameProps;
|
||||
mediaFrameProps = component.MediaWidgetControl.prototype.mapModelToMediaFrameProps.call( control, modelProps );
|
||||
mediaFrameProps.link = 'embed';
|
||||
return mediaFrameProps;
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetches embed data for external videos.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
fetchEmbed: function fetchEmbed() {
|
||||
var control = this, url;
|
||||
url = control.model.get( 'url' );
|
||||
|
||||
// If we already have a local cache of the embed response, return.
|
||||
if ( control.oembedResponses[ url ] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If there is an in-flight embed request, abort it.
|
||||
if ( control.fetchEmbedDfd && 'pending' === control.fetchEmbedDfd.state() ) {
|
||||
control.fetchEmbedDfd.abort();
|
||||
}
|
||||
|
||||
control.fetchEmbedDfd = wp.apiRequest({
|
||||
url: wp.media.view.settings.oEmbedProxyUrl,
|
||||
data: {
|
||||
url: control.model.get( 'url' ),
|
||||
maxwidth: control.model.get( 'width' ),
|
||||
maxheight: control.model.get( 'height' ),
|
||||
discover: false
|
||||
},
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
context: control
|
||||
});
|
||||
|
||||
control.fetchEmbedDfd.done( function( response ) {
|
||||
control.oembedResponses[ url ] = response;
|
||||
control.renderPreview();
|
||||
});
|
||||
|
||||
control.fetchEmbedDfd.fail( function() {
|
||||
control.oembedResponses[ url ] = null;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether a url is a supported external host.
|
||||
*
|
||||
* @deprecated since 4.9.
|
||||
*
|
||||
* @returns {boolean} Whether url is a supported video host.
|
||||
*/
|
||||
isHostedVideo: function isHostedVideo() {
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Render preview.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
renderPreview: function renderPreview() {
|
||||
var control = this, previewContainer, previewTemplate, attachmentId, attachmentUrl, poster, html = '', isOEmbed = false, mime, error, urlParser, matches;
|
||||
attachmentId = control.model.get( 'attachment_id' );
|
||||
attachmentUrl = control.model.get( 'url' );
|
||||
error = control.model.get( 'error' );
|
||||
|
||||
if ( ! attachmentId && ! attachmentUrl ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify the selected attachment mime is supported.
|
||||
mime = control.selectedAttachment.get( 'mime' );
|
||||
if ( mime && attachmentId ) {
|
||||
if ( ! _.contains( _.values( wp.media.view.settings.embedMimes ), mime ) ) {
|
||||
error = 'unsupported_file_type';
|
||||
}
|
||||
} else if ( ! attachmentId ) {
|
||||
urlParser = document.createElement( 'a' );
|
||||
urlParser.href = attachmentUrl;
|
||||
matches = urlParser.pathname.toLowerCase().match( /\.(\w+)$/ );
|
||||
if ( matches ) {
|
||||
if ( ! _.contains( _.keys( wp.media.view.settings.embedMimes ), matches[1] ) ) {
|
||||
error = 'unsupported_file_type';
|
||||
}
|
||||
} else {
|
||||
isOEmbed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isOEmbed ) {
|
||||
control.fetchEmbed();
|
||||
if ( control.oembedResponses[ attachmentUrl ] ) {
|
||||
poster = control.oembedResponses[ attachmentUrl ].thumbnail_url;
|
||||
html = control.oembedResponses[ attachmentUrl ].html.replace( /\swidth="\d+"/, ' width="100%"' ).replace( /\sheight="\d+"/, '' );
|
||||
}
|
||||
}
|
||||
|
||||
previewContainer = control.$el.find( '.media-widget-preview' );
|
||||
previewTemplate = wp.template( 'wp-media-widget-video-preview' );
|
||||
|
||||
previewContainer.html( previewTemplate({
|
||||
model: {
|
||||
attachment_id: attachmentId,
|
||||
html: html,
|
||||
src: attachmentUrl,
|
||||
poster: poster
|
||||
},
|
||||
is_oembed: isOEmbed,
|
||||
error: error
|
||||
}));
|
||||
wp.mediaelement.initialize();
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the media image-edit frame to modify the selected item.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
editMedia: function editMedia() {
|
||||
var control = this, mediaFrame, metadata, updateCallback;
|
||||
|
||||
metadata = control.mapModelToMediaFrameProps( control.model.toJSON() );
|
||||
|
||||
// Set up the media frame.
|
||||
mediaFrame = new VideoDetailsMediaFrame({
|
||||
frame: 'video',
|
||||
state: 'video-details',
|
||||
metadata: metadata
|
||||
});
|
||||
wp.media.frame = mediaFrame;
|
||||
mediaFrame.$el.addClass( 'media-widget' );
|
||||
|
||||
updateCallback = function( mediaFrameProps ) {
|
||||
|
||||
// Update cached attachment object to avoid having to re-fetch. This also triggers re-rendering of preview.
|
||||
control.selectedAttachment.set( mediaFrameProps );
|
||||
|
||||
control.model.set( _.extend(
|
||||
_.omit( control.model.defaults(), 'title' ),
|
||||
control.mapMediaToModelProps( mediaFrameProps ),
|
||||
{ error: false }
|
||||
) );
|
||||
};
|
||||
|
||||
mediaFrame.state( 'video-details' ).on( 'update', updateCallback );
|
||||
mediaFrame.state( 'replace-video' ).on( 'replace', updateCallback );
|
||||
mediaFrame.on( 'close', function() {
|
||||
mediaFrame.detach();
|
||||
});
|
||||
|
||||
mediaFrame.open();
|
||||
}
|
||||
});
|
||||
|
||||
// Exports.
|
||||
component.controlConstructors.media_video = VideoWidgetControl;
|
||||
component.modelConstructors.media_video = VideoWidgetModel;
|
||||
|
||||
})( wp.mediaWidgets );
|
1
msd2/wordpress/wp-admin/js/widgets/media-video-widget.min.js
vendored
Normal file
1
msd2/wordpress/wp-admin/js/widgets/media-video-widget.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
!function(a){"use strict";var b,c,d;d=wp.media.view.MediaFrame.VideoDetails.extend({createStates:function(){this.states.add([new wp.media.controller.VideoDetails({media:this.media}),new wp.media.controller.MediaLibrary({type:"video",id:"add-video-source",title:wp.media.view.l10n.videoAddSourceTitle,toolbar:"add-video-source",media:this.media,menu:!1}),new wp.media.controller.MediaLibrary({type:"text",id:"add-track",title:wp.media.view.l10n.videoAddTrackTitle,toolbar:"add-track",media:this.media,menu:"video-details"})])}}),b=a.MediaWidgetModel.extend({}),c=a.MediaWidgetControl.extend({showDisplaySettings:!1,oembedResponses:{},mapModelToMediaFrameProps:function(b){var c,d=this;return c=a.MediaWidgetControl.prototype.mapModelToMediaFrameProps.call(d,b),c.link="embed",c},fetchEmbed:function(){var a,b=this;a=b.model.get("url"),b.oembedResponses[a]||(b.fetchEmbedDfd&&"pending"===b.fetchEmbedDfd.state()&&b.fetchEmbedDfd.abort(),b.fetchEmbedDfd=wp.apiRequest({url:wp.media.view.settings.oEmbedProxyUrl,data:{url:b.model.get("url"),maxwidth:b.model.get("width"),maxheight:b.model.get("height"),discover:!1},type:"GET",dataType:"json",context:b}),b.fetchEmbedDfd.done(function(c){b.oembedResponses[a]=c,b.renderPreview()}),b.fetchEmbedDfd.fail(function(){b.oembedResponses[a]=null}))},isHostedVideo:function(){return!0},renderPreview:function(){var a,b,c,d,e,f,g,h,i,j=this,k="",l=!1;c=j.model.get("attachment_id"),d=j.model.get("url"),g=j.model.get("error"),(c||d)&&(f=j.selectedAttachment.get("mime"),f&&c?_.contains(_.values(wp.media.view.settings.embedMimes),f)||(g="unsupported_file_type"):c||(h=document.createElement("a"),h.href=d,i=h.pathname.toLowerCase().match(/\.(\w+)$/),i?_.contains(_.keys(wp.media.view.settings.embedMimes),i[1])||(g="unsupported_file_type"):l=!0),l&&(j.fetchEmbed(),j.oembedResponses[d]&&(e=j.oembedResponses[d].thumbnail_url,k=j.oembedResponses[d].html.replace(/\swidth="\d+"/,' width="100%"').replace(/\sheight="\d+"/,""))),a=j.$el.find(".media-widget-preview"),b=wp.template("wp-media-widget-video-preview"),a.html(b({model:{attachment_id:c,html:k,src:d,poster:e},is_oembed:l,error:g})),wp.mediaelement.initialize())},editMedia:function(){var a,b,c,e=this;b=e.mapModelToMediaFrameProps(e.model.toJSON()),a=new d({frame:"video",state:"video-details",metadata:b}),wp.media.frame=a,a.$el.addClass("media-widget"),c=function(a){e.selectedAttachment.set(a),e.model.set(_.extend(_.omit(e.model.defaults(),"title"),e.mapMediaToModelProps(a),{error:!1}))},a.state("video-details").on("update",c),a.state("replace-video").on("replace",c),a.on("close",function(){a.detach()}),a.open()}}),a.controlConstructors.media_video=c,a.modelConstructors.media_video=b}(wp.mediaWidgets);
|
1334
msd2/wordpress/wp-admin/js/widgets/media-widgets.js
Normal file
1334
msd2/wordpress/wp-admin/js/widgets/media-widgets.js
Normal file
File diff suppressed because it is too large
Load Diff
1
msd2/wordpress/wp-admin/js/widgets/media-widgets.min.js
vendored
Normal file
1
msd2/wordpress/wp-admin/js/widgets/media-widgets.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
552
msd2/wordpress/wp-admin/js/widgets/text-widgets.js
Normal file
552
msd2/wordpress/wp-admin/js/widgets/text-widgets.js
Normal file
@ -0,0 +1,552 @@
|
||||
/**
|
||||
* @output wp-admin/js/widgets/text-widgets.js
|
||||
*/
|
||||
|
||||
/* global tinymce, switchEditors */
|
||||
/* eslint consistent-this: [ "error", "control" ] */
|
||||
|
||||
/**
|
||||
* @namespace wp.textWidgets
|
||||
*/
|
||||
wp.textWidgets = ( function( $ ) {
|
||||
'use strict';
|
||||
|
||||
var component = {
|
||||
dismissedPointers: [],
|
||||
idBases: [ 'text' ]
|
||||
};
|
||||
|
||||
component.TextWidgetControl = Backbone.View.extend(/** @lends wp.textWidgets.TextWidgetControl.prototype */{
|
||||
|
||||
/**
|
||||
* View events.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
events: {},
|
||||
|
||||
/**
|
||||
* Text widget control.
|
||||
*
|
||||
* @constructs wp.textWidgets.TextWidgetControl
|
||||
* @augments Backbone.View
|
||||
* @abstract
|
||||
*
|
||||
* @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 ) {
|
||||
var control = this;
|
||||
|
||||
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;
|
||||
|
||||
control.$el.addClass( 'text-widget-fields' );
|
||||
control.$el.html( wp.template( 'widget-text-control-fields' ) );
|
||||
|
||||
control.customHtmlWidgetPointer = control.$el.find( '.wp-pointer.custom-html-widget-pointer' );
|
||||
if ( control.customHtmlWidgetPointer.length ) {
|
||||
control.customHtmlWidgetPointer.find( '.close' ).on( 'click', function( event ) {
|
||||
event.preventDefault();
|
||||
control.customHtmlWidgetPointer.hide();
|
||||
$( '#' + control.fields.text.attr( 'id' ) + '-html' ).focus();
|
||||
control.dismissPointers( [ 'text_widget_custom_html' ] );
|
||||
});
|
||||
control.customHtmlWidgetPointer.find( '.add-widget' ).on( 'click', function( event ) {
|
||||
event.preventDefault();
|
||||
control.customHtmlWidgetPointer.hide();
|
||||
control.openAvailableWidgetsPanel();
|
||||
});
|
||||
}
|
||||
|
||||
control.pasteHtmlPointer = control.$el.find( '.wp-pointer.paste-html-pointer' );
|
||||
if ( control.pasteHtmlPointer.length ) {
|
||||
control.pasteHtmlPointer.find( '.close' ).on( 'click', function( event ) {
|
||||
event.preventDefault();
|
||||
control.pasteHtmlPointer.hide();
|
||||
control.editor.focus();
|
||||
control.dismissPointers( [ 'text_widget_custom_html', 'text_widget_paste_html' ] );
|
||||
});
|
||||
}
|
||||
|
||||
control.fields = {
|
||||
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.syncContainer.find( '.sync-input.' + fieldName );
|
||||
if ( syncInput.val() !== fieldInput.val() ) {
|
||||
syncInput.val( fieldInput.val() );
|
||||
syncInput.trigger( 'change' );
|
||||
}
|
||||
});
|
||||
|
||||
// Note that syncInput cannot be re-used because it will be destroyed with each widget-updated event.
|
||||
fieldInput.val( control.syncContainer.find( '.sync-input.' + fieldName ).val() );
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Dismiss pointers for Custom HTML widget.
|
||||
*
|
||||
* @since 4.8.1
|
||||
*
|
||||
* @param {Array} pointers Pointer IDs to dismiss.
|
||||
* @returns {void}
|
||||
*/
|
||||
dismissPointers: function dismissPointers( pointers ) {
|
||||
_.each( pointers, function( pointer ) {
|
||||
wp.ajax.post( 'dismiss-wp-pointer', {
|
||||
pointer: pointer
|
||||
});
|
||||
component.dismissedPointers.push( pointer );
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Open available widgets panel.
|
||||
*
|
||||
* @since 4.8.1
|
||||
* @returns {void}
|
||||
*/
|
||||
openAvailableWidgetsPanel: function openAvailableWidgetsPanel() {
|
||||
var sidebarControl;
|
||||
wp.customize.section.each( function( section ) {
|
||||
if ( section.extended( wp.customize.Widgets.SidebarSection ) && section.expanded() ) {
|
||||
sidebarControl = wp.customize.control( 'sidebars_widgets[' + section.params.sidebarId + ']' );
|
||||
}
|
||||
});
|
||||
if ( ! sidebarControl ) {
|
||||
return;
|
||||
}
|
||||
setTimeout( function() { // Timeout to prevent click event from causing panel to immediately collapse.
|
||||
wp.customize.Widgets.availableWidgetsPanel.open( sidebarControl );
|
||||
wp.customize.Widgets.availableWidgetsPanel.$search.val( 'HTML' ).trigger( 'keyup' );
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Update input fields from the sync fields.
|
||||
*
|
||||
* This function is called at the widget-updated and widget-synced events.
|
||||
* A field will only be updated if it is not currently focused, to avoid
|
||||
* overwriting content that the user is entering.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
updateFields: function updateFields() {
|
||||
var control = this, syncInput;
|
||||
|
||||
if ( ! control.fields.title.is( document.activeElement ) ) {
|
||||
syncInput = control.syncContainer.find( '.sync-input.title' );
|
||||
control.fields.title.val( syncInput.val() );
|
||||
}
|
||||
|
||||
syncInput = control.syncContainer.find( '.sync-input.text' );
|
||||
if ( control.fields.text.is( ':visible' ) ) {
|
||||
if ( ! control.fields.text.is( document.activeElement ) ) {
|
||||
control.fields.text.val( syncInput.val() );
|
||||
}
|
||||
} else if ( control.editor && ! control.editorFocused && syncInput.val() !== control.fields.text.val() ) {
|
||||
control.editor.setContent( wp.editor.autop( syncInput.val() ) );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize editor.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
initializeEditor: function initializeEditor() {
|
||||
var control = this, changeDebounceDelay = 1000, id, textarea, triggerChangeIfDirty, restoreTextMode = false, needsTextareaChangeTrigger = false, previousValue;
|
||||
textarea = control.fields.text;
|
||||
id = textarea.attr( 'id' );
|
||||
previousValue = textarea.val();
|
||||
|
||||
/**
|
||||
* Trigger change if dirty.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
triggerChangeIfDirty = function() {
|
||||
var updateWidgetBuffer = 300; // See wp.customize.Widgets.WidgetControl._setupUpdateUI() which uses 250ms for updateWidgetDebounced.
|
||||
if ( control.editor.isDirty() ) {
|
||||
|
||||
/*
|
||||
* Account for race condition in customizer where user clicks Save & Publish while
|
||||
* focus was just previously given to the editor. Since updates to the editor
|
||||
* are debounced at 1 second and since widget input changes are only synced to
|
||||
* settings after 250ms, the customizer needs to be put into the processing
|
||||
* state during the time between the change event is triggered and updateWidget
|
||||
* logic starts. Note that the debounced update-widget request should be able
|
||||
* to be removed with the removal of the update-widget request entirely once
|
||||
* widgets are able to mutate their own instance props directly in JS without
|
||||
* having to make server round-trips to call the respective WP_Widget::update()
|
||||
* callbacks. See <https://core.trac.wordpress.org/ticket/33507>.
|
||||
*/
|
||||
if ( wp.customize && wp.customize.state ) {
|
||||
wp.customize.state( 'processing' ).set( wp.customize.state( 'processing' ).get() + 1 );
|
||||
_.delay( function() {
|
||||
wp.customize.state( 'processing' ).set( wp.customize.state( 'processing' ).get() - 1 );
|
||||
}, updateWidgetBuffer );
|
||||
}
|
||||
|
||||
if ( ! control.editor.isHidden() ) {
|
||||
control.editor.save();
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger change on textarea when it has changed so the widget can enter a dirty state.
|
||||
if ( needsTextareaChangeTrigger && previousValue !== textarea.val() ) {
|
||||
textarea.trigger( 'change' );
|
||||
needsTextareaChangeTrigger = false;
|
||||
previousValue = textarea.val();
|
||||
}
|
||||
};
|
||||
|
||||
// Just-in-time force-update the hidden input fields.
|
||||
control.syncContainer.closest( '.widget' ).find( '[name=savewidget]:first' ).on( 'click', function onClickSaveButton() {
|
||||
triggerChangeIfDirty();
|
||||
});
|
||||
|
||||
/**
|
||||
* Build (or re-build) the visual editor.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
function buildEditor() {
|
||||
var editor, onInit, showPointerElement;
|
||||
|
||||
// Abort building if the textarea is gone, likely due to the widget having been deleted entirely.
|
||||
if ( ! document.getElementById( id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The user has disabled TinyMCE.
|
||||
if ( typeof window.tinymce === 'undefined' ) {
|
||||
wp.editor.initialize( id, {
|
||||
quicktags: true,
|
||||
mediaButtons: true
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Destroy any existing editor so that it can be re-initialized after a widget-updated event.
|
||||
if ( tinymce.get( id ) ) {
|
||||
restoreTextMode = tinymce.get( id ).isHidden();
|
||||
wp.editor.remove( id );
|
||||
}
|
||||
|
||||
// Add or enable the `wpview` plugin.
|
||||
$( document ).one( 'wp-before-tinymce-init.text-widget-init', function( event, init ) {
|
||||
// If somebody has removed all plugins, they must have a good reason.
|
||||
// Keep it that way.
|
||||
if ( ! init.plugins ) {
|
||||
return;
|
||||
} else if ( ! /\bwpview\b/.test( init.plugins ) ) {
|
||||
init.plugins += ',wpview';
|
||||
}
|
||||
} );
|
||||
|
||||
wp.editor.initialize( id, {
|
||||
tinymce: {
|
||||
wpautop: true
|
||||
},
|
||||
quicktags: true,
|
||||
mediaButtons: true
|
||||
});
|
||||
|
||||
/**
|
||||
* Show a pointer, focus on dismiss, and speak the contents for a11y.
|
||||
*
|
||||
* @param {jQuery} pointerElement Pointer element.
|
||||
* @returns {void}
|
||||
*/
|
||||
showPointerElement = function( pointerElement ) {
|
||||
pointerElement.show();
|
||||
pointerElement.find( '.close' ).focus();
|
||||
wp.a11y.speak( pointerElement.find( 'h3, p' ).map( function() {
|
||||
return $( this ).text();
|
||||
} ).get().join( '\n\n' ) );
|
||||
};
|
||||
|
||||
editor = window.tinymce.get( id );
|
||||
if ( ! editor ) {
|
||||
throw new Error( 'Failed to initialize editor' );
|
||||
}
|
||||
onInit = function() {
|
||||
|
||||
// When a widget is moved in the DOM the dynamically-created TinyMCE iframe will be destroyed and has to be re-built.
|
||||
$( editor.getWin() ).on( 'unload', function() {
|
||||
_.defer( buildEditor );
|
||||
});
|
||||
|
||||
// If a prior mce instance was replaced, and it was in text mode, toggle to text mode.
|
||||
if ( restoreTextMode ) {
|
||||
switchEditors.go( id, 'html' );
|
||||
}
|
||||
|
||||
// Show the pointer.
|
||||
$( '#' + id + '-html' ).on( 'click', function() {
|
||||
control.pasteHtmlPointer.hide(); // Hide the HTML pasting pointer.
|
||||
|
||||
if ( -1 !== component.dismissedPointers.indexOf( 'text_widget_custom_html' ) ) {
|
||||
return;
|
||||
}
|
||||
showPointerElement( control.customHtmlWidgetPointer );
|
||||
});
|
||||
|
||||
// Hide the pointer when switching tabs.
|
||||
$( '#' + id + '-tmce' ).on( 'click', function() {
|
||||
control.customHtmlWidgetPointer.hide();
|
||||
});
|
||||
|
||||
// Show pointer when pasting HTML.
|
||||
editor.on( 'pastepreprocess', function( event ) {
|
||||
var content = event.content;
|
||||
if ( -1 !== component.dismissedPointers.indexOf( 'text_widget_paste_html' ) || ! content || ! /<\w+.*?>/.test( content ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Show the pointer after a slight delay so the user sees what they pasted.
|
||||
_.delay( function() {
|
||||
showPointerElement( control.pasteHtmlPointer );
|
||||
}, 250 );
|
||||
});
|
||||
};
|
||||
|
||||
if ( editor.initialized ) {
|
||||
onInit();
|
||||
} else {
|
||||
editor.on( 'init', onInit );
|
||||
}
|
||||
|
||||
control.editorFocused = false;
|
||||
|
||||
editor.on( 'focus', function onEditorFocus() {
|
||||
control.editorFocused = true;
|
||||
});
|
||||
editor.on( 'paste', function onEditorPaste() {
|
||||
editor.setDirty( true ); // Because pasting doesn't currently set the dirty state.
|
||||
triggerChangeIfDirty();
|
||||
});
|
||||
editor.on( 'NodeChange', function onNodeChange() {
|
||||
needsTextareaChangeTrigger = true;
|
||||
});
|
||||
editor.on( 'NodeChange', _.debounce( triggerChangeIfDirty, changeDebounceDelay ) );
|
||||
editor.on( 'blur hide', function onEditorBlur() {
|
||||
control.editorFocused = false;
|
||||
triggerChangeIfDirty();
|
||||
});
|
||||
|
||||
control.editor = editor;
|
||||
}
|
||||
|
||||
buildEditor();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Mapping of widget ID to instances of TextWidgetControl subclasses.
|
||||
*
|
||||
* @memberOf wp.textWidgets
|
||||
*
|
||||
* @type {Object.<string, wp.textWidgets.TextWidgetControl>}
|
||||
*/
|
||||
component.widgetControls = {};
|
||||
|
||||
/**
|
||||
* Handle widget being added or initialized for the first time at the widget-added event.
|
||||
*
|
||||
* @memberOf wp.textWidgets
|
||||
*
|
||||
* @param {jQuery.Event} event - Event.
|
||||
* @param {jQuery} widgetContainer - Widget container element.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
component.handleWidgetAdded = function handleWidgetAdded( event, widgetContainer ) {
|
||||
var widgetForm, idBase, widgetControl, widgetId, animatedCheckDelay = 50, 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();
|
||||
if ( -1 === component.idBases.indexOf( idBase ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent initializing already-added widgets.
|
||||
widgetId = widgetForm.find( '.widget-id' ).val();
|
||||
if ( component.widgetControls[ widgetId ] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Bypass using TinyMCE when widget is in legacy mode.
|
||||
if ( ! widgetForm.find( '.visual' ).val() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a container element for the widget control fields.
|
||||
* This is inserted into the DOM immediately before 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 = $( '<div></div>' );
|
||||
syncContainer = widgetContainer.find( '.widget-content:first' );
|
||||
syncContainer.before( fieldContainer );
|
||||
|
||||
widgetControl = new component.TextWidgetControl({
|
||||
el: fieldContainer,
|
||||
syncContainer: syncContainer
|
||||
});
|
||||
|
||||
component.widgetControls[ widgetId ] = widgetControl;
|
||||
|
||||
/*
|
||||
* Render the widget once the widget parent's container finishes animating,
|
||||
* as the widget-added event fires with a slideDown of the container.
|
||||
* This ensures that the textarea is visible and an iframe can be embedded
|
||||
* with TinyMCE being able to set contenteditable on it.
|
||||
*/
|
||||
renderWhenAnimationDone = function() {
|
||||
if ( ! widgetContainer.hasClass( 'open' ) ) {
|
||||
setTimeout( renderWhenAnimationDone, animatedCheckDelay );
|
||||
} else {
|
||||
widgetControl.initializeEditor();
|
||||
}
|
||||
};
|
||||
renderWhenAnimationDone();
|
||||
};
|
||||
|
||||
/**
|
||||
* Setup widget in accessibility mode.
|
||||
*
|
||||
* @memberOf wp.textWidgets
|
||||
*
|
||||
* @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 ( -1 === component.idBases.indexOf( idBase ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Bypass using TinyMCE when widget is in legacy mode.
|
||||
if ( ! widgetForm.find( '.visual' ).val() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
fieldContainer = $( '<div></div>' );
|
||||
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.
|
||||
*
|
||||
* This gets called via the 'widget-updated' event when saving a widget from
|
||||
* the widgets admin screen and also via the 'widget-synced' event when making
|
||||
* a change to a widget in the customizer.
|
||||
*
|
||||
* @memberOf wp.textWidgets
|
||||
*
|
||||
* @param {jQuery.Event} event - Event.
|
||||
* @param {jQuery} widgetContainer - Widget container element.
|
||||
* @returns {void}
|
||||
*/
|
||||
component.handleWidgetUpdated = function handleWidgetUpdated( event, widgetContainer ) {
|
||||
var widgetForm, widgetId, widgetControl, idBase;
|
||||
widgetForm = widgetContainer.find( '> .widget-inside > .form, > .widget-inside > form' );
|
||||
|
||||
idBase = widgetForm.find( '> .id_base' ).val();
|
||||
if ( -1 === component.idBases.indexOf( idBase ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
widgetId = widgetForm.find( '> .widget-id' ).val();
|
||||
widgetControl = component.widgetControls[ widgetId ];
|
||||
if ( ! widgetControl ) {
|
||||
return;
|
||||
}
|
||||
|
||||
widgetControl.updateFields();
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize functionality.
|
||||
*
|
||||
* This function exists to prevent the JS file from having to boot itself.
|
||||
* When WordPress enqueues this script, it should have an inline script
|
||||
* attached which calls wp.textWidgets.init().
|
||||
*
|
||||
* @memberOf wp.textWidgets
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
component.init = function init() {
|
||||
var $document = $( document );
|
||||
$document.on( 'widget-added', component.handleWidgetAdded );
|
||||
$document.on( 'widget-synced widget-updated', component.handleWidgetUpdated );
|
||||
|
||||
/*
|
||||
* Manually trigger widget-added events for media widgets on the admin
|
||||
* screen once they are expanded. The widget-added event is not triggered
|
||||
* for each pre-existing widget on the widgets admin screen like it is
|
||||
* on the customizer. Likewise, the customizer only triggers widget-added
|
||||
* when the widget is expanded to just-in-time construct the widget form
|
||||
* when it is actually going to be displayed. So the following implements
|
||||
* the same for the widgets admin screen, to invoke the widget-added
|
||||
* handler when a pre-existing media widget is expanded.
|
||||
*/
|
||||
$( function initializeExistingWidgetContainers() {
|
||||
var widgetContainers;
|
||||
if ( 'widgets' !== window.pagenow ) {
|
||||
return;
|
||||
}
|
||||
widgetContainers = $( '.widgets-holder-wrap:not(#available-widgets)' ).find( 'div.widget' );
|
||||
widgetContainers.one( 'click.toggle-widget-expanded', function toggleWidgetExpanded() {
|
||||
var widgetContainer = $( this );
|
||||
component.handleWidgetAdded( new jQuery.Event( 'widget-added' ), widgetContainer );
|
||||
});
|
||||
|
||||
// Accessibility mode.
|
||||
$( window ).on( 'load', function() {
|
||||
component.setupAccessibleMode();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return component;
|
||||
})( jQuery );
|
1
msd2/wordpress/wp-admin/js/widgets/text-widgets.min.js
vendored
Normal file
1
msd2/wordpress/wp-admin/js/widgets/text-widgets.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user