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