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