[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-admin/js/ -> editor-expand.js (source)

   1  /**
   2   * @output wp-admin/js/editor-expand.js
   3   */
   4  
   5  /**
   6   * Handles the editor Distraction-Free Writing (DFW) functionality.
   7   *
   8   * @param {Window}       window    The global window object.
   9   * @param {JQueryStatic} $         The jQuery object.
  10   * @param {undefined}    undefined The undefined value.
  11   */
  12  ( function( window, $, undefined ) {
  13      'use strict';
  14  
  15      var $window = $( window ),
  16          $document = $( document ),
  17          $adminBar = $( '#wpadminbar' ),
  18          $footer = $( '#wpfooter' );
  19  
  20      /**
  21       * Handles the resizing of the editor.
  22       *
  23       * @since 4.0.0
  24       *
  25       * @return {void}
  26       */
  27      $( function() {
  28          var $wrap = $( '#postdivrich' ),
  29              $contentWrap = $( '#wp-content-wrap' ),
  30              $tools = $( '#wp-content-editor-tools' ),
  31              $visualTop = $(),
  32              $visualEditor = $(),
  33              $textTop = $( '#ed_toolbar' ),
  34              $textEditor = $( '#content' ),
  35              textEditor = $textEditor[0],
  36              oldTextLength = 0,
  37              $bottom = $( '#post-status-info' ),
  38              $menuBar = $(),
  39              $statusBar = $(),
  40              $sideSortables = $( '#side-sortables' ),
  41              $postboxContainer = $( '#postbox-container-1' ),
  42              $postBody = $('#post-body'),
  43              fullscreen = window.wp.editor && window.wp.editor.fullscreen,
  44              mceEditor,
  45              mceBind = function(){},
  46              mceUnbind = function(){},
  47              fixedTop = false,
  48              fixedBottom = false,
  49              fixedSideTop = false,
  50              fixedSideBottom = false,
  51              scrollTimer,
  52              lastScrollPosition = 0,
  53              pageYOffsetAtTop = 130,
  54              pinnedToolsTop = 56,
  55              sidebarBottom = 20,
  56              autoresizeMinHeight = 300,
  57              initialMode = $contentWrap.hasClass( 'tmce-active' ) ? 'tinymce' : 'html',
  58              advanced = !! parseInt( window.getUserSetting( 'hidetb' ), 10 ),
  59              // These are corrected when adjust() runs, except on scrolling if already set.
  60              heights = {
  61                  windowHeight: 0,
  62                  windowWidth: 0,
  63                  adminBarHeight: 0,
  64                  toolsHeight: 0,
  65                  menuBarHeight: 0,
  66                  visualTopHeight: 0,
  67                  textTopHeight: 0,
  68                  bottomHeight: 0,
  69                  statusBarHeight: 0,
  70                  sideSortablesHeight: 0
  71              };
  72  
  73          /**
  74           * Resizes textarea based on scroll height and width.
  75           *
  76           * Doesn't shrink the editor size below the 300px auto resize minimum height.
  77           *
  78           * @since 4.6.1
  79           *
  80           * @return {void}
  81           */
  82          var shrinkTextarea = window._.throttle( function() {
  83              var x = window.scrollX || document.documentElement.scrollLeft;
  84              var y = window.scrollY || document.documentElement.scrollTop;
  85              var height = parseInt( textEditor.style.height, 10 );
  86  
  87              textEditor.style.height = autoresizeMinHeight + 'px';
  88  
  89              if ( textEditor.scrollHeight > autoresizeMinHeight ) {
  90                  textEditor.style.height = textEditor.scrollHeight + 'px';
  91              }
  92  
  93              if ( typeof x !== 'undefined' ) {
  94                  window.scrollTo( x, y );
  95              }
  96  
  97              if ( textEditor.scrollHeight < height ) {
  98                  adjust();
  99              }
 100          }, 300 );
 101  
 102          /**
 103           * Resizes the text editor depending on the old text length.
 104           *
 105           * If there is an mceEditor and it is hidden, it resizes the editor depending
 106           * on the old text length. If the current length of the text is smaller than
 107           * the old text length, it shrinks the text area. Otherwise it resizes the editor to
 108           * the scroll height.
 109           *
 110           * @since 4.6.1
 111           *
 112           * @return {void}
 113           */
 114  		function textEditorResize() {
 115              var length = textEditor.value.length;
 116  
 117              if ( mceEditor && ! mceEditor.isHidden() ) {
 118                  return;
 119              }
 120  
 121              if ( ! mceEditor && initialMode === 'tinymce' ) {
 122                  return;
 123              }
 124  
 125              if ( length < oldTextLength ) {
 126                  shrinkTextarea();
 127              } else if ( parseInt( textEditor.style.height, 10 ) < textEditor.scrollHeight ) {
 128                  textEditor.style.height = Math.ceil( textEditor.scrollHeight ) + 'px';
 129                  adjust();
 130              }
 131  
 132              oldTextLength = length;
 133          }
 134  
 135          /**
 136           * Gets the height and widths of elements.
 137           *
 138           * Gets the heights of the window, the adminbar, the tools, the menu,
 139           * the visualTop, the textTop, the bottom, the statusbar and sideSortables
 140           * and stores these in the heights object. Defaults to 0.
 141           * Gets the width of the window and stores this in the heights object.
 142           *
 143           * @since 4.0.0
 144           *
 145           * @return {void}
 146           */
 147  		function getHeights() {
 148              var windowWidth = $window.width();
 149  
 150              heights = {
 151                  windowHeight: $window.height(),
 152                  windowWidth: windowWidth,
 153                  adminBarHeight: ( windowWidth > 600 ? $adminBar.outerHeight() : 0 ),
 154                  toolsHeight: $tools.outerHeight() || 0,
 155                  menuBarHeight: $menuBar.outerHeight() || 0,
 156                  visualTopHeight: $visualTop.outerHeight() || 0,
 157                  textTopHeight: $textTop.outerHeight() || 0,
 158                  bottomHeight: $bottom.outerHeight() || 0,
 159                  statusBarHeight: $statusBar.outerHeight() || 0,
 160                  sideSortablesHeight: $sideSortables.height() || 0
 161              };
 162  
 163              // Adjust for hidden menubar.
 164              if ( heights.menuBarHeight < 3 ) {
 165                  heights.menuBarHeight = 0;
 166              }
 167          }
 168  
 169          // We need to wait for TinyMCE to initialize.
 170          /**
 171           * Binds all necessary functions for editor expand to the editor when the editor
 172           * is initialized.
 173           *
 174           * @since 4.0.0
 175           *
 176           * @param {event}  event  The TinyMCE editor init event.
 177           * @param {Object} editor The editor to bind the vents on.
 178           *
 179           * @return {void}
 180           */
 181          $document.on( 'tinymce-editor-init.editor-expand', function( event, editor ) {
 182              // VK contains the type of key pressed. VK = virtual keyboard.
 183              var VK = window.tinymce.util.VK,
 184                  /**
 185                   * Hides any float panel with a hover state. Additionally hides tooltips.
 186                   *
 187                   * @return {void}
 188                   */
 189                  hideFloatPanels = _.debounce( function() {
 190                      ! $( '.mce-floatpanel:hover' ).length && window.tinymce.ui.FloatPanel.hideAll();
 191                      $( '.mce-tooltip' ).hide();
 192                  }, 1000, true );
 193  
 194              // Make sure it's the main editor.
 195              if ( editor.id !== 'content' ) {
 196                  return;
 197              }
 198  
 199              // Copy the editor instance.
 200              mceEditor = editor;
 201  
 202              // Set the minimum height to the initial viewport height.
 203              editor.settings.autoresize_min_height = autoresizeMinHeight;
 204  
 205              // Get the necessary UI elements.
 206              $visualTop = $contentWrap.find( '.mce-toolbar-grp' );
 207              $visualEditor = $contentWrap.find( '.mce-edit-area' );
 208              $statusBar = $contentWrap.find( '.mce-statusbar' );
 209              $menuBar = $contentWrap.find( '.mce-menubar' );
 210  
 211              /**
 212               * Gets the offset of the editor.
 213               *
 214               * @return {number|boolean} Returns the offset of the editor
 215               * or false if there is no offset height.
 216               */
 217  			function mceGetCursorOffset() {
 218                  var node = editor.selection.getNode(),
 219                      range, view, offset;
 220  
 221                  /*
 222                   * If editor.wp.getView and the selection node from the editor selection
 223                   * are defined, use this as a view for the offset.
 224                   */
 225                  if ( editor.wp && editor.wp.getView && ( view = editor.wp.getView( node ) ) ) {
 226                      offset = view.getBoundingClientRect();
 227                  } else {
 228                      range = editor.selection.getRng();
 229  
 230                      // Try to get the offset from a range.
 231                      try {
 232                          offset = range.getClientRects()[0];
 233                      } catch( er ) {}
 234  
 235                      // Get the offset from the bounding client rectangle of the node.
 236                      if ( ! offset ) {
 237                          offset = node.getBoundingClientRect();
 238                      }
 239                  }
 240  
 241                  return offset.height ? offset : false;
 242              }
 243  
 244              /**
 245               * Filters the special keys that should not be used for scrolling.
 246               *
 247               * @since 4.0.0
 248               *
 249               * @param {event} event The event to get the key code from.
 250               *
 251               * @return {void}
 252               */
 253  			function mceKeyup( event ) {
 254                  var key = event.keyCode;
 255  
 256                  // Bail on special keys. Key code 47 is a '/'.
 257                  if ( key <= 47 && ! ( key === VK.SPACEBAR || key === VK.ENTER || key === VK.DELETE || key === VK.BACKSPACE || key === VK.UP || key === VK.LEFT || key === VK.DOWN || key === VK.UP ) ) {
 258                      return;
 259                  // OS keys, function keys, num lock, scroll lock. Key code 91-93 are OS keys.
 260                  // Key code 112-123 are F1 to F12. Key code 144 is num lock. Key code 145 is scroll lock.
 261                  } else if ( ( key >= 91 && key <= 93 ) || ( key >= 112 && key <= 123 ) || key === 144 || key === 145 ) {
 262                      return;
 263                  }
 264  
 265                  mceScroll( key );
 266              }
 267  
 268              /**
 269               * Makes sure the cursor is always visible in the editor.
 270               *
 271               * Makes sure the cursor is kept between the toolbars of the editor and scrolls
 272               * the window when the cursor moves out of the viewport to a wpview.
 273               * Setting a buffer > 0 will prevent the browser default.
 274               * Some browsers will scroll to the middle,
 275               * others to the top/bottom of the *window* when moving the cursor out of the viewport.
 276               *
 277               * @since 4.1.0
 278               *
 279               * @param {string} key The key code of the pressed key.
 280               *
 281               * @return {void}
 282               */
 283  			function mceScroll( key ) {
 284                  var offset = mceGetCursorOffset(),
 285                      buffer = 50,
 286                      cursorTop, cursorBottom, editorTop, editorBottom;
 287  
 288                  // Don't scroll if there is no offset.
 289                  if ( ! offset ) {
 290                      return;
 291                  }
 292  
 293                  // Determine the cursorTop based on the offset and the top of the editor iframe.
 294                  cursorTop = offset.top + editor.iframeElement.getBoundingClientRect().top;
 295  
 296                  // Determine the cursorBottom based on the cursorTop and offset height.
 297                  cursorBottom = cursorTop + offset.height;
 298  
 299                  // Subtract the buffer from the cursorTop.
 300                  cursorTop = cursorTop - buffer;
 301  
 302                  // Add the buffer to the cursorBottom.
 303                  cursorBottom = cursorBottom + buffer;
 304                  editorTop = heights.adminBarHeight + heights.toolsHeight + heights.menuBarHeight + heights.visualTopHeight;
 305  
 306                  /*
 307                   * Set the editorBottom based on the window Height, and add the bottomHeight and statusBarHeight if the
 308                   * advanced editor is enabled.
 309                   */
 310                  editorBottom = heights.windowHeight - ( advanced ? heights.bottomHeight + heights.statusBarHeight : 0 );
 311  
 312                  // Don't scroll if the node is taller than the visible part of the editor.
 313                  if ( editorBottom - editorTop < offset.height ) {
 314                      return;
 315                  }
 316  
 317                  /*
 318                   * If the cursorTop is smaller than the editorTop and the up, left
 319                   * or backspace key is pressed, scroll the editor to the position defined
 320                   * by the cursorTop, pageYOffset and editorTop.
 321                   */
 322                  if ( cursorTop < editorTop && ( key === VK.UP || key === VK.LEFT || key === VK.BACKSPACE ) ) {
 323                      window.scrollTo( window.pageXOffset, cursorTop + window.pageYOffset - editorTop );
 324  
 325                  /*
 326                   * If any other key is pressed or the cursorTop is bigger than the editorTop,
 327                   * scroll the editor to the position defined by the cursorBottom,
 328                   * pageYOffset and editorBottom.
 329                   */
 330                  } else if ( cursorBottom > editorBottom ) {
 331                      window.scrollTo( window.pageXOffset, cursorBottom + window.pageYOffset - editorBottom );
 332                  }
 333              }
 334  
 335              /**
 336               * If the editor is fullscreen, calls adjust.
 337               *
 338               * @since 4.1.0
 339               *
 340               * @param {event} event The FullscreenStateChanged event.
 341               *
 342               * @return {void}
 343               */
 344  			function mceFullscreenToggled( event ) {
 345                  // event.state is true if the editor is fullscreen.
 346                  if ( ! event.state ) {
 347                      adjust();
 348                  }
 349              }
 350  
 351              /**
 352               * Shows the editor when scrolled.
 353               *
 354               * Binds the hideFloatPanels function on the window scroll.mce-float-panels event.
 355               * Executes the wpAutoResize on the active editor.
 356               *
 357               * @since 4.0.0
 358               *
 359               * @return {void}
 360               */
 361  			function mceShow() {
 362                  $window.on( 'scroll.mce-float-panels', hideFloatPanels );
 363  
 364                  setTimeout( function() {
 365                      editor.execCommand( 'wpAutoResize' );
 366                      adjust();
 367                  }, 300 );
 368              }
 369  
 370              /**
 371               * Resizes the editor.
 372               *
 373               * Removes all functions from the window scroll.mce-float-panels event.
 374               * Resizes the text editor and scrolls to a position based on the pageXOffset and adminBarHeight.
 375               *
 376               * @since 4.0.0
 377               *
 378               * @return {void}
 379               */
 380  			function mceHide() {
 381                  $window.off( 'scroll.mce-float-panels' );
 382  
 383                  setTimeout( function() {
 384                      var top = $contentWrap.offset().top;
 385  
 386                      if ( window.pageYOffset > top ) {
 387                          window.scrollTo( window.pageXOffset, top - heights.adminBarHeight );
 388                      }
 389  
 390                      textEditorResize();
 391                      adjust();
 392                  }, 100 );
 393  
 394                  adjust();
 395              }
 396  
 397              /**
 398               * Toggles advanced states.
 399               *
 400               * @since 4.1.0
 401               *
 402               * @return {void}
 403               */
 404  			function toggleAdvanced() {
 405                  advanced = ! advanced;
 406              }
 407  
 408              /**
 409               * Binds events of the editor and window.
 410               *
 411               * @since 4.0.0
 412               *
 413               * @return {void}
 414               */
 415              mceBind = function() {
 416                  editor.on( 'keyup', mceKeyup );
 417                  editor.on( 'show', mceShow );
 418                  editor.on( 'hide', mceHide );
 419                  editor.on( 'wp-toolbar-toggle', toggleAdvanced );
 420  
 421                  // Adjust when the editor resizes.
 422                  editor.on( 'setcontent wp-autoresize wp-toolbar-toggle', adjust );
 423  
 424                  // Don't hide the caret after undo/redo.
 425                  editor.on( 'undo redo', mceScroll );
 426  
 427                  // Adjust when exiting TinyMCE's fullscreen mode.
 428                  editor.on( 'FullscreenStateChanged', mceFullscreenToggled );
 429  
 430                  $window.off( 'scroll.mce-float-panels' ).on( 'scroll.mce-float-panels', hideFloatPanels );
 431              };
 432  
 433              /**
 434               * Unbinds the events of the editor and window.
 435               *
 436               * @since 4.0.0
 437               *
 438               * @return {void}
 439               */
 440              mceUnbind = function() {
 441                  editor.off( 'keyup', mceKeyup );
 442                  editor.off( 'show', mceShow );
 443                  editor.off( 'hide', mceHide );
 444                  editor.off( 'wp-toolbar-toggle', toggleAdvanced );
 445                  editor.off( 'setcontent wp-autoresize wp-toolbar-toggle', adjust );
 446                  editor.off( 'undo redo', mceScroll );
 447                  editor.off( 'FullscreenStateChanged', mceFullscreenToggled );
 448  
 449                  $window.off( 'scroll.mce-float-panels' );
 450              };
 451  
 452              if ( $wrap.hasClass( 'wp-editor-expand' ) ) {
 453  
 454                  // Adjust "immediately".
 455                  mceBind();
 456                  initialResize( adjust );
 457              }
 458          } );
 459  
 460          /**
 461           * Adjusts the toolbars heights and positions.
 462           *
 463           * Adjusts the toolbars heights and positions based on the scroll position on
 464           * the page, the active editor mode and the heights of the editor, admin bar and
 465           * side bar.
 466           *
 467           * @since 4.0.0
 468           *
 469           * @param {event} event The event that calls this function.
 470           *
 471           * @return {void}
 472           */
 473  		function adjust( event ) {
 474  
 475              // Makes sure we're not in fullscreen mode.
 476              if ( fullscreen && fullscreen.settings.visible ) {
 477                  return;
 478              }
 479  
 480              var windowPos = $window.scrollTop(),
 481                  type = event && event.type,
 482                  resize = type !== 'scroll',
 483                  visual = mceEditor && ! mceEditor.isHidden(),
 484                  buffer = autoresizeMinHeight,
 485                  postBodyTop = $postBody.offset().top,
 486                  borderWidth = 1,
 487                  contentWrapWidth = $contentWrap.width(),
 488                  $top, $editor, sidebarTop, footerTop, canPin,
 489                  topPos, topHeight, editorPos, editorHeight;
 490  
 491              /*
 492               * Refresh the heights if type isn't 'scroll'
 493               * or heights.windowHeight isn't set.
 494               */
 495              if ( resize || ! heights.windowHeight ) {
 496                  getHeights();
 497              }
 498  
 499              // Resize on resize event when the editor is in text mode.
 500              if ( ! visual && type === 'resize' ) {
 501                  textEditorResize();
 502              }
 503  
 504              if ( visual ) {
 505                  $top = $visualTop;
 506                  $editor = $visualEditor;
 507                  topHeight = heights.visualTopHeight;
 508              } else {
 509                  $top = $textTop;
 510                  $editor = $textEditor;
 511                  topHeight = heights.textTopHeight;
 512              }
 513  
 514              // Return if TinyMCE is still initializing.
 515              if ( ! visual && ! $top.length ) {
 516                  return;
 517              }
 518  
 519              topPos = $top.parent().offset().top;
 520              editorPos = $editor.offset().top;
 521              editorHeight = $editor.outerHeight();
 522  
 523              /*
 524               * If in visual mode, checks if the editorHeight is greater than the autoresizeMinHeight + topHeight.
 525               * If not in visual mode, checks if the editorHeight is greater than the autoresizeMinHeight + 20.
 526               */
 527              canPin = visual ? autoresizeMinHeight + topHeight : autoresizeMinHeight + 20; // 20px from textarea padding.
 528              canPin = editorHeight > ( canPin + 5 );
 529  
 530              if ( ! canPin ) {
 531                  if ( resize ) {
 532                      $tools.css( {
 533                          position: 'absolute',
 534                          top: 0,
 535                          width: contentWrapWidth
 536                      } );
 537  
 538                      if ( visual && $menuBar.length ) {
 539                          $menuBar.css( {
 540                              position: 'absolute',
 541                              top: 0,
 542                              width: contentWrapWidth - ( borderWidth * 2 )
 543                          } );
 544                      }
 545  
 546                      $top.css( {
 547                          position: 'absolute',
 548                          top: heights.menuBarHeight,
 549                          width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
 550                      } );
 551  
 552                      $statusBar.attr( 'style', advanced ? '' : 'visibility: hidden;' );
 553                      $bottom.attr( 'style', '' );
 554                  }
 555              } else {
 556                  // Check if the top is not already in a fixed position.
 557                  if ( ( ! fixedTop || resize ) &&
 558                      ( windowPos >= ( topPos - heights.toolsHeight - heights.adminBarHeight ) &&
 559                      windowPos <= ( topPos - heights.toolsHeight - heights.adminBarHeight + editorHeight - buffer ) ) ) {
 560                      fixedTop = true;
 561  
 562                      $tools.css( {
 563                          position: 'fixed',
 564                          top: heights.adminBarHeight,
 565                          width: contentWrapWidth
 566                      } );
 567  
 568                      if ( visual && $menuBar.length ) {
 569                          $menuBar.css( {
 570                              position: 'fixed',
 571                              top: heights.adminBarHeight + heights.toolsHeight,
 572                              width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
 573                          } );
 574                      }
 575  
 576                      $top.css( {
 577                          position: 'fixed',
 578                          top: heights.adminBarHeight + heights.toolsHeight + heights.menuBarHeight,
 579                          width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
 580                      } );
 581                      // Check if the top is already in a fixed position.
 582                  } else if ( fixedTop || resize ) {
 583                      if ( windowPos <= ( topPos - heights.toolsHeight - heights.adminBarHeight ) ) {
 584                          fixedTop = false;
 585  
 586                          $tools.css( {
 587                              position: 'absolute',
 588                              top: 0,
 589                              width: contentWrapWidth
 590                          } );
 591  
 592                          if ( visual && $menuBar.length ) {
 593                              $menuBar.css( {
 594                                  position: 'absolute',
 595                                  top: 0,
 596                                  width: contentWrapWidth - ( borderWidth * 2 )
 597                              } );
 598                          }
 599  
 600                          $top.css( {
 601                              position: 'absolute',
 602                              top: heights.menuBarHeight,
 603                              width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
 604                          } );
 605                      } else if ( windowPos >= ( topPos - heights.toolsHeight - heights.adminBarHeight + editorHeight - buffer ) ) {
 606                          fixedTop = false;
 607  
 608                          $tools.css( {
 609                              position: 'absolute',
 610                              top: editorHeight - buffer,
 611                              width: contentWrapWidth
 612                          } );
 613  
 614                          if ( visual && $menuBar.length ) {
 615                              $menuBar.css( {
 616                                  position: 'absolute',
 617                                  top: editorHeight - buffer,
 618                                  width: contentWrapWidth - ( borderWidth * 2 )
 619                              } );
 620                          }
 621  
 622                          $top.css( {
 623                              position: 'absolute',
 624                              top: editorHeight - buffer + heights.menuBarHeight,
 625                              width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
 626                          } );
 627                      }
 628                  }
 629  
 630                  // Check if the bottom is not already in a fixed position.
 631                  if ( ( ! fixedBottom || ( resize && advanced ) ) &&
 632                          // Add borderWidth for the border around the .wp-editor-container.
 633                          ( windowPos + heights.windowHeight ) <= ( editorPos + editorHeight + heights.bottomHeight + heights.statusBarHeight + borderWidth ) ) {
 634  
 635                      if ( event && event.deltaHeight > 0 && event.deltaHeight < 100 ) {
 636                          window.scrollBy( 0, event.deltaHeight );
 637                      } else if ( visual && advanced ) {
 638                          fixedBottom = true;
 639  
 640                          $statusBar.css( {
 641                              position: 'fixed',
 642                              bottom: heights.bottomHeight,
 643                              visibility: '',
 644                              width: contentWrapWidth - ( borderWidth * 2 )
 645                          } );
 646  
 647                          $bottom.css( {
 648                              position: 'fixed',
 649                              bottom: 0,
 650                              width: contentWrapWidth
 651                          } );
 652                      }
 653                  } else if ( ( ! advanced && fixedBottom ) ||
 654                          ( ( fixedBottom || resize ) &&
 655                          ( windowPos + heights.windowHeight ) > ( editorPos + editorHeight + heights.bottomHeight + heights.statusBarHeight - borderWidth ) ) ) {
 656                      fixedBottom = false;
 657  
 658                      $statusBar.attr( 'style', advanced ? '' : 'visibility: hidden;' );
 659                      $bottom.attr( 'style', '' );
 660                  }
 661              }
 662  
 663              // The postbox container is positioned with @media from CSS. Ensure it is pinned on the side.
 664              if ( $postboxContainer.width() < 300 && heights.windowWidth > 600 &&
 665  
 666                  // Check if the sidebar is not taller than the document height.
 667                  $document.height() > ( $sideSortables.height() + postBodyTop + 120 ) &&
 668  
 669                  // Check if the editor is taller than the viewport.
 670                  heights.windowHeight < editorHeight ) {
 671  
 672                  if ( ( heights.sideSortablesHeight + pinnedToolsTop + sidebarBottom ) > heights.windowHeight || fixedSideTop || fixedSideBottom ) {
 673  
 674                      // Reset the sideSortables style when scrolling to the top.
 675                      if ( windowPos + pinnedToolsTop <= postBodyTop ) {
 676                          $sideSortables.attr( 'style', '' );
 677                          fixedSideTop = fixedSideBottom = false;
 678                      } else {
 679  
 680                          // When scrolling down.
 681                          if ( windowPos > lastScrollPosition ) {
 682                              if ( fixedSideTop ) {
 683  
 684                                  // Let it scroll.
 685                                  fixedSideTop = false;
 686                                  sidebarTop = $sideSortables.offset().top - heights.adminBarHeight;
 687                                  footerTop = $footer.offset().top;
 688  
 689                                  // Don't get over the footer.
 690                                  if ( footerTop < sidebarTop + heights.sideSortablesHeight + sidebarBottom ) {
 691                                      sidebarTop = footerTop - heights.sideSortablesHeight - 12;
 692                                  }
 693  
 694                                  $sideSortables.css({
 695                                      position: 'absolute',
 696                                      top: sidebarTop,
 697                                      bottom: ''
 698                                  });
 699                              } else if ( ! fixedSideBottom && heights.sideSortablesHeight + $sideSortables.offset().top + sidebarBottom < windowPos + heights.windowHeight ) {
 700                                  // Pin the bottom.
 701                                  fixedSideBottom = true;
 702  
 703                                  $sideSortables.css({
 704                                      position: 'fixed',
 705                                      top: 'auto',
 706                                      bottom: sidebarBottom
 707                                  });
 708                              }
 709  
 710                          // When scrolling up.
 711                          } else if ( windowPos < lastScrollPosition ) {
 712                              if ( fixedSideBottom ) {
 713                                  // Let it scroll.
 714                                  fixedSideBottom = false;
 715                                  sidebarTop = $sideSortables.offset().top - sidebarBottom;
 716                                  footerTop = $footer.offset().top;
 717  
 718                                  // Don't get over the footer.
 719                                  if ( footerTop < sidebarTop + heights.sideSortablesHeight + sidebarBottom ) {
 720                                      sidebarTop = footerTop - heights.sideSortablesHeight - 12;
 721                                  }
 722  
 723                                  $sideSortables.css({
 724                                      position: 'absolute',
 725                                      top: sidebarTop,
 726                                      bottom: ''
 727                                  });
 728                              } else if ( ! fixedSideTop && $sideSortables.offset().top >= windowPos + pinnedToolsTop ) {
 729                                  // Pin the top.
 730                                  fixedSideTop = true;
 731  
 732                                  $sideSortables.css({
 733                                      position: 'fixed',
 734                                      top: pinnedToolsTop,
 735                                      bottom: ''
 736                                  });
 737                              }
 738                          }
 739                      }
 740                  } else {
 741                      // If the sidebar container is smaller than the viewport, then pin/unpin the top when scrolling.
 742                      if ( windowPos >= ( postBodyTop - pinnedToolsTop ) ) {
 743  
 744                          $sideSortables.css( {
 745                              position: 'fixed',
 746                              top: pinnedToolsTop
 747                          } );
 748                      } else {
 749                          $sideSortables.attr( 'style', '' );
 750                      }
 751  
 752                      fixedSideTop = fixedSideBottom = false;
 753                  }
 754  
 755                  lastScrollPosition = windowPos;
 756              } else {
 757                  $sideSortables.attr( 'style', '' );
 758                  fixedSideTop = fixedSideBottom = false;
 759              }
 760  
 761              if ( resize ) {
 762                  $contentWrap.css( {
 763                      paddingTop: heights.toolsHeight
 764                  } );
 765  
 766                  if ( visual ) {
 767                      $visualEditor.css( {
 768                          paddingTop: heights.visualTopHeight + heights.menuBarHeight
 769                      } );
 770                  } else {
 771                      $textEditor.css( {
 772                          marginTop: heights.textTopHeight
 773                      } );
 774                  }
 775              }
 776          }
 777  
 778          /**
 779           * Resizes the editor and adjusts the toolbars.
 780           *
 781           * @since 4.0.0
 782           *
 783           * @return {void}
 784           */
 785  		function fullscreenHide() {
 786              textEditorResize();
 787              adjust();
 788          }
 789  
 790          /**
 791           * Runs the passed function with 500ms intervals.
 792           *
 793           * @since 4.0.0
 794           *
 795           * @param {Function} callback The function to run in the timeout.
 796           *
 797           * @return {void}
 798           */
 799  		function initialResize( callback ) {
 800              for ( var i = 1; i < 6; i++ ) {
 801                  setTimeout( callback, 500 * i );
 802              }
 803          }
 804  
 805          /**
 806           * Runs adjust after 100ms.
 807           *
 808           * @since 4.0.0
 809           *
 810           * @return {void}
 811           */
 812  		function afterScroll() {
 813              clearTimeout( scrollTimer );
 814              scrollTimer = setTimeout( adjust, 100 );
 815          }
 816  
 817          /**
 818           * Binds editor expand events on elements.
 819           *
 820           * @since 4.0.0
 821           *
 822           * @return {void}
 823           */
 824          function on() {
 825              /*
 826               * Scroll to the top when triggering this from JS.
 827               * Ensure the toolbars are pinned properly.
 828               */
 829              if ( window.pageYOffset && window.pageYOffset > pageYOffsetAtTop ) {
 830                  window.scrollTo( window.pageXOffset, 0 );
 831              }
 832  
 833              $wrap.addClass( 'wp-editor-expand' );
 834  
 835              // Adjust when the window is scrolled or resized.
 836              $window.on( 'scroll.editor-expand resize.editor-expand', function( event ) {
 837                  adjust( event.type );
 838                  afterScroll();
 839              } );
 840  
 841              /*
 842                * Adjust when collapsing the menu, changing the columns
 843                * or changing the body class.
 844               */
 845              $document.on( 'wp-collapse-menu.editor-expand postboxes-columnchange.editor-expand editor-classchange.editor-expand', adjust )
 846                  .on( 'postbox-toggled.editor-expand postbox-moved.editor-expand', function() {
 847                      if ( ! fixedSideTop && ! fixedSideBottom && window.pageYOffset > pinnedToolsTop ) {
 848                          fixedSideBottom = true;
 849                          window.scrollBy( 0, -1 );
 850                          adjust();
 851                          window.scrollBy( 0, 1 );
 852                      }
 853  
 854                      adjust();
 855                  }).on( 'wp-window-resized.editor-expand', function() {
 856                      if ( mceEditor && ! mceEditor.isHidden() ) {
 857                          mceEditor.execCommand( 'wpAutoResize' );
 858                      } else {
 859                          textEditorResize();
 860                      }
 861                  });
 862  
 863              $textEditor.on( 'focus.editor-expand input.editor-expand propertychange.editor-expand', textEditorResize );
 864              mceBind();
 865  
 866              // Adjust when entering or exiting fullscreen mode.
 867              fullscreen && fullscreen.pubsub.subscribe( 'hidden', fullscreenHide );
 868  
 869              if ( mceEditor ) {
 870                  mceEditor.settings.wp_autoresize_on = true;
 871                  mceEditor.execCommand( 'wpAutoResizeOn' );
 872  
 873                  if ( ! mceEditor.isHidden() ) {
 874                      mceEditor.execCommand( 'wpAutoResize' );
 875                  }
 876              }
 877  
 878              if ( ! mceEditor || mceEditor.isHidden() ) {
 879                  textEditorResize();
 880              }
 881  
 882              adjust();
 883  
 884              $document.trigger( 'editor-expand-on' );
 885          }
 886  
 887          /**
 888           * Unbinds editor expand events.
 889           *
 890           * @since 4.0.0
 891           *
 892           * @return {void}
 893           */
 894  		function off() {
 895              var height = parseInt( window.getUserSetting( 'ed_size', 300 ), 10 );
 896  
 897              if ( height < 50 ) {
 898                  height = 50;
 899              } else if ( height > 5000 ) {
 900                  height = 5000;
 901              }
 902  
 903              /*
 904               * Scroll to the top when triggering this from JS.
 905               * Ensure the toolbars are reset properly.
 906               */
 907              if ( window.pageYOffset && window.pageYOffset > pageYOffsetAtTop ) {
 908                  window.scrollTo( window.pageXOffset, 0 );
 909              }
 910  
 911              $wrap.removeClass( 'wp-editor-expand' );
 912  
 913              $window.off( '.editor-expand' );
 914              $document.off( '.editor-expand' );
 915              $textEditor.off( '.editor-expand' );
 916              mceUnbind();
 917  
 918              // Adjust when entering or exiting fullscreen mode.
 919              fullscreen && fullscreen.pubsub.unsubscribe( 'hidden', fullscreenHide );
 920  
 921              // Reset all CSS.
 922              $.each( [ $visualTop, $textTop, $tools, $menuBar, $bottom, $statusBar, $contentWrap, $visualEditor, $textEditor, $sideSortables ], function( i, element ) {
 923                  element && element.attr( 'style', '' );
 924              });
 925  
 926              fixedTop = fixedBottom = fixedSideTop = fixedSideBottom = false;
 927  
 928              if ( mceEditor ) {
 929                  mceEditor.settings.wp_autoresize_on = false;
 930                  mceEditor.execCommand( 'wpAutoResizeOff' );
 931  
 932                  if ( ! mceEditor.isHidden() ) {
 933                      $textEditor.hide();
 934  
 935                      if ( height ) {
 936                          mceEditor.theme.resizeTo( null, height );
 937                      }
 938                  }
 939              }
 940  
 941              // If there is a height found in the user setting.
 942              if ( height ) {
 943                  $textEditor.height( height );
 944              }
 945  
 946              $document.trigger( 'editor-expand-off' );
 947          }
 948  
 949          // Start on load.
 950          if ( $wrap.hasClass( 'wp-editor-expand' ) ) {
 951              on();
 952  
 953              // Resize just after CSS has fully loaded and QuickTags is ready.
 954              if ( $contentWrap.hasClass( 'html-active' ) ) {
 955                  initialResize( function() {
 956                      adjust();
 957                      textEditorResize();
 958                  } );
 959              }
 960          }
 961  
 962          // Show the on/off checkbox.
 963          $( '#adv-settings .editor-expand' ).show();
 964          $( '#editor-expand-toggle' ).on( 'change.editor-expand', function() {
 965              if ( $(this).prop( 'checked' ) ) {
 966                  on();
 967                  window.setUserSetting( 'editor_expand', 'on' );
 968              } else {
 969                  off();
 970                  window.setUserSetting( 'editor_expand', 'off' );
 971              }
 972          });
 973  
 974          // Expose on() and off().
 975          window.editorExpand = {
 976              on: on,
 977              off: off
 978          };
 979      } );
 980  
 981      /**
 982       * Handles the distraction free writing of TinyMCE.
 983       *
 984       * @since 4.1.0
 985       *
 986       * @return {void}
 987       */
 988      $( function() {
 989          var $body = $( document.body ),
 990              $wrap = $( '#wpcontent' ),
 991              $editor = $( '#post-body-content' ),
 992              $title = $( '#title' ),
 993              $content = $( '#content' ),
 994              $overlay = $( document.createElement( 'DIV' ) ),
 995              $slug = $( '#edit-slug-box' ),
 996              $slugFocusEl = $slug.find( 'a' )
 997                  .add( $slug.find( 'button' ) )
 998                  .add( $slug.find( 'input' ) ),
 999              $menuWrap = $( '#adminmenuwrap' ),
1000              $editorWindow = $(),
1001              $editorIframe = $(),
1002              _isActive = window.getUserSetting( 'editor_expand', 'on' ) === 'on',
1003              _isOn = _isActive ? window.getUserSetting( 'post_dfw' ) === 'on' : false,
1004              traveledX = 0,
1005              traveledY = 0,
1006              buffer = 20,
1007              faded, fadedAdminBar, fadedSlug,
1008              editorRect, x, y, mouseY, scrollY,
1009              focusLostTimer, overlayTimer, editorHasFocus;
1010  
1011          $body.append( $overlay );
1012  
1013          $overlay.css( {
1014              display: 'none',
1015              position: 'fixed',
1016              top: $adminBar.height(),
1017              right: 0,
1018              bottom: 0,
1019              left: 0,
1020              'z-index': 9997
1021          } );
1022  
1023          $editor.css( {
1024              position: 'relative'
1025          } );
1026  
1027          $window.on( 'mousemove.focus', function( event ) {
1028              mouseY = event.pageY;
1029          } );
1030  
1031          /**
1032           * Recalculates the bottom and right position of the editor in the DOM.
1033           *
1034           * @since 4.1.0
1035           *
1036           * @return {void}
1037           */
1038  		function recalcEditorRect() {
1039              editorRect = $editor.offset();
1040              editorRect.right = editorRect.left + $editor.outerWidth();
1041              editorRect.bottom = editorRect.top + $editor.outerHeight();
1042          }
1043  
1044          /**
1045           * Activates the distraction free writing mode.
1046           *
1047           * @since 4.1.0
1048           *
1049           * @return {void}
1050           */
1051  		function activate() {
1052              if ( ! _isActive ) {
1053                  _isActive = true;
1054  
1055                  $document.trigger( 'dfw-activate' );
1056                  $content.on( 'keydown.focus-shortcut', toggleViaKeyboard );
1057              }
1058          }
1059  
1060          /**
1061           * Deactivates the distraction free writing mode.
1062           *
1063           * @since 4.1.0
1064           *
1065           * @return {void}
1066           */
1067  		function deactivate() {
1068              if ( _isActive ) {
1069                  off();
1070  
1071                  _isActive = false;
1072  
1073                  $document.trigger( 'dfw-deactivate' );
1074                  $content.off( 'keydown.focus-shortcut' );
1075              }
1076          }
1077  
1078          /**
1079           * Returns _isActive.
1080           *
1081           * @since 4.1.0
1082           *
1083           * @return {boolean} Returns true is _isActive is true.
1084           */
1085  		function isActive() {
1086              return _isActive;
1087          }
1088  
1089          /**
1090           * Binds events on the editor for distraction free writing.
1091           *
1092           * @since 4.1.0
1093           *
1094           * @return {void}
1095           */
1096          function on() {
1097              if ( ! _isOn && _isActive ) {
1098                  _isOn = true;
1099  
1100                  $content.on( 'keydown.focus', fadeOut );
1101  
1102                  $title.add( $content ).on( 'blur.focus', maybeFadeIn );
1103  
1104                  fadeOut();
1105  
1106                  window.setUserSetting( 'post_dfw', 'on' );
1107  
1108                  $document.trigger( 'dfw-on' );
1109              }
1110          }
1111  
1112          /**
1113           * Unbinds events on the editor for distraction free writing.
1114           *
1115           * @since 4.1.0
1116           *
1117           * @return {void}
1118           */
1119  		function off() {
1120              if ( _isOn ) {
1121                  _isOn = false;
1122  
1123                  $title.add( $content ).off( '.focus' );
1124  
1125                  fadeIn();
1126  
1127                  $editor.off( '.focus' );
1128  
1129                  window.setUserSetting( 'post_dfw', 'off' );
1130  
1131                  $document.trigger( 'dfw-off' );
1132              }
1133          }
1134  
1135          /**
1136           * Binds or unbinds the editor expand events.
1137           *
1138           * @since 4.1.0
1139           *
1140           * @return {void}
1141           */
1142  		function toggle() {
1143              if ( _isOn ) {
1144                  off();
1145              } else {
1146                  on();
1147              }
1148          }
1149  
1150          /**
1151           * Returns the value of _isOn.
1152           *
1153           * @since 4.1.0
1154           *
1155           * @return {boolean} Returns true if _isOn is true.
1156           */
1157  		function isOn() {
1158              return _isOn;
1159          }
1160  
1161          /**
1162           * Fades out all elements except for the editor.
1163           *
1164           * The fading is done based on key presses and mouse movements.
1165           * Also calls the fadeIn on certain key presses
1166           * or if the mouse leaves the editor.
1167           *
1168           * @since 4.1.0
1169           *
1170           * @param {Event} event The event that triggers this function.
1171           *
1172           * @return {void}
1173           */
1174  		function fadeOut( event ) {
1175              var isMac,
1176                  key = event && event.keyCode;
1177  
1178              if ( window.navigator.platform ) {
1179                  isMac = ( window.navigator.platform.indexOf( 'Mac' ) > -1 );
1180              }
1181  
1182              // Fade in and returns on Escape and keyboard shortcut Alt+Shift+W and Ctrl+Opt+W.
1183              if ( key === 27 || ( key === 87 && event.altKey && ( ( ! isMac && event.shiftKey ) || ( isMac && event.ctrlKey ) ) ) ) {
1184                  fadeIn( event );
1185                  return;
1186              }
1187  
1188              // Return if any of the following keys or combinations of keys is pressed.
1189              if ( event && ( event.metaKey || ( event.ctrlKey && ! event.altKey ) || ( event.altKey && event.shiftKey ) || ( key && (
1190                  // Special keys ( tab, ctrl, alt, esc, arrow keys... ).
1191                  ( key <= 47 && key !== 8 && key !== 13 && key !== 32 && key !== 46 ) ||
1192                  // Windows keys.
1193                  ( key >= 91 && key <= 93 ) ||
1194                  // F keys.
1195                  ( key >= 112 && key <= 135 ) ||
1196                  // Num Lock, Scroll Lock, OEM.
1197                  ( key >= 144 && key <= 150 ) ||
1198                  // OEM or non-printable.
1199                  key >= 224
1200              ) ) ) ) {
1201                  return;
1202              }
1203  
1204              if ( ! faded ) {
1205                  faded = true;
1206  
1207                  clearTimeout( overlayTimer );
1208  
1209                  overlayTimer = setTimeout( function() {
1210                      $overlay.show();
1211                  }, 600 );
1212  
1213                  $editor.css( 'z-index', 9998 );
1214  
1215                  $overlay
1216                      // Always recalculate the editor area when entering the overlay with the mouse.
1217                      .on( 'mouseenter.focus', function() {
1218                          recalcEditorRect();
1219  
1220                          $window.on( 'scroll.focus', function() {
1221                              var nScrollY = window.pageYOffset;
1222  
1223                              if ( (
1224                                  scrollY && mouseY &&
1225                                  scrollY !== nScrollY
1226                              ) && (
1227                                  mouseY < editorRect.top - buffer ||
1228                                  mouseY > editorRect.bottom + buffer
1229                              ) ) {
1230                                  fadeIn();
1231                              }
1232  
1233                              scrollY = nScrollY;
1234                          } );
1235                      } )
1236                      .on( 'mouseleave.focus', function() {
1237                          x = y =  null;
1238                          traveledX = traveledY = 0;
1239  
1240                          $window.off( 'scroll.focus' );
1241                      } )
1242                      // Fade in when the mouse moves away form the editor area.
1243                      .on( 'mousemove.focus', function( event ) {
1244                          var nx = event.clientX,
1245                              ny = event.clientY,
1246                              pageYOffset = window.pageYOffset,
1247                              pageXOffset = window.pageXOffset;
1248  
1249                          if ( x && y && ( nx !== x || ny !== y ) ) {
1250                              if (
1251                                  ( ny <= y && ny < editorRect.top - pageYOffset ) ||
1252                                  ( ny >= y && ny > editorRect.bottom - pageYOffset ) ||
1253                                  ( nx <= x && nx < editorRect.left - pageXOffset ) ||
1254                                  ( nx >= x && nx > editorRect.right - pageXOffset )
1255                              ) {
1256                                  traveledX += Math.abs( x - nx );
1257                                  traveledY += Math.abs( y - ny );
1258  
1259                                  if ( (
1260                                      ny <= editorRect.top - buffer - pageYOffset ||
1261                                      ny >= editorRect.bottom + buffer - pageYOffset ||
1262                                      nx <= editorRect.left - buffer - pageXOffset ||
1263                                      nx >= editorRect.right + buffer - pageXOffset
1264                                  ) && (
1265                                      traveledX > 10 ||
1266                                      traveledY > 10
1267                                  ) ) {
1268                                      fadeIn();
1269  
1270                                      x = y =  null;
1271                                      traveledX = traveledY = 0;
1272  
1273                                      return;
1274                                  }
1275                              } else {
1276                                  traveledX = traveledY = 0;
1277                              }
1278                          }
1279  
1280                          x = nx;
1281                          y = ny;
1282                      } )
1283  
1284                      // When the overlay is touched, fade in and cancel the event.
1285                      .on( 'touchstart.focus', function( event ) {
1286                          event.preventDefault();
1287                          fadeIn();
1288                      } );
1289  
1290                  $editor.off( 'mouseenter.focus' );
1291  
1292                  if ( focusLostTimer ) {
1293                      clearTimeout( focusLostTimer );
1294                      focusLostTimer = null;
1295                  }
1296  
1297                  $body.addClass( 'focus-on' ).removeClass( 'focus-off' );
1298              }
1299  
1300              fadeOutAdminBar();
1301              fadeOutSlug();
1302          }
1303  
1304          /**
1305           * Fades all elements back in.
1306           *
1307           * @since 4.1.0
1308           *
1309           * @param {Event} event The event that triggers this function.
1310           *
1311           * @return {void}
1312           */
1313  		function fadeIn( event ) {
1314              if ( faded ) {
1315                  faded = false;
1316  
1317                  clearTimeout( overlayTimer );
1318  
1319                  overlayTimer = setTimeout( function() {
1320                      $overlay.hide();
1321                  }, 200 );
1322  
1323                  $editor.css( 'z-index', '' );
1324  
1325                  $overlay.off( 'mouseenter.focus mouseleave.focus mousemove.focus touchstart.focus' );
1326  
1327                  /*
1328                   * When fading in, temporarily watch for refocus and fade back out - helps
1329                   * with 'accidental' editor exits with the mouse. When fading in and the event
1330                   * is a key event (Escape or Alt+Shift+W) don't watch for refocus.
1331                   */
1332                  if ( 'undefined' === typeof event ) {
1333                      $editor.on( 'mouseenter.focus', function() {
1334                          if ( $.contains( $editor.get( 0 ), document.activeElement ) || editorHasFocus ) {
1335                              fadeOut();
1336                          }
1337                      } );
1338                  }
1339  
1340                  focusLostTimer = setTimeout( function() {
1341                      focusLostTimer = null;
1342                      $editor.off( 'mouseenter.focus' );
1343                  }, 1000 );
1344  
1345                  $body.addClass( 'focus-off' ).removeClass( 'focus-on' );
1346              }
1347  
1348              fadeInAdminBar();
1349              fadeInSlug();
1350          }
1351  
1352          /**
1353           * Fades in if the focused element based on it position.
1354           *
1355           * @since 4.1.0
1356           *
1357           * @return {void}
1358           */
1359  		function maybeFadeIn() {
1360              setTimeout( function() {
1361                  var position = document.activeElement.compareDocumentPosition( $editor.get( 0 ) );
1362  
1363                  /**
1364                   * Determines whether the passed element has focus.
1365                   *
1366                   * @param {jQuery} $el The element to check for focus.
1367                   * @return {boolean} True if the element has focus, false otherwise.
1368                   */
1369  				function hasFocus( $el ) {
1370                      return $.contains( $el.get( 0 ), document.activeElement );
1371                  }
1372  
1373                  // The focused node is before or behind the editor area, and not outside the wrap.
1374                  if ( ( position === 2 || position === 4 ) && ( hasFocus( $menuWrap ) || hasFocus( $wrap ) || hasFocus( $footer ) ) ) {
1375                      fadeIn();
1376                  }
1377              }, 0 );
1378          }
1379  
1380          /**
1381           * Fades out the admin bar based on focus on the admin bar.
1382           *
1383           * @since 4.1.0
1384           *
1385           * @return {void}
1386           */
1387  		function fadeOutAdminBar() {
1388              if ( ! fadedAdminBar && faded ) {
1389                  fadedAdminBar = true;
1390  
1391                  $adminBar
1392                      .on( 'mouseenter.focus', function() {
1393                          $adminBar.addClass( 'focus-off' );
1394                      } )
1395                      .on( 'mouseleave.focus', function() {
1396                          $adminBar.removeClass( 'focus-off' );
1397                      } );
1398              }
1399          }
1400  
1401          /**
1402           * Fades in the admin bar.
1403           *
1404           * @since 4.1.0
1405           *
1406           * @return {void}
1407           */
1408  		function fadeInAdminBar() {
1409              if ( fadedAdminBar ) {
1410                  fadedAdminBar = false;
1411  
1412                  $adminBar.off( '.focus' );
1413              }
1414          }
1415  
1416          /**
1417           * Fades out the edit slug box.
1418           *
1419           * @since 4.1.0
1420           *
1421           * @return {void}
1422           */
1423  		function fadeOutSlug() {
1424              if ( ! fadedSlug && faded && ! $slug.find( ':focus').length ) {
1425                  fadedSlug = true;
1426  
1427                  $slug.stop().fadeTo( 'fast', 0.3 ).on( 'mouseenter.focus', fadeInSlug ).off( 'mouseleave.focus' );
1428  
1429                  $slugFocusEl.on( 'focus.focus', fadeInSlug ).off( 'blur.focus' );
1430              }
1431          }
1432  
1433          /**
1434           * Fades in the edit slug box.
1435           *
1436           * @since 4.1.0
1437           *
1438           * @return {void}
1439           */
1440  		function fadeInSlug() {
1441              if ( fadedSlug ) {
1442                  fadedSlug = false;
1443  
1444                  $slug.stop().fadeTo( 'fast', 1 ).on( 'mouseleave.focus', fadeOutSlug ).off( 'mouseenter.focus' );
1445  
1446                  $slugFocusEl.on( 'blur.focus', fadeOutSlug ).off( 'focus.focus' );
1447              }
1448          }
1449  
1450          /**
1451           * Triggers the toggle on Alt + Shift + W.
1452           *
1453           * Keycode 87 = w.
1454           *
1455           * @since 4.1.0
1456           *
1457           * @param {event} event The event to trigger the toggle.
1458           *
1459           * @return {void}
1460           */
1461  		function toggleViaKeyboard( event ) {
1462              if ( event.altKey && event.shiftKey && 87 === event.keyCode ) {
1463                  toggle();
1464              }
1465          }
1466  
1467          if ( $( '#postdivrich' ).hasClass( 'wp-editor-expand' ) ) {
1468              $content.on( 'keydown.focus-shortcut', toggleViaKeyboard );
1469          }
1470  
1471          /**
1472           * Adds the distraction free writing button when setting up TinyMCE.
1473           *
1474           * @since 4.1.0
1475           *
1476           * @param {event}  event  The TinyMCE editor setup event.
1477           * @param {Object} editor The editor to add the button to.
1478           *
1479           * @return {void}
1480           */
1481          $document.on( 'tinymce-editor-setup.focus', function( event, editor ) {
1482              editor.addButton( 'dfw', {
1483                  active: _isOn,
1484                  classes: 'wp-dfw btn widget',
1485                  disabled: ! _isActive,
1486                  onclick: toggle,
1487                  onPostRender: function() {
1488                      var button = this;
1489  
1490                      editor.on( 'init', function() {
1491                          if ( button.disabled() ) {
1492                              button.hide();
1493                          }
1494                      } );
1495  
1496                      $document
1497                      .on( 'dfw-activate.focus', function() {
1498                          button.disabled( false );
1499                          button.show();
1500                      } )
1501                      .on( 'dfw-deactivate.focus', function() {
1502                          button.disabled( true );
1503                          button.hide();
1504                      } )
1505                      .on( 'dfw-on.focus', function() {
1506                          button.active( true );
1507                      } )
1508                      .on( 'dfw-off.focus', function() {
1509                          button.active( false );
1510                      } );
1511                  },
1512                  tooltip: 'Distraction-free writing mode',
1513                  shortcut: 'Alt+Shift+W'
1514              } );
1515  
1516              editor.addCommand( 'wpToggleDFW', toggle );
1517              editor.addShortcut( 'access+w', '', 'wpToggleDFW' );
1518          } );
1519  
1520          /**
1521           * Binds and unbinds events on the editor.
1522           *
1523           * @since 4.1.0
1524           *
1525           * @param {event}  event  The TinyMCE editor init event.
1526           * @param {Object} editor The editor to bind events on.
1527           *
1528           * @return {void}
1529           */
1530          $document.on( 'tinymce-editor-init.focus', function( event, editor ) {
1531              var mceBind, mceUnbind;
1532  
1533              /**
1534               * Tracks when the TinyMCE editor receives focus.
1535               */
1536  			function focus() {
1537                  editorHasFocus = true;
1538              }
1539  
1540              /**
1541               * Tracks when the TinyMCE editor loses focus.
1542               */
1543  			function blur() {
1544                  editorHasFocus = false;
1545              }
1546  
1547              if ( editor.id === 'content' ) {
1548                  $editorWindow = $( editor.getWin() );
1549                  $editorIframe = $( editor.getContentAreaContainer() ).find( 'iframe' );
1550  
1551                  mceBind = function() {
1552                      editor.on( 'keydown', fadeOut );
1553                      editor.on( 'blur', maybeFadeIn );
1554                      editor.on( 'focus', focus );
1555                      editor.on( 'blur', blur );
1556                      editor.on( 'wp-autoresize', recalcEditorRect );
1557                  };
1558  
1559                  mceUnbind = function() {
1560                      editor.off( 'keydown', fadeOut );
1561                      editor.off( 'blur', maybeFadeIn );
1562                      editor.off( 'focus', focus );
1563                      editor.off( 'blur', blur );
1564                      editor.off( 'wp-autoresize', recalcEditorRect );
1565                  };
1566  
1567                  if ( _isOn ) {
1568                      mceBind();
1569                  }
1570  
1571                  // Bind and unbind based on the distraction free writing focus.
1572                  $document.on( 'dfw-on.focus', mceBind ).on( 'dfw-off.focus', mceUnbind );
1573  
1574                  // Focus the editor when it is the target of the click event.
1575                  editor.on( 'click', function( event ) {
1576                      if ( event.target === editor.getDoc().documentElement ) {
1577                          editor.focus();
1578                      }
1579                  } );
1580              }
1581          } );
1582  
1583          /**
1584           * Binds events on quicktags init.
1585           *
1586           * @since 4.1.0
1587           *
1588           * @param {event}  event  The quicktags init event.
1589           * @param {Object} editor The editor to bind events on.
1590           *
1591           * @return {void}
1592           */
1593          $document.on( 'quicktags-init', function( event, editor ) {
1594              var $button;
1595  
1596              // Bind the distraction free writing events if the distraction free writing button is available.
1597              if ( editor.settings.buttons && ( ',' + editor.settings.buttons + ',' ).indexOf( ',dfw,' ) !== -1 ) {
1598                  $button = $( '#' + editor.name + '_dfw' );
1599  
1600                  $( document )
1601                  .on( 'dfw-activate', function() {
1602                      $button.prop( 'disabled', false );
1603                  } )
1604                  .on( 'dfw-deactivate', function() {
1605                      $button.prop( 'disabled', true );
1606                  } )
1607                  .on( 'dfw-on', function() {
1608                      $button.addClass( 'active' );
1609                  } )
1610                  .on( 'dfw-off', function() {
1611                      $button.removeClass( 'active' );
1612                  } );
1613              }
1614          } );
1615  
1616          $document.on( 'editor-expand-on.focus', activate ).on( 'editor-expand-off.focus', deactivate );
1617  
1618          if ( _isOn ) {
1619              $content.on( 'keydown.focus', fadeOut );
1620  
1621              $title.add( $content ).on( 'blur.focus', maybeFadeIn );
1622          }
1623  
1624          window.wp = window.wp || {};
1625          window.wp.editor = window.wp.editor || {};
1626          window.wp.editor.dfw = {
1627              activate: activate,
1628              deactivate: deactivate,
1629              isActive: isActive,
1630              on: on,
1631              off: off,
1632              toggle: toggle,
1633              isOn: isOn
1634          };
1635      } );
1636  } )( window, window.jQuery );


Generated : Sun Sep 13 08:20:28 2026 Cross-referenced by PHPXref