]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-admin/js/editor-expand.js
WordPress 4.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                         $textEditorClone = $( '<div id="content-textarea-clone"></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.plugins.wpview && ( view = editor.plugins.wpview.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                         topPos = $top.parent().offset().top;
354                         editorPos = $editor.offset().top;
355                         editorHeight = $editor.outerHeight();
356
357                         // Should we pin?
358                         canPin = visual ? autoresizeMinHeight + topHeight : autoresizeMinHeight + 20; // 20px from textarea padding
359                         canPin = editorHeight > ( canPin + 5 );
360
361                         if ( ! canPin ) {
362                                 if ( resize ) {
363                                         $tools.css( {
364                                                 position: 'absolute',
365                                                 top: 0,
366                                                 width: contentWrapWidth
367                                         } );
368
369                                         if ( visual && $menuBar.length ) {
370                                                 $menuBar.css( {
371                                                         position: 'absolute',
372                                                         top: 0,
373                                                         width: contentWrapWidth - ( borderWidth * 2 )
374                                                 } );
375                                         }
376
377                                         $top.css( {
378                                                 position: 'absolute',
379                                                 top: heights.menuBarHeight,
380                                                 width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
381                                         } );
382
383                                         $statusBar.add( $bottom ).attr( 'style', '' );
384                                 }
385                         } else {
386                                 // Maybe pin the top.
387                                 if ( ( ! fixedTop || resize ) &&
388                                         // Handle scrolling down.
389                                         ( windowPos >= ( topPos - heights.toolsHeight - heights.adminBarHeight ) &&
390                                         // Handle scrolling up.
391                                         windowPos <= ( topPos - heights.toolsHeight - heights.adminBarHeight + editorHeight - buffer ) ) ) {
392                                         fixedTop = true;
393
394                                         $tools.css( {
395                                                 position: 'fixed',
396                                                 top: heights.adminBarHeight,
397                                                 width: contentWrapWidth
398                                         } );
399
400                                         if ( visual && $menuBar.length ) {
401                                                 $menuBar.css( {
402                                                         position: 'fixed',
403                                                         top: heights.adminBarHeight + heights.toolsHeight,
404                                                         width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
405                                                 } );
406                                         }
407
408                                         $top.css( {
409                                                 position: 'fixed',
410                                                 top: heights.adminBarHeight + heights.toolsHeight + heights.menuBarHeight,
411                                                 width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
412                                         } );
413                                 // Maybe unpin the top.
414                                 } else if ( fixedTop || resize ) {
415                                         // Handle scrolling up.
416                                         if ( windowPos <= ( topPos - heights.toolsHeight - heights.adminBarHeight ) ) {
417                                                 fixedTop = false;
418
419                                                 $tools.css( {
420                                                         position: 'absolute',
421                                                         top: 0,
422                                                         width: contentWrapWidth
423                                                 } );
424
425                                                 if ( visual && $menuBar.length ) {
426                                                         $menuBar.css( {
427                                                                 position: 'absolute',
428                                                                 top: 0,
429                                                                 width: contentWrapWidth - ( borderWidth * 2 )
430                                                         } );
431                                                 }
432
433                                                 $top.css( {
434                                                         position: 'absolute',
435                                                         top: heights.menuBarHeight,
436                                                         width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
437                                                 } );
438                                         // Handle scrolling down.
439                                         } else if ( windowPos >= ( topPos - heights.toolsHeight - heights.adminBarHeight + editorHeight - buffer ) ) {
440                                                 fixedTop = false;
441
442                                                 $tools.css( {
443                                                         position: 'absolute',
444                                                         top: editorHeight - buffer,
445                                                         width: contentWrapWidth
446                                                 } );
447
448                                                 if ( visual && $menuBar.length ) {
449                                                         $menuBar.css( {
450                                                                 position: 'absolute',
451                                                                 top: editorHeight - buffer,
452                                                                 width: contentWrapWidth - ( borderWidth * 2 )
453                                                         } );
454                                                 }
455
456                                                 $top.css( {
457                                                         position: 'absolute',
458                                                         top: editorHeight - buffer + heights.menuBarHeight,
459                                                         width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
460                                                 } );
461                                         }
462                                 }
463
464                                 // Maybe adjust the bottom bar.
465                                 if ( ( ! fixedBottom || ( resize && advanced ) ) &&
466                                                 // +[n] for the border around the .wp-editor-container.
467                                                 ( windowPos + heights.windowHeight ) <= ( editorPos + editorHeight + heights.bottomHeight + heights.statusBarHeight + borderWidth ) ) {
468
469                                         if ( event && event.deltaHeight > 0 && event.deltaHeight < 100 ) {
470                                                 window.scrollBy( 0, event.deltaHeight );
471                                         } else if ( advanced ) {
472                                                 fixedBottom = true;
473
474                                                 $statusBar.css( {
475                                                         position: 'fixed',
476                                                         bottom: heights.bottomHeight,
477                                                         visibility: '',
478                                                         width: contentWrapWidth - ( borderWidth * 2 )
479                                                 } );
480
481                                                 $bottom.css( {
482                                                         position: 'fixed',
483                                                         bottom: 0,
484                                                         width: contentWrapWidth
485                                                 } );
486                                         }
487                                 } else if ( ( ! advanced && fixedBottom ) ||
488                                                 ( ( fixedBottom || resize ) &&
489                                                 ( windowPos + heights.windowHeight ) > ( editorPos + editorHeight + heights.bottomHeight + heights.statusBarHeight - borderWidth ) ) ) {
490                                         fixedBottom = false;
491
492                                         $statusBar.add( $bottom ).attr( 'style', '' );
493
494                                         if ( ! advanced ) {
495                                                 $statusBar.css( 'visibility', 'hidden' );
496                                         }
497                                 }
498                         }
499
500                         // Sidebar pinning
501                         if ( $postboxContainer.width() < 300 && heights.windowWidth > 600 && // sidebar position is changed with @media from CSS, make sure it is on the side
502                                 $document.height() > ( $sideSortables.height() + postBodyTop + 120 ) && // the sidebar is not the tallest element
503                                 heights.windowHeight < editorHeight ) { // the editor is taller than the viewport
504
505                                 if ( ( heights.sideSortablesHeight + pinnedToolsTop + sidebarBottom ) > heights.windowHeight || fixedSideTop || fixedSideBottom ) {
506                                         // Reset when scrolling to the top
507                                         if ( windowPos + pinnedToolsTop <= postBodyTop ) {
508                                                 $sideSortables.attr( 'style', '' );
509                                                 fixedSideTop = fixedSideBottom = false;
510                                         } else {
511                                                 if ( windowPos > lastScrollPosition ) {
512                                                         // Scrolling down
513                                                         if ( fixedSideTop ) {
514                                                                 // let it scroll
515                                                                 fixedSideTop = false;
516                                                                 sidebarTop = $sideSortables.offset().top - heights.adminBarHeight;
517                                                                 footerTop = $footer.offset().top;
518
519                                                                 // don't get over the footer
520                                                                 if ( footerTop < sidebarTop + heights.sideSortablesHeight + sidebarBottom ) {
521                                                                         sidebarTop = footerTop - heights.sideSortablesHeight - 12;
522                                                                 }
523
524                                                                 $sideSortables.css({
525                                                                         position: 'absolute',
526                                                                         top: sidebarTop,
527                                                                         bottom: ''
528                                                                 });
529                                                         } else if ( ! fixedSideBottom && heights.sideSortablesHeight + $sideSortables.offset().top + sidebarBottom < windowPos + heights.windowHeight ) {
530                                                                 // pin the bottom
531                                                                 fixedSideBottom = true;
532
533                                                                 $sideSortables.css({
534                                                                         position: 'fixed',
535                                                                         top: 'auto',
536                                                                         bottom: sidebarBottom
537                                                                 });
538                                                         }
539                                                 } else if ( windowPos < lastScrollPosition ) {
540                                                         // Scrolling up
541                                                         if ( fixedSideBottom ) {
542                                                                 // let it scroll
543                                                                 fixedSideBottom = false;
544                                                                 sidebarTop = $sideSortables.offset().top - sidebarBottom;
545                                                                 footerTop = $footer.offset().top;
546
547                                                                 // don't get over the footer
548                                                                 if ( footerTop < sidebarTop + heights.sideSortablesHeight + sidebarBottom ) {
549                                                                         sidebarTop = footerTop - heights.sideSortablesHeight - 12;
550                                                                 }
551
552                                                                 $sideSortables.css({
553                                                                         position: 'absolute',
554                                                                         top: sidebarTop,
555                                                                         bottom: ''
556                                                                 });
557                                                         } else if ( ! fixedSideTop && $sideSortables.offset().top >= windowPos + pinnedToolsTop ) {
558                                                                 // pin the top
559                                                                 fixedSideTop = true;
560
561                                                                 $sideSortables.css({
562                                                                         position: 'fixed',
563                                                                         top: pinnedToolsTop,
564                                                                         bottom: ''
565                                                                 });
566                                                         }
567                                                 }
568                                         }
569                                 } else {
570                                         // if the sidebar container is smaller than the viewport, then pin/unpin the top when scrolling
571                                         if ( windowPos >= ( postBodyTop - pinnedToolsTop ) ) {
572
573                                                 $sideSortables.css( {
574                                                         position: 'fixed',
575                                                         top: pinnedToolsTop
576                                                 } );
577                                         } else {
578                                                 $sideSortables.attr( 'style', '' );
579                                         }
580
581                                         fixedSideTop = fixedSideBottom = false;
582                                 }
583
584                                 lastScrollPosition = windowPos;
585                         } else {
586                                 $sideSortables.attr( 'style', '' );
587                                 fixedSideTop = fixedSideBottom = false;
588                         }
589
590                         if ( resize ) {
591                                 $contentWrap.css( {
592                                         paddingTop: heights.toolsHeight
593                                 } );
594
595                                 if ( visual ) {
596                                         $visualEditor.css( {
597                                                 paddingTop: heights.visualTopHeight + heights.menuBarHeight
598                                         } );
599                                 } else {
600                                         $textEditor.css( {
601                                                 marginTop: heights.textTopHeight
602                                         } );
603
604                                         $textEditorClone.width( contentWrapWidth - 20 - ( borderWidth * 2 ) );
605                                 }
606                         }
607                 }
608
609                 function fullscreenHide() {
610                         textEditorResize();
611                         adjust();
612                 }
613
614                 function initialResize( callback ) {
615                         for ( var i = 1; i < 6; i++ ) {
616                                 setTimeout( callback, 500 * i );
617                         }
618                 }
619
620                 function afterScroll() {
621                         clearTimeout( scrollTimer );
622                         scrollTimer = setTimeout( adjust, 100 );
623                 }
624
625                 function on() {
626                         // Scroll to the top when triggering this from JS.
627                         // Ensures toolbars are pinned properly.
628                         if ( window.pageYOffset && window.pageYOffset > pageYOffsetAtTop ) {
629                                 window.scrollTo( window.pageXOffset, 0 );
630                         }
631
632                         $wrap.addClass( 'wp-editor-expand' );
633
634                         // Adjust when the window is scrolled or resized.
635                         $window.on( 'scroll.editor-expand resize.editor-expand', function( event ) {
636                                 adjust( event.type );
637                                 afterScroll();
638                         } );
639
640                         // Adjust when collapsing the menu, changing the columns, changing the body class.
641                         $document.on( 'wp-collapse-menu.editor-expand postboxes-columnchange.editor-expand editor-classchange.editor-expand', adjust )
642                                 .on( 'postbox-toggled.editor-expand', function() {
643                                         if ( ! fixedSideTop && ! fixedSideBottom && window.pageYOffset > pinnedToolsTop ) {
644                                                 fixedSideBottom = true;
645                                                 window.scrollBy( 0, -1 );
646                                                 adjust();
647                                                 window.scrollBy( 0, 1 );
648                                         }
649
650                                         adjust();
651                                 }).on( 'wp-window-resized.editor-expand', function() {
652                                         if ( mceEditor && ! mceEditor.isHidden() ) {
653                                                 mceEditor.execCommand( 'wpAutoResize' );
654                                         } else {
655                                                 textEditorResize();
656                                         }
657                                 });
658
659                         $textEditor.on( 'focus.editor-expand input.editor-expand propertychange.editor-expand', textEditorResize );
660                         $textEditor.on( 'keyup.editor-expand', textEditorKeyup );
661                         mceBind();
662
663                         // Adjust when entering/exiting fullscreen mode.
664                         fullscreen && fullscreen.pubsub.subscribe( 'hidden', fullscreenHide );
665
666                         if ( mceEditor ) {
667                                 mceEditor.settings.wp_autoresize_on = true;
668                                 mceEditor.execCommand( 'wpAutoResizeOn' );
669
670                                 if ( ! mceEditor.isHidden() ) {
671                                         mceEditor.execCommand( 'wpAutoResize' );
672                                 }
673                         }
674
675                         if ( ! mceEditor || mceEditor.isHidden() ) {
676                                 textEditorResize();
677                         }
678
679                         adjust();
680
681                         $document.trigger( 'editor-expand-on' );
682                 }
683
684                 function off() {
685                         var height = window.getUserSetting('ed_size');
686
687                         // Scroll to the top when triggering this from JS.
688                         // Ensures toolbars are reset properly.
689                         if ( window.pageYOffset && window.pageYOffset > pageYOffsetAtTop ) {
690                                 window.scrollTo( window.pageXOffset, 0 );
691                         }
692
693                         $wrap.removeClass( 'wp-editor-expand' );
694
695                         $window.off( '.editor-expand' );
696                         $document.off( '.editor-expand' );
697                         $textEditor.off( '.editor-expand' );
698                         mceUnbind();
699
700                         // Adjust when entering/exiting fullscreen mode.
701                         fullscreen && fullscreen.pubsub.unsubscribe( 'hidden', fullscreenHide );
702
703                         // Reset all css
704                         $.each( [ $visualTop, $textTop, $tools, $menuBar, $bottom, $statusBar, $contentWrap, $visualEditor, $textEditor, $sideSortables ], function( i, element ) {
705                                 element && element.attr( 'style', '' );
706                         });
707
708                         fixedTop = fixedBottom = fixedSideTop = fixedSideBottom = false;
709
710                         if ( mceEditor ) {
711                                 mceEditor.settings.wp_autoresize_on = false;
712                                 mceEditor.execCommand( 'wpAutoResizeOff' );
713
714                                 if ( ! mceEditor.isHidden() ) {
715                                         $textEditor.hide();
716
717                                         if ( height ) {
718                                                 mceEditor.theme.resizeTo( null, height );
719                                         }
720                                 }
721                         }
722
723                         if ( height ) {
724                                 $textEditor.height( height );
725                         }
726
727                         $document.trigger( 'editor-expand-off' );
728                 }
729
730                 // Start on load
731                 if ( $wrap.hasClass( 'wp-editor-expand' ) ) {
732                         on();
733
734                         // Ideally we need to resize just after CSS has fully loaded and QuickTags is ready.
735                         if ( $contentWrap.hasClass( 'html-active' ) ) {
736                                 initialResize( function() {
737                                         adjust();
738                                         textEditorResize();
739                                 } );
740                         }
741                 }
742
743                 // Show the on/off checkbox
744                 $( '#adv-settings .editor-expand' ).show();
745                 $( '#editor-expand-toggle' ).on( 'change.editor-expand', function() {
746                         if ( $(this).prop( 'checked' ) ) {
747                                 on();
748                                 window.setUserSetting( 'editor_expand', 'on' );
749                         } else {
750                                 off();
751                                 window.setUserSetting( 'editor_expand', 'off' );
752                         }
753                 });
754
755                 // Expose on() and off()
756                 window.editorExpand = {
757                         on: on,
758                         off: off
759                 };
760         } );
761
762         /* DFW. */
763         $( function() {
764                 var $body = $( document.body ),
765                         $wrap = $( '#wpcontent' ),
766                         $editor = $( '#post-body-content' ),
767                         $title = $( '#title' ),
768                         $content = $( '#content' ),
769                         $overlay = $( document.createElement( 'DIV' ) ),
770                         $slug = $( '#edit-slug-box' ),
771                         $slugFocusEl = $slug.find( 'a' )
772                                 .add( $slug.find( 'button' ) )
773                                 .add( $slug.find( 'input' ) ),
774                         $menuWrap = $( '#adminmenuwrap' ),
775                         $editorWindow = $(),
776                         $editorIframe = $(),
777                         _isActive = window.getUserSetting( 'editor_expand', 'on' ) === 'on',
778                         _isOn = _isActive ? window.getUserSetting( 'post_dfw' ) === 'on' : false,
779                         traveledX = 0,
780                         traveledY = 0,
781                         buffer = 20,
782                         faded, fadedAdminBar, fadedSlug,
783                         editorRect, x, y, mouseY, scrollY,
784                         focusLostTimer, overlayTimer, editorHasFocus;
785
786                 $body.append( $overlay );
787
788                 $overlay.css( {
789                         display: 'none',
790                         position: 'fixed',
791                         top: $adminBar.height(),
792                         right: 0,
793                         bottom: 0,
794                         left: 0,
795                         'z-index': 9997
796                 } );
797
798                 $editor.css( {
799                         position: 'relative'
800                 } );
801
802                 $window.on( 'mousemove.focus', function( event ) {
803                         mouseY = event.pageY;
804                 } );
805
806                 function recalcEditorRect() {
807                         editorRect = $editor.offset();
808                         editorRect.right = editorRect.left + $editor.outerWidth();
809                         editorRect.bottom = editorRect.top + $editor.outerHeight();
810                 }
811
812                 function activate() {
813                         if ( ! _isActive ) {
814                                 _isActive = true;
815
816                                 $document.trigger( 'dfw-activate' );
817                                 $content.on( 'keydown.focus-shortcut', toggleViaKeyboard );
818                         }
819                 }
820
821                 function deactivate() {
822                         if ( _isActive ) {
823                                 off();
824
825                                 _isActive = false;
826
827                                 $document.trigger( 'dfw-deactivate' );
828                                 $content.off( 'keydown.focus-shortcut' );
829                         }
830                 }
831
832                 function isActive() {
833                         return _isActive;
834                 }
835
836                 function on() {
837                         if ( ! _isOn && _isActive ) {
838                                 _isOn = true;
839
840                                 $content.on( 'keydown.focus', fadeOut );
841
842                                 $title.add( $content ).on( 'blur.focus', maybeFadeIn );
843
844                                 fadeOut();
845
846                                 window.setUserSetting( 'post_dfw', 'on' );
847
848                                 $document.trigger( 'dfw-on' );
849                         }
850                 }
851
852                 function off() {
853                         if ( _isOn ) {
854                                 _isOn = false;
855
856                                 $title.add( $content ).off( '.focus' );
857
858                                 fadeIn();
859
860                                 $editor.off( '.focus' );
861
862                                 window.setUserSetting( 'post_dfw', 'off' );
863
864                                 $document.trigger( 'dfw-off' );
865                         }
866                 }
867
868                 function toggle() {
869                         if ( _isOn ) {
870                                 off();
871                         } else {
872                                 on();
873                         }
874                 }
875
876                 function isOn() {
877                         return _isOn;
878                 }
879
880                 function fadeOut( event ) {
881                         var key = event && event.keyCode;
882
883                         // fadeIn and return on Escape and keyboard shortcut Alt+Shift+W.
884                         if ( key === 27 || ( key === 87 && event.altKey && event.shiftKey ) ) {
885                                 fadeIn( event );
886                                 return;
887                         }
888
889                         if ( event && ( event.metaKey || ( event.ctrlKey && ! event.altKey ) || ( event.altKey && event.shiftKey ) || ( key && (
890                                 // Special keys ( tab, ctrl, alt, esc, arrow keys... )
891                                 ( key <= 47 && key !== 8 && key !== 13 && key !== 32 && key !== 46 ) ||
892                                 // Windows keys
893                                 ( key >= 91 && key <= 93 ) ||
894                                 // F keys
895                                 ( key >= 112 && key <= 135 ) ||
896                                 // Num Lock, Scroll Lock, OEM
897                                 ( key >= 144 && key <= 150 ) ||
898                                 // OEM or non-printable
899                                 key >= 224
900                         ) ) ) ) {
901                                 return;
902                         }
903
904                         if ( ! faded ) {
905                                 faded = true;
906
907                                 clearTimeout( overlayTimer );
908
909                                 overlayTimer = setTimeout( function() {
910                                         $overlay.show();
911                                 }, 600 );
912
913                                 $editor.css( 'z-index', 9998 );
914
915                                 $overlay
916                                         // Always recalculate the editor area entering the overlay with the mouse.
917                                         .on( 'mouseenter.focus', function() {
918                                                 recalcEditorRect();
919
920                                                 $window.on( 'scroll.focus', function() {
921                                                         var nScrollY = window.pageYOffset;
922
923                                                         if ( (
924                                                                 scrollY && mouseY &&
925                                                                 scrollY !== nScrollY
926                                                         ) && (
927                                                                 mouseY < editorRect.top - buffer ||
928                                                                 mouseY > editorRect.bottom + buffer
929                                                         ) ) {
930                                                                 fadeIn();
931                                                         }
932
933                                                         scrollY = nScrollY;
934                                                 } );
935                                         } )
936                                         .on( 'mouseleave.focus', function() {
937                                                 x = y =  null;
938                                                 traveledX = traveledY = 0;
939
940                                                 $window.off( 'scroll.focus' );
941                                         } )
942                                         // Fade in when the mouse moves away form the editor area.
943                                         .on( 'mousemove.focus', function( event ) {
944                                                 var nx = event.clientX,
945                                                         ny = event.clientY,
946                                                         pageYOffset = window.pageYOffset,
947                                                         pageXOffset = window.pageXOffset;
948
949                                                 if ( x && y && ( nx !== x || ny !== y ) ) {
950                                                         if (
951                                                                 ( ny <= y && ny < editorRect.top - pageYOffset ) ||
952                                                                 ( ny >= y && ny > editorRect.bottom - pageYOffset ) ||
953                                                                 ( nx <= x && nx < editorRect.left - pageXOffset ) ||
954                                                                 ( nx >= x && nx > editorRect.right - pageXOffset )
955                                                         ) {
956                                                                 traveledX += Math.abs( x - nx );
957                                                                 traveledY += Math.abs( y - ny );
958
959                                                                 if ( (
960                                                                         ny <= editorRect.top - buffer - pageYOffset ||
961                                                                         ny >= editorRect.bottom + buffer - pageYOffset ||
962                                                                         nx <= editorRect.left - buffer - pageXOffset ||
963                                                                         nx >= editorRect.right + buffer - pageXOffset
964                                                                 ) && (
965                                                                         traveledX > 10 ||
966                                                                         traveledY > 10
967                                                                 ) ) {
968                                                                         fadeIn();
969
970                                                                         x = y =  null;
971                                                                         traveledX = traveledY = 0;
972
973                                                                         return;
974                                                                 }
975                                                         } else {
976                                                                 traveledX = traveledY = 0;
977                                                         }
978                                                 }
979
980                                                 x = nx;
981                                                 y = ny;
982                                         } )
983                                         // When the overlay is touched, always fade in and cancel the event.
984                                         .on( 'touchstart.focus', function( event ) {
985                                                 event.preventDefault();
986                                                 fadeIn();
987                                         } );
988
989                                 $editor.off( 'mouseenter.focus' );
990
991                                 if ( focusLostTimer ) {
992                                         clearTimeout( focusLostTimer );
993                                         focusLostTimer = null;
994                                 }
995
996                                 $body.addClass( 'focus-on' ).removeClass( 'focus-off' );
997                         }
998
999                         fadeOutAdminBar();
1000                         fadeOutSlug();
1001                 }
1002
1003                 function fadeIn( event ) {
1004                         if ( faded ) {
1005                                 faded = false;
1006
1007                                 clearTimeout( overlayTimer );
1008
1009                                 overlayTimer = setTimeout( function() {
1010                                         $overlay.hide();
1011                                 }, 200 );
1012
1013                                 $editor.css( 'z-index', '' );
1014
1015                                 $overlay.off( 'mouseenter.focus mouseleave.focus mousemove.focus touchstart.focus' );
1016
1017                                 /*
1018                                  * When fading in, temporarily watch for refocus and fade back out - helps
1019                                  * with 'accidental' editor exits with the mouse. When fading in and the event
1020                                  * is a key event (Escape or Alt+Shift+W) don't watch for refocus.
1021                                  */
1022                                 if ( 'undefined' === typeof event ) {
1023                                         $editor.on( 'mouseenter.focus', function() {
1024                                                 if ( $.contains( $editor.get( 0 ), document.activeElement ) || editorHasFocus ) {
1025                                                         fadeOut();
1026                                                 }
1027                                         } );
1028                                 }
1029
1030                                 focusLostTimer = setTimeout( function() {
1031                                         focusLostTimer = null;
1032                                         $editor.off( 'mouseenter.focus' );
1033                                 }, 1000 );
1034
1035                                 $body.addClass( 'focus-off' ).removeClass( 'focus-on' );
1036                         }
1037
1038                         fadeInAdminBar();
1039                         fadeInSlug();
1040                 }
1041
1042                 function maybeFadeIn() {
1043                         setTimeout( function() {
1044                                 var position = document.activeElement.compareDocumentPosition( $editor.get( 0 ) );
1045
1046                                 function hasFocus( $el ) {
1047                                         return $.contains( $el.get( 0 ), document.activeElement );
1048                                 }
1049
1050                                 // The focused node is before or behind the editor area, and not outside the wrap.
1051                                 if ( ( position === 2 || position === 4 ) && ( hasFocus( $menuWrap ) || hasFocus( $wrap ) || hasFocus( $footer ) ) ) {
1052                                         fadeIn();
1053                                 }
1054                         }, 0 );
1055                 }
1056
1057                 function fadeOutAdminBar() {
1058                         if ( ! fadedAdminBar && faded ) {
1059                                 fadedAdminBar = true;
1060
1061                                 $adminBar
1062                                         .on( 'mouseenter.focus', function() {
1063                                                 $adminBar.addClass( 'focus-off' );
1064                                         } )
1065                                         .on( 'mouseleave.focus', function() {
1066                                                 $adminBar.removeClass( 'focus-off' );
1067                                         } );
1068                         }
1069                 }
1070
1071                 function fadeInAdminBar() {
1072                         if ( fadedAdminBar ) {
1073                                 fadedAdminBar = false;
1074
1075                                 $adminBar.off( '.focus' );
1076                         }
1077                 }
1078
1079                 function fadeOutSlug() {
1080                         if ( ! fadedSlug && faded && ! $slug.find( ':focus').length ) {
1081                                 fadedSlug = true;
1082
1083                                 $slug.stop().fadeTo( 'fast', 0.3 ).on( 'mouseenter.focus', fadeInSlug ).off( 'mouseleave.focus' );
1084
1085                                 $slugFocusEl.on( 'focus.focus', fadeInSlug ).off( 'blur.focus' );
1086                         }
1087                 }
1088
1089                 function fadeInSlug() {
1090                         if ( fadedSlug ) {
1091                                 fadedSlug = false;
1092
1093                                 $slug.stop().fadeTo( 'fast', 1 ).on( 'mouseleave.focus', fadeOutSlug ).off( 'mouseenter.focus' );
1094
1095                                 $slugFocusEl.on( 'blur.focus', fadeOutSlug ).off( 'focus.focus' );
1096                         }
1097                 }
1098
1099                 function toggleViaKeyboard( event ) {
1100                         if ( event.altKey && event.shiftKey && 87 === event.keyCode ) {
1101                                 toggle();
1102                         }
1103                 }
1104
1105                 if ( $( '#postdivrich' ).hasClass( 'wp-editor-expand' ) ) {
1106                         $content.on( 'keydown.focus-shortcut', toggleViaKeyboard );
1107                 }
1108
1109                 $document.on( 'tinymce-editor-setup.focus', function( event, editor ) {
1110                         editor.addButton( 'dfw', {
1111                                 active: _isOn,
1112                                 classes: 'wp-dfw btn widget',
1113                                 disabled: ! _isActive,
1114                                 onclick: toggle,
1115                                 onPostRender: function() {
1116                                         var button = this;
1117
1118                                         $document
1119                                         .on( 'dfw-activate.focus', function() {
1120                                                 button.disabled( false );
1121                                         } )
1122                                         .on( 'dfw-deactivate.focus', function() {
1123                                                 button.disabled( true );
1124                                         } )
1125                                         .on( 'dfw-on.focus', function() {
1126                                                 button.active( true );
1127                                         } )
1128                                         .on( 'dfw-off.focus', function() {
1129                                                 button.active( false );
1130                                         } );
1131                                 },
1132                                 tooltip: 'Distraction-free writing mode',
1133                                 shortcut: 'Alt+Shift+W'
1134                         } );
1135
1136                         editor.addCommand( 'wpToggleDFW', toggle );
1137                         editor.addShortcut( 'alt+shift+w', '', 'wpToggleDFW' );
1138                 } );
1139
1140                 $document.on( 'tinymce-editor-init.focus', function( event, editor ) {
1141                         var mceBind, mceUnbind;
1142
1143                         function focus() {
1144                                 editorHasFocus = true;
1145                         }
1146
1147                         function blur() {
1148                                 editorHasFocus = false;
1149                         }
1150
1151                         if ( editor.id === 'content' ) {
1152                                 $editorWindow = $( editor.getWin() );
1153                                 $editorIframe = $( editor.getContentAreaContainer() ).find( 'iframe' );
1154
1155                                 mceBind = function() {
1156                                         editor.on( 'keydown', fadeOut );
1157                                         editor.on( 'blur', maybeFadeIn );
1158                                         editor.on( 'focus', focus );
1159                                         editor.on( 'blur', blur );
1160                                         editor.on( 'wp-autoresize', recalcEditorRect );
1161                                 };
1162
1163                                 mceUnbind = function() {
1164                                         editor.off( 'keydown', fadeOut );
1165                                         editor.off( 'blur', maybeFadeIn );
1166                                         editor.off( 'focus', focus );
1167                                         editor.off( 'blur', blur );
1168                                         editor.off( 'wp-autoresize', recalcEditorRect );
1169                                 };
1170
1171                                 if ( _isOn ) {
1172                                         mceBind();
1173                                 }
1174
1175                                 $document.on( 'dfw-on.focus', mceBind ).on( 'dfw-off.focus', mceUnbind );
1176
1177                                 // Make sure the body focuses when clicking outside it.
1178                                 editor.on( 'click', function( event ) {
1179                                         if ( event.target === editor.getDoc().documentElement ) {
1180                                                 editor.focus();
1181                                         }
1182                                 } );
1183                         }
1184                 } );
1185
1186                 $document.on( 'quicktags-init', function( event, editor ) {
1187                         var $button;
1188
1189                         if ( editor.settings.buttons && ( ',' + editor.settings.buttons + ',' ).indexOf( ',dfw,' ) !== -1 ) {
1190                                 $button = $( '#' + editor.name + '_dfw' );
1191
1192                                 $( document )
1193                                 .on( 'dfw-activate', function() {
1194                                         $button.prop( 'disabled', false );
1195                                 } )
1196                                 .on( 'dfw-deactivate', function() {
1197                                         $button.prop( 'disabled', true );
1198                                 } )
1199                                 .on( 'dfw-on', function() {
1200                                         $button.addClass( 'active' );
1201                                 } )
1202                                 .on( 'dfw-off', function() {
1203                                         $button.removeClass( 'active' );
1204                                 } );
1205                         }
1206                 } );
1207
1208                 $document.on( 'editor-expand-on.focus', activate ).on( 'editor-expand-off.focus', deactivate );
1209
1210                 if ( _isOn ) {
1211                         $content.on( 'keydown.focus', fadeOut );
1212
1213                         $title.add( $content ).on( 'blur.focus', maybeFadeIn );
1214                 }
1215
1216                 window.wp = window.wp || {};
1217                 window.wp.editor = window.wp.editor || {};
1218                 window.wp.editor.dfw = {
1219                         activate: activate,
1220                         deactivate: deactivate,
1221                         isActive: isActive,
1222                         on: on,
1223                         off: off,
1224                         toggle: toggle,
1225                         isOn: isOn
1226                 };
1227         } );
1228 } )( window, window.jQuery );