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


Generated : Thu Nov 21 08:20:01 2024 Cross-referenced by PHPXref