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' => get_option('default_post_edit_rows', 10), // rows="..."
37 'editor_css' => '', // intended for extra styles for both visual and HTML editors buttons, needs to include the <style> tags, can use "scoped".
38 'editor_class' => '', // add extra class(es) to the editor textarea
39 'teeny' => false, // output the minimal editor config used in Press This
40 'dfw' => false, // replace the default fullscreen with DFW (needs specific DOM elements and css)
41 'tinymce' => true, // load TinyMCE, can be used to pass settings directly to TinyMCE using an array()
42 'quicktags' => true // load Quicktags, can be used to pass settings directly to Quicktags using an array()
45 self::$this_tinymce = ( $set['tinymce'] && user_can_richedit() );
46 self::$this_quicktags = (bool) $set['quicktags'];
48 if ( self::$this_tinymce )
49 self::$has_tinymce = true;
51 if ( self::$this_quicktags )
52 self::$has_quicktags = true;
58 * Outputs the HTML for a single instance of the editor.
60 * @param string $content The initial content of the editor.
61 * @param string $editor_id ID for the textarea and TinyMCE and Quicktags instances (can contain only ASCII letters and numbers).
62 * @param array $settings See the _parse_settings() method for description.
64 public static function editor( $content, $editor_id, $settings = array() ) {
66 $set = self::parse_settings($editor_id, $settings);
67 $editor_class = ' class="' . trim( $set['editor_class'] . ' wp-editor-area' ) . '"';
68 $tabindex = $set['tabindex'] ? ' tabindex="' . (int) $set['tabindex'] . '"' : '';
69 $rows = ' rows="' . (int) $set['textarea_rows'] . '"';
70 $switch_class = 'html-active';
71 $toolbar = $buttons = '';
73 if ( !current_user_can( 'upload_files' ) )
74 $set['media_buttons'] = false;
76 if ( self::$this_quicktags && self::$this_tinymce ) {
77 $switch_class = 'html-active';
79 if ( 'html' == wp_default_editor() ) {
80 add_filter('the_editor_content', 'wp_htmledit_pre');
82 add_filter('the_editor_content', 'wp_richedit_pre');
83 $switch_class = 'tmce-active';
86 $buttons .= '<a id="' . $editor_id . '-html" class="hide-if-no-js wp-switch-editor switch-html" onclick="switchEditors.switchto(this);">' . __('HTML') . "</a>\n";
87 $buttons .= '<a id="' . $editor_id . '-tmce" class="hide-if-no-js wp-switch-editor switch-tmce" onclick="switchEditors.switchto(this);">' . __('Visual') . "</a>\n";
90 echo '<div id="wp-' . $editor_id . '-wrap" class="wp-editor-wrap ' . $switch_class . '">';
92 if ( self::$editor_buttons_css ) {
93 wp_print_styles('editor-buttons');
94 self::$editor_buttons_css = false;
97 if ( !empty($set['editor_css']) )
98 echo $set['editor_css'] . "\n";
100 if ( !empty($buttons) || $set['media_buttons'] ) {
101 echo '<div id="wp-' . $editor_id . '-editor-tools" class="wp-editor-tools">';
104 if ( $set['media_buttons'] ) {
105 self::$has_medialib = true;
107 if ( !function_exists('media_buttons') )
108 include(ABSPATH . 'wp-admin/includes/media.php');
110 echo '<div id="wp-' . $editor_id . '-media-buttons" class="hide-if-no-js wp-media-buttons">';
111 do_action('media_buttons', $editor_id);
117 $the_editor = apply_filters('the_editor', '<div id="wp-' . $editor_id . '-editor-container" class="wp-editor-container"><textarea' . $editor_class . $rows . $tabindex . ' cols="40" name="' . $set['textarea_name'] . '" id="' . $editor_id . '">%s</textarea></div>');
118 $content = apply_filters('the_editor_content', $content);
120 printf($the_editor, $content);
123 self::editor_settings($editor_id, $set);
126 public static function editor_settings($editor_id, $set) {
127 global $editor_styles, $post;
130 if ( empty(self::$first_init) ) {
132 add_action( 'admin_print_footer_scripts', array( __CLASS__, 'editor_js'), 50 );
133 add_action( 'admin_footer', array( __CLASS__, 'enqueue_scripts'), 1 );
135 add_action( 'wp_print_footer_scripts', array( __CLASS__, 'editor_js'), 50 );
136 add_action( 'wp_footer', array( __CLASS__, 'enqueue_scripts'), 1 );
140 if ( self::$this_quicktags ) {
147 if ( is_array($set['quicktags']) )
148 $qtInit = array_merge($qtInit, $set['quicktags']);
150 if ( empty($qtInit['buttons']) )
151 $qtInit['buttons'] = 'strong,em,link,block,del,ins,img,ul,ol,li,code,more,spell,close';
154 $qtInit['buttons'] .= ',fullscreen';
156 $qtInit = apply_filters('quicktags_settings', $qtInit, $editor_id);
157 self::$qt_settings[$editor_id] = $qtInit;
159 self::$qt_buttons = array_merge( self::$qt_buttons, explode(',', $qtInit['buttons']) );
162 if ( self::$this_tinymce ) {
164 if ( empty(self::$first_init) ) {
165 self::$baseurl = includes_url('js/tinymce');
166 self::$mce_locale = $mce_locale = ( '' == get_locale() ) ? 'en' : strtolower( substr(get_locale(), 0, 2) ); // only ISO 639-1
167 $no_captions = (bool) apply_filters( 'disable_captions', '' );
168 $plugins = array( 'inlinepopups', 'spellchecker', 'tabfocus', 'paste', 'media', 'fullscreen', 'wordpress', 'wpeditimage', 'wpgallery', 'wplink', 'wpdialogs' );
172 if ( $set['teeny'] ) {
173 self::$plugins = $plugins = apply_filters( 'teeny_mce_plugins', array('inlinepopups', 'fullscreen', 'wordpress', 'wplink', 'wpdialogs'), $editor_id );
176 The following filter takes an associative array of external plugins for TinyMCE in the form 'plugin_name' => 'url'.
177 It adds the plugin's name to TinyMCE's plugins init and the call to PluginManager to load the plugin.
178 The url should be absolute and should include the js file name to be loaded. Example:
179 array( 'myplugin' => 'http://my-site.com/wp-content/plugins/myfolder/mce_plugin.js' )
180 If the plugin uses a button, it should be added with one of the "$mce_buttons" filters.
182 $mce_external_plugins = apply_filters('mce_external_plugins', array());
184 if ( ! empty($mce_external_plugins) ) {
187 The following filter loads external language files for TinyMCE plugins.
188 It takes an associative array 'plugin_name' => 'path', where path is the
189 include path to the file. The language file should follow the same format as
190 /tinymce/langs/wp-langs.php and should define a variable $strings that
191 holds all translated strings.
192 When this filter is not used, the function will try to load {mce_locale}.js.
193 If that is not found, en.js will be tried next.
195 $mce_external_languages = apply_filters('mce_external_languages', array());
197 $loaded_langs = array();
200 if ( ! empty($mce_external_languages) ) {
201 foreach ( $mce_external_languages as $name => $path ) {
202 if ( @is_file($path) && @is_readable($path) ) {
204 $ext_plugins .= $strings . "\n";
205 $loaded_langs[] = $name;
210 foreach ( $mce_external_plugins as $name => $url ) {
212 if ( is_ssl() ) $url = str_replace('http://', 'https://', $url);
214 $plugins[] = '-' . $name;
216 $plugurl = dirname($url);
217 $strings = $str1 = $str2 = '';
218 if ( ! in_array($name, $loaded_langs) ) {
219 $path = str_replace( content_url(), '', $plugurl );
220 $path = WP_CONTENT_DIR . $path . '/langs/';
222 if ( function_exists('realpath') )
223 $path = trailingslashit( realpath($path) );
225 if ( @is_file($path . $mce_locale . '.js') )
226 $strings .= @file_get_contents($path . $mce_locale . '.js') . "\n";
228 if ( @is_file($path . $mce_locale . '_dlg.js') )
229 $strings .= @file_get_contents($path . $mce_locale . '_dlg.js') . "\n";
231 if ( 'en' != $mce_locale && empty($strings) ) {
232 if ( @is_file($path . 'en.js') ) {
233 $str1 = @file_get_contents($path . 'en.js');
234 $strings .= preg_replace( '/([\'"])en\./', '$1' . $mce_locale . '.', $str1, 1 ) . "\n";
237 if ( @is_file($path . 'en_dlg.js') ) {
238 $str2 = @file_get_contents($path . 'en_dlg.js');
239 $strings .= preg_replace( '/([\'"])en\./', '$1' . $mce_locale . '.', $str2, 1 ) . "\n";
243 if ( ! empty($strings) )
244 $ext_plugins .= "\n" . $strings . "\n";
247 $ext_plugins .= 'tinyMCEPreInit.load_ext("' . $plugurl . '", "' . $mce_locale . '");' . "\n";
248 $ext_plugins .= 'tinymce.PluginManager.load("' . $name . '", "' . $url . '");' . "\n";
252 $plugins = array_unique( apply_filters('tiny_mce_plugins', $plugins) );
256 $plugins[] = 'wpfullscreen';
258 self::$plugins = $plugins;
259 self::$ext_plugins = $ext_plugins;
262 translators: These languages show up in the spellchecker drop-down menu, in the order specified, and with the first
263 language listed being the default language. They must be comma-separated and take the format of name=code, where name
264 is the language name (which you may internationalize), and code is a valid ISO 639 language code. Please test the
265 spellchecker with your values.
267 $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' );
270 The following filter allows localization scripts to change the languages displayed in the spellchecker's drop-down menu.
271 By default it uses Google's spellchecker API, but can be configured to use PSpell/ASpell if installed on the server.
272 The + sign marks the default language. More: http://www.tinymce.com/wiki.php/Plugin:spellchecker.
274 $mce_spellchecker_languages = apply_filters( 'mce_spellchecker_languages', '+' . $mce_spellchecker_languages );
276 self::$first_init = array(
279 'theme' => 'advanced',
280 'skin' => 'wp_theme',
281 'language' => self::$mce_locale,
282 'spellchecker_languages' => $mce_spellchecker_languages,
283 'theme_advanced_toolbar_location' => 'top',
284 'theme_advanced_toolbar_align' => 'left',
285 'theme_advanced_statusbar_location' => 'bottom',
286 'theme_advanced_resizing' => true,
287 'theme_advanced_resize_horizontal' => false,
288 'dialog_type' => 'modal',
291 {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}},
292 {selector : 'img,table', classes : 'alignleft'}
295 {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}},
296 {selector : 'img,table', classes : 'aligncenter'}
299 {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}},
300 {selector : 'img,table', classes : 'alignright'}
302 strikethrough : {inline : 'del'}
304 'relative_urls' => false,
305 'remove_script_host' => false,
306 'convert_urls' => false,
307 'remove_linebreaks' => true,
308 'gecko_spellcheck' => true,
309 'fix_list_elements' => true,
310 'keep_styles' => false,
311 'entities' => '38,amp,60,lt,62,gt',
312 'accessibility_focus' => true,
313 'tabfocus_elements' => 'title,publish',
314 'media_strict' => false,
315 'paste_remove_styles' => true,
316 'paste_remove_spans' => true,
317 'paste_strip_class_attributes' => 'all',
318 'paste_text_use_dialog' => true,
319 'spellchecker_rpc_url' => self::$baseurl . '/plugins/spellchecker/rpc.php',
320 'extended_valid_elements' => 'article[*],aside[*],audio[*],canvas[*],command[*],datalist[*],details[*],embed[*],figcaption[*],figure[*],footer[*],header[*],hgroup[*],keygen[*],mark[*],meter[*],nav[*],output[*],progress[*],section[*],source[*],summary,time[*],video[*],wbr',
321 'wpeditimage_disable_captions' => $no_captions,
322 'wp_fullscreen_content_css' => self::$baseurl . '/plugins/wpfullscreen/css/wp-fullscreen.css',
323 'plugins' => implode( ',', $plugins )
326 // load editor_style.css if the current theme supports it
327 if ( ! empty( $editor_styles ) && is_array( $editor_styles ) ) {
329 $editor_styles = array_unique($editor_styles);
330 $style_uri = get_stylesheet_directory_uri();
331 $style_dir = get_stylesheet_directory();
333 if ( is_child_theme() ) {
334 $template_uri = get_template_directory_uri();
335 $template_dir = get_template_directory();
337 foreach ( $editor_styles as $key => $file ) {
338 if ( $file && file_exists( "$template_dir/$file" ) ) {
339 $mce_css[] = "$template_uri/$file";
340 $editor_styles[$key] = '';
345 foreach ( $editor_styles as $file ) {
346 if ( $file && file_exists( "$style_dir/$file" ) )
347 $mce_css[] = "$style_uri/$file";
350 $mce_css = implode( ',', $mce_css );
355 $mce_css = trim( apply_filters( 'mce_css', $mce_css ), ' ,' );
357 if ( ! empty($mce_css) )
358 self::$first_init['content_css'] = $mce_css;
361 if ( $set['teeny'] ) {
362 $mce_buttons = apply_filters( 'teeny_mce_buttons', array('bold', 'italic', 'underline', 'blockquote', 'separator', 'strikethrough', 'bullist', 'numlist', 'justifyleft', 'justifycenter', 'justifyright', 'undo', 'redo', 'link', 'unlink', 'fullscreen'), $editor_id );
363 $mce_buttons_2 = $mce_buttons_3 = $mce_buttons_4 = array();
365 $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);
366 $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);
367 $mce_buttons_3 = apply_filters('mce_buttons_3', array(), $editor_id);
368 $mce_buttons_4 = apply_filters('mce_buttons_4', array(), $editor_id);
371 $body_class = $editor_id;
374 $body_class .= " post-type-$post->post_type";
376 if ( !empty($set['tinymce']['body_class']) ) {
377 $body_class .= ' ' . $set['tinymce']['body_class'];
378 unset($set['tinymce']['body_class']);
382 // replace the first 'fullscreen' with 'wp_fullscreen'
383 if ( ($key = array_search('fullscreen', $mce_buttons)) !== false )
384 $mce_buttons[$key] = 'wp_fullscreen';
385 elseif ( ($key = array_search('fullscreen', $mce_buttons_2)) !== false )
386 $mce_buttons_2[$key] = 'wp_fullscreen';
387 elseif ( ($key = array_search('fullscreen', $mce_buttons_3)) !== false )
388 $mce_buttons_3[$key] = 'wp_fullscreen';
389 elseif ( ($key = array_search('fullscreen', $mce_buttons_4)) !== false )
390 $mce_buttons_4[$key] = 'wp_fullscreen';
394 'elements' => $editor_id,
395 'wpautop' => (bool) $set['wpautop'],
396 'remove_linebreaks' => (bool) $set['wpautop'],
397 'apply_source_formatting' => (bool) !$set['wpautop'],
398 'theme_advanced_buttons1' => implode($mce_buttons, ','),
399 'theme_advanced_buttons2' => implode($mce_buttons_2, ','),
400 'theme_advanced_buttons3' => implode($mce_buttons_3, ','),
401 'theme_advanced_buttons4' => implode($mce_buttons_4, ','),
402 'body_class' => $body_class
406 $mceInit = array_merge(self::$first_init, $mceInit);
408 if ( is_array($set['tinymce']) )
409 $mceInit = array_merge($mceInit, $set['tinymce']);
411 // For people who really REALLY know what they're doing with TinyMCE
412 // You can modify initArray to add, remove, change elements of the config before tinyMCE.init
413 // Setting "valid_elements", "invalid_elements" and "extended_valid_elements" can be done through this filter.
414 // Best is to use the default cleanup by not specifying valid_elements, as TinyMCE contains full set of XHTML 1.0.
415 if ( $set['teeny'] ) {
416 $mceInit = apply_filters('teeny_mce_before_init', $mceInit, $editor_id);
418 $mceInit = apply_filters('tiny_mce_before_init', $mceInit, $editor_id);
421 if ( empty($mceInit['theme_advanced_buttons3']) && !empty($mceInit['theme_advanced_buttons4']) ) {
422 $mceInit['theme_advanced_buttons3'] = $mceInit['theme_advanced_buttons4'];
423 $mceInit['theme_advanced_buttons4'] = '';
426 self::$mce_settings[$editor_id] = $mceInit;
427 } // end if self::$this_tinymce
430 private static function _parse_init($init) {
433 foreach ( $init as $k => $v ) {
435 $val = $v ? 'true' : 'false';
436 $options .= $k . ':' . $val . ',';
438 } elseif ( !empty($v) && is_string($v) && ( ('{' == $v{0} && '}' == $v{strlen($v) - 1}) || ('[' == $v{0} && ']' == $v{strlen($v) - 1}) || preg_match('/^\(?function ?\(/', $v) ) ) {
439 $options .= $k . ':' . $v . ',';
442 $options .= $k . ':"' . $v . '",';
445 return '{' . trim( $options, ' ,' ) . '}';
448 public static function enqueue_scripts() {
449 wp_enqueue_script('word-count');
451 if ( self::$has_tinymce )
452 wp_enqueue_script('editor');
454 if ( self::$has_quicktags )
455 wp_enqueue_script('quicktags');
457 if ( in_array('wplink', self::$plugins, true) || in_array('link', self::$qt_buttons, true) ) {
458 wp_enqueue_script('wplink');
459 wp_enqueue_script('wpdialogs-popup');
460 wp_enqueue_style('wp-jquery-ui-dialog');
463 if ( in_array('wpfullscreen', self::$plugins, true) || in_array('fullscreen', self::$qt_buttons, true) )
464 wp_enqueue_script('wp-fullscreen');
466 if ( self::$has_medialib ) {
468 wp_enqueue_script('media-upload');
472 public static function editor_js() {
473 global $tinymce_version, $concatenate_scripts, $compress_scripts;
476 * Filter "tiny_mce_version" is deprecated
478 * The tiny_mce_version filter is not needed since external plugins are loaded directly by TinyMCE.
479 * These plugins can be refreshed by appending query string to the URL passed to "mce_external_plugins" filter.
480 * 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).
482 $version = 'ver=' . $tinymce_version;
483 $tmce_on = !empty(self::$mce_settings);
485 if ( ! isset($concatenate_scripts) )
486 script_concat_settings();
488 $compressed = $compress_scripts && $concatenate_scripts && isset($_SERVER['HTTP_ACCEPT_ENCODING'])
489 && false !== stripos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip');
491 if ( $tmce_on && 'en' != self::$mce_locale )
492 include_once(ABSPATH . WPINC . '/js/tinymce/langs/wp-langs.php');
494 $mceInit = $qtInit = '';
496 foreach ( self::$mce_settings as $editor_id => $init ) {
497 $options = self::_parse_init( $init );
498 $mceInit .= "'$editor_id':{$options},";
500 $mceInit = '{' . trim($mceInit, ',') . '}';
505 if ( !empty(self::$qt_settings) ) {
506 foreach ( self::$qt_settings as $editor_id => $init ) {
507 $options = self::_parse_init( $init );
508 $qtInit .= "'$editor_id':{$options},";
510 $qtInit = '{' . trim($qtInit, ',') . '}';
516 'plugins' => implode( ',', self::$plugins ),
517 'theme' => 'advanced',
518 'language' => self::$mce_locale
521 $suffix = ( defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ) ? '_src' : '';
523 do_action('before_wp_tiny_mce', self::$mce_settings);
526 <script type="text/javascript">
528 base : "<?php echo self::$baseurl; ?>",
529 suffix : "<?php echo $suffix; ?>",
530 query : "<?php echo $version; ?>",
531 mceInit : <?php echo $mceInit; ?>,
532 qtInit : <?php echo $qtInit; ?>,
533 ref : <?php echo self::_parse_init( $ref ); ?>,
534 load_ext : function(url,lang){var sl=tinymce.ScriptLoader;sl.markDone(url+'/langs/'+lang+'.js');sl.markDone(url+'/langs/'+lang+'_dlg.js');}
539 $baseurl = self::$baseurl;
543 echo "<script type='text/javascript' src='{$baseurl}/wp-tinymce.php?c=1&$version'></script>\n";
545 echo "<script type='text/javascript' src='{$baseurl}/tiny_mce.js?$version'></script>\n";
547 if ( 'en' != self::$mce_locale && isset($lang) )
548 echo "<script type='text/javascript'>\n$lang\n</script>\n";
550 echo "<script type='text/javascript' src='{$baseurl}/langs/wp-langs-en.js?$version'></script>\n";
554 <script type="text/javascript">
556 var init, ed, qt, first_init, mce = <?php echo wp_default_editor() == 'tinymce' ? 'true' : 'false'; ?>;
558 if ( typeof(tinymce) == 'object' ) {
559 // mark wp_theme/ui.css as loaded
560 tinymce.DOM.files[tinymce.baseURI.getURI() + '/themes/advanced/skins/wp_theme/ui.css'] = true;
562 for ( ed in tinyMCEPreInit.mceInit ) {
564 init = tinyMCEPreInit.mceInit[ed] = tinymce.extend( {}, first_init, tinyMCEPreInit.mceInit[ed] );
566 init = first_init = tinyMCEPreInit.mceInit[ed];
570 try { tinymce.init(init); } catch(e){}
574 if ( typeof(QTags) == 'function' ) {
575 for ( qt in tinyMCEPreInit.qtInit ) {
576 try { quicktags( tinyMCEPreInit.qtInit[qt] ); } catch(e){}
583 jQuery('.wp-editor-wrap').mousedown(function(e){
584 wpActiveEditor = this.id.slice(3, -5);
589 if ( self::$ext_plugins )
590 echo self::$ext_plugins . "\n";
592 if ( ! $compressed && $tmce_on ) {
594 (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');}});})();
599 echo 'var ajaxurl = "' . admin_url( 'admin-ajax.php', 'relative' ) . '";';
604 if ( in_array('wplink', self::$plugins, true) || in_array('link', self::$qt_buttons, true) )
605 self::wp_link_dialog();
607 if ( in_array('wpfullscreen', self::$plugins, true) || in_array('fullscreen', self::$qt_buttons, true) )
608 self::wp_fullscreen_html();
610 do_action('after_wp_tiny_mce', self::$mce_settings);
613 public static function wp_fullscreen_html() {
614 global $content_width, $post;
616 $width = isset($content_width) && 800 > $content_width ? $content_width : 800;
617 $width = $width + 22; // compensate for the padding and border
618 $dfw_width = get_user_setting( 'dfw_width', $width );
619 $save = isset($post->post_status) && $post->post_status == 'publish' ? __('Update') : __('Save');
621 <div id="wp-fullscreen-body"<?php if ( is_rtl() ) echo ' class="rtl"'; ?>>
622 <div id="fullscreen-topbar">
623 <div id="wp-fullscreen-toolbar">
624 <div id="wp-fullscreen-close"><a href="#" onclick="fullscreen.off();return false;"><?php _e('Exit fullscreen'); ?></a></div>
625 <div id="wp-fullscreen-central-toolbar" style="width:<?php echo $width; ?>px;">
627 <div id="wp-fullscreen-mode-bar"><div id="wp-fullscreen-modes">
628 <a href="#" onclick="fullscreen.switchmode('tinymce');return false;"><?php _e('Visual'); ?></a>
629 <a href="#" onclick="fullscreen.switchmode('html');return false;"><?php _e('HTML'); ?></a>
632 <div id="wp-fullscreen-button-bar"><div id="wp-fullscreen-buttons" class="wp_themeSkin">
636 // format: title, onclick, show in both editors
637 'bold' => array( 'title' => __('Bold (Ctrl + B)'), 'onclick' => 'fullscreen.b();', 'both' => false ),
638 'italic' => array( 'title' => __('Italic (Ctrl + I)'), 'onclick' => 'fullscreen.i();', 'both' => false ),
640 'bullist' => array( 'title' => __('Unordered list (Alt + Shift + U)'), 'onclick' => 'fullscreen.ul();', 'both' => false ),
641 'numlist' => array( 'title' => __('Ordered list (Alt + Shift + O)'), 'onclick' => 'fullscreen.ol();', 'both' => false ),
643 'blockquote' => array( 'title' => __('Blockquote (Alt + Shift + Q)'), 'onclick' => 'fullscreen.blockquote();', 'both' => false ),
644 'image' => array( 'title' => __('Insert/edit image (Alt + Shift + M)'), 'onclick' => "fullscreen.medialib();", 'both' => true ),
646 'link' => array( 'title' => __('Insert/edit link (Alt + Shift + A)'), 'onclick' => 'fullscreen.link();', 'both' => true ),
647 'unlink' => array( 'title' => __('Unlink (Alt + Shift + S)'), 'onclick' => 'fullscreen.unlink();', 'both' => false ),
649 'help' => array( 'title' => __('Help (Alt + Shift + H)'), 'onclick' => 'fullscreen.help();', 'both' => false )
652 $buttons = apply_filters( 'wp_fullscreen_buttons', $buttons );
654 foreach ( $buttons as $button => $args ) {
655 if ( 'separator' == $args ) { ?>
656 <div><span aria-orientation="vertical" role="separator" class="mceSeparator"></span></div>
660 <div<?php if ( $args['both'] ) { ?> class="wp-fullscreen-both"<?php } ?>>
661 <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">
662 <span class="mceIcon mce_<?php echo $button; ?>"></span>
670 <div id="wp-fullscreen-save">
671 <span><?php if ( $post->post_status == 'publish' ) _e('Updated.'); else _e('Saved.'); ?></span>
672 <img src="<?php echo admin_url('images/wpspin_light.gif'); ?>" alt="" />
673 <input type="button" class="button-primary" value="<?php echo $save; ?>" onclick="fullscreen.save();" />
680 <div id="wp-fullscreen-wrap" style="width:<?php echo $dfw_width; ?>px;">
681 <?php if ( post_type_supports($post->post_type, 'title') ) { ?>
682 <label id="wp-fullscreen-title-prompt-text" for="wp-fullscreen-title"><?php echo apply_filters( 'enter_title_here', __( 'Enter title here' ), $post ); ?></label>
683 <input type="text" id="wp-fullscreen-title" value="" autocomplete="off" />
686 <div id="wp-fullscreen-container">
687 <textarea id="wp_mce_fullscreen"></textarea>
690 <div id="wp-fullscreen-status">
691 <div id="wp-fullscreen-count"><?php printf( __( 'Word count: %s' ), '<span class="word-count">0</span>' ); ?></div>
692 <div id="wp-fullscreen-tagline"><?php _e('Just write.'); ?></div>
697 <div class="fullscreen-overlay" id="fullscreen-overlay"></div>
698 <div class="fullscreen-overlay fullscreen-fader fade-600" id="fullscreen-fader"></div>
703 * Performs post queries for internal linking.
707 * @param array $args Optional. Accepts 'pagenum' and 's' (search) arguments.
708 * @return array Results.
710 public static function wp_link_query( $args = array() ) {
711 $pts = get_post_types( array( 'public' => true ), 'objects' );
712 $pt_names = array_keys( $pts );
715 'post_type' => $pt_names,
716 'suppress_filters' => true,
717 'update_post_term_cache' => false,
718 'update_post_meta_cache' => false,
719 'post_status' => 'publish',
721 'orderby' => 'post_date',
722 'posts_per_page' => 20,
725 $args['pagenum'] = isset( $args['pagenum'] ) ? absint( $args['pagenum'] ) : 1;
727 if ( isset( $args['s'] ) )
728 $query['s'] = $args['s'];
730 $query['offset'] = $args['pagenum'] > 1 ? $query['posts_per_page'] * ( $args['pagenum'] - 1 ) : 0;
733 $get_posts = new WP_Query;
734 $posts = $get_posts->query( $query );
735 // Check if any posts were found.
736 if ( ! $get_posts->post_count )
741 foreach ( $posts as $post ) {
742 if ( 'post' == $post->post_type )
743 $info = mysql2date( __( 'Y/m/d' ), $post->post_date );
745 $info = $pts[ $post->post_type ]->labels->singular_name;
749 'title' => trim( esc_html( strip_tags( get_the_title( $post ) ) ) ),
750 'permalink' => get_permalink( $post->ID ),
759 * Dialog for internal linking.
763 public static function wp_link_dialog() {
765 <div style="display:none;">
766 <form id="wp-link" tabindex="-1">
767 <?php wp_nonce_field( 'internal-linking', '_ajax_linking_nonce', false ); ?>
768 <div id="link-selector">
769 <div id="link-options">
770 <p class="howto"><?php _e( 'Enter the destination URL' ); ?></p>
772 <label><span><?php _e( 'URL' ); ?></span><input id="url-field" type="text" tabindex="10" name="href" /></label>
775 <label><span><?php _e( 'Title' ); ?></span><input id="link-title-field" type="text" tabindex="20" name="linktitle" /></label>
777 <div class="link-target">
778 <label><input type="checkbox" id="link-target-checkbox" tabindex="30" /> <?php _e( 'Open link in a new window/tab' ); ?></label>
781 <?php $show_internal = '1' == get_user_setting( 'wplink', '0' ); ?>
782 <p class="howto toggle-arrow <?php if ( $show_internal ) echo 'toggle-arrow-active'; ?>" id="internal-toggle"><?php _e( 'Or link to existing content' ); ?></p>
783 <div id="search-panel"<?php if ( ! $show_internal ) echo ' style="display:none"'; ?>>
784 <div class="link-search-wrapper">
786 <span><?php _e( 'Search' ); ?></span>
787 <input type="search" id="search-field" class="link-search-field" tabindex="60" autocomplete="off" />
788 <img class="waiting" src="<?php echo esc_url( admin_url( 'images/wpspin_light.gif' ) ); ?>" alt="" />
791 <div id="search-results" class="query-results">
793 <div class="river-waiting">
794 <img class="waiting" src="<?php echo esc_url( admin_url( 'images/wpspin_light.gif' ) ); ?>" alt="" />
797 <div id="most-recent-results" class="query-results">
798 <div class="query-notice"><em><?php _e( 'No search term specified. Showing recent items.' ); ?></em></div>
800 <div class="river-waiting">
801 <img class="waiting" src="<?php echo esc_url( admin_url( 'images/wpspin_light.gif' ) ); ?>" alt="" />
806 <div class="submitbox">
807 <div id="wp-link-cancel">
808 <a class="submitdelete deletion" href="#"><?php _e( 'Cancel' ); ?></a>
810 <div id="wp-link-update">
811 <input type="submit" tabindex="100" value="<?php esc_attr_e( 'Add Link' ); ?>" class="button-primary" id="wp-link-submit" name="wp-link-submit">