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