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