Index: /trunk/src/wp-admin/edit-form-advanced.php =================================================================== --- /trunk/src/wp-admin/edit-form-advanced.php (revision 30337) +++ /trunk/src/wp-admin/edit-form-advanced.php (revision 30338) @@ -513,5 +513,4 @@ post_content, 'content', array( - 'dfw' => true, 'drag_drop_upload' => true, 'tabfocus_elements' => 'content-html,save-post', Index: /trunk/src/wp-admin/js/editor-expand.js =================================================================== --- /trunk/src/wp-admin/js/editor-expand.js (revision 30337) +++ /trunk/src/wp-admin/js/editor-expand.js (revision 30338) @@ -1,244 +1,120 @@ -/* global tinymce */ - -window.wp = window.wp || {}; - -jQuery( document ).ready( function( $ ) { +( function( window, $, undefined ) { + 'use strict'; + var $window = $( window ), $document = $( document ), $adminBar = $( '#wpadminbar' ), - $footer = $( '#wpfooter' ), - $wrap = $( '#postdivrich' ), - $contentWrap = $( '#wp-content-wrap' ), - $tools = $( '#wp-content-editor-tools' ), - $visualTop = $(), - $visualEditor = $(), - $textTop = $( '#ed_toolbar' ), - $textEditor = $( '#content' ), - $textEditorClone = $( '
' ), - $bottom = $( '#post-status-info' ), - $menuBar = $(), - $statusBar = $(), - $sideSortables = $( '#side-sortables' ), - $postboxContainer = $( '#postbox-container-1' ), - $postBody = $('#post-body'), - fullscreen = window.wp.editor && window.wp.editor.fullscreen, - mceEditor, - mceBind = function(){}, - mceUnbind = function(){}, - fixedTop = false, - fixedBottom = false, - fixedSideTop = false, - fixedSideBottom = false, - scrollTimer, - lastScrollPosition = 0, - pageYOffsetAtTop = 130, - pinnedToolsTop = 56, - sidebarBottom = 20, - autoresizeMinHeight = 300, - initialMode = window.getUserSetting( 'editor' ), - // These are corrected when adjust() runs, except on scrolling if already set. - heights = { - windowHeight: 0, - windowWidth: 0, - adminBarHeight: 0, - toolsHeight: 0, - menuBarHeight: 0, - visualTopHeight: 0, - textTopHeight: 0, - bottomHeight: 0, - statusBarHeight: 0, - sideSortablesHeight: 0 - }; - - $textEditorClone.insertAfter( $textEditor ); - - $textEditorClone.css( { - 'font-family': $textEditor.css( 'font-family' ), - 'font-size': $textEditor.css( 'font-size' ), - 'line-height': $textEditor.css( 'line-height' ), - 'white-space': 'pre-wrap', - 'word-wrap': 'break-word' - } ); - - function getHeights() { - var windowWidth = $window.width(); - - heights = { - windowHeight: $window.height(), - windowWidth: windowWidth, - adminBarHeight: ( windowWidth > 600 ? $adminBar.outerHeight() : 0 ), - toolsHeight: $tools.outerHeight() || 0, - menuBarHeight: $menuBar.outerHeight() || 0, - visualTopHeight: $visualTop.outerHeight() || 0, - textTopHeight: $textTop.outerHeight() || 0, - bottomHeight: $bottom.outerHeight() || 0, - statusBarHeight: $statusBar.outerHeight() || 0, - sideSortablesHeight: $sideSortables.height() || 0 - }; - - // Adjust for hidden - if ( heights.menuBarHeight < 3 ) { - heights.menuBarHeight = 0; - } - } - - function textEditorKeyup( event ) { - var VK = jQuery.ui.keyCode, - key = event.keyCode, - range = document.createRange(), - selStart = $textEditor[0].selectionStart, - selEnd = $textEditor[0].selectionEnd, - textNode = $textEditorClone[0].firstChild, - buffer = 10, - offset, cursorTop, cursorBottom, editorTop, editorBottom; - - if ( selStart && selEnd && selStart !== selEnd ) { - return; - } - - // These are not TinyMCE ranges. - try { - range.setStart( textNode, selStart ); - range.setEnd( textNode, selEnd + 1 ); - } catch ( ex ) {} - - offset = range.getBoundingClientRect(); - - if ( ! offset.height ) { - return; - } - - cursorTop = offset.top - buffer; - cursorBottom = cursorTop + offset.height + buffer; - editorTop = heights.adminBarHeight + heights.toolsHeight + heights.textTopHeight; - editorBottom = heights.windowHeight - heights.bottomHeight; - - if ( cursorTop < editorTop && ( key === VK.UP || key === VK.LEFT || key === VK.BACKSPACE ) ) { - window.scrollTo( window.pageXOffset, cursorTop + window.pageYOffset - editorTop ); - } else if ( cursorBottom > editorBottom ) { - window.scrollTo( window.pageXOffset, cursorBottom + window.pageYOffset - editorBottom ); - } - } - - function textEditorResize() { - if ( ( mceEditor && ! mceEditor.isHidden() ) || ( ! mceEditor && initialMode === 'tinymce' ) ) { - return; - } - - var textEditorHeight = $textEditor.height(), - hiddenHeight; - - $textEditorClone.width( $textEditor.width() - 22 ); - $textEditorClone.text( $textEditor.val() + ' ' ); - - hiddenHeight = $textEditorClone.height(); - - if ( hiddenHeight < autoresizeMinHeight ) { - hiddenHeight = autoresizeMinHeight; - } - - if ( hiddenHeight === textEditorHeight ) { - return; - } - - $textEditor.height( hiddenHeight ); - - adjust(); - } - - // We need to wait for TinyMCE to initialize. - $document.on( 'tinymce-editor-init.editor-expand', function( event, editor ) { - var hideFloatPanels = _.debounce( function() { - ! $( '.mce-floatpanel:hover' ).length && tinymce.ui.FloatPanel.hideAll(); - $( '.mce-tooltip' ).hide(); - }, 1000, true ); - - // Make sure it's the main editor. - if ( editor.id !== 'content' ) { - return; - } - - // Copy the editor instance. - mceEditor = editor; - - // Set the minimum height to the initial viewport height. - editor.settings.autoresize_min_height = autoresizeMinHeight; - - // Get the necessary UI elements. - $visualTop = $contentWrap.find( '.mce-toolbar-grp' ); - $visualEditor = $contentWrap.find( '.mce-edit-area' ); - $statusBar = $contentWrap.find( '.mce-statusbar' ); - $menuBar = $contentWrap.find( '.mce-menubar' ); - - function mceGetCursorOffset() { - var node = editor.selection.getNode(), - range, view, offset; - - if ( editor.plugins.wpview && ( view = editor.plugins.wpview.getView( node ) ) ) { - offset = view.getBoundingClientRect(); - } else { - range = editor.selection.getRng(); - - try { - offset = range.getClientRects()[0]; - } catch( er ) {} - - if ( ! offset ) { - offset = node.getBoundingClientRect(); - } - } - - return offset.height ? offset : false; - } - - // Make sure the cursor is always visible. - // This is not only necessary to keep the cursor between the toolbars, - // but also to scroll the window when the cursor moves out of the viewport to a wpview. - // Setting a buffer > 0 will prevent the browser default. - // Some browsers will scroll to the middle, - // others to the top/bottom of the *window* when moving the cursor out of the viewport. - function mceKeyup( event ) { - var VK = tinymce.util.VK, - key = event.keyCode; - - // Bail on special keys. - if ( key <= 47 && ! ( key === VK.SPACEBAR || key === VK.ENTER || key === VK.DELETE || key === VK.BACKSPACE || - key === VK.UP || key === VK.RIGHT || key === VK.DOWN || key === VK.LEFT ) ) { - + $footer = $( '#wpfooter' ); + + /* Autoresize editor. */ + $( function() { + var $wrap = $( '#postdivrich' ), + $contentWrap = $( '#wp-content-wrap' ), + $tools = $( '#wp-content-editor-tools' ), + $visualTop = $(), + $visualEditor = $(), + $textTop = $( '#ed_toolbar' ), + $textEditor = $( '#content' ), + $textEditorClone = $( '' ), + $bottom = $( '#post-status-info' ), + $menuBar = $(), + $statusBar = $(), + $sideSortables = $( '#side-sortables' ), + $postboxContainer = $( '#postbox-container-1' ), + $postBody = $('#post-body'), + fullscreen = window.wp.editor && window.wp.editor.fullscreen, + mceEditor, + mceBind = function(){}, + mceUnbind = function(){}, + fixedTop = false, + fixedBottom = false, + fixedSideTop = false, + fixedSideBottom = false, + scrollTimer, + lastScrollPosition = 0, + pageYOffsetAtTop = 130, + pinnedToolsTop = 56, + sidebarBottom = 20, + autoresizeMinHeight = 300, + initialMode = window.getUserSetting( 'editor' ), + advanced = !! parseInt( window.getUserSetting( 'hidetb' ), 10 ), + // These are corrected when adjust() runs, except on scrolling if already set. + heights = { + windowHeight: 0, + windowWidth: 0, + adminBarHeight: 0, + toolsHeight: 0, + menuBarHeight: 0, + visualTopHeight: 0, + textTopHeight: 0, + bottomHeight: 0, + statusBarHeight: 0, + sideSortablesHeight: 0 + }; + + $textEditorClone.insertAfter( $textEditor ); + + $textEditorClone.css( { + 'font-family': $textEditor.css( 'font-family' ), + 'font-size': $textEditor.css( 'font-size' ), + 'line-height': $textEditor.css( 'line-height' ), + 'white-space': 'pre-wrap', + 'word-wrap': 'break-word' + } ); + + function getHeights() { + var windowWidth = $window.width(); + + heights = { + windowHeight: $window.height(), + windowWidth: windowWidth, + adminBarHeight: ( windowWidth > 600 ? $adminBar.outerHeight() : 0 ), + toolsHeight: $tools.outerHeight() || 0, + menuBarHeight: $menuBar.outerHeight() || 0, + visualTopHeight: $visualTop.outerHeight() || 0, + textTopHeight: $textTop.outerHeight() || 0, + bottomHeight: $bottom.outerHeight() || 0, + statusBarHeight: $statusBar.outerHeight() || 0, + sideSortablesHeight: $sideSortables.height() || 0 + }; + + // Adjust for hidden + if ( heights.menuBarHeight < 3 ) { + heights.menuBarHeight = 0; + } + } + + function textEditorKeyup( event ) { + var VK = jQuery.ui.keyCode, + key = event.keyCode, + range = document.createRange(), + selStart = $textEditor[0].selectionStart, + selEnd = $textEditor[0].selectionEnd, + textNode = $textEditorClone[0].firstChild, + buffer = 10, + offset, cursorTop, cursorBottom, editorTop, editorBottom; + + if ( selStart && selEnd && selStart !== selEnd ) { return; - // OS keys, function keys, num lock, scroll lock - } else if ( ( key >= 91 && key <= 93 ) || ( key >= 112 && key <= 123 ) || key === 144 || key === 145 ) { + } + + // These are not TinyMCE ranges. + try { + range.setStart( textNode, selStart ); + range.setEnd( textNode, selEnd + 1 ); + } catch ( ex ) {} + + offset = range.getBoundingClientRect(); + + if ( ! offset.height ) { return; } - mceScroll( key ); - } - - function mceScroll( key ) { - var VK = tinymce.util.VK, - offset = mceGetCursorOffset(), - buffer = 10, - cursorTop, cursorBottom, editorTop, editorBottom; - - if ( ! offset ) { - return; - } - - cursorTop = offset.top + editor.iframeElement.getBoundingClientRect().top; + cursorTop = offset.top - buffer; cursorBottom = cursorTop + offset.height + buffer; - cursorTop -= buffer; - editorTop = heights.adminBarHeight + heights.toolsHeight + heights.menuBarHeight + heights.visualTopHeight; - editorBottom = heights.windowHeight - heights.bottomHeight - heights.statusBarHeight; - - // Don't scroll if the node is taller than the visible part of the editor - if ( editorBottom - editorTop < offset.height ) { - return; - } - - // WebKit browsers scroll-into-view to the middle of the window but not for arrow keys/backspace. - // The others scroll to the top of the window, we need to account for the adminbar and editor toolbar(s). - if ( cursorTop < editorTop && ( ! tinymce.Env.webkit || - ( key === VK.UP || key === VK.RIGHT || key === VK.DOWN || key === VK.LEFT || key === VK.BACKSPACE ) ) ) { - + editorTop = heights.adminBarHeight + heights.toolsHeight + heights.textTopHeight; + editorBottom = heights.windowHeight - heights.bottomHeight; + + if ( cursorTop < editorTop && ( key === VK.UP || key === VK.LEFT || key === VK.BACKSPACE ) ) { window.scrollTo( window.pageXOffset, cursorTop + window.pageYOffset - editorTop ); } else if ( cursorBottom > editorBottom ) { @@ -247,162 +123,234 @@ } - // Adjust when switching editor modes. - function mceShow() { - $window.on( 'scroll.mce-float-panels', hideFloatPanels ); - - setTimeout( function() { - editor.execCommand( 'wpAutoResize' ); + function textEditorResize() { + if ( ( mceEditor && ! mceEditor.isHidden() ) || ( ! mceEditor && initialMode === 'tinymce' ) ) { + return; + } + + var textEditorHeight = $textEditor.height(), + hiddenHeight; + + $textEditorClone.width( $textEditor.width() - 22 ); + $textEditorClone.text( $textEditor.val() + ' ' ); + + hiddenHeight = $textEditorClone.height(); + + if ( hiddenHeight < autoresizeMinHeight ) { + hiddenHeight = autoresizeMinHeight; + } + + if ( hiddenHeight === textEditorHeight ) { + return; + } + + $textEditor.height( hiddenHeight ); + + adjust(); + } + + // We need to wait for TinyMCE to initialize. + $document.on( 'tinymce-editor-init.editor-expand', function( event, editor ) { + var VK = window.tinymce.util.VK, + hideFloatPanels = _.debounce( function() { + ! $( '.mce-floatpanel:hover' ).length && window.tinymce.ui.FloatPanel.hideAll(); + $( '.mce-tooltip' ).hide(); + }, 1000, true ); + + // Make sure it's the main editor. + if ( editor.id !== 'content' ) { + return; + } + + // Copy the editor instance. + mceEditor = editor; + + // Set the minimum height to the initial viewport height. + editor.settings.autoresize_min_height = autoresizeMinHeight; + + // Get the necessary UI elements. + $visualTop = $contentWrap.find( '.mce-toolbar-grp' ); + $visualEditor = $contentWrap.find( '.mce-edit-area' ); + $statusBar = $contentWrap.find( '.mce-statusbar' ); + $menuBar = $contentWrap.find( '.mce-menubar' ); + + function mceGetCursorOffset() { + var node = editor.selection.getNode(), + range, view, offset; + + if ( editor.plugins.wpview && ( view = editor.plugins.wpview.getView( node ) ) ) { + offset = view.getBoundingClientRect(); + } else { + range = editor.selection.getRng(); + + try { + offset = range.getClientRects()[0]; + } catch( er ) {} + + if ( ! offset ) { + offset = node.getBoundingClientRect(); + } + } + + return offset.height ? offset : false; + } + + // Make sure the cursor is always visible. + // This is not only necessary to keep the cursor between the toolbars, + // but also to scroll the window when the cursor moves out of the viewport to a wpview. + // Setting a buffer > 0 will prevent the browser default. + // Some browsers will scroll to the middle, + // others to the top/bottom of the *window* when moving the cursor out of the viewport. + function mceKeyup( event ) { + var key = event.keyCode; + + // Bail on special keys. + 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 ) ) { + return; + // OS keys, function keys, num lock, scroll lock + } else if ( ( key >= 91 && key <= 93 ) || ( key >= 112 && key <= 123 ) || key === 144 || key === 145 ) { + return; + } + + mceScroll( key ); + } + + function mceScroll( key ) { + var offset = mceGetCursorOffset(), + buffer = 50, + cursorTop, cursorBottom, editorTop, editorBottom; + + if ( ! offset ) { + return; + } + + cursorTop = offset.top + editor.iframeElement.getBoundingClientRect().top; + cursorBottom = cursorTop + offset.height; + cursorTop = cursorTop - buffer; + cursorBottom = cursorBottom + buffer; + editorTop = heights.adminBarHeight + heights.toolsHeight + heights.menuBarHeight + heights.visualTopHeight; + editorBottom = heights.windowHeight - ( advanced ? heights.bottomHeight + heights.statusBarHeight : 0 ); + + // Don't scroll if the node is taller than the visible part of the editor + if ( editorBottom - editorTop < offset.height ) { + return; + } + + if ( cursorTop < editorTop && ( key === VK.UP || key === VK.LEFT || key === VK.BACKSPACE ) ) { + window.scrollTo( window.pageXOffset, cursorTop + window.pageYOffset - editorTop ); + } else if ( cursorBottom > editorBottom ) { + window.scrollTo( window.pageXOffset, cursorBottom + window.pageYOffset - editorBottom ); + } + } + + // Adjust when switching editor modes. + function mceShow() { + $window.on( 'scroll.mce-float-panels', hideFloatPanels ); + + setTimeout( function() { + editor.execCommand( 'wpAutoResize' ); + adjust(); + }, 300 ); + } + + function mceHide() { + $window.off( 'scroll.mce-float-panels' ); + + setTimeout( function() { + var top = $contentWrap.offset().top; + + if ( window.pageYOffset > top ) { + window.scrollTo( window.pageXOffset, top - heights.adminBarHeight ); + } + + textEditorResize(); + adjust(); + }, 100 ); + adjust(); - }, 300 ); - } - - function mceHide() { - $window.off( 'scroll.mce-float-panels' ); - - setTimeout( function() { - var top = $contentWrap.offset().top; - - if ( window.pageYOffset > top ) { - window.scrollTo( window.pageXOffset, top - heights.adminBarHeight ); - } - + } + + function toggleAdvanced() { + advanced = ! advanced; + } + + mceBind = function() { + editor.on( 'keyup', mceKeyup ); + editor.on( 'show', mceShow ); + editor.on( 'hide', mceHide ); + editor.on( 'wp-toolbar-toggle', toggleAdvanced ); + // Adjust when the editor resizes. + editor.on( 'setcontent wp-autoresize wp-toolbar-toggle', adjust ); + // Don't hide the caret after undo/redo + editor.on( 'undo redo', mceScroll ); + + $window.off( 'scroll.mce-float-panels' ).on( 'scroll.mce-float-panels', hideFloatPanels ); + }; + + mceUnbind = function() { + editor.off( 'keyup', mceKeyup ); + editor.off( 'show', mceShow ); + editor.off( 'hide', mceHide ); + editor.off( 'wp-toolbar-toggle', toggleAdvanced ); + editor.off( 'setcontent wp-autoresize wp-toolbar-toggle', adjust ); + editor.off( 'undo redo', mceScroll ); + + $window.off( 'scroll.mce-float-panels' ); + }; + + if ( $wrap.hasClass( 'wp-editor-expand' ) ) { + // Adjust "immediately" + mceBind(); + initialResize( adjust ); + } + } ); + + // Adjust the toolbars based on the active editor mode. + function adjust( event ) { + var type = event && event.type; + + // Make sure we're not in fullscreen mode. + if ( fullscreen && fullscreen.settings.visible ) { + return; + } + + var windowPos = $window.scrollTop(), + resize = type !== 'scroll', + visual = ( mceEditor && ! mceEditor.isHidden() ), + buffer = autoresizeMinHeight, + postBodyTop = $postBody.offset().top, + borderWidth = 1, + contentWrapWidth = $contentWrap.width(), + $top, $editor, sidebarTop, footerTop, canPin, + topPos, topHeight, editorPos, editorHeight; + + // Refresh the heights + if ( resize || ! heights.windowHeight ) { + getHeights(); + } + + if ( ! visual && type === 'resize' ) { textEditorResize(); - adjust(); - }, 100 ); - - adjust(); - } - - mceBind = function() { - editor.on( 'keyup', mceKeyup ); - editor.on( 'show', mceShow ); - editor.on( 'hide', mceHide ); - // Adjust when the editor resizes. - editor.on( 'setcontent wp-autoresize wp-toolbar-toggle', adjust ); - // Scroll to the caret or selection after undo/redo - editor.on( 'undo redo', mceScroll ); - - $window.off( 'scroll.mce-float-panels' ).on( 'scroll.mce-float-panels', hideFloatPanels ); - }; - - mceUnbind = function() { - editor.off( 'keyup', mceKeyup ); - editor.off( 'show', mceShow ); - editor.off( 'hide', mceHide ); - editor.off( 'setcontent wp-autoresize wp-toolbar-toggle', adjust ); - editor.off( 'undo redo', mceScroll ); - - $window.off( 'scroll.mce-float-panels' ); - }; - - if ( $wrap.hasClass( 'wp-editor-expand' ) ) { - // Adjust "immediately" - mceBind(); - initialResize( adjust ); - } - } ); - - // Adjust the toolbars based on the active editor mode. - function adjust( type ) { - // Make sure we're not in fullscreen mode. - if ( fullscreen && fullscreen.settings.visible ) { - return; - } - - var windowPos = $window.scrollTop(), - resize = type !== 'scroll', - visual = ( mceEditor && ! mceEditor.isHidden() ), - buffer = autoresizeMinHeight, - postBodyTop = $postBody.offset().top, - borderWidth = 1, - contentWrapWidth = $contentWrap.width(), - $top, $editor, sidebarTop, footerTop, canPin, - topPos, topHeight, editorPos, editorHeight; - - // Refresh the heights - if ( resize || ! heights.windowHeight ) { - getHeights(); - } - - if ( ! visual && type === 'resize' ) { - textEditorResize(); - } - - if ( visual ) { - $top = $visualTop; - $editor = $visualEditor; - topHeight = heights.visualTopHeight; - } else { - $top = $textTop; - $editor = $textEditor; - topHeight = heights.textTopHeight; - } - - topPos = $top.parent().offset().top; - editorPos = $editor.offset().top; - editorHeight = $editor.outerHeight(); - - // Should we pin? - canPin = visual ? autoresizeMinHeight + topHeight : autoresizeMinHeight + 20; // 20px from textarea padding - canPin = editorHeight > ( canPin + 5 ); - - if ( ! canPin ) { - if ( resize ) { - $tools.css( { - position: 'absolute', - top: 0, - width: contentWrapWidth - } ); - - if ( visual && $menuBar.length ) { - $menuBar.css( { - position: 'absolute', - top: 0, - width: contentWrapWidth - ( borderWidth * 2 ) - } ); - } - - $top.css( { - position: 'absolute', - top: heights.menuBarHeight, - width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) ) - } ); - - $statusBar.add( $bottom ).attr( 'style', '' ); - } - } else { - // Maybe pin the top. - if ( ( ! fixedTop || resize ) && - // Handle scrolling down. - ( windowPos >= ( topPos - heights.toolsHeight - heights.adminBarHeight ) && - // Handle scrolling up. - windowPos <= ( topPos - heights.toolsHeight - heights.adminBarHeight + editorHeight - buffer ) ) ) { - fixedTop = true; - - $tools.css( { - position: 'fixed', - top: heights.adminBarHeight, - width: contentWrapWidth - } ); - - if ( visual && $menuBar.length ) { - $menuBar.css( { - position: 'fixed', - top: heights.adminBarHeight + heights.toolsHeight, - width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) ) - } ); - } - - $top.css( { - position: 'fixed', - top: heights.adminBarHeight + heights.toolsHeight + heights.menuBarHeight, - width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) ) - } ); - // Maybe unpin the top. - } else if ( fixedTop || resize ) { - // Handle scrolling up. - if ( windowPos <= ( topPos - heights.toolsHeight - heights.adminBarHeight ) ) { - fixedTop = false; - + } + + if ( visual ) { + $top = $visualTop; + $editor = $visualEditor; + topHeight = heights.visualTopHeight; + } else { + $top = $textTop; + $editor = $textEditor; + topHeight = heights.textTopHeight; + } + + topPos = $top.parent().offset().top; + editorPos = $editor.offset().top; + editorHeight = $editor.outerHeight(); + + // Should we pin? + canPin = visual ? autoresizeMinHeight + topHeight : autoresizeMinHeight + 20; // 20px from textarea padding + canPin = editorHeight > ( canPin + 5 ); + + if ( ! canPin ) { + if ( resize ) { $tools.css( { position: 'absolute', @@ -424,11 +372,19 @@ width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) ) } ); - // Handle scrolling down. - } else if ( windowPos >= ( topPos - heights.toolsHeight - heights.adminBarHeight + editorHeight - buffer ) ) { - fixedTop = false; + + $statusBar.add( $bottom ).attr( 'style', '' ); + } + } else { + // Maybe pin the top. + if ( ( ! fixedTop || resize ) && + // Handle scrolling down. + ( windowPos >= ( topPos - heights.toolsHeight - heights.adminBarHeight ) && + // Handle scrolling up. + windowPos <= ( topPos - heights.toolsHeight - heights.adminBarHeight + editorHeight - buffer ) ) ) { + fixedTop = true; $tools.css( { - position: 'absolute', - top: editorHeight - buffer, + position: 'fixed', + top: heights.adminBarHeight, width: contentWrapWidth } ); @@ -436,298 +392,791 @@ if ( visual && $menuBar.length ) { $menuBar.css( { + position: 'fixed', + top: heights.adminBarHeight + heights.toolsHeight, + width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) ) + } ); + } + + $top.css( { + position: 'fixed', + top: heights.adminBarHeight + heights.toolsHeight + heights.menuBarHeight, + width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) ) + } ); + // Maybe unpin the top. + } else if ( fixedTop || resize ) { + // Handle scrolling up. + if ( windowPos <= ( topPos - heights.toolsHeight - heights.adminBarHeight ) ) { + fixedTop = false; + + $tools.css( { + position: 'absolute', + top: 0, + width: contentWrapWidth + } ); + + if ( visual && $menuBar.length ) { + $menuBar.css( { + position: 'absolute', + top: 0, + width: contentWrapWidth - ( borderWidth * 2 ) + } ); + } + + $top.css( { + position: 'absolute', + top: heights.menuBarHeight, + width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) ) + } ); + // Handle scrolling down. + } else if ( windowPos >= ( topPos - heights.toolsHeight - heights.adminBarHeight + editorHeight - buffer ) ) { + fixedTop = false; + + $tools.css( { position: 'absolute', top: editorHeight - buffer, + width: contentWrapWidth + } ); + + if ( visual && $menuBar.length ) { + $menuBar.css( { + position: 'absolute', + top: editorHeight - buffer, + width: contentWrapWidth - ( borderWidth * 2 ) + } ); + } + + $top.css( { + position: 'absolute', + top: editorHeight - buffer + heights.menuBarHeight, + width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) ) + } ); + } + } + + // Maybe adjust the bottom bar. + if ( ( ! fixedBottom || ( resize && advanced ) ) && + // +[n] for the border around the .wp-editor-container. + ( windowPos + heights.windowHeight ) <= ( editorPos + editorHeight + heights.bottomHeight + heights.statusBarHeight + borderWidth ) ) { + + if ( event && event.deltaHeight > 0 ) { + window.scrollBy( 0, event.deltaHeight ); + } else if ( advanced ) { + fixedBottom = true; + + $statusBar.css( { + position: 'fixed', + bottom: heights.bottomHeight, + visibility: '', width: contentWrapWidth - ( borderWidth * 2 ) } ); - } - - $top.css( { - position: 'absolute', - top: editorHeight - buffer + heights.menuBarHeight, - width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) ) - } ); - } - } - - // Maybe adjust the bottom bar. - if ( ( ! fixedBottom || resize ) && - // +[n] for the border around the .wp-editor-container. - ( windowPos + heights.windowHeight ) <= ( editorPos + editorHeight + heights.bottomHeight + heights.statusBarHeight + borderWidth ) ) { - fixedBottom = true; - - $statusBar.css( { - position: 'fixed', - bottom: heights.bottomHeight, - width: contentWrapWidth - ( borderWidth * 2 ) + + $bottom.css( { + position: 'fixed', + bottom: 0, + width: contentWrapWidth + } ); + } + } else if ( ( ! advanced && fixedBottom ) || + ( ( fixedBottom || resize ) && + ( windowPos + heights.windowHeight ) > ( editorPos + editorHeight + heights.bottomHeight + heights.statusBarHeight - borderWidth ) ) ) { + fixedBottom = false; + + $statusBar.add( $bottom ).attr( 'style', '' ); + + ! advanced && $statusBar.css( 'visibility', 'hidden' ); + } + } + + // Sidebar pinning + if ( $postboxContainer.width() < 300 && heights.windowWidth > 600 && // sidebar position is changed with @media from CSS, make sure it is on the side + $document.height() > ( $sideSortables.height() + postBodyTop + 120 ) && // the sidebar is not the tallest element + heights.windowHeight < editorHeight ) { // the editor is taller than the viewport + + if ( ( heights.sideSortablesHeight + pinnedToolsTop + sidebarBottom ) > heights.windowHeight || fixedSideTop || fixedSideBottom ) { + // Reset when scrolling to the top + if ( windowPos + pinnedToolsTop <= postBodyTop ) { + $sideSortables.attr( 'style', '' ); + fixedSideTop = fixedSideBottom = false; + } else { + if ( windowPos > lastScrollPosition ) { + // Scrolling down + if ( fixedSideTop ) { + // let it scroll + fixedSideTop = false; + sidebarTop = $sideSortables.offset().top - heights.adminBarHeight; + footerTop = $footer.offset().top; + + // don't get over the footer + if ( footerTop < sidebarTop + heights.sideSortablesHeight + sidebarBottom ) { + sidebarTop = footerTop - heights.sideSortablesHeight - 12; + } + + $sideSortables.css({ + position: 'absolute', + top: sidebarTop, + bottom: '' + }); + } else if ( ! fixedSideBottom && heights.sideSortablesHeight + $sideSortables.offset().top + sidebarBottom < windowPos + heights.windowHeight ) { + // pin the bottom + fixedSideBottom = true; + + $sideSortables.css({ + position: 'fixed', + top: 'auto', + bottom: sidebarBottom + }); + } + } else if ( windowPos < lastScrollPosition ) { + // Scrolling up + if ( fixedSideBottom ) { + // let it scroll + fixedSideBottom = false; + sidebarTop = $sideSortables.offset().top - sidebarBottom; + footerTop = $footer.offset().top; + + // don't get over the footer + if ( footerTop < sidebarTop + heights.sideSortablesHeight + sidebarBottom ) { + sidebarTop = footerTop - heights.sideSortablesHeight - 12; + } + + $sideSortables.css({ + position: 'absolute', + top: sidebarTop, + bottom: '' + }); + } else if ( ! fixedSideTop && $sideSortables.offset().top >= windowPos + pinnedToolsTop ) { + // pin the top + fixedSideTop = true; + + $sideSortables.css({ + position: 'fixed', + top: pinnedToolsTop, + bottom: '' + }); + } + } + } + } else { + // if the sidebar container is smaller than the viewport, then pin/unpin the top when scrolling + if ( windowPos >= ( postBodyTop - pinnedToolsTop ) ) { + + $sideSortables.css( { + position: 'fixed', + top: pinnedToolsTop + } ); + } else { + $sideSortables.attr( 'style', '' ); + } + + fixedSideTop = fixedSideBottom = false; + } + + lastScrollPosition = windowPos; + } else { + $sideSortables.attr( 'style', '' ); + fixedSideTop = fixedSideBottom = false; + } + + if ( resize ) { + $contentWrap.css( { + paddingTop: heights.toolsHeight } ); - $bottom.css( { - position: 'fixed', - bottom: 0, - width: contentWrapWidth - } ); - } else if ( ( fixedBottom || resize ) && - ( windowPos + heights.windowHeight ) > ( editorPos + editorHeight + heights.bottomHeight + heights.statusBarHeight - borderWidth ) ) { - fixedBottom = false; - - $statusBar.add( $bottom ).attr( 'style', '' ); - } - } - - // Sidebar pinning - if ( $postboxContainer.width() < 300 && heights.windowWidth > 600 && // sidebar position is changed with @media from CSS, make sure it is on the side - $document.height() > ( $sideSortables.height() + postBodyTop + 120 ) && // the sidebar is not the tallest element - heights.windowHeight < editorHeight ) { // the editor is taller than the viewport - - if ( ( heights.sideSortablesHeight + pinnedToolsTop + sidebarBottom ) > heights.windowHeight || fixedSideTop || fixedSideBottom ) { - // Reset when scrolling to the top - if ( windowPos + pinnedToolsTop <= postBodyTop ) { - $sideSortables.attr( 'style', '' ); - fixedSideTop = fixedSideBottom = false; - } else { - if ( windowPos > lastScrollPosition ) { - // Scrolling down - if ( fixedSideTop ) { - // let it scroll - fixedSideTop = false; - sidebarTop = $sideSortables.offset().top - heights.adminBarHeight; - footerTop = $footer.offset().top; - - // don't get over the footer - if ( footerTop < sidebarTop + heights.sideSortablesHeight + sidebarBottom ) { - sidebarTop = footerTop - heights.sideSortablesHeight - 12; - } - - $sideSortables.css({ - position: 'absolute', - top: sidebarTop, - bottom: '' - }); - } else if ( ! fixedSideBottom && heights.sideSortablesHeight + $sideSortables.offset().top + sidebarBottom < windowPos + heights.windowHeight ) { - // pin the bottom - fixedSideBottom = true; - - $sideSortables.css({ - position: 'fixed', - top: 'auto', - bottom: sidebarBottom - }); - } - } else if ( windowPos < lastScrollPosition ) { - // Scrolling up - if ( fixedSideBottom ) { - // let it scroll - fixedSideBottom = false; - sidebarTop = $sideSortables.offset().top - sidebarBottom; - footerTop = $footer.offset().top; - - // don't get over the footer - if ( footerTop < sidebarTop + heights.sideSortablesHeight + sidebarBottom ) { - sidebarTop = footerTop - heights.sideSortablesHeight - 12; - } - - $sideSortables.css({ - position: 'absolute', - top: sidebarTop, - bottom: '' - }); - } else if ( ! fixedSideTop && $sideSortables.offset().top >= windowPos + pinnedToolsTop ) { - // pin the top - fixedSideTop = true; - - $sideSortables.css({ - position: 'fixed', - top: pinnedToolsTop, - bottom: '' - }); - } - } - } - } else { - // if the sidebar container is smaller than the viewport, then pin/unpin the top when scrolling - if ( windowPos >= ( postBodyTop - pinnedToolsTop ) ) { - - $sideSortables.css( { - position: 'fixed', - top: pinnedToolsTop + if ( visual ) { + $visualEditor.css( { + paddingTop: heights.visualTopHeight + heights.menuBarHeight } ); } else { - $sideSortables.attr( 'style', '' ); - } - - fixedSideTop = fixedSideBottom = false; - } - - lastScrollPosition = windowPos; - } else { - $sideSortables.attr( 'style', '' ); - fixedSideTop = fixedSideBottom = false; - } - - if ( resize ) { - $contentWrap.css( { - paddingTop: heights.toolsHeight + $textEditor.css( { + marginTop: heights.textTopHeight + } ); + + $textEditorClone.width( contentWrapWidth - 20 - ( borderWidth * 2 ) ); + } + } + } + + function fullscreenHide() { + textEditorResize(); + adjust(); + } + + function initialResize( callback ) { + for ( var i = 1; i < 6; i++ ) { + setTimeout( callback, 500 * i ); + } + } + + function afterScroll() { + clearTimeout( scrollTimer ); + scrollTimer = setTimeout( adjust, 100 ); + } + + function on() { + // Scroll to the top when triggering this from JS. + // Ensures toolbars are pinned properly. + if ( window.pageYOffset && window.pageYOffset > pageYOffsetAtTop ) { + window.scrollTo( window.pageXOffset, 0 ); + } + + $wrap.addClass( 'wp-editor-expand' ); + + // Adjust when the window is scrolled or resized. + $window.on( 'scroll.editor-expand resize.editor-expand', function( event ) { + adjust( event.type ); + afterScroll(); } ); - if ( visual ) { - $visualEditor.css( { - paddingTop: heights.visualTopHeight + heights.menuBarHeight + // Adjust when collapsing the menu, changing the columns, changing the body class. + $document.on( 'wp-collapse-menu.editor-expand postboxes-columnchange.editor-expand editor-classchange.editor-expand', adjust ) + .on( 'postbox-toggled.editor-expand', function() { + if ( ! fixedSideTop && ! fixedSideBottom && window.pageYOffset > pinnedToolsTop ) { + fixedSideBottom = true; + window.scrollBy( 0, -1 ); + adjust(); + window.scrollBy( 0, 1 ); + } + + adjust(); + }).on( 'wp-window-resized.editor-expand', function() { + if ( mceEditor && ! mceEditor.isHidden() ) { + mceEditor.execCommand( 'wpAutoResize' ); + } else { + textEditorResize(); + } + }); + + $textEditor.on( 'focus.editor-expand input.editor-expand propertychange.editor-expand', textEditorResize ); + $textEditor.on( 'keyup.editor-expand', textEditorKeyup ); + mceBind(); + + // Adjust when entering/exiting fullscreen mode. + fullscreen && fullscreen.pubsub.subscribe( 'hidden', fullscreenHide ); + + if ( mceEditor ) { + mceEditor.settings.wp_autoresize_on = true; + mceEditor.execCommand( 'wpAutoResizeOn' ); + + if ( ! mceEditor.isHidden() ) { + mceEditor.execCommand( 'wpAutoResize' ); + } + } + + if ( ! mceEditor || mceEditor.isHidden() ) { + textEditorResize(); + } + + adjust(); + + $document.trigger( 'editor-expand-on' ); + } + + function off() { + var height = window.getUserSetting('ed_size'); + + // Scroll to the top when triggering this from JS. + // Ensures toolbars are reset properly. + if ( window.pageYOffset && window.pageYOffset > pageYOffsetAtTop ) { + window.scrollTo( window.pageXOffset, 0 ); + } + + $wrap.removeClass( 'wp-editor-expand' ); + + $window.off( '.editor-expand' ); + $document.off( '.editor-expand' ); + $textEditor.off( '.editor-expand' ); + mceUnbind(); + + // Adjust when entering/exiting fullscreen mode. + fullscreen && fullscreen.pubsub.unsubscribe( 'hidden', fullscreenHide ); + + // Reset all css + $.each( [ $visualTop, $textTop, $tools, $menuBar, $bottom, $statusBar, $contentWrap, $visualEditor, $textEditor, $sideSortables ], function( i, element ) { + element && element.attr( 'style', '' ); + }); + + fixedTop = fixedBottom = fixedSideTop = fixedSideBottom = false; + + if ( mceEditor ) { + mceEditor.settings.wp_autoresize_on = false; + mceEditor.execCommand( 'wpAutoResizeOff' ); + + if ( ! mceEditor.isHidden() ) { + $textEditor.hide(); + + if ( height ) { + mceEditor.theme.resizeTo( null, height ); + } + } + } + + if ( height ) { + $textEditor.height( height ); + } + + $document.trigger( 'editor-expand-off' ); + } + + // Start on load + if ( $wrap.hasClass( 'wp-editor-expand' ) ) { + on(); + + // Ideally we need to resize just after CSS has fully loaded and QuickTags is ready. + if ( $contentWrap.hasClass( 'html-active' ) ) { + initialResize( function() { + adjust(); + textEditorResize(); } ); + } + } + + // Show the on/off checkbox + $( '#adv-settings .editor-expand' ).show(); + $( '#editor-expand-toggle' ).on( 'change.editor-expand', function() { + if ( $(this).prop( 'checked' ) ) { + on(); + window.setUserSetting( 'editor_expand', 'on' ); } else { - $textEditor.css( { - marginTop: heights.textTopHeight + off(); + window.setUserSetting( 'editor_expand', 'off' ); + } + }); + + // Expose on() and off() + window.editorExpand = { + on: on, + off: off + }; + } ); + + /* DFW. */ + $( function() { + var $body = $( document.body ), + $wrap = $( '#wpcontent' ), + $editor = $( '#post-body-content' ), + $title = $( '#title' ), + $content = $( '#content' ), + $overlay = $( document.createElement( 'DIV' ) ), + $slug = $( '#edit-slug-box' ), + $slugFocusEl = $slug.find( 'a' ) + .add( $slug.find( 'button' ) ) + .add( $slug.find( 'input' ) ), + $menuWrap = $( '#adminmenuwrap' ), + $editorWindow = $(), + $editorIframe = $(), + _isActive = window.getUserSetting( 'editor_expand', 'on' ) === 'on', + _isOn = _isActive ? !! parseInt( window.getUserSetting( 'dfw', '1' ), 10 ) : false, + traveledX = 0, + traveledY = 0, + buffer = 20, + faded, fadedAdminBar, fadedSlug, + editorRect, x, y, mouseY, scrollY, + focusLostTimer, overlayTimer, editorHasFocus; + + $body.append( $overlay ); + + $overlay.css( { + display: 'none', + position: 'fixed', + top: $adminBar.height(), + right: 0, + bottom: 0, + left: 0, + 'z-index': 9997 + } ); + + $editor.css( { + position: 'relative' + } ); + + $window.on( 'mousemove.focus', function( event ) { + mouseY = event.pageY; + } ); + + function activate() { + if ( ! _isActive ) { + _isActive = true; + + $document.trigger( 'dfw-activate' ); + } + } + + function deactivate() { + if ( _isActive ) { + off(); + + _isActive = false; + + $document.trigger( 'dfw-deactivate' ); + } + } + + function isActive() { + return _isActive; + } + + function on() { + if ( ! _isOn && _isActive ) { + _isOn = true; + + $content.on( 'keydown.focus', fadeOut ); + + $title.add( $content ).on( 'blur.focus', maybeFadeIn ); + + fadeOut(); + + window.setUserSetting( 'dfw', '1' ); + + $document.trigger( 'dfw-on' ); + } + } + + function off() { + if ( _isOn ) { + _isOn = false; + + $title.add( $content ).off( '.focus' ); + + fadeIn(); + + $editor.off( '.focus' ); + + window.setUserSetting( 'dfw', '0' ); + + $document.trigger( 'dfw-off' ); + } + } + + function toggle() { + ( _isOn ? off : on )(); + } + + function isOn() { + return _isOn; + } + + function fadeOut( event ) { + var key = event && event.keyCode; + + if ( key === 27 ) { + fadeIn(); + return; + } + + if ( event && ( event.metaKey || ( event.ctrlKey && ! event.altKey ) || ( key && ( + // Special keys ( tab, ctrl, alt, esc, arrow keys... ) + ( key <= 47 && key !== 8 && key !== 13 && key !== 32 && key !== 46 ) || + // Windows keys + ( key >= 91 && key <= 93 ) || + // F keys + ( key >= 112 && key <= 135 ) || + // Num Lock, Scroll Lock, OEM + ( key >= 144 && key <= 150 ) || + // OEM or non-printable + key >= 224 + ) ) ) ) { + return; + } + + if ( ! faded ) { + faded = true; + + clearTimeout( overlayTimer ); + + overlayTimer = setTimeout( function() { + $overlay.show(); + }, 600 ); + + $editor.css( 'z-index', 9998 ); + + $overlay + // Always recalculate the editor area entering the overlay with the mouse. + .on( 'mouseenter.focus', function() { + editorRect = $editor.offset(); + editorRect.right = editorRect.left + $editor.outerWidth(); + editorRect.bottom = editorRect.top + $editor.outerHeight(); + + $window.on( 'scroll.focus', function() { + var nScrollY = window.pageYOffset; + + if ( ( + scrollY && mouseY && + scrollY !== nScrollY + ) && ( + mouseY < editorRect.top - buffer || + mouseY > editorRect.bottom + buffer + ) ) { + fadeIn(); + } + + scrollY = nScrollY; + } ); + } ) + .on( 'mouseleave.focus', function() { + x = y = null; + traveledX = traveledY = 0; + + $window.off( 'scroll.focus' ); + } ) + // Fade in when the mouse moves away form the editor area. + .on( 'mousemove.focus', function( event ) { + var nx = event.pageX, + ny = event.pageY; + + if ( x && y && ( nx !== x || ny !== y ) ) { + if ( + ( ny <= y && ny < editorRect.top ) || + ( ny >= y && ny > editorRect.bottom ) || + ( nx <= x && nx < editorRect.left ) || + ( nx >= x && nx > editorRect.right ) + ) { + traveledX += Math.abs( x - nx ); + traveledY += Math.abs( y - ny ); + + if ( ( + ny <= editorRect.top - buffer || + ny >= editorRect.bottom + buffer || + nx <= editorRect.left - buffer || + nx >= editorRect.right + buffer + ) && ( + traveledX > 10 || + traveledY > 10 + ) ) { + fadeIn(); + + x = y = null; + traveledX = traveledY = 0; + + return; + } + } else { + traveledX = traveledY = 0; + } + } + + x = nx; + y = ny; + } ) + // When the overlay is touched, always fade in and cancel the event. + .on( 'touchstart.focus', function( event ) { + event.preventDefault(); + fadeIn(); + } ); + + $editor.off( 'mouseenter.focus' ); + + if ( focusLostTimer ) { + clearTimeout( focusLostTimer ); + focusLostTimer = null; + } + + $body.addClass( 'focus-on' ).removeClass( 'focus-off' ); + } + + fadeOutAdminBar(); + fadeOutSlug(); + } + + function fadeIn() { + if ( faded ) { + faded = false; + + clearTimeout( overlayTimer ); + + overlayTimer = setTimeout( function() { + $overlay.hide(); + }, 200 ); + + $editor.css( 'z-index', '' ); + + $overlay.off( 'mouseenter.focus mouseleave.focus mousemove.focus touchstart.focus' ); + + $editor.on( 'mouseenter.focus', function() { + if ( $.contains( $editor.get( 0 ), document.activeElement ) || editorHasFocus ) { + fadeOut(); + } } ); - $textEditorClone.width( contentWrapWidth - 20 - ( borderWidth * 2 ) ); - } - } - } - - function fullscreenHide() { - textEditorResize(); - adjust(); - } - - function initialResize( callback ) { - for ( var i = 1; i < 6; i++ ) { - setTimeout( callback, 500 * i ); - } - } - - function afterScroll() { - clearTimeout( scrollTimer ); - scrollTimer = setTimeout( adjust, 100 ); - } - - function on() { - // Scroll to the top when triggering this from JS. - // Ensures toolbars are pinned properly. - if ( window.pageYOffset && window.pageYOffset > pageYOffsetAtTop ) { - window.scrollTo( window.pageXOffset, 0 ); - } - - $wrap.addClass( 'wp-editor-expand' ); - - // Adjust when the window is scrolled or resized. - $window.on( 'scroll.editor-expand resize.editor-expand', function( event ) { - adjust( event.type ); - afterScroll(); + focusLostTimer = setTimeout( function() { + focusLostTimer = null; + $editor.off( 'mouseenter.focus' ); + }, 1000 ); + + $body.addClass( 'focus-off' ).removeClass( 'focus-on' ); + } + + fadeInAdminBar(); + fadeInSlug(); + } + + function maybeFadeIn() { + setTimeout( function() { + var position = document.activeElement.compareDocumentPosition( $editor.get( 0 ) ); + + function hasFocus( $el ) { + return $.contains( $el.get( 0 ), document.activeElement ); + } + + // The focussed node is before or behind the editor area, and not ouside the wrap. + if ( ( position === 2 || position === 4 ) && ( hasFocus( $menuWrap ) || hasFocus( $wrap ) || hasFocus( $footer ) ) ) { + fadeIn(); + } + }, 0 ); + } + + function fadeOutAdminBar() { + if ( ! fadedAdminBar && faded ) { + fadedAdminBar = true; + + $adminBar + .on( 'mouseenter.focus', function() { + $adminBar.addClass( 'focus-off' ); + } ) + .on( 'mouseleave.focus', function() { + $adminBar.removeClass( 'focus-off' ); + } ); + } + } + + function fadeInAdminBar() { + if ( fadedAdminBar ) { + fadedAdminBar = false; + + $adminBar.off( '.focus' ); + } + } + + function fadeOutSlug() { + if ( ! fadedSlug && faded && ! $slug.find( ':focus').length ) { + fadedSlug = true; + + $slug.stop().fadeTo( 'fast', 0.3 ).on( 'mouseenter.focus', fadeInSlug ).off( 'mouseleave.focus' ); + + $slugFocusEl.on( 'focus.focus', fadeInSlug ).off( 'blur.focus' ); + } + } + + function fadeInSlug() { + if ( fadedSlug ) { + fadedSlug = false; + + $slug.stop().fadeTo( 'fast', 1 ).on( 'mouseleave.focus', fadeOutSlug ).off( 'mouseenter.focus' ); + + $slugFocusEl.on( 'blur.focus', fadeOutSlug ).off( 'focus.focus' ); + } + } + + $document.on( 'tinymce-editor-setup.focus', function( event, editor ) { + editor.addButton( 'dfw', { + active: _isOn, + classes: 'wp-dfw btn widget', + disabled: ! _isActive, + onclick: toggle, + onPostRender: function() { + var button = this; + + $document + .on( 'dfw-activate.focus', function() { + button.disabled( false ); + } ) + .on( 'dfw-deactivate.focus', function() { + button.disabled( true ); + } ) + .on( 'dfw-on.focus', function() { + button.active( true ); + } ) + .on( 'dfw-off.focus', function() { + button.active( false ); + } ); + }, + tooltip: 'Distraction Free Writing' + } ); } ); - // Adjust when collapsing the menu, changing the columns, changing the body class. - $document.on( 'wp-collapse-menu.editor-expand postboxes-columnchange.editor-expand editor-classchange.editor-expand', adjust ) - .on( 'postbox-toggled.editor-expand', function() { - if ( ! fixedSideTop && ! fixedSideBottom && window.pageYOffset > pinnedToolsTop ) { - fixedSideBottom = true; - window.scrollBy( 0, -1 ); - adjust(); - window.scrollBy( 0, 1 ); - } - - adjust(); - }).on( 'wp-window-resized.editor-expand', function() { - if ( mceEditor && ! mceEditor.isHidden() ) { - mceEditor.execCommand( 'wpAutoResize' ); - } else { - textEditorResize(); - } - }); - - $textEditor.on( 'focus.editor-expand input.editor-expand propertychange.editor-expand', textEditorResize ); - $textEditor.on( 'keyup.editor-expand', textEditorKeyup ); - mceBind(); - - // Adjust when entering/exiting fullscreen mode. - fullscreen && fullscreen.pubsub.subscribe( 'hidden', fullscreenHide ); - - if ( mceEditor ) { - mceEditor.settings.wp_autoresize_on = true; - mceEditor.execCommand( 'wpAutoResizeOn' ); - - if ( ! mceEditor.isHidden() ) { - mceEditor.execCommand( 'wpAutoResize' ); - } - } - - if ( ! mceEditor || mceEditor.isHidden() ) { - textEditorResize(); - } - - adjust(); - } - - function off() { - var height = window.getUserSetting('ed_size'); - - // Scroll to the top when triggering this from JS. - // Ensures toolbars are reset properly. - if ( window.pageYOffset && window.pageYOffset > pageYOffsetAtTop ) { - window.scrollTo( window.pageXOffset, 0 ); - } - - $wrap.removeClass( 'wp-editor-expand' ); - - $window.off( '.editor-expand' ); - $document.off( '.editor-expand' ); - $textEditor.off( '.editor-expand' ); - mceUnbind(); - - // Adjust when entering/exiting fullscreen mode. - fullscreen && fullscreen.pubsub.unsubscribe( 'hidden', fullscreenHide ); - - // Reset all css - $.each( [ $visualTop, $textTop, $tools, $menuBar, $bottom, $statusBar, $contentWrap, $visualEditor, $textEditor, $sideSortables ], function( i, element ) { - element && element.attr( 'style', '' ); - }); - - fixedTop = fixedBottom = fixedSideTop = fixedSideBottom = false; - - if ( mceEditor ) { - mceEditor.settings.wp_autoresize_on = false; - mceEditor.execCommand( 'wpAutoResizeOff' ); - - if ( ! mceEditor.isHidden() ) { - $textEditor.hide(); - - if ( height ) { - mceEditor.theme.resizeTo( null, height ); - } - } - } - - if ( height ) { - $textEditor.height( height ); - } - } - - // Start on load - if ( $wrap.hasClass( 'wp-editor-expand' ) ) { - on(); - - // Ideally we need to resize just after CSS has fully loaded and QuickTags is ready. - if ( $contentWrap.hasClass( 'html-active' ) ) { - initialResize( function() { - adjust(); - textEditorResize(); - } ); - } - } - - // Show the on/off checkbox - $( '#adv-settings .editor-expand' ).show(); - $( '#editor-expand-toggle' ).on( 'change.editor-expand', function() { - if ( $(this).prop( 'checked' ) ) { - on(); - window.setUserSetting( 'editor_expand', 'on' ); - } else { - off(); - window.setUserSetting( 'editor_expand', 'off' ); - } - }); - - // Expose on() and off() - window.editorExpand = { - on: on, - off: off - }; -}); + $document.on( 'tinymce-editor-init.focus', function( event, editor ) { + var mceBind, mceUnbind; + + function focus() { + editorHasFocus = true; + } + + function blur() { + editorHasFocus = false; + } + + if ( editor.id === 'content' ) { + $editorWindow = $( editor.getWin() ); + $editorIframe = $( editor.getContentAreaContainer() ).find( 'iframe' ); + + mceBind = function() { + editor.on( 'keydown', fadeOut ); + editor.on( 'blur', maybeFadeIn ); + editor.on( 'focus', focus ); + editor.on( 'blur', blur ); + }; + + mceUnbind = function() { + editor.off( 'keydown', fadeOut ); + editor.off( 'blur', maybeFadeIn ); + editor.off( 'focus', focus ); + editor.off( 'blur', blur ); + }; + + if ( _isOn ) { + mceBind(); + } + + $document.on( 'dfw-on.focus', mceBind ).on( 'dfw-off.focus', mceUnbind ); + + // Make sure the body focusses when clicking outside it. + editor.on( 'click', function( event ) { + if ( event.target === editor.getDoc().documentElement ) { + editor.focus(); + } + } ); + } + } ); + + $document.on( 'quicktags-init', function( event, editor ) { + var $button; + + if ( editor.settings.buttons && ( ',' + editor.settings.buttons + ',' ).indexOf( ',dfw,' ) !== -1 ) { + $button = $( '#' + editor.name + '_dfw' ); + + $( document ) + .on( 'dfw-activate', function() { + $button.prop( 'disabled', false ); + } ) + .on( 'dfw-deactivate', function() { + $button.prop( 'disabled', true ); + } ) + .on( 'dfw-on', function() { + $button.addClass( 'active' ); + } ) + .on( 'dfw-off', function() { + $button.removeClass( 'active' ); + } ); + } + } ); + + $document.on( 'editor-expand-on.focus', activate ).on( 'editor-expand-off.focus', deactivate ); + + if ( _isOn ) { + $content.on( 'keydown.focus', fadeOut ); + + $title.add( $content ).on( 'blur.focus', maybeFadeIn ); + } + + window.wp = window.wp || {}; + window.wp.editor = window.wp.editor || {}; + window.wp.editor.dfw = { + activate: activate, + deactivate: deactivate, + isActive: isActive, + on: on, + off: off, + toggle: toggle, + isOn: isOn + }; + } ); +} )( window, window.jQuery ); Index: /trunk/src/wp-includes/class-wp-editor.php =================================================================== --- /trunk/src/wp-includes/class-wp-editor.php (revision 30337) +++ /trunk/src/wp-includes/class-wp-editor.php (revision 30338) @@ -279,4 +279,7 @@ $qtInit['buttons'] .= ',fullscreen'; + if ( $editor_id === 'content' && ! wp_is_mobile() ) + $qtInit['buttons'] .= ',dfw'; + /** * Filter the Quicktags settings. @@ -547,4 +550,11 @@ $mce_buttons_2 = $mce_buttons_3 = $mce_buttons_4 = array(); } else { + $mce_buttons = array( 'bold', 'italic', 'strikethrough', 'bullist', 'numlist', 'blockquote', 'hr', 'alignleft', 'aligncenter', 'alignright', 'link', 'unlink', 'wp_more', 'spellchecker', 'wp_adv' ); + + if ( $editor_id ) { + $mce_buttons[] = 'dfw'; + } else { + $mce_buttons[] = 'fullscreen'; + } /** @@ -556,5 +566,5 @@ * @param string $editor_id Unique editor identifier, e.g. 'content'. */ - $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 ); + $mce_buttons = apply_filters( 'mce_buttons', $mce_buttons, $editor_id ); /** Index: /trunk/src/wp-includes/css/editor.css =================================================================== --- /trunk/src/wp-includes/css/editor.css (revision 30337) +++ /trunk/src/wp-includes/css/editor.css (revision 30338) @@ -284,5 +284,5 @@ .mce-toolbar .mce-btn, -.qt-fullscreen { +.qt-dfw { border-color: transparent; background: transparent; @@ -295,5 +295,5 @@ #wp-fullscreen-buttons .mce-btn, .mce-toolbar .mce-btn-group .mce-btn, -.qt-fullscreen { +.qt-dfw { border: 1px solid transparent; margin: 2px; @@ -309,6 +309,6 @@ #wp-fullscreen-buttons .mce-btn:focus, .mce-toolbar .mce-btn-group .mce-btn:focus, -.qt-fullscreen:hover, -.qt-fullscreen:focus { +.qt-dfw:hover, +.qt-dfw:focus { background: #fafafa; border-color: #999; @@ -316,4 +316,5 @@ -webkit-box-shadow: inset 0 1px 0 #fff, 0 1px 0 rgba( 0, 0, 0, 0.08 ); box-shadow: inset 0 1px 0 #fff, 0 1px 0 rgba( 0, 0, 0, 0.08 ); + outline: none; } @@ -321,5 +322,6 @@ #wp-fullscreen-buttons .mce-btn.mce-active, .mce-toolbar .mce-btn-group .mce-btn:active, -#wp-fullscreen-buttons .mce-btn:active { +#wp-fullscreen-buttons .mce-btn:active, +.qt-dfw.active { background: #ebebeb; border-color: #999; @@ -354,5 +356,5 @@ .mce-toolbar .mce-btn button, -.qt-fullscreen { +.qt-dfw { padding: 2px 3px; line-height: normal; @@ -669,5 +671,5 @@ } -.qt-fullscreen { +.qt-dfw { color: #777; line-height: 20px; @@ -703,4 +705,5 @@ i.mce-i-fullscreen, i.mce-i-wp_fullscreen, +i.mce-i-dfw, i.mce-i-wp_adv, i.mce-i-underline, @@ -734,5 +737,5 @@ } -.qt-fullscreen { +.qt-dfw { font: normal 20px/1 'dashicons'; vertical-align: top; @@ -796,5 +799,6 @@ i.mce-i-fullscreen:before, i.mce-i-wp_fullscreen:before, -.qt-fullscreen:before { +i.mce-i-dfw:before, +.qt-dfw:before { content: '\f211'; } @@ -1072,6 +1076,6 @@ } -.mce-toolbar .mce-btn-group .mce-btn.mce-wp-fullscreen, -.qt-fullscreen { +.mce-toolbar .mce-btn-group .mce-btn.mce-wp-dfw, +.qt-dfw { position: absolute; top: 0; @@ -1082,5 +1086,5 @@ @media screen and ( max-width: 782px ) { .mce-toolbar .mce-btn button, - .qt-fullscreen { + .qt-dfw { padding: 6px 7px; } @@ -1091,10 +1095,10 @@ } - .qt-fullscreen { + .qt-dfw { width: 36px; height: 34px; } - .mce-toolbar .mce-btn-group .mce-btn.mce-wp-fullscreen { + .mce-toolbar .mce-btn-group .mce-btn.mce-wp-dfw { margin: 4px 4px 0 0; } @@ -2110,2 +2114,77 @@ /* TODO: DFW responsive */ + +/* DFW 2 +-------------------------------------------------------------- */ + +.focus-on .wrap > h2, +.focus-on #wpfooter, +.focus-on .postbox-container, +.focus-on div.updated, +.focus-on div.error, +.focus-on #wp-toolbar { + opacity: 0; + -webkit-transition-duration: 0.6s; + transition-duration: 0.6s; + -webkit-transition-property: opacity; + transition-property: opacity; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; +} + +.focus-on #wp-toolbar { + opacity: 0.3; +} + +.focus-off .wrap > h2, +.focus-off #wpfooter, +.focus-off .postbox-container, +.focus-off div.updated, +.focus-off div.error, +.focus-off #wp-toolbar { + opacity: 1; + -webkit-transition-duration: 0.2s; + transition-duration: 0.2s; + -webkit-transition-property: opacity; + transition-property: opacity; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; +} + +.focus-on #adminmenuback, +.focus-on #adminmenuwrap, +.focus-on .screen-meta-toggle { + -webkit-transition-duration: 0.6s; + transition-duration: 0.6s; + -webkit-transition-property: -webkit-transform; + transition-property: transform; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; +} + +.focus-on #adminmenuback, +.focus-on #adminmenuwrap { + -webkit-transform: translateX( -100% ); + -ms-transform: translateX( -100% ); + transform: translateX( -100% ); +} + +.focus-on .screen-meta-toggle { + -webkit-transform: translateY( -100% ); + -ms-transform: translateY( -100% ); + transform: translateY( -100% ); +} + +.focus-off #adminmenuback, +.focus-off #adminmenuwrap, +.focus-off .screen-meta-toggle { + -webkit-transform: translateX( 0 ); + -ms-transform: translateX( 0 ); + transform: translateX( 0 ); + -webkit-transition-duration: 0.2s; + transition-duration: 0.2s; + -webkit-transition-property: -webkit-transform; + transition-property: transform; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; +} Index: /trunk/src/wp-includes/js/quicktags.js =================================================================== --- /trunk/src/wp-includes/js/quicktags.js (revision 30337) +++ /trunk/src/wp-includes/js/quicktags.js (revision 30338) @@ -289,4 +289,8 @@ } + if ( use && use.indexOf(',dfw,') !== -1 ) { + theButtons.dfw = new qt.DFWButton(); + html += theButtons.dfw.html( name + '_' ); + } if ( 'rtl' === document.getElementsByTagName('html')[0].dir ) { @@ -297,4 +301,6 @@ ed.toolbar.innerHTML = html; ed.theButtons = theButtons; + + window.jQuery && window.jQuery( document ).trigger( 'quicktags-init', [ ed ] ); } t.buttonsInitDone = true; @@ -406,9 +412,17 @@ }; qt.Button.prototype.html = function(idPrefix) { - var title = this.title ? ' title="' + this.title + '"' : ''; + var title = this.title ? ' title="' + this.title + '"' : '', + active, on, wp, + dfw = ( wp = window.wp ) && wp.editor && wp.editor.dfw; if ( this.id === 'fullscreen' ) { - return ''; - } + return ''; + } else if ( this.id === 'dfw' ) { + active = dfw && dfw.isActive() ? '' : ' disabled="disabled"'; + on = dfw && dfw.isOn() ? ' active' : ''; + + return ''; + } + return ''; }; @@ -620,4 +634,18 @@ }; + qt.DFWButton = function() { + qt.Button.call( this, 'dfw', '', 'f', quicktagsL10n.dfw ); + }; + qt.DFWButton.prototype = new qt.Button(); + qt.DFWButton.prototype.callback = function() { + var wp; + + if ( ! ( wp = window.wp ) || ! wp.editor || ! wp.editor.dfw ) { + return; + } + + window.wp.editor.dfw.toggle(); + }; + qt.TextDirectionButton = function() { qt.Button.call(this, 'textdirection', quicktagsL10n.textdirection, '', quicktagsL10n.toggleTextdirection); Index: /trunk/src/wp-includes/js/tinymce/plugins/wordpress/plugin.js =================================================================== --- /trunk/src/wp-includes/js/tinymce/plugins/wordpress/plugin.js (revision 30337) +++ /trunk/src/wp-includes/js/tinymce/plugins/wordpress/plugin.js (revision 30338) @@ -7,4 +7,8 @@ var DOM = tinymce.DOM, wpAdvButton, modKey, style, last = 0; + + if ( typeof window.jQuery !== 'undefined' ) { + window.jQuery( document ).triggerHandler( 'tinymce-editor-setup', [ editor ] ); + } function toggleToolbars( state ) { Index: /trunk/src/wp-includes/js/tinymce/plugins/wpautoresize/plugin.js =================================================================== --- /trunk/src/wp-includes/js/tinymce/plugins/wpautoresize/plugin.js (revision 30337) +++ /trunk/src/wp-includes/js/tinymce/plugins/wpautoresize/plugin.js (revision 30338) @@ -118,5 +118,5 @@ } - editor.fire( 'wp-autoresize', { height: resizeHeight } ); + editor.fire( 'wp-autoresize', { height: resizeHeight, deltaHeight: e.type === 'nodechange' ? deltaSize : null } ); } } Index: /trunk/src/wp-includes/js/tinymce/plugins/wpfullscreen/plugin.js =================================================================== --- /trunk/src/wp-includes/js/tinymce/plugins/wpfullscreen/plugin.js (revision 30337) +++ /trunk/src/wp-includes/js/tinymce/plugins/wpfullscreen/plugin.js (revision 30338) @@ -66,5 +66,5 @@ shortcut: 'Alt+Shift+W', onclick: toggleFullscreen, - classes: 'wp-fullscreen btn widget' // This overwrites all classes on the container! + classes: 'wp-dfw btn widget' // This overwrites all classes on the container! }); Index: /trunk/src/wp-includes/js/tinymce/skins/wordpress/wp-content.css =================================================================== --- /trunk/src/wp-includes/js/tinymce/skins/wordpress/wp-content.css (revision 30337) +++ /trunk/src/wp-includes/js/tinymce/skins/wordpress/wp-content.css (revision 30338) @@ -17,4 +17,5 @@ margin: 9px 10px; max-width: 100%; + -webkit-font-smoothing: antialiased !important; } Index: /trunk/src/wp-includes/script-loader.php =================================================================== --- /trunk/src/wp-includes/script-loader.php (revision 30337) +++ /trunk/src/wp-includes/script-loader.php (revision 30338) @@ -95,5 +95,6 @@ 'toggleFullscreen' => esc_attr__( 'Toggle fullscreen mode' ), 'textdirection' => esc_attr__( 'text direction' ), - 'toggleTextdirection' => esc_attr__( 'Toggle Editor Text Direction' ) + 'toggleTextdirection' => esc_attr__( 'Toggle Editor Text Direction' ), + 'dfw' => esc_attr__( 'Distraction Free Writing' ) ) ); Index: /trunk/src/wp-includes/version.php =================================================================== --- /trunk/src/wp-includes/version.php (revision 30337) +++ /trunk/src/wp-includes/version.php (revision 30338) @@ -19,5 +19,5 @@ * @global string $tinymce_version */ -$tinymce_version = '4106-20141022'; +$tinymce_version = '4106-20141113'; /**