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