Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 
 

1208 řádky
33 KiB

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