PageRenderTime 62ms CodeModel.GetById 14ms app.highlight 35ms RepoModel.GetById 2ms app.codeStats 0ms

/wp-includes/class-wp-editor.php

https://bitbucket.org/julianelve/vendor-wordpress
PHP | 863 lines | 641 code | 145 blank | 77 comment | 150 complexity | 43ca11e1771c38b016d3ebaa23806f0d MD5 | raw file
  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
 11final 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}