WordPress.org

Make WordPress Core

Changeset 30338


Ignore:
Timestamp:
11/13/2014 10:30:55 PM (5 years ago)
Author:
markjaquith
Message:

Introduce Distraction-Free Writing v2, a re-think of DFW that uses the main editor instance

  • the "DFW" button is now an auto/off toggle
  • defaulting to auto during beta, decide later for release
  • "auto" means that DFW gets enabled as you start typing in editor
  • tabbing and mousing out will bring the full interface back
  • there is a slight grace period during which your mouse can quickly return

Feature plugin work happened here: https://github.com/avryl/focus

props avryl, azaozz, Michael Arestad
fixes #29806

Location:
trunk/src
Files:
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/edit-form-advanced.php

    r30028 r30338  
    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',
  • trunk/src/wp-admin/js/editor-expand.js

    r29929 r30338  
    1 /* global tinymce */
    2 
    3 window.wp = window.wp || {};
    4 
    5 jQuery( document ).ready( function( $ ) {
     1( function( window, $, undefined ) {
     2    'use strict';
     3
    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;
    94 
    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;
    115 
    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     }
    122 
    123     function textEditorResize() {
    124         if ( ( mceEditor && ! mceEditor.isHidden() ) || ( ! mceEditor && initialMode === 'tinymce' ) ) {
    125             return;
    126         }
    127 
    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();
    135 
    136         if ( hiddenHeight < autoresizeMinHeight ) {
    137             hiddenHeight = autoresizeMinHeight;
    138         }
    139 
    140         if ( hiddenHeight === textEditorHeight ) {
    141             return;
    142         }
    143 
    144         $textEditor.height( hiddenHeight );
    145 
    146         adjust();
    147     }
    148 
    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 );
    155 
    156         // Make sure it's the main editor.
    157         if ( editor.id !== 'content' ) {
    158             return;
    159         }
    160 
    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;
    166 
    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' );
    172 
    173         function mceGetCursorOffset() {
    174             var node = editor.selection.getNode(),
    175                 range, view, offset;
    176 
    177             if ( editor.plugins.wpview && ( view = editor.plugins.wpview.getView( node ) ) ) {
    178                 offset = view.getBoundingClientRect();
    179             } else {
    180                 range = editor.selection.getRng();
    181 
    182                 try {
    183                     offset = range.getClientRects()[0];
    184                 } catch( er ) {}
    185 
    186                 if ( ! offset ) {
    187                     offset = node.getBoundingClientRect();
    188                 }
    189             }
    190 
    191             return offset.height ? offset : false;
    192         }
    193 
    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;
    203 
    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 ) ) {
    207 
     7        $footer = $( '#wpfooter' );
     8
     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        } );
     64
     65        function getHeights() {
     66            var windowWidth = $window.width();
     67
     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            }
     85        }
     86
     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;
     96
     97            if ( selStart && selEnd && selStart !== selEnd ) {
    20898                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 ) {
     99            }
     100
     101            // These are not TinyMCE ranges.
     102            try {
     103                range.setStart( textNode, selStart );
     104                range.setEnd( textNode, selEnd + 1 );
     105            } catch ( ex ) {}
     106
     107            offset = range.getBoundingClientRect();
     108
     109            if ( ! offset.height ) {
    211110                return;
    212111            }
    213112
    214             mceScroll( key );
    215         }
    216 
    217         function mceScroll( key ) {
    218             var VK = tinymce.util.VK,
    219                 offset = mceGetCursorOffset(),
    220                 buffer = 10,
    221                 cursorTop, cursorBottom, editorTop, editorBottom;
    222 
    223             if ( ! offset ) {
    224                 return;
    225             }
    226 
    227             cursorTop = offset.top + editor.iframeElement.getBoundingClientRect().top;
     113            cursorTop = offset.top - buffer;
    228114            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;
    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;
    236             }
    237 
    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 ) ) ) {
    242 
     115            editorTop = heights.adminBarHeight + heights.toolsHeight + heights.textTopHeight;
     116            editorBottom = heights.windowHeight - heights.bottomHeight;
     117
     118            if ( cursorTop < editorTop && ( key === VK.UP || key === VK.LEFT || key === VK.BACKSPACE ) ) {
    243119                window.scrollTo( window.pageXOffset, cursorTop + window.pageYOffset - editorTop );
    244120            } else if ( cursorBottom > editorBottom ) {
     
    247123        }
    248124
    249         // Adjust when switching editor modes.
    250         function mceShow() {
    251             $window.on( 'scroll.mce-float-panels', hideFloatPanels );
    252 
    253             setTimeout( function() {
    254                 editor.execCommand( 'wpAutoResize' );
     125        function textEditorResize() {
     126            if ( ( mceEditor && ! mceEditor.isHidden() ) || ( ! mceEditor && initialMode === 'tinymce' ) ) {
     127                return;
     128            }
     129
     130            var textEditorHeight = $textEditor.height(),
     131                hiddenHeight;
     132
     133            $textEditorClone.width( $textEditor.width() - 22 );
     134            $textEditorClone.text( $textEditor.val() + '&nbsp;' );
     135
     136            hiddenHeight = $textEditorClone.height();
     137
     138            if ( hiddenHeight < autoresizeMinHeight ) {
     139                hiddenHeight = autoresizeMinHeight;
     140            }
     141
     142            if ( hiddenHeight === textEditorHeight ) {
     143                return;
     144            }
     145
     146            $textEditor.height( hiddenHeight );
     147
     148            adjust();
     149        }
     150
     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 );
     158
     159            // Make sure it's the main editor.
     160            if ( editor.id !== 'content' ) {
     161                return;
     162            }
     163
     164            // Copy the editor instance.
     165            mceEditor = editor;
     166
     167            // Set the minimum height to the initial viewport height.
     168            editor.settings.autoresize_min_height = autoresizeMinHeight;
     169
     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;
     179
     180                if ( editor.plugins.wpview && ( view = editor.plugins.wpview.getView( node ) ) ) {
     181                    offset = view.getBoundingClientRect();
     182                } else {
     183                    range = editor.selection.getRng();
     184
     185                    try {
     186                        offset = range.getClientRects()[0];
     187                    } catch( er ) {}
     188
     189                    if ( ! offset ) {
     190                        offset = node.getBoundingClientRect();
     191                    }
     192                }
     193
     194                return offset.height ? offset : false;
     195            }
     196
     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                }
     213
     214                mceScroll( key );
     215            }
     216
     217            function mceScroll( key ) {
     218                var offset = mceGetCursorOffset(),
     219                    buffer = 50,
     220                    cursorTop, cursorBottom, editorTop, editorBottom;
     221
     222                if ( ! offset ) {
     223                    return;
     224                }
     225
     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;
     236                }
     237
     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            }
     244
     245            // Adjust when switching editor modes.
     246            function mceShow() {
     247                $window.on( 'scroll.mce-float-panels', hideFloatPanels );
     248
     249                setTimeout( function() {
     250                    editor.execCommand( 'wpAutoResize' );
     251                    adjust();
     252                }, 300 );
     253            }
     254
     255            function mceHide() {
     256                $window.off( 'scroll.mce-float-panels' );
     257
     258                setTimeout( function() {
     259                    var top = $contentWrap.offset().top;
     260
     261                    if ( window.pageYOffset > top ) {
     262                        window.scrollTo( window.pageXOffset, top - heights.adminBarHeight );
     263                    }
     264
     265                    textEditorResize();
     266                    adjust();
     267                }, 100 );
     268
    255269                adjust();
    256             }, 300 );
    257         }
    258 
    259         function mceHide() {
    260             $window.off( 'scroll.mce-float-panels' );
    261 
    262             setTimeout( function() {
    263                 var top = $contentWrap.offset().top;
    264 
    265                 if ( window.pageYOffset > top ) {
    266                     window.scrollTo( window.pageXOffset, top - heights.adminBarHeight );
    267                 }
    268 
     270            }
     271
     272            function toggleAdvanced() {
     273                advanced = ! advanced;
     274            }
     275
     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        } );
     306
     307        // Adjust the toolbars based on the active editor mode.
     308        function adjust( event ) {
     309            var type = event && event.type;
     310
     311            // Make sure we're not in fullscreen mode.
     312            if ( fullscreen && fullscreen.settings.visible ) {
     313                return;
     314            }
     315
     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            }
     330
     331            if ( ! visual && type === 'resize' ) {
    269332                textEditorResize();
    270                 adjust();
    271             }, 100 );
    272 
    273             adjust();
    274         }
    275 
    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 );
    284 
    285             $window.off( 'scroll.mce-float-panels' ).on( 'scroll.mce-float-panels', hideFloatPanels );
    286         };
    287 
    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 );
    294 
    295             $window.off( 'scroll.mce-float-panels' );
    296         };
    297 
    298         if ( $wrap.hasClass( 'wp-editor-expand' ) ) {
    299             // Adjust "immediately"
    300             mceBind();
    301             initialResize( adjust );
    302         }
    303     } );
    304 
    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         }
    326 
    327         if ( ! visual && type === 'resize' ) {
    328             textEditorResize();
    329         }
    330 
    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 );
    348 
    349         if ( ! canPin ) {
    350             if ( resize ) {
    351                 $tools.css( {
    352                     position: 'absolute',
    353                     top: 0,
    354                     width: contentWrapWidth
    355                 } );
    356 
    357                 if ( visual && $menuBar.length ) {
    358                     $menuBar.css( {
    359                         position: 'absolute',
    360                         top: 0,
    361                         width: contentWrapWidth - ( borderWidth * 2 )
    362                     } );
    363                 }
    364 
    365                 $top.css( {
    366                     position: 'absolute',
    367                     top: heights.menuBarHeight,
    368                     width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
    369                 } );
    370 
    371                 $statusBar.add( $bottom ).attr( 'style', '' );
    372             }
    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                 } );
    387 
    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                 }
    395 
    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;
    406 
     333            }
     334
     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            }
     344
     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 );
     352
     353            if ( ! canPin ) {
     354                if ( resize ) {
    407355                    $tools.css( {
    408356                        position: 'absolute',
     
    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                    } );
     
    436392                    if ( visual && $menuBar.length ) {
    437393                        $menuBar.css( {
     394                            position: 'fixed',
     395                            top: heights.adminBarHeight + heights.toolsHeight,
     396                            width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
     397                        } );
     398                    }
     399
     400                    $top.css( {
     401                        position: 'fixed',
     402                        top: heights.adminBarHeight + heights.toolsHeight + heights.menuBarHeight,
     403                        width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
     404                    } );
     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;
     410
     411                        $tools.css( {
     412                            position: 'absolute',
     413                            top: 0,
     414                            width: contentWrapWidth
     415                        } );
     416
     417                        if ( visual && $menuBar.length ) {
     418                            $menuBar.css( {
     419                                position: 'absolute',
     420                                top: 0,
     421                                width: contentWrapWidth - ( borderWidth * 2 )
     422                            } );
     423                        }
     424
     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;
     433
     434                        $tools.css( {
    438435                            position: 'absolute',
    439436                            top: editorHeight - buffer,
     437                            width: contentWrapWidth
     438                        } );
     439
     440                        if ( visual && $menuBar.length ) {
     441                            $menuBar.css( {
     442                                position: 'absolute',
     443                                top: editorHeight - buffer,
     444                                width: contentWrapWidth - ( borderWidth * 2 )
     445                            } );
     446                        }
     447
     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: '',
    440470                            width: contentWrapWidth - ( borderWidth * 2 )
    441471                        } );
    442                     }
    443 
    444                     $top.css( {
    445                         position: 'absolute',
    446                         top: editorHeight - buffer + heights.menuBarHeight,
    447                         width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
    448                     } );
    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 )
     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                            }
     557                        }
     558                    }
     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;
     572                }
     573
     574                lastScrollPosition = windowPos;
     575            } else {
     576                $sideSortables.attr( 'style', '' );
     577                fixedSideTop = fixedSideBottom = false;
     578            }
     579
     580            if ( resize ) {
     581                $contentWrap.css( {
     582                    paddingTop: heights.toolsHeight
    462583                } );
    463584
    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;
    472 
    473                 $statusBar.add( $bottom ).attr( 'style', '' );
    474             }
    475         }
    476 
    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
    481 
    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                             }
    500 
    501                             $sideSortables.css({
    502                                 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                             });
    515                         }
    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                             }
    528 
    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                             });
    543                         }
    544                     }
    545                 }
    546             } else {
    547                 // if the sidebar container is smaller than the viewport, then pin/unpin the top when scrolling
    548                 if ( windowPos >= ( postBodyTop - pinnedToolsTop ) ) {
    549 
    550                     $sideSortables.css( {
    551                         position: 'fixed',
    552                         top: pinnedToolsTop
     585                if ( visual ) {
     586                    $visualEditor.css( {
     587                        paddingTop: heights.visualTopHeight + heights.menuBarHeight
    553588                    } );
    554589                } else {
    555                     $sideSortables.attr( 'style', '' );
    556                 }
    557 
    558                 fixedSideTop = fixedSideBottom = false;
    559             }
    560 
    561             lastScrollPosition = windowPos;
    562         } else {
    563             $sideSortables.attr( 'style', '' );
    564             fixedSideTop = fixedSideBottom = false;
    565         }
    566 
    567         if ( resize ) {
    568             $contentWrap.css( {
    569                 paddingTop: heights.toolsHeight
     590                    $textEditor.css( {
     591                        marginTop: heights.textTopHeight
     592                    } );
     593
     594                    $textEditorClone.width( contentWrapWidth - 20 - ( borderWidth * 2 ) );
     595                }
     596            }
     597        }
     598
     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 );
     607            }
     608        }
     609
     610        function afterScroll() {
     611            clearTimeout( scrollTimer );
     612            scrollTimer = setTimeout( adjust, 100 );
     613        }
     614
     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
     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                    }
     639
     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();
     667            }
     668
     669            adjust();
     670
     671            $document.trigger( 'editor-expand-on' );
     672        }
     673
     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' );
     703
     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' );
     718        }
     719
     720        // Start on load
     721        if ( $wrap.hasClass( 'wp-editor-expand' ) ) {
     722            on();
     723
     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();
    575729                } );
     730            }
     731        }
     732
     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' );
    576739            } else {
    577                 $textEditor.css( {
    578                     marginTop: heights.textTopHeight
     740                off();
     741                window.setUserSetting( 'editor_expand', 'off' );
     742            }
     743        });
     744
     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
     786        } );
     787
     788        $editor.css( {
     789            position: 'relative'
     790        } );
     791
     792        $window.on( 'mousemove.focus', function( event ) {
     793            mouseY = event.pageY;
     794        } );
     795
     796        function activate() {
     797            if ( ! _isActive ) {
     798                _isActive = true;
     799
     800                $document.trigger( 'dfw-activate' );
     801            }
     802        }
     803
     804        function deactivate() {
     805            if ( _isActive ) {
     806                off();
     807
     808                _isActive = false;
     809
     810                $document.trigger( 'dfw-deactivate' );
     811            }
     812        }
     813
     814        function isActive() {
     815            return _isActive;
     816        }
     817
     818        function on() {
     819            if ( ! _isOn && _isActive ) {
     820                _isOn = true;
     821
     822                $content.on( 'keydown.focus', fadeOut );
     823
     824                $title.add( $content ).on( 'blur.focus', maybeFadeIn );
     825
     826                fadeOut();
     827
     828                window.setUserSetting( 'dfw', '1' );
     829
     830                $document.trigger( 'dfw-on' );
     831            }
     832        }
     833
     834        function off() {
     835            if ( _isOn ) {
     836                _isOn = false;
     837
     838                $title.add( $content ).off( '.focus' );
     839
     840                fadeIn();
     841
     842                $editor.off( '.focus' );
     843
     844                window.setUserSetting( 'dfw', '0' );
     845
     846                $document.trigger( 'dfw-off' );
     847            }
     848        }
     849
     850        function toggle() {
     851            ( _isOn ? off : on )();
     852        }
     853
     854        function isOn() {
     855            return _isOn;
     856        }
     857
     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;
     971                }
     972
     973                $body.addClass( 'focus-on' ).removeClass( 'focus-off' );
     974            }
     975
     976            fadeOutAdminBar();
     977            fadeOutSlug();
     978        }
     979
     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                    }
    579998                } );
    580999
    581                 $textEditorClone.width( contentWrapWidth - 20 - ( borderWidth * 2 ) );
    582             }
    583         }
    584     }
    585 
    586     function fullscreenHide() {
    587         textEditorResize();
    588         adjust();
    589     }
    590 
    591     function initialResize( callback ) {
    592         for ( var i = 1; i < 6; i++ ) {
    593             setTimeout( callback, 500 * i );
    594         }
    595     }
    596 
    597     function afterScroll() {
    598         clearTimeout( scrollTimer );
    599         scrollTimer = setTimeout( adjust, 100 );
    600     }
    601 
    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 );
    607         }
    608 
    609         $wrap.addClass( 'wp-editor-expand' );
    610 
    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();
     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();
     1010        }
     1011
     1012        function maybeFadeIn() {
     1013            setTimeout( function() {
     1014                var position = document.activeElement.compareDocumentPosition( $editor.get( 0 ) );
     1015
     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'
     1093            } );
    6151094        } );
    6161095
    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                 }
    626 
    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             });
    635 
    636         $textEditor.on( 'focus.editor-expand input.editor-expand propertychange.editor-expand', textEditorResize );
    637         $textEditor.on( 'keyup.editor-expand', textEditorKeyup );
    638         mceBind();
    639 
    640         // Adjust when entering/exiting fullscreen mode.
    641         fullscreen && fullscreen.pubsub.subscribe( 'hidden', fullscreenHide );
    642 
    643         if ( mceEditor ) {
    644             mceEditor.settings.wp_autoresize_on = true;
    645             mceEditor.execCommand( 'wpAutoResizeOn' );
    646 
    647             if ( ! mceEditor.isHidden() ) {
    648                 mceEditor.execCommand( 'wpAutoResize' );
    649             }
    650         }
    651 
    652         if ( ! mceEditor || mceEditor.isHidden() ) {
    653             textEditorResize();
    654         }
    655 
    656         adjust();
    657     }
    658 
    659     function off() {
    660         var height = window.getUserSetting('ed_size');
    661 
    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 );
    666         }
    667 
    668         $wrap.removeClass( 'wp-editor-expand' );
    669 
    670         $window.off( '.editor-expand' );
    671         $document.off( '.editor-expand' );
    672         $textEditor.off( '.editor-expand' );
    673         mceUnbind();
    674 
    675         // Adjust when entering/exiting fullscreen mode.
    676         fullscreen && fullscreen.pubsub.unsubscribe( 'hidden', fullscreenHide );
    677 
    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         });
    682 
    683         fixedTop = fixedBottom = fixedSideTop = fixedSideBottom = false;
    684 
    685         if ( mceEditor ) {
    686             mceEditor.settings.wp_autoresize_on = false;
    687             mceEditor.execCommand( 'wpAutoResizeOff' );
    688 
    689             if ( ! mceEditor.isHidden() ) {
    690                 $textEditor.hide();
    691 
    692                 if ( height ) {
    693                     mceEditor.theme.resizeTo( null, height );
    694                 }
    695             }
    696         }
    697 
    698         if ( height ) {
    699             $textEditor.height( height );
    700         }
    701     }
    702 
    703     // Start on load
    704     if ( $wrap.hasClass( 'wp-editor-expand' ) ) {
    705         on();
    706 
    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();
    712             } );
    713         }
    714     }
    715 
    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 });
     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 );
     1168        }
     1169
     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 );
  • trunk/src/wp-includes/class-wp-editor.php

    r30319 r30338  
    279279                $qtInit['buttons'] .= ',fullscreen';
    280280
     281            if ( $editor_id === 'content' && ! wp_is_mobile() )
     282                $qtInit['buttons'] .= ',dfw';
     283
    281284            /**
    282285             * Filter the Quicktags settings.
     
    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                /**
     
    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                /**
  • trunk/src/wp-includes/css/editor.css

    r30318 r30338  
    284284
    285285.mce-toolbar .mce-btn,
    286 .qt-fullscreen {
     286.qt-dfw {
    287287    border-color: transparent;
    288288    background: transparent;
     
    295295#wp-fullscreen-buttons .mce-btn,
    296296.mce-toolbar .mce-btn-group .mce-btn,
    297 .qt-fullscreen {
     297.qt-dfw {
    298298    border: 1px solid transparent;
    299299    margin: 2px;
     
    309309#wp-fullscreen-buttons .mce-btn:focus,
    310310.mce-toolbar .mce-btn-group .mce-btn:focus,
    311 .qt-fullscreen:hover,
    312 .qt-fullscreen:focus {
     311.qt-dfw:hover,
     312.qt-dfw:focus {
    313313    background: #fafafa;
    314314    border-color: #999;
     
    316316    -webkit-box-shadow: inset 0 1px 0 #fff, 0 1px 0 rgba( 0, 0, 0, 0.08 );
    317317    box-shadow: inset 0 1px 0 #fff, 0 1px 0 rgba( 0, 0, 0, 0.08 );
     318    outline: none;
    318319}
    319320
     
    321322#wp-fullscreen-buttons .mce-btn.mce-active,
    322323.mce-toolbar .mce-btn-group .mce-btn:active,
    323 #wp-fullscreen-buttons .mce-btn:active {
     324#wp-fullscreen-buttons .mce-btn:active,
     325.qt-dfw.active {
    324326    background: #ebebeb;
    325327    border-color: #999;
     
    354356
    355357.mce-toolbar .mce-btn button,
    356 .qt-fullscreen {
     358.qt-dfw {
    357359    padding: 2px 3px;
    358360    line-height: normal;
     
    669671}
    670672
    671 .qt-fullscreen {
     673.qt-dfw {
    672674    color: #777;
    673675    line-height: 20px;
     
    703705i.mce-i-fullscreen,
    704706i.mce-i-wp_fullscreen,
     707i.mce-i-dfw,
    705708i.mce-i-wp_adv,
    706709i.mce-i-underline,
     
    734737}
    735738
    736 .qt-fullscreen {
     739.qt-dfw {
    737740    font: normal 20px/1 'dashicons';
    738741    vertical-align: top;
     
    796799i.mce-i-fullscreen:before,
    797800i.mce-i-wp_fullscreen:before,
    798 .qt-fullscreen:before {
     801i.mce-i-dfw:before,
     802.qt-dfw:before {
    799803    content: '\f211';
    800804}
     
    10721076}
    10731077
    1074 .mce-toolbar .mce-btn-group .mce-btn.mce-wp-fullscreen,
    1075 .qt-fullscreen {
     1078.mce-toolbar .mce-btn-group .mce-btn.mce-wp-dfw,
     1079.qt-dfw {
    10761080    position: absolute;
    10771081    top: 0;
     
    10821086@media screen and ( max-width: 782px ) {
    10831087    .mce-toolbar .mce-btn button,
    1084     .qt-fullscreen {
     1088    .qt-dfw {
    10851089        padding: 6px 7px;
    10861090    }
     
    10911095    }
    10921096
    1093     .qt-fullscreen {
     1097    .qt-dfw {
    10941098        width: 36px;
    10951099        height: 34px;
    10961100    }
    10971101
    1098     .mce-toolbar .mce-btn-group .mce-btn.mce-wp-fullscreen {
     1102    .mce-toolbar .mce-btn-group .mce-btn.mce-wp-dfw {
    10991103        margin: 4px 4px 0 0;
    11001104    }
     
    21102114
    21112115/* TODO: DFW responsive */
     2116
     2117/* DFW 2
     2118-------------------------------------------------------------- */
     2119
     2120.focus-on .wrap > h2,
     2121.focus-on #wpfooter,
     2122.focus-on .postbox-container,
     2123.focus-on div.updated,
     2124.focus-on div.error,
     2125.focus-on #wp-toolbar {
     2126    opacity: 0;
     2127    -webkit-transition-duration: 0.6s;
     2128    transition-duration: 0.6s;
     2129    -webkit-transition-property: opacity;
     2130    transition-property: opacity;
     2131    -webkit-transition-timing-function: ease-in-out;
     2132    transition-timing-function: ease-in-out;
     2133}
     2134
     2135.focus-on #wp-toolbar {
     2136    opacity: 0.3;
     2137}
     2138
     2139.focus-off .wrap > h2,
     2140.focus-off #wpfooter,
     2141.focus-off .postbox-container,
     2142.focus-off div.updated,
     2143.focus-off div.error,
     2144.focus-off #wp-toolbar {
     2145    opacity: 1;
     2146    -webkit-transition-duration: 0.2s;
     2147    transition-duration: 0.2s;
     2148    -webkit-transition-property: opacity;
     2149    transition-property: opacity;
     2150    -webkit-transition-timing-function: ease-in-out;
     2151    transition-timing-function: ease-in-out;
     2152}
     2153
     2154.focus-on #adminmenuback,
     2155.focus-on #adminmenuwrap,
     2156.focus-on .screen-meta-toggle {
     2157    -webkit-transition-duration: 0.6s;
     2158    transition-duration: 0.6s;
     2159    -webkit-transition-property: -webkit-transform;
     2160    transition-property: transform;
     2161    -webkit-transition-timing-function: ease-in-out;
     2162    transition-timing-function: ease-in-out;
     2163}
     2164
     2165.focus-on #adminmenuback,
     2166.focus-on #adminmenuwrap {
     2167    -webkit-transform: translateX( -100% );
     2168    -ms-transform: translateX( -100% );
     2169    transform: translateX( -100% );
     2170}
     2171
     2172.focus-on .screen-meta-toggle {
     2173    -webkit-transform: translateY( -100% );
     2174    -ms-transform: translateY( -100% );
     2175    transform: translateY( -100% );
     2176}
     2177
     2178.focus-off #adminmenuback,
     2179.focus-off #adminmenuwrap,
     2180.focus-off .screen-meta-toggle {
     2181    -webkit-transform: translateX( 0 );
     2182    -ms-transform: translateX( 0 );
     2183    transform: translateX( 0 );
     2184    -webkit-transition-duration: 0.2s;
     2185    transition-duration: 0.2s;
     2186    -webkit-transition-property: -webkit-transform;
     2187    transition-property: transform;
     2188    -webkit-transition-timing-function: ease-in-out;
     2189    transition-timing-function: ease-in-out;
     2190}
  • trunk/src/wp-includes/js/quicktags.js

    r29889 r30338  
    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 ) {
     
    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;
     
    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>';
    412         }
     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>';
     425        }
     426
    413427        return '<input type="button" id="' + idPrefix + this.id + '" class="ed_button button button-small"' + title + ' value="' + this.display + '" />';
    414428    };
     
    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);
  • trunk/src/wp-includes/js/tinymce/plugins/wordpress/plugin.js

    r29317 r30338  
    77    var DOM = tinymce.DOM, wpAdvButton, modKey, style,
    88        last = 0;
     9
     10    if ( typeof window.jQuery !== 'undefined' ) {
     11        window.jQuery( document ).triggerHandler( 'tinymce-editor-setup', [ editor ] );
     12    }
    913
    1014    function toggleToolbars( state ) {
  • trunk/src/wp-includes/js/tinymce/plugins/wpautoresize/plugin.js

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

    r29049 r30338  
    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
  • trunk/src/wp-includes/js/tinymce/skins/wordpress/wp-content.css

    r29986 r30338  
    1717    margin: 9px 10px;
    1818    max-width: 100%;
     19    -webkit-font-smoothing: antialiased !important;
    1920}
    2021
  • trunk/src/wp-includes/script-loader.php

    r29989 r30338  
    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
  • trunk/src/wp-includes/version.php

    r30134 r30338  
    1919 * @global string $tinymce_version
    2020 */
    21 $tinymce_version = '4106-20141022';
     21$tinymce_version = '4106-20141113';
    2222
    2323/**
Note: See TracChangeset for help on using the changeset viewer.