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