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