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