web/wp-includes/class-wp-editor.php
changeset 194 32102edaa81b
child 204 09a1c134465b
equal deleted inserted replaced
193:2f6f6f7551ca 194:32102edaa81b
       
     1 <?php
       
     2 /**
       
     3  * Facilitates adding of the WordPress editor as used on the Write and Edit screens.
       
     4  *
       
     5  * @package WordPress
       
     6  * @since 3.3.0
       
     7  *
       
     8  * Private, not included by default. See wp_editor() in wp-includes/general-template.php.
       
     9  */
       
    10 
       
    11 final class _WP_Editors {
       
    12 	public static $mce_locale;
       
    13 
       
    14 	private static $mce_settings = array();
       
    15 	private static $qt_settings = array();
       
    16 	private static $plugins = array();
       
    17 	private static $qt_buttons = array();
       
    18 	private static $ext_plugins;
       
    19 	private static $baseurl;
       
    20 	private static $first_init;
       
    21 	private static $this_tinymce = false;
       
    22 	private static $this_quicktags = false;
       
    23 	private static $has_tinymce = false;
       
    24 	private static $has_quicktags = false;
       
    25 	private static $has_medialib = false;
       
    26 	private static $editor_buttons_css = true;
       
    27 
       
    28 	private function __construct() {}
       
    29 
       
    30 	public static function parse_settings($editor_id, $settings) {
       
    31 		$set = wp_parse_args( $settings,  array(
       
    32 			'wpautop' => true, // use wpautop?
       
    33 			'media_buttons' => true, // show insert/upload button(s)
       
    34 			'textarea_name' => $editor_id, // set the textarea name to something different, square brackets [] can be used here
       
    35 			'textarea_rows' => get_option('default_post_edit_rows', 10), // rows="..."
       
    36 			'tabindex' => '',
       
    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()
       
    43 		) );
       
    44 
       
    45 		self::$this_tinymce = ( $set['tinymce'] && user_can_richedit() );
       
    46 		self::$this_quicktags = (bool) $set['quicktags'];
       
    47 
       
    48 		if ( self::$this_tinymce )
       
    49 			self::$has_tinymce = true;
       
    50 
       
    51 		if ( self::$this_quicktags )
       
    52 			self::$has_quicktags = true;
       
    53 
       
    54 		return $set;
       
    55 	}
       
    56 
       
    57 	/**
       
    58 	 * Outputs the HTML for a single instance of the editor.
       
    59 	 *
       
    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.
       
    63 	 */
       
    64 	public static function editor( $content, $editor_id, $settings = array() ) {
       
    65 
       
    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 = '';
       
    72 
       
    73 		if ( !current_user_can( 'upload_files' ) )
       
    74 			$set['media_buttons'] = false;
       
    75 
       
    76 		if ( self::$this_quicktags && self::$this_tinymce ) {
       
    77 			$switch_class = 'html-active';
       
    78 
       
    79 			if ( 'html' == wp_default_editor() ) {
       
    80 				add_filter('the_editor_content', 'wp_htmledit_pre');
       
    81 			} else {
       
    82 				add_filter('the_editor_content', 'wp_richedit_pre');
       
    83 				$switch_class = 'tmce-active';
       
    84 			}
       
    85 
       
    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";
       
    88 		}
       
    89 
       
    90 		echo '<div id="wp-' . $editor_id . '-wrap" class="wp-editor-wrap ' . $switch_class . '">';
       
    91 
       
    92 		if ( self::$editor_buttons_css ) {
       
    93 			wp_print_styles('editor-buttons');
       
    94 			self::$editor_buttons_css = false;
       
    95 		}
       
    96 
       
    97 		if ( !empty($set['editor_css']) )
       
    98 			echo $set['editor_css'] . "\n";
       
    99 
       
   100 		if ( !empty($buttons) || $set['media_buttons'] ) {
       
   101 			echo '<div id="wp-' . $editor_id . '-editor-tools" class="wp-editor-tools">';
       
   102 			echo $buttons;
       
   103 
       
   104 			if ( $set['media_buttons'] ) {
       
   105 				self::$has_medialib = true;
       
   106 
       
   107 				if ( !function_exists('media_buttons') )
       
   108 					include(ABSPATH . 'wp-admin/includes/media.php');
       
   109 
       
   110 				echo '<div id="wp-' . $editor_id . '-media-buttons" class="hide-if-no-js wp-media-buttons">';
       
   111 				do_action('media_buttons', $editor_id);
       
   112 				echo "</div>\n";
       
   113 			}
       
   114 			echo "</div>\n";
       
   115 		}
       
   116 
       
   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);
       
   119 
       
   120 		printf($the_editor, $content);
       
   121 		echo "\n</div>\n\n";
       
   122 
       
   123 		self::editor_settings($editor_id, $set);
       
   124 	}
       
   125 
       
   126 	public static function editor_settings($editor_id, $set) {
       
   127 		global $editor_styles, $post;
       
   128 		$first_run = false;
       
   129 
       
   130 		if ( empty(self::$first_init) ) {
       
   131 			if ( is_admin() ) {
       
   132 				add_action( 'admin_print_footer_scripts', array( __CLASS__, 'editor_js'), 50 );
       
   133 				add_action( 'admin_footer', array( __CLASS__, 'enqueue_scripts'), 1 );
       
   134 			} else {
       
   135 				add_action( 'wp_print_footer_scripts', array( __CLASS__, 'editor_js'), 50 );
       
   136 				add_action( 'wp_footer', array( __CLASS__, 'enqueue_scripts'), 1 );
       
   137 			}
       
   138 		}
       
   139 
       
   140 		if ( self::$this_quicktags ) {
       
   141 
       
   142 			$qtInit = array(
       
   143 				'id' => $editor_id,
       
   144 				'buttons' => ''
       
   145 			);
       
   146 
       
   147 			if ( is_array($set['quicktags']) )
       
   148 				$qtInit = array_merge($qtInit, $set['quicktags']);
       
   149 
       
   150 			if ( empty($qtInit['buttons']) )
       
   151 				$qtInit['buttons'] = 'strong,em,link,block,del,ins,img,ul,ol,li,code,more,spell,close';
       
   152 
       
   153 			if ( $set['dfw'] )
       
   154 				$qtInit['buttons'] .= ',fullscreen';
       
   155 
       
   156 			$qtInit = apply_filters('quicktags_settings', $qtInit, $editor_id);
       
   157 			self::$qt_settings[$editor_id] = $qtInit;
       
   158 
       
   159 			self::$qt_buttons = array_merge( self::$qt_buttons, explode(',', $qtInit['buttons']) );
       
   160 		}
       
   161 
       
   162 		if ( self::$this_tinymce ) {
       
   163 
       
   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' );
       
   169 				$first_run = true;
       
   170 				$ext_plugins = '';
       
   171 
       
   172 				if ( $set['teeny'] ) {
       
   173 					self::$plugins = $plugins = apply_filters( 'teeny_mce_plugins', array('inlinepopups', 'fullscreen', 'wordpress', 'wplink', 'wpdialogs'), $editor_id );
       
   174 				} else {
       
   175 					/*
       
   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.
       
   181 					*/
       
   182 					$mce_external_plugins = apply_filters('mce_external_plugins', array());
       
   183 
       
   184 					if ( ! empty($mce_external_plugins) ) {
       
   185 
       
   186 						/*
       
   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.
       
   194 						*/
       
   195 						$mce_external_languages = apply_filters('mce_external_languages', array());
       
   196 
       
   197 						$loaded_langs = array();
       
   198 						$strings = '';
       
   199 
       
   200 						if ( ! empty($mce_external_languages) ) {
       
   201 							foreach ( $mce_external_languages as $name => $path ) {
       
   202 								if ( @is_file($path) && @is_readable($path) ) {
       
   203 									include_once($path);
       
   204 									$ext_plugins .= $strings . "\n";
       
   205 									$loaded_langs[] = $name;
       
   206 								}
       
   207 							}
       
   208 						}
       
   209 
       
   210 						foreach ( $mce_external_plugins as $name => $url ) {
       
   211 
       
   212 							if ( is_ssl() ) $url = str_replace('http://', 'https://', $url);
       
   213 
       
   214 							$plugins[] = '-' . $name;
       
   215 
       
   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/';
       
   221 
       
   222 								if ( function_exists('realpath') )
       
   223 									$path = trailingslashit( realpath($path) );
       
   224 
       
   225 								if ( @is_file($path . $mce_locale . '.js') )
       
   226 									$strings .= @file_get_contents($path . $mce_locale . '.js') . "\n";
       
   227 
       
   228 								if ( @is_file($path . $mce_locale . '_dlg.js') )
       
   229 									$strings .= @file_get_contents($path . $mce_locale . '_dlg.js') . "\n";
       
   230 
       
   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";
       
   235 									}
       
   236 
       
   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";
       
   240 									}
       
   241 								}
       
   242 
       
   243 								if ( ! empty($strings) )
       
   244 									$ext_plugins .= "\n" . $strings . "\n";
       
   245 							}
       
   246 
       
   247 							$ext_plugins .= 'tinyMCEPreInit.load_ext("' . $plugurl . '", "' . $mce_locale . '");' . "\n";
       
   248 							$ext_plugins .= 'tinymce.PluginManager.load("' . $name . '", "' . $url . '");' . "\n";
       
   249 						}
       
   250 					}
       
   251 
       
   252 					$plugins = array_unique( apply_filters('tiny_mce_plugins', $plugins) );
       
   253 				}
       
   254 
       
   255 				if ( $set['dfw'] )
       
   256 					$plugins[] = 'wpfullscreen';
       
   257 
       
   258 				self::$plugins = $plugins;
       
   259 				self::$ext_plugins = $ext_plugins;
       
   260 
       
   261 				/*
       
   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.
       
   266 				*/
       
   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' );
       
   268 
       
   269 				/*
       
   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.
       
   273 				*/
       
   274 				$mce_spellchecker_languages = apply_filters( 'mce_spellchecker_languages', '+' . $mce_spellchecker_languages );
       
   275 
       
   276 				self::$first_init = array(
       
   277 					'mode' => 'exact',
       
   278 					'width' => '100%',
       
   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',
       
   289 					'formats' => "{
       
   290 						alignleft : [
       
   291 							{selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}},
       
   292 							{selector : 'img,table', classes : 'alignleft'}
       
   293 						],
       
   294 						aligncenter : [
       
   295 							{selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}},
       
   296 							{selector : 'img,table', classes : 'aligncenter'}
       
   297 						],
       
   298 						alignright : [
       
   299 							{selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}},
       
   300 							{selector : 'img,table', classes : 'alignright'}
       
   301 						],
       
   302 						strikethrough : {inline : 'del'}
       
   303 					}",
       
   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 )
       
   324 				);
       
   325 
       
   326 				// load editor_style.css if the current theme supports it
       
   327 				if ( ! empty( $editor_styles ) && is_array( $editor_styles ) ) {
       
   328 					$mce_css = array();
       
   329 					$editor_styles = array_unique($editor_styles);
       
   330 					$style_uri = get_stylesheet_directory_uri();
       
   331 					$style_dir = get_stylesheet_directory();
       
   332 
       
   333 					if ( is_child_theme() ) {
       
   334 						$template_uri = get_template_directory_uri();
       
   335 						$template_dir = get_template_directory();
       
   336 
       
   337 						foreach ( $editor_styles as $key => $file ) {
       
   338 							if ( $file && file_exists( "$template_dir/$file" ) ) {
       
   339 								$mce_css[] = "$template_uri/$file";
       
   340 							}
       
   341 						}
       
   342 					}
       
   343 
       
   344 					foreach ( $editor_styles as $file ) {
       
   345 						if ( $file && file_exists( "$style_dir/$file" ) )
       
   346 							$mce_css[] = "$style_uri/$file";
       
   347 					}
       
   348 
       
   349 					$mce_css = implode( ',', $mce_css );
       
   350 				} else {
       
   351 					$mce_css = '';
       
   352 				}
       
   353 
       
   354 				$mce_css = trim( apply_filters( 'mce_css', $mce_css ), ' ,' );
       
   355 
       
   356 				if ( ! empty($mce_css) )
       
   357 					self::$first_init['content_css'] = $mce_css;
       
   358 			}
       
   359 
       
   360 			if ( $set['teeny'] ) {
       
   361 				$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 );
       
   362 				$mce_buttons_2 = $mce_buttons_3 = $mce_buttons_4 = array();
       
   363 			} else {
       
   364 				$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);
       
   365 				$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);
       
   366 				$mce_buttons_3 = apply_filters('mce_buttons_3', array(), $editor_id);
       
   367 				$mce_buttons_4 = apply_filters('mce_buttons_4', array(), $editor_id);
       
   368 			}
       
   369 
       
   370 			$body_class = $editor_id;
       
   371 
       
   372 			if ( isset($post) )
       
   373 				$body_class .= " post-type-$post->post_type";
       
   374 
       
   375 			if ( !empty($set['tinymce']['body_class']) ) {
       
   376 				$body_class .= ' ' . $set['tinymce']['body_class'];
       
   377 				unset($set['tinymce']['body_class']);
       
   378 			}
       
   379 
       
   380 			if ( $set['dfw'] ) {
       
   381 				// replace the first 'fullscreen' with 'wp_fullscreen'
       
   382 				if ( ($key = array_search('fullscreen', $mce_buttons)) !== false )
       
   383 					$mce_buttons[$key] = 'wp_fullscreen';
       
   384 				elseif ( ($key = array_search('fullscreen', $mce_buttons_2)) !== false )
       
   385 					$mce_buttons_2[$key] = 'wp_fullscreen';
       
   386 				elseif ( ($key = array_search('fullscreen', $mce_buttons_3)) !== false )
       
   387 					$mce_buttons_3[$key] = 'wp_fullscreen';
       
   388 				elseif ( ($key = array_search('fullscreen', $mce_buttons_4)) !== false )
       
   389 					$mce_buttons_4[$key] = 'wp_fullscreen';
       
   390 			}
       
   391 
       
   392 			$mceInit = array (
       
   393 				'elements' => $editor_id,
       
   394 				'wpautop' => (bool) $set['wpautop'],
       
   395 				'remove_linebreaks' => (bool) $set['wpautop'],
       
   396 				'apply_source_formatting' => (bool) !$set['wpautop'],
       
   397 				'theme_advanced_buttons1' => implode($mce_buttons, ','),
       
   398 				'theme_advanced_buttons2' => implode($mce_buttons_2, ','),
       
   399 				'theme_advanced_buttons3' => implode($mce_buttons_3, ','),
       
   400 				'theme_advanced_buttons4' => implode($mce_buttons_4, ','),
       
   401 				'body_class' => $body_class
       
   402 			);
       
   403 
       
   404 			if ( $first_run )
       
   405 				$mceInit = array_merge(self::$first_init, $mceInit);
       
   406 
       
   407 			if ( is_array($set['tinymce']) )
       
   408 				$mceInit = array_merge($mceInit, $set['tinymce']);
       
   409 
       
   410 			// For people who really REALLY know what they're doing with TinyMCE
       
   411 			// You can modify initArray to add, remove, change elements of the config before tinyMCE.init
       
   412 			// Setting "valid_elements", "invalid_elements" and "extended_valid_elements" can be done through this filter.
       
   413 			// Best is to use the default cleanup by not specifying valid_elements, as TinyMCE contains full set of XHTML 1.0.
       
   414 			if ( $set['teeny'] ) {
       
   415 				$mceInit = apply_filters('teeny_mce_before_init', $mceInit, $editor_id);
       
   416 			} else {
       
   417 				$mceInit = apply_filters('tiny_mce_before_init', $mceInit, $editor_id);
       
   418 			}
       
   419 
       
   420 			if ( empty($mceInit['theme_advanced_buttons3']) && !empty($mceInit['theme_advanced_buttons4']) ) {
       
   421 				$mceInit['theme_advanced_buttons3'] = $mceInit['theme_advanced_buttons4'];
       
   422 				$mceInit['theme_advanced_buttons4'] = '';
       
   423 			}
       
   424 
       
   425 			self::$mce_settings[$editor_id] = $mceInit;
       
   426 		} // end if self::$this_tinymce
       
   427 	}
       
   428 
       
   429 	private static function _parse_init($init) {
       
   430 		$options = '';
       
   431 
       
   432 		foreach ( $init as $k => $v ) {
       
   433 			if ( is_bool($v) ) {
       
   434 				$val = $v ? 'true' : 'false';
       
   435 				$options .= $k . ':' . $val . ',';
       
   436 				continue;
       
   437 			} elseif ( !empty($v) && is_string($v) && ( ('{' == $v{0} && '}' == $v{strlen($v) - 1}) || ('[' == $v{0} && ']' == $v{strlen($v) - 1}) || preg_match('/^\(?function ?\(/', $v) ) ) {
       
   438 				$options .= $k . ':' . $v . ',';
       
   439 				continue;
       
   440 			}
       
   441 			$options .= $k . ':"' . $v . '",';
       
   442 		}
       
   443 
       
   444 		return '{' . trim( $options, ' ,' ) . '}';
       
   445 	}
       
   446 
       
   447 	public static function enqueue_scripts() {
       
   448 		wp_enqueue_script('word-count');
       
   449 
       
   450 		if ( self::$has_tinymce )
       
   451 			wp_enqueue_script('editor');
       
   452 
       
   453 		if ( self::$has_quicktags )
       
   454 			wp_enqueue_script('quicktags');
       
   455 
       
   456 		if ( in_array('wplink', self::$plugins, true) || in_array('link', self::$qt_buttons, true) ) {
       
   457 			wp_enqueue_script('wplink');
       
   458 			wp_enqueue_script('wpdialogs-popup');
       
   459 			wp_enqueue_style('wp-jquery-ui-dialog');
       
   460 		}
       
   461 
       
   462 		if ( in_array('wpfullscreen', self::$plugins, true) || in_array('fullscreen', self::$qt_buttons, true) )
       
   463 			wp_enqueue_script('wp-fullscreen');
       
   464 
       
   465 		if ( self::$has_medialib ) {
       
   466 			add_thickbox();
       
   467 			wp_enqueue_script('media-upload');
       
   468 		}
       
   469 	}
       
   470 
       
   471 	public static function editor_js() {
       
   472 		global $tinymce_version, $concatenate_scripts, $compress_scripts;
       
   473 
       
   474 		/**
       
   475 		 * Filter "tiny_mce_version" is deprecated
       
   476 		 *
       
   477 		 * The tiny_mce_version filter is not needed since external plugins are loaded directly by TinyMCE.
       
   478 		 * These plugins can be refreshed by appending query string to the URL passed to "mce_external_plugins" filter.
       
   479 		 * 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).
       
   480 		 */
       
   481 		$version = 'ver=' . $tinymce_version;
       
   482 		$tmce_on = !empty(self::$mce_settings);
       
   483 
       
   484 		if ( ! isset($concatenate_scripts) )
       
   485 			script_concat_settings();
       
   486 
       
   487 		$compressed = $compress_scripts && $concatenate_scripts && isset($_SERVER['HTTP_ACCEPT_ENCODING'])
       
   488 			&& false !== stripos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip');
       
   489 
       
   490 		if ( $tmce_on && 'en' != self::$mce_locale )
       
   491 			include_once(ABSPATH . WPINC . '/js/tinymce/langs/wp-langs.php');
       
   492 
       
   493 		$mceInit = $qtInit = '';
       
   494 		if ( $tmce_on ) {
       
   495 			foreach ( self::$mce_settings as $editor_id => $init ) {
       
   496 				$options = self::_parse_init( $init );
       
   497 				$mceInit .= "'$editor_id':{$options},";
       
   498 			}
       
   499 			$mceInit = '{' . trim($mceInit, ',') . '}';
       
   500 		} else {
       
   501 			$mceInit = '{}';
       
   502 		}
       
   503 
       
   504 		if ( !empty(self::$qt_settings) ) {
       
   505 			foreach ( self::$qt_settings as $editor_id => $init ) {
       
   506 				$options = self::_parse_init( $init );
       
   507 				$qtInit .= "'$editor_id':{$options},";
       
   508 			}
       
   509 			$qtInit = '{' . trim($qtInit, ',') . '}';
       
   510 		} else {
       
   511 			$qtInit = '{}';
       
   512 		}
       
   513 
       
   514 		$ref = array(
       
   515 			'plugins' => implode( ',', self::$plugins ),
       
   516 			'theme' => 'advanced',
       
   517 			'language' => self::$mce_locale
       
   518 		);
       
   519 
       
   520 		$suffix = ( defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ) ? '_src' : '';
       
   521 
       
   522 		do_action('before_wp_tiny_mce', self::$mce_settings);
       
   523 ?>
       
   524 
       
   525 	<script type="text/javascript">
       
   526 		tinyMCEPreInit = {
       
   527 			base : "<?php echo self::$baseurl; ?>",
       
   528 			suffix : "<?php echo $suffix; ?>",
       
   529 			query : "<?php echo $version; ?>",
       
   530 			mceInit : <?php echo $mceInit; ?>,
       
   531 			qtInit : <?php echo $qtInit; ?>,
       
   532 			ref : <?php echo self::_parse_init( $ref ); ?>,
       
   533 			load_ext : function(url,lang){var sl=tinymce.ScriptLoader;sl.markDone(url+'/langs/'+lang+'.js');sl.markDone(url+'/langs/'+lang+'_dlg.js');}
       
   534 		};
       
   535 	</script>
       
   536 <?php
       
   537 
       
   538 		$baseurl = self::$baseurl;
       
   539 
       
   540 		if ( $tmce_on ) {
       
   541 			if ( $compressed )
       
   542 				echo "<script type='text/javascript' src='{$baseurl}/wp-tinymce.php?c=1&amp;$version'></script>\n";
       
   543 			else
       
   544 				echo "<script type='text/javascript' src='{$baseurl}/tiny_mce.js?$version'></script>\n";
       
   545 
       
   546 			if ( 'en' != self::$mce_locale && isset($lang) )
       
   547 				echo "<script type='text/javascript'>\n$lang\n</script>\n";
       
   548 			else
       
   549 				echo "<script type='text/javascript' src='{$baseurl}/langs/wp-langs-en.js?$version'></script>\n";
       
   550 		}
       
   551 ?>
       
   552 
       
   553 	<script type="text/javascript">
       
   554 		(function(){
       
   555 			var init, ed, qt, first_init, mce = <?php echo wp_default_editor() == 'tinymce' ? 'true' : 'false'; ?>;
       
   556 
       
   557 			if ( typeof(tinymce) == 'object' ) {
       
   558 				// mark wp_theme/ui.css as loaded
       
   559 				tinymce.DOM.files[tinymce.baseURI.getURI() + '/themes/advanced/skins/wp_theme/ui.css'] = true;
       
   560 
       
   561 				for ( ed in tinyMCEPreInit.mceInit ) {
       
   562 					if ( first_init ) {
       
   563 						init = tinyMCEPreInit.mceInit[ed] = tinymce.extend( {}, first_init, tinyMCEPreInit.mceInit[ed] );
       
   564 					} else {
       
   565 						init = first_init = tinyMCEPreInit.mceInit[ed];
       
   566 					}
       
   567 
       
   568 					if ( mce )
       
   569 						try { tinymce.init(init); } catch(e){}
       
   570 				}
       
   571 			}
       
   572 
       
   573 			if ( typeof(QTags) == 'function' ) {
       
   574 				for ( qt in tinyMCEPreInit.qtInit ) {
       
   575 					try { quicktags( tinyMCEPreInit.qtInit[qt] ); } catch(e){}
       
   576 				}
       
   577 			}
       
   578 		})();
       
   579 
       
   580 		var wpActiveEditor;
       
   581 
       
   582 		jQuery('.wp-editor-wrap').mousedown(function(e){
       
   583 			wpActiveEditor = this.id.slice(3, -5);
       
   584 		});
       
   585 
       
   586 <?php
       
   587 
       
   588 		if ( self::$ext_plugins )
       
   589 			echo self::$ext_plugins . "\n";
       
   590 
       
   591 		if ( ! $compressed && $tmce_on ) {
       
   592 ?>
       
   593 		(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');}});})();
       
   594 <?php
       
   595 		}
       
   596 
       
   597 		if ( !is_admin() )
       
   598 			echo 'var ajaxurl = "' . admin_url( 'admin-ajax.php', 'relative' ) . '";';
       
   599 ?>
       
   600 	</script>
       
   601 <?php
       
   602 
       
   603 		if ( in_array('wplink', self::$plugins, true) || in_array('link', self::$qt_buttons, true) )
       
   604 			self::wp_link_dialog();
       
   605 
       
   606 		if ( in_array('wpfullscreen', self::$plugins, true) || in_array('fullscreen', self::$qt_buttons, true) )
       
   607 			self::wp_fullscreen_html();
       
   608 
       
   609 		do_action('after_wp_tiny_mce', self::$mce_settings);
       
   610 	}
       
   611 
       
   612 	public static function wp_fullscreen_html() {
       
   613 		global $content_width, $post;
       
   614 
       
   615 		$width = isset($content_width) && 800 > $content_width ? $content_width : 800;
       
   616 		$width = $width + 22; // compensate for the padding and border
       
   617 		$dfw_width = get_user_setting( 'dfw_width', $width );
       
   618 		$save = isset($post->post_status) && $post->post_status == 'publish' ? __('Update') : __('Save');
       
   619 	?>
       
   620 	<div id="wp-fullscreen-body"<?php if ( is_rtl() ) echo ' class="rtl"'; ?>>
       
   621 	<div id="fullscreen-topbar">
       
   622 		<div id="wp-fullscreen-toolbar">
       
   623 			<div id="wp-fullscreen-close"><a href="#" onclick="fullscreen.off();return false;"><?php _e('Exit fullscreen'); ?></a></div>
       
   624 			<div id="wp-fullscreen-central-toolbar" style="width:<?php echo $width; ?>px;">
       
   625 
       
   626 			<div id="wp-fullscreen-mode-bar"><div id="wp-fullscreen-modes">
       
   627 				<a href="#" onclick="fullscreen.switchmode('tinymce');return false;"><?php _e('Visual'); ?></a>
       
   628 				<a href="#" onclick="fullscreen.switchmode('html');return false;"><?php _e('HTML'); ?></a>
       
   629 			</div></div>
       
   630 
       
   631 			<div id="wp-fullscreen-button-bar"><div id="wp-fullscreen-buttons" class="wp_themeSkin">
       
   632 	<?php
       
   633 
       
   634 		$buttons = array(
       
   635 			// format: title, onclick, show in both editors
       
   636 			'bold' => array( 'title' => __('Bold (Ctrl + B)'), 'onclick' => 'fullscreen.b();', 'both' => false ),
       
   637 			'italic' => array( 'title' => __('Italic (Ctrl + I)'), 'onclick' => 'fullscreen.i();', 'both' => false ),
       
   638 			'0' => 'separator',
       
   639 			'bullist' => array( 'title' => __('Unordered list (Alt + Shift + U)'), 'onclick' => 'fullscreen.ul();', 'both' => false ),
       
   640 			'numlist' => array( 'title' => __('Ordered list (Alt + Shift + O)'), 'onclick' => 'fullscreen.ol();', 'both' => false ),
       
   641 			'1' => 'separator',
       
   642 			'blockquote' => array( 'title' => __('Blockquote (Alt + Shift + Q)'), 'onclick' => 'fullscreen.blockquote();', 'both' => false ),
       
   643 			'image' => array( 'title' => __('Insert/edit image (Alt + Shift + M)'), 'onclick' => "fullscreen.medialib();", 'both' => true ),
       
   644 			'2' => 'separator',
       
   645 			'link' => array( 'title' => __('Insert/edit link (Alt + Shift + A)'), 'onclick' => 'fullscreen.link();', 'both' => true ),
       
   646 			'unlink' => array( 'title' => __('Unlink (Alt + Shift + S)'), 'onclick' => 'fullscreen.unlink();', 'both' => false ),
       
   647 			'3' => 'separator',
       
   648 			'help' => array( 'title' => __('Help (Alt + Shift + H)'), 'onclick' => 'fullscreen.help();', 'both' => false )
       
   649 		);
       
   650 
       
   651 		$buttons = apply_filters( 'wp_fullscreen_buttons', $buttons );
       
   652 
       
   653 		foreach ( $buttons as $button => $args ) {
       
   654 			if ( 'separator' == $args ) { ?>
       
   655 				<div><span aria-orientation="vertical" role="separator" class="mceSeparator"></span></div>
       
   656 	<?php		continue;
       
   657 			} ?>
       
   658 
       
   659 			<div<?php if ( $args['both'] ) { ?> class="wp-fullscreen-both"<?php } ?>>
       
   660 			<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">
       
   661 			<span class="mceIcon mce_<?php echo $button; ?>"></span>
       
   662 			</a>
       
   663 			</div>
       
   664 	<?php
       
   665 		} ?>
       
   666 
       
   667 			</div></div>
       
   668 
       
   669 			<div id="wp-fullscreen-save">
       
   670 				<span><?php if ( $post->post_status == 'publish' ) _e('Updated.'); else _e('Saved.'); ?></span>
       
   671 				<img src="<?php echo admin_url('images/wpspin_light.gif'); ?>" alt="" />
       
   672 				<input type="button" class="button-primary" value="<?php echo $save; ?>" onclick="fullscreen.save();" />
       
   673 			</div>
       
   674 
       
   675 			</div>
       
   676 		</div>
       
   677 	</div>
       
   678 
       
   679 	<div id="wp-fullscreen-wrap" style="width:<?php echo $dfw_width; ?>px;">
       
   680 		<?php if ( post_type_supports($post->post_type, 'title') ) { ?>
       
   681 		<label id="wp-fullscreen-title-prompt-text" for="wp-fullscreen-title"><?php echo apply_filters( 'enter_title_here', __( 'Enter title here' ), $post ); ?></label>
       
   682 		<input type="text" id="wp-fullscreen-title" value="" autocomplete="off" />
       
   683 		<?php } ?>
       
   684 
       
   685 		<div id="wp-fullscreen-container">
       
   686 			<textarea id="wp_mce_fullscreen"></textarea>
       
   687 		</div>
       
   688 
       
   689 		<div id="wp-fullscreen-status">
       
   690 			<div id="wp-fullscreen-count"><?php printf( __( 'Word count: %s' ), '<span class="word-count">0</span>' ); ?></div>
       
   691 			<div id="wp-fullscreen-tagline"><?php _e('Just write.'); ?></div>
       
   692 		</div>
       
   693 	</div>
       
   694 	</div>
       
   695 
       
   696 	<div class="fullscreen-overlay" id="fullscreen-overlay"></div>
       
   697 	<div class="fullscreen-overlay fullscreen-fader fade-600" id="fullscreen-fader"></div>
       
   698 	<?php
       
   699 	}
       
   700 
       
   701 	/**
       
   702 	 * Performs post queries for internal linking.
       
   703 	 *
       
   704 	 * @since 3.1.0
       
   705 	 *
       
   706 	 * @param array $args Optional. Accepts 'pagenum' and 's' (search) arguments.
       
   707 	 * @return array Results.
       
   708 	 */
       
   709 	public static function wp_link_query( $args = array() ) {
       
   710 		$pts = get_post_types( array( 'public' => true ), 'objects' );
       
   711 		$pt_names = array_keys( $pts );
       
   712 
       
   713 		$query = array(
       
   714 			'post_type' => $pt_names,
       
   715 			'suppress_filters' => true,
       
   716 			'update_post_term_cache' => false,
       
   717 			'update_post_meta_cache' => false,
       
   718 			'post_status' => 'publish',
       
   719 			'order' => 'DESC',
       
   720 			'orderby' => 'post_date',
       
   721 			'posts_per_page' => 20,
       
   722 		);
       
   723 
       
   724 		$args['pagenum'] = isset( $args['pagenum'] ) ? absint( $args['pagenum'] ) : 1;
       
   725 
       
   726 		if ( isset( $args['s'] ) )
       
   727 			$query['s'] = $args['s'];
       
   728 
       
   729 		$query['offset'] = $args['pagenum'] > 1 ? $query['posts_per_page'] * ( $args['pagenum'] - 1 ) : 0;
       
   730 
       
   731 		// Do main query.
       
   732 		$get_posts = new WP_Query;
       
   733 		$posts = $get_posts->query( $query );
       
   734 		// Check if any posts were found.
       
   735 		if ( ! $get_posts->post_count )
       
   736 			return false;
       
   737 
       
   738 		// Build results.
       
   739 		$results = array();
       
   740 		foreach ( $posts as $post ) {
       
   741 			if ( 'post' == $post->post_type )
       
   742 				$info = mysql2date( __( 'Y/m/d' ), $post->post_date );
       
   743 			else
       
   744 				$info = $pts[ $post->post_type ]->labels->singular_name;
       
   745 
       
   746 			$results[] = array(
       
   747 				'ID' => $post->ID,
       
   748 				'title' => trim( esc_html( strip_tags( get_the_title( $post ) ) ) ),
       
   749 				'permalink' => get_permalink( $post->ID ),
       
   750 				'info' => $info,
       
   751 			);
       
   752 		}
       
   753 
       
   754 		return $results;
       
   755 	}
       
   756 
       
   757 	/**
       
   758 	 * Dialog for internal linking.
       
   759 	 *
       
   760 	 * @since 3.1.0
       
   761 	 */
       
   762 	public static function wp_link_dialog() {
       
   763 	?>
       
   764 	<div style="display:none;">
       
   765 	<form id="wp-link" tabindex="-1">
       
   766 	<?php wp_nonce_field( 'internal-linking', '_ajax_linking_nonce', false ); ?>
       
   767 	<div id="link-selector">
       
   768 		<div id="link-options">
       
   769 			<p class="howto"><?php _e( 'Enter the destination URL' ); ?></p>
       
   770 			<div>
       
   771 				<label><span><?php _e( 'URL' ); ?></span><input id="url-field" type="text" tabindex="10" name="href" /></label>
       
   772 			</div>
       
   773 			<div>
       
   774 				<label><span><?php _e( 'Title' ); ?></span><input id="link-title-field" type="text" tabindex="20" name="linktitle" /></label>
       
   775 			</div>
       
   776 			<div class="link-target">
       
   777 				<label><input type="checkbox" id="link-target-checkbox" tabindex="30" /> <?php _e( 'Open link in a new window/tab' ); ?></label>
       
   778 			</div>
       
   779 		</div>
       
   780 		<?php $show_internal = '1' == get_user_setting( 'wplink', '0' ); ?>
       
   781 		<p class="howto toggle-arrow <?php if ( $show_internal ) echo 'toggle-arrow-active'; ?>" id="internal-toggle"><?php _e( 'Or link to existing content' ); ?></p>
       
   782 		<div id="search-panel"<?php if ( ! $show_internal ) echo ' style="display:none"'; ?>>
       
   783 			<div class="link-search-wrapper">
       
   784 				<label>
       
   785 					<span><?php _e( 'Search' ); ?></span>
       
   786 					<input type="text" id="search-field" class="link-search-field" tabindex="60" autocomplete="off" />
       
   787 					<img class="waiting" src="<?php echo esc_url( admin_url( 'images/wpspin_light.gif' ) ); ?>" alt="" />
       
   788 				</label>
       
   789 			</div>
       
   790 			<div id="search-results" class="query-results">
       
   791 				<ul></ul>
       
   792 				<div class="river-waiting">
       
   793 					<img class="waiting" src="<?php echo esc_url( admin_url( 'images/wpspin_light.gif' ) ); ?>" alt="" />
       
   794 				</div>
       
   795 			</div>
       
   796 			<div id="most-recent-results" class="query-results">
       
   797 				<div class="query-notice"><em><?php _e( 'No search term specified. Showing recent items.' ); ?></em></div>
       
   798 				<ul></ul>
       
   799 				<div class="river-waiting">
       
   800 					<img class="waiting" src="<?php echo esc_url( admin_url( 'images/wpspin_light.gif' ) ); ?>" alt="" />
       
   801 				</div>
       
   802 			</div>
       
   803 		</div>
       
   804 	</div>
       
   805 	<div class="submitbox">
       
   806 		<div id="wp-link-cancel">
       
   807 			<a class="submitdelete deletion" href="#"><?php _e( 'Cancel' ); ?></a>
       
   808 		</div>
       
   809 		<div id="wp-link-update">
       
   810 			<input type="submit" tabindex="100" value="<?php esc_attr_e( 'Add Link' ); ?>" class="button-primary" id="wp-link-submit" name="wp-link-submit">
       
   811 		</div>
       
   812 	</div>
       
   813 	</form>
       
   814 	</div>
       
   815 	<?php
       
   816 	}
       
   817 }