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


Generated : Fri Sep 4 08:20:24 2026 Cross-referenced by PHPXref