3 * Facilitates adding of the WordPress editor as used on the Write and Edit screens.
8 * Private, not included by default. See wp_editor() in wp-includes/general-template.php.
11 final class _WP_Editors {
12 public static $mce_locale;
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;
28 private function __construct() {}
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,
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()
46 self::$this_tinymce = ( $set['tinymce'] && user_can_richedit() );
47 self::$this_quicktags = (bool) $set['quicktags'];
49 if ( self::$this_tinymce )
50 self::$has_tinymce = true;
52 if ( self::$this_quicktags )
53 self::$has_quicktags = true;
55 if ( empty( $set['editor_height'] ) )
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' );
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'];
69 $set['editor_height'] = $cookie;
72 if ( $set['editor_height'] < 50 )
73 $set['editor_height'] = 50;
74 elseif ( $set['editor_height'] > 5000 )
75 $set['editor_height'] = 5000;
81 * Outputs the HTML for a single instance of the editor.
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.
87 public static function editor( $content, $editor_id, $settings = array() ) {
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 = '';
95 if ( ! empty( $set['editor_height'] ) )
96 $height = ' style="height: ' . $set['editor_height'] . 'px"';
98 $height = ' rows="' . $set['textarea_rows'] . '"';
100 if ( !current_user_can( 'upload_files' ) )
101 $set['media_buttons'] = false;
103 if ( self::$this_quicktags && self::$this_tinymce ) {
104 $switch_class = 'html-active';
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');
110 add_filter('the_editor_content', 'wp_richedit_pre');
111 $switch_class = 'tmce-active';
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";
118 echo '<div id="wp-' . $editor_id . '-wrap" class="wp-core-ui wp-editor-wrap ' . $switch_class . '">';
120 if ( self::$editor_buttons_css ) {
121 wp_print_styles('editor-buttons');
122 self::$editor_buttons_css = false;
125 if ( !empty($set['editor_css']) )
126 echo $set['editor_css'] . "\n";
128 if ( !empty($buttons) || $set['media_buttons'] ) {
129 echo '<div id="wp-' . $editor_id . '-editor-tools" class="wp-editor-tools hide-if-no-js">';
132 if ( $set['media_buttons'] ) {
133 self::$has_medialib = true;
135 if ( !function_exists('media_buttons') )
136 include(ABSPATH . 'wp-admin/includes/media.php');
138 echo '<div id="wp-' . $editor_id . '-media-buttons" class="wp-media-buttons">';
139 do_action('media_buttons', $editor_id);
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);
148 printf($the_editor, $content);
151 self::editor_settings($editor_id, $set);
154 public static function editor_settings($editor_id, $set) {
157 if ( empty(self::$first_init) ) {
159 add_action( 'admin_print_footer_scripts', array( __CLASS__, 'editor_js'), 50 );
160 add_action( 'admin_footer', array( __CLASS__, 'enqueue_scripts'), 1 );
162 add_action( 'wp_print_footer_scripts', array( __CLASS__, 'editor_js'), 50 );
163 add_action( 'wp_footer', array( __CLASS__, 'enqueue_scripts'), 1 );
167 if ( self::$this_quicktags ) {
174 if ( is_array($set['quicktags']) )
175 $qtInit = array_merge($qtInit, $set['quicktags']);
177 if ( empty($qtInit['buttons']) )
178 $qtInit['buttons'] = 'strong,em,link,block,del,ins,img,ul,ol,li,code,more,close';
181 $qtInit['buttons'] .= ',fullscreen';
183 $qtInit = apply_filters('quicktags_settings', $qtInit, $editor_id);
184 self::$qt_settings[$editor_id] = $qtInit;
186 self::$qt_buttons = array_merge( self::$qt_buttons, explode(',', $qtInit['buttons']) );
189 if ( self::$this_tinymce ) {
191 if ( empty(self::$first_init) ) {
192 self::$baseurl = includes_url('js/tinymce');
193 self::$mce_locale = $mce_locale = ( '' == get_locale() ) ? 'en' : strtolower( substr(get_locale(), 0, 2) ); // only ISO 639-1
194 $no_captions = (bool) apply_filters( 'disable_captions', '' );
195 $plugins = array( 'inlinepopups', 'tabfocus', 'paste', 'media', 'fullscreen', 'wordpress', 'wpeditimage', 'wpgallery', 'wplink', 'wpdialogs' );
199 if ( $set['teeny'] ) {
200 self::$plugins = $plugins = apply_filters( 'teeny_mce_plugins', array('inlinepopups', 'fullscreen', 'wordpress', 'wplink', 'wpdialogs' ), $editor_id );
203 The following filter takes an associative array of external plugins for TinyMCE in the form 'plugin_name' => 'url'.
204 It adds the plugin's name to TinyMCE's plugins init and the call to PluginManager to load the plugin.
205 The url should be absolute and should include the js file name to be loaded. Example:
206 array( 'myplugin' => 'http://my-site.com/wp-content/plugins/myfolder/mce_plugin.js' )
207 If the plugin uses a button, it should be added with one of the "$mce_buttons" filters.
209 $mce_external_plugins = apply_filters('mce_external_plugins', array());
211 if ( ! empty($mce_external_plugins) ) {
214 The following filter loads external language files for TinyMCE plugins.
215 It takes an associative array 'plugin_name' => 'path', where path is the
216 include path to the file. The language file should follow the same format as
217 /tinymce/langs/wp-langs.php and should define a variable $strings that
218 holds all translated strings.
219 When this filter is not used, the function will try to load {mce_locale}.js.
220 If that is not found, en.js will be tried next.
222 $mce_external_languages = apply_filters('mce_external_languages', array());
224 $loaded_langs = array();
227 if ( ! empty($mce_external_languages) ) {
228 foreach ( $mce_external_languages as $name => $path ) {
229 if ( @is_file($path) && @is_readable($path) ) {
231 $ext_plugins .= $strings . "\n";
232 $loaded_langs[] = $name;
237 foreach ( $mce_external_plugins as $name => $url ) {
239 $url = set_url_scheme( $url );
241 $plugins[] = '-' . $name;
243 $plugurl = dirname($url);
244 $strings = $str1 = $str2 = '';
245 if ( ! in_array($name, $loaded_langs) ) {
246 $path = str_replace( content_url(), '', $plugurl );
247 $path = WP_CONTENT_DIR . $path . '/langs/';
249 if ( function_exists('realpath') )
250 $path = trailingslashit( realpath($path) );
252 if ( @is_file($path . $mce_locale . '.js') )
253 $strings .= @file_get_contents($path . $mce_locale . '.js') . "\n";
255 if ( @is_file($path . $mce_locale . '_dlg.js') )
256 $strings .= @file_get_contents($path . $mce_locale . '_dlg.js') . "\n";
258 if ( 'en' != $mce_locale && empty($strings) ) {
259 if ( @is_file($path . 'en.js') ) {
260 $str1 = @file_get_contents($path . 'en.js');
261 $strings .= preg_replace( '/([\'"])en\./', '$1' . $mce_locale . '.', $str1, 1 ) . "\n";
264 if ( @is_file($path . 'en_dlg.js') ) {
265 $str2 = @file_get_contents($path . 'en_dlg.js');
266 $strings .= preg_replace( '/([\'"])en\./', '$1' . $mce_locale . '.', $str2, 1 ) . "\n";
270 if ( ! empty($strings) )
271 $ext_plugins .= "\n" . $strings . "\n";
274 $ext_plugins .= 'tinyMCEPreInit.load_ext("' . $plugurl . '", "' . $mce_locale . '");' . "\n";
275 $ext_plugins .= 'tinymce.PluginManager.load("' . $name . '", "' . $url . '");' . "\n";
279 $plugins = array_unique( apply_filters('tiny_mce_plugins', $plugins) );
283 $plugins[] = 'wpfullscreen';
285 self::$plugins = $plugins;
286 self::$ext_plugins = $ext_plugins;
288 if ( in_array( 'spellchecker', $plugins ) ) {
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.
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' );
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.
302 $mce_spellchecker_languages = apply_filters( 'mce_spellchecker_languages', '+' . $mce_spellchecker_languages );
305 self::$first_init = array(
308 'theme' => 'advanced',
309 'skin' => 'wp_theme',
310 'language' => self::$mce_locale,
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',
319 {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}},
320 {selector : 'img,table', classes : 'alignleft'}
323 {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}},
324 {selector : 'img,table', classes : 'aligncenter'}
327 {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}},
328 {selector : 'img,table', classes : 'alignright'}
330 strikethrough : {inline : 'del'}
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 'preview_styles' => 'font-family font-weight text-decoration text-transform',
349 'wpeditimage_disable_captions' => $no_captions,
350 'wp_fullscreen_content_css' => self::$baseurl . '/plugins/wpfullscreen/css/wp-fullscreen.css',
351 'plugins' => implode( ',', $plugins )
354 if ( in_array( 'spellchecker', $plugins ) ) {
355 self::$first_init['spellchecker_rpc_url'] = self::$baseurl . '/plugins/spellchecker/rpc.php';
356 self::$first_init['spellchecker_languages'] = $mce_spellchecker_languages;
359 // load editor_style.css if the current theme supports it
360 if ( ! empty( $GLOBALS['editor_styles'] ) && is_array( $GLOBALS['editor_styles'] ) ) {
361 $editor_styles = $GLOBALS['editor_styles'];
364 $editor_styles = array_unique( array_filter( $editor_styles ) );
365 $style_uri = get_stylesheet_directory_uri();
366 $style_dir = get_stylesheet_directory();
368 // Support externally referenced styles (like, say, fonts).
369 foreach ( $editor_styles as $key => $file ) {
370 if ( preg_match( '~^(https?:)?//~', $file ) ) {
371 $mce_css[] = esc_url_raw( $file );
372 unset( $editor_styles[ $key ] );
376 // Look in a parent theme first, that way child theme CSS overrides.
377 if ( is_child_theme() ) {
378 $template_uri = get_template_directory_uri();
379 $template_dir = get_template_directory();
381 foreach ( $editor_styles as $key => $file ) {
382 if ( $file && file_exists( "$template_dir/$file" ) )
383 $mce_css[] = "$template_uri/$file";
387 foreach ( $editor_styles as $file ) {
388 if ( $file && file_exists( "$style_dir/$file" ) )
389 $mce_css[] = "$style_uri/$file";
392 $mce_css = implode( ',', $mce_css );
397 $mce_css = trim( apply_filters( 'mce_css', $mce_css ), ' ,' );
399 if ( ! empty($mce_css) )
400 self::$first_init['content_css'] = $mce_css;
403 if ( $set['teeny'] ) {
404 $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 );
405 $mce_buttons_2 = $mce_buttons_3 = $mce_buttons_4 = array();
407 $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);
408 $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);
409 $mce_buttons_3 = apply_filters('mce_buttons_3', array(), $editor_id);
410 $mce_buttons_4 = apply_filters('mce_buttons_4', array(), $editor_id);
413 $body_class = $editor_id;
415 if ( $post = get_post() ) {
416 $body_class .= ' post-type-' . sanitize_html_class( $post->post_type ) . ' post-status-' . sanitize_html_class( $post->post_status );
417 if ( post_type_supports( $post->post_type, 'post-formats' ) ) {
418 $post_format = get_post_format( $post );
419 if ( $post_format && ! is_wp_error( $post_format ) )
420 $body_class .= ' post-format-' . sanitize_html_class( $post_format );
422 $body_class .= ' post-format-standard';
426 if ( !empty($set['tinymce']['body_class']) ) {
427 $body_class .= ' ' . $set['tinymce']['body_class'];
428 unset($set['tinymce']['body_class']);
432 // replace the first 'fullscreen' with 'wp_fullscreen'
433 if ( ($key = array_search('fullscreen', $mce_buttons)) !== false )
434 $mce_buttons[$key] = 'wp_fullscreen';
435 elseif ( ($key = array_search('fullscreen', $mce_buttons_2)) !== false )
436 $mce_buttons_2[$key] = 'wp_fullscreen';
437 elseif ( ($key = array_search('fullscreen', $mce_buttons_3)) !== false )
438 $mce_buttons_3[$key] = 'wp_fullscreen';
439 elseif ( ($key = array_search('fullscreen', $mce_buttons_4)) !== false )
440 $mce_buttons_4[$key] = 'wp_fullscreen';
444 'elements' => $editor_id,
445 'wpautop' => (bool) $set['wpautop'],
446 'remove_linebreaks' => (bool) $set['wpautop'],
447 'apply_source_formatting' => (bool) !$set['wpautop'],
448 'theme_advanced_buttons1' => implode($mce_buttons, ','),
449 'theme_advanced_buttons2' => implode($mce_buttons_2, ','),
450 'theme_advanced_buttons3' => implode($mce_buttons_3, ','),
451 'theme_advanced_buttons4' => implode($mce_buttons_4, ','),
452 'tabfocus_elements' => $set['tabfocus_elements'],
453 'body_class' => $body_class
456 // The main editor doesn't use the TinyMCE resizing cookie.
457 $mceInit['theme_advanced_resizing_use_cookie'] = 'content' !== $editor_id || empty( $set['editor_height'] );
460 $mceInit = array_merge(self::$first_init, $mceInit);
462 if ( is_array($set['tinymce']) )
463 $mceInit = array_merge($mceInit, $set['tinymce']);
465 // For people who really REALLY know what they're doing with TinyMCE
466 // You can modify $mceInit to add, remove, change elements of the config before tinyMCE.init
467 // Setting "valid_elements", "invalid_elements" and "extended_valid_elements" can be done through this filter.
468 // Best is to use the default cleanup by not specifying valid_elements, as TinyMCE contains full set of XHTML 1.0.
469 if ( $set['teeny'] ) {
470 $mceInit = apply_filters('teeny_mce_before_init', $mceInit, $editor_id);
472 $mceInit = apply_filters('tiny_mce_before_init', $mceInit, $editor_id);
475 if ( empty($mceInit['theme_advanced_buttons3']) && !empty($mceInit['theme_advanced_buttons4']) ) {
476 $mceInit['theme_advanced_buttons3'] = $mceInit['theme_advanced_buttons4'];
477 $mceInit['theme_advanced_buttons4'] = '';
480 self::$mce_settings[$editor_id] = $mceInit;
481 } // end if self::$this_tinymce
484 private static function _parse_init($init) {
487 foreach ( $init as $k => $v ) {
489 $val = $v ? 'true' : 'false';
490 $options .= $k . ':' . $val . ',';
492 } elseif ( !empty($v) && is_string($v) && ( ('{' == $v{0} && '}' == $v{strlen($v) - 1}) || ('[' == $v{0} && ']' == $v{strlen($v) - 1}) || preg_match('/^\(?function ?\(/', $v) ) ) {
493 $options .= $k . ':' . $v . ',';
496 $options .= $k . ':"' . $v . '",';
499 return '{' . trim( $options, ' ,' ) . '}';
502 public static function enqueue_scripts() {
503 wp_enqueue_script('word-count');
505 if ( self::$has_tinymce )
506 wp_enqueue_script('editor');
508 if ( self::$has_quicktags )
509 wp_enqueue_script('quicktags');
511 if ( in_array('wplink', self::$plugins, true) || in_array('link', self::$qt_buttons, true) ) {
512 wp_enqueue_script('wplink');
513 wp_enqueue_script('wpdialogs-popup');
514 wp_enqueue_style('wp-jquery-ui-dialog');
517 if ( in_array('wpfullscreen', self::$plugins, true) || in_array('fullscreen', self::$qt_buttons, true) )
518 wp_enqueue_script('wp-fullscreen');
520 if ( self::$has_medialib ) {
522 wp_enqueue_script('media-upload');
526 public static function editor_js() {
527 global $tinymce_version, $concatenate_scripts, $compress_scripts;
530 * Filter "tiny_mce_version" is deprecated
532 * The tiny_mce_version filter is not needed since external plugins are loaded directly by TinyMCE.
533 * These plugins can be refreshed by appending query string to the URL passed to "mce_external_plugins" filter.
534 * 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).
536 $version = 'ver=' . $tinymce_version;
537 $tmce_on = !empty(self::$mce_settings);
539 if ( ! isset($concatenate_scripts) )
540 script_concat_settings();
542 $compressed = $compress_scripts && $concatenate_scripts && isset($_SERVER['HTTP_ACCEPT_ENCODING'])
543 && false !== stripos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip');
545 if ( $tmce_on && 'en' != self::$mce_locale )
546 include_once(ABSPATH . WPINC . '/js/tinymce/langs/wp-langs.php');
548 $mceInit = $qtInit = '';
550 foreach ( self::$mce_settings as $editor_id => $init ) {
551 $options = self::_parse_init( $init );
552 $mceInit .= "'$editor_id':{$options},";
554 $mceInit = '{' . trim($mceInit, ',') . '}';
559 if ( !empty(self::$qt_settings) ) {
560 foreach ( self::$qt_settings as $editor_id => $init ) {
561 $options = self::_parse_init( $init );
562 $qtInit .= "'$editor_id':{$options},";
564 $qtInit = '{' . trim($qtInit, ',') . '}';
570 'plugins' => implode( ',', self::$plugins ),
571 'theme' => 'advanced',
572 'language' => self::$mce_locale
575 $suffix = ( defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ) ? '_src' : '';
577 do_action('before_wp_tiny_mce', self::$mce_settings);
580 <script type="text/javascript">
582 base : "<?php echo self::$baseurl; ?>",
583 suffix : "<?php echo $suffix; ?>",
584 query : "<?php echo $version; ?>",
585 mceInit : <?php echo $mceInit; ?>,
586 qtInit : <?php echo $qtInit; ?>,
587 ref : <?php echo self::_parse_init( $ref ); ?>,
588 load_ext : function(url,lang){var sl=tinymce.ScriptLoader;sl.markDone(url+'/langs/'+lang+'.js');sl.markDone(url+'/langs/'+lang+'_dlg.js');}
593 $baseurl = self::$baseurl;
597 echo "<script type='text/javascript' src='{$baseurl}/wp-tinymce.php?c=1&$version'></script>\n";
599 echo "<script type='text/javascript' src='{$baseurl}/tiny_mce.js?$version'></script>\n";
600 echo "<script type='text/javascript' src='{$baseurl}/wp-tinymce-schema.js?$version'></script>\n";
603 if ( 'en' != self::$mce_locale && isset($lang) )
604 echo "<script type='text/javascript'>\n$lang\n</script>\n";
606 echo "<script type='text/javascript' src='{$baseurl}/langs/wp-langs-en.js?$version'></script>\n";
609 $mce = ( self::$has_tinymce && wp_default_editor() == 'tinymce' ) || ! self::$has_quicktags;
612 <script type="text/javascript">
616 var init, ed, qt, first_init, DOM, el, i, mce = <?php echo (int) $mce; ?>;
618 if ( typeof(tinymce) == 'object' ) {
620 // mark wp_theme/ui.css as loaded
621 DOM.files[tinymce.baseURI.getURI() + '/themes/advanced/skins/wp_theme/ui.css'] = true;
623 DOM.events.add( DOM.select('.wp-editor-wrap'), 'mousedown', function(e){
625 wpActiveEditor = this.id.slice(3, -5);
628 for ( ed in tinyMCEPreInit.mceInit ) {
630 init = tinyMCEPreInit.mceInit[ed] = tinymce.extend( {}, first_init, tinyMCEPreInit.mceInit[ed] );
632 init = first_init = tinyMCEPreInit.mceInit[ed];
636 try { tinymce.init(init); } catch(e){}
639 if ( tinyMCEPreInit.qtInit ) {
640 for ( i in tinyMCEPreInit.qtInit ) {
641 el = tinyMCEPreInit.qtInit[i].id;
643 document.getElementById('wp-'+el+'-wrap').onmousedown = function(){ wpActiveEditor = this.id.slice(3, -5); }
648 if ( typeof(QTags) == 'function' ) {
649 for ( qt in tinyMCEPreInit.qtInit ) {
650 try { quicktags( tinyMCEPreInit.qtInit[qt] ); } catch(e){}
656 if ( self::$ext_plugins )
657 echo self::$ext_plugins . "\n";
659 if ( ! $compressed && $tmce_on ) {
661 (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');}});})();
666 echo 'var ajaxurl = "' . admin_url( 'admin-ajax.php', 'relative' ) . '";';
672 if ( in_array('wplink', self::$plugins, true) || in_array('link', self::$qt_buttons, true) )
673 self::wp_link_dialog();
675 if ( in_array('wpfullscreen', self::$plugins, true) || in_array('fullscreen', self::$qt_buttons, true) )
676 self::wp_fullscreen_html();
678 do_action('after_wp_tiny_mce', self::$mce_settings);
681 public static function wp_fullscreen_html() {
682 global $content_width;
685 $width = isset($content_width) && 800 > $content_width ? $content_width : 800;
686 $width = $width + 22; // compensate for the padding and border
687 $dfw_width = get_user_setting( 'dfw_width', $width );
688 $save = isset($post->post_status) && $post->post_status == 'publish' ? __('Update') : __('Save');
690 <div id="wp-fullscreen-body"<?php if ( is_rtl() ) echo ' class="rtl"'; ?>>
691 <div id="fullscreen-topbar">
692 <div id="wp-fullscreen-toolbar">
693 <div id="wp-fullscreen-close"><a href="#" onclick="fullscreen.off();return false;"><?php _e('Exit fullscreen'); ?></a></div>
694 <div id="wp-fullscreen-central-toolbar" style="width:<?php echo $width; ?>px;">
696 <div id="wp-fullscreen-mode-bar"><div id="wp-fullscreen-modes">
697 <a href="#" onclick="fullscreen.switchmode('tinymce');return false;"><?php _e( 'Visual' ); ?></a>
698 <a href="#" onclick="fullscreen.switchmode('html');return false;"><?php _ex( 'Text', 'Name for the Text editor tab (formerly HTML)' ); ?></a>
701 <div id="wp-fullscreen-button-bar"><div id="wp-fullscreen-buttons" class="wp_themeSkin">
705 // format: title, onclick, show in both editors
706 'bold' => array( 'title' => __('Bold (Ctrl + B)'), 'onclick' => 'fullscreen.b();', 'both' => false ),
707 'italic' => array( 'title' => __('Italic (Ctrl + I)'), 'onclick' => 'fullscreen.i();', 'both' => false ),
709 'bullist' => array( 'title' => __('Unordered list (Alt + Shift + U)'), 'onclick' => 'fullscreen.ul();', 'both' => false ),
710 'numlist' => array( 'title' => __('Ordered list (Alt + Shift + O)'), 'onclick' => 'fullscreen.ol();', 'both' => false ),
712 'blockquote' => array( 'title' => __('Blockquote (Alt + Shift + Q)'), 'onclick' => 'fullscreen.blockquote();', 'both' => false ),
713 'image' => array( 'title' => __('Insert/edit image (Alt + Shift + M)'), 'onclick' => "fullscreen.medialib();", 'both' => true ),
715 'link' => array( 'title' => __('Insert/edit link (Alt + Shift + A)'), 'onclick' => 'fullscreen.link();', 'both' => true ),
716 'unlink' => array( 'title' => __('Unlink (Alt + Shift + S)'), 'onclick' => 'fullscreen.unlink();', 'both' => false ),
718 'help' => array( 'title' => __('Help (Alt + Shift + H)'), 'onclick' => 'fullscreen.help();', 'both' => false )
721 $buttons = apply_filters( 'wp_fullscreen_buttons', $buttons );
723 foreach ( $buttons as $button => $args ) {
724 if ( 'separator' == $args ) { ?>
725 <div><span aria-orientation="vertical" role="separator" class="mceSeparator"></span></div>
729 <div<?php if ( $args['both'] ) { ?> class="wp-fullscreen-both"<?php } ?>>
730 <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">
731 <span class="mceIcon mce_<?php echo $button; ?>"></span>
739 <div id="wp-fullscreen-save">
740 <input type="button" class="button-primary right" value="<?php echo $save; ?>" onclick="fullscreen.save();" />
741 <span class="spinner"></span>
742 <span class="fs-saved"><?php if ( $post->post_status == 'publish' ) _e('Updated.'); else _e('Saved.'); ?></span>
749 <div id="wp-fullscreen-wrap" style="width:<?php echo $dfw_width; ?>px;">
750 <?php if ( post_type_supports($post->post_type, 'title') ) { ?>
751 <label id="wp-fullscreen-title-prompt-text" for="wp-fullscreen-title"><?php echo apply_filters( 'enter_title_here', __( 'Enter title here' ), $post ); ?></label>
752 <input type="text" id="wp-fullscreen-title" value="" autocomplete="off" />
755 <div id="wp-fullscreen-container">
756 <textarea id="wp_mce_fullscreen"></textarea>
759 <div id="wp-fullscreen-status">
760 <div id="wp-fullscreen-count"><?php printf( __( 'Word count: %s' ), '<span class="word-count">0</span>' ); ?></div>
761 <div id="wp-fullscreen-tagline"><?php _e('Just write.'); ?></div>
766 <div class="fullscreen-overlay" id="fullscreen-overlay"></div>
767 <div class="fullscreen-overlay fullscreen-fader fade-600" id="fullscreen-fader"></div>
772 * Performs post queries for internal linking.
776 * @param array $args Optional. Accepts 'pagenum' and 's' (search) arguments.
777 * @return array Results.
779 public static function wp_link_query( $args = array() ) {
780 $pts = get_post_types( array( 'public' => true ), 'objects' );
781 $pt_names = array_keys( $pts );
784 'post_type' => $pt_names,
785 'suppress_filters' => true,
786 'update_post_term_cache' => false,
787 'update_post_meta_cache' => false,
788 'post_status' => 'publish',
790 'orderby' => 'post_date',
791 'posts_per_page' => 20,
794 $args['pagenum'] = isset( $args['pagenum'] ) ? absint( $args['pagenum'] ) : 1;
796 if ( isset( $args['s'] ) )
797 $query['s'] = $args['s'];
799 $query['offset'] = $args['pagenum'] > 1 ? $query['posts_per_page'] * ( $args['pagenum'] - 1 ) : 0;
802 $get_posts = new WP_Query;
803 $posts = $get_posts->query( $query );
804 // Check if any posts were found.
805 if ( ! $get_posts->post_count )
810 foreach ( $posts as $post ) {
811 if ( 'post' == $post->post_type )
812 $info = mysql2date( __( 'Y/m/d' ), $post->post_date );
814 $info = $pts[ $post->post_type ]->labels->singular_name;
818 'title' => trim( esc_html( strip_tags( get_the_title( $post ) ) ) ),
819 'permalink' => get_permalink( $post->ID ),
828 * Dialog for internal linking.
832 public static function wp_link_dialog() {
834 <div style="display:none;">
835 <form id="wp-link" tabindex="-1">
836 <?php wp_nonce_field( 'internal-linking', '_ajax_linking_nonce', false ); ?>
837 <div id="link-selector">
838 <div id="link-options">
839 <p class="howto"><?php _e( 'Enter the destination URL' ); ?></p>
841 <label><span><?php _e( 'URL' ); ?></span><input id="url-field" type="text" name="href" /></label>
844 <label><span><?php _e( 'Title' ); ?></span><input id="link-title-field" type="text" name="linktitle" /></label>
846 <div class="link-target">
847 <label><input type="checkbox" id="link-target-checkbox" /> <?php _e( 'Open link in a new window/tab' ); ?></label>
850 <?php $show_internal = '1' == get_user_setting( 'wplink', '0' ); ?>
851 <p class="howto toggle-arrow <?php if ( $show_internal ) echo 'toggle-arrow-active'; ?>" id="internal-toggle"><?php _e( 'Or link to existing content' ); ?></p>
852 <div id="search-panel"<?php if ( ! $show_internal ) echo ' style="display:none"'; ?>>
853 <div class="link-search-wrapper">
855 <span class="search-label"><?php _e( 'Search' ); ?></span>
856 <input type="search" id="search-field" class="link-search-field" autocomplete="off" />
857 <span class="spinner"></span>
860 <div id="search-results" class="query-results">
862 <div class="river-waiting">
863 <span class="spinner"></span>
866 <div id="most-recent-results" class="query-results">
867 <div class="query-notice"><em><?php _e( 'No search term specified. Showing recent items.' ); ?></em></div>
869 <div class="river-waiting">
870 <span class="spinner"></span>
875 <div class="submitbox">
876 <div id="wp-link-update">
877 <input type="submit" value="<?php esc_attr_e( 'Add Link' ); ?>" class="button-primary" id="wp-link-submit" name="wp-link-submit">
879 <div id="wp-link-cancel">
880 <a class="submitdelete deletion" href="#"><?php _e( 'Cancel' ); ?></a>