WordPress.org

Make WordPress Core

Ticket #29806: 29806.5.patch

File 29806.5.patch, 64.8 KB (added by iseulde, 5 years ago)
  • src/wp-admin/edit-form-advanced.php

     
    512512<div id="postdivrich" class="postarea<?php if ( $_wp_editor_expand ) { echo ' wp-editor-expand'; } ?>">
    513513
    514514<?php wp_editor( $post->post_content, 'content', array(
    515         'dfw' => true,
    516515        'drag_drop_upload' => true,
    517516        'tabfocus_elements' => 'content-html,save-post',
    518517        'editor_height' => 300,
  • src/wp-admin/js/editor-expand.js

     
    1 /* global tinymce */
     1( function( window, $, undefined ) {
     2        'use strict';
    23
    3 window.wp = window.wp || {};
    4 
    5 jQuery( document ).ready( function( $ ) {
    64        var $window = $( window ),
    75                $document = $( document ),
    86                $adminBar = $( '#wpadminbar' ),
    9                 $footer = $( '#wpfooter' ),
    10                 $wrap = $( '#postdivrich' ),
    11                 $contentWrap = $( '#wp-content-wrap' ),
    12                 $tools = $( '#wp-content-editor-tools' ),
    13                 $visualTop = $(),
    14                 $visualEditor = $(),
    15                 $textTop = $( '#ed_toolbar' ),
    16                 $textEditor = $( '#content' ),
    17                 $textEditorClone = $( '<div id="content-textarea-clone"></div>' ),
    18                 $bottom = $( '#post-status-info' ),
    19                 $menuBar = $(),
    20                 $statusBar = $(),
    21                 $sideSortables = $( '#side-sortables' ),
    22                 $postboxContainer = $( '#postbox-container-1' ),
    23                 $postBody = $('#post-body'),
    24                 fullscreen = window.wp.editor && window.wp.editor.fullscreen,
    25                 mceEditor,
    26                 mceBind = function(){},
    27                 mceUnbind = function(){},
    28                 fixedTop = false,
    29                 fixedBottom = false,
    30                 fixedSideTop = false,
    31                 fixedSideBottom = false,
    32                 scrollTimer,
    33                 lastScrollPosition = 0,
    34                 pageYOffsetAtTop = 130,
    35                 pinnedToolsTop = 56,
    36                 sidebarBottom = 20,
    37                 autoresizeMinHeight = 300,
    38                 initialMode = window.getUserSetting( 'editor' ),
    39                 // These are corrected when adjust() runs, except on scrolling if already set.
    40                 heights = {
    41                         windowHeight: 0,
    42                         windowWidth: 0,
    43                         adminBarHeight: 0,
    44                         toolsHeight: 0,
    45                         menuBarHeight: 0,
    46                         visualTopHeight: 0,
    47                         textTopHeight: 0,
    48                         bottomHeight: 0,
    49                         statusBarHeight: 0,
    50                         sideSortablesHeight: 0
    51                 };
    52 
    53         $textEditorClone.insertAfter( $textEditor );
    54 
    55         $textEditorClone.css( {
    56                 'font-family': $textEditor.css( 'font-family' ),
    57                 'font-size': $textEditor.css( 'font-size' ),
    58                 'line-height': $textEditor.css( 'line-height' ),
    59                 'white-space': 'pre-wrap',
    60                 'word-wrap': 'break-word'
    61         } );
    62 
    63         function getHeights() {
    64                 var windowWidth = $window.width();
    65 
    66                 heights = {
    67                         windowHeight: $window.height(),
    68                         windowWidth: windowWidth,
    69                         adminBarHeight: ( windowWidth > 600 ? $adminBar.outerHeight() : 0 ),
    70                         toolsHeight: $tools.outerHeight() || 0,
    71                         menuBarHeight: $menuBar.outerHeight() || 0,
    72                         visualTopHeight: $visualTop.outerHeight() || 0,
    73                         textTopHeight: $textTop.outerHeight() || 0,
    74                         bottomHeight: $bottom.outerHeight() || 0,
    75                         statusBarHeight: $statusBar.outerHeight() || 0,
    76                         sideSortablesHeight: $sideSortables.height() || 0
    77                 };
    78 
    79                 // Adjust for hidden
    80                 if ( heights.menuBarHeight < 3 ) {
    81                         heights.menuBarHeight = 0;
    82                 }
    83         }
    84 
    85         function textEditorKeyup( event ) {
    86                 var VK = jQuery.ui.keyCode,
    87                         key = event.keyCode,
    88                         range = document.createRange(),
    89                         selStart = $textEditor[0].selectionStart,
    90                         selEnd = $textEditor[0].selectionEnd,
    91                         textNode = $textEditorClone[0].firstChild,
    92                         buffer = 10,
    93                         offset, cursorTop, cursorBottom, editorTop, editorBottom;
     7                $footer = $( '#wpfooter' );
    948
    95                 if ( selStart && selEnd && selStart !== selEnd ) {
    96                         return;
    97                 }
    98 
    99                 // These are not TinyMCE ranges.
    100                 try {
    101                         range.setStart( textNode, selStart );
    102                         range.setEnd( textNode, selEnd + 1 );
    103                 } catch ( ex ) {}
    104 
    105                 offset = range.getBoundingClientRect();
    106 
    107                 if ( ! offset.height ) {
    108                         return;
    109                 }
    110 
    111                 cursorTop = offset.top - buffer;
    112                 cursorBottom = cursorTop + offset.height + buffer;
    113                 editorTop = heights.adminBarHeight + heights.toolsHeight + heights.textTopHeight;
    114                 editorBottom = heights.windowHeight - heights.bottomHeight;
     9        /* Autoresize editor. */
     10        $( function() {
     11                var $wrap = $( '#postdivrich' ),
     12                        $contentWrap = $( '#wp-content-wrap' ),
     13                        $tools = $( '#wp-content-editor-tools' ),
     14                        $visualTop = $(),
     15                        $visualEditor = $(),
     16                        $textTop = $( '#ed_toolbar' ),
     17                        $textEditor = $( '#content' ),
     18                        $textEditorClone = $( '<div id="content-textarea-clone"></div>' ),
     19                        $bottom = $( '#post-status-info' ),
     20                        $menuBar = $(),
     21                        $statusBar = $(),
     22                        $sideSortables = $( '#side-sortables' ),
     23                        $postboxContainer = $( '#postbox-container-1' ),
     24                        $postBody = $('#post-body'),
     25                        fullscreen = window.wp.editor && window.wp.editor.fullscreen,
     26                        mceEditor,
     27                        mceBind = function(){},
     28                        mceUnbind = function(){},
     29                        fixedTop = false,
     30                        fixedBottom = false,
     31                        fixedSideTop = false,
     32                        fixedSideBottom = false,
     33                        scrollTimer,
     34                        lastScrollPosition = 0,
     35                        pageYOffsetAtTop = 130,
     36                        pinnedToolsTop = 56,
     37                        sidebarBottom = 20,
     38                        autoresizeMinHeight = 300,
     39                        initialMode = window.getUserSetting( 'editor' ),
     40                        advanced = !! parseInt( window.getUserSetting( 'hidetb' ), 10 ),
     41                        // These are corrected when adjust() runs, except on scrolling if already set.
     42                        heights = {
     43                                windowHeight: 0,
     44                                windowWidth: 0,
     45                                adminBarHeight: 0,
     46                                toolsHeight: 0,
     47                                menuBarHeight: 0,
     48                                visualTopHeight: 0,
     49                                textTopHeight: 0,
     50                                bottomHeight: 0,
     51                                statusBarHeight: 0,
     52                                sideSortablesHeight: 0
     53                        };
     54
     55                $textEditorClone.insertAfter( $textEditor );
     56
     57                $textEditorClone.css( {
     58                        'font-family': $textEditor.css( 'font-family' ),
     59                        'font-size': $textEditor.css( 'font-size' ),
     60                        'line-height': $textEditor.css( 'line-height' ),
     61                        'white-space': 'pre-wrap',
     62                        'word-wrap': 'break-word'
     63                } );
    11564
    116                 if ( cursorTop < editorTop && ( key === VK.UP || key === VK.LEFT || key === VK.BACKSPACE ) ) {
    117                         window.scrollTo( window.pageXOffset, cursorTop + window.pageYOffset - editorTop );
    118                 } else if ( cursorBottom > editorBottom ) {
    119                         window.scrollTo( window.pageXOffset, cursorBottom + window.pageYOffset - editorBottom );
    120                 }
    121         }
     65                function getHeights() {
     66                        var windowWidth = $window.width();
    12267
    123         function textEditorResize() {
    124                 if ( ( mceEditor && ! mceEditor.isHidden() ) || ( ! mceEditor && initialMode === 'tinymce' ) ) {
    125                         return;
     68                        heights = {
     69                                windowHeight: $window.height(),
     70                                windowWidth: windowWidth,
     71                                adminBarHeight: ( windowWidth > 600 ? $adminBar.outerHeight() : 0 ),
     72                                toolsHeight: $tools.outerHeight() || 0,
     73                                menuBarHeight: $menuBar.outerHeight() || 0,
     74                                visualTopHeight: $visualTop.outerHeight() || 0,
     75                                textTopHeight: $textTop.outerHeight() || 0,
     76                                bottomHeight: $bottom.outerHeight() || 0,
     77                                statusBarHeight: $statusBar.outerHeight() || 0,
     78                                sideSortablesHeight: $sideSortables.height() || 0
     79                        };
     80
     81                        // Adjust for hidden
     82                        if ( heights.menuBarHeight < 3 ) {
     83                                heights.menuBarHeight = 0;
     84                        }
    12685                }
    12786
    128                 var textEditorHeight = $textEditor.height(),
    129                         hiddenHeight;
    130 
    131                 $textEditorClone.width( $textEditor.width() - 22 );
    132                 $textEditorClone.text( $textEditor.val() + '&nbsp;' );
    133 
    134                 hiddenHeight = $textEditorClone.height();
     87                function textEditorKeyup( event ) {
     88                        var VK = jQuery.ui.keyCode,
     89                                key = event.keyCode,
     90                                range = document.createRange(),
     91                                selStart = $textEditor[0].selectionStart,
     92                                selEnd = $textEditor[0].selectionEnd,
     93                                textNode = $textEditorClone[0].firstChild,
     94                                buffer = 10,
     95                                offset, cursorTop, cursorBottom, editorTop, editorBottom;
    13596
    136                 if ( hiddenHeight < autoresizeMinHeight ) {
    137                         hiddenHeight = autoresizeMinHeight;
    138                 }
     97                        if ( selStart && selEnd && selStart !== selEnd ) {
     98                                return;
     99                        }
    139100
    140                 if ( hiddenHeight === textEditorHeight ) {
    141                         return;
    142                 }
     101                        // These are not TinyMCE ranges.
     102                        try {
     103                                range.setStart( textNode, selStart );
     104                                range.setEnd( textNode, selEnd + 1 );
     105                        } catch ( ex ) {}
    143106
    144                 $textEditor.height( hiddenHeight );
     107                        offset = range.getBoundingClientRect();
    145108
    146                 adjust();
    147         }
     109                        if ( ! offset.height ) {
     110                                return;
     111                        }
    148112
    149         // We need to wait for TinyMCE to initialize.
    150         $document.on( 'tinymce-editor-init.editor-expand', function( event, editor ) {
    151                 var hideFloatPanels = _.debounce( function() {
    152                         ! $( '.mce-floatpanel:hover' ).length && tinymce.ui.FloatPanel.hideAll();
    153                         $( '.mce-tooltip' ).hide();
    154                 }, 1000, true );
     113                        cursorTop = offset.top - buffer;
     114                        cursorBottom = cursorTop + offset.height + buffer;
     115                        editorTop = heights.adminBarHeight + heights.toolsHeight + heights.textTopHeight;
     116                        editorBottom = heights.windowHeight - heights.bottomHeight;
    155117
    156                 // Make sure it's the main editor.
    157                 if ( editor.id !== 'content' ) {
    158                         return;
     118                        if ( cursorTop < editorTop && ( key === VK.UP || key === VK.LEFT || key === VK.BACKSPACE ) ) {
     119                                window.scrollTo( window.pageXOffset, cursorTop + window.pageYOffset - editorTop );
     120                        } else if ( cursorBottom > editorBottom ) {
     121                                window.scrollTo( window.pageXOffset, cursorBottom + window.pageYOffset - editorBottom );
     122                        }
    159123                }
    160124
    161                 // Copy the editor instance.
    162                 mceEditor = editor;
    163 
    164                 // Set the minimum height to the initial viewport height.
    165                 editor.settings.autoresize_min_height = autoresizeMinHeight;
     125                function textEditorResize() {
     126                        if ( ( mceEditor && ! mceEditor.isHidden() ) || ( ! mceEditor && initialMode === 'tinymce' ) ) {
     127                                return;
     128                        }
    166129
    167                 // Get the necessary UI elements.
    168                 $visualTop = $contentWrap.find( '.mce-toolbar-grp' );
    169                 $visualEditor = $contentWrap.find( '.mce-edit-area' );
    170                 $statusBar = $contentWrap.find( '.mce-statusbar' );
    171                 $menuBar = $contentWrap.find( '.mce-menubar' );
     130                        var textEditorHeight = $textEditor.height(),
     131                                hiddenHeight;
    172132
    173                 function mceGetCursorOffset() {
    174                         var node = editor.selection.getNode(),
    175                                 range, view, offset;
     133                        $textEditorClone.width( $textEditor.width() - 22 );
     134                        $textEditorClone.text( $textEditor.val() + '&nbsp;' );
    176135
    177                         if ( editor.plugins.wpview && ( view = editor.plugins.wpview.getView( node ) ) ) {
    178                                 offset = view.getBoundingClientRect();
    179                         } else {
    180                                 range = editor.selection.getRng();
     136                        hiddenHeight = $textEditorClone.height();
    181137
    182                                 try {
    183                                         offset = range.getClientRects()[0];
    184                                 } catch( er ) {}
     138                        if ( hiddenHeight < autoresizeMinHeight ) {
     139                                hiddenHeight = autoresizeMinHeight;
     140                        }
    185141
    186                                 if ( ! offset ) {
    187                                         offset = node.getBoundingClientRect();
    188                                 }
     142                        if ( hiddenHeight === textEditorHeight ) {
     143                                return;
    189144                        }
    190145
    191                         return offset.height ? offset : false;
    192                 }
     146                        $textEditor.height( hiddenHeight );
    193147
    194                 // Make sure the cursor is always visible.
    195                 // This is not only necessary to keep the cursor between the toolbars,
    196                 // but also to scroll the window when the cursor moves out of the viewport to a wpview.
    197                 // Setting a buffer > 0 will prevent the browser default.
    198                 // Some browsers will scroll to the middle,
    199                 // others to the top/bottom of the *window* when moving the cursor out of the viewport.
    200                 function mceKeyup( event ) {
    201                         var VK = tinymce.util.VK,
    202                                 key = event.keyCode;
     148                        adjust();
     149                }
    203150
    204                         // Bail on special keys.
    205                         if ( key <= 47 && ! ( key === VK.SPACEBAR || key === VK.ENTER || key === VK.DELETE || key === VK.BACKSPACE ||
    206                                 key === VK.UP || key === VK.RIGHT || key === VK.DOWN || key === VK.LEFT ) ) {
     151                // We need to wait for TinyMCE to initialize.
     152                $document.on( 'tinymce-editor-init.editor-expand', function( event, editor ) {
     153                        var VK = window.tinymce.util.VK,
     154                                hideFloatPanels = _.debounce( function() {
     155                                        ! $( '.mce-floatpanel:hover' ).length && window.tinymce.ui.FloatPanel.hideAll();
     156                                        $( '.mce-tooltip' ).hide();
     157                                }, 1000, true );
    207158
    208                                 return;
    209                         // OS keys, function keys, num lock, scroll lock
    210                         } else if ( ( key >= 91 && key <= 93 ) || ( key >= 112 && key <= 123 ) || key === 144 || key === 145 ) {
     159                        // Make sure it's the main editor.
     160                        if ( editor.id !== 'content' ) {
    211161                                return;
    212162                        }
    213163
    214                         mceScroll( key );
    215                 }
     164                        // Copy the editor instance.
     165                        mceEditor = editor;
    216166
    217                 function mceScroll( key ) {
    218                         var VK = tinymce.util.VK,
    219                                 offset = mceGetCursorOffset(),
    220                                 buffer = 10,
    221                                 cursorTop, cursorBottom, editorTop, editorBottom;
     167                        // Set the minimum height to the initial viewport height.
     168                        editor.settings.autoresize_min_height = autoresizeMinHeight;
    222169
    223                         if ( ! offset ) {
    224                                 return;
    225                         }
     170                        // Get the necessary UI elements.
     171                        $visualTop = $contentWrap.find( '.mce-toolbar-grp' );
     172                        $visualEditor = $contentWrap.find( '.mce-edit-area' );
     173                        $statusBar = $contentWrap.find( '.mce-statusbar' );
     174                        $menuBar = $contentWrap.find( '.mce-menubar' );
     175
     176                        function mceGetCursorOffset() {
     177                                var node = editor.selection.getNode(),
     178                                        range, view, offset;
    226179
    227                         cursorTop = offset.top + editor.iframeElement.getBoundingClientRect().top;
    228                         cursorBottom = cursorTop + offset.height + buffer;
    229                         cursorTop -= buffer;
    230                         editorTop = heights.adminBarHeight + heights.toolsHeight + heights.menuBarHeight + heights.visualTopHeight;
    231                         editorBottom = heights.windowHeight - heights.bottomHeight - heights.statusBarHeight;
     180                                if ( editor.plugins.wpview && ( view = editor.plugins.wpview.getView( node ) ) ) {
     181                                        offset = view.getBoundingClientRect();
     182                                } else {
     183                                        range = editor.selection.getRng();
    232184
    233                         // Don't scroll if the node is taller than the visible part of the editor
    234                         if ( editorBottom - editorTop < offset.height ) {
    235                                 return;
    236                         }
     185                                        try {
     186                                                offset = range.getClientRects()[0];
     187                                        } catch( er ) {}
    237188
    238                         // WebKit browsers scroll-into-view to the middle of the window but not for arrow keys/backspace.
    239                         // The others scroll to the top of the window, we need to account for the adminbar and editor toolbar(s).
    240                         if ( cursorTop < editorTop && ( ! tinymce.Env.webkit ||
    241                                 ( key === VK.UP || key === VK.RIGHT || key === VK.DOWN || key === VK.LEFT || key === VK.BACKSPACE ) ) ) {
     189                                        if ( ! offset ) {
     190                                                offset = node.getBoundingClientRect();
     191                                        }
     192                                }
    242193
    243                                 window.scrollTo( window.pageXOffset, cursorTop + window.pageYOffset - editorTop );
    244                         } else if ( cursorBottom > editorBottom ) {
    245                                 window.scrollTo( window.pageXOffset, cursorBottom + window.pageYOffset - editorBottom );
     194                                return offset.height ? offset : false;
    246195                        }
    247                 }
    248196
    249                 // Adjust when switching editor modes.
    250                 function mceShow() {
    251                         $window.on( 'scroll.mce-float-panels', hideFloatPanels );
     197                        // Make sure the cursor is always visible.
     198                        // This is not only necessary to keep the cursor between the toolbars,
     199                        // but also to scroll the window when the cursor moves out of the viewport to a wpview.
     200                        // Setting a buffer > 0 will prevent the browser default.
     201                        // Some browsers will scroll to the middle,
     202                        // others to the top/bottom of the *window* when moving the cursor out of the viewport.
     203                        function mceKeyup( event ) {
     204                                var key = event.keyCode;
     205
     206                                // Bail on special keys.
     207                                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 ) ) {
     208                                        return;
     209                                // OS keys, function keys, num lock, scroll lock
     210                                } else if ( ( key >= 91 && key <= 93 ) || ( key >= 112 && key <= 123 ) || key === 144 || key === 145 ) {
     211                                        return;
     212                                }
    252213
    253                         setTimeout( function() {
    254                                 editor.execCommand( 'wpAutoResize' );
    255                                 adjust();
    256                         }, 300 );
    257                 }
     214                                mceScroll( key );
     215                        }
    258216
    259                 function mceHide() {
    260                         $window.off( 'scroll.mce-float-panels' );
     217                        function mceScroll( key ) {
     218                                var offset = mceGetCursorOffset(),
     219                                        buffer = 50,
     220                                        cursorTop, cursorBottom, editorTop, editorBottom;
    261221
    262                         setTimeout( function() {
    263                                 var top = $contentWrap.offset().top;
     222                                if ( ! offset ) {
     223                                        return;
     224                                }
    264225
    265                                 if ( window.pageYOffset > top ) {
    266                                         window.scrollTo( window.pageXOffset, top - heights.adminBarHeight );
     226                                cursorTop = offset.top + editor.iframeElement.getBoundingClientRect().top;
     227                                cursorBottom = cursorTop + offset.height;
     228                                cursorTop = cursorTop - buffer;
     229                                cursorBottom = cursorBottom + buffer;
     230                                editorTop = heights.adminBarHeight + heights.toolsHeight + heights.menuBarHeight + heights.visualTopHeight;
     231                                editorBottom = heights.windowHeight - ( advanced ? heights.bottomHeight + heights.statusBarHeight : 0 );
     232
     233                                // Don't scroll if the node is taller than the visible part of the editor
     234                                if ( editorBottom - editorTop < offset.height ) {
     235                                        return;
    267236                                }
    268237
    269                                 textEditorResize();
    270                                 adjust();
    271                         }, 100 );
     238                                if ( cursorTop < editorTop && ( key === VK.UP || key === VK.LEFT || key === VK.BACKSPACE ) ) {
     239                                        window.scrollTo( window.pageXOffset, cursorTop + window.pageYOffset - editorTop );
     240                                } else if ( cursorBottom > editorBottom ) {
     241                                        window.scrollTo( window.pageXOffset, cursorBottom + window.pageYOffset - editorBottom );
     242                                }
     243                        }
    272244
    273                         adjust();
    274                 }
     245                        // Adjust when switching editor modes.
     246                        function mceShow() {
     247                                $window.on( 'scroll.mce-float-panels', hideFloatPanels );
    275248
    276                 mceBind = function() {
    277                         editor.on( 'keyup', mceKeyup );
    278                         editor.on( 'show', mceShow );
    279                         editor.on( 'hide', mceHide );
    280                         // Adjust when the editor resizes.
    281                         editor.on( 'setcontent wp-autoresize wp-toolbar-toggle', adjust );
    282                         // Scroll to the caret or selection after undo/redo
    283                         editor.on( 'undo redo', mceScroll );
     249                                setTimeout( function() {
     250                                        editor.execCommand( 'wpAutoResize' );
     251                                        adjust();
     252                                }, 300 );
     253                        }
    284254
    285                         $window.off( 'scroll.mce-float-panels' ).on( 'scroll.mce-float-panels', hideFloatPanels );
    286                 };
     255                        function mceHide() {
     256                                $window.off( 'scroll.mce-float-panels' );
    287257
    288                 mceUnbind = function() {
    289                         editor.off( 'keyup', mceKeyup );
    290                         editor.off( 'show', mceShow );
    291                         editor.off( 'hide', mceHide );
    292                         editor.off( 'setcontent wp-autoresize wp-toolbar-toggle', adjust );
    293                         editor.off( 'undo redo', mceScroll );
     258                                setTimeout( function() {
     259                                        var top = $contentWrap.offset().top;
    294260
    295                         $window.off( 'scroll.mce-float-panels' );
    296                 };
     261                                        if ( window.pageYOffset > top ) {
     262                                                window.scrollTo( window.pageXOffset, top - heights.adminBarHeight );
     263                                        }
    297264
    298                 if ( $wrap.hasClass( 'wp-editor-expand' ) ) {
    299                         // Adjust "immediately"
    300                         mceBind();
    301                         initialResize( adjust );
    302                 }
    303         } );
     265                                        textEditorResize();
     266                                        adjust();
     267                                }, 100 );
    304268
    305         // Adjust the toolbars based on the active editor mode.
    306         function adjust( type ) {
    307                 // Make sure we're not in fullscreen mode.
    308                 if ( fullscreen && fullscreen.settings.visible ) {
    309                         return;
    310                 }
    311 
    312                 var windowPos = $window.scrollTop(),
    313                         resize = type !== 'scroll',
    314                         visual = ( mceEditor && ! mceEditor.isHidden() ),
    315                         buffer = autoresizeMinHeight,
    316                         postBodyTop = $postBody.offset().top,
    317                         borderWidth = 1,
    318                         contentWrapWidth = $contentWrap.width(),
    319                         $top, $editor, sidebarTop, footerTop, canPin,
    320                         topPos, topHeight, editorPos, editorHeight;
    321 
    322                 // Refresh the heights
    323                 if ( resize || ! heights.windowHeight ) {
    324                         getHeights();
    325                 }
     269                                adjust();
     270                        }
    326271
    327                 if ( ! visual && type === 'resize' ) {
    328                         textEditorResize();
    329                 }
     272                        function toggleAdvanced() {
     273                                advanced = ! advanced;
     274                        }
    330275
    331                 if ( visual ) {
    332                         $top = $visualTop;
    333                         $editor = $visualEditor;
    334                         topHeight = heights.visualTopHeight;
    335                 } else {
    336                         $top = $textTop;
    337                         $editor = $textEditor;
    338                         topHeight = heights.textTopHeight;
    339                 }
    340 
    341                 topPos = $top.parent().offset().top;
    342                 editorPos = $editor.offset().top;
    343                 editorHeight = $editor.outerHeight();
    344 
    345                 // Should we pin?
    346                 canPin = visual ? autoresizeMinHeight + topHeight : autoresizeMinHeight + 20; // 20px from textarea padding
    347                 canPin = editorHeight > ( canPin + 5 );
     276                        mceBind = function() {
     277                                editor.on( 'keyup', mceKeyup );
     278                                editor.on( 'show', mceShow );
     279                                editor.on( 'hide', mceHide );
     280                                editor.on( 'wp-toolbar-toggle', toggleAdvanced );
     281                                // Adjust when the editor resizes.
     282                                editor.on( 'setcontent wp-autoresize wp-toolbar-toggle', adjust );
     283                                // Don't hide the caret after undo/redo
     284                                editor.on( 'undo redo', mceScroll );
     285
     286                                $window.off( 'scroll.mce-float-panels' ).on( 'scroll.mce-float-panels', hideFloatPanels );
     287                        };
     288
     289                        mceUnbind = function() {
     290                                editor.off( 'keyup', mceKeyup );
     291                                editor.off( 'show', mceShow );
     292                                editor.off( 'hide', mceHide );
     293                                editor.off( 'wp-toolbar-toggle', toggleAdvanced );
     294                                editor.off( 'setcontent wp-autoresize wp-toolbar-toggle', adjust );
     295                                editor.off( 'undo redo', mceScroll );
     296
     297                                $window.off( 'scroll.mce-float-panels' );
     298                        };
     299
     300                        if ( $wrap.hasClass( 'wp-editor-expand' ) ) {
     301                                // Adjust "immediately"
     302                                mceBind();
     303                                initialResize( adjust );
     304                        }
     305                } );
    348306
    349                 if ( ! canPin ) {
    350                         if ( resize ) {
    351                                 $tools.css( {
    352                                         position: 'absolute',
    353                                         top: 0,
    354                                         width: contentWrapWidth
    355                                 } );
     307                // Adjust the toolbars based on the active editor mode.
     308                function adjust( event ) {
     309                        var type = event && event.type;
    356310
    357                                 if ( visual && $menuBar.length ) {
    358                                         $menuBar.css( {
    359                                                 position: 'absolute',
    360                                                 top: 0,
    361                                                 width: contentWrapWidth - ( borderWidth * 2 )
    362                                         } );
    363                                 }
     311                        // Make sure we're not in fullscreen mode.
     312                        if ( fullscreen && fullscreen.settings.visible ) {
     313                                return;
     314                        }
    364315
    365                                 $top.css( {
    366                                         position: 'absolute',
    367                                         top: heights.menuBarHeight,
    368                                         width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
    369                                 } );
     316                        var windowPos = $window.scrollTop(),
     317                                resize = type !== 'scroll',
     318                                visual = ( mceEditor && ! mceEditor.isHidden() ),
     319                                buffer = autoresizeMinHeight,
     320                                postBodyTop = $postBody.offset().top,
     321                                borderWidth = 1,
     322                                contentWrapWidth = $contentWrap.width(),
     323                                $top, $editor, sidebarTop, footerTop, canPin,
     324                                topPos, topHeight, editorPos, editorHeight;
     325
     326                        // Refresh the heights
     327                        if ( resize || ! heights.windowHeight ) {
     328                                getHeights();
     329                        }
    370330
    371                                 $statusBar.add( $bottom ).attr( 'style', '' );
     331                        if ( ! visual && type === 'resize' ) {
     332                                textEditorResize();
    372333                        }
    373                 } else {
    374                         // Maybe pin the top.
    375                         if ( ( ! fixedTop || resize ) &&
    376                                 // Handle scrolling down.
    377                                 ( windowPos >= ( topPos - heights.toolsHeight - heights.adminBarHeight ) &&
    378                                 // Handle scrolling up.
    379                                 windowPos <= ( topPos - heights.toolsHeight - heights.adminBarHeight + editorHeight - buffer ) ) ) {
    380                                 fixedTop = true;
    381 
    382                                 $tools.css( {
    383                                         position: 'fixed',
    384                                         top: heights.adminBarHeight,
    385                                         width: contentWrapWidth
    386                                 } );
    387334
    388                                 if ( visual && $menuBar.length ) {
    389                                         $menuBar.css( {
    390                                                 position: 'fixed',
    391                                                 top: heights.adminBarHeight + heights.toolsHeight,
    392                                                 width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
    393                                         } );
    394                                 }
     335                        if ( visual ) {
     336                                $top = $visualTop;
     337                                $editor = $visualEditor;
     338                                topHeight = heights.visualTopHeight;
     339                        } else {
     340                                $top = $textTop;
     341                                $editor = $textEditor;
     342                                topHeight = heights.textTopHeight;
     343                        }
    395344
    396                                 $top.css( {
    397                                         position: 'fixed',
    398                                         top: heights.adminBarHeight + heights.toolsHeight + heights.menuBarHeight,
    399                                         width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
    400                                 } );
    401                         // Maybe unpin the top.
    402                         } else if ( fixedTop || resize ) {
    403                                 // Handle scrolling up.
    404                                 if ( windowPos <= ( topPos - heights.toolsHeight - heights.adminBarHeight ) ) {
    405                                         fixedTop = false;
     345                        topPos = $top.parent().offset().top;
     346                        editorPos = $editor.offset().top;
     347                        editorHeight = $editor.outerHeight();
     348
     349                        // Should we pin?
     350                        canPin = visual ? autoresizeMinHeight + topHeight : autoresizeMinHeight + 20; // 20px from textarea padding
     351                        canPin = editorHeight > ( canPin + 5 );
    406352
     353                        if ( ! canPin ) {
     354                                if ( resize ) {
    407355                                        $tools.css( {
    408356                                                position: 'absolute',
    409357                                                top: 0,
     
    423371                                                top: heights.menuBarHeight,
    424372                                                width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
    425373                                        } );
    426                                 // Handle scrolling down.
    427                                 } else if ( windowPos >= ( topPos - heights.toolsHeight - heights.adminBarHeight + editorHeight - buffer ) ) {
    428                                         fixedTop = false;
     374
     375                                        $statusBar.add( $bottom ).attr( 'style', '' );
     376                                }
     377                        } else {
     378                                // Maybe pin the top.
     379                                if ( ( ! fixedTop || resize ) &&
     380                                        // Handle scrolling down.
     381                                        ( windowPos >= ( topPos - heights.toolsHeight - heights.adminBarHeight ) &&
     382                                        // Handle scrolling up.
     383                                        windowPos <= ( topPos - heights.toolsHeight - heights.adminBarHeight + editorHeight - buffer ) ) ) {
     384                                        fixedTop = true;
    429385
    430386                                        $tools.css( {
    431                                                 position: 'absolute',
    432                                                 top: editorHeight - buffer,
     387                                                position: 'fixed',
     388                                                top: heights.adminBarHeight,
    433389                                                width: contentWrapWidth
    434390                                        } );
    435391
    436392                                        if ( visual && $menuBar.length ) {
    437393                                                $menuBar.css( {
    438                                                         position: 'absolute',
    439                                                         top: editorHeight - buffer,
    440                                                         width: contentWrapWidth - ( borderWidth * 2 )
     394                                                        position: 'fixed',
     395                                                        top: heights.adminBarHeight + heights.toolsHeight,
     396                                                        width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
    441397                                                } );
    442398                                        }
    443399
    444400                                        $top.css( {
    445                                                 position: 'absolute',
    446                                                 top: editorHeight - buffer + heights.menuBarHeight,
     401                                                position: 'fixed',
     402                                                top: heights.adminBarHeight + heights.toolsHeight + heights.menuBarHeight,
    447403                                                width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
    448404                                        } );
    449                                 }
    450                         }
    451 
    452                         // Maybe adjust the bottom bar.
    453                         if ( ( ! fixedBottom || resize ) &&
    454                                 // +[n] for the border around the .wp-editor-container.
    455                                 ( windowPos + heights.windowHeight ) <= ( editorPos + editorHeight + heights.bottomHeight + heights.statusBarHeight + borderWidth ) ) {
    456                                 fixedBottom = true;
    457 
    458                                 $statusBar.css( {
    459                                         position: 'fixed',
    460                                         bottom: heights.bottomHeight,
    461                                         width: contentWrapWidth - ( borderWidth * 2 )
    462                                 } );
     405                                // Maybe unpin the top.
     406                                } else if ( fixedTop || resize ) {
     407                                        // Handle scrolling up.
     408                                        if ( windowPos <= ( topPos - heights.toolsHeight - heights.adminBarHeight ) ) {
     409                                                fixedTop = false;
    463410
    464                                 $bottom.css( {
    465                                         position: 'fixed',
    466                                         bottom: 0,
    467                                         width: contentWrapWidth
    468                                 } );
    469                         } else if ( ( fixedBottom || resize ) &&
    470                                         ( windowPos + heights.windowHeight ) > ( editorPos + editorHeight + heights.bottomHeight + heights.statusBarHeight - borderWidth ) ) {
    471                                 fixedBottom = false;
     411                                                $tools.css( {
     412                                                        position: 'absolute',
     413                                                        top: 0,
     414                                                        width: contentWrapWidth
     415                                                } );
    472416
    473                                 $statusBar.add( $bottom ).attr( 'style', '' );
    474                         }
    475                 }
     417                                                if ( visual && $menuBar.length ) {
     418                                                        $menuBar.css( {
     419                                                                position: 'absolute',
     420                                                                top: 0,
     421                                                                width: contentWrapWidth - ( borderWidth * 2 )
     422                                                        } );
     423                                                }
    476424
    477                 // Sidebar pinning
    478                 if ( $postboxContainer.width() < 300 && heights.windowWidth > 600 && // sidebar position is changed with @media from CSS, make sure it is on the side
    479                         $document.height() > ( $sideSortables.height() + postBodyTop + 120 ) && // the sidebar is not the tallest element
    480                         heights.windowHeight < editorHeight ) { // the editor is taller than the viewport
     425                                                $top.css( {
     426                                                        position: 'absolute',
     427                                                        top: heights.menuBarHeight,
     428                                                        width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
     429                                                } );
     430                                        // Handle scrolling down.
     431                                        } else if ( windowPos >= ( topPos - heights.toolsHeight - heights.adminBarHeight + editorHeight - buffer ) ) {
     432                                                fixedTop = false;
    481433
    482                         if ( ( heights.sideSortablesHeight + pinnedToolsTop + sidebarBottom ) > heights.windowHeight || fixedSideTop || fixedSideBottom ) {
    483                                 // Reset when scrolling to the top
    484                                 if ( windowPos + pinnedToolsTop <= postBodyTop ) {
    485                                         $sideSortables.attr( 'style', '' );
    486                                         fixedSideTop = fixedSideBottom = false;
    487                                 } else {
    488                                         if ( windowPos > lastScrollPosition ) {
    489                                                 // Scrolling down
    490                                                 if ( fixedSideTop ) {
    491                                                         // let it scroll
    492                                                         fixedSideTop = false;
    493                                                         sidebarTop = $sideSortables.offset().top - heights.adminBarHeight;
    494                                                         footerTop = $footer.offset().top;
    495 
    496                                                         // don't get over the footer
    497                                                         if ( footerTop < sidebarTop + heights.sideSortablesHeight + sidebarBottom ) {
    498                                                                 sidebarTop = footerTop - heights.sideSortablesHeight - 12;
    499                                                         }
     434                                                $tools.css( {
     435                                                        position: 'absolute',
     436                                                        top: editorHeight - buffer,
     437                                                        width: contentWrapWidth
     438                                                } );
    500439
    501                                                         $sideSortables.css({
     440                                                if ( visual && $menuBar.length ) {
     441                                                        $menuBar.css( {
    502442                                                                position: 'absolute',
    503                                                                 top: sidebarTop,
    504                                                                 bottom: ''
    505                                                         });
    506                                                 } else if ( ! fixedSideBottom && heights.sideSortablesHeight + $sideSortables.offset().top + sidebarBottom < windowPos + heights.windowHeight ) {
    507                                                         // pin the bottom
    508                                                         fixedSideBottom = true;
    509 
    510                                                         $sideSortables.css({
    511                                                                 position: 'fixed',
    512                                                                 top: 'auto',
    513                                                                 bottom: sidebarBottom
    514                                                         });
     443                                                                top: editorHeight - buffer,
     444                                                                width: contentWrapWidth - ( borderWidth * 2 )
     445                                                        } );
    515446                                                }
    516                                         } else if ( windowPos < lastScrollPosition ) {
    517                                                 // Scrolling up
    518                                                 if ( fixedSideBottom ) {
    519                                                         // let it scroll
    520                                                         fixedSideBottom = false;
    521                                                         sidebarTop = $sideSortables.offset().top - sidebarBottom;
    522                                                         footerTop = $footer.offset().top;
    523 
    524                                                         // don't get over the footer
    525                                                         if ( footerTop < sidebarTop + heights.sideSortablesHeight + sidebarBottom ) {
    526                                                                 sidebarTop = footerTop - heights.sideSortablesHeight - 12;
    527                                                         }
    528447
    529                                                         $sideSortables.css({
    530                                                                 position: 'absolute',
    531                                                                 top: sidebarTop,
    532                                                                 bottom: ''
    533                                                         });
    534                                                 } else if ( ! fixedSideTop && $sideSortables.offset().top >= windowPos + pinnedToolsTop ) {
    535                                                         // pin the top
    536                                                         fixedSideTop = true;
    537 
    538                                                         $sideSortables.css({
    539                                                                 position: 'fixed',
    540                                                                 top: pinnedToolsTop,
    541                                                                 bottom: ''
    542                                                         });
     448                                                $top.css( {
     449                                                        position: 'absolute',
     450                                                        top: editorHeight - buffer + heights.menuBarHeight,
     451                                                        width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
     452                                                } );
     453                                        }
     454                                }
     455
     456                                // Maybe adjust the bottom bar.
     457                                if ( ( ! fixedBottom || ( resize && advanced ) ) &&
     458                                                // +[n] for the border around the .wp-editor-container.
     459                                                ( windowPos + heights.windowHeight ) <= ( editorPos + editorHeight + heights.bottomHeight + heights.statusBarHeight + borderWidth ) ) {
     460
     461                                        if ( event && event.deltaHeight > 0 ) {
     462                                                window.scrollBy( 0, event.deltaHeight );
     463                                        } else if ( advanced ) {
     464                                                fixedBottom = true;
     465
     466                                                $statusBar.css( {
     467                                                        position: 'fixed',
     468                                                        bottom: heights.bottomHeight,
     469                                                        visibility: '',
     470                                                        width: contentWrapWidth - ( borderWidth * 2 )
     471                                                } );
     472
     473                                                $bottom.css( {
     474                                                        position: 'fixed',
     475                                                        bottom: 0,
     476                                                        width: contentWrapWidth
     477                                                } );
     478                                        }
     479                                } else if ( ( ! advanced && fixedBottom ) ||
     480                                                ( ( fixedBottom || resize ) &&
     481                                                ( windowPos + heights.windowHeight ) > ( editorPos + editorHeight + heights.bottomHeight + heights.statusBarHeight - borderWidth ) ) ) {
     482                                        fixedBottom = false;
     483
     484                                        $statusBar.add( $bottom ).attr( 'style', '' );
     485
     486                                        ! advanced && $statusBar.css( 'visibility', 'hidden' );
     487                                }
     488                        }
     489
     490                        // Sidebar pinning
     491                        if ( $postboxContainer.width() < 300 && heights.windowWidth > 600 && // sidebar position is changed with @media from CSS, make sure it is on the side
     492                                $document.height() > ( $sideSortables.height() + postBodyTop + 120 ) && // the sidebar is not the tallest element
     493                                heights.windowHeight < editorHeight ) { // the editor is taller than the viewport
     494
     495                                if ( ( heights.sideSortablesHeight + pinnedToolsTop + sidebarBottom ) > heights.windowHeight || fixedSideTop || fixedSideBottom ) {
     496                                        // Reset when scrolling to the top
     497                                        if ( windowPos + pinnedToolsTop <= postBodyTop ) {
     498                                                $sideSortables.attr( 'style', '' );
     499                                                fixedSideTop = fixedSideBottom = false;
     500                                        } else {
     501                                                if ( windowPos > lastScrollPosition ) {
     502                                                        // Scrolling down
     503                                                        if ( fixedSideTop ) {
     504                                                                // let it scroll
     505                                                                fixedSideTop = false;
     506                                                                sidebarTop = $sideSortables.offset().top - heights.adminBarHeight;
     507                                                                footerTop = $footer.offset().top;
     508
     509                                                                // don't get over the footer
     510                                                                if ( footerTop < sidebarTop + heights.sideSortablesHeight + sidebarBottom ) {
     511                                                                        sidebarTop = footerTop - heights.sideSortablesHeight - 12;
     512                                                                }
     513
     514                                                                $sideSortables.css({
     515                                                                        position: 'absolute',
     516                                                                        top: sidebarTop,
     517                                                                        bottom: ''
     518                                                                });
     519                                                        } else if ( ! fixedSideBottom && heights.sideSortablesHeight + $sideSortables.offset().top + sidebarBottom < windowPos + heights.windowHeight ) {
     520                                                                // pin the bottom
     521                                                                fixedSideBottom = true;
     522
     523                                                                $sideSortables.css({
     524                                                                        position: 'fixed',
     525                                                                        top: 'auto',
     526                                                                        bottom: sidebarBottom
     527                                                                });
     528                                                        }
     529                                                } else if ( windowPos < lastScrollPosition ) {
     530                                                        // Scrolling up
     531                                                        if ( fixedSideBottom ) {
     532                                                                // let it scroll
     533                                                                fixedSideBottom = false;
     534                                                                sidebarTop = $sideSortables.offset().top - sidebarBottom;
     535                                                                footerTop = $footer.offset().top;
     536
     537                                                                // don't get over the footer
     538                                                                if ( footerTop < sidebarTop + heights.sideSortablesHeight + sidebarBottom ) {
     539                                                                        sidebarTop = footerTop - heights.sideSortablesHeight - 12;
     540                                                                }
     541
     542                                                                $sideSortables.css({
     543                                                                        position: 'absolute',
     544                                                                        top: sidebarTop,
     545                                                                        bottom: ''
     546                                                                });
     547                                                        } else if ( ! fixedSideTop && $sideSortables.offset().top >= windowPos + pinnedToolsTop ) {
     548                                                                // pin the top
     549                                                                fixedSideTop = true;
     550
     551                                                                $sideSortables.css({
     552                                                                        position: 'fixed',
     553                                                                        top: pinnedToolsTop,
     554                                                                        bottom: ''
     555                                                                });
     556                                                        }
    543557                                                }
    544558                                        }
     559                                } else {
     560                                        // if the sidebar container is smaller than the viewport, then pin/unpin the top when scrolling
     561                                        if ( windowPos >= ( postBodyTop - pinnedToolsTop ) ) {
     562
     563                                                $sideSortables.css( {
     564                                                        position: 'fixed',
     565                                                        top: pinnedToolsTop
     566                                                } );
     567                                        } else {
     568                                                $sideSortables.attr( 'style', '' );
     569                                        }
     570
     571                                        fixedSideTop = fixedSideBottom = false;
    545572                                }
     573
     574                                lastScrollPosition = windowPos;
    546575                        } else {
    547                                 // if the sidebar container is smaller than the viewport, then pin/unpin the top when scrolling
    548                                 if ( windowPos >= ( postBodyTop - pinnedToolsTop ) ) {
     576                                $sideSortables.attr( 'style', '' );
     577                                fixedSideTop = fixedSideBottom = false;
     578                        }
    549579
    550                                         $sideSortables.css( {
    551                                                 position: 'fixed',
    552                                                 top: pinnedToolsTop
     580                        if ( resize ) {
     581                                $contentWrap.css( {
     582                                        paddingTop: heights.toolsHeight
     583                                } );
     584
     585                                if ( visual ) {
     586                                        $visualEditor.css( {
     587                                                paddingTop: heights.visualTopHeight + heights.menuBarHeight
    553588                                        } );
    554589                                } else {
    555                                         $sideSortables.attr( 'style', '' );
     590                                        $textEditor.css( {
     591                                                marginTop: heights.textTopHeight
     592                                        } );
     593
     594                                        $textEditorClone.width( contentWrapWidth - 20 - ( borderWidth * 2 ) );
    556595                                }
     596                        }
     597                }
    557598
    558                                 fixedSideTop = fixedSideBottom = false;
     599                function fullscreenHide() {
     600                        textEditorResize();
     601                        adjust();
     602                }
     603
     604                function initialResize( callback ) {
     605                        for ( var i = 1; i < 6; i++ ) {
     606                                setTimeout( callback, 500 * i );
    559607                        }
     608                }
    560609
    561                         lastScrollPosition = windowPos;
    562                 } else {
    563                         $sideSortables.attr( 'style', '' );
    564                         fixedSideTop = fixedSideBottom = false;
     610                function afterScroll() {
     611                        clearTimeout( scrollTimer );
     612                        scrollTimer = setTimeout( adjust, 100 );
    565613                }
    566614
    567                 if ( resize ) {
    568                         $contentWrap.css( {
    569                                 paddingTop: heights.toolsHeight
     615                function on() {
     616                        // Scroll to the top when triggering this from JS.
     617                        // Ensures toolbars are pinned properly.
     618                        if ( window.pageYOffset && window.pageYOffset > pageYOffsetAtTop ) {
     619                                window.scrollTo( window.pageXOffset, 0 );
     620                        }
     621
     622                        $wrap.addClass( 'wp-editor-expand' );
     623
     624                        // Adjust when the window is scrolled or resized.
     625                        $window.on( 'scroll.editor-expand resize.editor-expand', function( event ) {
     626                                adjust( event.type );
     627                                afterScroll();
    570628                        } );
    571629
    572                         if ( visual ) {
    573                                 $visualEditor.css( {
    574                                         paddingTop: heights.visualTopHeight + heights.menuBarHeight
    575                                 } );
    576                         } else {
    577                                 $textEditor.css( {
    578                                         marginTop: heights.textTopHeight
    579                                 } );
     630                        // Adjust when collapsing the menu, changing the columns, changing the body class.
     631                        $document.on( 'wp-collapse-menu.editor-expand postboxes-columnchange.editor-expand editor-classchange.editor-expand', adjust )
     632                                .on( 'postbox-toggled.editor-expand', function() {
     633                                        if ( ! fixedSideTop && ! fixedSideBottom && window.pageYOffset > pinnedToolsTop ) {
     634                                                fixedSideBottom = true;
     635                                                window.scrollBy( 0, -1 );
     636                                                adjust();
     637                                                window.scrollBy( 0, 1 );
     638                                        }
    580639
    581                                 $textEditorClone.width( contentWrapWidth - 20 - ( borderWidth * 2 ) );
     640                                        adjust();
     641                                }).on( 'wp-window-resized.editor-expand', function() {
     642                                        if ( mceEditor && ! mceEditor.isHidden() ) {
     643                                                mceEditor.execCommand( 'wpAutoResize' );
     644                                        } else {
     645                                                textEditorResize();
     646                                        }
     647                                });
     648
     649                        $textEditor.on( 'focus.editor-expand input.editor-expand propertychange.editor-expand', textEditorResize );
     650                        $textEditor.on( 'keyup.editor-expand', textEditorKeyup );
     651                        mceBind();
     652
     653                        // Adjust when entering/exiting fullscreen mode.
     654                        fullscreen && fullscreen.pubsub.subscribe( 'hidden', fullscreenHide );
     655
     656                        if ( mceEditor ) {
     657                                mceEditor.settings.wp_autoresize_on = true;
     658                                mceEditor.execCommand( 'wpAutoResizeOn' );
     659
     660                                if ( ! mceEditor.isHidden() ) {
     661                                        mceEditor.execCommand( 'wpAutoResize' );
     662                                }
     663                        }
     664
     665                        if ( ! mceEditor || mceEditor.isHidden() ) {
     666                                textEditorResize();
    582667                        }
     668
     669                        adjust();
     670
     671                        $document.trigger( 'editor-expand-on' );
    583672                }
    584         }
    585673
    586         function fullscreenHide() {
    587                 textEditorResize();
    588                 adjust();
    589         }
     674                function off() {
     675                        var height = window.getUserSetting('ed_size');
     676
     677                        // Scroll to the top when triggering this from JS.
     678                        // Ensures toolbars are reset properly.
     679                        if ( window.pageYOffset && window.pageYOffset > pageYOffsetAtTop ) {
     680                                window.scrollTo( window.pageXOffset, 0 );
     681                        }
     682
     683                        $wrap.removeClass( 'wp-editor-expand' );
     684
     685                        $window.off( '.editor-expand' );
     686                        $document.off( '.editor-expand' );
     687                        $textEditor.off( '.editor-expand' );
     688                        mceUnbind();
     689
     690                        // Adjust when entering/exiting fullscreen mode.
     691                        fullscreen && fullscreen.pubsub.unsubscribe( 'hidden', fullscreenHide );
     692
     693                        // Reset all css
     694                        $.each( [ $visualTop, $textTop, $tools, $menuBar, $bottom, $statusBar, $contentWrap, $visualEditor, $textEditor, $sideSortables ], function( i, element ) {
     695                                element && element.attr( 'style', '' );
     696                        });
     697
     698                        fixedTop = fixedBottom = fixedSideTop = fixedSideBottom = false;
     699
     700                        if ( mceEditor ) {
     701                                mceEditor.settings.wp_autoresize_on = false;
     702                                mceEditor.execCommand( 'wpAutoResizeOff' );
    590703
    591         function initialResize( callback ) {
    592                 for ( var i = 1; i < 6; i++ ) {
    593                         setTimeout( callback, 500 * i );
     704                                if ( ! mceEditor.isHidden() ) {
     705                                        $textEditor.hide();
     706
     707                                        if ( height ) {
     708                                                mceEditor.theme.resizeTo( null, height );
     709                                        }
     710                                }
     711                        }
     712
     713                        if ( height ) {
     714                                $textEditor.height( height );
     715                        }
     716
     717                        $document.trigger( 'editor-expand-off' );
    594718                }
    595         }
    596719
    597         function afterScroll() {
    598                 clearTimeout( scrollTimer );
    599                 scrollTimer = setTimeout( adjust, 100 );
    600         }
     720                // Start on load
     721                if ( $wrap.hasClass( 'wp-editor-expand' ) ) {
     722                        on();
    601723
    602         function on() {
    603                 // Scroll to the top when triggering this from JS.
    604                 // Ensures toolbars are pinned properly.
    605                 if ( window.pageYOffset && window.pageYOffset > pageYOffsetAtTop ) {
    606                         window.scrollTo( window.pageXOffset, 0 );
     724                        // Ideally we need to resize just after CSS has fully loaded and QuickTags is ready.
     725                        if ( $contentWrap.hasClass( 'html-active' ) ) {
     726                                initialResize( function() {
     727                                        adjust();
     728                                        textEditorResize();
     729                                } );
     730                        }
    607731                }
    608732
    609                 $wrap.addClass( 'wp-editor-expand' );
     733                // Show the on/off checkbox
     734                $( '#adv-settings .editor-expand' ).show();
     735                $( '#editor-expand-toggle' ).on( 'change.editor-expand', function() {
     736                        if ( $(this).prop( 'checked' ) ) {
     737                                on();
     738                                window.setUserSetting( 'editor_expand', 'on' );
     739                        } else {
     740                                off();
     741                                window.setUserSetting( 'editor_expand', 'off' );
     742                        }
     743                });
    610744
    611                 // Adjust when the window is scrolled or resized.
    612                 $window.on( 'scroll.editor-expand resize.editor-expand', function( event ) {
    613                         adjust( event.type );
    614                         afterScroll();
     745                // Expose on() and off()
     746                window.editorExpand = {
     747                        on: on,
     748                        off: off
     749                };
     750        } );
     751
     752        /* DFW. */
     753        $( function() {
     754                var $body = $( document.body ),
     755                        $wrap = $( '#wpcontent' ),
     756                        $editor = $( '#post-body-content' ),
     757                        $title = $( '#title' ),
     758                        $content = $( '#content' ),
     759                        $overlay = $( document.createElement( 'DIV' ) ),
     760                        $slug = $( '#edit-slug-box' ),
     761                        $slugFocusEl = $slug.find( 'a' )
     762                                .add( $slug.find( 'button' ) )
     763                                .add( $slug.find( 'input' ) ),
     764                        $menuWrap = $( '#adminmenuwrap' ),
     765                        $editorWindow = $(),
     766                        $editorIframe = $(),
     767                        _isActive = window.getUserSetting( 'editor_expand', 'on' ) === 'on',
     768                        _isOn = _isActive ? !! parseInt( window.getUserSetting( 'dfw', '1' ), 10 ) : false,
     769                        traveledX = 0,
     770                        traveledY = 0,
     771                        buffer = 20,
     772                        faded, fadedAdminBar, fadedSlug,
     773                        editorRect, x, y, mouseY, scrollY,
     774                        focusLostTimer, overlayTimer, editorHasFocus;
     775
     776                $body.append( $overlay );
     777
     778                $overlay.css( {
     779                        display: 'none',
     780                        position: 'fixed',
     781                        top: $adminBar.height(),
     782                        right: 0,
     783                        bottom: 0,
     784                        left: 0,
     785                        'z-index': 9997
    615786                } );
    616787
    617                 // Adjust when collapsing the menu, changing the columns, changing the body class.
    618                 $document.on( 'wp-collapse-menu.editor-expand postboxes-columnchange.editor-expand editor-classchange.editor-expand', adjust )
    619                         .on( 'postbox-toggled.editor-expand', function() {
    620                                 if ( ! fixedSideTop && ! fixedSideBottom && window.pageYOffset > pinnedToolsTop ) {
    621                                         fixedSideBottom = true;
    622                                         window.scrollBy( 0, -1 );
    623                                         adjust();
    624                                         window.scrollBy( 0, 1 );
    625                                 }
     788                $editor.css( {
     789                        position: 'relative'
     790                } );
    626791
    627                                 adjust();
    628                         }).on( 'wp-window-resized.editor-expand', function() {
    629                                 if ( mceEditor && ! mceEditor.isHidden() ) {
    630                                         mceEditor.execCommand( 'wpAutoResize' );
    631                                 } else {
    632                                         textEditorResize();
    633                                 }
    634                         });
     792                $window.on( 'mousemove.focus', function( event ) {
     793                        mouseY = event.pageY;
     794                } );
    635795
    636                 $textEditor.on( 'focus.editor-expand input.editor-expand propertychange.editor-expand', textEditorResize );
    637                 $textEditor.on( 'keyup.editor-expand', textEditorKeyup );
    638                 mceBind();
     796                function activate() {
     797                        if ( ! _isActive ) {
     798                                _isActive = true;
    639799
    640                 // Adjust when entering/exiting fullscreen mode.
    641                 fullscreen && fullscreen.pubsub.subscribe( 'hidden', fullscreenHide );
     800                                $document.trigger( 'dfw-activate' );
     801                        }
     802                }
    642803
    643                 if ( mceEditor ) {
    644                         mceEditor.settings.wp_autoresize_on = true;
    645                         mceEditor.execCommand( 'wpAutoResizeOn' );
     804                function deactivate() {
     805                        if ( _isActive ) {
     806                                off();
    646807
    647                         if ( ! mceEditor.isHidden() ) {
    648                                 mceEditor.execCommand( 'wpAutoResize' );
     808                                _isActive = false;
     809
     810                                $document.trigger( 'dfw-deactivate' );
    649811                        }
    650812                }
    651813
    652                 if ( ! mceEditor || mceEditor.isHidden() ) {
    653                         textEditorResize();
     814                function isActive() {
     815                        return _isActive;
    654816                }
    655817
    656                 adjust();
    657         }
     818                function on() {
     819                        if ( ! _isOn && _isActive ) {
     820                                _isOn = true;
    658821
    659         function off() {
    660                 var height = window.getUserSetting('ed_size');
     822                                $content.on( 'keydown.focus', fadeOut );
    661823
    662                 // Scroll to the top when triggering this from JS.
    663                 // Ensures toolbars are reset properly.
    664                 if ( window.pageYOffset && window.pageYOffset > pageYOffsetAtTop ) {
    665                         window.scrollTo( window.pageXOffset, 0 );
     824                                $title.add( $content ).on( 'blur.focus', maybeFadeIn );
     825
     826                                fadeOut();
     827
     828                                window.setUserSetting( 'dfw', '1' );
     829
     830                                $document.trigger( 'dfw-on' );
     831                        }
    666832                }
    667833
    668                 $wrap.removeClass( 'wp-editor-expand' );
     834                function off() {
     835                        if ( _isOn ) {
     836                                _isOn = false;
    669837
    670                 $window.off( '.editor-expand' );
    671                 $document.off( '.editor-expand' );
    672                 $textEditor.off( '.editor-expand' );
    673                 mceUnbind();
     838                                $title.add( $content ).off( '.focus' );
    674839
    675                 // Adjust when entering/exiting fullscreen mode.
    676                 fullscreen && fullscreen.pubsub.unsubscribe( 'hidden', fullscreenHide );
     840                                fadeIn();
    677841
    678                 // Reset all css
    679                 $.each( [ $visualTop, $textTop, $tools, $menuBar, $bottom, $statusBar, $contentWrap, $visualEditor, $textEditor, $sideSortables ], function( i, element ) {
    680                         element && element.attr( 'style', '' );
    681                 });
     842                                $editor.off( '.focus' );
    682843
    683                 fixedTop = fixedBottom = fixedSideTop = fixedSideBottom = false;
     844                                window.setUserSetting( 'dfw', '0' );
    684845
    685                 if ( mceEditor ) {
    686                         mceEditor.settings.wp_autoresize_on = false;
    687                         mceEditor.execCommand( 'wpAutoResizeOff' );
     846                                $document.trigger( 'dfw-off' );
     847                        }
     848                }
    688849
    689                         if ( ! mceEditor.isHidden() ) {
    690                                 $textEditor.hide();
     850                function toggle() {
     851                        ( _isOn ? off : on )();
     852                }
     853
     854                function isOn() {
     855                        return _isOn;
     856                }
    691857
    692                                 if ( height ) {
    693                                         mceEditor.theme.resizeTo( null, height );
     858                function fadeOut( event ) {
     859                        var key = event && event.keyCode;
     860
     861                        if ( key === 27 ) {
     862                                fadeIn();
     863                                return;
     864                        }
     865
     866                        if ( event && ( event.metaKey || ( event.ctrlKey && ! event.altKey ) || ( key && (
     867                                // Special keys ( tab, ctrl, alt, esc, arrow keys... )
     868                                ( key <= 47 && key !== 8 && key !== 13 && key !== 32 && key !== 46 ) ||
     869                                // Windows keys
     870                                ( key >= 91 && key <= 93 ) ||
     871                                // F keys
     872                                ( key >= 112 && key <= 135 ) ||
     873                                // Num Lock, Scroll Lock, OEM
     874                                ( key >= 144 && key <= 150 ) ||
     875                                // OEM or non-printable
     876                                key >= 224
     877                        ) ) ) ) {
     878                                return;
     879                        }
     880
     881                        if ( ! faded ) {
     882                                faded = true;
     883
     884                                clearTimeout( overlayTimer );
     885
     886                                overlayTimer = setTimeout( function() {
     887                                        $overlay.show();
     888                                }, 600 );
     889
     890                                $editor.css( 'z-index', 9998 );
     891
     892                                $overlay
     893                                        // Always recalculate the editor area entering the overlay with the mouse.
     894                                        .on( 'mouseenter.focus', function() {
     895                                                editorRect = $editor.offset();
     896                                                editorRect.right = editorRect.left + $editor.outerWidth();
     897                                                editorRect.bottom = editorRect.top + $editor.outerHeight();
     898
     899                                                $window.on( 'scroll.focus', function() {
     900                                                        var nScrollY = window.pageYOffset;
     901
     902                                                        if ( (
     903                                                                scrollY && mouseY &&
     904                                                                scrollY !== nScrollY
     905                                                        ) && (
     906                                                                mouseY < editorRect.top - buffer ||
     907                                                                mouseY > editorRect.bottom + buffer
     908                                                        ) ) {
     909                                                                fadeIn();
     910                                                        }
     911
     912                                                        scrollY = nScrollY;
     913                                                } );
     914                                        } )
     915                                        .on( 'mouseleave.focus', function() {
     916                                                x = y =  null;
     917                                                traveledX = traveledY = 0;
     918
     919                                                $window.off( 'scroll.focus' );
     920                                        } )
     921                                        // Fade in when the mouse moves away form the editor area.
     922                                        .on( 'mousemove.focus', function( event ) {
     923                                                var nx = event.pageX,
     924                                                        ny = event.pageY;
     925
     926                                                if ( x && y && ( nx !== x || ny !== y ) ) {
     927                                                        if (
     928                                                                ( ny <= y && ny < editorRect.top ) ||
     929                                                                ( ny >= y && ny > editorRect.bottom ) ||
     930                                                                ( nx <= x && nx < editorRect.left ) ||
     931                                                                ( nx >= x && nx > editorRect.right )
     932                                                        ) {
     933                                                                traveledX += Math.abs( x - nx );
     934                                                                traveledY += Math.abs( y - ny );
     935
     936                                                                if ( (
     937                                                                        ny <= editorRect.top - buffer ||
     938                                                                        ny >= editorRect.bottom + buffer ||
     939                                                                        nx <= editorRect.left - buffer ||
     940                                                                        nx >= editorRect.right + buffer
     941                                                                ) && (
     942                                                                        traveledX > 10 ||
     943                                                                        traveledY > 10
     944                                                                ) ) {
     945                                                                        fadeIn();
     946
     947                                                                        x = y =  null;
     948                                                                        traveledX = traveledY = 0;
     949
     950                                                                        return;
     951                                                                }
     952                                                        } else {
     953                                                                traveledX = traveledY = 0;
     954                                                        }
     955                                                }
     956
     957                                                x = nx;
     958                                                y = ny;
     959                                        } )
     960                                        // When the overlay is touched, always fade in and cancel the event.
     961                                        .on( 'touchstart.focus', function( event ) {
     962                                                event.preventDefault();
     963                                                fadeIn();
     964                                        } );
     965
     966                                $editor.off( 'mouseenter.focus' );
     967
     968                                if ( focusLostTimer ) {
     969                                        clearTimeout( focusLostTimer );
     970                                        focusLostTimer = null;
    694971                                }
     972
     973                                $body.addClass( 'focus-on' ).removeClass( 'focus-off' );
    695974                        }
     975
     976                        fadeOutAdminBar();
     977                        fadeOutSlug();
    696978                }
    697979
    698                 if ( height ) {
    699                         $textEditor.height( height );
     980                function fadeIn() {
     981                        if ( faded ) {
     982                                faded = false;
     983
     984                                clearTimeout( overlayTimer );
     985
     986                                overlayTimer = setTimeout( function() {
     987                                        $overlay.hide();
     988                                }, 200 );
     989
     990                                $editor.css( 'z-index', '' );
     991
     992                                $overlay.off( 'mouseenter.focus mouseleave.focus mousemove.focus touchstart.focus' );
     993
     994                                $editor.on( 'mouseenter.focus', function() {
     995                                        if ( $.contains( $editor.get( 0 ), document.activeElement ) || editorHasFocus ) {
     996                                                fadeOut();
     997                                        }
     998                                } );
     999
     1000                                focusLostTimer = setTimeout( function() {
     1001                                        focusLostTimer = null;
     1002                                        $editor.off( 'mouseenter.focus' );
     1003                                }, 1000 );
     1004
     1005                                $body.addClass( 'focus-off' ).removeClass( 'focus-on' );
     1006                        }
     1007
     1008                        fadeInAdminBar();
     1009                        fadeInSlug();
    7001010                }
    701         }
    7021011
    703         // Start on load
    704         if ( $wrap.hasClass( 'wp-editor-expand' ) ) {
    705                 on();
     1012                function maybeFadeIn() {
     1013                        setTimeout( function() {
     1014                                var position = document.activeElement.compareDocumentPosition( $editor.get( 0 ) );
    7061015
    707                 // Ideally we need to resize just after CSS has fully loaded and QuickTags is ready.
    708                 if ( $contentWrap.hasClass( 'html-active' ) ) {
    709                         initialResize( function() {
    710                                 adjust();
    711                                 textEditorResize();
     1016                                function hasFocus( $el ) {
     1017                                        return $.contains( $el.get( 0 ), document.activeElement );
     1018                                }
     1019
     1020                                // The focussed node is before or behind the editor area, and not ouside the wrap.
     1021                                if ( ( position === 2 || position === 4 ) && ( hasFocus( $menuWrap ) || hasFocus( $wrap ) || hasFocus( $footer ) ) ) {
     1022                                        fadeIn();
     1023                                }
     1024                        }, 0 );
     1025                }
     1026
     1027                function fadeOutAdminBar() {
     1028                        if ( ! fadedAdminBar && faded ) {
     1029                                fadedAdminBar = true;
     1030
     1031                                $adminBar
     1032                                        .on( 'mouseenter.focus', function() {
     1033                                                $adminBar.addClass( 'focus-off' );
     1034                                        } )
     1035                                        .on( 'mouseleave.focus', function() {
     1036                                                $adminBar.removeClass( 'focus-off' );
     1037                                        } );
     1038                        }
     1039                }
     1040
     1041                function fadeInAdminBar() {
     1042                        if ( fadedAdminBar ) {
     1043                                fadedAdminBar = false;
     1044
     1045                                $adminBar.off( '.focus' );
     1046                        }
     1047                }
     1048
     1049                function fadeOutSlug() {
     1050                        if ( ! fadedSlug && faded && ! $slug.find( ':focus').length ) {
     1051                                fadedSlug = true;
     1052
     1053                                $slug.stop().fadeTo( 'fast', 0.3 ).on( 'mouseenter.focus', fadeInSlug ).off( 'mouseleave.focus' );
     1054
     1055                                $slugFocusEl.on( 'focus.focus', fadeInSlug ).off( 'blur.focus' );
     1056                        }
     1057                }
     1058
     1059                function fadeInSlug() {
     1060                        if ( fadedSlug ) {
     1061                                fadedSlug = false;
     1062
     1063                                $slug.stop().fadeTo( 'fast', 1 ).on( 'mouseleave.focus', fadeOutSlug ).off( 'mouseenter.focus' );
     1064
     1065                                $slugFocusEl.on( 'blur.focus', fadeOutSlug ).off( 'focus.focus' );
     1066                        }
     1067                }
     1068
     1069                $document.on( 'tinymce-editor-setup.focus', function( event, editor ) {
     1070                        editor.addButton( 'dfw', {
     1071                                active: _isOn,
     1072                                classes: 'wp-dfw btn widget',
     1073                                disabled: ! _isActive,
     1074                                onclick: toggle,
     1075                                onPostRender: function() {
     1076                                        var button = this;
     1077
     1078                                        $document
     1079                                        .on( 'dfw-activate.focus', function() {
     1080                                                button.disabled( false );
     1081                                        } )
     1082                                        .on( 'dfw-deactivate.focus', function() {
     1083                                                button.disabled( true );
     1084                                        } )
     1085                                        .on( 'dfw-on.focus', function() {
     1086                                                button.active( true );
     1087                                        } )
     1088                                        .on( 'dfw-off.focus', function() {
     1089                                                button.active( false );
     1090                                        } );
     1091                                },
     1092                                tooltip: 'Distraction Free Writing'
    7121093                        } );
     1094                } );
     1095
     1096                $document.on( 'tinymce-editor-init.focus', function( event, editor ) {
     1097                        var mceBind, mceUnbind;
     1098
     1099                        function focus() {
     1100                                editorHasFocus = true;
     1101                        }
     1102
     1103                        function blur() {
     1104                                editorHasFocus = false;
     1105                        }
     1106
     1107                        if ( editor.id === 'content' ) {
     1108                                $editorWindow = $( editor.getWin() );
     1109                                $editorIframe = $( editor.getContentAreaContainer() ).find( 'iframe' );
     1110
     1111                                mceBind = function() {
     1112                                        editor.on( 'keydown', fadeOut );
     1113                                        editor.on( 'blur', maybeFadeIn );
     1114                                        editor.on( 'focus', focus );
     1115                                        editor.on( 'blur', blur );
     1116                                };
     1117
     1118                                mceUnbind = function() {
     1119                                        editor.off( 'keydown', fadeOut );
     1120                                        editor.off( 'blur', maybeFadeIn );
     1121                                        editor.off( 'focus', focus );
     1122                                        editor.off( 'blur', blur );
     1123                                };
     1124
     1125                                if ( _isOn ) {
     1126                                        mceBind();
     1127                                }
     1128
     1129                                $document.on( 'dfw-on.focus', mceBind ).on( 'dfw-off.focus', mceUnbind );
     1130
     1131                                // Make sure the body focusses when clicking outside it.
     1132                                editor.on( 'click', function( event ) {
     1133                                        if ( event.target === editor.getDoc().documentElement ) {
     1134                                                editor.focus();
     1135                                        }
     1136                                } );
     1137                        }
     1138                } );
     1139
     1140                $document.on( 'quicktags-init', function( event, editor ) {
     1141                        var $button;
     1142
     1143                        if ( editor.settings.buttons && ( ',' + editor.settings.buttons + ',' ).indexOf( ',dfw,' ) !== -1 ) {
     1144                                $button = $( '#' + editor.name + '_dfw' );
     1145
     1146                                $( document )
     1147                                .on( 'dfw-activate', function() {
     1148                                        $button.prop( 'disabled', false );
     1149                                } )
     1150                                .on( 'dfw-deactivate', function() {
     1151                                        $button.prop( 'disabled', true );
     1152                                } )
     1153                                .on( 'dfw-on', function() {
     1154                                        $button.addClass( 'active' );
     1155                                } )
     1156                                .on( 'dfw-off', function() {
     1157                                        $button.removeClass( 'active' );
     1158                                } );
     1159                        }
     1160                } );
     1161
     1162                $document.on( 'editor-expand-on.focus', activate ).on( 'editor-expand-off.focus', deactivate );
     1163
     1164                if ( _isOn ) {
     1165                        $content.on( 'keydown.focus', fadeOut );
     1166
     1167                        $title.add( $content ).on( 'blur.focus', maybeFadeIn );
    7131168                }
    714         }
    7151169
    716         // Show the on/off checkbox
    717         $( '#adv-settings .editor-expand' ).show();
    718         $( '#editor-expand-toggle' ).on( 'change.editor-expand', function() {
    719                 if ( $(this).prop( 'checked' ) ) {
    720                         on();
    721                         window.setUserSetting( 'editor_expand', 'on' );
    722                 } else {
    723                         off();
    724                         window.setUserSetting( 'editor_expand', 'off' );
    725                 }
    726         });
    727 
    728         // Expose on() and off()
    729         window.editorExpand = {
    730                 on: on,
    731                 off: off
    732         };
    733 });
     1170                window.wp = window.wp || {};
     1171                window.wp.editor = window.wp.editor || {};
     1172                window.wp.editor.dfw = {
     1173                        activate: activate,
     1174                        deactivate: deactivate,
     1175                        isActive: isActive,
     1176                        on: on,
     1177                        off: off,
     1178                        toggle: toggle,
     1179                        isOn: isOn
     1180                };
     1181        } );
     1182} )( window, window.jQuery );
  • src/wp-includes/class-wp-editor.php

     
    278278                        if ( $set['dfw'] )
    279279                                $qtInit['buttons'] .= ',fullscreen';
    280280
     281                        if ( $editor_id === 'content' && ! wp_is_mobile() )
     282                                $qtInit['buttons'] .= ',dfw';
     283
    281284                        /**
    282285                         * Filter the Quicktags settings.
    283286                         *
     
    546549                                $mce_buttons = apply_filters( 'teeny_mce_buttons', array('bold', 'italic', 'underline', 'blockquote', 'strikethrough', 'bullist', 'numlist', 'alignleft', 'aligncenter', 'alignright', 'undo', 'redo', 'link', 'unlink', 'fullscreen'), $editor_id );
    547550                                $mce_buttons_2 = $mce_buttons_3 = $mce_buttons_4 = array();
    548551                        } else {
     552                                $mce_buttons = array( 'bold', 'italic', 'strikethrough', 'bullist', 'numlist', 'blockquote', 'hr', 'alignleft', 'aligncenter', 'alignright', 'link', 'unlink', 'wp_more', 'spellchecker', 'wp_adv' );
     553
     554                                if ( $editor_id ) {
     555                                        $mce_buttons[] = 'dfw';
     556                                } else {
     557                                        $mce_buttons[] = 'fullscreen';
     558                                }
    549559
    550560                                /**
    551561                                 * Filter the first-row list of TinyMCE buttons (Visual tab).
     
    555565                                 * @param array  $buttons   First-row list of buttons.
    556566                                 * @param string $editor_id Unique editor identifier, e.g. 'content'.
    557567                                 */
    558                                 $mce_buttons = apply_filters( 'mce_buttons', array('bold', 'italic', 'strikethrough', 'bullist', 'numlist', 'blockquote', 'hr', 'alignleft', 'aligncenter', 'alignright', 'link', 'unlink', 'wp_more', 'spellchecker', 'fullscreen', 'wp_adv' ), $editor_id );
     568                                $mce_buttons = apply_filters( 'mce_buttons', $mce_buttons, $editor_id );
    559569
    560570                                /**
    561571                                 * Filter the second-row list of TinyMCE buttons (Visual tab).
  • src/wp-includes/css/editor.css

     
    183183}
    184184
    185185.mce-toolbar .mce-btn,
    186 .qt-fullscreen {
     186.qt-dfw {
    187187        border-color: transparent;
    188188        background: transparent;
    189189        -webkit-box-shadow: none;
     
    194194
    195195#wp-fullscreen-buttons .mce-btn,
    196196.mce-toolbar .mce-btn-group .mce-btn,
    197 .qt-fullscreen {
     197.qt-dfw {
    198198        border: 1px solid transparent;
    199199        margin: 2px;
    200200        background-image: none;
     
    208208.mce-toolbar .mce-btn-group .mce-btn:hover,
    209209#wp-fullscreen-buttons .mce-btn:focus,
    210210.mce-toolbar .mce-btn-group .mce-btn:focus,
    211 .qt-fullscreen:hover,
    212 .qt-fullscreen:focus {
     211.qt-dfw:hover,
     212.qt-dfw:focus {
    213213        background: #fafafa;
    214214        border-color: #999;
    215215        color: #222;
    216216        -webkit-box-shadow: inset 0 1px 0 #fff, 0 1px 0 rgba( 0, 0, 0, 0.08 );
    217217        box-shadow: inset 0 1px 0 #fff, 0 1px 0 rgba( 0, 0, 0, 0.08 );
     218        outline: none;
    218219}
    219220
    220221.mce-toolbar .mce-btn-group .mce-btn.mce-active,
    221222#wp-fullscreen-buttons .mce-btn.mce-active,
    222223.mce-toolbar .mce-btn-group .mce-btn:active,
    223 #wp-fullscreen-buttons .mce-btn:active {
     224#wp-fullscreen-buttons .mce-btn:active,
     225.qt-dfw.active {
    224226        background: #ebebeb;
    225227        border-color: #999;
    226228        -webkit-box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.3 );
     
    253255}
    254256
    255257.mce-toolbar .mce-btn button,
    256 .qt-fullscreen {
     258.qt-dfw {
    257259        padding: 2px 3px;
    258260        line-height: normal;
    259261}
     
    568570        padding: 0;
    569571}
    570572
    571 .qt-fullscreen {
     573.qt-dfw {
    572574        color: #777;
    573575        line-height: 20px;
    574576        width: 28px;
     
    602604i.mce-i-spellchecker,
    603605i.mce-i-fullscreen,
    604606i.mce-i-wp_fullscreen,
     607i.mce-i-dfw,
    605608i.mce-i-wp_adv,
    606609i.mce-i-underline,
    607610i.mce-i-alignjustify,
     
    632635        padding-right: 2px;
    633636}
    634637
    635 .qt-fullscreen {
     638.qt-dfw {
    636639        font: normal 20px/1 'dashicons';
    637640        vertical-align: top;
    638641        speak: none;
     
    694697
    695698i.mce-i-fullscreen:before,
    696699i.mce-i-wp_fullscreen:before,
    697 .qt-fullscreen:before {
     700i.mce-i-dfw:before,
     701.qt-dfw:before {
    698702        content: '\f211';
    699703}
    700704
     
    970974        font-weight: bold;
    971975}
    972976
    973 .mce-toolbar .mce-btn-group .mce-btn.mce-wp-fullscreen,
    974 .qt-fullscreen {
     977.mce-toolbar .mce-btn-group .mce-btn.mce-wp-dfw,
     978.qt-dfw {
    975979        position: absolute;
    976980        top: 0;
    977981        right: 0;
     
    980984
    981985@media screen and ( max-width: 782px ) {
    982986        .mce-toolbar .mce-btn button,
    983         .qt-fullscreen {
     987        .qt-dfw {
    984988                padding: 6px 7px;
    985989        }
    986990
     
    989993                margin: 1px;
    990994        }
    991995
    992         .qt-fullscreen {
     996        .qt-dfw {
    993997                width: 36px;
    994998                height: 34px;
    995999        }
    9961000
    997         .mce-toolbar .mce-btn-group .mce-btn.mce-wp-fullscreen {
     1001        .mce-toolbar .mce-btn-group .mce-btn.mce-wp-dfw {
    9981002                margin: 4px 4px 0 0;
    9991003        }
    10001004
     
    20082012}
    20092013
    20102014/* TODO: DFW responsive */
     2015
     2016/* DFW 2
     2017-------------------------------------------------------------- */
     2018
     2019.focus-on .wrap > h2,
     2020.focus-on #wpfooter,
     2021.focus-on .postbox-container,
     2022.focus-on div.updated,
     2023.focus-on div.error,
     2024.focus-on #wp-toolbar {
     2025        opacity: 0;
     2026        -webkit-transition-duration: 0.6s;
     2027        transition-duration: 0.6s;
     2028        -webkit-transition-property: opacity;
     2029        transition-property: opacity;
     2030        -webkit-transition-timing-function: ease-in-out;
     2031        transition-timing-function: ease-in-out;
     2032}
     2033
     2034.focus-on #wp-toolbar {
     2035        opacity: 0.3;
     2036}
     2037
     2038.focus-off .wrap > h2,
     2039.focus-off #wpfooter,
     2040.focus-off .postbox-container,
     2041.focus-off div.updated,
     2042.focus-off div.error,
     2043.focus-off #wp-toolbar {
     2044        opacity: 1;
     2045        -webkit-transition-duration: 0.2s;
     2046        transition-duration: 0.2s;
     2047        -webkit-transition-property: opacity;
     2048        transition-property: opacity;
     2049        -webkit-transition-timing-function: ease-in-out;
     2050        transition-timing-function: ease-in-out;
     2051}
     2052
     2053.focus-on #adminmenuback,
     2054.focus-on #adminmenuwrap,
     2055.focus-on .screen-meta-toggle {
     2056        -webkit-transition-duration: 0.6s;
     2057        transition-duration: 0.6s;
     2058        -webkit-transition-property: -webkit-transform;
     2059        transition-property: transform;
     2060        -webkit-transition-timing-function: ease-in-out;
     2061        transition-timing-function: ease-in-out;
     2062}
     2063
     2064.focus-on #adminmenuback,
     2065.focus-on #adminmenuwrap {
     2066        -webkit-transform: translateX( -100% );
     2067        -ms-transform: translateX( -100% );
     2068        transform: translateX( -100% );
     2069}
     2070
     2071.focus-on .screen-meta-toggle {
     2072        -webkit-transform: translateY( -100% );
     2073        -ms-transform: translateY( -100% );
     2074        transform: translateY( -100% );
     2075}
     2076
     2077.focus-off #adminmenuback,
     2078.focus-off #adminmenuwrap,
     2079.focus-off .screen-meta-toggle {
     2080        -webkit-transform: translateX( 0 );
     2081        -ms-transform: translateX( 0 );
     2082        transform: translateX( 0 );
     2083        -webkit-transition-duration: 0.2s;
     2084        transition-duration: 0.2s;
     2085        -webkit-transition-property: -webkit-transform;
     2086        transition-property: transform;
     2087        -webkit-transition-timing-function: ease-in-out;
     2088        transition-timing-function: ease-in-out;
     2089}
  • src/wp-includes/js/quicktags.js

     
    288288                                html += theButtons.fullscreen.html(name + '_');
    289289                        }
    290290
     291                        if ( use && use.indexOf(',dfw,') !== -1 ) {
     292                                theButtons.dfw = new qt.DFWButton();
     293                                html += theButtons.dfw.html( name + '_' );
     294                        }
    291295
    292296                        if ( 'rtl' === document.getElementsByTagName('html')[0].dir ) {
    293297                                theButtons.textdirection = new qt.TextDirectionButton();
     
    296300
    297301                        ed.toolbar.innerHTML = html;
    298302                        ed.theButtons = theButtons;
     303
     304                        window.jQuery && window.jQuery( document ).trigger( 'quicktags-init', [ ed ] );
    299305                }
    300306                t.buttonsInitDone = true;
    301307        };
     
    405411                t.instance = instance || '';
    406412        };
    407413        qt.Button.prototype.html = function(idPrefix) {
    408                 var title = this.title ? ' title="' + this.title + '"' : '';
     414                var title = this.title ? ' title="' + this.title + '"' : '',
     415                        active, on, wp,
     416                        dfw = ( wp = window.wp ) && wp.editor && wp.editor.dfw;
    409417
    410418                if ( this.id === 'fullscreen' ) {
    411                         return '<button type="button" id="' + idPrefix + this.id + '" class="ed_button qt-fullscreen"' + title + '></button>';
     419                        return '<button type="button" id="' + idPrefix + this.id + '" class="ed_button qt-dfw"' + title + '></button>';
     420                } else if ( this.id === 'dfw' ) {
     421                        active = dfw && dfw.isActive() ? '' : ' disabled="disabled"';
     422                        on = dfw && dfw.isOn() ? ' active' : '';
     423
     424                        return '<button type="button" id="' + idPrefix + this.id + '" class="ed_button qt-dfw' + on + '"' + title + active + '></button>';
    412425                }
     426
    413427                return '<input type="button" id="' + idPrefix + this.id + '" class="ed_button button button-small"' + title + ' value="' + this.display + '" />';
    414428        };
    415429        qt.Button.prototype.callback = function(){};
     
    619633                wp.editor.fullscreen.on();
    620634        };
    621635
     636        qt.DFWButton = function() {
     637                qt.Button.call( this, 'dfw', '', 'f', quicktagsL10n.dfw );
     638        };
     639        qt.DFWButton.prototype = new qt.Button();
     640        qt.DFWButton.prototype.callback = function() {
     641                var wp;
     642
     643                if ( ! ( wp = window.wp ) || ! wp.editor || ! wp.editor.dfw ) {
     644                        return;
     645                }
     646
     647                window.wp.editor.dfw.toggle();
     648        };
     649
    622650        qt.TextDirectionButton = function() {
    623651                qt.Button.call(this, 'textdirection', quicktagsL10n.textdirection, '', quicktagsL10n.toggleTextdirection);
    624652        };
  • src/wp-includes/js/tinymce/plugins/wordpress/plugin.js

     
    77        var DOM = tinymce.DOM, wpAdvButton, modKey, style,
    88                last = 0;
    99
     10        if ( typeof window.jQuery !== 'undefined' ) {
     11                window.jQuery( document ).triggerHandler( 'tinymce-editor-setup', [ editor ] );
     12        }
     13
    1014        function toggleToolbars( state ) {
    1115                var iframe, initial, toolbars,
    1216                        pixels = 0;
  • src/wp-includes/js/tinymce/plugins/wpautoresize/plugin.js

     
    117117                                resize( e );
    118118                        }
    119119
    120                         editor.fire( 'wp-autoresize', { height: resizeHeight } );
     120                        editor.fire( 'wp-autoresize', { height: resizeHeight, deltaHeight: e.type === 'nodechange' ? deltaSize : null } );
    121121                }
    122122        }
    123123
  • src/wp-includes/js/tinymce/plugins/wpfullscreen/plugin.js

     
    6565                tooltip: 'Distraction Free Writing',
    6666                shortcut: 'Alt+Shift+W',
    6767                onclick: toggleFullscreen,
    68                 classes: 'wp-fullscreen btn widget' // This overwrites all classes on the container!
     68                classes: 'wp-dfw btn widget' // This overwrites all classes on the container!
    6969        });
    7070
    7171        editor.addMenuItem( 'wp_fullscreen', {
  • src/wp-includes/script-loader.php

     
    9494                'fullscreen'            => __( 'fullscreen' ),
    9595                'toggleFullscreen'      => esc_attr__( 'Toggle fullscreen mode' ),
    9696                'textdirection'         => esc_attr__( 'text direction' ),
    97                 'toggleTextdirection'   => esc_attr__( 'Toggle Editor Text Direction' )
     97                'toggleTextdirection'   => esc_attr__( 'Toggle Editor Text Direction' ),
     98                'dfw'                   => esc_attr__( 'Distraction Free Writing' )
    9899        ) );
    99100
    100101        $scripts->add( 'colorpicker', "/wp-includes/js/colorpicker$suffix.js", array('prototype'), '3517m' );