Make WordPress Core

Ticket #41319: class-wp-editor.php

File class-wp-editor.php, 60.7 KB (added by upadalavipul, 9 years ago)
Line 
1<?php
2/**
3 * Facilitates adding of the WordPress editor as used on the Write and Edit screens.
4 *
5 * @package WordPress
6 * @since 3.3.0
7 *
8 * Private, not included by default. See wp_editor() in wp-includes/general-template.php.
9 */
10
11final class _WP_Editors {
12        public static $mce_locale;
13
14        private static $mce_settings = array();
15        private static $qt_settings = array();
16        private static $plugins = array();
17        private static $qt_buttons = array();
18        private static $ext_plugins;
19        private static $baseurl;
20        private static $first_init;
21        private static $this_tinymce = false;
22        private static $this_quicktags = false;
23        private static $has_tinymce = false;
24        private static $has_quicktags = false;
25        private static $has_medialib = false;
26        private static $editor_buttons_css = true;
27        private static $drag_drop_upload = false;
28        private static $old_dfw_compat = false;
29        private static $translation;
30        private static $tinymce_scripts_printed = false;
31        private static $link_dialog_printed = false;
32
33        private function __construct() {}
34
35        /**
36         * Parse default arguments for the editor instance.
37         *
38         * @static
39         * @param string $editor_id ID for the current editor instance.
40         * @param array  $settings {
41         *     Array of editor arguments.
42         *
43         *     @type bool       $wpautop           Whether to use wpautop(). Default true.
44         *     @type bool       $media_buttons     Whether to show the Add Media/other media buttons.
45         *     @type string     $default_editor    When both TinyMCE and Quicktags are used, set which
46         *                                         editor is shown on page load. Default empty.
47         *     @type bool       $drag_drop_upload  Whether to enable drag & drop on the editor uploading. Default false.
48         *                                         Requires the media modal.
49         *     @type string     $textarea_name     Give the textarea a unique name here. Square brackets
50         *                                         can be used here. Default $editor_id.
51         *     @type int        $textarea_rows     Number rows in the editor textarea. Default 20.
52         *     @type string|int $tabindex          Tabindex value to use. Default empty.
53         *     @type string     $tabfocus_elements The previous and next element ID to move the focus to
54         *                                         when pressing the Tab key in TinyMCE. Default ':prev,:next'.
55         *     @type string     $editor_css        Intended for extra styles for both Visual and Text editors.
56         *                                         Should include `<style>` tags, and can use "scoped". Default empty.
57         *     @type string     $editor_class      Extra classes to add to the editor textarea element. Default empty.
58         *     @type bool       $teeny             Whether to output the minimal editor config. Examples include
59         *                                         Press This and the Comment editor. Default false.
60         *     @type bool       $dfw               Deprecated in 4.1. Since 4.3 used only to enqueue wp-fullscreen-stub.js
61         *                                         for backward compatibility.
62         *     @type bool|array $tinymce           Whether to load TinyMCE. Can be used to pass settings directly to
63         *                                         TinyMCE using an array. Default true.
64         *     @type bool|array $quicktags         Whether to load Quicktags. Can be used to pass settings directly to
65         *                                         Quicktags using an array. Default true.
66         * }
67         * @return array Parsed arguments array.
68         */
69        public static function parse_settings( $editor_id, $settings ) {
70
71                /**
72                 * Filters the wp_editor() settings.
73                 *
74                 * @since 4.0.0
75                 *
76                 * @see _WP_Editors()::parse_settings()
77                 *
78                 * @param array  $settings  Array of editor arguments.
79                 * @param string $editor_id ID for the current editor instance.
80                 */
81                $settings = apply_filters( 'wp_editor_settings', $settings, $editor_id );
82
83                $set = wp_parse_args( $settings, array(
84                        'wpautop'             => true,
85                        'media_buttons'       => true,
86                        'default_editor'      => '',
87                        'drag_drop_upload'    => false,
88                        'textarea_name'       => $editor_id,
89                        'textarea_rows'       => 20,
90                        'tabindex'            => '',
91                        'tabfocus_elements'   => ':prev,:next',
92                        'editor_css'          => '',
93                        'editor_class'        => '',
94                        'teeny'               => false,
95                        'dfw'                 => false,
96                        '_content_editor_dfw' => false,
97                        'tinymce'             => true,
98                        'quicktags'           => true
99                ) );
100
101                self::$this_tinymce = ( $set['tinymce'] && user_can_richedit() );
102
103                if ( self::$this_tinymce ) {
104                        if ( false !== strpos( $editor_id, '[' ) ) {
105                                self::$this_tinymce = false;
106                                _deprecated_argument( 'wp_editor()', '3.9.0', 'TinyMCE editor IDs cannot have brackets.' );
107                        }
108                }
109
110                self::$this_quicktags = (bool) $set['quicktags'];
111
112                if ( self::$this_tinymce )
113                        self::$has_tinymce = true;
114
115                if ( self::$this_quicktags )
116                        self::$has_quicktags = true;
117
118                if ( $set['dfw'] ) {
119                        self::$old_dfw_compat = true;
120                }
121
122                if ( empty( $set['editor_height'] ) )
123                        return $set;
124
125                if ( 'content' === $editor_id && empty( $set['tinymce']['wp_autoresize_on'] ) ) {
126                        // A cookie (set when a user resizes the editor) overrides the height.
127                        $cookie = (int) get_user_setting( 'ed_size' );
128
129                        if ( $cookie )
130                                $set['editor_height'] = $cookie;
131                }
132
133                if ( $set['editor_height'] < 50 )
134                        $set['editor_height'] = 50;
135                elseif ( $set['editor_height'] > 5000 )
136                        $set['editor_height'] = 5000;
137
138                return $set;
139        }
140
141        /**
142         * Outputs the HTML for a single instance of the editor.
143         *
144         * @static
145         * @param string $content The initial content of the editor.
146         * @param string $editor_id ID for the textarea and TinyMCE and Quicktags instances (can contain only ASCII letters and numbers).
147         * @param array $settings See _WP_Editors()::parse_settings() for description.
148         */
149        public static function editor( $content, $editor_id, $settings = array() ) {
150                $set = self::parse_settings( $editor_id, $settings );
151                $editor_class = ' class="' . trim( esc_attr( $set['editor_class'] ) . ' wp-editor-area' ) . '"';
152                $tabindex = $set['tabindex'] ? ' tabindex="' . (int) $set['tabindex'] . '"' : '';
153                $default_editor = 'html';
154                $buttons = $autocomplete = '';
155                $editor_id_attr = esc_attr( $editor_id );
156
157                if ( $set['drag_drop_upload'] ) {
158                        self::$drag_drop_upload = true;
159                }
160
161                if ( ! empty( $set['editor_height'] ) ) {
162                        $height = ' style="height: ' . (int) $set['editor_height'] . 'px"';
163                } else {
164                        $height = ' rows="' . (int) $set['textarea_rows'] . '"';
165                }
166
167                if ( ! current_user_can( 'upload_files' ) ) {
168                        $set['media_buttons'] = false;
169                }
170
171                if ( self::$this_tinymce ) {
172                        $autocomplete = ' autocomplete="off"';
173
174                        if ( self::$this_quicktags ) {
175                                $default_editor = $set['default_editor'] ? $set['default_editor'] : wp_default_editor();
176                                // 'html' is used for the "Text" editor tab.
177                                if ( 'html' !== $default_editor ) {
178                                        $default_editor = 'tinymce';
179                                }
180
181                                $buttons .= '<button type="button" id="' . $editor_id_attr . '-tmce" class="wp-switch-editor switch-tmce"' .
182                                        ' data-wp-editor-id="' . $editor_id_attr . '">' . __('Visual') . "</button>\n";
183                                $buttons .= '<button type="button" id="' . $editor_id_attr . '-html" class="wp-switch-editor switch-html"' .
184                                        ' data-wp-editor-id="' . $editor_id_attr . '">' . _x( 'Text', 'Name for the Text editor tab (formerly HTML)' ) . "</button>\n";
185                        } else {
186                                $default_editor = 'tinymce';
187                        }
188                }
189
190                $switch_class = 'html' === $default_editor ? 'html-active' : 'tmce-active';
191                $wrap_class = 'wp-core-ui wp-editor-wrap ' . $switch_class;
192
193                if ( $set['_content_editor_dfw'] ) {
194                        $wrap_class .= ' has-dfw';
195                }
196
197                echo '<div id="wp-' . $editor_id_attr . '-wrap" class="' . $wrap_class . '">';
198
199                if ( self::$editor_buttons_css ) {
200                        wp_print_styles( 'editor-buttons' );
201                        self::$editor_buttons_css = false;
202                }
203
204                if ( ! empty( $set['editor_css'] ) ) {
205                        echo $set['editor_css'] . "\n";
206                }
207
208                if ( ! empty( $buttons ) || $set['media_buttons'] ) {
209                        echo '<div id="wp-' . $editor_id_attr . '-editor-tools" class="wp-editor-tools hide-if-no-js">';
210
211                        if ( $set['media_buttons'] ) {
212                                self::$has_medialib = true;
213
214                                if ( ! function_exists( 'media_buttons' ) )
215                                        include( ABSPATH . 'wp-admin/includes/media.php' );
216
217                                echo '<div id="wp-' . $editor_id_attr . '-media-buttons" class="wp-media-buttons">';
218
219                                /**
220                                 * Fires after the default media button(s) are displayed.
221                                 *
222                                 * @since 2.5.0
223                                 *
224                                 * @param string $editor_id Unique editor identifier, e.g. 'content'.
225                                 */
226                                do_action( 'media_buttons', $editor_id );
227                                echo "</div>\n";
228                        }
229
230                        echo '<div class="wp-editor-tabs">' . $buttons . "</div>\n";
231                        echo "</div>\n";
232                }
233
234                $quicktags_toolbar = '';
235
236                if ( self::$this_quicktags ) {
237                        if ( 'content' === $editor_id && ! empty( $GLOBALS['current_screen'] ) && $GLOBALS['current_screen']->base === 'post' ) {
238                                $toolbar_id = 'ed_toolbar';
239                        } else {
240                                $toolbar_id = 'qt_' . $editor_id_attr . '_toolbar';
241                        }
242
243                        $quicktags_toolbar = '<div id="' . $toolbar_id . '" class="quicktags-toolbar"></div>';
244                }
245
246                /**
247                 * Filters the HTML markup output that displays the editor.
248                 *
249                 * @since 2.1.0
250                 *
251                 * @param string $output Editor's HTML markup.
252                 */
253                $the_editor = apply_filters( 'the_editor', '<div id="wp-' . $editor_id_attr . '-editor-container" class="wp-editor-container">' .
254                        $quicktags_toolbar .
255                        '<textarea' . $editor_class . $height . $tabindex . $autocomplete . ' cols="40" name="' . esc_attr( $set['textarea_name'] ) . '" ' .
256                        'id="' . $editor_id_attr . '">%s</textarea></div>' );
257
258                // Prepare the content for the Visual or Text editor, only when TinyMCE is used (back-compat).
259                if ( self::$this_tinymce ) {
260                        add_filter( 'the_editor_content', 'format_for_editor', 10, 2 );
261                }
262
263                /**
264                 * Filters the default editor content.
265                 *
266                 * @since 2.1.0
267                 *
268                 * @param string $content        Default editor content.
269                 * @param string $default_editor The default editor for the current user.
270                 *                               Either 'html' or 'tinymce'.
271                 */
272                $content = apply_filters( 'the_editor_content', $content, $default_editor );
273
274                // Remove the filter as the next editor on the same page may not need it.
275                if ( self::$this_tinymce ) {
276                        remove_filter( 'the_editor_content', 'format_for_editor' );
277                }
278
279                // Back-compat for the `htmledit_pre` and `richedit_pre` filters
280                if ( 'html' === $default_editor && has_filter( 'htmledit_pre' ) ) {
281                        // TODO: needs _deprecated_filter(), use _deprecated_function() as substitute for now
282                        _deprecated_function( 'add_filter( htmledit_pre )', '4.3.0', 'add_filter( format_for_editor )' );
283                        $content = apply_filters( 'htmledit_pre', $content );
284                } elseif ( 'tinymce' === $default_editor && has_filter( 'richedit_pre' ) ) {
285                        _deprecated_function( 'add_filter( richedit_pre )', '4.3.0', 'add_filter( format_for_editor )' );
286                        $content = apply_filters( 'richedit_pre', $content );
287                }
288
289                if ( false !== stripos( $content, 'textarea' ) ) {
290                        $content = preg_replace( '%</textarea%i', '&lt;/textarea', $content );
291                }
292
293                printf( $the_editor, $content );
294                echo "\n</div>\n\n";
295
296                self::editor_settings( $editor_id, $set );
297        }
298
299        /**
300         * @static
301         *
302         * @global string $tinymce_version
303         *
304         * @param string $editor_id
305         * @param array  $set
306         */
307        public static function editor_settings($editor_id, $set) {
308                global $tinymce_version;
309
310                if ( empty(self::$first_init) ) {
311                        if ( is_admin() ) {
312                                add_action( 'admin_print_footer_scripts', array( __CLASS__, 'editor_js' ), 50 );
313                                add_action( 'admin_print_footer_scripts', array( __CLASS__, 'enqueue_scripts' ), 1 );
314                        } else {
315                                add_action( 'wp_print_footer_scripts', array( __CLASS__, 'editor_js' ), 50 );
316                                add_action( 'wp_print_footer_scripts', array( __CLASS__, 'enqueue_scripts' ), 1 );
317                        }
318                }
319
320                if ( self::$this_quicktags ) {
321
322                        $qtInit = array(
323                                'id' => $editor_id,
324                                'buttons' => ''
325                        );
326
327                        if ( is_array($set['quicktags']) )
328                                $qtInit = array_merge($qtInit, $set['quicktags']);
329
330                        if ( empty($qtInit['buttons']) )
331                                $qtInit['buttons'] = 'strong,em,link,block,del,ins,img,ul,ol,li,code,more,close';
332
333                        if ( $set['_content_editor_dfw'] ) {
334                                $qtInit['buttons'] .= ',dfw';
335                        }
336
337                        /**
338                         * Filters the Quicktags settings.
339                         *
340                         * @since 3.3.0
341                         *
342                         * @param array  $qtInit    Quicktags settings.
343                         * @param string $editor_id The unique editor ID, e.g. 'content'.
344                         */
345                        $qtInit = apply_filters( 'quicktags_settings', $qtInit, $editor_id );
346
347                        self::$qt_settings[$editor_id] = $qtInit;
348
349                        self::$qt_buttons = array_merge( self::$qt_buttons, explode(',', $qtInit['buttons']) );
350                }
351
352                if ( self::$this_tinymce ) {
353
354                        if ( empty( self::$first_init ) ) {
355                                $baseurl = self::get_baseurl();
356                                $mce_locale = self::get_mce_locale();
357                                $ext_plugins = '';
358
359                                if ( $set['teeny'] ) {
360
361                                        /**
362                                         * Filters the list of teenyMCE plugins.
363                                         *
364                                         * @since 2.7.0
365                                         *
366                                         * @param array  $plugins   An array of teenyMCE plugins.
367                                         * @param string $editor_id Unique editor identifier, e.g. 'content'.
368                                         */
369                                        $plugins = apply_filters( 'teeny_mce_plugins', array( 'colorpicker', 'lists', 'fullscreen', 'image', 'wordpress', 'wpeditimage', 'wplink' ), $editor_id );
370                                } else {
371
372                                        /**
373                                         * Filters the list of TinyMCE external plugins.
374                                         *
375                                         * The filter takes an associative array of external plugins for
376                                         * TinyMCE in the form 'plugin_name' => 'url'.
377                                         *
378                                         * The url should be absolute, and should include the js filename
379                                         * to be loaded. For example:
380                                         * 'myplugin' => 'http://mysite.com/wp-content/plugins/myfolder/mce_plugin.js'.
381                                         *
382                                         * If the external plugin adds a button, it should be added with
383                                         * one of the 'mce_buttons' filters.
384                                         *
385                                         * @since 2.5.0
386                                         *
387                                         * @param array $external_plugins An array of external TinyMCE plugins.
388                                         */
389                                        $mce_external_plugins = apply_filters( 'mce_external_plugins', array() );
390
391                                        $plugins = array(
392                                                'charmap',
393                                                'colorpicker',
394                                                'hr',
395                                                'lists',
396                                                'media',
397                                                'paste',
398                                                'tabfocus',
399                                                'textcolor',
400                                                'fullscreen',
401                                                'wordpress',
402                                                'wpautoresize',
403                                                'wpeditimage',
404                                                'wpemoji',
405                                                'wpgallery',
406                                                'wplink',
407                                                'wpdialogs',
408                                                'wptextpattern',
409                                                'wpview',
410                                        );
411
412                                        if ( ! self::$has_medialib ) {
413                                                $plugins[] = 'image';
414                                        }
415
416                                        /**
417                                         * Filters the list of default TinyMCE plugins.
418                                         *
419                                         * The filter specifies which of the default plugins included
420                                         * in WordPress should be added to the TinyMCE instance.
421                                         *
422                                         * @since 3.3.0
423                                         *
424                                         * @param array $plugins An array of default TinyMCE plugins.
425                                         */
426                                        $plugins = array_unique( apply_filters( 'tiny_mce_plugins', $plugins ) );
427
428                                        if ( ( $key = array_search( 'spellchecker', $plugins ) ) !== false ) {
429                                                // Remove 'spellchecker' from the internal plugins if added with 'tiny_mce_plugins' filter to prevent errors.
430                                                // It can be added with 'mce_external_plugins'.
431                                                unset( $plugins[$key] );
432                                        }
433
434                                        if ( ! empty( $mce_external_plugins ) ) {
435
436                                                /**
437                                                 * Filters the translations loaded for external TinyMCE 3.x plugins.
438                                                 *
439                                                 * The filter takes an associative array ('plugin_name' => 'path')
440                                                 * where 'path' is the include path to the file.
441                                                 *
442                                                 * The language file should follow the same format as wp_mce_translation(),
443                                                 * and should define a variable ($strings) that holds all translated strings.
444                                                 *
445                                                 * @since 2.5.0
446                                                 *
447                                                 * @param array $translations Translations for external TinyMCE plugins.
448                                                 */
449                                                $mce_external_languages = apply_filters( 'mce_external_languages', array() );
450
451                                                $loaded_langs = array();
452                                                $strings = '';
453
454                                                if ( ! empty( $mce_external_languages ) ) {
455                                                        foreach ( $mce_external_languages as $name => $path ) {
456                                                                if ( @is_file( $path ) && @is_readable( $path ) ) {
457                                                                        include_once( $path );
458                                                                        $ext_plugins .= $strings . "\n";
459                                                                        $loaded_langs[] = $name;
460                                                                }
461                                                        }
462                                                }
463
464                                                foreach ( $mce_external_plugins as $name => $url ) {
465                                                        if ( in_array( $name, $plugins, true ) ) {
466                                                                unset( $mce_external_plugins[ $name ] );
467                                                                continue;
468                                                        }
469
470                                                        $url = set_url_scheme( $url );
471                                                        $mce_external_plugins[ $name ] = $url;
472                                                        $plugurl = dirname( $url );
473                                                        $strings = '';
474
475                                                        // Try to load langs/[locale].js and langs/[locale]_dlg.js
476                                                        if ( ! in_array( $name, $loaded_langs, true ) ) {
477                                                                $path = str_replace( content_url(), '', $plugurl );
478                                                                $path = WP_CONTENT_DIR . $path . '/langs/';
479
480                                                                if ( function_exists('realpath') )
481                                                                        $path = trailingslashit( realpath($path) );
482
483                                                                if ( @is_file( $path . $mce_locale . '.js' ) )
484                                                                        $strings .= @file_get_contents( $path . $mce_locale . '.js' ) . "\n";
485
486                                                                if ( @is_file( $path . $mce_locale . '_dlg.js' ) )
487                                                                        $strings .= @file_get_contents( $path . $mce_locale . '_dlg.js' ) . "\n";
488
489                                                                if ( 'en' != $mce_locale && empty( $strings ) ) {
490                                                                        if ( @is_file( $path . 'en.js' ) ) {
491                                                                                $str1 = @file_get_contents( $path . 'en.js' );
492                                                                                $strings .= preg_replace( '/([\'"])en\./', '$1' . $mce_locale . '.', $str1, 1 ) . "\n";
493                                                                        }
494
495                                                                        if ( @is_file( $path . 'en_dlg.js' ) ) {
496                                                                                $str2 = @file_get_contents( $path . 'en_dlg.js' );
497                                                                                $strings .= preg_replace( '/([\'"])en\./', '$1' . $mce_locale . '.', $str2, 1 ) . "\n";
498                                                                        }
499                                                                }
500
501                                                                if ( ! empty( $strings ) )
502                                                                        $ext_plugins .= "\n" . $strings . "\n";
503                                                        }
504
505                                                        $ext_plugins .= 'tinyMCEPreInit.load_ext("' . $plugurl . '", "' . $mce_locale . '");' . "\n";
506                                                        $ext_plugins .= 'tinymce.PluginManager.load("' . $name . '", "' . $url . '");' . "\n";
507                                                }
508                                        }
509                                }
510
511                                self::$plugins = $plugins;
512                                self::$ext_plugins = $ext_plugins;
513
514                                $settings = self::default_settings();
515                                $settings['plugins'] = implode( ',', $plugins );
516
517                                if ( ! empty( $mce_external_plugins ) ) {
518                                        $settings['external_plugins'] = wp_json_encode( $mce_external_plugins );
519                                }
520
521                                /** This filter is documented in wp-admin/includes/media.php */
522                                if ( apply_filters( 'disable_captions', '' ) ) {
523                                        $settings['wpeditimage_disable_captions'] = true;
524                                }
525
526                                $mce_css = $settings['content_css'];
527                                $editor_styles = get_editor_stylesheets();
528
529                                if ( ! empty( $editor_styles ) ) {
530                                        // Force urlencoding of commas.
531                                        foreach ( $editor_styles as $key => $url ) {
532                                                if ( strpos( $url, ',' ) !== false ) {
533                                                        $editor_styles[ $key ] = str_replace( ',', '%2C', $url );
534                                                }
535                                        }
536
537                                        $mce_css .= ',' . implode( ',', $editor_styles );
538                                }
539
540                                /**
541                                 * Filters the comma-delimited list of stylesheets to load in TinyMCE.
542                                 *
543                                 * @since 2.1.0
544                                 *
545                                 * @param string $stylesheets Comma-delimited list of stylesheets.
546                                 */
547                                $mce_css = trim( apply_filters( 'mce_css', $mce_css ), ' ,' );
548
549                                if ( ! empty( $mce_css ) ) {
550                                        $settings['content_css'] = $mce_css;
551                                } else {
552                                        unset( $settings['content_css'] );
553                                }
554
555                                self::$first_init = $settings;
556                        }
557
558                        if ( $set['teeny'] ) {
559
560                                /**
561                                 * Filters the list of teenyMCE buttons (Text tab).
562                                 *
563                                 * @since 2.7.0
564                                 *
565                                 * @param array  $buttons   An array of teenyMCE buttons.
566                                 * @param string $editor_id Unique editor identifier, e.g. 'content'.
567                                 */
568                                $mce_buttons = apply_filters( 'teeny_mce_buttons', array('bold', 'italic', 'underline', 'blockquote', 'strikethrough', 'bullist', 'numlist', 'alignleft', 'aligncenter', 'alignright', 'undo', 'redo', 'link', 'unlink', 'fullscreen'), $editor_id );
569                                $mce_buttons_2 = $mce_buttons_3 = $mce_buttons_4 = array();
570                        } else {
571                                $mce_buttons = array( 'formatselect', 'bold', 'italic', 'bullist', 'numlist', 'blockquote', 'alignleft', 'aligncenter', 'alignright', 'link', 'unlink', 'wp_more', 'spellchecker' );
572
573                                if ( ! wp_is_mobile() ) {
574                                        if ( $set['_content_editor_dfw'] ) {
575                                                $mce_buttons[] = 'dfw';
576                                        } else {
577                                                $mce_buttons[] = 'fullscreen';
578                                        }
579                                }
580
581                                $mce_buttons[] = 'wp_adv';
582
583                                /**
584                                 * Filters the first-row list of TinyMCE buttons (Visual tab).
585                                 *
586                                 * @since 2.0.0
587                                 *
588                                 * @param array  $buttons   First-row list of buttons.
589                                 * @param string $editor_id Unique editor identifier, e.g. 'content'.
590                                 */
591                                $mce_buttons = apply_filters( 'mce_buttons', $mce_buttons, $editor_id );
592
593                                $mce_buttons_2 = array( 'strikethrough', 'hr', 'forecolor', 'pastetext', 'removeformat', 'charmap', 'outdent', 'indent', 'undo', 'redo' );
594
595                                if ( ! wp_is_mobile() ) {
596                                        $mce_buttons_2[] = 'wp_help';
597                                }
598
599                                /**
600                                 * Filters the second-row list of TinyMCE buttons (Visual tab).
601                                 *
602                                 * @since 2.0.0
603                                 *
604                                 * @param array  $buttons   Second-row list of buttons.
605                                 * @param string $editor_id Unique editor identifier, e.g. 'content'.
606                                 */
607                                $mce_buttons_2 = apply_filters( 'mce_buttons_2', $mce_buttons_2, $editor_id );
608
609                                /**
610                                 * Filters the third-row list of TinyMCE buttons (Visual tab).
611                                 *
612                                 * @since 2.0.0
613                                 *
614                                 * @param array  $buttons   Third-row list of buttons.
615                                 * @param string $editor_id Unique editor identifier, e.g. 'content'.
616                                 */
617                                $mce_buttons_3 = apply_filters( 'mce_buttons_3', array(), $editor_id );
618
619                                /**
620                                 * Filters the fourth-row list of TinyMCE buttons (Visual tab).
621                                 *
622                                 * @since 2.5.0
623                                 *
624                                 * @param array  $buttons   Fourth-row list of buttons.
625                                 * @param string $editor_id Unique editor identifier, e.g. 'content'.
626                                 */
627                                $mce_buttons_4 = apply_filters( 'mce_buttons_4', array(), $editor_id );
628                        }
629
630                        $body_class = $editor_id;
631
632                        if ( $post = get_post() ) {
633                                $body_class .= ' post-type-' . sanitize_html_class( $post->post_type ) . ' post-status-' . sanitize_html_class( $post->post_status );
634
635                                if ( post_type_supports( $post->post_type, 'post-formats' ) ) {
636                                        $post_format = get_post_format( $post );
637                                        if ( $post_format && ! is_wp_error( $post_format ) )
638                                                $body_class .= ' post-format-' . sanitize_html_class( $post_format );
639                                        else
640                                                $body_class .= ' post-format-standard';
641                                }
642
643                                $page_template = get_page_template_slug( $post );
644
645                                if ( $page_template !== false ) {
646                                        $page_template = empty( $page_template ) ? 'default' : str_replace( '.', '-', basename( $page_template, '.php' ) );
647                                        $body_class .= ' page-template-' . sanitize_html_class( $page_template );
648                                }
649                        }
650
651                        $body_class .= ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_user_locale() ) ) );
652
653                        if ( ! empty( $set['tinymce']['body_class'] ) ) {
654                                $body_class .= ' ' . $set['tinymce']['body_class'];
655                                unset( $set['tinymce']['body_class'] );
656                        }
657
658                        $mceInit = array (
659                                'selector' => "#$editor_id",
660                                'wpautop' => (bool) $set['wpautop'],
661                                'indent' => ! $set['wpautop'],
662                                'toolbar1' => implode( ',', $mce_buttons ),
663                                'toolbar2' => implode( ',', $mce_buttons_2 ),
664                                'toolbar3' => implode( ',', $mce_buttons_3 ),
665                                'toolbar4' => implode( ',', $mce_buttons_4 ),
666                                'tabfocus_elements' => $set['tabfocus_elements'],
667                                'body_class' => $body_class
668                        );
669
670                        // Merge with the first part of the init array
671                        $mceInit = array_merge( self::$first_init, $mceInit );
672
673                        if ( is_array( $set['tinymce'] ) )
674                                $mceInit = array_merge( $mceInit, $set['tinymce'] );
675
676                        /*
677                         * For people who really REALLY know what they're doing with TinyMCE
678                         * You can modify $mceInit to add, remove, change elements of the config
679                         * before tinyMCE.init. Setting "valid_elements", "invalid_elements"
680                         * and "extended_valid_elements" can be done through this filter. Best
681                         * is to use the default cleanup by not specifying valid_elements,
682                         * as TinyMCE checks against the full set of HTML 5.0 elements and attributes.
683                         */
684                        if ( $set['teeny'] ) {
685
686                                /**
687                                 * Filters the teenyMCE config before init.
688                                 *
689                                 * @since 2.7.0
690                                 *
691                                 * @param array  $mceInit   An array with teenyMCE config.
692                                 * @param string $editor_id Unique editor identifier, e.g. 'content'.
693                                 */
694                                $mceInit = apply_filters( 'teeny_mce_before_init', $mceInit, $editor_id );
695                        } else {
696
697                                /**
698                                 * Filters the TinyMCE config before init.
699                                 *
700                                 * @since 2.5.0
701                                 *
702                                 * @param array  $mceInit   An array with TinyMCE config.
703                                 * @param string $editor_id Unique editor identifier, e.g. 'content'.
704                                 */
705                                $mceInit = apply_filters( 'tiny_mce_before_init', $mceInit, $editor_id );
706                        }
707
708                        if ( empty( $mceInit['toolbar3'] ) && ! empty( $mceInit['toolbar4'] ) ) {
709                                $mceInit['toolbar3'] = $mceInit['toolbar4'];
710                                $mceInit['toolbar4'] = '';
711                        }
712
713                        self::$mce_settings[$editor_id] = $mceInit;
714                } // end if self::$this_tinymce
715        }
716
717        /**
718         *
719         * @static
720         * @param array $init
721         * @return string
722         */
723        private static function _parse_init( $init ) {
724                $options = '';
725
726                foreach ( $init as $key => $value ) {
727                        if ( is_bool( $value ) ) {
728                                $val = $value ? 'true' : 'false';
729                                $options .= $key . ':' . $val . ',';
730                                continue;
731                        } elseif ( ! empty( $value ) && is_string( $value ) && (
732                                ( '{' == $value{0} && '}' == $value{strlen( $value ) - 1} ) ||
733                                ( '[' == $value{0} && ']' == $value{strlen( $value ) - 1} ) ||
734                                preg_match( '/^\(?function ?\(/', $value ) ) ) {
735
736                                $options .= $key . ':' . $value . ',';
737                                continue;
738                        }
739                        $options .= $key . ':"' . $value . '",';
740                }
741
742                return '{' . trim( $options, ' ,' ) . '}';
743        }
744
745        /**
746         *
747         * @static
748         */
749        public static function enqueue_scripts( $default_scripts = false ) {
750                if ( $default_scripts || self::$has_tinymce ) {
751                        wp_enqueue_script( 'editor' );
752                }
753
754                if ( $default_scripts || self::$has_quicktags ) {
755                        wp_enqueue_script( 'quicktags' );
756                        wp_enqueue_style( 'buttons' );
757                }
758
759                if ( $default_scripts || in_array( 'wplink', self::$plugins, true ) || in_array( 'link', self::$qt_buttons, true ) ) {
760                        wp_enqueue_script( 'wplink' );
761                        wp_enqueue_script( 'jquery-ui-autocomplete' );
762                }
763
764                if ( self::$old_dfw_compat ) {
765                        wp_enqueue_script( 'wp-fullscreen-stub' );
766                }
767
768                if ( self::$has_medialib ) {
769                        add_thickbox();
770                        wp_enqueue_script( 'media-upload' );
771                        wp_enqueue_script( 'wp-embed' );
772                } elseif ( $default_scripts ) {
773                        wp_enqueue_script( 'media-upload' );
774                }
775
776                /**
777                 * Fires when scripts and styles are enqueued for the editor.
778                 *
779                 * @since 3.9.0
780                 *
781                 * @param array $to_load An array containing boolean values whether TinyMCE
782                 *                       and Quicktags are being loaded.
783                 */
784                do_action( 'wp_enqueue_editor', array(
785                        'tinymce'   => ( $default_scripts || self::$has_tinymce ),
786                        'quicktags' => ( $default_scripts || self::$has_quicktags ),
787                ) );
788        }
789
790        /**
791         * Enqueue all editor scripts.
792         * For use when the editor is going to be initialized after page load.
793         *
794         * @since 4.8.0
795         */
796        public static function enqueue_default_editor() {
797                // We are past the point where scripts can be enqueued properly.
798                if ( did_action( 'wp_enqueue_editor' ) ) {
799                        return;
800                }
801
802                self::enqueue_scripts( true );
803
804                // Also add wp-includes/css/editor.css
805                wp_enqueue_style( 'editor-buttons' );
806
807                if ( is_admin() ) {
808                        add_action( 'admin_print_footer_scripts', array( __CLASS__, 'print_default_editor_scripts' ), 45 );
809                } else {
810                        add_action( 'wp_print_footer_scripts', array( __CLASS__, 'print_default_editor_scripts' ), 45 );
811                }
812        }
813
814        /**
815         * Print (output) all editor scripts and default settings.
816         * For use when the editor is going to be initialized after page load.
817         *
818         * @since 4.8.0
819         *
820         */
821        public static function print_default_editor_scripts() {
822                $user_can_richedit = user_can_richedit();
823
824                if ( $user_can_richedit ) {
825                        $settings = self::default_settings();
826
827                        $settings['toolbar1'] = 'bold,italic,bullist,numlist,link';
828                        $settings['wpautop'] = false;
829                        $settings['indent'] = true;
830                        $settings['elementpath'] = false;
831
832                        if ( is_rtl() ) {
833                                $settings['directionality'] = 'rtl';
834                        }
835
836                        // In production all plugins are loaded (they are in wp-editor.js.gz).
837                        // The 'wpview', 'wpdialogs', and 'media' TinyMCE plugins are not initialized by default.
838                        // Can be added from js by using the 'wp-before-tinymce-init' event.
839                        $settings['plugins'] = implode( ',', array(
840                                'charmap',
841                                'colorpicker',
842                                'hr',
843                                'lists',
844                                'paste',
845                                'tabfocus',
846                                'textcolor',
847                                'fullscreen',
848                                'wordpress',
849                                'wpautoresize',
850                                'wpeditimage',
851                                'wpemoji',
852                                'wpgallery',
853                                'wplink',
854                                'wptextpattern',
855                        ) );
856
857                        $settings = self::_parse_init( $settings );
858                } else {
859                        $settings = '{}';
860                }
861
862                ?>
863                <script type="text/javascript">
864                window.wp = window.wp || {};
865                window.wp.editor = window.wp.editor || {};
866                window.wp.editor.getDefaultSettings = function() {
867                        return {
868                                tinymce: <?php echo $settings; ?>,
869                                quicktags: {
870                                        buttons: 'strong,em,link,ul,ol,li,code'
871                                }
872                        };
873                };
874
875                <?php
876
877                if ( $user_can_richedit ) {
878                        $suffix = SCRIPT_DEBUG ? '' : '.min';
879                        $baseurl = self::get_baseurl();
880
881                        ?>
882                        var tinyMCEPreInit = {
883                                baseURL: "<?php echo $baseurl; ?>",
884                                suffix: "<?php echo $suffix; ?>",
885                                mceInit: {},
886                                qtInit: {},
887                                load_ext: function(url,lang){var sl=tinymce.ScriptLoader;sl.markDone(url+'/langs/'+lang+'.js');sl.markDone(url+'/langs/'+lang+'_dlg.js');}
888                        };
889                        <?php
890                }
891                ?>
892                </script>
893                <?php
894
895                if ( $user_can_richedit ) {
896                        self::print_tinymce_scripts();
897                }
898
899                /**
900                 * Fires when the editor scripts are loaded for later initialization,
901                 * after all scripts and settings are printed.
902                 *
903                 * @since 4.8.0
904                 */
905                do_action( 'print_default_editor_scripts' );
906
907                self::wp_link_dialog();
908        }
909
910        public static function get_mce_locale() {
911                if ( empty( self::$mce_locale ) ) {
912                        $mce_locale = get_user_locale();
913                        self::$mce_locale = empty( $mce_locale ) ? 'en' : strtolower( substr( $mce_locale, 0, 2 ) ); // ISO 639-1
914                }
915
916                return self::$mce_locale;
917        }
918
919        public static function get_baseurl() {
920                if ( empty( self::$baseurl ) ) {
921                        self::$baseurl = includes_url( 'js/tinymce' );
922                }
923
924                return self::$baseurl;
925        }
926
927        /**
928         * Returns the default TinyMCE settings.
929         * Doesn't include plugins, buttons, editor selector.
930         *
931         * @global string $tinymce_version
932         *@static
933         * @return array
934         */
935        private static function default_settings() {
936                global $tinymce_version;
937
938                $shortcut_labels = array();
939
940                foreach ( self::get_translation() as $name => $value ) {
941                        if ( is_array( $value ) ) {
942                                $shortcut_labels[$name] = $value[1];
943                        }
944                }
945
946                $settings = array(
947                        'theme' => 'modern',
948                        'skin' => 'lightgray',
949                        'language' => self::get_mce_locale(),
950                        'formats' => '{' .
951                                'alignleft: [' .
952                                        '{selector: "p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li", styles: {textAlign:"left"}},' .
953                                        '{selector: "img,table,dl.wp-caption", classes: "alignleft"}' .
954                                '],' .
955                                'aligncenter: [' .
956                                        '{selector: "p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li", styles: {textAlign:"center"}},' .
957                                        '{selector: "img,table,dl.wp-caption", classes: "aligncenter"}' .
958                                '],' .
959                                'alignright: [' .
960                                        '{selector: "p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li", styles: {textAlign:"right"}},' .
961                                        '{selector: "img,table,dl.wp-caption", classes: "alignright"}' .
962                                '],' .
963                                'strikethrough: {inline: "del"}' .
964                        '}',
965                        'relative_urls' => false,
966                        'remove_script_host' => false,
967                        'convert_urls' => false,
968                        'browser_spellcheck' => true,
969                        'fix_list_elements' => true,
970                        'entities' => '38,amp,60,lt,62,gt',
971                        'entity_encoding' => 'raw',
972                        'keep_styles' => false,
973                        'cache_suffix' => 'wp-mce-' . $tinymce_version,
974                        'resize' => 'vertical',
975                        'menubar' => false,
976                        'branding' => false,
977
978                        // Limit the preview styles in the menu/toolbar
979                        'preview_styles' => 'font-family font-size font-weight font-style text-decoration text-transform',
980
981                        'end_container_on_empty_block' => true,
982                        'wpeditimage_html5_captions' => true,
983                        'wp_lang_attr' => get_bloginfo( 'language' ),
984                        'wp_shortcut_labels' => wp_json_encode( $shortcut_labels ),
985                );
986
987                $suffix = SCRIPT_DEBUG ? '' : '.min';
988                $version = 'ver=' . get_bloginfo( 'version' );
989
990                // Default stylesheets
991                $settings['content_css'] = includes_url( "css/dashicons$suffix.css?$version" ) . ',' .
992                        includes_url( "js/tinymce/skins/wordpress/wp-content.css?$version" );
993
994                return $settings;
995        }
996
997        private static function get_translation() {
998                if ( empty( self::$translation ) ) {
999                        self::$translation = array(
1000                        // Default TinyMCE strings
1001                        'New document' => __( 'New document' ),
1002                        'Formats' => _x( 'Formats', 'TinyMCE' ),
1003
1004                        'Headings' => _x( 'Headings', 'TinyMCE' ),
1005                        'Heading 1' => array( __( 'Heading 1' ), 'access1' ),
1006                        'Heading 2' => array( __( 'Heading 2' ), 'access2' ),
1007                        'Heading 3' => array( __( 'Heading 3' ), 'access3' ),
1008                        'Heading 4' => array( __( 'Heading 4' ), 'access4' ),
1009                        'Heading 5' => array( __( 'Heading 5' ), 'access5' ),
1010                        'Heading 6' => array( __( 'Heading 6' ), 'access6' ),
1011
1012                        /* translators: block tags */
1013                        'Blocks' => _x( 'Blocks', 'TinyMCE' ),
1014                        'Paragraph' => array( __( 'Paragraph' ), 'access7' ),
1015                        'Blockquote' => array( __( 'Blockquote' ), 'accessQ' ),
1016                        'Div' => _x( 'Div', 'HTML tag' ),
1017                        'Pre' => _x( 'Pre', 'HTML tag' ),
1018                        'Preformatted' => _x( 'Preformatted', 'HTML tag' ),
1019                        'Address' => _x( 'Address', 'HTML tag' ),
1020
1021                        'Inline' => _x( 'Inline', 'HTML elements' ),
1022                        'Underline' => array( __( 'Underline' ), 'metaU' ),
1023                        'Strikethrough' => array( __( 'Strikethrough' ), 'accessD' ),
1024                        'Subscript' => __( 'Subscript' ),
1025                        'Superscript' => __( 'Superscript' ),
1026                        'Clear formatting' => __( 'Clear formatting' ),
1027                        'Bold' => array( __( 'Bold' ), 'metaB' ),
1028                        'Italic' => array( __( 'Italic' ), 'metaI' ),
1029                        'Code' => array( __( 'Code' ), 'accessX' ),
1030                        'Source code' => __( 'Source code' ),
1031                        'Font Family' => __( 'Font Family' ),
1032                        'Font Sizes' => __( 'Font Sizes' ),
1033
1034                        'Align center' => array( __( 'Align center' ), 'accessC' ),
1035                        'Align right' => array( __( 'Align right' ), 'accessR' ),
1036                        'Align left' => array( __( 'Align left' ), 'accessL' ),
1037                        'Justify' => array( __( 'Justify' ), 'accessJ' ),
1038                        'Increase indent' => __( 'Increase indent' ),
1039                        'Decrease indent' => __( 'Decrease indent' ),
1040
1041                        'Cut' => array( __( 'Cut' ), 'metaX' ),
1042                        'Copy' => array( __( 'Copy' ), 'metaC' ),
1043                        'Paste' => array( __( 'Paste' ), 'metaV' ),
1044                        'Select all' => array( __( 'Select all' ), 'metaA' ),
1045                        'Undo' => array( __( 'Undo' ), 'metaZ' ),
1046                        'Redo' => array( __( 'Redo' ), 'metaY' ),
1047
1048                        'Ok' => __( 'OK' ),
1049                        'Cancel' => __( 'Cancel' ),
1050                        'Close' => __( 'Close' ),
1051                        'Visual aids' => __( 'Visual aids' ),
1052
1053                        'Bullet list' => array( __( 'Bulleted list' ), 'accessU' ),
1054                        'Numbered list' => array( __( 'Numbered list' ), 'accessO' ),
1055                        'Square' => _x( 'Square', 'list style' ),
1056                        'Default' => _x( 'Default', 'list style' ),
1057                        'Circle' => _x( 'Circle', 'list style' ),
1058                        'Disc' => _x('Disc', 'list style' ),
1059                        'Lower Greek' => _x( 'Lower Greek', 'list style' ),
1060                        'Lower Alpha' => _x( 'Lower Alpha', 'list style' ),
1061                        'Upper Alpha' => _x( 'Upper Alpha', 'list style' ),
1062                        'Upper Roman' => _x( 'Upper Roman', 'list style' ),
1063                        'Lower Roman' => _x( 'Lower Roman', 'list style' ),
1064
1065                        // Anchor plugin
1066                        'Name' => _x( 'Name', 'Name of link anchor (TinyMCE)' ),
1067                        'Anchor' => _x( 'Anchor', 'Link anchor (TinyMCE)' ),
1068                        'Anchors' => _x( 'Anchors', 'Link anchors (TinyMCE)' ),
1069                        'Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.' =>
1070                                __( 'Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.' ),
1071                        'Id' => _x( 'Id', 'Id for link anchor (TinyMCE)' ),
1072
1073                        // Fullpage plugin
1074                        'Document properties' => __( 'Document properties' ),
1075                        'Robots' => __( 'Robots' ),
1076                        'Title' => __( 'Title' ),
1077                        'Keywords' => __( 'Keywords' ),
1078                        'Encoding' => __( 'Encoding' ),
1079                        'Description' => __( 'Description' ),
1080                        'Author' => __( 'Author' ),
1081
1082                        // Media, image plugins
1083                        'Image' => __( 'Image' ),
1084                        'Insert/edit image' => array( __( 'Insert/edit image' ), 'accessM' ),
1085                        'General' => __( 'General' ),
1086                        'Advanced' => __( 'Advanced' ),
1087                        'Source' => __( 'Source' ),
1088                        'Border' => __( 'Border' ),
1089                        'Constrain proportions' => __( 'Constrain proportions' ),
1090                        'Vertical space' => __( 'Vertical space' ),
1091                        'Image description' => __( 'Image description' ),
1092                        'Style' => __( 'Style' ),
1093                        'Dimensions' => __( 'Dimensions' ),
1094                        'Insert image' => __( 'Insert image' ),
1095                        'Date/time' => __( 'Date/time' ),
1096                        'Insert date/time' => __( 'Insert date/time' ),
1097                        'Table of Contents' => __( 'Table of Contents' ),
1098                        'Insert/Edit code sample' => __( 'Insert/edit code sample' ),
1099                        'Language' => __( 'Language' ),
1100                        'Media' => __( 'Media' ),
1101                        'Insert/edit media' => __( 'Insert/edit media' ),
1102                        'Poster' => __( 'Poster' ),
1103                        'Alternative source' => __( 'Alternative source' ),
1104                        'Paste your embed code below:' => __( 'Paste your embed code below:' ),
1105                        'Insert video' => __( 'Insert video' ),
1106                        'Embed' => __( 'Embed' ),
1107
1108                        // Each of these have a corresponding plugin
1109                        'Special character' => __( 'Special character' ),
1110                        'Right to left' => _x( 'Right to left', 'editor button' ),
1111                        'Left to right' => _x( 'Left to right', 'editor button' ),
1112                        'Emoticons' => __( 'Emoticons' ),
1113                        'Nonbreaking space' => __( 'Nonbreaking space' ),
1114                        'Page break' => __( 'Page break' ),
1115                        'Paste as text' => __( 'Paste as text' ),
1116                        'Preview' => __( 'Preview' ),
1117                        'Print' => __( 'Print' ),
1118                        'Save' => __( 'Save' ),
1119                        'Fullscreen' => __( 'Fullscreen' ),
1120                        'Horizontal line' => __( 'Horizontal line' ),
1121                        'Horizontal space' => __( 'Horizontal space' ),
1122                        'Restore last draft' => __( 'Restore last draft' ),
1123                        'Insert/edit link' => array( __( 'Insert/edit link' ), 'metaK' ),
1124                        'Remove link' => array( __( 'Remove link' ), 'accessS' ),
1125
1126                        // Link plugin
1127                        'Link' => __( 'Link' ),
1128                        'Insert link' => __( 'Insert link' ),
1129                        'Insert/edit link' => __( 'Insert/edit link' ),
1130                        'Target' => __( 'Target' ),
1131                        'New window' => __( 'New window' ),
1132                        'Text to display' => __( 'Text to display' ),
1133                        'Url' => __( 'URL' ),
1134                        'The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?' =>
1135                                __( 'The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?' ),
1136                        'The URL you entered seems to be an external link. Do you want to add the required http:// prefix?' =>
1137                                __( 'The URL you entered seems to be an external link. Do you want to add the required http:// prefix?' ),
1138
1139                        'Color' => __( 'Color' ),
1140                        'Custom color' => __( 'Custom color' ),
1141                        'Custom...' => _x( 'Custom...', 'label for custom color' ), // no ellipsis
1142                        'No color' => __( 'No color' ),
1143                        'R' => _x( 'R', 'Short for red in RGB' ),
1144                        'G' => _x( 'G', 'Short for green in RGB' ),
1145                        'B' => _x( 'B', 'Short for blue in RGB' ),
1146
1147                        // Spelling, search/replace plugins
1148                        'Could not find the specified string.' => __( 'Could not find the specified string.' ),
1149                        'Replace' => _x( 'Replace', 'find/replace' ),
1150                        'Next' => _x( 'Next', 'find/replace' ),
1151                        /* translators: previous */
1152                        'Prev' => _x( 'Prev', 'find/replace' ),
1153                        'Whole words' => _x( 'Whole words', 'find/replace' ),
1154                        'Find and replace' => __( 'Find and replace' ),
1155                        'Replace with' => _x('Replace with', 'find/replace' ),
1156                        'Find' => _x( 'Find', 'find/replace' ),
1157                        'Replace all' => _x( 'Replace all', 'find/replace' ),
1158                        'Match case' => __( 'Match case' ),
1159                        'Spellcheck' => __( 'Check Spelling' ),
1160                        'Finish' => _x( 'Finish', 'spellcheck' ),
1161                        'Ignore all' => _x( 'Ignore all', 'spellcheck' ),
1162                        'Ignore' => _x( 'Ignore', 'spellcheck' ),
1163                        'Add to Dictionary' => __( 'Add to Dictionary' ),
1164
1165                        // TinyMCE tables
1166                        'Insert table' => __( 'Insert table' ),
1167                        'Delete table' => __( 'Delete table' ),
1168                        'Table properties' => __( 'Table properties' ),
1169                        'Row properties' => __( 'Table row properties' ),
1170                        'Cell properties' => __( 'Table cell properties' ),
1171                        'Border color' => __( 'Border color' ),
1172
1173                        'Row' => __( 'Row' ),
1174                        'Rows' => __( 'Rows' ),
1175                        'Column' => _x( 'Column', 'table column' ),
1176                        'Cols' => _x( 'Cols', 'table columns' ),
1177                        'Cell' => _x( 'Cell', 'table cell' ),
1178                        'Header cell' => __( 'Header cell' ),
1179                        'Header' => _x( 'Header', 'table header' ),
1180                        'Body' => _x( 'Body', 'table body' ),
1181                        'Footer' => _x( 'Footer', 'table footer' ),
1182
1183                        'Insert row before' => __( 'Insert row before' ),
1184                        'Insert row after' => __( 'Insert row after' ),
1185                        'Insert column before' => __( 'Insert column before' ),
1186                        'Insert column after' => __( 'Insert column after' ),
1187                        'Paste row before' => __( 'Paste table row before' ),
1188                        'Paste row after' => __( 'Paste table row after' ),
1189                        'Delete row' => __( 'Delete row' ),
1190                        'Delete column' => __( 'Delete column' ),
1191                        'Cut row' => __( 'Cut table row' ),
1192                        'Copy row' => __( 'Copy table row' ),
1193                        'Merge cells' => __( 'Merge table cells' ),
1194                        'Split cell' => __( 'Split table cell' ),
1195
1196                        'Height' => __( 'Height' ),
1197                        'Width' => __( 'Width' ),
1198                        'Caption' => __( 'Caption' ),
1199                        'Alignment' => __( 'Alignment' ),
1200                        'H Align' => _x( 'H Align', 'horizontal table cell alignment' ),
1201                        'Left' => __( 'Left' ),
1202                        'Center' => __( 'Center' ),
1203                        'Right' => __( 'Right' ),
1204                        'None' => _x( 'None', 'table cell alignment attribute' ),
1205                        'V Align' => _x( 'V Align', 'vertical table cell alignment' ),
1206                        'Top' => __( 'Top' ),
1207                        'Middle' => __( 'Middle' ),
1208                        'Bottom' => __( 'Bottom' ),
1209
1210                        'Row group' => __( 'Row group' ),
1211                        'Column group' => __( 'Column group' ),
1212                        'Row type' => __( 'Row type' ),
1213                        'Cell type' => __( 'Cell type' ),
1214                        'Cell padding' => __( 'Cell padding' ),
1215                        'Cell spacing' => __( 'Cell spacing' ),
1216                        'Scope' => _x( 'Scope', 'table cell scope attribute' ),
1217
1218                        'Insert template' => _x( 'Insert template', 'TinyMCE' ),
1219                        'Templates' => _x( 'Templates', 'TinyMCE' ),
1220
1221                        'Background color' => __( 'Background color' ),
1222                        'Text color' => __( 'Text color' ),
1223                        'Show blocks' => _x( 'Show blocks', 'editor button' ),
1224                        'Show invisible characters' => __( 'Show invisible characters' ),
1225
1226                        /* translators: word count */
1227                        'Words: {0}' => sprintf( __( 'Words: %s' ), '{0}' ),
1228                        'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.' =>
1229                                __( 'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.' ) . "\n\n" .
1230                                __( 'If you&#8217;re looking to paste rich content from Microsoft Word, try turning this option off. The editor will clean up text pasted from Word automatically.' ),
1231                        'Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help' =>
1232                                __( 'Rich Text Area. Press Alt-Shift-H for help.' ),
1233                        'Rich Text Area. Press Control-Option-H for help.' => __( 'Rich Text Area. Press Control-Option-H for help.' ),
1234                        'You have unsaved changes are you sure you want to navigate away?' =>
1235                                __( 'The changes you made will be lost if you navigate away from this page.' ),
1236                        'Your browser doesn\'t support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.' =>
1237                                __( 'Your browser does not support direct access to the clipboard. Please use keyboard shortcuts or your browser&#8217;s edit menu instead.' ),
1238
1239                        // TinyMCE menus
1240                        'Insert' => _x( 'Insert', 'TinyMCE menu' ),
1241                        'File' => _x( 'File', 'TinyMCE menu' ),
1242                        'Edit' => _x( 'Edit', 'TinyMCE menu' ),
1243                        'Tools' => _x( 'Tools', 'TinyMCE menu' ),
1244                        'View' => _x( 'View', 'TinyMCE menu' ),
1245                        'Table' => _x( 'Table', 'TinyMCE menu' ),
1246                        'Format' => _x( 'Format', 'TinyMCE menu' ),
1247
1248                        // WordPress strings
1249                        'Toolbar Toggle' => array( __( 'Toolbar Toggle' ), 'accessZ' ),
1250                        'Insert Read More tag' => array( __( 'Insert Read More tag' ), 'accessT' ),
1251                        'Insert Page Break tag' => array( __( 'Insert Page Break tag' ), 'accessP' ),
1252                        'Read more...' => __( 'Read more...' ), // Title on the placeholder inside the editor (no ellipsis)
1253                        'Distraction-free writing mode' => array( __( 'Distraction-free writing mode' ), 'accessW' ),
1254                        'No alignment' => __( 'No alignment' ), // Tooltip for the 'alignnone' button in the image toolbar
1255                        'Remove' => __( 'Remove' ), // Tooltip for the 'remove' button in the image toolbar
1256                        'Edit ' => __( 'Edit' ), // Tooltip for the 'edit' button in the image toolbar
1257                        'Paste URL or type to search' => __( 'Paste URL or type to search' ), // Placeholder for the inline link dialog
1258                        'Apply'  => __( 'Apply' ), // Tooltip for the 'apply' button in the inline link dialog
1259                        'Link options'  => __( 'Link options' ), // Tooltip for the 'link options' button in the inline link dialog
1260                        'Visual' => __( 'Visual' ), // Editor switch tab label
1261                        'Text' => __( 'Text' ), // Editor switch tab label
1262
1263                        // Shortcuts help modal
1264                        'Keyboard Shortcuts' => array( __( 'Keyboard Shortcuts' ), 'accessH' ),
1265                        'Default shortcuts,' => __( 'Default shortcuts,' ),
1266                        'Additional shortcuts,' => __( 'Additional shortcuts,' ),
1267                        'Focus shortcuts:' => __( 'Focus shortcuts:' ),
1268                        'Inline toolbar (when an image, link or preview is selected)' => __( 'Inline toolbar (when an image, link or preview is selected)' ),
1269                        'Editor menu (when enabled)' => __( 'Editor menu (when enabled)' ),
1270                        'Editor toolbar' => __( 'Editor toolbar' ),
1271                        'Elements path' => __( 'Elements path' ),
1272                        'Ctrl + Alt + letter:' => __( 'Ctrl + Alt + letter:' ),
1273                        'Shift + Alt + letter:' => __( 'Shift + Alt + letter:' ),
1274                        'Cmd + letter:' => __( 'Cmd + letter:' ),
1275                        'Ctrl + letter:' => __( 'Ctrl + letter:' ),
1276                        'Letter' => __( 'Letter' ),
1277                        'Action' => __( 'Action' ),
1278                        'Warning: the link has been inserted but may have errors. Please test it.' => __( 'Warning: the link has been inserted but may have errors. Please test it.' ),
1279                        'To move focus to other buttons use Tab or the arrow keys. To return focus to the editor press Escape or use one of the buttons.' =>
1280                                __( 'To move focus to other buttons use Tab or the arrow keys. To return focus to the editor press Escape or use one of the buttons.' ),
1281                        'When starting a new paragraph with one of these formatting shortcuts followed by a space, the formatting will be applied automatically. Press Backspace or Escape to undo.' =>
1282                                __( 'When starting a new paragraph with one of these formatting shortcuts followed by a space, the formatting will be applied automatically. Press Backspace or Escape to undo.' ),
1283                        'The following formatting shortcuts are replaced when pressing Enter. Press Escape or the Undo button to undo.' =>
1284                                __( 'The following formatting shortcuts are replaced when pressing Enter. Press Escape or the Undo button to undo.' ),
1285                        'The next group of formatting shortcuts are applied as you type or when you insert them around plain text in the same paragraph. Press Escape or the Undo button to undo.' =>
1286                                __( 'The next group of formatting shortcuts are applied as you type or when you insert them around plain text in the same paragraph. Press Escape or the Undo button to undo.' ),
1287                        );
1288                }
1289
1290                /*
1291                Imagetools plugin (not included):
1292                        'Edit image' => __( 'Edit image' ),
1293                        'Image options' => __( 'Image options' ),
1294                        'Back' => __( 'Back' ),
1295                        'Invert' => __( 'Invert' ),
1296                        'Flip horizontally' => __( 'Flip horizontally' ),
1297                        'Flip vertically' => __( 'Flip vertically' ),
1298                        'Crop' => __( 'Crop' ),
1299                        'Orientation' => __( 'Orientation' ),
1300                        'Resize' => __( 'Resize' ),
1301                        'Rotate clockwise' => __( 'Rotate clockwise' ),
1302                        'Rotate counterclockwise' => __( 'Rotate counterclockwise' ),
1303                        'Sharpen' => __( 'Sharpen' ),
1304                        'Brightness' => __( 'Brightness' ),
1305                        'Color levels' => __( 'Color levels' ),
1306                        'Contrast' => __( 'Contrast' ),
1307                        'Gamma' => __( 'Gamma' ),
1308                        'Zoom in' => __( 'Zoom in' ),
1309                        'Zoom out' => __( 'Zoom out' ),
1310                */
1311
1312                return self::$translation;
1313        }
1314
1315        /**
1316         * Translates the default TinyMCE strings and returns them as JSON encoded object ready to be loaded with tinymce.addI18n(),
1317         * or as JS snippet that should run after tinymce.js is loaded.
1318         *
1319         * @static
1320         * @param string $mce_locale The locale used for the editor.
1321         * @param bool $json_only optional Whether to include the JavaScript calls to tinymce.addI18n() and tinymce.ScriptLoader.markDone().
1322         * @return string Translation object, JSON encoded.
1323         */
1324        public static function wp_mce_translation( $mce_locale = '', $json_only = false ) {
1325                if ( ! $mce_locale ) {
1326                        $mce_locale = self::get_mce_locale();
1327                }
1328
1329                $mce_translation = self::get_translation();
1330
1331                foreach ( $mce_translation as $name => $value ) {
1332                        if ( is_array( $value ) ) {
1333                                $mce_translation[$name] = $value[0];
1334                        }
1335                }
1336
1337                /**
1338                 * Filters translated strings prepared for TinyMCE.
1339                 *
1340                 * @since 3.9.0
1341                 *
1342                 * @param array  $mce_translation Key/value pairs of strings.
1343                 * @param string $mce_locale      Locale.
1344                 */
1345                $mce_translation = apply_filters( 'wp_mce_translation', $mce_translation, $mce_locale );
1346
1347                foreach ( $mce_translation as $key => $value ) {
1348                        // Remove strings that are not translated.
1349                        if ( $key === $value ) {
1350                                unset( $mce_translation[$key] );
1351                                continue;
1352                        }
1353
1354                        if ( false !== strpos( $value, '&' ) ) {
1355                                $mce_translation[$key] = html_entity_decode( $value, ENT_QUOTES, 'UTF-8' );
1356                        }
1357                }
1358
1359                // Set direction
1360                if ( is_rtl() ) {
1361                        $mce_translation['_dir'] = 'rtl';
1362                }
1363
1364                if ( $json_only ) {
1365                        return wp_json_encode( $mce_translation );
1366                }
1367
1368                $baseurl = self::get_baseurl();
1369
1370                return "tinymce.addI18n( '$mce_locale', " . wp_json_encode( $mce_translation ) . ");\n" .
1371                        "tinymce.ScriptLoader.markDone( '$baseurl/langs/$mce_locale.js' );\n";
1372        }
1373
1374        /**
1375         * Print (output) the main TinyMCE scripts.
1376         *
1377         * @since 4.8
1378         *
1379         * @static
1380         * @global string $tinymce_version
1381         * @global bool   $concatenate_scripts
1382         * @global bool   $compress_scripts
1383         */
1384        public static function print_tinymce_scripts() {
1385                global $tinymce_version, $concatenate_scripts, $compress_scripts;
1386
1387                if ( self::$tinymce_scripts_printed ) {
1388                        return;
1389                }
1390
1391                self::$tinymce_scripts_printed = true;
1392
1393                if ( ! isset( $concatenate_scripts ) ) {
1394                        script_concat_settings();
1395                }
1396
1397                $suffix = SCRIPT_DEBUG ? '' : '.min';
1398                $version = 'ver=' . $tinymce_version;
1399                $baseurl = self::get_baseurl();
1400
1401                $compressed = $compress_scripts && $concatenate_scripts && isset($_SERVER['HTTP_ACCEPT_ENCODING'])
1402                        && false !== stripos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip');
1403
1404                // Load tinymce.js when running from /src, else load wp-tinymce.js.gz (production) or tinymce.min.js (SCRIPT_DEBUG)
1405                $mce_suffix = false !== strpos( get_bloginfo( 'version' ), '-src' ) ? '' : '.min';
1406
1407                if ( $compressed ) {
1408                        echo "<script type='text/javascript' src='{$baseurl}/wp-tinymce.php?c=1&amp;$version'></script>\n";
1409                } else {
1410                        echo "<script type='text/javascript' src='{$baseurl}/tinymce{$mce_suffix}.js?$version'></script>\n";
1411                        echo "<script type='text/javascript' src='{$baseurl}/plugins/compat3x/plugin{$suffix}.js?$version'></script>\n";
1412                }
1413
1414                echo "<script type='text/javascript'>\n" . self::wp_mce_translation() . "</script>\n";
1415        }
1416
1417        /**
1418         * Print (output) the TinyMCE configuration and initialization scripts.
1419         *
1420         * @static
1421         * @global string $tinymce_version
1422         */
1423        public static function editor_js() {
1424                global $tinymce_version;
1425
1426                $tmce_on = ! empty( self::$mce_settings );
1427                $mceInit = $qtInit = '';
1428
1429                if ( $tmce_on ) {
1430                        foreach ( self::$mce_settings as $editor_id => $init ) {
1431                                $options = self::_parse_init( $init );
1432                                $mceInit .= "'$editor_id':{$options},";
1433                        }
1434                        $mceInit = '{' . trim( $mceInit, ',' ) . '}';
1435                } else {
1436                        $mceInit = '{}';
1437                }
1438
1439                if ( ! empty( self::$qt_settings ) ) {
1440                        foreach ( self::$qt_settings as $editor_id => $init ) {
1441                                $options = self::_parse_init( $init );
1442                                $qtInit .= "'$editor_id':{$options},";
1443                        }
1444                        $qtInit = '{' . trim( $qtInit, ',' ) . '}';
1445                } else {
1446                        $qtInit = '{}';
1447                }
1448
1449                $ref = array(
1450                        'plugins' => implode( ',', self::$plugins ),
1451                        'theme' => 'modern',
1452                        'language' => self::$mce_locale
1453                );
1454
1455                $suffix = SCRIPT_DEBUG ? '' : '.min';
1456                $baseurl = self::get_baseurl();
1457                $version = 'ver=' . $tinymce_version;
1458
1459                /**
1460                 * Fires immediately before the TinyMCE settings are printed.
1461                 *
1462                 * @since 3.2.0
1463                 *
1464                 * @param array $mce_settings TinyMCE settings array.
1465                 */
1466                do_action( 'before_wp_tiny_mce', self::$mce_settings );
1467                ?>
1468
1469                <script type="text/javascript">
1470                tinyMCEPreInit = {
1471                        baseURL: "<?php echo $baseurl; ?>",
1472                        suffix: "<?php echo $suffix; ?>",
1473                        <?php
1474
1475                        if ( self::$drag_drop_upload ) {
1476                                echo 'dragDropUpload: true,';
1477                        }
1478
1479                        ?>
1480                        mceInit: <?php echo $mceInit; ?>,
1481                        qtInit: <?php echo $qtInit; ?>,
1482                        ref: <?php echo self::_parse_init( $ref ); ?>,
1483                        load_ext: function(url,lang){var sl=tinymce.ScriptLoader;sl.markDone(url+'/langs/'+lang+'.js');sl.markDone(url+'/langs/'+lang+'_dlg.js');}
1484                };
1485                </script>
1486                <?php
1487
1488                if ( $tmce_on ) {
1489                        self::print_tinymce_scripts();
1490
1491                        if ( self::$ext_plugins ) {
1492                                // Load the old-format English strings to prevent unsightly labels in old style popups
1493                                echo "<script type='text/javascript' src='{$baseurl}/langs/wp-langs-en.js?$version'></script>\n";
1494                        }
1495                }
1496
1497                /**
1498                 * Fires after tinymce.js is loaded, but before any TinyMCE editor
1499                 * instances are created.
1500                 *
1501                 * @since 3.9.0
1502                 *
1503                 * @param array $mce_settings TinyMCE settings array.
1504                 */
1505                do_action( 'wp_tiny_mce_init', self::$mce_settings );
1506
1507                ?>
1508                <script type="text/javascript">
1509                <?php
1510
1511                if ( self::$ext_plugins )
1512                        echo self::$ext_plugins . "\n";
1513
1514                if ( ! is_admin() )
1515                        echo 'var ajaxurl = "' . admin_url( 'admin-ajax.php', 'relative' ) . '";';
1516
1517                ?>
1518
1519                ( function() {
1520                        var init, id, $wrap;
1521
1522                        if ( typeof tinymce !== 'undefined' ) {
1523                                if ( tinymce.Env.ie && tinymce.Env.ie < 11 ) {
1524                                        tinymce.$( '.wp-editor-wrap ' ).removeClass( 'tmce-active' ).addClass( 'html-active' );
1525                                        return;
1526                                }
1527
1528                                for ( id in tinyMCEPreInit.mceInit ) {
1529                                        init = tinyMCEPreInit.mceInit[id];
1530                                        $wrap = tinymce.$( '#wp-' + id + '-wrap' );
1531
1532                                        if ( ( $wrap.hasClass( 'tmce-active' ) || ! tinyMCEPreInit.qtInit.hasOwnProperty( id ) ) && ! init.wp_skip_init ) {
1533                                                tinymce.init( init );
1534
1535                                                if ( ! window.wpActiveEditor ) {
1536                                                        window.wpActiveEditor = id;
1537                                                }
1538                                        }
1539                                }
1540                        }
1541
1542                        if ( typeof quicktags !== 'undefined' ) {
1543                                for ( id in tinyMCEPreInit.qtInit ) {
1544                                        quicktags( tinyMCEPreInit.qtInit[id] );
1545
1546                                        if ( ! window.wpActiveEditor ) {
1547                                                window.wpActiveEditor = id;
1548                                        }
1549                                }
1550                        }
1551                }());
1552                </script>
1553                <?php
1554
1555                if ( in_array( 'wplink', self::$plugins, true ) || in_array( 'link', self::$qt_buttons, true ) ) {
1556                        self::wp_link_dialog();
1557                }
1558
1559                /**
1560                 * Fires after any core TinyMCE editor instances are created.
1561                 *
1562                 * @since 3.2.0
1563                 *
1564                 * @param array $mce_settings TinyMCE settings array.
1565                 */
1566                do_action( 'after_wp_tiny_mce', self::$mce_settings );
1567        }
1568
1569        /**
1570         * Outputs the HTML for distraction-free writing mode.
1571         *
1572         * @since 3.2.0
1573         * @deprecated 4.3.0
1574         *
1575         * @static
1576         */
1577        public static function wp_fullscreen_html() {
1578                _deprecated_function( __FUNCTION__, '4.3.0' );
1579        }
1580
1581        /**
1582         * Performs post queries for internal linking.
1583         *
1584         * @since 3.1.0
1585         *
1586         * @static
1587         * @param array $args Optional. Accepts 'pagenum' and 's' (search) arguments.
1588         * @return false|array Results.
1589         */
1590        public static function wp_link_query( $args = array() ) {
1591                $pts = get_post_types( array( 'public' => true ), 'objects' );
1592                $pt_names = array_keys( $pts );
1593
1594                $query = array(
1595                        'post_type' => $pt_names,
1596                        'suppress_filters' => true,
1597                        'update_post_term_cache' => false,
1598                        'update_post_meta_cache' => false,
1599                        'post_status' => 'publish',
1600                        'posts_per_page' => 20,
1601                );
1602
1603                $args['pagenum'] = isset( $args['pagenum'] ) ? absint( $args['pagenum'] ) : 1;
1604
1605                if ( isset( $args['s'] ) )
1606                        $query['s'] = $args['s'];
1607
1608                $query['offset'] = $args['pagenum'] > 1 ? $query['posts_per_page'] * ( $args['pagenum'] - 1 ) : 0;
1609
1610                /**
1611                 * Filters the link query arguments.
1612                 *
1613                 * Allows modification of the link query arguments before querying.
1614                 *
1615                 * @see WP_Query for a full list of arguments
1616                 *
1617                 * @since 3.7.0
1618                 *
1619                 * @param array $query An array of WP_Query arguments.
1620                 */
1621                $query = apply_filters( 'wp_link_query_args', $query );
1622
1623                // Do main query.
1624                $get_posts = new WP_Query;
1625                $posts = $get_posts->query( $query );
1626                // Check if any posts were found.
1627                if ( ! $get_posts->post_count )
1628                        return false;
1629
1630                // Build results.
1631                $results = array();
1632                foreach ( $posts as $post ) {
1633                        if ( 'post' == $post->post_type )
1634                                $info = mysql2date( __( 'Y/m/d' ), $post->post_date );
1635                        else
1636                                $info = $pts[ $post->post_type ]->labels->singular_name;
1637
1638                        $results[] = array(
1639                                'ID' => $post->ID,
1640                                'title' => trim( esc_html( strip_tags( get_the_title( $post ) ) ) ),
1641                                'permalink' => get_permalink( $post->ID ),
1642                                'info' => $info,
1643                        );
1644                }
1645
1646                /**
1647                 * Filters the link query results.
1648                 *
1649                 * Allows modification of the returned link query results.
1650                 *
1651                 * @since 3.7.0
1652                 *
1653                 * @see 'wp_link_query_args' filter
1654                 *
1655                 * @param array $results {
1656                 *     An associative array of query results.
1657                 *
1658                 *     @type array {
1659                 *         @type int    $ID        Post ID.
1660                 *         @type string $title     The trimmed, escaped post title.
1661                 *         @type string $permalink Post permalink.
1662                 *         @type string $info      A 'Y/m/d'-formatted date for 'post' post type,
1663                 *                                 the 'singular_name' post type label otherwise.
1664                 *     }
1665                 * }
1666                 * @param array $query  An array of WP_Query arguments.
1667                 */
1668                return apply_filters( 'wp_link_query', $results, $query );
1669        }
1670
1671        /**
1672         * Dialog for internal linking.
1673         *
1674         * @since 3.1.0
1675         *
1676         * @static
1677         */
1678        public static function wp_link_dialog() {
1679                // Run once
1680                if ( self::$link_dialog_printed ) {
1681                        return;
1682                }
1683
1684                self::$link_dialog_printed = true;
1685
1686                // display: none is required here, see #WP27605
1687                ?>
1688                <div id="wp-link-backdrop" style="display: none"></div>
1689                <div id="wp-link-wrap" class="wp-core-ui" style="display: none" role="dialog" aria-labelledby="link-modal-title">
1690                <form id="wp-link" tabindex="-1">
1691                <?php wp_nonce_field( 'internal-linking', '_ajax_linking_nonce', false ); ?>
1692                <h1 id="link-modal-title"><?php _e( 'Insert/edit link' ) ?></h1>
1693                <button type="button" id="wp-link-close"><span class="screen-reader-text"><?php _e( 'Close' ); ?></span></button>
1694                <div id="link-selector">
1695                        <div id="link-options">
1696                                <p class="howto" id="wplink-enter-url"><?php _e( 'Enter the destination URL' ); ?></p>
1697                                <div>
1698                                        <label><span><?php _e( 'URL' ); ?></span>
1699                                        <input id="wp-link-url" type="text" aria-describedby="wplink-enter-url" /></label>
1700                                </div>
1701                                <div class="wp-link-text-field">
1702                                        <label><span><?php _e( 'Link Text' ); ?></span>
1703                                        <input id="wp-link-text" type="text" /></label>
1704                                </div>
1705                                <div class="link-target">
1706                                        <label><span></span>
1707                                        <input type="checkbox" id="wp-link-target" /> <?php _e( 'Open link in a new tab' ); ?></label>
1708                                </div>
1709                        </div>
1710                        <p class="howto" id="wplink-link-existing-content"><?php _e( 'Or link to existing content' ); ?></p>
1711                        <div id="search-panel">
1712                                <div class="link-search-wrapper">
1713                                        <label>
1714                                                <span class="search-label"><?php _e( 'Search' ); ?></span>
1715                                                <input type="search" id="wp-link-search" class="link-search-field" autocomplete="off" aria-describedby="wplink-link-existing-content" />
1716                                                <span class="spinner"></span>
1717                                        </label>
1718                                </div>
1719                                <div id="search-results" class="query-results" tabindex="0">
1720                                        <ul></ul>
1721                                        <div class="river-waiting">
1722                                                <span class="spinner"></span>
1723                                        </div>
1724                                </div>
1725                                <div id="most-recent-results" class="query-results" tabindex="0">
1726                                        <div class="query-notice" id="query-notice-message">
1727                                                <em class="query-notice-default"><?php _e( 'No search term specified. Showing recent items.' ); ?></em>
1728                                                <em class="query-notice-hint screen-reader-text"><?php _e( 'Search or use up and down arrow keys to select an item.' ); ?></em>
1729                                        </div>
1730                                        <ul></ul>
1731                                        <div class="river-waiting">
1732                                                <span class="spinner"></span>
1733                                        </div>
1734                                </div>
1735                        </div>
1736                </div>
1737                <div class="submitbox">
1738                        <div id="wp-link-cancel">
1739                                <button type="button" class="button"><?php _e( 'Cancel' ); ?></button>
1740                        </div>
1741                        <div id="wp-link-update">
1742                                <input type="submit" value="<?php esc_attr_e( 'Add Link' ); ?>" class="button button-primary" id="wp-link-submit" name="wp-link-submit">
1743                        </div>
1744                </div>
1745                </form>
1746                </div>
1747                <?php
1748        }
1749}