home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress2 / wp-admin / js / editor-expand.js < prev    next >
Encoding:
JavaScript  |  2017-08-24  |  41.8 KB  |  1,605 lines

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