Wordpress 3.5
[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
28         private function __construct() {}
29
30         public static function parse_settings($editor_id, $settings) {
31                 $set = wp_parse_args( $settings,  array(
32                         'wpautop' => true, // use wpautop?
33                         'media_buttons' => true, // show insert/upload button(s)
34                         'textarea_name' => $editor_id, // set the textarea name to something different, square brackets [] can be used here
35                         'textarea_rows' => 20,
36                         'tabindex' => '',
37                         'tabfocus_elements' => ':prev,:next', // the previous and next element ID to move the focus to when pressing the Tab key in TinyMCE
38                         'editor_css' => '', // intended for extra styles for both visual and Text editors buttons, needs to include the <style> tags, can use "scoped".
39                         'editor_class' => '', // add extra class(es) to the editor textarea
40                         'teeny' => false, // output the minimal editor config used in Press This
41                         'dfw' => false, // replace the default fullscreen with DFW (needs specific DOM elements and css)
42                         'tinymce' => true, // load TinyMCE, can be used to pass settings directly to TinyMCE using an array()
43                         'quicktags' => true // load Quicktags, can be used to pass settings directly to Quicktags using an array()
44                 ) );
45
46                 self::$this_tinymce = ( $set['tinymce'] && user_can_richedit() );
47                 self::$this_quicktags = (bool) $set['quicktags'];
48
49                 if ( self::$this_tinymce )
50                         self::$has_tinymce = true;
51
52                 if ( self::$this_quicktags )
53                         self::$has_quicktags = true;
54
55                 if ( empty( $set['editor_height'] ) )
56                         return $set;
57
58                 if ( 'content' === $editor_id ) {
59                         // A cookie (set when a user resizes the editor) overrides the height.
60                         $cookie = (int) get_user_setting( 'ed_size' );
61
62                         // Upgrade an old TinyMCE cookie if it is still around, and the new one isn't.
63                         if ( ! $cookie && isset( $_COOKIE['TinyMCE_content_size'] ) ) {
64                                 parse_str( $_COOKIE['TinyMCE_content_size'], $cookie );
65                                 $cookie = $cookie['ch'];
66                         }
67
68                         if ( $cookie )
69                                 $set['editor_height'] = $cookie;
70                 }
71
72                 if ( $set['editor_height'] < 50 )
73                         $set['editor_height'] = 50;
74                 elseif ( $set['editor_height'] > 5000 )
75                         $set['editor_height'] = 5000;
76
77                 return $set;
78         }
79
80         /**
81          * Outputs the HTML for a single instance of the editor.
82          *
83          * @param string $content The initial content of the editor.
84          * @param string $editor_id ID for the textarea and TinyMCE and Quicktags instances (can contain only ASCII letters and numbers).
85          * @param array $settings See the _parse_settings() method for description.
86          */
87         public static function editor( $content, $editor_id, $settings = array() ) {
88
89                 $set = self::parse_settings($editor_id, $settings);
90                 $editor_class = ' class="' . trim( $set['editor_class'] . ' wp-editor-area' ) . '"';
91                 $tabindex = $set['tabindex'] ? ' tabindex="' . (int) $set['tabindex'] . '"' : '';
92                 $switch_class = 'html-active';
93                 $toolbar = $buttons = '';
94
95                 if ( ! empty( $set['editor_height'] ) )
96                         $height = ' style="height: ' . $set['editor_height'] . 'px"';
97                 else
98                         $height = ' rows="' . $set['textarea_rows'] . '"';
99
100                 if ( !current_user_can( 'upload_files' ) )
101                         $set['media_buttons'] = false;
102
103                 if ( self::$this_quicktags && self::$this_tinymce ) {
104                         $switch_class = 'html-active';
105
106                         // 'html' and 'switch-html' are used for the "Text" editor tab.
107                         if ( 'html' == wp_default_editor() ) {
108                                 add_filter('the_editor_content', 'wp_htmledit_pre');
109                         } else {
110                                 add_filter('the_editor_content', 'wp_richedit_pre');
111                                 $switch_class = 'tmce-active';
112                         }
113
114                         $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";
115                         $buttons .= '<a id="' . $editor_id . '-tmce" class="wp-switch-editor switch-tmce" onclick="switchEditors.switchto(this);">' . __('Visual') . "</a>\n";
116                 }
117
118                 echo '<div id="wp-' . $editor_id . '-wrap" class="wp-core-ui wp-editor-wrap ' . $switch_class . '">';
119
120                 if ( self::$editor_buttons_css ) {
121                         wp_print_styles('editor-buttons');
122                         self::$editor_buttons_css = false;
123                 }
124
125                 if ( !empty($set['editor_css']) )
126                         echo $set['editor_css'] . "\n";
127
128                 if ( !empty($buttons) || $set['media_buttons'] ) {
129                         echo '<div id="wp-' . $editor_id . '-editor-tools" class="wp-editor-tools hide-if-no-js">';
130                         echo $buttons;
131
132                         if ( $set['media_buttons'] ) {
133                                 self::$has_medialib = true;
134
135                                 if ( !function_exists('media_buttons') )
136                                         include(ABSPATH . 'wp-admin/includes/media.php');
137
138                                 echo '<div id="wp-' . $editor_id . '-media-buttons" class="wp-media-buttons">';
139                                 do_action('media_buttons', $editor_id);
140                                 echo "</div>\n";
141                         }
142                         echo "</div>\n";
143                 }
144
145                 $the_editor = apply_filters('the_editor', '<div id="wp-' . $editor_id . '-editor-container" class="wp-editor-container"><textarea' . $editor_class . $height . $tabindex . ' cols="40" name="' . $set['textarea_name'] . '" id="' . $editor_id . '">%s</textarea></div>');
146                 $content = apply_filters('the_editor_content', $content);
147
148                 printf($the_editor, $content);
149                 echo "\n</div>\n\n";
150
151                 self::editor_settings($editor_id, $set);
152         }
153
154         public static function editor_settings($editor_id, $set) {
155                 global $editor_styles;
156                 $first_run = false;
157
158                 if ( empty(self::$first_init) ) {
159                         if ( is_admin() ) {
160                                 add_action( 'admin_print_footer_scripts', array( __CLASS__, 'editor_js'), 50 );
161                                 add_action( 'admin_footer', array( __CLASS__, 'enqueue_scripts'), 1 );
162                         } else {
163                                 add_action( 'wp_print_footer_scripts', array( __CLASS__, 'editor_js'), 50 );
164                                 add_action( 'wp_footer', array( __CLASS__, 'enqueue_scripts'), 1 );
165                         }
166                 }
167
168                 if ( self::$this_quicktags ) {
169
170                         $qtInit = array(
171                                 'id' => $editor_id,
172                                 'buttons' => ''
173                         );
174
175                         if ( is_array($set['quicktags']) )
176                                 $qtInit = array_merge($qtInit, $set['quicktags']);
177
178                         if ( empty($qtInit['buttons']) )
179                                 $qtInit['buttons'] = 'strong,em,link,block,del,ins,img,ul,ol,li,code,more,spell,close';
180
181                         if ( $set['dfw'] )
182                                 $qtInit['buttons'] .= ',fullscreen';
183
184                         $qtInit = apply_filters('quicktags_settings', $qtInit, $editor_id);
185                         self::$qt_settings[$editor_id] = $qtInit;
186
187                         self::$qt_buttons = array_merge( self::$qt_buttons, explode(',', $qtInit['buttons']) );
188                 }
189
190                 if ( self::$this_tinymce ) {
191
192                         if ( empty(self::$first_init) ) {
193                                 self::$baseurl = includes_url('js/tinymce');
194                                 self::$mce_locale = $mce_locale = ( '' == get_locale() ) ? 'en' : strtolower( substr(get_locale(), 0, 2) ); // only ISO 639-1
195                                 $no_captions = (bool) apply_filters( 'disable_captions', '' );
196                                 $plugins = array( 'inlinepopups', 'spellchecker', 'tabfocus', 'paste', 'media', 'fullscreen', 'wordpress', 'wpeditimage', 'wpgallery', 'wplink', 'wpdialogs' );
197                                 $first_run = true;
198                                 $ext_plugins = '';
199
200                                 if ( $set['teeny'] ) {
201                                         self::$plugins = $plugins = apply_filters( 'teeny_mce_plugins', array('inlinepopups', 'fullscreen', 'wordpress', 'wplink', 'wpdialogs' ), $editor_id );
202                                 } else {
203                                         /*
204                                         The following filter takes an associative array of external plugins for TinyMCE in the form 'plugin_name' => 'url'.
205                                         It adds the plugin's name to TinyMCE's plugins init and the call to PluginManager to load the plugin.
206                                         The url should be absolute and should include the js file name to be loaded. Example:
207                                         array( 'myplugin' => 'http://my-site.com/wp-content/plugins/myfolder/mce_plugin.js' )
208                                         If the plugin uses a button, it should be added with one of the "$mce_buttons" filters.
209                                         */
210                                         $mce_external_plugins = apply_filters('mce_external_plugins', array());
211
212                                         if ( ! empty($mce_external_plugins) ) {
213
214                                                 /*
215                                                 The following filter loads external language files for TinyMCE plugins.
216                                                 It takes an associative array 'plugin_name' => 'path', where path is the
217                                                 include path to the file. The language file should follow the same format as
218                                                 /tinymce/langs/wp-langs.php and should define a variable $strings that
219                                                 holds all translated strings.
220                                                 When this filter is not used, the function will try to load {mce_locale}.js.
221                                                 If that is not found, en.js will be tried next.
222                                                 */
223                                                 $mce_external_languages = apply_filters('mce_external_languages', array());
224
225                                                 $loaded_langs = array();
226                                                 $strings = '';
227
228                                                 if ( ! empty($mce_external_languages) ) {
229                                                         foreach ( $mce_external_languages as $name => $path ) {
230                                                                 if ( @is_file($path) && @is_readable($path) ) {
231                                                                         include_once($path);
232                                                                         $ext_plugins .= $strings . "\n";
233                                                                         $loaded_langs[] = $name;
234                                                                 }
235                                                         }
236                                                 }
237
238                                                 foreach ( $mce_external_plugins as $name => $url ) {
239
240                                                         $url = set_url_scheme( $url );
241
242                                                         $plugins[] = '-' . $name;
243
244                                                         $plugurl = dirname($url);
245                                                         $strings = $str1 = $str2 = '';
246                                                         if ( ! in_array($name, $loaded_langs) ) {
247                                                                 $path = str_replace( content_url(), '', $plugurl );
248                                                                 $path = WP_CONTENT_DIR . $path . '/langs/';
249
250                                                                 if ( function_exists('realpath') )
251                                                                         $path = trailingslashit( realpath($path) );
252
253                                                                 if ( @is_file($path . $mce_locale . '.js') )
254                                                                         $strings .= @file_get_contents($path . $mce_locale . '.js') . "\n";
255
256                                                                 if ( @is_file($path . $mce_locale . '_dlg.js') )
257                                                                         $strings .= @file_get_contents($path . $mce_locale . '_dlg.js') . "\n";
258
259                                                                 if ( 'en' != $mce_locale && empty($strings) ) {
260                                                                         if ( @is_file($path . 'en.js') ) {
261                                                                                 $str1 = @file_get_contents($path . 'en.js');
262                                                                                 $strings .= preg_replace( '/([\'"])en\./', '$1' . $mce_locale . '.', $str1, 1 ) . "\n";
263                                                                         }
264
265                                                                         if ( @is_file($path . 'en_dlg.js') ) {
266                                                                                 $str2 = @file_get_contents($path . 'en_dlg.js');
267                                                                                 $strings .= preg_replace( '/([\'"])en\./', '$1' . $mce_locale . '.', $str2, 1 ) . "\n";
268                                                                         }
269                                                                 }
270
271                                                                 if ( ! empty($strings) )
272                                                                         $ext_plugins .= "\n" . $strings . "\n";
273                                                         }
274
275                                                         $ext_plugins .= 'tinyMCEPreInit.load_ext("' . $plugurl . '", "' . $mce_locale . '");' . "\n";
276                                                         $ext_plugins .= 'tinymce.PluginManager.load("' . $name . '", "' . $url . '");' . "\n";
277                                                 }
278                                         }
279
280                                         $plugins = array_unique( apply_filters('tiny_mce_plugins', $plugins) );
281                                 }
282
283                                 if ( $set['dfw'] )
284                                         $plugins[] = 'wpfullscreen';
285
286                                 self::$plugins = $plugins;
287                                 self::$ext_plugins = $ext_plugins;
288
289                                 /*
290                                 translators: These languages show up in the spellchecker drop-down menu, in the order specified, and with the first
291                                 language listed being the default language. They must be comma-separated and take the format of name=code, where name
292                                 is the language name (which you may internationalize), and code is a valid ISO 639 language code. Please test the
293                                 spellchecker with your values.
294                                 */
295                                 $mce_spellchecker_languages = __( 'English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,Portuguese=pt,Spanish=es,Swedish=sv' );
296
297                                 /*
298                                 The following filter allows localization scripts to change the languages displayed in the spellchecker's drop-down menu.
299                                 By default it uses Google's spellchecker API, but can be configured to use PSpell/ASpell if installed on the server.
300                                 The + sign marks the default language. More: http://www.tinymce.com/wiki.php/Plugin:spellchecker.
301                                 */
302                                 $mce_spellchecker_languages = apply_filters( 'mce_spellchecker_languages', '+' . $mce_spellchecker_languages );
303
304                                 self::$first_init = array(
305                                         'mode' => 'exact',
306                                         'width' => '100%',
307                                         'theme' => 'advanced',
308                                         'skin' => 'wp_theme',
309                                         'language' => self::$mce_locale,
310                                         'spellchecker_languages' => $mce_spellchecker_languages,
311                                         'theme_advanced_toolbar_location' => 'top',
312                                         'theme_advanced_toolbar_align' => 'left',
313                                         'theme_advanced_statusbar_location' => 'bottom',
314                                         'theme_advanced_resizing' => true,
315                                         'theme_advanced_resize_horizontal' => false,
316                                         'dialog_type' => 'modal',
317                                         'formats' => "{
318                                                 alignleft : [
319                                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}},
320                                                         {selector : 'img,table', classes : 'alignleft'}
321                                                 ],
322                                                 aligncenter : [
323                                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}},
324                                                         {selector : 'img,table', classes : 'aligncenter'}
325                                                 ],
326                                                 alignright : [
327                                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}},
328                                                         {selector : 'img,table', classes : 'alignright'}
329                                                 ],
330                                                 strikethrough : {inline : 'del'}
331                                         }",
332                                         'relative_urls' => false,
333                                         'remove_script_host' => false,
334                                         'convert_urls' => false,
335                                         'remove_linebreaks' => true,
336                                         'gecko_spellcheck' => true,
337                                         'fix_list_elements' => true,
338                                         'keep_styles' => false,
339                                         'entities' => '38,amp,60,lt,62,gt',
340                                         'accessibility_focus' => true,
341                                         'media_strict' => false,
342                                         'paste_remove_styles' => true,
343                                         'paste_remove_spans' => true,
344                                         'paste_strip_class_attributes' => 'all',
345                                         'paste_text_use_dialog' => true,
346                                         'webkit_fake_resize' => false,
347                                         'spellchecker_rpc_url' => self::$baseurl . '/plugins/spellchecker/rpc.php',
348                                         'schema' => 'html5',
349                                         'wpeditimage_disable_captions' => $no_captions,
350                                         'wp_fullscreen_content_css' => self::$baseurl . '/plugins/wpfullscreen/css/wp-fullscreen.css',
351                                         'plugins' => implode( ',', $plugins )
352                                 );
353
354                                 // load editor_style.css if the current theme supports it
355                                 if ( ! empty( $editor_styles ) && is_array( $editor_styles ) ) {
356                                         $mce_css = array();
357                                         $editor_styles = array_unique($editor_styles);
358                                         $style_uri = get_stylesheet_directory_uri();
359                                         $style_dir = get_stylesheet_directory();
360
361                                         if ( is_child_theme() ) {
362                                                 $template_uri = get_template_directory_uri();
363                                                 $template_dir = get_template_directory();
364
365                                                 foreach ( $editor_styles as $key => $file ) {
366                                                         if ( $file && file_exists( "$template_dir/$file" ) )
367                                                                 $mce_css[] = "$template_uri/$file";
368                                                 }
369                                         }
370
371                                         foreach ( $editor_styles as $file ) {
372                                                 if ( $file && file_exists( "$style_dir/$file" ) )
373                                                         $mce_css[] = "$style_uri/$file";
374                                         }
375
376                                         $mce_css = implode( ',', $mce_css );
377                                 } else {
378                                         $mce_css = '';
379                                 }
380
381                                 $mce_css = trim( apply_filters( 'mce_css', $mce_css ), ' ,' );
382
383                                 if ( ! empty($mce_css) )
384                                         self::$first_init['content_css'] = $mce_css;
385                         }
386
387                         if ( $set['teeny'] ) {
388                                 $mce_buttons = apply_filters( 'teeny_mce_buttons', array('bold', 'italic', 'underline', 'blockquote', 'strikethrough', 'bullist', 'numlist', 'justifyleft', 'justifycenter', 'justifyright', 'undo', 'redo', 'link', 'unlink', 'fullscreen'), $editor_id );
389                                 $mce_buttons_2 = $mce_buttons_3 = $mce_buttons_4 = array();
390                         } else {
391                                 $mce_buttons = apply_filters('mce_buttons', array('bold', 'italic', 'strikethrough', 'bullist', 'numlist', 'blockquote', 'justifyleft', 'justifycenter', 'justifyright', 'link', 'unlink', 'wp_more', 'spellchecker', 'fullscreen', 'wp_adv' ), $editor_id);
392                                 $mce_buttons_2 = apply_filters('mce_buttons_2', array( 'formatselect', 'underline', 'justifyfull', 'forecolor', 'pastetext', 'pasteword', 'removeformat', 'charmap', 'outdent', 'indent', 'undo', 'redo', 'wp_help' ), $editor_id);
393                                 $mce_buttons_3 = apply_filters('mce_buttons_3', array(), $editor_id);
394                                 $mce_buttons_4 = apply_filters('mce_buttons_4', array(), $editor_id);
395                         }
396
397                         $body_class = $editor_id;
398
399                         if ( $post = get_post() )
400                                 $body_class .= ' post-type-' . $post->post_type;
401
402                         if ( !empty($set['tinymce']['body_class']) ) {
403                                 $body_class .= ' ' . $set['tinymce']['body_class'];
404                                 unset($set['tinymce']['body_class']);
405                         }
406
407                         if ( $set['dfw'] ) {
408                                 // replace the first 'fullscreen' with 'wp_fullscreen'
409                                 if ( ($key = array_search('fullscreen', $mce_buttons)) !== false )
410                                         $mce_buttons[$key] = 'wp_fullscreen';
411                                 elseif ( ($key = array_search('fullscreen', $mce_buttons_2)) !== false )
412                                         $mce_buttons_2[$key] = 'wp_fullscreen';
413                                 elseif ( ($key = array_search('fullscreen', $mce_buttons_3)) !== false )
414                                         $mce_buttons_3[$key] = 'wp_fullscreen';
415                                 elseif ( ($key = array_search('fullscreen', $mce_buttons_4)) !== false )
416                                         $mce_buttons_4[$key] = 'wp_fullscreen';
417                         }
418
419                         $mceInit = array (
420                                 'elements' => $editor_id,
421                                 'wpautop' => (bool) $set['wpautop'],
422                                 'remove_linebreaks' => (bool) $set['wpautop'],
423                                 'apply_source_formatting' => (bool) !$set['wpautop'],
424                                 'theme_advanced_buttons1' => implode($mce_buttons, ','),
425                                 'theme_advanced_buttons2' => implode($mce_buttons_2, ','),
426                                 'theme_advanced_buttons3' => implode($mce_buttons_3, ','),
427                                 'theme_advanced_buttons4' => implode($mce_buttons_4, ','),
428                                 'tabfocus_elements' => $set['tabfocus_elements'],
429                                 'body_class' => $body_class
430                         );
431
432                         // The main editor doesn't use the TinyMCE resizing cookie.
433                         $mceInit['theme_advanced_resizing_use_cookie'] = 'content' !== $editor_id || empty( $set['editor_height'] );
434
435                         if ( $first_run )
436                                 $mceInit = array_merge(self::$first_init, $mceInit);
437
438                         if ( is_array($set['tinymce']) )
439                                 $mceInit = array_merge($mceInit, $set['tinymce']);
440
441                         // For people who really REALLY know what they're doing with TinyMCE
442                         // You can modify $mceInit to add, remove, change elements of the config before tinyMCE.init
443                         // Setting "valid_elements", "invalid_elements" and "extended_valid_elements" can be done through this filter.
444                         // Best is to use the default cleanup by not specifying valid_elements, as TinyMCE contains full set of XHTML 1.0.
445                         if ( $set['teeny'] ) {
446                                 $mceInit = apply_filters('teeny_mce_before_init', $mceInit, $editor_id);
447                         } else {
448                                 $mceInit = apply_filters('tiny_mce_before_init', $mceInit, $editor_id);
449                         }
450
451                         if ( empty($mceInit['theme_advanced_buttons3']) && !empty($mceInit['theme_advanced_buttons4']) ) {
452                                 $mceInit['theme_advanced_buttons3'] = $mceInit['theme_advanced_buttons4'];
453                                 $mceInit['theme_advanced_buttons4'] = '';
454                         }
455
456                         self::$mce_settings[$editor_id] = $mceInit;
457                 } // end if self::$this_tinymce
458         }
459
460         private static function _parse_init($init) {
461                 $options = '';
462
463                 foreach ( $init as $k => $v ) {
464                         if ( is_bool($v) ) {
465                                 $val = $v ? 'true' : 'false';
466                                 $options .= $k . ':' . $val . ',';
467                                 continue;
468                         } elseif ( !empty($v) && is_string($v) && ( ('{' == $v{0} && '}' == $v{strlen($v) - 1}) || ('[' == $v{0} && ']' == $v{strlen($v) - 1}) || preg_match('/^\(?function ?\(/', $v) ) ) {
469                                 $options .= $k . ':' . $v . ',';
470                                 continue;
471                         }
472                         $options .= $k . ':"' . $v . '",';
473                 }
474
475                 return '{' . trim( $options, ' ,' ) . '}';
476         }
477
478         public static function enqueue_scripts() {
479                 wp_enqueue_script('word-count');
480
481                 if ( self::$has_tinymce )
482                         wp_enqueue_script('editor');
483
484                 if ( self::$has_quicktags )
485                         wp_enqueue_script('quicktags');
486
487                 if ( in_array('wplink', self::$plugins, true) || in_array('link', self::$qt_buttons, true) ) {
488                         wp_enqueue_script('wplink');
489                         wp_enqueue_script('wpdialogs-popup');
490                         wp_enqueue_style('wp-jquery-ui-dialog');
491                 }
492
493                 if ( in_array('wpfullscreen', self::$plugins, true) || in_array('fullscreen', self::$qt_buttons, true) )
494                         wp_enqueue_script('wp-fullscreen');
495
496                 if ( self::$has_medialib ) {
497                         add_thickbox();
498                         wp_enqueue_script('media-upload');
499                 }
500         }
501
502         public static function editor_js() {
503                 global $tinymce_version, $concatenate_scripts, $compress_scripts;
504
505                 /**
506                  * Filter "tiny_mce_version" is deprecated
507                  *
508                  * The tiny_mce_version filter is not needed since external plugins are loaded directly by TinyMCE.
509                  * These plugins can be refreshed by appending query string to the URL passed to "mce_external_plugins" filter.
510                  * 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).
511                  */
512                 $version = 'ver=' . $tinymce_version;
513                 $tmce_on = !empty(self::$mce_settings);
514
515                 if ( ! isset($concatenate_scripts) )
516                         script_concat_settings();
517
518                 $compressed = $compress_scripts && $concatenate_scripts && isset($_SERVER['HTTP_ACCEPT_ENCODING'])
519                         && false !== stripos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip');
520
521                 if ( $tmce_on && 'en' != self::$mce_locale )
522                         include_once(ABSPATH . WPINC . '/js/tinymce/langs/wp-langs.php');
523
524                 $mceInit = $qtInit = '';
525                 if ( $tmce_on ) {
526                         foreach ( self::$mce_settings as $editor_id => $init ) {
527                                 $options = self::_parse_init( $init );
528                                 $mceInit .= "'$editor_id':{$options},";
529                         }
530                         $mceInit = '{' . trim($mceInit, ',') . '}';
531                 } else {
532                         $mceInit = '{}';
533                 }
534
535                 if ( !empty(self::$qt_settings) ) {
536                         foreach ( self::$qt_settings as $editor_id => $init ) {
537                                 $options = self::_parse_init( $init );
538                                 $qtInit .= "'$editor_id':{$options},";
539                         }
540                         $qtInit = '{' . trim($qtInit, ',') . '}';
541                 } else {
542                         $qtInit = '{}';
543                 }
544
545                 $ref = array(
546                         'plugins' => implode( ',', self::$plugins ),
547                         'theme' => 'advanced',
548                         'language' => self::$mce_locale
549                 );
550
551                 $suffix = ( defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ) ? '_src' : '';
552
553                 do_action('before_wp_tiny_mce', self::$mce_settings);
554 ?>
555
556         <script type="text/javascript">
557                 tinyMCEPreInit = {
558                         base : "<?php echo self::$baseurl; ?>",
559                         suffix : "<?php echo $suffix; ?>",
560                         query : "<?php echo $version; ?>",
561                         mceInit : <?php echo $mceInit; ?>,
562                         qtInit : <?php echo $qtInit; ?>,
563                         ref : <?php echo self::_parse_init( $ref ); ?>,
564                         load_ext : function(url,lang){var sl=tinymce.ScriptLoader;sl.markDone(url+'/langs/'+lang+'.js');sl.markDone(url+'/langs/'+lang+'_dlg.js');}
565                 };
566         </script>
567 <?php
568
569                 $baseurl = self::$baseurl;
570
571                 if ( $tmce_on ) {
572                         if ( $compressed ) {
573                                 echo "<script type='text/javascript' src='{$baseurl}/wp-tinymce.php?c=1&amp;$version'></script>\n";
574                         } else {
575                                 echo "<script type='text/javascript' src='{$baseurl}/tiny_mce.js?$version'></script>\n";
576                                 echo "<script type='text/javascript' src='{$baseurl}/wp-tinymce-schema.js?$version'></script>\n";
577                         }
578
579                         if ( 'en' != self::$mce_locale && isset($lang) )
580                                 echo "<script type='text/javascript'>\n$lang\n</script>\n";
581                         else
582                                 echo "<script type='text/javascript' src='{$baseurl}/langs/wp-langs-en.js?$version'></script>\n";
583                 }
584
585                 $mce = ( self::$has_tinymce && wp_default_editor() == 'tinymce' ) || ! self::$has_quicktags;
586 ?>
587
588         <script type="text/javascript">
589                 var wpActiveEditor;
590
591                 (function(){
592                         var init, ed, qt, first_init, DOM, el, i, mce = <?php echo (int) $mce; ?>;
593
594                         if ( typeof(tinymce) == 'object' ) {
595                                 DOM = tinymce.DOM;
596                                 // mark wp_theme/ui.css as loaded
597                                 DOM.files[tinymce.baseURI.getURI() + '/themes/advanced/skins/wp_theme/ui.css'] = true;
598
599                                 DOM.events.add( DOM.select('.wp-editor-wrap'), 'mousedown', function(e){
600                                         if ( this.id )
601                                                 wpActiveEditor = this.id.slice(3, -5);
602                                 });
603
604                                 for ( ed in tinyMCEPreInit.mceInit ) {
605                                         if ( first_init ) {
606                                                 init = tinyMCEPreInit.mceInit[ed] = tinymce.extend( {}, first_init, tinyMCEPreInit.mceInit[ed] );
607                                         } else {
608                                                 init = first_init = tinyMCEPreInit.mceInit[ed];
609                                         }
610
611                                         if ( mce )
612                                                 try { tinymce.init(init); } catch(e){}
613                                 }
614                         } else {
615                                 if ( tinyMCEPreInit.qtInit ) {
616                                         for ( i in tinyMCEPreInit.qtInit ) {
617                                                 el = tinyMCEPreInit.qtInit[i].id;
618                                                 if ( el )
619                                                         document.getElementById('wp-'+el+'-wrap').onmousedown = function(){ wpActiveEditor = this.id.slice(3, -5); }
620                                         }
621                                 }
622                         }
623
624                         if ( typeof(QTags) == 'function' ) {
625                                 for ( qt in tinyMCEPreInit.qtInit ) {
626                                         try { quicktags( tinyMCEPreInit.qtInit[qt] ); } catch(e){}
627                                 }
628                         }
629                 })();
630                 <?php
631
632                 if ( self::$ext_plugins )
633                         echo self::$ext_plugins . "\n";
634
635                 if ( ! $compressed && $tmce_on ) {
636                         ?>
637                         (function(){var t=tinyMCEPreInit,sl=tinymce.ScriptLoader,ln=t.ref.language,th=t.ref.theme,pl=t.ref.plugins;sl.markDone(t.base+'/langs/'+ln+'.js');sl.markDone(t.base+'/themes/'+th+'/langs/'+ln+'.js');sl.markDone(t.base+'/themes/'+th+'/langs/'+ln+'_dlg.js');sl.markDone(t.base+'/themes/advanced/skins/wp_theme/ui.css');tinymce.each(pl.split(','),function(n){if(n&&n.charAt(0)!='-'){sl.markDone(t.base+'/plugins/'+n+'/langs/'+ln+'.js');sl.markDone(t.base+'/plugins/'+n+'/langs/'+ln+'_dlg.js');}});})();
638                         <?php
639                 }
640
641                 if ( !is_admin() )
642                         echo 'var ajaxurl = "' . admin_url( 'admin-ajax.php', 'relative' ) . '";';
643
644                 ?>
645                 </script>
646                 <?php
647
648                 if ( in_array('wplink', self::$plugins, true) || in_array('link', self::$qt_buttons, true) )
649                         self::wp_link_dialog();
650
651                 if ( in_array('wpfullscreen', self::$plugins, true) || in_array('fullscreen', self::$qt_buttons, true) )
652                         self::wp_fullscreen_html();
653
654                 do_action('after_wp_tiny_mce', self::$mce_settings);
655         }
656
657         public static function wp_fullscreen_html() {
658                 global $content_width;
659                 $post = get_post();
660
661                 $width = isset($content_width) && 800 > $content_width ? $content_width : 800;
662                 $width = $width + 22; // compensate for the padding and border
663                 $dfw_width = get_user_setting( 'dfw_width', $width );
664                 $save = isset($post->post_status) && $post->post_status == 'publish' ? __('Update') : __('Save');
665         ?>
666         <div id="wp-fullscreen-body"<?php if ( is_rtl() ) echo ' class="rtl"'; ?>>
667         <div id="fullscreen-topbar">
668                 <div id="wp-fullscreen-toolbar">
669                         <div id="wp-fullscreen-close"><a href="#" onclick="fullscreen.off();return false;"><?php _e('Exit fullscreen'); ?></a></div>
670                         <div id="wp-fullscreen-central-toolbar" style="width:<?php echo $width; ?>px;">
671
672                         <div id="wp-fullscreen-mode-bar"><div id="wp-fullscreen-modes">
673                                 <a href="#" onclick="fullscreen.switchmode('tinymce');return false;"><?php _e( 'Visual' ); ?></a>
674                                 <a href="#" onclick="fullscreen.switchmode('html');return false;"><?php _ex( 'Text', 'Name for the Text editor tab (formerly HTML)' ); ?></a>
675                         </div></div>
676
677                         <div id="wp-fullscreen-button-bar"><div id="wp-fullscreen-buttons" class="wp_themeSkin">
678         <?php
679
680                 $buttons = array(
681                         // format: title, onclick, show in both editors
682                         'bold' => array( 'title' => __('Bold (Ctrl + B)'), 'onclick' => 'fullscreen.b();', 'both' => false ),
683                         'italic' => array( 'title' => __('Italic (Ctrl + I)'), 'onclick' => 'fullscreen.i();', 'both' => false ),
684                         '0' => 'separator',
685                         'bullist' => array( 'title' => __('Unordered list (Alt + Shift + U)'), 'onclick' => 'fullscreen.ul();', 'both' => false ),
686                         'numlist' => array( 'title' => __('Ordered list (Alt + Shift + O)'), 'onclick' => 'fullscreen.ol();', 'both' => false ),
687                         '1' => 'separator',
688                         'blockquote' => array( 'title' => __('Blockquote (Alt + Shift + Q)'), 'onclick' => 'fullscreen.blockquote();', 'both' => false ),
689                         'image' => array( 'title' => __('Insert/edit image (Alt + Shift + M)'), 'onclick' => "fullscreen.medialib();", 'both' => true ),
690                         '2' => 'separator',
691                         'link' => array( 'title' => __('Insert/edit link (Alt + Shift + A)'), 'onclick' => 'fullscreen.link();', 'both' => true ),
692                         'unlink' => array( 'title' => __('Unlink (Alt + Shift + S)'), 'onclick' => 'fullscreen.unlink();', 'both' => false ),
693                         '3' => 'separator',
694                         'help' => array( 'title' => __('Help (Alt + Shift + H)'), 'onclick' => 'fullscreen.help();', 'both' => false )
695                 );
696
697                 $buttons = apply_filters( 'wp_fullscreen_buttons', $buttons );
698
699                 foreach ( $buttons as $button => $args ) {
700                         if ( 'separator' == $args ) { ?>
701                                 <div><span aria-orientation="vertical" role="separator" class="mceSeparator"></span></div>
702         <?php           continue;
703                         } ?>
704
705                         <div<?php if ( $args['both'] ) { ?> class="wp-fullscreen-both"<?php } ?>>
706                         <a title="<?php echo $args['title']; ?>" onclick="<?php echo $args['onclick']; ?>return false;" class="mceButton mceButtonEnabled mce_<?php echo $button; ?>" href="#" id="wp_fs_<?php echo $button; ?>" role="button" aria-pressed="false">
707                         <span class="mceIcon mce_<?php echo $button; ?>"></span>
708                         </a>
709                         </div>
710         <?php
711                 } ?>
712
713                         </div></div>
714
715                         <div id="wp-fullscreen-save">
716                                 <input type="button" class="button-primary right" value="<?php echo $save; ?>" onclick="fullscreen.save();" />
717                                 <span class="spinner"></span>
718                                 <span class="fs-saved"><?php if ( $post->post_status == 'publish' ) _e('Updated.'); else _e('Saved.'); ?></span>
719                         </div>
720
721                         </div>
722                 </div>
723         </div>
724
725         <div id="wp-fullscreen-wrap" style="width:<?php echo $dfw_width; ?>px;">
726                 <?php if ( post_type_supports($post->post_type, 'title') ) { ?>
727                 <label id="wp-fullscreen-title-prompt-text" for="wp-fullscreen-title"><?php echo apply_filters( 'enter_title_here', __( 'Enter title here' ), $post ); ?></label>
728                 <input type="text" id="wp-fullscreen-title" value="" autocomplete="off" />
729                 <?php } ?>
730
731                 <div id="wp-fullscreen-container">
732                         <textarea id="wp_mce_fullscreen"></textarea>
733                 </div>
734
735                 <div id="wp-fullscreen-status">
736                         <div id="wp-fullscreen-count"><?php printf( __( 'Word count: %s' ), '<span class="word-count">0</span>' ); ?></div>
737                         <div id="wp-fullscreen-tagline"><?php _e('Just write.'); ?></div>
738                 </div>
739         </div>
740         </div>
741
742         <div class="fullscreen-overlay" id="fullscreen-overlay"></div>
743         <div class="fullscreen-overlay fullscreen-fader fade-600" id="fullscreen-fader"></div>
744         <?php
745         }
746
747         /**
748          * Performs post queries for internal linking.
749          *
750          * @since 3.1.0
751          *
752          * @param array $args Optional. Accepts 'pagenum' and 's' (search) arguments.
753          * @return array Results.
754          */
755         public static function wp_link_query( $args = array() ) {
756                 $pts = get_post_types( array( 'public' => true ), 'objects' );
757                 $pt_names = array_keys( $pts );
758
759                 $query = array(
760                         'post_type' => $pt_names,
761                         'suppress_filters' => true,
762                         'update_post_term_cache' => false,
763                         'update_post_meta_cache' => false,
764                         'post_status' => 'publish',
765                         'order' => 'DESC',
766                         'orderby' => 'post_date',
767                         'posts_per_page' => 20,
768                 );
769
770                 $args['pagenum'] = isset( $args['pagenum'] ) ? absint( $args['pagenum'] ) : 1;
771
772                 if ( isset( $args['s'] ) )
773                         $query['s'] = $args['s'];
774
775                 $query['offset'] = $args['pagenum'] > 1 ? $query['posts_per_page'] * ( $args['pagenum'] - 1 ) : 0;
776
777                 // Do main query.
778                 $get_posts = new WP_Query;
779                 $posts = $get_posts->query( $query );
780                 // Check if any posts were found.
781                 if ( ! $get_posts->post_count )
782                         return false;
783
784                 // Build results.
785                 $results = array();
786                 foreach ( $posts as $post ) {
787                         if ( 'post' == $post->post_type )
788                                 $info = mysql2date( __( 'Y/m/d' ), $post->post_date );
789                         else
790                                 $info = $pts[ $post->post_type ]->labels->singular_name;
791
792                         $results[] = array(
793                                 'ID' => $post->ID,
794                                 'title' => trim( esc_html( strip_tags( get_the_title( $post ) ) ) ),
795                                 'permalink' => get_permalink( $post->ID ),
796                                 'info' => $info,
797                         );
798                 }
799
800                 return $results;
801         }
802
803         /**
804          * Dialog for internal linking.
805          *
806          * @since 3.1.0
807          */
808         public static function wp_link_dialog() {
809         ?>
810         <div style="display:none;">
811         <form id="wp-link" tabindex="-1">
812         <?php wp_nonce_field( 'internal-linking', '_ajax_linking_nonce', false ); ?>
813         <div id="link-selector">
814                 <div id="link-options">
815                         <p class="howto"><?php _e( 'Enter the destination URL' ); ?></p>
816                         <div>
817                                 <label><span><?php _e( 'URL' ); ?></span><input id="url-field" type="text" name="href" /></label>
818                         </div>
819                         <div>
820                                 <label><span><?php _e( 'Title' ); ?></span><input id="link-title-field" type="text" name="linktitle" /></label>
821                         </div>
822                         <div class="link-target">
823                                 <label><input type="checkbox" id="link-target-checkbox" /> <?php _e( 'Open link in a new window/tab' ); ?></label>
824                         </div>
825                 </div>
826                 <?php $show_internal = '1' == get_user_setting( 'wplink', '0' ); ?>
827                 <p class="howto toggle-arrow <?php if ( $show_internal ) echo 'toggle-arrow-active'; ?>" id="internal-toggle"><?php _e( 'Or link to existing content' ); ?></p>
828                 <div id="search-panel"<?php if ( ! $show_internal ) echo ' style="display:none"'; ?>>
829                         <div class="link-search-wrapper">
830                                 <label>
831                                         <span class="search-label"><?php _e( 'Search' ); ?></span>
832                                         <input type="search" id="search-field" class="link-search-field" autocomplete="off" />
833                                         <span class="spinner"></span>
834                                 </label>
835                         </div>
836                         <div id="search-results" class="query-results">
837                                 <ul></ul>
838                                 <div class="river-waiting">
839                                         <span class="spinner"></span>
840                                 </div>
841                         </div>
842                         <div id="most-recent-results" class="query-results">
843                                 <div class="query-notice"><em><?php _e( 'No search term specified. Showing recent items.' ); ?></em></div>
844                                 <ul></ul>
845                                 <div class="river-waiting">
846                                         <span class="spinner"></span>
847                                 </div>
848                         </div>
849                 </div>
850         </div>
851         <div class="submitbox">
852                 <div id="wp-link-update">
853                         <input type="submit" value="<?php esc_attr_e( 'Add Link' ); ?>" class="button-primary" id="wp-link-submit" name="wp-link-submit">
854                 </div>
855                 <div id="wp-link-cancel">
856                         <a class="submitdelete deletion" href="#"><?php _e( 'Cancel' ); ?></a>
857                 </div>
858         </div>
859         </form>
860         </div>
861         <?php
862         }
863 }