WordPress PHP Cross Reference Blogging Systems

Source: /wp-includes/class-wp-editor.php - 1481 lines - 52878 bytes - Summary - Text - Print

Description: Facilitates adding of the WordPress editor as used on the Write and Edit screens.

   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      private static $drag_drop_upload = false;
  28      private static $old_dfw_compat = false;
  29  
  30  	private function __construct() {}
  31  
  32      /**
  33       * Parse default arguments for the editor instance.
  34       *
  35       * @static
  36       * @param string $editor_id ID for the current editor instance.
  37       * @param array  $settings {
  38       *     Array of editor arguments.
  39       *
  40       *     @type bool       $wpautop           Whether to use wpautop(). Default true.
  41       *     @type bool       $media_buttons     Whether to show the Add Media/other media buttons.
  42       *     @type string     $default_editor    When both TinyMCE and Quicktags are used, set which
  43       *                                         editor is shown on page load. Default empty.
  44       *     @type bool       $drag_drop_upload  Whether to enable drag & drop on the editor uploading. Default false.
  45       *                                         Requires the media modal.
  46       *     @type string     $textarea_name     Give the textarea a unique name here. Square brackets
  47       *                                         can be used here. Default $editor_id.
  48       *     @type int        $textarea_rows     Number rows in the editor textarea. Default 20.
  49       *     @type string|int $tabindex          Tabindex value to use. Default empty.
  50       *     @type string     $tabfocus_elements The previous and next element ID to move the focus to
  51       *                                         when pressing the Tab key in TinyMCE. Default ':prev,:next'.
  52       *     @type string     $editor_css        Intended for extra styles for both Visual and Text editors.
  53       *                                         Should include `<style>` tags, and can use "scoped". Default empty.
  54       *     @type string     $editor_class      Extra classes to add to the editor textarea element. Default empty.
  55       *     @type bool       $teeny             Whether to output the minimal editor config. Examples include
  56       *                                         Press This and the Comment editor. Default false.
  57       *     @type bool       $dfw               Deprecated in 4.1. Since 4.3 used only to enqueue wp-fullscreen-stub.js
  58       *                                         for backward compatibility.
  59       *     @type bool|array $tinymce           Whether to load TinyMCE. Can be used to pass settings directly to
  60       *                                         TinyMCE using an array. Default true.
  61       *     @type bool|array $quicktags         Whether to load Quicktags. Can be used to pass settings directly to
  62       *                                         Quicktags using an array. Default true.
  63       * }
  64       * @return array Parsed arguments array.
  65       */
  66  	public static function parse_settings( $editor_id, $settings ) {
  67  
  68          /**
  69           * Filters the wp_editor() settings.
  70           *
  71           * @since 4.0.0
  72           *
  73           * @see _WP_Editors()::parse_settings()
  74           *
  75           * @param array  $settings  Array of editor arguments.
  76           * @param string $editor_id ID for the current editor instance.
  77           */
  78          $settings = apply_filters( 'wp_editor_settings', $settings, $editor_id );
  79  
  80          $set = wp_parse_args( $settings, array(
  81              'wpautop'             => true,
  82              'media_buttons'       => true,
  83              'default_editor'      => '',
  84              'drag_drop_upload'    => false,
  85              'textarea_name'       => $editor_id,
  86              'textarea_rows'       => 20,
  87              'tabindex'            => '',
  88              'tabfocus_elements'   => ':prev,:next',
  89              'editor_css'          => '',
  90              'editor_class'        => '',
  91              'teeny'               => false,
  92              'dfw'                 => false,
  93              '_content_editor_dfw' => false,
  94              'tinymce'             => true,
  95              'quicktags'           => true
  96          ) );
  97  
  98          self::$this_tinymce = ( $set['tinymce'] && user_can_richedit() );
  99  
 100          if ( self::$this_tinymce ) {
 101              if ( false !== strpos( $editor_id, '[' ) ) {
 102                  self::$this_tinymce = false;
 103                  _deprecated_argument( 'wp_editor()', '3.9.0', 'TinyMCE editor IDs cannot have brackets.' );
 104              }
 105          }
 106  
 107          self::$this_quicktags = (bool) $set['quicktags'];
 108  
 109          if ( self::$this_tinymce )
 110              self::$has_tinymce = true;
 111  
 112          if ( self::$this_quicktags )
 113              self::$has_quicktags = true;
 114  
 115          if ( $set['dfw'] ) {
 116              self::$old_dfw_compat = true;
 117          }
 118  
 119          if ( empty( $set['editor_height'] ) )
 120              return $set;
 121  
 122          if ( 'content' === $editor_id && empty( $set['tinymce']['wp_autoresize_on'] ) ) {
 123              // A cookie (set when a user resizes the editor) overrides the height.
 124              $cookie = (int) get_user_setting( 'ed_size' );
 125  
 126              if ( $cookie )
 127                  $set['editor_height'] = $cookie;
 128          }
 129  
 130          if ( $set['editor_height'] < 50 )
 131              $set['editor_height'] = 50;
 132          elseif ( $set['editor_height'] > 5000 )
 133              $set['editor_height'] = 5000;
 134  
 135          return $set;
 136      }
 137  
 138      /**
 139       * Outputs the HTML for a single instance of the editor.
 140       *
 141       * @static
 142       * @param string $content The initial content of the editor.
 143       * @param string $editor_id ID for the textarea and TinyMCE and Quicktags instances (can contain only ASCII letters and numbers).
 144       * @param array $settings See the _parse_settings() method for description.
 145       */
 146  	public static function editor( $content, $editor_id, $settings = array() ) {
 147          $set = self::parse_settings( $editor_id, $settings );
 148          $editor_class = ' class="' . trim( esc_attr( $set['editor_class'] ) . ' wp-editor-area' ) . '"';
 149          $tabindex = $set['tabindex'] ? ' tabindex="' . (int) $set['tabindex'] . '"' : '';
 150          $default_editor = 'html';
 151          $buttons = $autocomplete = '';
 152          $editor_id_attr = esc_attr( $editor_id );
 153  
 154          if ( $set['drag_drop_upload'] ) {
 155              self::$drag_drop_upload = true;
 156          }
 157  
 158          if ( ! empty( $set['editor_height'] ) ) {
 159              $height = ' style="height: ' . (int) $set['editor_height'] . 'px"';
 160          } else {
 161              $height = ' rows="' . (int) $set['textarea_rows'] . '"';
 162          }
 163  
 164          if ( ! current_user_can( 'upload_files' ) ) {
 165              $set['media_buttons'] = false;
 166          }
 167  
 168          if ( self::$this_tinymce ) {
 169              $autocomplete = ' autocomplete="off"';
 170  
 171              if ( self::$this_quicktags ) {
 172                  $default_editor = $set['default_editor'] ? $set['default_editor'] : wp_default_editor();
 173                  // 'html' is used for the "Text" editor tab.
 174                  if ( 'html' !== $default_editor ) {
 175                      $default_editor = 'tinymce';
 176                  }
 177  
 178                  $buttons .= '<button type="button" id="' . $editor_id_attr . '-tmce" class="wp-switch-editor switch-tmce"' .
 179                      ' data-wp-editor-id="' . $editor_id_attr . '">' . __('Visual') . "</button>\n";
 180                  $buttons .= '<button type="button" id="' . $editor_id_attr . '-html" class="wp-switch-editor switch-html"' .
 181                      ' data-wp-editor-id="' . $editor_id_attr . '">' . _x( 'Text', 'Name for the Text editor tab (formerly HTML)' ) . "</button>\n";
 182              } else {
 183                  $default_editor = 'tinymce';
 184              }
 185          }
 186  
 187          $switch_class = 'html' === $default_editor ? 'html-active' : 'tmce-active';
 188          $wrap_class = 'wp-core-ui wp-editor-wrap ' . $switch_class;
 189  
 190          if ( $set['_content_editor_dfw'] ) {
 191              $wrap_class .= ' has-dfw';
 192          }
 193  
 194          echo '<div id="wp-' . $editor_id_attr . '-wrap" class="' . $wrap_class . '">';
 195  
 196          if ( self::$editor_buttons_css ) {
 197              wp_print_styles( 'editor-buttons' );
 198              self::$editor_buttons_css = false;
 199          }
 200  
 201          if ( ! empty( $set['editor_css'] ) ) {
 202              echo $set['editor_css'] . "\n";
 203          }
 204  
 205          if ( ! empty( $buttons ) || $set['media_buttons'] ) {
 206              echo '<div id="wp-' . $editor_id_attr . '-editor-tools" class="wp-editor-tools hide-if-no-js">';
 207  
 208              if ( $set['media_buttons'] ) {
 209                  self::$has_medialib = true;
 210  
 211                  if ( ! function_exists( 'media_buttons' ) )
 212                      include ( ABSPATH . 'wp-admin/includes/media.php' );
 213  
 214                  echo '<div id="wp-' . $editor_id_attr . '-media-buttons" class="wp-media-buttons">';
 215  
 216                  /**
 217                   * Fires after the default media button(s) are displayed.
 218                   *
 219                   * @since 2.5.0
 220                   *
 221                   * @param string $editor_id Unique editor identifier, e.g. 'content'.
 222                   */
 223                  do_action( 'media_buttons', $editor_id );
 224                  echo "</div>\n";
 225              }
 226  
 227              echo '<div class="wp-editor-tabs">' . $buttons . "</div>\n";
 228              echo "</div>\n";
 229          }
 230  
 231          $quicktags_toolbar = '';
 232  
 233          if ( self::$this_quicktags ) {
 234              if ( 'content' === $editor_id && ! empty( $GLOBALS['current_screen'] ) && $GLOBALS['current_screen']->base === 'post' ) {
 235                  $toolbar_id = 'ed_toolbar';
 236              } else {
 237                  $toolbar_id = 'qt_' . $editor_id_attr . '_toolbar';
 238              }
 239  
 240              $quicktags_toolbar = '<div id="' . $toolbar_id . '" class="quicktags-toolbar"></div>';
 241          }
 242  
 243          /**
 244           * Filters the HTML markup output that displays the editor.
 245           *
 246           * @since 2.1.0
 247           *
 248           * @param string $output Editor's HTML markup.
 249           */
 250          $the_editor = apply_filters( 'the_editor', '<div id="wp-' . $editor_id_attr . '-editor-container" class="wp-editor-container">' .
 251              $quicktags_toolbar .
 252              '<textarea' . $editor_class . $height . $tabindex . $autocomplete . ' cols="40" name="' . esc_attr( $set['textarea_name'] ) . '" ' .
 253              'id="' . $editor_id_attr . '">%s</textarea></div>' );
 254  
 255          // Prepare the content for the Visual or Text editor, only when TinyMCE is used (back-compat).
 256          if ( self::$this_tinymce ) {
 257              add_filter( 'the_editor_content', 'format_for_editor', 10, 2 );
 258          }
 259  
 260          /**
 261           * Filters the default editor content.
 262           *
 263           * @since 2.1.0
 264           *
 265           * @param string $content        Default editor content.
 266           * @param string $default_editor The default editor for the current user.
 267           *                               Either 'html' or 'tinymce'.
 268           */
 269          $content = apply_filters( 'the_editor_content', $content, $default_editor );
 270  
 271          // Remove the filter as the next editor on the same page may not need it.
 272          if ( self::$this_tinymce ) {
 273              remove_filter( 'the_editor_content', 'format_for_editor' );
 274          }
 275  
 276          // Back-compat for the `htmledit_pre` and `richedit_pre` filters
 277          if ( 'html' === $default_editor && has_filter( 'htmledit_pre' ) ) {
 278              // TODO: needs _deprecated_filter(), use _deprecated_function() as substitute for now
 279              _deprecated_function( 'add_filter( htmledit_pre )', '4.3.0', 'add_filter( format_for_editor )' );
 280              $content = apply_filters( 'htmledit_pre', $content );
 281          } elseif ( 'tinymce' === $default_editor && has_filter( 'richedit_pre' ) ) {
 282              _deprecated_function( 'add_filter( richedit_pre )', '4.3.0', 'add_filter( format_for_editor )' );
 283              $content = apply_filters( 'richedit_pre', $content );
 284          }
 285  
 286          if ( false !== stripos( $content, 'textarea' ) ) {
 287              $content = preg_replace( '%</textarea%i', '&lt;/textarea', $content );
 288          }
 289  
 290          printf( $the_editor, $content );
 291          echo "\n</div>\n\n";
 292  
 293          self::editor_settings( $editor_id, $set );
 294      }
 295  
 296      /**
 297       * @static
 298       *
 299       * @global string $wp_version
 300       * @global string $tinymce_version
 301       *
 302       * @param string $editor_id
 303       * @param array  $set
 304       */
 305  	public static function editor_settings($editor_id, $set) {
 306          global $wp_version, $tinymce_version;
 307  
 308          if ( empty(self::$first_init) ) {
 309              if ( is_admin() ) {
 310                  add_action( 'admin_print_footer_scripts', array( __CLASS__, 'editor_js' ), 50 );
 311                  add_action( 'admin_print_footer_scripts', array( __CLASS__, 'enqueue_scripts' ), 1 );
 312              } else {
 313                  add_action( 'wp_print_footer_scripts', array( __CLASS__, 'editor_js' ), 50 );
 314                  add_action( 'wp_print_footer_scripts', array( __CLASS__, 'enqueue_scripts' ), 1 );
 315              }
 316          }
 317  
 318          if ( self::$this_quicktags ) {
 319  
 320              $qtInit = array(
 321                  'id' => $editor_id,
 322                  'buttons' => ''
 323              );
 324  
 325              if ( is_array($set['quicktags']) )
 326                  $qtInit = array_merge($qtInit, $set['quicktags']);
 327  
 328              if ( empty($qtInit['buttons']) )
 329                  $qtInit['buttons'] = 'strong,em,link,block,del,ins,img,ul,ol,li,code,more,close';
 330  
 331              if ( $set['_content_editor_dfw'] ) {
 332                  $qtInit['buttons'] .= ',dfw';
 333              }
 334  
 335              /**
 336               * Filters the Quicktags settings.
 337               *
 338               * @since 3.3.0
 339               *
 340               * @param array  $qtInit    Quicktags settings.
 341               * @param string $editor_id The unique editor ID, e.g. 'content'.
 342               */
 343              $qtInit = apply_filters( 'quicktags_settings', $qtInit, $editor_id );
 344  
 345              self::$qt_settings[$editor_id] = $qtInit;
 346  
 347              self::$qt_buttons = array_merge( self::$qt_buttons, explode(',', $qtInit['buttons']) );
 348          }
 349  
 350          if ( self::$this_tinymce ) {
 351  
 352              if ( empty( self::$first_init ) ) {
 353                  self::$baseurl = includes_url( 'js/tinymce' );
 354  
 355                  $mce_locale = get_locale();
 356                  self::$mce_locale = $mce_locale = empty( $mce_locale ) ? 'en' : strtolower( substr( $mce_locale, 0, 2 ) ); // ISO 639-1
 357  
 358                  /** This filter is documented in wp-admin/includes/media.php */
 359                  $no_captions = (bool) apply_filters( 'disable_captions', '' );
 360                  $ext_plugins = '';
 361  
 362                  if ( $set['teeny'] ) {
 363  
 364                      /**
 365                       * Filters the list of teenyMCE plugins.
 366                       *
 367                       * @since 2.7.0
 368                       *
 369                       * @param array  $plugins   An array of teenyMCE plugins.
 370                       * @param string $editor_id Unique editor identifier, e.g. 'content'.
 371                       */
 372                      self::$plugins = $plugins = apply_filters( 'teeny_mce_plugins', array( 'colorpicker', 'lists', 'fullscreen', 'image', 'wordpress', 'wpeditimage', 'wplink' ), $editor_id );
 373                  } else {
 374  
 375                      /**
 376                       * Filters the list of TinyMCE external plugins.
 377                       *
 378                       * The filter takes an associative array of external plugins for
 379                       * TinyMCE in the form 'plugin_name' => 'url'.
 380                       *
 381                       * The url should be absolute, and should include the js filename
 382                       * to be loaded. For example:
 383                       * 'myplugin' => 'http://mysite.com/wp-content/plugins/myfolder/mce_plugin.js'.
 384                       *
 385                       * If the external plugin adds a button, it should be added with
 386                       * one of the 'mce_buttons' filters.
 387                       *
 388                       * @since 2.5.0
 389                       *
 390                       * @param array $external_plugins An array of external TinyMCE plugins.
 391                       */
 392                      $mce_external_plugins = apply_filters( 'mce_external_plugins', array() );
 393  
 394                      $plugins = array(
 395                          'charmap',
 396                          'colorpicker',
 397                          'hr',
 398                          'lists',
 399                          'media',
 400                          'paste',
 401                          'tabfocus',
 402                          'textcolor',
 403                          'fullscreen',
 404                          'wordpress',
 405                          'wpautoresize',
 406                          'wpeditimage',
 407                          'wpemoji',
 408                          'wpgallery',
 409                          'wplink',
 410                          'wpdialogs',
 411                          'wptextpattern',
 412                          'wpview',
 413                          'wpembed',
 414                      );
 415  
 416                      if ( ! self::$has_medialib ) {
 417                          $plugins[] = 'image';
 418                      }
 419  
 420                      /**
 421                       * Filters the list of default TinyMCE plugins.
 422                       *
 423                       * The filter specifies which of the default plugins included
 424                       * in WordPress should be added to the TinyMCE instance.
 425                       *
 426                       * @since 3.3.0
 427                       *
 428                       * @param array $plugins An array of default TinyMCE plugins.
 429                       */
 430                      $plugins = array_unique( apply_filters( 'tiny_mce_plugins', $plugins ) );
 431  
 432                      if ( ( $key = array_search( 'spellchecker', $plugins ) ) !== false ) {
 433                          // Remove 'spellchecker' from the internal plugins if added with 'tiny_mce_plugins' filter to prevent errors.
 434                          // It can be added with 'mce_external_plugins'.
 435                          unset( $plugins[$key] );
 436                      }
 437  
 438                      if ( ! empty( $mce_external_plugins ) ) {
 439  
 440                          /**
 441                           * Filters the translations loaded for external TinyMCE 3.x plugins.
 442                           *
 443                           * The filter takes an associative array ('plugin_name' => 'path')
 444                           * where 'path' is the include path to the file.
 445                           *
 446                           * The language file should follow the same format as wp_mce_translation(),
 447                           * and should define a variable ($strings) that holds all translated strings.
 448                           *
 449                           * @since 2.5.0
 450                           *
 451                           * @param array $translations Translations for external TinyMCE plugins.
 452                           */
 453                          $mce_external_languages = apply_filters( 'mce_external_languages', array() );
 454  
 455                          $loaded_langs = array();
 456                          $strings = '';
 457  
 458                          if ( ! empty( $mce_external_languages ) ) {
 459                              foreach ( $mce_external_languages as $name => $path ) {
 460                                  if ( @is_file( $path ) && @is_readable( $path ) ) {
 461                                      include_once( $path );
 462                                      $ext_plugins .= $strings . "\n";
 463                                      $loaded_langs[] = $name;
 464                                  }
 465                              }
 466                          }
 467  
 468                          foreach ( $mce_external_plugins as $name => $url ) {
 469                              if ( in_array( $name, $plugins, true ) ) {
 470                                  unset( $mce_external_plugins[ $name ] );
 471                                  continue;
 472                              }
 473  
 474                              $url = set_url_scheme( $url );
 475                              $mce_external_plugins[ $name ] = $url;
 476                              $plugurl = dirname( $url );
 477                              $strings = '';
 478  
 479                              // Try to load langs/[locale].js and langs/[locale]_dlg.js
 480                              if ( ! in_array( $name, $loaded_langs, true ) ) {
 481                                  $path = str_replace( content_url(), '', $plugurl );
 482                                  $path = WP_CONTENT_DIR . $path . '/langs/';
 483  
 484                                  if ( function_exists('realpath') )
 485                                      $path = trailingslashit( realpath($path) );
 486  
 487                                  if ( @is_file( $path . $mce_locale . '.js' ) )
 488                                      $strings .= @file_get_contents( $path . $mce_locale . '.js' ) . "\n";
 489  
 490                                  if ( @is_file( $path . $mce_locale . '_dlg.js' ) )
 491                                      $strings .= @file_get_contents( $path . $mce_locale . '_dlg.js' ) . "\n";
 492  
 493                                  if ( 'en' != $mce_locale && empty( $strings ) ) {
 494                                      if ( @is_file( $path . 'en.js' ) ) {
 495                                          $str1 = @file_get_contents( $path . 'en.js' );
 496                                          $strings .= preg_replace( '/([\'"])en\./', '$1' . $mce_locale . '.', $str1, 1 ) . "\n";
 497                                      }
 498  
 499                                      if ( @is_file( $path . 'en_dlg.js' ) ) {
 500                                          $str2 = @file_get_contents( $path . 'en_dlg.js' );
 501                                          $strings .= preg_replace( '/([\'"])en\./', '$1' . $mce_locale . '.', $str2, 1 ) . "\n";
 502                                      }
 503                                  }
 504  
 505                                  if ( ! empty( $strings ) )
 506                                      $ext_plugins .= "\n" . $strings . "\n";
 507                              }
 508  
 509                              $ext_plugins .= 'tinyMCEPreInit.load_ext("' . $plugurl . '", "' . $mce_locale . '");' . "\n";
 510                              $ext_plugins .= 'tinymce.PluginManager.load("' . $name . '", "' . $url . '");' . "\n";
 511                          }
 512                      }
 513                  }
 514  
 515                  self::$plugins = $plugins;
 516                  self::$ext_plugins = $ext_plugins;
 517  
 518                  self::$first_init = array(
 519                      'theme' => 'modern',
 520                      'skin' => 'lightgray',
 521                      'language' => self::$mce_locale,
 522                      'formats' => '{' .
 523                          'alignleft: [' .
 524                              '{selector: "p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li", styles: {textAlign:"left"}},' .
 525                              '{selector: "img,table,dl.wp-caption", classes: "alignleft"}' .
 526                          '],' .
 527                          'aligncenter: [' .
 528                              '{selector: "p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li", styles: {textAlign:"center"}},' .
 529                              '{selector: "img,table,dl.wp-caption", classes: "aligncenter"}' .
 530                          '],' .
 531                          'alignright: [' .
 532                              '{selector: "p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li", styles: {textAlign:"right"}},' .
 533                              '{selector: "img,table,dl.wp-caption", classes: "alignright"}' .
 534                          '],' .
 535                          'strikethrough: {inline: "del"}' .
 536                      '}',
 537                      'relative_urls' => false,
 538                      'remove_script_host' => false,
 539                      'convert_urls' => false,
 540                      'browser_spellcheck' => true,
 541                      'fix_list_elements' => true,
 542                      'entities' => '38,amp,60,lt,62,gt',
 543                      'entity_encoding' => 'raw',
 544                      'keep_styles' => false,
 545                      'cache_suffix' => 'wp-mce-' . $tinymce_version,
 546  
 547                      // Limit the preview styles in the menu/toolbar
 548                      'preview_styles' => 'font-family font-size font-weight font-style text-decoration text-transform',
 549  
 550                      'end_container_on_empty_block' => true,
 551                      'wpeditimage_disable_captions' => $no_captions,
 552                      'wpeditimage_html5_captions' => current_theme_supports( 'html5', 'caption' ),
 553                      'plugins' => implode( ',', $plugins ),
 554                      'wp_lang_attr' => get_bloginfo( 'language' )
 555                  );
 556  
 557                  if ( ! empty( $mce_external_plugins ) ) {
 558                      self::$first_init['external_plugins'] = wp_json_encode( $mce_external_plugins );
 559                  }
 560  
 561                  $suffix = SCRIPT_DEBUG ? '' : '.min';
 562                  $version = 'ver=' . $wp_version;
 563                  $dashicons = includes_url( "css/dashicons$suffix.css?$version" );
 564  
 565                  // WordPress default stylesheet and dashicons
 566                  $mce_css = array(
 567                      $dashicons,
 568                      self::$baseurl . '/skins/wordpress/wp-content.css?' . $version
 569                  );
 570  
 571                  $editor_styles = get_editor_stylesheets();
 572                  if ( ! empty( $editor_styles ) ) {
 573                      foreach ( $editor_styles as $style ) {
 574                          $mce_css[] = $style;
 575                      }
 576                  }
 577  
 578                  /**
 579                   * Filters the comma-delimited list of stylesheets to load in TinyMCE.
 580                   *
 581                   * @since 2.1.0
 582                   *
 583                   * @param string $stylesheets Comma-delimited list of stylesheets.
 584                   */
 585                  $mce_css = trim( apply_filters( 'mce_css', implode( ',', $mce_css ) ), ' ,' );
 586  
 587                  if ( ! empty($mce_css) )
 588                      self::$first_init['content_css'] = $mce_css;
 589              }
 590  
 591              if ( $set['teeny'] ) {
 592  
 593                  /**
 594                   * Filters the list of teenyMCE buttons (Text tab).
 595                   *
 596                   * @since 2.7.0
 597                   *
 598                   * @param array  $buttons   An array of teenyMCE buttons.
 599                   * @param string $editor_id Unique editor identifier, e.g. 'content'.
 600                   */
 601                  $mce_buttons = apply_filters( 'teeny_mce_buttons', array('bold', 'italic', 'underline', 'blockquote', 'strikethrough', 'bullist', 'numlist', 'alignleft', 'aligncenter', 'alignright', 'undo', 'redo', 'link', 'unlink', 'fullscreen'), $editor_id );
 602                  $mce_buttons_2 = $mce_buttons_3 = $mce_buttons_4 = array();
 603              } else {
 604                  $mce_buttons = array( 'bold', 'italic', 'strikethrough', 'bullist', 'numlist', 'blockquote', 'hr', 'alignleft', 'aligncenter', 'alignright', 'link', 'unlink', 'wp_more', 'spellchecker' );
 605  
 606                  if ( ! wp_is_mobile() ) {
 607                      if ( $set['_content_editor_dfw'] ) {
 608                          $mce_buttons[] = 'dfw';
 609                      } else {
 610                          $mce_buttons[] = 'fullscreen';
 611                      }
 612                  }
 613  
 614                  $mce_buttons[] = 'wp_adv';
 615  
 616                  /**
 617                   * Filters the first-row list of TinyMCE buttons (Visual tab).
 618                   *
 619                   * @since 2.0.0
 620                   *
 621                   * @param array  $buttons   First-row list of buttons.
 622                   * @param string $editor_id Unique editor identifier, e.g. 'content'.
 623                   */
 624                  $mce_buttons = apply_filters( 'mce_buttons', $mce_buttons, $editor_id );
 625  
 626                  $mce_buttons_2 = array( 'formatselect', 'underline', 'alignjustify', 'forecolor', 'pastetext', 'removeformat', 'charmap', 'outdent', 'indent', 'undo', 'redo' );
 627  
 628                  if ( ! wp_is_mobile() ) {
 629                      $mce_buttons_2[] = 'wp_help';
 630                  }
 631  
 632                  /**
 633                   * Filters the second-row list of TinyMCE buttons (Visual tab).
 634                   *
 635                   * @since 2.0.0
 636                   *
 637                   * @param array  $buttons   Second-row list of buttons.
 638                   * @param string $editor_id Unique editor identifier, e.g. 'content'.
 639                   */
 640                  $mce_buttons_2 = apply_filters( 'mce_buttons_2', $mce_buttons_2, $editor_id );
 641  
 642                  /**
 643                   * Filters the third-row list of TinyMCE buttons (Visual tab).
 644                   *
 645                   * @since 2.0.0
 646                   *
 647                   * @param array  $buttons   Third-row list of buttons.
 648                   * @param string $editor_id Unique editor identifier, e.g. 'content'.
 649                   */
 650                  $mce_buttons_3 = apply_filters( 'mce_buttons_3', array(), $editor_id );
 651  
 652                  /**
 653                   * Filters the fourth-row list of TinyMCE buttons (Visual tab).
 654                   *
 655                   * @since 2.5.0
 656                   *
 657                   * @param array  $buttons   Fourth-row list of buttons.
 658                   * @param string $editor_id Unique editor identifier, e.g. 'content'.
 659                   */
 660                  $mce_buttons_4 = apply_filters( 'mce_buttons_4', array(), $editor_id );
 661              }
 662  
 663              $body_class = $editor_id;
 664  
 665              if ( $post = get_post() ) {
 666                  $body_class .= ' post-type-' . sanitize_html_class( $post->post_type ) . ' post-status-' . sanitize_html_class( $post->post_status );
 667                  if ( post_type_supports( $post->post_type, 'post-formats' ) ) {
 668                      $post_format = get_post_format( $post );
 669                      if ( $post_format && ! is_wp_error( $post_format ) )
 670                          $body_class .= ' post-format-' . sanitize_html_class( $post_format );
 671                      else
 672                          $body_class .= ' post-format-standard';
 673                  }
 674              }
 675  
 676              $body_class .= ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_locale() ) ) );
 677  
 678              if ( !empty($set['tinymce']['body_class']) ) {
 679                  $body_class .= ' ' . $set['tinymce']['body_class'];
 680                  unset($set['tinymce']['body_class']);
 681              }
 682  
 683              $mceInit = array (
 684                  'selector' => "#$editor_id",
 685                  'resize' => 'vertical',
 686                  'menubar' => false,
 687                  'wpautop' => (bool) $set['wpautop'],
 688                  'indent' => ! $set['wpautop'],
 689                  'toolbar1' => implode($mce_buttons, ','),
 690                  'toolbar2' => implode($mce_buttons_2, ','),
 691                  'toolbar3' => implode($mce_buttons_3, ','),
 692                  'toolbar4' => implode($mce_buttons_4, ','),
 693                  'tabfocus_elements' => $set['tabfocus_elements'],
 694                  'body_class' => $body_class
 695              );
 696  
 697              // Merge with the first part of the init array
 698              $mceInit = array_merge( self::$first_init, $mceInit );
 699  
 700              if ( is_array( $set['tinymce'] ) )
 701                  $mceInit = array_merge( $mceInit, $set['tinymce'] );
 702  
 703              /*
 704               * For people who really REALLY know what they're doing with TinyMCE
 705               * You can modify $mceInit to add, remove, change elements of the config
 706               * before tinyMCE.init. Setting "valid_elements", "invalid_elements"
 707               * and "extended_valid_elements" can be done through this filter. Best
 708               * is to use the default cleanup by not specifying valid_elements,
 709               * as TinyMCE checks against the full set of HTML 5.0 elements and attributes.
 710               */
 711              if ( $set['teeny'] ) {
 712  
 713                  /**
 714                   * Filters the teenyMCE config before init.
 715                   *
 716                   * @since 2.7.0
 717                   *
 718                   * @param array  $mceInit   An array with teenyMCE config.
 719                   * @param string $editor_id Unique editor identifier, e.g. 'content'.
 720                   */
 721                  $mceInit = apply_filters( 'teeny_mce_before_init', $mceInit, $editor_id );
 722              } else {
 723  
 724                  /**
 725                   * Filters the TinyMCE config before init.
 726                   *
 727                   * @since 2.5.0
 728                   *
 729                   * @param array  $mceInit   An array with TinyMCE config.
 730                   * @param string $editor_id Unique editor identifier, e.g. 'content'.
 731                   */
 732                  $mceInit = apply_filters( 'tiny_mce_before_init', $mceInit, $editor_id );
 733              }
 734  
 735              if ( empty( $mceInit['toolbar3'] ) && ! empty( $mceInit['toolbar4'] ) ) {
 736                  $mceInit['toolbar3'] = $mceInit['toolbar4'];
 737                  $mceInit['toolbar4'] = '';
 738              }
 739  
 740              self::$mce_settings[$editor_id] = $mceInit;
 741          } // end if self::$this_tinymce
 742      }
 743  
 744      /**
 745       *
 746       * @static
 747       * @param array $init
 748       * @return string
 749       */
 750  	private static function _parse_init($init) {
 751          $options = '';
 752  
 753          foreach ( $init as $k => $v ) {
 754              if ( is_bool($v) ) {
 755                  $val = $v ? 'true' : 'false';
 756                  $options .= $k . ':' . $val . ',';
 757                  continue;
 758              } elseif ( !empty($v) && is_string($v) && ( ('{' == $v{0} && '}' == $v{strlen($v) - 1}) || ('[' == $v{0} && ']' == $v{strlen($v) - 1}) || preg_match('/^\(?function ?\(/', $v) ) ) {
 759                  $options .= $k . ':' . $v . ',';
 760                  continue;
 761              }
 762              $options .= $k . ':"' . $v . '",';
 763          }
 764  
 765          return '{' . trim( $options, ' ,' ) . '}';
 766      }
 767  
 768      /**
 769       *
 770       * @static
 771       */
 772  	public static function enqueue_scripts() {
 773          if ( self::$has_tinymce )
 774              wp_enqueue_script('editor');
 775  
 776          if ( self::$has_quicktags ) {
 777              wp_enqueue_script( 'quicktags' );
 778              wp_enqueue_style( 'buttons' );
 779          }
 780  
 781          if ( in_array('wplink', self::$plugins, true) || in_array('link', self::$qt_buttons, true) ) {
 782              wp_enqueue_script('wplink');
 783              wp_enqueue_script( 'jquery-ui-autocomplete' );
 784          }
 785  
 786          if ( self::$old_dfw_compat ) {
 787              wp_enqueue_script('wp-fullscreen-stub');
 788          }
 789  
 790          if ( self::$has_medialib ) {
 791              add_thickbox();
 792              wp_enqueue_script( 'media-upload' );
 793              wp_enqueue_script( 'wp-embed' );
 794          }
 795  
 796          /**
 797           * Fires when scripts and styles are enqueued for the editor.
 798           *
 799           * @since 3.9.0
 800           *
 801           * @param array $to_load An array containing boolean values whether TinyMCE
 802           *                       and Quicktags are being loaded.
 803           */
 804          do_action( 'wp_enqueue_editor', array(
 805              'tinymce'   => self::$has_tinymce,
 806              'quicktags' => self::$has_quicktags,
 807          ) );
 808      }
 809  
 810      /**
 811       * Translates the default TinyMCE strings and returns them as JSON encoded object ready to be loaded with tinymce.addI18n().
 812       * Can be used directly (_WP_Editors::wp_mce_translation()) by passing the same locale as set in the TinyMCE init object.
 813       *
 814       * @static
 815       * @param string $mce_locale The locale used for the editor.
 816       * @param bool $json_only optional Whether to include the JavaScript calls to tinymce.addI18n() and tinymce.ScriptLoader.markDone().
 817       * @return string Translation object, JSON encoded.
 818       */
 819  	public static function wp_mce_translation( $mce_locale = '', $json_only = false ) {
 820  
 821          $mce_translation = array(
 822              // Default TinyMCE strings
 823              'New document' => __( 'New document' ),
 824              'Formats' => _x( 'Formats', 'TinyMCE' ),
 825  
 826              'Headings' => _x( 'Headings', 'TinyMCE' ),
 827              'Heading 1' => __( 'Heading 1' ),
 828              'Heading 2' => __( 'Heading 2' ),
 829              'Heading 3' => __( 'Heading 3' ),
 830              'Heading 4' => __( 'Heading 4' ),
 831              'Heading 5' => __( 'Heading 5' ),
 832              'Heading 6' => __( 'Heading 6' ),
 833  
 834              /* translators: block tags */
 835              'Blocks' => _x( 'Blocks', 'TinyMCE' ),
 836              'Paragraph' => __( 'Paragraph' ),
 837              'Blockquote' => __( 'Blockquote' ),
 838              'Div' => _x( 'Div', 'HTML tag' ),
 839              'Pre' => _x( 'Pre', 'HTML tag' ),
 840              'Preformatted' => _x( 'Preformatted', 'HTML tag' ),
 841              'Address' => _x( 'Address', 'HTML tag' ),
 842  
 843              'Inline' => _x( 'Inline', 'HTML elements' ),
 844              'Underline' => __( 'Underline' ),
 845              'Strikethrough' => __( 'Strikethrough' ),
 846              'Subscript' => __( 'Subscript' ),
 847              'Superscript' => __( 'Superscript' ),
 848              'Clear formatting' => __( 'Clear formatting' ),
 849              'Bold' => __( 'Bold' ),
 850              'Italic' => __( 'Italic' ),
 851              'Code' => __( 'Code' ),
 852              'Source code' => __( 'Source code' ),
 853              'Font Family' => __( 'Font Family' ),
 854              'Font Sizes' => __( 'Font Sizes' ),
 855  
 856              'Align center' => __( 'Align center' ),
 857              'Align right' => __( 'Align right' ),
 858              'Align left' => __( 'Align left' ),
 859              'Justify' => __( 'Justify' ),
 860              'Increase indent' => __( 'Increase indent' ),
 861              'Decrease indent' => __( 'Decrease indent' ),
 862  
 863              'Cut' => __( 'Cut' ),
 864              'Copy' => __( 'Copy' ),
 865              'Paste' => __( 'Paste' ),
 866              'Select all' => __( 'Select all' ),
 867              'Undo' => __( 'Undo' ),
 868              'Redo' => __( 'Redo' ),
 869  
 870              'Ok' => __( 'OK' ),
 871              'Cancel' => __( 'Cancel' ),
 872              'Close' => __( 'Close' ),
 873              'Visual aids' => __( 'Visual aids' ),
 874  
 875              'Bullet list' => __( 'Bulleted list' ),
 876              'Numbered list' => __( 'Numbered list' ),
 877              'Square' => _x( 'Square', 'list style' ),
 878              'Default' => _x( 'Default', 'list style' ),
 879              'Circle' => _x( 'Circle', 'list style' ),
 880              'Disc' => _x('Disc', 'list style' ),
 881              'Lower Greek' => _x( 'Lower Greek', 'list style' ),
 882              'Lower Alpha' => _x( 'Lower Alpha', 'list style' ),
 883              'Upper Alpha' => _x( 'Upper Alpha', 'list style' ),
 884              'Upper Roman' => _x( 'Upper Roman', 'list style' ),
 885              'Lower Roman' => _x( 'Lower Roman', 'list style' ),
 886  
 887              // Anchor plugin
 888              'Name' => _x( 'Name', 'Name of link anchor (TinyMCE)' ),
 889              'Anchor' => _x( 'Anchor', 'Link anchor (TinyMCE)' ),
 890              'Anchors' => _x( 'Anchors', 'Link anchors (TinyMCE)' ),
 891  
 892              // Fullpage plugin
 893              'Document properties' => __( 'Document properties' ),
 894              'Robots' => __( 'Robots' ),
 895              'Title' => __( 'Title' ),
 896              'Keywords' => __( 'Keywords' ),
 897              'Encoding' => __( 'Encoding' ),
 898              'Description' => __( 'Description' ),
 899              'Author' => __( 'Author' ),
 900  
 901              // Media, image plugins
 902              'Insert/edit image' => __( 'Insert/edit image' ),
 903              'General' => __( 'General' ),
 904              'Advanced' => __( 'Advanced' ),
 905              'Source' => __( 'Source' ),
 906              'Border' => __( 'Border' ),
 907              'Constrain proportions' => __( 'Constrain proportions' ),
 908              'Vertical space' => __( 'Vertical space' ),
 909              'Image description' => __( 'Image description' ),
 910              'Style' => __( 'Style' ),
 911              'Dimensions' => __( 'Dimensions' ),
 912              'Insert image' => __( 'Insert image' ),
 913              'Insert date/time' => __( 'Insert date/time' ),
 914              'Insert/edit video' => __( 'Insert/edit video' ),
 915              'Poster' => __( 'Poster' ),
 916              'Alternative source' => __( 'Alternative source' ),
 917              'Paste your embed code below:' => __( 'Paste your embed code below:' ),
 918              'Insert video' => __( 'Insert video' ),
 919              'Embed' => __( 'Embed' ),
 920  
 921              // Each of these have a corresponding plugin
 922              'Special character' => __( 'Special character' ),
 923              'Right to left' => _x( 'Right to left', 'editor button' ),
 924              'Left to right' => _x( 'Left to right', 'editor button' ),
 925              'Emoticons' => __( 'Emoticons' ),
 926              'Nonbreaking space' => __( 'Nonbreaking space' ),
 927              'Page break' => __( 'Page break' ),
 928              'Paste as text' => __( 'Paste as text' ),
 929              'Preview' => __( 'Preview' ),
 930              'Print' => __( 'Print' ),
 931              'Save' => __( 'Save' ),
 932              'Fullscreen' => __( 'Fullscreen' ),
 933              'Horizontal line' => __( 'Horizontal line' ),
 934              'Horizontal space' => __( 'Horizontal space' ),
 935              'Restore last draft' => __( 'Restore last draft' ),
 936              'Insert/edit link' => __( 'Insert/edit link' ),
 937              'Remove link' => __( 'Remove link' ),
 938  
 939              'Color' => __( 'Color' ),
 940              'Custom color' => __( 'Custom color' ),
 941              'Custom...' => _x( 'Custom...', 'label for custom color' ), // no ellipsis
 942              'No color' => __( 'No color' ),
 943  
 944              // Spelling, search/replace plugins
 945              'Could not find the specified string.' => __( 'Could not find the specified string.' ),
 946              'Replace' => _x( 'Replace', 'find/replace' ),
 947              'Next' => _x( 'Next', 'find/replace' ),
 948              /* translators: previous */
 949              'Prev' => _x( 'Prev', 'find/replace' ),
 950              'Whole words' => _x( 'Whole words', 'find/replace' ),
 951              'Find and replace' => __( 'Find and replace' ),
 952              'Replace with' => _x('Replace with', 'find/replace' ),
 953              'Find' => _x( 'Find', 'find/replace' ),
 954              'Replace all' => _x( 'Replace all', 'find/replace' ),
 955              'Match case' => __( 'Match case' ),
 956              'Spellcheck' => __( 'Check Spelling' ),
 957              'Finish' => _x( 'Finish', 'spellcheck' ),
 958              'Ignore all' => _x( 'Ignore all', 'spellcheck' ),
 959              'Ignore' => _x( 'Ignore', 'spellcheck' ),
 960              'Add to Dictionary' => __( 'Add to Dictionary' ),
 961  
 962              // TinyMCE tables
 963              'Insert table' => __( 'Insert table' ),
 964              'Delete table' => __( 'Delete table' ),
 965              'Table properties' => __( 'Table properties' ),
 966              'Row properties' => __( 'Table row properties' ),
 967              'Cell properties' => __( 'Table cell properties' ),
 968              'Border color' => __( 'Border color' ),
 969  
 970              'Row' => __( 'Row' ),
 971              'Rows' => __( 'Rows' ),
 972              'Column' => _x( 'Column', 'table column' ),
 973              'Cols' => _x( 'Cols', 'table columns' ),
 974              'Cell' => _x( 'Cell', 'table cell' ),
 975              'Header cell' => __( 'Header cell' ),
 976              'Header' => _x( 'Header', 'table header' ),
 977              'Body' => _x( 'Body', 'table body' ),
 978              'Footer' => _x( 'Footer', 'table footer' ),
 979  
 980              'Insert row before' => __( 'Insert row before' ),
 981              'Insert row after' => __( 'Insert row after' ),
 982              'Insert column before' => __( 'Insert column before' ),
 983              'Insert column after' => __( 'Insert column after' ),
 984              'Paste row before' => __( 'Paste table row before' ),
 985              'Paste row after' => __( 'Paste table row after' ),
 986              'Delete row' => __( 'Delete row' ),
 987              'Delete column' => __( 'Delete column' ),
 988              'Cut row' => __( 'Cut table row' ),
 989              'Copy row' => __( 'Copy table row' ),
 990              'Merge cells' => __( 'Merge table cells' ),
 991              'Split cell' => __( 'Split table cell' ),
 992  
 993              'Height' => __( 'Height' ),
 994              'Width' => __( 'Width' ),
 995              'Caption' => __( 'Caption' ),
 996              'Alignment' => __( 'Alignment' ),
 997              'H Align' => _x( 'H Align', 'horizontal table cell alignment' ),
 998              'Left' => __( 'Left' ),
 999              'Center' => __( 'Center' ),
1000              'Right' => __( 'Right' ),
1001              'None' => _x( 'None', 'table cell alignment attribute' ),
1002              'V Align' => _x( 'V Align', 'vertical table cell alignment' ),
1003              'Top' => __( 'Top' ),
1004              'Middle' => __( 'Middle' ),
1005              'Bottom' => __( 'Bottom' ),
1006  
1007              'Row group' => __( 'Row group' ),
1008              'Column group' => __( 'Column group' ),
1009              'Row type' => __( 'Row type' ),
1010              'Cell type' => __( 'Cell type' ),
1011              'Cell padding' => __( 'Cell padding' ),
1012              'Cell spacing' => __( 'Cell spacing' ),
1013              'Scope' => _x( 'Scope', 'table cell scope attribute' ),
1014  
1015              'Insert template' => _x( 'Insert template', 'TinyMCE' ),
1016              'Templates' => _x( 'Templates', 'TinyMCE' ),
1017  
1018              'Background color' => __( 'Background color' ),
1019              'Text color' => __( 'Text color' ),
1020              'Show blocks' => _x( 'Show blocks', 'editor button' ),
1021              'Show invisible characters' => __( 'Show invisible characters' ),
1022  
1023              /* translators: word count */
1024              'Words: {0}' => sprintf( __( 'Words: %s' ), '{0}' ),
1025              'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.' => __( 'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.' ) . "\n\n" . __( 'If you&#8217;re looking to paste rich content from Microsoft Word, try turning this option off. The editor will clean up text pasted from Word automatically.' ),
1026              'Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help' => __( 'Rich Text Area. Press Alt-Shift-H for help.' ),
1027              'Rich Text Area. Press Control-Option-H for help.' => __( 'Rich Text Area. Press Control-Option-H for help.' ),
1028              'You have unsaved changes are you sure you want to navigate away?' => __( 'The changes you made will be lost if you navigate away from this page.' ),
1029              'Your browser doesn\'t support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.' => __( 'Your browser does not support direct access to the clipboard. Please use keyboard shortcuts or your browser&#8217;s edit menu instead.' ),
1030  
1031              // TinyMCE menus
1032              'Insert' => _x( 'Insert', 'TinyMCE menu' ),
1033              'File' => _x( 'File', 'TinyMCE menu' ),
1034              'Edit' => _x( 'Edit', 'TinyMCE menu' ),
1035              'Tools' => _x( 'Tools', 'TinyMCE menu' ),
1036              'View' => _x( 'View', 'TinyMCE menu' ),
1037              'Table' => _x( 'Table', 'TinyMCE menu' ),
1038              'Format' => _x( 'Format', 'TinyMCE menu' ),
1039  
1040              // WordPress strings
1041              'Toolbar Toggle' => __( 'Toolbar Toggle' ),
1042              'Insert Read More tag' => __( 'Insert Read More tag' ),
1043              'Insert Page Break tag' => __( 'Insert Page Break tag' ),
1044              'Read more...' => __( 'Read more...' ), // Title on the placeholder inside the editor (no ellipsis)
1045              'Distraction-free writing mode' => __( 'Distraction-free writing mode' ),
1046              'No alignment' => __( 'No alignment' ), // Tooltip for the 'alignnone' button in the image toolbar
1047              'Remove' => __( 'Remove' ), // Tooltip for the 'remove' button in the image toolbar
1048              'Edit ' => __( 'Edit' ), // Tooltip for the 'edit' button in the image toolbar
1049              'Paste URL or type to search' => __( 'Paste URL or type to search' ), // Placeholder for the inline link dialog
1050              'Apply'  => __( 'Apply' ), // Tooltip for the 'apply' button in the inline link dialog
1051              'Link options'  => __( 'Link options' ), // Tooltip for the 'link options' button in the inline link dialog
1052  
1053              // Shortcuts help modal
1054              'Keyboard Shortcuts' => __( 'Keyboard Shortcuts' ),
1055              'Default shortcuts,' => __( 'Default shortcuts,' ),
1056              'Additional shortcuts,' => __( 'Additional shortcuts,' ),
1057              'Focus shortcuts:' => __( 'Focus shortcuts:' ),
1058              'Inline toolbar (when an image, link or preview is selected)' => __( 'Inline toolbar (when an image, link or preview is selected)' ),
1059              'Editor menu (when enabled)' => __( 'Editor menu (when enabled)' ),
1060              'Editor toolbar' => __( 'Editor toolbar' ),
1061              'Elements path' => __( 'Elements path' ),
1062              'Ctrl + Alt + letter:' => __( 'Ctrl + Alt + letter:' ),
1063              'Shift + Alt + letter:' => __( 'Shift + Alt + letter:' ),
1064              'Cmd + letter:' => __( 'Cmd + letter:' ),
1065              'Ctrl + letter:' => __( 'Ctrl + letter:' ),
1066              'Letter' => __( 'Letter' ),
1067              'Action' => __( 'Action' ),
1068              'Warning: the link has been inserted but may have errors. Please test it.' => __( 'Warning: the link has been inserted but may have errors. Please test it.' ),
1069              'To move focus to other buttons use Tab or the arrow keys. To return focus to the editor press Escape or use one of the buttons.' =>
1070                  __( 'To move focus to other buttons use Tab or the arrow keys. To return focus to the editor press Escape or use one of the buttons.' ),
1071              'When starting a new paragraph with one of these formatting shortcuts followed by a space, the formatting will be applied automatically. Press Backspace or Escape to undo.' =>
1072                  __( 'When starting a new paragraph with one of these formatting shortcuts followed by a space, the formatting will be applied automatically. Press Backspace or Escape to undo.' ),
1073              'The following formatting shortcuts are replaced when pressing Enter. Press Escape or the Undo button to undo.' =>
1074                  __( 'The following formatting shortcuts are replaced when pressing Enter. Press Escape or the Undo button to undo.' ),
1075              'The next group of formatting shortcuts are applied as you type or when you insert them around plain text in the same paragraph. Press Escape or the Undo button to undo.' =>
1076                  __( 'The next group of formatting shortcuts are applied as you type or when you insert them around plain text in the same paragraph. Press Escape or the Undo button to undo.' ),
1077          );
1078  
1079          /**
1080           * Link plugin (not included):
1081           *    Insert link
1082           *    Target
1083           *    New window
1084           *    Text to display
1085           *    The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?
1086           *    The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?
1087           *    Url
1088           */
1089  
1090          if ( ! $mce_locale ) {
1091              $mce_locale = self::$mce_locale;
1092          }
1093  
1094          /**
1095           * Filters translated strings prepared for TinyMCE.
1096           *
1097           * @since 3.9.0
1098           *
1099           * @param array  $mce_translation Key/value pairs of strings.
1100           * @param string $mce_locale      Locale.
1101           */
1102          $mce_translation = apply_filters( 'wp_mce_translation', $mce_translation, $mce_locale );
1103  
1104          foreach ( $mce_translation as $key => $value ) {
1105              // Remove strings that are not translated.
1106              if ( $key === $value ) {
1107                  unset( $mce_translation[$key] );
1108                  continue;
1109              }
1110  
1111              if ( false !== strpos( $value, '&' ) ) {
1112                  $mce_translation[$key] = html_entity_decode( $value, ENT_QUOTES, 'UTF-8' );
1113              }
1114          }
1115  
1116          // Set direction
1117          if ( is_rtl() ) {
1118              $mce_translation['_dir'] = 'rtl';
1119          }
1120  
1121          if ( $json_only ) {
1122              return wp_json_encode( $mce_translation );
1123          }
1124  
1125          $baseurl = self::$baseurl ? self::$baseurl : includes_url( 'js/tinymce' );
1126  
1127          return "tinymce.addI18n( '$mce_locale', " . wp_json_encode( $mce_translation ) . ");\n" .
1128              "tinymce.ScriptLoader.markDone( '$baseurl/langs/$mce_locale.js' );\n";
1129      }
1130  
1131      /**
1132       *
1133       * @static
1134       * @global string $wp_version
1135       * @global string $tinymce_version
1136       * @global bool   $concatenate_scripts
1137       * @global bool   $compress_scripts
1138       */
1139  	public static function editor_js() {
1140          global $wp_version, $tinymce_version, $concatenate_scripts, $compress_scripts;
1141  
1142          /**
1143           * Filters "tiny_mce_version" is deprecated
1144           *
1145           * The tiny_mce_version filter is not needed since external plugins are loaded directly by TinyMCE.
1146           * These plugins can be refreshed by appending query string to the URL passed to "mce_external_plugins" filter.
1147           * 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).
1148           */
1149          $version = 'ver=' . $tinymce_version;
1150          $tmce_on = !empty(self::$mce_settings);
1151  
1152          if ( ! isset($concatenate_scripts) )
1153              script_concat_settings();
1154  
1155          $compressed = $compress_scripts && $concatenate_scripts && isset($_SERVER['HTTP_ACCEPT_ENCODING'])
1156              && false !== stripos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip');
1157  
1158          $mceInit = $qtInit = '';
1159          if ( $tmce_on ) {
1160              foreach ( self::$mce_settings as $editor_id => $init ) {
1161                  $options = self::_parse_init( $init );
1162                  $mceInit .= "'$editor_id':{$options},";
1163              }
1164              $mceInit = '{' . trim($mceInit, ',') . '}';
1165          } else {
1166              $mceInit = '{}';
1167          }
1168  
1169          if ( !empty(self::$qt_settings) ) {
1170              foreach ( self::$qt_settings as $editor_id => $init ) {
1171                  $options = self::_parse_init( $init );
1172                  $qtInit .= "'$editor_id':{$options},";
1173              }
1174              $qtInit = '{' . trim($qtInit, ',') . '}';
1175          } else {
1176              $qtInit = '{}';
1177          }
1178  
1179          $ref = array(
1180              'plugins' => implode( ',', self::$plugins ),
1181              'theme' => 'modern',
1182              'language' => self::$mce_locale
1183          );
1184  
1185          $suffix = SCRIPT_DEBUG ? '' : '.min';
1186  
1187          /**
1188           * Fires immediately before the TinyMCE settings are printed.
1189           *
1190           * @since 3.2.0
1191           *
1192           * @param array $mce_settings TinyMCE settings array.
1193           */
1194          do_action( 'before_wp_tiny_mce', self::$mce_settings );
1195          ?>
1196  
1197          <script type="text/javascript">
1198          tinyMCEPreInit = {
1199              baseURL: "<?php echo self::$baseurl; ?>",
1200              suffix: "<?php echo $suffix; ?>",
1201              <?php
1202  
1203              if ( self::$drag_drop_upload ) {
1204                  echo 'dragDropUpload: true,';
1205              }
1206  
1207              ?>
1208              mceInit: <?php echo $mceInit; ?>,
1209              qtInit: <?php echo $qtInit; ?>,
1210              ref: <?php echo self::_parse_init( $ref ); ?>,
1211              load_ext: function(url,lang){var sl=tinymce.ScriptLoader;sl.markDone(url+'/langs/'+lang+'.js');sl.markDone(url+'/langs/'+lang+'_dlg.js');}
1212          };
1213          </script>
1214          <?php
1215  
1216          $baseurl = self::$baseurl;
1217          // Load tinymce.js when running from /src, else load wp-tinymce.js.gz (production) or tinymce.min.js (SCRIPT_DEBUG)
1218          $mce_suffix = false !== strpos( $wp_version, '-src' ) ? '' : '.min';
1219  
1220          if ( $tmce_on ) {
1221              if ( $compressed ) {
1222                  echo "<script type='text/javascript' src='{$baseurl}/wp-tinymce.php?c=1&amp;$version'></script>\n";
1223              } else {
1224                  echo "<script type='text/javascript' src='{$baseurl}/tinymce{$mce_suffix}.js?$version'></script>\n";
1225                  echo "<script type='text/javascript' src='{$baseurl}/plugins/compat3x/plugin{$suffix}.js?$version'></script>\n";
1226              }
1227  
1228              echo "<script type='text/javascript'>\n" . self::wp_mce_translation() . "</script>\n";
1229  
1230              if ( self::$ext_plugins ) {
1231                  // Load the old-format English strings to prevent unsightly labels in old style popups
1232                  echo "<script type='text/javascript' src='{$baseurl}/langs/wp-langs-en.js?$version'></script>\n";
1233              }
1234          }
1235  
1236          /**
1237           * Fires after tinymce.js is loaded, but before any TinyMCE editor
1238           * instances are created.
1239           *
1240           * @since 3.9.0
1241           *
1242           * @param array $mce_settings TinyMCE settings array.
1243           */
1244          do_action( 'wp_tiny_mce_init', self::$mce_settings );
1245  
1246          ?>
1247          <script type="text/javascript">
1248          <?php
1249  
1250          if ( self::$ext_plugins )
1251              echo self::$ext_plugins . "\n";
1252  
1253          if ( ! is_admin() )
1254              echo 'var ajaxurl = "' . admin_url( 'admin-ajax.php', 'relative' ) . '";';
1255  
1256          ?>
1257  
1258          ( function() {
1259              var init, id, $wrap;
1260  
1261              if ( typeof tinymce !== 'undefined' ) {
1262                  // Fix RTL
1263                  tinymce.on( 'addeditor', function( event ) {
1264                      event.editor.rtl = event.editor.settings.rtl_ui ||
1265                          ( event.editor.editorManager &&
1266                          event.editor.editorManager.i18n &&
1267                          event.editor.editorManager.i18n.rtl );
1268                  }, true );
1269  
1270                  for ( id in tinyMCEPreInit.mceInit ) {
1271                      init = tinyMCEPreInit.mceInit[id];
1272                      $wrap = tinymce.$( '#wp-' + id + '-wrap' );
1273  
1274                      if ( ( $wrap.hasClass( 'tmce-active' ) || ! tinyMCEPreInit.qtInit.hasOwnProperty( id ) ) && ! init.wp_skip_init ) {
1275                          tinymce.init( init );
1276  
1277                          if ( ! window.wpActiveEditor ) {
1278                              window.wpActiveEditor = id;
1279                          }
1280                      }
1281                  }
1282              }
1283  
1284              if ( typeof quicktags !== 'undefined' ) {
1285                  for ( id in tinyMCEPreInit.qtInit ) {
1286                      quicktags( tinyMCEPreInit.qtInit[id] );
1287  
1288                      if ( ! window.wpActiveEditor ) {
1289                          window.wpActiveEditor = id;
1290                      }
1291                  }
1292              }
1293          }());
1294          </script>
1295          <?php
1296  
1297          if ( in_array( 'wplink', self::$plugins, true ) || in_array( 'link', self::$qt_buttons, true ) ) {
1298              self::wp_link_dialog();
1299          }
1300  
1301          /**
1302           * Fires after any core TinyMCE editor instances are created.
1303           *
1304           * @since 3.2.0
1305           *
1306           * @param array $mce_settings TinyMCE settings array.
1307           */
1308          do_action( 'after_wp_tiny_mce', self::$mce_settings );
1309      }
1310  
1311      /**
1312       *
1313       * @static
1314       * @global int $content_width
1315       */
1316  	public static function wp_fullscreen_html() {
1317          _deprecated_function( __FUNCTION__, '4.3.0' );
1318      }
1319  
1320      /**
1321       * Performs post queries for internal linking.
1322       *
1323       * @since 3.1.0
1324       *
1325       * @static
1326       * @param array $args Optional. Accepts 'pagenum' and 's' (search) arguments.
1327       * @return false|array Results.
1328       */
1329  	public static function wp_link_query( $args = array() ) {
1330          $pts = get_post_types( array( 'public' => true ), 'objects' );
1331          $pt_names = array_keys( $pts );
1332  
1333          $query = array(
1334              'post_type' => $pt_names,
1335              'suppress_filters' => true,
1336              'update_post_term_cache' => false,
1337              'update_post_meta_cache' => false,
1338              'post_status' => 'publish',
1339              'posts_per_page' => 20,
1340          );
1341  
1342          $args['pagenum'] = isset( $args['pagenum'] ) ? absint( $args['pagenum'] ) : 1;
1343  
1344          if ( isset( $args['s'] ) )
1345              $query['s'] = $args['s'];
1346  
1347          $query['offset'] = $args['pagenum'] > 1 ? $query['posts_per_page'] * ( $args['pagenum'] - 1 ) : 0;
1348  
1349          /**
1350           * Filters the link query arguments.
1351           *
1352           * Allows modification of the link query arguments before querying.
1353           *
1354           * @see WP_Query for a full list of arguments
1355           *
1356           * @since 3.7.0
1357           *
1358           * @param array $query An array of WP_Query arguments.
1359           */
1360          $query = apply_filters( 'wp_link_query_args', $query );
1361  
1362          // Do main query.
1363          $get_posts = new WP_Query;
1364          $posts = $get_posts->query( $query );
1365          // Check if any posts were found.
1366          if ( ! $get_posts->post_count )
1367              return false;
1368  
1369          // Build results.
1370          $results = array();
1371          foreach ( $posts as $post ) {
1372              if ( 'post' == $post->post_type )
1373                  $info = mysql2date( __( 'Y/m/d' ), $post->post_date );
1374              else
1375                  $info = $pts[ $post->post_type ]->labels->singular_name;
1376  
1377              $results[] = array(
1378                  'ID' => $post->ID,
1379                  'title' => trim( esc_html( strip_tags( get_the_title( $post ) ) ) ),
1380                  'permalink' => get_permalink( $post->ID ),
1381                  'info' => $info,
1382              );
1383          }
1384  
1385          /**
1386           * Filters the link query results.
1387           *
1388           * Allows modification of the returned link query results.
1389           *
1390           * @since 3.7.0
1391           *
1392           * @see 'wp_link_query_args' filter
1393           *
1394           * @param array $results {
1395           *     An associative array of query results.
1396           *
1397           *     @type array {
1398           *         @type int    $ID        Post ID.
1399           *         @type string $title     The trimmed, escaped post title.
1400           *         @type string $permalink Post permalink.
1401           *         @type string $info      A 'Y/m/d'-formatted date for 'post' post type,
1402           *                                 the 'singular_name' post type label otherwise.
1403           *     }
1404           * }
1405           * @param array $query  An array of WP_Query arguments.
1406           */
1407          return apply_filters( 'wp_link_query', $results, $query );
1408      }
1409  
1410      /**
1411       * Dialog for internal linking.
1412       *
1413       * @since 3.1.0
1414       *
1415       * @static
1416       */
1417  	public static function wp_link_dialog() {
1418          // display: none is required here, see #WP27605
1419          ?>
1420          <div id="wp-link-backdrop" style="display: none"></div>
1421          <div id="wp-link-wrap" class="wp-core-ui" style="display: none" role="dialog" aria-labelledby="link-modal-title">
1422          <form id="wp-link" tabindex="-1">
1423          <?php wp_nonce_field( 'internal-linking', '_ajax_linking_nonce', false ); ?>
1424          <h1 id="link-modal-title"><?php _e( 'Insert/edit link' ) ?></h1>
1425          <button type="button" id="wp-link-close"><span class="screen-reader-text"><?php _e( 'Close' ); ?></span></button>
1426          <div id="link-selector">
1427              <div id="link-options">
1428                  <p class="howto" id="wplink-enter-url"><?php _e( 'Enter the destination URL' ); ?></p>
1429                  <div>
1430                      <label><span><?php _e( 'URL' ); ?></span>
1431                      <input id="wp-link-url" type="text" aria-describedby="wplink-enter-url" /></label>
1432                  </div>
1433                  <div class="wp-link-text-field">
1434                      <label><span><?php _e( 'Link Text' ); ?></span>
1435                      <input id="wp-link-text" type="text" /></label>
1436                  </div>
1437                  <div class="link-target">
1438                      <label><span></span>
1439                      <input type="checkbox" id="wp-link-target" /> <?php _e( 'Open link in a new tab' ); ?></label>
1440                  </div>
1441              </div>
1442              <p class="howto" id="wplink-link-existing-content"><?php _e( 'Or link to existing content' ); ?></p>
1443              <div id="search-panel">
1444                  <div class="link-search-wrapper">
1445                      <label>
1446                          <span class="search-label"><?php _e( 'Search' ); ?></span>
1447                          <input type="search" id="wp-link-search" class="link-search-field" autocomplete="off" aria-describedby="wplink-link-existing-content" />
1448                          <span class="spinner"></span>
1449                      </label>
1450                  </div>
1451                  <div id="search-results" class="query-results" tabindex="0">
1452                      <ul></ul>
1453                      <div class="river-waiting">
1454                          <span class="spinner"></span>
1455                      </div>
1456                  </div>
1457                  <div id="most-recent-results" class="query-results" tabindex="0">
1458                      <div class="query-notice" id="query-notice-message">
1459                          <em class="query-notice-default"><?php _e( 'No search term specified. Showing recent items.' ); ?></em>
1460                          <em class="query-notice-hint screen-reader-text"><?php _e( 'Search or use up and down arrow keys to select an item.' ); ?></em>
1461                      </div>
1462                      <ul></ul>
1463                      <div class="river-waiting">
1464                          <span class="spinner"></span>
1465                      </div>
1466                   </div>
1467               </div>
1468          </div>
1469          <div class="submitbox">
1470              <div id="wp-link-cancel">
1471                  <button type="button" class="button"><?php _e( 'Cancel' ); ?></button>
1472              </div>
1473              <div id="wp-link-update">
1474                  <input type="submit" value="<?php esc_attr_e( 'Add Link' ); ?>" class="button button-primary" id="wp-link-submit" name="wp-link-submit">
1475              </div>
1476          </div>
1477          </form>
1478          </div>
1479          <?php
1480      }
1481  }

title

Description

title

Description

title

Description

title

title

Body