From a91d3980dfee019e09b91919dac290716082203e Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 21 Sep 2017 23:03:06 +0000 Subject: [PATCH] Customize: Introduce extensible code editor Customizer control for CodeMirror. * Adds `WP_Customize_Code_Editor_Control` and `wp.customize.CodeEditorControl()`. * Control respects user preference for syntax highlighting, showing a textarea when user opts out. * Code editor control takes the ad hoc code for Additional CSS and makes it reusable and extensible, for Additional CSS in core and plugins to use (such as Jetpack). * Replace `settings` arg in `wp_enqueue_code_editor()` with separate args for `codemirror`, `csslint`, `jshint`, and `htmlhint`. * Prefix `codemirror` script and style handles with `wp-` to prevent collisions, as also the object is exported as `wp.CodeMirror` in JS. * Reduce indent size in Customizer code editor instances and Custom HTML widget to use tab size of 2 instead of 4 to save on space. See #12423, #38707, #35395. Fixes #41897. git-svn-id: https://develop.svn.wordpress.org/trunk@41558 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/css/customize-controls.css | 22 +- src/wp-admin/js/customize-controls.js | 422 +++++++++++------- .../class-wp-customize-manager.php | 35 +- ...class-wp-customize-code-editor-control.php | 104 +++++ src/wp-includes/general-template.php | 41 +- src/wp-includes/script-loader.php | 8 +- .../widgets/class-wp-widget-custom-html.php | 4 + tests/phpunit/tests/customize/manager.php | 2 +- 8 files changed, 404 insertions(+), 234 deletions(-) create mode 100644 src/wp-includes/customize/class-wp-customize-code-editor-control.php diff --git a/src/wp-admin/css/customize-controls.css b/src/wp-admin/css/customize-controls.css index c1aa128a34..cc5a37f08c 100644 --- a/src/wp-admin/css/customize-controls.css +++ b/src/wp-admin/css/customize-controls.css @@ -1164,12 +1164,26 @@ p.customize-section-description { } /** - * Custom CSS Section + * Code Editor Control and Custom CSS Section * * Modifications to the Section Container to make the textarea full-width and * full-height, if the control is the only control in the section. */ +.customize-control-code_editor textarea { + width: 100%; + font-family: Consolas, Monaco, monospace; + font-size: 12px; + padding: 6px 8px; + -moz-tab-size: 2; + -o-tab-size: 2; + tab-size: 2; +} +.customize-control-code_editor textarea, +.customize-control-code_editor .CodeMirror { + height: 14em; +} + #customize-controls .customize-section-description-container.section-meta.customize-info { border-bottom: none; } @@ -1180,13 +1194,7 @@ p.customize-section-description { #customize-control-custom_css textarea { display: block; - font-family: Consolas, Monaco, monospace; - font-size: 12px; - padding: 6px 8px; height: 500px; - -moz-tab-size: 4; - -o-tab-size: 4; - tab-size: 4; } .customize-section-description-container + #customize-control-custom_css:last-child textarea { diff --git a/src/wp-admin/js/customize-controls.js b/src/wp-admin/js/customize-controls.js index b483aea335..c80ef86e52 100644 --- a/src/wp-admin/js/customize-controls.js +++ b/src/wp-admin/js/customize-controls.js @@ -3677,6 +3677,250 @@ } }); + /** + * Class wp.customize.CodeEditorControl + * + * @since 4.9.0 + * + * @constructor + * @augments wp.customize.Control + * @augments wp.customize.Class + */ + api.CodeEditorControl = api.Control.extend({ + + /** + * Initialize the editor when the containing section is ready and expanded. + * + * @since 4.9.0 + * @returns {void} + */ + ready: function() { + var control = this; + if ( ! control.section() ) { + control.initEditor(); + return; + } + + // Wait to initialize editor until section is embedded and expanded. + api.section( control.section(), function( section ) { + section.deferred.embedded.done( function() { + var onceExpanded; + if ( section.expanded() ) { + control.initEditor(); + } else { + onceExpanded = function( isExpanded ) { + if ( isExpanded ) { + control.initEditor(); + section.expanded.unbind( onceExpanded ); + } + }; + section.expanded.bind( onceExpanded ); + } + } ); + } ); + }, + + /** + * Initialize editor. + * + * @since 4.9.0 + * @returns {void} + */ + initEditor: function() { + var control = this, element; + + element = new api.Element( control.container.find( 'textarea' ) ); + control.elements.push( element ); + element.sync( control.setting ); + element.set( control.setting() ); + + if ( control.params.editor_settings ) { + control.initSyntaxHighlightingEditor( control.params.editor_settings ); + } else { + control.initPlainTextareaEditor(); + } + }, + + /** + * Make sure editor gets focused when control is focused. + * + * @since 4.9.0 + * @param {Object} [params] - Focus params. + * @param {Function} [params.completeCallback] - Function to call when expansion is complete. + * @returns {void} + */ + focus: function( params ) { + var control = this, extendedParams = _.extend( {}, params ), originalCompleteCallback; + originalCompleteCallback = extendedParams.completeCallback; + extendedParams.completeCallback = function() { + if ( originalCompleteCallback ) { + originalCompleteCallback(); + } + if ( control.editor ) { + control.editor.codemirror.focus(); + } + }; + api.Control.prototype.focus.call( control, extendedParams ); + }, + + /** + * Initialize syntax-highlighting editor. + * + * @since 4.9.0 + * @param {object} codeEditorSettings - Code editor settings. + * @returns {void} + */ + initSyntaxHighlightingEditor: function( codeEditorSettings ) { + var control = this, $textarea = control.container.find( 'textarea' ), settings, suspendEditorUpdate = false; + + settings = _.extend( {}, codeEditorSettings, { + onTabNext: _.bind( control.onTabNext, control ), + onTabPrevious: _.bind( control.onTabPrevious, control ), + onUpdateErrorNotice: _.bind( control.onUpdateErrorNotice, control ) + }); + + control.editor = wp.codeEditor.initialize( $textarea, settings ); + + // Refresh when receiving focus. + control.editor.codemirror.on( 'focus', function( codemirror ) { + codemirror.refresh(); + }); + + /* + * When the CodeMirror instance changes, mirror to the textarea, + * where we have our "true" change event handler bound. + */ + control.editor.codemirror.on( 'change', function( codemirror ) { + suspendEditorUpdate = true; + $textarea.val( codemirror.getValue() ).trigger( 'change' ); + suspendEditorUpdate = false; + }); + + // Update CodeMirror when the setting is changed by another plugin. + control.setting.bind( function( value ) { + if ( ! suspendEditorUpdate ) { + control.editor.codemirror.setValue( value ); + } + }); + + // Prevent collapsing section when hitting Esc to tab out of editor. + control.editor.codemirror.on( 'keydown', function onKeydown( codemirror, event ) { + var escKeyCode = 27; + if ( escKeyCode === event.keyCode ) { + event.stopPropagation(); + } + }); + }, + + /** + * Handle tabbing to the field after the editor. + * + * @since 4.9.0 + * @returns {void} + */ + onTabNext: function onTabNext() { + var control = this, controls, controlIndex, section; + section = api.section( control.section() ); + controls = section.controls(); + controlIndex = controls.indexOf( control ); + if ( controls.length === controlIndex + 1 ) { + $( '#customize-footer-actions .collapse-sidebar' ).focus(); + } else { + controls[ controlIndex + 1 ].container.find( ':focusable:first' ).focus(); + } + }, + + /** + * Handle tabbing to the field before the editor. + * + * @since 4.9.0 + * @returns {void} + */ + onTabPrevious: function onTabPrevious() { + var control = this, controls, controlIndex, section; + section = api.section( control.section() ); + controls = section.controls(); + controlIndex = controls.indexOf( control ); + if ( 0 === controlIndex ) { + section.contentContainer.find( '.customize-section-title .customize-help-toggle, .customize-section-title .customize-section-description.open .section-description-close' ).last().focus(); + } else { + controls[ controlIndex - 1 ].contentContainer.find( ':focusable:first' ).focus(); + } + }, + + /** + * Update error notice. + * + * @since 4.9.0 + * @param {Array} errorAnnotations - Error annotations. + * @returns {void} + */ + onUpdateErrorNotice: function onUpdateErrorNotice( errorAnnotations ) { + var control = this, message; + control.setting.notifications.remove( 'csslint_error' ); + + if ( 0 !== errorAnnotations.length ) { + if ( 1 === errorAnnotations.length ) { + message = api.l10n.customCssError.singular.replace( '%d', '1' ); + } else { + message = api.l10n.customCssError.plural.replace( '%d', String( errorAnnotations.length ) ); + } + control.setting.notifications.add( 'csslint_error', new api.Notification( 'csslint_error', { + message: message, + type: 'error' + } ) ); + } + }, + + /** + * Initialize plain-textarea editor when syntax highlighting is disabled. + * + * @since 4.9.0 + * @returns {void} + */ + initPlainTextareaEditor: function() { + var control = this, $textarea = control.container.find( 'textarea' ), textarea = $textarea[0]; + + $textarea.on( 'blur', function onBlur() { + $textarea.data( 'next-tab-blurs', false ); + } ); + + $textarea.on( 'keydown', function onKeydown( event ) { + var selectionStart, selectionEnd, value, tabKeyCode = 9, escKeyCode = 27; + + if ( escKeyCode === event.keyCode ) { + if ( ! $textarea.data( 'next-tab-blurs' ) ) { + $textarea.data( 'next-tab-blurs', true ); + event.stopPropagation(); // Prevent collapsing the section. + } + return; + } + + // Short-circuit if tab key is not being pressed or if a modifier key *is* being pressed. + if ( tabKeyCode !== event.keyCode || event.ctrlKey || event.altKey || event.shiftKey ) { + return; + } + + // Prevent capturing Tab characters if Esc was pressed. + if ( $textarea.data( 'next-tab-blurs' ) ) { + return; + } + + selectionStart = textarea.selectionStart; + selectionEnd = textarea.selectionEnd; + value = textarea.value; + + if ( selectionStart >= 0 ) { + textarea.value = value.substring( 0, selectionStart ).concat( '\t', value.substring( selectionEnd ) ); + $textarea.selectionStart = textarea.selectionEnd = selectionStart + 1; + } + + event.stopPropagation(); + event.preventDefault(); + }); + } + }); + // Change objects contained within the main customize object to Settings. api.defaultConstructor = api.Setting; @@ -4372,7 +4616,8 @@ header: api.HeaderControl, background: api.BackgroundControl, background_position: api.BackgroundPositionControl, - theme: api.ThemeControl + theme: api.ThemeControl, + code_editor: api.CodeEditorControl }; api.panelConstructor = {}; api.sectionConstructor = { @@ -5712,7 +5957,7 @@ // Add code editor for Custom CSS. (function() { - var ready, sectionReady = $.Deferred(), controlReady = $.Deferred(); + var sectionReady = $.Deferred(); api.section( 'custom_css', function( section ) { section.deferred.embedded.done( function() { @@ -5727,16 +5972,10 @@ } }); }); - api.control( 'custom_css', function( control ) { - control.deferred.embedded.done( function() { - controlReady.resolve( control ); - }); - }); - - ready = $.when( sectionReady, controlReady ); // Set up the section desription behaviors. - ready.done( function setupSectionDescription( section, control ) { + sectionReady.done( function setupSectionDescription( section ) { + var control = api.control( 'custom_css' ); // Close the section description when clicking the close button. section.container.find( '.section-description-buttons .section-description-close' ).on( 'click', function() { @@ -5747,174 +5986,13 @@ }); // Reveal help text if setting is empty. - if ( ! control.setting.get() ) { + if ( control && ! control.setting.get() ) { section.container.find( '.section-meta .customize-section-description:first' ) .addClass( 'open' ) .show() .attr( 'aria-expanded', 'true' ); } }); - - // Set up the code editor itself. - if ( api.settings.customCss && api.settings.customCss.codeEditor ) { - - // Set up the syntax highlighting editor. - ready.done( function setupSyntaxHighlightingEditor( section, control ) { - var $textarea = control.container.find( 'textarea' ), settings, suspendEditorUpdate = false; - - // Make sure editor gets focused when control is focused. - control.focus = (function( originalFocus ) { // eslint-disable-line max-nested-callbacks - return function( params ) { // eslint-disable-line max-nested-callbacks - var extendedParams = _.extend( {}, params ), originalCompleteCallback; - originalCompleteCallback = extendedParams.completeCallback; - extendedParams.completeCallback = function() { - if ( originalCompleteCallback ) { - originalCompleteCallback(); - } - if ( control.editor ) { - control.editor.codemirror.focus(); - } - }; - originalFocus.call( this, extendedParams ); - }; - })( control.focus ); - - settings = _.extend( {}, api.settings.customCss.codeEditor, { - - /** - * Handle tabbing to the field after the editor. - * - * @returns {void} - */ - onTabNext: function onTabNext() { - var controls, controlIndex; - controls = section.controls(); - controlIndex = controls.indexOf( control ); - if ( controls.length === controlIndex + 1 ) { - $( '#customize-footer-actions .collapse-sidebar' ).focus(); - } else { - controls[ controlIndex + 1 ].container.find( ':focusable:first' ).focus(); - } - }, - - /** - * Handle tabbing to the field before the editor. - * - * @returns {void} - */ - onTabPrevious: function onTabPrevious() { - var controls, controlIndex; - controls = section.controls(); - controlIndex = controls.indexOf( control ); - if ( 0 === controlIndex ) { - section.contentContainer.find( '.customize-section-title .customize-help-toggle, .customize-section-title .customize-section-description.open .section-description-close' ).last().focus(); - } else { - controls[ controlIndex - 1 ].contentContainer.find( ':focusable:first' ).focus(); - } - }, - - /** - * Update error notice. - * - * @param {Array} errorAnnotations - Error annotations. - * @returns {void} - */ - onUpdateErrorNotice: function onUpdateErrorNotice( errorAnnotations ) { - var message; - control.setting.notifications.remove( 'csslint_error' ); - - if ( 0 !== errorAnnotations.length ) { - if ( 1 === errorAnnotations.length ) { - message = api.l10n.customCssError.singular.replace( '%d', '1' ); - } else { - message = api.l10n.customCssError.plural.replace( '%d', String( errorAnnotations.length ) ); - } - control.setting.notifications.add( 'csslint_error', new api.Notification( 'csslint_error', { - message: message, - type: 'error' - } ) ); - } - } - }); - - control.editor = wp.codeEditor.initialize( $textarea, settings ); - - // Refresh when receiving focus. - control.editor.codemirror.on( 'focus', function( codemirror ) { - codemirror.refresh(); - }); - - /* - * When the CodeMirror instance changes, mirror to the textarea, - * where we have our "true" change event handler bound. - */ - control.editor.codemirror.on( 'change', function( codemirror ) { - suspendEditorUpdate = true; - $textarea.val( codemirror.getValue() ).trigger( 'change' ); - suspendEditorUpdate = false; - }); - - // Update CodeMirror when the setting is changed by another plugin. - control.setting.bind( function( value ) { - if ( ! suspendEditorUpdate ) { - control.editor.codemirror.setValue( value ); - } - }); - - // Prevent collapsing section when hitting Esc to tab out of editor. - control.editor.codemirror.on( 'keydown', function onKeydown( codemirror, event ) { - var escKeyCode = 27; - if ( escKeyCode === event.keyCode ) { - event.stopPropagation(); - } - }); - }); - } else { - - // Allow tabs to be entered in Custom CSS textarea. - ready.done( function allowTabs( section, control ) { - - var $textarea = control.container.find( 'textarea' ), textarea = $textarea[0]; - - $textarea.on( 'blur', function onBlur() { - $textarea.data( 'next-tab-blurs', false ); - } ); - - $textarea.on( 'keydown', function onKeydown( event ) { - var selectionStart, selectionEnd, value, tabKeyCode = 9, escKeyCode = 27; - - if ( escKeyCode === event.keyCode ) { - if ( ! $textarea.data( 'next-tab-blurs' ) ) { - $textarea.data( 'next-tab-blurs', true ); - event.stopPropagation(); // Prevent collapsing the section. - } - return; - } - - // Short-circuit if tab key is not being pressed or if a modifier key *is* being pressed. - if ( tabKeyCode !== event.keyCode || event.ctrlKey || event.altKey || event.shiftKey ) { - return; - } - - // Prevent capturing Tab characters if Esc was pressed. - if ( $textarea.data( 'next-tab-blurs' ) ) { - return; - } - - selectionStart = textarea.selectionStart; - selectionEnd = textarea.selectionEnd; - value = textarea.value; - - if ( selectionStart >= 0 ) { - textarea.value = value.substring( 0, selectionStart ).concat( '\t', value.substring( selectionEnd ) ); - $textarea.selectionStart = textarea.selectionEnd = selectionStart + 1; - } - - event.stopPropagation(); - event.preventDefault(); - }); - }); - } })(); // Toggle visibility of Header Video notice when active state change. diff --git a/src/wp-includes/class-wp-customize-manager.php b/src/wp-includes/class-wp-customize-manager.php index 87a14193f3..648740df18 100644 --- a/src/wp-includes/class-wp-customize-manager.php +++ b/src/wp-includes/class-wp-customize-manager.php @@ -212,21 +212,6 @@ final class WP_Customize_Manager { */ private $_changeset_data; - /** - * Code Editor Settings for Custom CSS. - * - * This variable contains the settings returned by `wp_enqueue_code_editor()` which are then later output - * to the client in `WP_Customize_Manager::customize_pane_settings()`. A value of false means that the - * Custom CSS section or control was removed, or that the Syntax Highlighting user pref was turned off. - * - * @see wp_enqueue_code_editor() - * @see WP_Customize_Manager::enqueue_control_scripts() - * @see WP_Customize_Manager::customize_pane_settings() - * @since 4.9.0 - * @var array|false - */ - private $_custom_css_code_editor_settings = false; - /** * Constructor. * @@ -291,6 +276,7 @@ final class WP_Customize_Manager { require_once( ABSPATH . WPINC . '/customize/class-wp-customize-site-icon-control.php' ); require_once( ABSPATH . WPINC . '/customize/class-wp-customize-header-image-control.php' ); require_once( ABSPATH . WPINC . '/customize/class-wp-customize-theme-control.php' ); + require_once( ABSPATH . WPINC . '/customize/class-wp-customize-code-editor-control.php' ); require_once( ABSPATH . WPINC . '/customize/class-wp-widget-area-customize-control.php' ); require_once( ABSPATH . WPINC . '/customize/class-wp-widget-form-customize-control.php' ); require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-control.php' ); @@ -3337,12 +3323,6 @@ final class WP_Customize_Manager { foreach ( $this->controls as $control ) { $control->enqueue(); } - - if ( $this->get_section( 'custom_css' ) && $this->get_control( 'custom_css' ) ) { - $this->_custom_css_code_editor_settings = wp_enqueue_code_editor( array( - 'type' => 'text/css', - ) ); - } } /** @@ -3600,9 +3580,6 @@ final class WP_Customize_Manager { 'stylesheet' => $this->get_stylesheet(), 'active' => $this->is_theme_active(), ), - 'customCss' => array( - 'codeEditor' => $this->_custom_css_code_editor_settings, - ), 'url' => array( 'preview' => esc_url_raw( $this->get_preview_url() ), 'parent' => esc_url_raw( admin_url() ), @@ -3736,6 +3713,7 @@ final class WP_Customize_Manager { $this->register_control_type( 'WP_Customize_Cropped_Image_Control' ); $this->register_control_type( 'WP_Customize_Site_Icon_Control' ); $this->register_control_type( 'WP_Customize_Theme_Control' ); + $this->register_control_type( 'WP_Customize_Code_Editor_Control' ); /* Themes */ @@ -4239,14 +4217,11 @@ final class WP_Customize_Manager { ) ); $this->add_setting( $custom_css_setting ); - $this->add_control( 'custom_css', array( - 'type' => 'textarea', + $this->add_control( new WP_Customize_Code_Editor_Control( $this, 'custom_css', array( 'section' => 'custom_css', 'settings' => array( 'default' => $custom_css_setting->id ), - 'input_attrs' => array( - 'class' => 'code', // Ensures contents displayed as LTR instead of RTL. - ), - ) ); + 'code_type' => 'text/css', + ) ) ); } /** diff --git a/src/wp-includes/customize/class-wp-customize-code-editor-control.php b/src/wp-includes/customize/class-wp-customize-code-editor-control.php new file mode 100644 index 0000000000..b3a3c99eeb --- /dev/null +++ b/src/wp-includes/customize/class-wp-customize-code-editor-control.php @@ -0,0 +1,104 @@ +editor_settings = wp_enqueue_code_editor( array_merge( + array( + 'type' => $this->code_type, + 'codemirror' => array( + 'indentUnit' => 2, + 'tabSize' => 2, + ), + ), + $this->editor_settings + ) ); + } + + /** + * Refresh the parameters passed to the JavaScript via JSON. + * + * @since 4.9.0 + * @see WP_Customize_Control::json() + * + * @return array Array of parameters passed to the JavaScript. + */ + public function json() { + $json = parent::json(); + $json['code_type'] = $this->code_type; + $json['editor_settings'] = $this->editor_settings; + return $json; + } + + /** + * Don't render the control content from PHP, as it's rendered via JS on load. + * + * @since 4.9.0 + */ + public function render_content() {} + + /** + * Render a JS template for control display. + * + * @since 4.9.0 + */ + public function content_template() { + ?> + <# var elementIdPrefix = 'el' + String( Math.random() ); #> + <# if ( data.label ) { #> + + <# } #> + <# if ( data.description ) { #> + {{{ data.description }}} + <# } #> +
+ + $value ) { - $settings[ $key ] = array_merge( - $settings[ $key ], - $value - ); - } + foreach ( wp_array_slice_assoc( $args, array( 'codemirror', 'csslint', 'jshint', 'htmlhint' ) ) as $key => $value ) { + $settings[ $key ] = array_merge( + $settings[ $key ], + $value + ); } /** @@ -3428,11 +3429,14 @@ function wp_enqueue_code_editor( $args ) { * @param array $args { * Args passed when calling `wp_enqueue_code_editor()`. * - * @type string $type The MIME type of the file to be edited. - * @type string $file Filename being edited. - * @type array $settings Settings to merge on top of defaults which derive from `$type` or `$file` args. - * @type WP_Theme $theme Theme being edited when on theme editor. - * @type string $plugin Plugin being edited when on plugin editor. + * @type string $type The MIME type of the file to be edited. + * @type string $file Filename being edited. + * @type WP_Theme $theme Theme being edited when on theme editor. + * @type string $plugin Plugin being edited when on plugin editor. + * @type array $codemirror Additional CodeMirror setting overrides. + * @type array $csslint CSSLint rule overrides. + * @type array $jshint JSHint rule overrides. + * @type array $htmlhint JSHint rule overrides. * } */ $settings = apply_filters( 'wp_code_editor_settings', $settings, $args ); @@ -3444,9 +3448,6 @@ function wp_enqueue_code_editor( $args ) { wp_enqueue_script( 'code-editor' ); wp_enqueue_style( 'code-editor' ); - wp_enqueue_script( 'codemirror' ); - wp_enqueue_style( 'codemirror' ); - if ( isset( $settings['codemirror']['mode'] ) ) { $mode = $settings['codemirror']['mode']; if ( is_string( $mode ) ) { diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index ab230a055b..35257aedab 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -463,13 +463,13 @@ function wp_default_scripts( &$scripts ) { apply_filters( 'mejs_settings', $mejs_settings ) ); - $scripts->add( 'codemirror', '/wp-includes/js/codemirror/codemirror.min.js', array(), '5.29.1-alpha-ee20357' ); + $scripts->add( 'wp-codemirror', '/wp-includes/js/codemirror/codemirror.min.js', array(), '5.29.1-alpha-ee20357' ); $scripts->add( 'csslint', '/wp-includes/js/codemirror/csslint.js', array(), '1.0.5' ); $scripts->add( 'jshint', '/wp-includes/js/codemirror/jshint.js', array(), '2.9.5' ); $scripts->add( 'jsonlint', '/wp-includes/js/codemirror/jsonlint.js', array(), '1.6.2' ); $scripts->add( 'htmlhint', '/wp-includes/js/codemirror/htmlhint.js', array(), '0.9.14-xwp' ); $scripts->add( 'htmlhint-kses', '/wp-includes/js/codemirror/htmlhint-kses.js', array( 'htmlhint' ) ); - $scripts->add( 'code-editor', "/wp-admin/js/code-editor$suffix.js", array( 'jquery', 'codemirror' ) ); + $scripts->add( 'code-editor', "/wp-admin/js/code-editor$suffix.js", array( 'jquery', 'wp-codemirror' ) ); $scripts->add( 'wp-theme-plugin-editor', "/wp-admin/js/theme-plugin-editor$suffix.js", array( 'code-editor', 'jquery', 'jquery-ui-core', 'wp-a11y', 'underscore' ) ); did_action( 'init' ) && $scripts->add_inline_script( 'wp-theme-plugin-editor', sprintf( 'wp.themePluginEditor.l10n = %s;', wp_json_encode( wp_array_slice_assoc( /* translators: placeholder is error count */ @@ -952,7 +952,7 @@ function wp_default_styles( &$styles ) { $styles->add( 'widgets', "/wp-admin/css/widgets$suffix.css", array( 'wp-pointer' ) ); $styles->add( 'site-icon', "/wp-admin/css/site-icon$suffix.css" ); $styles->add( 'l10n', "/wp-admin/css/l10n$suffix.css" ); - $styles->add( 'code-editor', "/wp-admin/css/code-editor$suffix.css", array( 'codemirror' ) ); + $styles->add( 'code-editor', "/wp-admin/css/code-editor$suffix.css", array( 'wp-codemirror' ) ); $styles->add( 'wp-admin', false, array( 'dashicons', 'common', 'forms', 'admin-menu', 'dashboard', 'list-tables', 'edit', 'revisions', 'media', 'themes', 'about', 'nav-menus', 'widgets', 'site-icon', 'l10n' ) ); @@ -987,7 +987,7 @@ function wp_default_styles( &$styles ) { $styles->add( 'mediaelement', "/wp-includes/js/mediaelement/mediaelementplayer-legacy.min.css", array(), '4.2.5-74e01a40' ); $styles->add( 'wp-mediaelement', "/wp-includes/js/mediaelement/wp-mediaelement$suffix.css", array( 'mediaelement' ) ); $styles->add( 'thickbox', '/wp-includes/js/thickbox/thickbox.css', array( 'dashicons' ) ); - $styles->add( 'codemirror', '/wp-includes/js/codemirror/codemirror.min.css', array(), '5.29.1-alpha-ee20357' ); + $styles->add( 'wp-codemirror', '/wp-includes/js/codemirror/codemirror.min.css', array(), '5.29.1-alpha-ee20357' ); // Deprecated CSS $styles->add( 'deprecated-media', "/wp-admin/css/deprecated-media$suffix.css" ); diff --git a/src/wp-includes/widgets/class-wp-widget-custom-html.php b/src/wp-includes/widgets/class-wp-widget-custom-html.php index 7e382960fc..30e524bf35 100644 --- a/src/wp-includes/widgets/class-wp-widget-custom-html.php +++ b/src/wp-includes/widgets/class-wp-widget-custom-html.php @@ -160,6 +160,10 @@ class WP_Widget_Custom_HTML extends WP_Widget { public function enqueue_admin_scripts() { $settings = wp_enqueue_code_editor( array( 'type' => 'text/html', + 'codemirror' => array( + 'indentUnit' => 2, + 'tabSize' => 2, + ), ) ); wp_enqueue_script( 'custom-html-widgets' ); diff --git a/tests/phpunit/tests/customize/manager.php b/tests/phpunit/tests/customize/manager.php index 23aa759c8a..1955a1cd74 100644 --- a/tests/phpunit/tests/customize/manager.php +++ b/tests/phpunit/tests/customize/manager.php @@ -2351,7 +2351,7 @@ class Tests_WP_Customize_Manager extends WP_UnitTestCase { $data = json_decode( $json, true ); $this->assertNotEmpty( $data ); - $this->assertEqualSets( array( 'theme', 'url', 'browser', 'panels', 'sections', 'nonce', 'autofocus', 'documentTitleTmpl', 'previewableDevices', 'customCss', 'changeset', 'timeouts' ), array_keys( $data ) ); + $this->assertEqualSets( array( 'theme', 'url', 'browser', 'panels', 'sections', 'nonce', 'autofocus', 'documentTitleTmpl', 'previewableDevices', 'changeset', 'timeouts' ), array_keys( $data ) ); $this->assertEquals( $autofocus, $data['autofocus'] ); $this->assertArrayHasKey( 'save', $data['nonce'] ); $this->assertArrayHasKey( 'preview', $data['nonce'] );