WordPress 4.2
[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                                                 'wpemoji',
358                                                 'wpgallery',
359                                                 'wplink',
360                                                 'wpdialogs',
361                                                 'wpview',
362                                         );
363
364                                         if ( ! self::$has_medialib ) {
365                                                 $plugins[] = 'image';
366                                         }
367
368                                         /**
369                                          * Filter the list of default TinyMCE plugins.
370                                          *
371                                          * The filter specifies which of the default plugins included
372                                          * in WordPress should be added to the TinyMCE instance.
373                                          *
374                                          * @since 3.3.0
375                                          *
376                                          * @param array $plugins An array of default TinyMCE plugins.
377                                          */
378                                         $plugins = array_unique( apply_filters( 'tiny_mce_plugins', $plugins ) );
379
380                                         if ( ( $key = array_search( 'spellchecker', $plugins ) ) !== false ) {
381                                                 // Remove 'spellchecker' from the internal plugins if added with 'tiny_mce_plugins' filter to prevent errors.
382                                                 // It can be added with 'mce_external_plugins'.
383                                                 unset( $plugins[$key] );
384                                         }
385
386                                         if ( ! empty( $mce_external_plugins ) ) {
387
388                                                 /**
389                                                  * Filter the translations loaded for external TinyMCE 3.x plugins.
390                                                  *
391                                                  * The filter takes an associative array ('plugin_name' => 'path')
392                                                  * where 'path' is the include path to the file.
393                                                  *
394                                                  * The language file should follow the same format as wp_mce_translation(),
395                                                  * and should define a variable ($strings) that holds all translated strings.
396                                                  *
397                                                  * @since 2.5.0
398                                                  *
399                                                  * @param array $translations Translations for external TinyMCE plugins.
400                                                  */
401                                                 $mce_external_languages = apply_filters( 'mce_external_languages', array() );
402
403                                                 $loaded_langs = array();
404                                                 $strings = '';
405
406                                                 if ( ! empty( $mce_external_languages ) ) {
407                                                         foreach ( $mce_external_languages as $name => $path ) {
408                                                                 if ( @is_file( $path ) && @is_readable( $path ) ) {
409                                                                         include_once( $path );
410                                                                         $ext_plugins .= $strings . "\n";
411                                                                         $loaded_langs[] = $name;
412                                                                 }
413                                                         }
414                                                 }
415
416                                                 foreach ( $mce_external_plugins as $name => $url ) {
417                                                         if ( in_array( $name, $plugins, true ) ) {
418                                                                 unset( $mce_external_plugins[ $name ] );
419                                                                 continue;
420                                                         }
421
422                                                         $url = set_url_scheme( $url );
423                                                         $mce_external_plugins[ $name ] = $url;
424                                                         $plugurl = dirname( $url );
425                                                         $strings = '';
426
427                                                         // Try to load langs/[locale].js and langs/[locale]_dlg.js
428                                                         if ( ! in_array( $name, $loaded_langs, true ) ) {
429                                                                 $path = str_replace( content_url(), '', $plugurl );
430                                                                 $path = WP_CONTENT_DIR . $path . '/langs/';
431
432                                                                 if ( function_exists('realpath') )
433                                                                         $path = trailingslashit( realpath($path) );
434
435                                                                 if ( @is_file( $path . $mce_locale . '.js' ) )
436                                                                         $strings .= @file_get_contents( $path . $mce_locale . '.js' ) . "\n";
437
438                                                                 if ( @is_file( $path . $mce_locale . '_dlg.js' ) )
439                                                                         $strings .= @file_get_contents( $path . $mce_locale . '_dlg.js' ) . "\n";
440
441                                                                 if ( 'en' != $mce_locale && empty( $strings ) ) {
442                                                                         if ( @is_file( $path . 'en.js' ) ) {
443                                                                                 $str1 = @file_get_contents( $path . 'en.js' );
444                                                                                 $strings .= preg_replace( '/([\'"])en\./', '$1' . $mce_locale . '.', $str1, 1 ) . "\n";
445                                                                         }
446
447                                                                         if ( @is_file( $path . 'en_dlg.js' ) ) {
448                                                                                 $str2 = @file_get_contents( $path . 'en_dlg.js' );
449                                                                                 $strings .= preg_replace( '/([\'"])en\./', '$1' . $mce_locale . '.', $str2, 1 ) . "\n";
450                                                                         }
451                                                                 }
452
453                                                                 if ( ! empty( $strings ) )
454                                                                         $ext_plugins .= "\n" . $strings . "\n";
455                                                         }
456
457                                                         $ext_plugins .= 'tinyMCEPreInit.load_ext("' . $plugurl . '", "' . $mce_locale . '");' . "\n";
458                                                         $ext_plugins .= 'tinymce.PluginManager.load("' . $name . '", "' . $url . '");' . "\n";
459                                                 }
460                                         }
461                                 }
462
463                                 if ( $set['dfw'] )
464                                         $plugins[] = 'wpfullscreen';
465
466                                 self::$plugins = $plugins;
467                                 self::$ext_plugins = $ext_plugins;
468
469                                 self::$first_init = array(
470                                         'theme' => 'modern',
471                                         'skin' => 'lightgray',
472                                         'language' => self::$mce_locale,
473                                         'formats' => "{
474                                                 alignleft: [
475                                                         {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles: {textAlign:'left'}},
476                                                         {selector: 'img,table,dl.wp-caption', classes: 'alignleft'}
477                                                 ],
478                                                 aligncenter: [
479                                                         {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles: {textAlign:'center'}},
480                                                         {selector: 'img,table,dl.wp-caption', classes: 'aligncenter'}
481                                                 ],
482                                                 alignright: [
483                                                         {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles: {textAlign:'right'}},
484                                                         {selector: 'img,table,dl.wp-caption', classes: 'alignright'}
485                                                 ],
486                                                 strikethrough: {inline: 'del'}
487                                         }",
488                                         'relative_urls' => false,
489                                         'remove_script_host' => false,
490                                         'convert_urls' => false,
491                                         'browser_spellcheck' => true,
492                                         'fix_list_elements' => true,
493                                         'entities' => '38,amp,60,lt,62,gt',
494                                         'entity_encoding' => 'raw',
495                                         'keep_styles' => false,
496                                         'cache_suffix' => 'wp-mce-' . $GLOBALS['tinymce_version'],
497
498                                         // Limit the preview styles in the menu/toolbar
499                                         'preview_styles' => 'font-family font-size font-weight font-style text-decoration text-transform',
500
501                                         'end_container_on_empty_block' => true,
502                                         'wpeditimage_disable_captions' => $no_captions,
503                                         'wpeditimage_html5_captions' => current_theme_supports( 'html5', 'caption' ),
504                                         'plugins' => implode( ',', $plugins ),
505                                 );
506
507                                 if ( ! empty( $mce_external_plugins ) ) {
508                                         self::$first_init['external_plugins'] = wp_json_encode( $mce_external_plugins );
509                                 }
510
511                                 $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
512                                 $version = 'ver=' . $GLOBALS['wp_version'];
513                                 $dashicons = includes_url( "css/dashicons$suffix.css?$version" );
514
515                                 // WordPress default stylesheet and dashicons
516                                 $mce_css = array(
517                                         $dashicons,
518                                         self::$baseurl . '/skins/wordpress/wp-content.css?' . $version
519                                 );
520
521                                 $editor_styles = get_editor_stylesheets();
522                                 if ( ! empty( $editor_styles ) ) {
523                                         foreach ( $editor_styles as $style ) {
524                                                 $mce_css[] = $style;
525                                         }
526                                 }
527
528                                 /**
529                                  * Filter the comma-delimited list of stylesheets to load in TinyMCE.
530                                  *
531                                  * @since 2.1.0
532                                  *
533                                  * @param array $stylesheets Comma-delimited list of stylesheets.
534                                  */
535                                 $mce_css = trim( apply_filters( 'mce_css', implode( ',', $mce_css ) ), ' ,' );
536
537                                 if ( ! empty($mce_css) )
538                                         self::$first_init['content_css'] = $mce_css;
539                         }
540
541                         if ( $set['teeny'] ) {
542
543                                 /**
544                                  * Filter the list of teenyMCE buttons (Text tab).
545                                  *
546                                  * @since 2.7.0
547                                  *
548                                  * @param array  $buttons   An array of teenyMCE buttons.
549                                  * @param string $editor_id Unique editor identifier, e.g. 'content'.
550                                  */
551                                 $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 );
552                                 $mce_buttons_2 = $mce_buttons_3 = $mce_buttons_4 = array();
553                         } else {
554                                 $mce_buttons = array( 'bold', 'italic', 'strikethrough', 'bullist', 'numlist', 'blockquote', 'hr', 'alignleft', 'aligncenter', 'alignright', 'link', 'unlink', 'wp_more', 'spellchecker' );
555
556                                 if ( $set['_content_editor_dfw'] ) {
557                                         $mce_buttons[] = 'dfw';
558                                 } else {
559                                         $mce_buttons[] = 'fullscreen';
560                                 }
561
562                                 $mce_buttons[] = 'wp_adv';
563
564                                 /**
565                                  * Filter the first-row list of TinyMCE buttons (Visual tab).
566                                  *
567                                  * @since 2.0.0
568                                  *
569                                  * @param array  $buttons   First-row list of buttons.
570                                  * @param string $editor_id Unique editor identifier, e.g. 'content'.
571                                  */
572                                 $mce_buttons = apply_filters( 'mce_buttons', $mce_buttons, $editor_id );
573
574                                 $mce_buttons_2 = array( 'formatselect', 'underline', 'alignjustify', 'forecolor', 'pastetext', 'removeformat', 'charmap', 'outdent', 'indent', 'undo', 'redo' );
575
576                                 if ( ! wp_is_mobile() ) {
577                                         $mce_buttons_2[] = 'wp_help';
578                                 }
579
580                                 /**
581                                  * Filter the second-row list of TinyMCE buttons (Visual tab).
582                                  *
583                                  * @since 2.0.0
584                                  *
585                                  * @param array  $buttons   Second-row list of buttons.
586                                  * @param string $editor_id Unique editor identifier, e.g. 'content'.
587                                  */
588                                 $mce_buttons_2 = apply_filters( 'mce_buttons_2', $mce_buttons_2, $editor_id );
589
590                                 /**
591                                  * Filter the third-row list of TinyMCE buttons (Visual tab).
592                                  *
593                                  * @since 2.0.0
594                                  *
595                                  * @param array  $buttons   Third-row list of buttons.
596                                  * @param string $editor_id Unique editor identifier, e.g. 'content'.
597                                  */
598                                 $mce_buttons_3 = apply_filters( 'mce_buttons_3', array(), $editor_id );
599
600                                 /**
601                                  * Filter the fourth-row list of TinyMCE buttons (Visual tab).
602                                  *
603                                  * @since 2.5.0
604                                  *
605                                  * @param array  $buttons   Fourth-row list of buttons.
606                                  * @param string $editor_id Unique editor identifier, e.g. 'content'.
607                                  */
608                                 $mce_buttons_4 = apply_filters( 'mce_buttons_4', array(), $editor_id );
609                         }
610
611                         $body_class = $editor_id;
612
613                         if ( $post = get_post() ) {
614                                 $body_class .= ' post-type-' . sanitize_html_class( $post->post_type ) . ' post-status-' . sanitize_html_class( $post->post_status );
615                                 if ( post_type_supports( $post->post_type, 'post-formats' ) ) {
616                                         $post_format = get_post_format( $post );
617                                         if ( $post_format && ! is_wp_error( $post_format ) )
618                                                 $body_class .= ' post-format-' . sanitize_html_class( $post_format );
619                                         else
620                                                 $body_class .= ' post-format-standard';
621                                 }
622                         }
623
624                         $body_class .= ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_locale() ) ) );
625
626                         if ( !empty($set['tinymce']['body_class']) ) {
627                                 $body_class .= ' ' . $set['tinymce']['body_class'];
628                                 unset($set['tinymce']['body_class']);
629                         }
630
631                         if ( $set['dfw'] ) {
632                                 // replace the first 'fullscreen' with 'wp_fullscreen'
633                                 if ( ($key = array_search('fullscreen', $mce_buttons)) !== false )
634                                         $mce_buttons[$key] = 'wp_fullscreen';
635                                 elseif ( ($key = array_search('fullscreen', $mce_buttons_2)) !== false )
636                                         $mce_buttons_2[$key] = 'wp_fullscreen';
637                                 elseif ( ($key = array_search('fullscreen', $mce_buttons_3)) !== false )
638                                         $mce_buttons_3[$key] = 'wp_fullscreen';
639                                 elseif ( ($key = array_search('fullscreen', $mce_buttons_4)) !== false )
640                                         $mce_buttons_4[$key] = 'wp_fullscreen';
641                         }
642
643                         $mceInit = array (
644                                 'selector' => "#$editor_id",
645                                 'resize' => 'vertical',
646                                 'menubar' => false,
647                                 'wpautop' => (bool) $set['wpautop'],
648                                 'indent' => ! $set['wpautop'],
649                                 'toolbar1' => implode($mce_buttons, ','),
650                                 'toolbar2' => implode($mce_buttons_2, ','),
651                                 'toolbar3' => implode($mce_buttons_3, ','),
652                                 'toolbar4' => implode($mce_buttons_4, ','),
653                                 'tabfocus_elements' => $set['tabfocus_elements'],
654                                 'body_class' => $body_class
655                         );
656
657                         if ( $first_run )
658                                 $mceInit = array_merge( self::$first_init, $mceInit );
659
660                         if ( is_array( $set['tinymce'] ) )
661                                 $mceInit = array_merge( $mceInit, $set['tinymce'] );
662
663                         /*
664                          * For people who really REALLY know what they're doing with TinyMCE
665                          * You can modify $mceInit to add, remove, change elements of the config
666                          * before tinyMCE.init. Setting "valid_elements", "invalid_elements"
667                          * and "extended_valid_elements" can be done through this filter. Best
668                          * is to use the default cleanup by not specifying valid_elements,
669                          * as TinyMCE contains full set of XHTML 1.0.
670                          */
671                         if ( $set['teeny'] ) {
672
673                                 /**
674                                  * Filter the teenyMCE config before init.
675                                  *
676                                  * @since 2.7.0
677                                  *
678                                  * @param array  $mceInit   An array with teenyMCE config.
679                                  * @param string $editor_id Unique editor identifier, e.g. 'content'.
680                                  */
681                                 $mceInit = apply_filters( 'teeny_mce_before_init', $mceInit, $editor_id );
682                         } else {
683
684                                 /**
685                                  * Filter the TinyMCE config before init.
686                                  *
687                                  * @since 2.5.0
688                                  *
689                                  * @param array  $mceInit   An array with TinyMCE config.
690                                  * @param string $editor_id Unique editor identifier, e.g. 'content'.
691                                  */
692                                 $mceInit = apply_filters( 'tiny_mce_before_init', $mceInit, $editor_id );
693                         }
694
695                         if ( empty( $mceInit['toolbar3'] ) && ! empty( $mceInit['toolbar4'] ) ) {
696                                 $mceInit['toolbar3'] = $mceInit['toolbar4'];
697                                 $mceInit['toolbar4'] = '';
698                         }
699
700                         self::$mce_settings[$editor_id] = $mceInit;
701                 } // end if self::$this_tinymce
702         }
703
704         private static function _parse_init($init) {
705                 $options = '';
706
707                 foreach ( $init as $k => $v ) {
708                         if ( is_bool($v) ) {
709                                 $val = $v ? 'true' : 'false';
710                                 $options .= $k . ':' . $val . ',';
711                                 continue;
712                         } elseif ( !empty($v) && is_string($v) && ( ('{' == $v{0} && '}' == $v{strlen($v) - 1}) || ('[' == $v{0} && ']' == $v{strlen($v) - 1}) || preg_match('/^\(?function ?\(/', $v) ) ) {
713                                 $options .= $k . ':' . $v . ',';
714                                 continue;
715                         }
716                         $options .= $k . ':"' . $v . '",';
717                 }
718
719                 return '{' . trim( $options, ' ,' ) . '}';
720         }
721
722         public static function enqueue_scripts() {
723                 wp_enqueue_script('word-count');
724
725                 if ( self::$has_tinymce )
726                         wp_enqueue_script('editor');
727
728                 if ( self::$has_quicktags ) {
729                         wp_enqueue_script( 'quicktags' );
730                         wp_enqueue_style( 'buttons' );
731                 }
732
733                 if ( in_array('wplink', self::$plugins, true) || in_array('link', self::$qt_buttons, true) ) {
734                         wp_enqueue_script('wplink');
735                 }
736
737                 if ( in_array('wpfullscreen', self::$plugins, true) || in_array('fullscreen', self::$qt_buttons, true) )
738                         wp_enqueue_script('wp-fullscreen');
739
740                 if ( self::$has_medialib ) {
741                         add_thickbox();
742                         wp_enqueue_script('media-upload');
743                 }
744
745                 /**
746                  * Fires when scripts and styles are enqueued for the editor.
747                  *
748                  * @since 3.9.0
749                  *
750                  * @param array $to_load An array containing boolean values whether TinyMCE
751                  *                       and Quicktags are being loaded.
752                  */
753                 do_action( 'wp_enqueue_editor', array(
754                         'tinymce'   => self::$has_tinymce,
755                         'quicktags' => self::$has_quicktags,
756                 ) );
757         }
758
759         /**
760          * Translates the default TinyMCE strings and returns them as JSON encoded object ready to be loaded with tinymce.addI18n().
761          * Can be used directly (_WP_Editors::wp_mce_translation()) by passing the same locale as set in the TinyMCE init object.
762          *
763          * @param string $mce_locale The locale used for the editor.
764          * @param bool $json_only optional Whether to include the JavaScript calls to tinymce.addI18n() and tinymce.ScriptLoader.markDone().
765          * @return string Translation object, JSON encoded.
766          */
767         public static function wp_mce_translation( $mce_locale = '', $json_only = false ) {
768
769                 $mce_translation = array(
770                         // Default TinyMCE strings
771                         'New document' => __( 'New document' ),
772                         'Formats' => _x( 'Formats', 'TinyMCE' ),
773
774                         'Headings' => _x( 'Headings', 'TinyMCE' ),
775                         'Heading 1' => __( 'Heading 1' ),
776                         'Heading 2' => __( 'Heading 2' ),
777                         'Heading 3' => __( 'Heading 3' ),
778                         'Heading 4' => __( 'Heading 4' ),
779                         'Heading 5' => __( 'Heading 5' ),
780                         'Heading 6' => __( 'Heading 6' ),
781
782                         /* translators: block tags */
783                         'Blocks' => _x( 'Blocks', 'TinyMCE' ),
784                         'Paragraph' => __( 'Paragraph' ),
785                         'Blockquote' => __( 'Blockquote' ),
786                         'Div' => _x( 'Div', 'HTML tag' ),
787                         'Pre' => _x( 'Pre', 'HTML tag' ),
788                         'Address' => _x( 'Address', 'HTML tag' ),
789
790                         'Inline' => _x( 'Inline', 'HTML elements' ),
791                         'Underline' => __( 'Underline' ),
792                         'Strikethrough' => __( 'Strikethrough' ),
793                         'Subscript' => __( 'Subscript' ),
794                         'Superscript' => __( 'Superscript' ),
795                         'Clear formatting' => __( 'Clear formatting' ),
796                         'Bold' => __( 'Bold' ),
797                         'Italic' => __( 'Italic' ),
798                         'Code' => _x( 'Code', 'editor button' ),
799                         'Source code' => __( 'Source code' ),
800                         'Font Family' => __( 'Font Family' ),
801                         'Font Sizes' => __( 'Font Sizes' ),
802
803                         'Align center' => __( 'Align center' ),
804                         'Align right' => __( 'Align right' ),
805                         'Align left' => __( 'Align left' ),
806                         'Justify' => __( 'Justify' ),
807                         'Increase indent' => __( 'Increase indent' ),
808                         'Decrease indent' => __( 'Decrease indent' ),
809
810                         'Cut' => __( 'Cut' ),
811                         'Copy' => __( 'Copy' ),
812                         'Paste' => __( 'Paste' ),
813                         'Select all' => __( 'Select all' ),
814                         'Undo' => __( 'Undo' ),
815                         'Redo' => __( 'Redo' ),
816
817                         'Ok' => __( 'OK' ),
818                         'Cancel' => __( 'Cancel' ),
819                         'Close' => __( 'Close' ),
820                         'Visual aids' => __( 'Visual aids' ),
821
822                         'Bullet list' => __( 'Bulleted list' ),
823                         'Numbered list' => __( 'Numbered list' ),
824                         'Square' => _x( 'Square', 'list style' ),
825                         'Default' => _x( 'Default', 'list style' ),
826                         'Circle' => _x( 'Circle', 'list style' ),
827                         'Disc' => _x('Disc', 'list style' ),
828                         'Lower Greek' => _x( 'Lower Greek', 'list style' ),
829                         'Lower Alpha' => _x( 'Lower Alpha', 'list style' ),
830                         'Upper Alpha' => _x( 'Upper Alpha', 'list style' ),
831                         'Upper Roman' => _x( 'Upper Roman', 'list style' ),
832                         'Lower Roman' => _x( 'Lower Roman', 'list style' ),
833
834                         // Anchor plugin
835                         'Name' => _x( 'Name', 'Name of link anchor (TinyMCE)' ),
836                         'Anchor' => _x( 'Anchor', 'Link anchor (TinyMCE)' ),
837                         'Anchors' => _x( 'Anchors', 'Link anchors (TinyMCE)' ),
838
839                         // Fullpage plugin
840                         'Document properties' => __( 'Document properties' ),
841                         'Robots' => __( 'Robots' ),
842                         'Title' => __( 'Title' ),
843                         'Keywords' => __( 'Keywords' ),
844                         'Encoding' => __( 'Encoding' ),
845                         'Description' => __( 'Description' ),
846                         'Author' => __( 'Author' ),
847
848                         // Media, image plugins
849                         'Insert/edit image' => __( 'Insert/edit image' ),
850                         'General' => __( 'General' ),
851                         'Advanced' => __( 'Advanced' ),
852                         'Source' => __( 'Source' ),
853                         'Border' => __( 'Border' ),
854                         'Constrain proportions' => __( 'Constrain proportions' ),
855                         'Vertical space' => __( 'Vertical space' ),
856                         'Image description' => __( 'Image description' ),
857                         'Style' => __( 'Style' ),
858                         'Dimensions' => __( 'Dimensions' ),
859                         'Insert image' => __( 'Insert image' ),
860                         'Insert date/time' => __( 'Insert date/time' ),
861                         'Insert/edit video' => __( 'Insert/edit video' ),
862                         'Poster' => __( 'Poster' ),
863                         'Alternative source' => __( 'Alternative source' ),
864                         'Paste your embed code below:' => __( 'Paste your embed code below:' ),
865                         'Insert video' => __( 'Insert video' ),
866                         'Embed' => __( 'Embed' ),
867
868                         // Each of these have a corresponding plugin
869                         'Special character' => __( 'Special character' ),
870                         'Right to left' => _x( 'Right to left', 'editor button' ),
871                         'Left to right' => _x( 'Left to right', 'editor button' ),
872                         'Emoticons' => __( 'Emoticons' ),
873                         'Nonbreaking space' => __( 'Nonbreaking space' ),
874                         'Page break' => __( 'Page break' ),
875                         'Paste as text' => __( 'Paste as text' ),
876                         'Preview' => __( 'Preview' ),
877                         'Print' => __( 'Print' ),
878                         'Save' => __( 'Save' ),
879                         'Fullscreen' => __( 'Fullscreen' ),
880                         'Horizontal line' => __( 'Horizontal line' ),
881                         'Horizontal space' => __( 'Horizontal space' ),
882                         'Restore last draft' => __( 'Restore last draft' ),
883                         'Insert/edit link' => __( 'Insert/edit link' ),
884                         'Remove link' => __( 'Remove link' ),
885
886                         'Color' => __( 'Color' ),
887                         'Custom color' => __( 'Custom color' ),
888                         'Custom...' => _x( 'Custom...', 'label for custom color' ),
889                         'No color' => __( 'No color' ),
890
891                         // Spelling, search/replace plugins
892                         'Could not find the specified string.' => __( 'Could not find the specified string.' ),
893                         'Replace' => _x( 'Replace', 'find/replace' ),
894                         'Next' => _x( 'Next', 'find/replace' ),
895                         /* translators: previous */
896                         'Prev' => _x( 'Prev', 'find/replace' ),
897                         'Whole words' => _x( 'Whole words', 'find/replace' ),
898                         'Find and replace' => __( 'Find and replace' ),
899                         'Replace with' => _x('Replace with', 'find/replace' ),
900                         'Find' => _x( 'Find', 'find/replace' ),
901                         'Replace all' => _x( 'Replace all', 'find/replace' ),
902                         'Match case' => __( 'Match case' ),
903                         'Spellcheck' => __( 'Check Spelling' ),
904                         'Finish' => _x( 'Finish', 'spellcheck' ),
905                         'Ignore all' => _x( 'Ignore all', 'spellcheck' ),
906                         'Ignore' => _x( 'Ignore', 'spellcheck' ),
907                         'Add to Dictionary' => __( 'Add to Dictionary' ),
908
909                         // TinyMCE tables
910                         'Insert table' => __( 'Insert table' ),
911                         'Delete table' => __( 'Delete table' ),
912                         'Table properties' => __( 'Table properties' ),
913                         'Row properties' => __( 'Table row properties' ),
914                         'Cell properties' => __( 'Table cell properties' ),
915                         'Border color' => __( 'Border color' ),
916
917                         'Row' => __( 'Row' ),
918                         'Rows' => __( 'Rows' ),
919                         'Column' => _x( 'Column', 'table column' ),
920                         'Cols' => _x( 'Cols', 'table columns' ),
921                         'Cell' => _x( 'Cell', 'table cell' ),
922                         'Header cell' => __( 'Header cell' ),
923                         'Header' => _x( 'Header', 'table header' ),
924                         'Body' => _x( 'Body', 'table body' ),
925                         'Footer' => _x( 'Footer', 'table footer' ),
926
927                         'Insert row before' => __( 'Insert row before' ),
928                         'Insert row after' => __( 'Insert row after' ),
929                         'Insert column before' => __( 'Insert column before' ),
930                         'Insert column after' => __( 'Insert column after' ),
931                         'Paste row before' => __( 'Paste table row before' ),
932                         'Paste row after' => __( 'Paste table row after' ),
933                         'Delete row' => __( 'Delete row' ),
934                         'Delete column' => __( 'Delete column' ),
935                         'Cut row' => __( 'Cut table row' ),
936                         'Copy row' => __( 'Copy table row' ),
937                         'Merge cells' => __( 'Merge table cells' ),
938                         'Split cell' => __( 'Split table cell' ),
939
940                         'Height' => __( 'Height' ),
941                         'Width' => __( 'Width' ),
942                         'Caption' => __( 'Caption' ),
943                         'Alignment' => __( 'Alignment' ),
944                         'H Align' => _x( 'H Align', 'horizontal table cell alignment' ),
945                         'Left' => __( 'Left' ),
946                         'Center' => __( 'Center' ),
947                         'Right' => __( 'Right' ),
948                         'None' => _x( 'None', 'table cell alignment attribute' ),
949                         'V Align' => _x( 'V Align', 'vertical table cell alignment' ),
950                         'Top' => __( 'Top' ),
951                         'Middle' => __( 'Middle' ),
952                         'Bottom' => __( 'Bottom' ),
953
954                         'Row group' => __( 'Row group' ),
955                         'Column group' => __( 'Column group' ),
956                         'Row type' => __( 'Row type' ),
957                         'Cell type' => __( 'Cell type' ),
958                         'Cell padding' => __( 'Cell padding' ),
959                         'Cell spacing' => __( 'Cell spacing' ),
960                         'Scope' => _x( 'Scope', 'table cell scope attribute' ),
961
962                         'Insert template' => _x( 'Insert template', 'TinyMCE' ),
963                         'Templates' => _x( 'Templates', 'TinyMCE' ),
964
965                         'Background color' => __( 'Background color' ),
966                         'Text color' => __( 'Text color' ),
967                         'Show blocks' => _x( 'Show blocks', 'editor button' ),
968                         'Show invisible characters' => __( 'Show invisible characters' ),
969
970                         /* translators: word count */
971                         'Words: {0}' => sprintf( __( 'Words: %s' ), '{0}' ),
972                         '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.' ),
973                         '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' ),
974                         '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.' ),
975                         '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.' ),
976
977                         // TinyMCE menus
978                         'Insert' => _x( 'Insert', 'TinyMCE menu' ),
979                         'File' => _x( 'File', 'TinyMCE menu' ),
980                         'Edit' => _x( 'Edit', 'TinyMCE menu' ),
981                         'Tools' => _x( 'Tools', 'TinyMCE menu' ),
982                         'View' => _x( 'View', 'TinyMCE menu' ),
983                         'Table' => _x( 'Table', 'TinyMCE menu' ),
984                         'Format' => _x( 'Format', 'TinyMCE menu' ),
985
986                         // WordPress strings
987                         'Keyboard Shortcuts' => __( 'Keyboard Shortcuts' ),
988                         'Toolbar Toggle' => __( 'Toolbar Toggle' ),
989                         'Insert Read More tag' => __( 'Insert Read More tag' ),
990                         'Read more...' => __( 'Read more...' ), // Title on the placeholder inside the editor
991                         'Distraction-free writing mode' => __( 'Distraction-free writing mode' ),
992                         'No alignment' => __( 'No alignment' ), // Tooltip for the 'alignnone' button in the image toolbar
993                         'Remove' => __( 'Remove' ), // Tooltip for the 'remove' button in the image toolbar
994                         'Edit ' => __( 'Edit' ), // Tooltip for the 'edit' button in the image toolbar
995                 );
996
997                 /**
998                  * Link plugin (not included):
999                  *      Insert link
1000                  *      Target
1001                  *      New window
1002                  *      Text to display
1003                  *      The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?
1004                  *      The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?
1005                  *      Url
1006                  */
1007
1008                 if ( ! $mce_locale ) {
1009                         $mce_locale = self::$mce_locale;
1010                 }
1011
1012                 /**
1013                  * Filter translated strings prepared for TinyMCE.
1014                  *
1015                  * @since 3.9.0
1016                  *
1017                  * @param array  $mce_translation Key/value pairs of strings.
1018                  * @param string $mce_locale      Locale.
1019                  */
1020                 $mce_translation = apply_filters( 'wp_mce_translation', $mce_translation, $mce_locale );
1021
1022                 foreach ( $mce_translation as $key => $value ) {
1023                         // Remove strings that are not translated.
1024                         if ( $key === $value ) {
1025                                 unset( $mce_translation[$key] );
1026                                 continue;
1027                         }
1028
1029                         if ( false !== strpos( $value, '&' ) ) {
1030                                 $mce_translation[$key] = html_entity_decode( $value, ENT_QUOTES, 'UTF-8' );
1031                         }
1032                 }
1033
1034                 // Set direction
1035                 if ( is_rtl() ) {
1036                         $mce_translation['_dir'] = 'rtl';
1037                 }
1038
1039                 if ( $json_only ) {
1040                         return wp_json_encode( $mce_translation );
1041                 }
1042
1043                 $baseurl = self::$baseurl ? self::$baseurl : includes_url( 'js/tinymce' );
1044
1045                 return "tinymce.addI18n( '$mce_locale', " . wp_json_encode( $mce_translation ) . ");\n" .
1046                         "tinymce.ScriptLoader.markDone( '$baseurl/langs/$mce_locale.js' );\n";
1047         }
1048
1049         public static function editor_js() {
1050                 global $tinymce_version, $concatenate_scripts, $compress_scripts;
1051
1052                 /**
1053                  * Filter "tiny_mce_version" is deprecated
1054                  *
1055                  * The tiny_mce_version filter is not needed since external plugins are loaded directly by TinyMCE.
1056                  * These plugins can be refreshed by appending query string to the URL passed to "mce_external_plugins" filter.
1057                  * 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).
1058                  */
1059                 $version = 'ver=' . $tinymce_version;
1060                 $tmce_on = !empty(self::$mce_settings);
1061
1062                 if ( ! isset($concatenate_scripts) )
1063                         script_concat_settings();
1064
1065                 $compressed = $compress_scripts && $concatenate_scripts && isset($_SERVER['HTTP_ACCEPT_ENCODING'])
1066                         && false !== stripos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip');
1067
1068                 $mceInit = $qtInit = '';
1069                 if ( $tmce_on ) {
1070                         foreach ( self::$mce_settings as $editor_id => $init ) {
1071                                 $options = self::_parse_init( $init );
1072                                 $mceInit .= "'$editor_id':{$options},";
1073                         }
1074                         $mceInit = '{' . trim($mceInit, ',') . '}';
1075                 } else {
1076                         $mceInit = '{}';
1077                 }
1078
1079                 if ( !empty(self::$qt_settings) ) {
1080                         foreach ( self::$qt_settings as $editor_id => $init ) {
1081                                 $options = self::_parse_init( $init );
1082                                 $qtInit .= "'$editor_id':{$options},";
1083                         }
1084                         $qtInit = '{' . trim($qtInit, ',') . '}';
1085                 } else {
1086                         $qtInit = '{}';
1087                 }
1088
1089                 $ref = array(
1090                         'plugins' => implode( ',', self::$plugins ),
1091                         'theme' => 'modern',
1092                         'language' => self::$mce_locale
1093                 );
1094
1095                 $suffix = ( defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ) ? '' : '.min';
1096
1097                 /**
1098                  * Fires immediately before the TinyMCE settings are printed.
1099                  *
1100                  * @since 3.2.0
1101                  *
1102                  * @param array $mce_settings TinyMCE settings array.
1103                  */
1104                 do_action( 'before_wp_tiny_mce', self::$mce_settings );
1105                 ?>
1106
1107                 <script type="text/javascript">
1108                 tinyMCEPreInit = {
1109                         baseURL: "<?php echo self::$baseurl; ?>",
1110                         suffix: "<?php echo $suffix; ?>",
1111                         <?php
1112
1113                         if ( self::$drag_drop_upload ) {
1114                                 echo 'dragDropUpload: true,';
1115                         }
1116
1117                         ?>
1118                         mceInit: <?php echo $mceInit; ?>,
1119                         qtInit: <?php echo $qtInit; ?>,
1120                         ref: <?php echo self::_parse_init( $ref ); ?>,
1121                         load_ext: function(url,lang){var sl=tinymce.ScriptLoader;sl.markDone(url+'/langs/'+lang+'.js');sl.markDone(url+'/langs/'+lang+'_dlg.js');}
1122                 };
1123                 </script>
1124                 <?php
1125
1126                 $baseurl = self::$baseurl;
1127                 // Load tinymce.js when running from /src, else load wp-tinymce.js.gz (production) or tinymce.min.js (SCRIPT_DEBUG)
1128                 $mce_suffix = false !== strpos( $GLOBALS['wp_version'], '-src' ) ? '' : '.min';
1129
1130                 if ( $tmce_on ) {
1131                         if ( $compressed ) {
1132                                 echo "<script type='text/javascript' src='{$baseurl}/wp-tinymce.php?c=1&amp;$version'></script>\n";
1133                         } else {
1134                                 echo "<script type='text/javascript' src='{$baseurl}/tinymce{$mce_suffix}.js?$version'></script>\n";
1135                                 echo "<script type='text/javascript' src='{$baseurl}/plugins/compat3x/plugin{$suffix}.js?$version'></script>\n";
1136                         }
1137
1138                         echo "<script type='text/javascript'>\n" . self::wp_mce_translation() . "</script>\n";
1139
1140                         if ( self::$ext_plugins ) {
1141                                 // Load the old-format English strings to prevent unsightly labels in old style popups
1142                                 echo "<script type='text/javascript' src='{$baseurl}/langs/wp-langs-en.js?$version'></script>\n";
1143                         }
1144                 }
1145
1146                 /**
1147                  * Fires after tinymce.js is loaded, but before any TinyMCE editor
1148                  * instances are created.
1149                  *
1150                  * @since 3.9.0
1151                  *
1152                  * @param array $mce_settings TinyMCE settings array.
1153                  */
1154                 do_action( 'wp_tiny_mce_init', self::$mce_settings );
1155
1156                 ?>
1157                 <script type="text/javascript">
1158                 <?php
1159
1160                 if ( self::$ext_plugins )
1161                         echo self::$ext_plugins . "\n";
1162
1163                 if ( ! is_admin() )
1164                         echo 'var ajaxurl = "' . admin_url( 'admin-ajax.php', 'relative' ) . '";';
1165
1166                 ?>
1167
1168                 ( function() {
1169                         var init, edId, qtId, firstInit, wrapper;
1170
1171                         if ( typeof tinymce !== 'undefined' ) {
1172                                 for ( edId in tinyMCEPreInit.mceInit ) {
1173                                         if ( firstInit ) {
1174                                                 init = tinyMCEPreInit.mceInit[edId] = tinymce.extend( {}, firstInit, tinyMCEPreInit.mceInit[edId] );
1175                                         } else {
1176                                                 init = firstInit = tinyMCEPreInit.mceInit[edId];
1177                                         }
1178
1179                                         wrapper = tinymce.DOM.select( '#wp-' + edId + '-wrap' )[0];
1180
1181                                         if ( ( tinymce.DOM.hasClass( wrapper, 'tmce-active' ) || ! tinyMCEPreInit.qtInit.hasOwnProperty( edId ) ) &&
1182                                                 ! init.wp_skip_init ) {
1183
1184                                                 try {
1185                                                         tinymce.init( init );
1186
1187                                                         if ( ! window.wpActiveEditor ) {
1188                                                                 window.wpActiveEditor = edId;
1189                                                         }
1190                                                 } catch(e){}
1191                                         }
1192                                 }
1193                         }
1194
1195                         if ( typeof quicktags !== 'undefined' ) {
1196                                 for ( qtId in tinyMCEPreInit.qtInit ) {
1197                                         try {
1198                                                 quicktags( tinyMCEPreInit.qtInit[qtId] );
1199
1200                                                 if ( ! window.wpActiveEditor ) {
1201                                                         window.wpActiveEditor = qtId;
1202                                                 }
1203                                         } catch(e){};
1204                                 }
1205                         }
1206
1207                         if ( typeof jQuery !== 'undefined' ) {
1208                                 jQuery('.wp-editor-wrap').on( 'click.wp-editor', function() {
1209                                         if ( this.id ) {
1210                                                 window.wpActiveEditor = this.id.slice( 3, -5 );
1211                                         }
1212                                 });
1213                         } else {
1214                                 for ( qtId in tinyMCEPreInit.qtInit ) {
1215                                         document.getElementById( 'wp-' + qtId + '-wrap' ).onclick = function() {
1216                                                 window.wpActiveEditor = this.id.slice( 3, -5 );
1217                                         }
1218                                 }
1219                         }
1220                 }());
1221                 </script>
1222                 <?php
1223
1224                 if ( in_array( 'wplink', self::$plugins, true ) || in_array( 'link', self::$qt_buttons, true ) )
1225                         self::wp_link_dialog();
1226
1227                 if ( in_array( 'wpfullscreen', self::$plugins, true ) || in_array( 'fullscreen', self::$qt_buttons, true ) )
1228                         self::wp_fullscreen_html();
1229
1230                 /**
1231                  * Fires after any core TinyMCE editor instances are created.
1232                  *
1233                  * @since 3.2.0
1234                  *
1235                  * @param array $mce_settings TinyMCE settings array.
1236                  */
1237                 do_action( 'after_wp_tiny_mce', self::$mce_settings );
1238         }
1239
1240         public static function wp_fullscreen_html() {
1241                 global $content_width;
1242                 $post = get_post();
1243
1244                 $width = isset( $content_width ) && 800 > $content_width ? $content_width : 800;
1245                 $width = $width + 22; // compensate for the padding and border
1246                 $dfw_width = get_user_setting( 'dfw_width', $width );
1247                 $save = $post && $post->post_status == 'publish' ? __('Update') : __('Save');
1248
1249                 ?>
1250                 <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; ?>">
1251                 <div id="fullscreen-topbar">
1252                         <div id="wp-fullscreen-toolbar">
1253                         <div id="wp-fullscreen-close"><a href="#" onclick="wp.editor.fullscreen.off();return false;"><?php _e('Exit fullscreen'); ?></a></div>
1254                         <div id="wp-fullscreen-central-toolbar" style="width:<?php echo $width; ?>px;">
1255
1256                         <div id="wp-fullscreen-mode-bar">
1257                                 <div id="wp-fullscreen-modes" class="button-group">
1258                                         <a class="button wp-fullscreen-mode-tinymce" href="#" onclick="wp.editor.fullscreen.switchmode( 'tinymce' ); return false;"><?php _e( 'Visual' ); ?></a>
1259                                         <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>
1260                                 </div>
1261                         </div>
1262
1263                         <div id="wp-fullscreen-button-bar"><div id="wp-fullscreen-buttons" class="mce-toolbar">
1264                 <?php
1265
1266                 $buttons = array(
1267                         // format: title, onclick, show in both editors
1268                         'bold' => array( 'title' => __('Bold (Ctrl + B)'), 'both' => false ),
1269                         'italic' => array( 'title' => __('Italic (Ctrl + I)'), 'both' => false ),
1270                         'bullist' => array( 'title' => __('Unordered list (Alt + Shift + U)'), 'both' => false ),
1271                         'numlist' => array( 'title' => __('Ordered list (Alt + Shift + O)'), 'both' => false ),
1272                         'blockquote' => array( 'title' => __('Blockquote (Alt + Shift + Q)'), 'both' => false ),
1273                         'wp-media-library' => array( 'title' => __('Media library (Alt + Shift + M)'), 'both' => true ),
1274                         'link' => array( 'title' => __('Insert/edit link (Alt + Shift + A)'), 'both' => true ),
1275                         'unlink' => array( 'title' => __('Unlink (Alt + Shift + S)'), 'both' => false ),
1276                         'help' => array( 'title' => __('Help (Alt + Shift + H)'), 'both' => false ),
1277                 );
1278
1279                 /**
1280                  * Filter the list of TinyMCE buttons for the fullscreen
1281                  * 'Distraction-Free Writing' editor.
1282                  *
1283                  * @since 3.2.0
1284                  *
1285                  * @param array $buttons An array of TinyMCE buttons for the DFW editor.
1286                  */
1287                 $buttons = apply_filters( 'wp_fullscreen_buttons', $buttons );
1288
1289                 foreach ( $buttons as $button => $args ) {
1290                         if ( 'separator' == $args ) {
1291                                 continue;
1292                         }
1293
1294                         $onclick = ! empty( $args['onclick'] ) ? ' onclick="' . $args['onclick'] . '"' : '';
1295                         $title = esc_attr( $args['title'] );
1296                         ?>
1297
1298                         <div class="mce-widget mce-btn<?php if ( $args['both'] ) { ?> wp-fullscreen-both<?php } ?>">
1299                         <button type="button" aria-label="<?php echo $title; ?>" title="<?php echo $title; ?>"<?php echo $onclick; ?> id="wp_fs_<?php echo $button; ?>">
1300                                 <i class="mce-ico mce-i-<?php echo $button; ?>"></i>
1301                         </button>
1302                         </div>
1303                         <?php
1304                 }
1305
1306                 ?>
1307
1308                 </div></div>
1309
1310                 <div id="wp-fullscreen-save">
1311                         <input type="button" class="button button-primary right" value="<?php echo $save; ?>" onclick="wp.editor.fullscreen.save();" />
1312                         <span class="wp-fullscreen-saved-message"><?php if ( $post && $post->post_status == 'publish' ) _e('Updated.'); else _e('Saved.'); ?></span>
1313                         <span class="wp-fullscreen-error-message"><?php _e('Save failed.'); ?></span>
1314                         <span class="spinner"></span>
1315                 </div>
1316
1317                 </div>
1318                 </div>
1319         </div>
1320         <div id="wp-fullscreen-statusbar">
1321                 <div id="wp-fullscreen-status">
1322                         <div id="wp-fullscreen-count"><?php printf( __( 'Word count: %s' ), '<span class="word-count">0</span>' ); ?></div>
1323                         <div id="wp-fullscreen-tagline"><?php _e('Just write.'); ?></div>
1324                 </div>
1325         </div>
1326         </div>
1327
1328         <div class="fullscreen-overlay" id="fullscreen-overlay"></div>
1329         <div class="fullscreen-overlay fullscreen-fader fade-300" id="fullscreen-fader"></div>
1330         <?php
1331         }
1332
1333         /**
1334          * Performs post queries for internal linking.
1335          *
1336          * @since 3.1.0
1337          *
1338          * @param array $args Optional. Accepts 'pagenum' and 's' (search) arguments.
1339          * @return false|array Results.
1340          */
1341         public static function wp_link_query( $args = array() ) {
1342                 $pts = get_post_types( array( 'public' => true ), 'objects' );
1343                 $pt_names = array_keys( $pts );
1344
1345                 $query = array(
1346                         'post_type' => $pt_names,
1347                         'suppress_filters' => true,
1348                         'update_post_term_cache' => false,
1349                         'update_post_meta_cache' => false,
1350                         'post_status' => 'publish',
1351                         'posts_per_page' => 20,
1352                 );
1353
1354                 $args['pagenum'] = isset( $args['pagenum'] ) ? absint( $args['pagenum'] ) : 1;
1355
1356                 if ( isset( $args['s'] ) )
1357                         $query['s'] = $args['s'];
1358
1359                 $query['offset'] = $args['pagenum'] > 1 ? $query['posts_per_page'] * ( $args['pagenum'] - 1 ) : 0;
1360
1361                 /**
1362                  * Filter the link query arguments.
1363                  *
1364                  * Allows modification of the link query arguments before querying.
1365                  *
1366                  * @see WP_Query for a full list of arguments
1367                  *
1368                  * @since 3.7.0
1369                  *
1370                  * @param array $query An array of WP_Query arguments.
1371                  */
1372                 $query = apply_filters( 'wp_link_query_args', $query );
1373
1374                 // Do main query.
1375                 $get_posts = new WP_Query;
1376                 $posts = $get_posts->query( $query );
1377                 // Check if any posts were found.
1378                 if ( ! $get_posts->post_count )
1379                         return false;
1380
1381                 // Build results.
1382                 $results = array();
1383                 foreach ( $posts as $post ) {
1384                         if ( 'post' == $post->post_type )
1385                                 $info = mysql2date( __( 'Y/m/d' ), $post->post_date );
1386                         else
1387                                 $info = $pts[ $post->post_type ]->labels->singular_name;
1388
1389                         $results[] = array(
1390                                 'ID' => $post->ID,
1391                                 'title' => trim( esc_html( strip_tags( get_the_title( $post ) ) ) ),
1392                                 'permalink' => get_permalink( $post->ID ),
1393                                 'info' => $info,
1394                         );
1395                 }
1396
1397                 /**
1398                  * Filter the link query results.
1399                  *
1400                  * Allows modification of the returned link query results.
1401                  *
1402                  * @since 3.7.0
1403                  *
1404                  * @see 'wp_link_query_args' filter
1405                  *
1406                  * @param array $results {
1407                  *     An associative array of query results.
1408                  *
1409                  *     @type array {
1410                  *         @type int    $ID        Post ID.
1411                  *         @type string $title     The trimmed, escaped post title.
1412                  *         @type string $permalink Post permalink.
1413                  *         @type string $info      A 'Y/m/d'-formatted date for 'post' post type,
1414                  *                                 the 'singular_name' post type label otherwise.
1415                  *     }
1416                  * }
1417                  * @param array $query  An array of WP_Query arguments.
1418                  */
1419                 return apply_filters( 'wp_link_query', $results, $query );
1420         }
1421
1422         /**
1423          * Dialog for internal linking.
1424          *
1425          * @since 3.1.0
1426          */
1427         public static function wp_link_dialog() {
1428                 $search_panel_visible = '1' == get_user_setting( 'wplink', '0' ) ? ' search-panel-visible' : '';
1429
1430                 // display: none is required here, see #WP27605
1431                 ?>
1432                 <div id="wp-link-backdrop" style="display: none"></div>
1433                 <div id="wp-link-wrap" class="wp-core-ui<?php echo $search_panel_visible; ?>" style="display: none">
1434                 <form id="wp-link" tabindex="-1">
1435                 <?php wp_nonce_field( 'internal-linking', '_ajax_linking_nonce', false ); ?>
1436                 <div id="link-modal-title">
1437                         <?php _e( 'Insert/edit link' ) ?>
1438                         <button type="button" id="wp-link-close"><span class="screen-reader-text"><?php _e( 'Close' ); ?></span></button>
1439                 </div>
1440                 <div id="link-selector">
1441                         <div id="link-options">
1442                                 <p class="howto"><?php _e( 'Enter the destination URL' ); ?></p>
1443                                 <div>
1444                                         <label><span><?php _e( 'URL' ); ?></span><input id="wp-link-url" type="text" /></label>
1445                                 </div>
1446                                 <div class="wp-link-text-field">
1447                                         <label><span><?php _e( 'Link Text' ); ?></span><input id="wp-link-text" type="text" /></label>
1448                                 </div>
1449                                 <div class="link-target">
1450                                         <label><span>&nbsp;</span><input type="checkbox" id="wp-link-target" /> <?php _e( 'Open link in a new window/tab' ); ?></label>
1451                                 </div>
1452                         </div>
1453                         <p class="howto"><a href="#" id="wp-link-search-toggle"><?php _e( 'Or link to existing content' ); ?></a></p>
1454                         <div id="search-panel">
1455                                 <div class="link-search-wrapper">
1456                                         <label>
1457                                                 <span class="search-label"><?php _e( 'Search' ); ?></span>
1458                                                 <input type="search" id="wp-link-search" class="link-search-field" autocomplete="off" />
1459                                                 <span class="spinner"></span>
1460                                         </label>
1461                                 </div>
1462                                 <div id="search-results" class="query-results" tabindex="0">
1463                                         <ul></ul>
1464                                         <div class="river-waiting">
1465                                                 <span class="spinner"></span>
1466                                         </div>
1467                                 </div>
1468                                 <div id="most-recent-results" class="query-results" tabindex="0">
1469                                         <div class="query-notice" id="query-notice-message">
1470                                                 <em class="query-notice-default"><?php _e( 'No search term specified. Showing recent items.' ); ?></em>
1471                                                 <em class="query-notice-hint screen-reader-text"><?php _e( 'Search or use up and down arrow keys to select an item.' ); ?></em>
1472                                         </div>
1473                                         <ul></ul>
1474                                         <div class="river-waiting">
1475                                                 <span class="spinner"></span>
1476                                         </div>
1477                                 </div>
1478                         </div>
1479                 </div>
1480                 <div class="submitbox">
1481                         <div id="wp-link-cancel">
1482                                 <a class="submitdelete deletion" href="#"><?php _e( 'Cancel' ); ?></a>
1483                         </div>
1484                         <div id="wp-link-update">
1485                                 <input type="submit" value="<?php esc_attr_e( 'Add Link' ); ?>" class="button button-primary" id="wp-link-submit" name="wp-link-submit">
1486                         </div>
1487                 </div>
1488                 </form>
1489                 </div>
1490                 <?php
1491         }
1492 }