You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

933 rivejä
25 KiB

  1. /**
  2. * PressThis App
  3. *
  4. */
  5. ( function( $, window ) {
  6. var PressThis = function() {
  7. var editor, $mediaList, $mediaThumbWrap,
  8. $window = $( window ),
  9. $document = $( document ),
  10. saveAlert = false,
  11. textarea = document.createElement( 'textarea' ),
  12. sidebarIsOpen = false,
  13. settings = window.wpPressThisConfig || {},
  14. data = window.wpPressThisData || {},
  15. smallestWidth = 128,
  16. hasSetFocus = false,
  17. catsCache = [],
  18. isOffScreen = 'is-off-screen',
  19. isHidden = 'is-hidden',
  20. offscreenHidden = isOffScreen + ' ' + isHidden,
  21. iOS = /iPad|iPod|iPhone/.test( window.navigator.userAgent ),
  22. $textEditor = $( '#pressthis' ),
  23. textEditor = $textEditor[0],
  24. textEditorMinHeight = 600,
  25. textLength = 0,
  26. transitionEndEvent = ( function() {
  27. var style = document.documentElement.style;
  28. if ( typeof style.transition !== 'undefined' ) {
  29. return 'transitionend';
  30. }
  31. if ( typeof style.WebkitTransition !== 'undefined' ) {
  32. return 'webkitTransitionEnd';
  33. }
  34. return false;
  35. }() );
  36. /* ***************************************************************
  37. * HELPER FUNCTIONS
  38. *************************************************************** */
  39. /**
  40. * Emulates our PHP __() gettext function, powered by the strings exported in pressThisL10n.
  41. *
  42. * @param key string Key of the string to be translated, as found in pressThisL10n.
  43. * @returns string Original or translated string, or empty string if no key.
  44. */
  45. function __( key ) {
  46. if ( key && window.pressThisL10n ) {
  47. return window.pressThisL10n[key] || key;
  48. }
  49. return key || '';
  50. }
  51. /**
  52. * Strips HTML tags
  53. *
  54. * @param string string Text to have the HTML tags striped out of.
  55. * @returns string Stripped text.
  56. */
  57. function stripTags( string ) {
  58. string = string || '';
  59. return string
  60. .replace( /<!--[\s\S]*?(-->|$)/g, '' )
  61. .replace( /<(script|style)[^>]*>[\s\S]*?(<\/\1>|$)/ig, '' )
  62. .replace( /<\/?[a-z][\s\S]*?(>|$)/ig, '' );
  63. }
  64. /**
  65. * Strip HTML tags and convert HTML entities.
  66. *
  67. * @param text string Text.
  68. * @returns string Sanitized text.
  69. */
  70. function sanitizeText( text ) {
  71. var _text = stripTags( text );
  72. try {
  73. textarea.innerHTML = _text;
  74. _text = stripTags( textarea.value );
  75. } catch ( er ) {}
  76. return _text;
  77. }
  78. /**
  79. * Allow only HTTP or protocol relative URLs.
  80. *
  81. * @param url string The URL.
  82. * @returns string Processed URL.
  83. */
  84. function checkUrl( url ) {
  85. url = $.trim( url || '' );
  86. if ( /^(?:https?:)?\/\//.test( url ) ) {
  87. url = stripTags( url );
  88. return url.replace( /["\\]+/g, '' );
  89. }
  90. return '';
  91. }
  92. /**
  93. * Show UX spinner
  94. */
  95. function showSpinner() {
  96. $( '.spinner' ).addClass( 'is-active' );
  97. $( '.post-actions button' ).attr( 'disabled', 'disabled' );
  98. }
  99. /**
  100. * Hide UX spinner
  101. */
  102. function hideSpinner() {
  103. $( '.spinner' ).removeClass( 'is-active' );
  104. $( '.post-actions button' ).removeAttr( 'disabled' );
  105. }
  106. function textEditorResize( reset ) {
  107. var pageYOffset, height;
  108. if ( editor && ! editor.isHidden() ) {
  109. return;
  110. }
  111. reset = ( reset === 'reset' ) || ( textLength && textLength > textEditor.value.length );
  112. height = textEditor.style.height;
  113. if ( reset ) {
  114. pageYOffset = window.pageYOffset;
  115. textEditor.style.height = 'auto';
  116. textEditor.style.height = Math.max( textEditor.scrollHeight, textEditorMinHeight ) + 'px';
  117. window.scrollTo( window.pageXOffset, pageYOffset );
  118. } else if ( parseInt( textEditor.style.height, 10 ) < textEditor.scrollHeight ) {
  119. textEditor.style.height = textEditor.scrollHeight + 'px';
  120. }
  121. textLength = textEditor.value.length;
  122. }
  123. function mceGetCursorOffset() {
  124. if ( ! editor ) {
  125. return false;
  126. }
  127. var node = editor.selection.getNode(),
  128. range, view, offset;
  129. if ( editor.wp && editor.wp.getView && ( view = editor.wp.getView( node ) ) ) {
  130. offset = view.getBoundingClientRect();
  131. } else {
  132. range = editor.selection.getRng();
  133. try {
  134. offset = range.getClientRects()[0];
  135. } catch( er ) {}
  136. if ( ! offset ) {
  137. offset = node.getBoundingClientRect();
  138. }
  139. }
  140. return offset.height ? offset : false;
  141. }
  142. // Make sure the caret is always visible.
  143. function mceKeyup( event ) {
  144. var VK = window.tinymce.util.VK,
  145. key = event.keyCode;
  146. // Bail on special keys.
  147. 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 ) ) {
  148. return;
  149. // OS keys, function keys, num lock, scroll lock
  150. } else if ( ( key >= 91 && key <= 93 ) || ( key >= 112 && key <= 123 ) || key === 144 || key === 145 ) {
  151. return;
  152. }
  153. mceScroll( key );
  154. }
  155. function mceScroll( key ) {
  156. var cursorTop, cursorBottom, editorBottom,
  157. offset = mceGetCursorOffset(),
  158. bufferTop = 50,
  159. bufferBottom = 65,
  160. VK = window.tinymce.util.VK;
  161. if ( ! offset ) {
  162. return;
  163. }
  164. cursorTop = offset.top + editor.iframeElement.getBoundingClientRect().top;
  165. cursorBottom = cursorTop + offset.height;
  166. cursorTop = cursorTop - bufferTop;
  167. cursorBottom = cursorBottom + bufferBottom;
  168. editorBottom = $window.height();
  169. // Don't scroll if the node is taller than the visible part of the editor
  170. if ( editorBottom < offset.height ) {
  171. return;
  172. }
  173. if ( cursorTop < 0 && ( key === VK.UP || key === VK.LEFT || key === VK.BACKSPACE ) ) {
  174. window.scrollTo( window.pageXOffset, cursorTop + window.pageYOffset );
  175. } else if ( cursorBottom > editorBottom ) {
  176. window.scrollTo( window.pageXOffset, cursorBottom + window.pageYOffset - editorBottom );
  177. }
  178. }
  179. /**
  180. * Replace emoji images with chars and sanitize the text content.
  181. */
  182. function getTitleText() {
  183. var $element = $( '#title-container' );
  184. $element.find( 'img.emoji' ).each( function() {
  185. var $image = $( this );
  186. $image.replaceWith( $( '<span>' ).text( $image.attr( 'alt' ) ) );
  187. });
  188. return sanitizeText( $element.text() );
  189. }
  190. /**
  191. * Prepare the form data for saving.
  192. */
  193. function prepareFormData() {
  194. var $form = $( '#pressthis-form' ),
  195. $input = $( '<input type="hidden" name="post_category[]" value="">' );
  196. editor && editor.save();
  197. $( '#post_title' ).val( getTitleText() );
  198. // Make sure to flush out the tags with tagBox before saving
  199. if ( window.tagBox ) {
  200. $( 'div.tagsdiv' ).each( function() {
  201. window.tagBox.flushTags( this, false, 1 );
  202. } );
  203. }
  204. // Get selected categories
  205. $( '.categories-select .category' ).each( function( i, element ) {
  206. var $cat = $( element );
  207. if ( $cat.hasClass( 'selected' ) ) {
  208. // Have to append a node as we submit the actual form on preview
  209. $form.append( $input.clone().val( $cat.attr( 'data-term-id' ) || '' ) );
  210. }
  211. });
  212. }
  213. /**
  214. * Submit the post form via AJAX, and redirect to the proper screen if published vs saved as a draft.
  215. *
  216. * @param action string publish|draft
  217. */
  218. function submitPost( action ) {
  219. var data;
  220. saveAlert = false;
  221. showSpinner();
  222. if ( 'publish' === action ) {
  223. $( '#post_status' ).val( 'publish' );
  224. }
  225. prepareFormData();
  226. data = $( '#pressthis-form' ).serialize();
  227. $.ajax( {
  228. type: 'post',
  229. url: window.ajaxurl,
  230. data: data
  231. }).always( function() {
  232. hideSpinner();
  233. clearNotices();
  234. $( '.publish-button' ).removeClass( 'is-saving' );
  235. }).done( function( response ) {
  236. if ( ! response.success ) {
  237. renderError( response.data.errorMessage );
  238. } else if ( response.data.redirect ) {
  239. if ( window.opener && ( settings.redirInParent || response.data.force ) ) {
  240. try {
  241. window.opener.location.href = response.data.redirect;
  242. window.setTimeout( function() {
  243. window.self.close();
  244. }, 200 );
  245. } catch( er ) {
  246. window.location.href = response.data.redirect;
  247. }
  248. } else {
  249. window.location.href = response.data.redirect;
  250. }
  251. }
  252. }).fail( function() {
  253. renderError( __( 'serverError' ) );
  254. });
  255. }
  256. /**
  257. * Inserts the media a user has selected from the presented list inside the editor, as an image or embed, based on type
  258. *
  259. * @param type string img|embed
  260. * @param src string Source URL
  261. * @param link string Optional destination link, for images (defaults to src)
  262. */
  263. function insertSelectedMedia( $element ) {
  264. var src, link, newContent = '';
  265. src = checkUrl( $element.attr( 'data-wp-src' ) || '' );
  266. link = checkUrl( data.u );
  267. if ( $element.hasClass( 'is-image' ) ) {
  268. if ( ! link ) {
  269. link = src;
  270. }
  271. newContent = '<a href="' + link + '"><img class="alignnone size-full" src="' + src + '" alt="" /></a>';
  272. } else {
  273. newContent = '[embed]' + src + '[/embed]';
  274. }
  275. if ( editor && ! editor.isHidden() ) {
  276. if ( ! hasSetFocus ) {
  277. editor.setContent( '<p>' + newContent + '</p>' + editor.getContent() );
  278. } else {
  279. editor.execCommand( 'mceInsertContent', false, newContent );
  280. }
  281. } else if ( window.QTags ) {
  282. window.QTags.insertContent( newContent );
  283. }
  284. }
  285. /**
  286. * Save a new user-generated category via AJAX
  287. */
  288. function saveNewCategory() {
  289. var data,
  290. name = $( '#new-category' ).val();
  291. if ( ! name ) {
  292. return;
  293. }
  294. data = {
  295. action: 'press-this-add-category',
  296. post_id: $( '#post_ID' ).val() || 0,
  297. name: name,
  298. new_cat_nonce: $( '#_ajax_nonce-add-category' ).val() || '',
  299. parent: $( '#new-category-parent' ).val() || 0
  300. };
  301. $.post( window.ajaxurl, data, function( response ) {
  302. if ( ! response.success ) {
  303. renderError( response.data.errorMessage );
  304. } else {
  305. var $parent, $ul,
  306. $wrap = $( 'ul.categories-select' );
  307. $.each( response.data, function( i, newCat ) {
  308. var $node = $( '<li>' ).append( $( '<div class="category selected" tabindex="0" role="checkbox" aria-checked="true">' )
  309. .attr( 'data-term-id', newCat.term_id )
  310. .text( newCat.name ) );
  311. if ( newCat.parent ) {
  312. if ( ! $ul || ! $ul.length ) {
  313. $parent = $wrap.find( 'div[data-term-id="' + newCat.parent + '"]' ).parent();
  314. $ul = $parent.find( 'ul.children:first' );
  315. if ( ! $ul.length ) {
  316. $ul = $( '<ul class="children">' ).appendTo( $parent );
  317. }
  318. }
  319. $ul.prepend( $node );
  320. } else {
  321. $wrap.prepend( $node );
  322. }
  323. $node.focus();
  324. } );
  325. refreshCatsCache();
  326. }
  327. } );
  328. }
  329. /* ***************************************************************
  330. * RENDERING FUNCTIONS
  331. *************************************************************** */
  332. /**
  333. * Hide the form letting users enter a URL to be scanned, if a URL was already passed.
  334. */
  335. function renderToolsVisibility() {
  336. if ( data.hasData ) {
  337. $( '#scanbar' ).hide();
  338. }
  339. }
  340. /**
  341. * Render error notice
  342. *
  343. * @param msg string Notice/error message
  344. * @param error string error|notice CSS class for display
  345. */
  346. function renderNotice( msg, error ) {
  347. var $alerts = $( '.editor-wrapper div.alerts' ),
  348. className = error ? 'is-error' : 'is-notice';
  349. $alerts.append( $( '<p class="alert ' + className + '">' ).text( msg ) );
  350. }
  351. /**
  352. * Render error notice
  353. *
  354. * @param msg string Error message
  355. */
  356. function renderError( msg ) {
  357. renderNotice( msg, true );
  358. }
  359. function clearNotices() {
  360. $( 'div.alerts' ).empty();
  361. }
  362. /**
  363. * Render notices on page load, if any already
  364. */
  365. function renderStartupNotices() {
  366. // Render errors sent in the data, if any
  367. if ( data.errors ) {
  368. $.each( data.errors, function( i, msg ) {
  369. renderError( msg );
  370. } );
  371. }
  372. }
  373. /**
  374. * Add an image to the list of found images.
  375. */
  376. function addImg( src, displaySrc, i ) {
  377. var $element = $mediaThumbWrap.clone().addClass( 'is-image' );
  378. $element.attr( 'data-wp-src', src ).css( 'background-image', 'url(' + displaySrc + ')' )
  379. .find( 'span' ).text( __( 'suggestedImgAlt' ).replace( '%d', i + 1 ) );
  380. $mediaList.append( $element );
  381. }
  382. /**
  383. * Render the detected images and embed for selection, if any
  384. */
  385. function renderDetectedMedia() {
  386. var found = 0;
  387. $mediaList = $( 'ul.media-list' );
  388. $mediaThumbWrap = $( '<li class="suggested-media-thumbnail" tabindex="0"><span class="screen-reader-text"></span></li>' );
  389. if ( data._embeds ) {
  390. $.each( data._embeds, function ( i, src ) {
  391. var displaySrc = '',
  392. cssClass = '',
  393. $element = $mediaThumbWrap.clone().addClass( 'is-embed' );
  394. src = checkUrl( src );
  395. if ( src.indexOf( 'youtube.com/' ) > -1 ) {
  396. displaySrc = 'https://i.ytimg.com/vi/' + src.replace( /.+v=([^&]+).*/, '$1' ) + '/hqdefault.jpg';
  397. cssClass += ' is-video';
  398. } else if ( src.indexOf( 'youtu.be/' ) > -1 ) {
  399. displaySrc = 'https://i.ytimg.com/vi/' + src.replace( /\/([^\/])$/, '$1' ) + '/hqdefault.jpg';
  400. cssClass += ' is-video';
  401. } else if ( src.indexOf( 'dailymotion.com' ) > -1 ) {
  402. displaySrc = src.replace( '/video/', '/thumbnail/video/' );
  403. cssClass += ' is-video';
  404. } else if ( src.indexOf( 'soundcloud.com' ) > -1 ) {
  405. cssClass += ' is-audio';
  406. } else if ( src.indexOf( 'twitter.com' ) > -1 ) {
  407. cssClass += ' is-tweet';
  408. } else {
  409. cssClass += ' is-video';
  410. }
  411. $element.attr( 'data-wp-src', src ).find( 'span' ).text( __( 'suggestedEmbedAlt' ).replace( '%d', i + 1 ) );
  412. if ( displaySrc ) {
  413. $element.css( 'background-image', 'url(' + displaySrc + ')' );
  414. }
  415. $mediaList.append( $element );
  416. found++;
  417. } );
  418. }
  419. if ( data._images ) {
  420. $.each( data._images, function( i, src ) {
  421. var displaySrc, img = new Image();
  422. src = checkUrl( src );
  423. displaySrc = src.replace( /^(http[^\?]+)(\?.*)?$/, '$1' );
  424. if ( src.indexOf( 'files.wordpress.com/' ) > -1 ) {
  425. displaySrc = displaySrc.replace( /\?.*$/, '' ) + '?w=' + smallestWidth;
  426. } else if ( src.indexOf( 'gravatar.com/' ) > -1 ) {
  427. displaySrc = displaySrc.replace( /\?.*$/, '' ) + '?s=' + smallestWidth;
  428. } else {
  429. displaySrc = src;
  430. }
  431. img.onload = function() {
  432. if ( ( img.width && img.width < 256 ) ||
  433. ( img.height && img.height < 128 ) ) {
  434. return;
  435. }
  436. addImg( src, displaySrc, i );
  437. };
  438. img.src = src;
  439. found++;
  440. } );
  441. }
  442. if ( found ) {
  443. $( '.media-list-container' ).addClass( 'has-media' );
  444. }
  445. }
  446. /* ***************************************************************
  447. * MONITORING FUNCTIONS
  448. *************************************************************** */
  449. /**
  450. * Interactive navigation behavior for the options modal (post format, tags, categories)
  451. */
  452. function monitorOptionsModal() {
  453. var $postOptions = $( '.post-options' ),
  454. $postOption = $( '.post-option' ),
  455. $settingModal = $( '.setting-modal' ),
  456. $modalClose = $( '.modal-close' );
  457. $postOption.on( 'click', function() {
  458. var index = $( this ).index(),
  459. $targetSettingModal = $settingModal.eq( index );
  460. $postOptions.addClass( isOffScreen )
  461. .one( transitionEndEvent, function() {
  462. $( this ).addClass( isHidden );
  463. } );
  464. $targetSettingModal.removeClass( offscreenHidden )
  465. .one( transitionEndEvent, function() {
  466. $( this ).find( '.modal-close' ).focus();
  467. } );
  468. } );
  469. $modalClose.on( 'click', function() {
  470. var $targetSettingModal = $( this ).parent(),
  471. index = $targetSettingModal.index();
  472. $postOptions.removeClass( offscreenHidden );
  473. $targetSettingModal.addClass( isOffScreen );
  474. if ( transitionEndEvent ) {
  475. $targetSettingModal.one( transitionEndEvent, function() {
  476. $( this ).addClass( isHidden );
  477. $postOption.eq( index - 1 ).focus();
  478. } );
  479. } else {
  480. setTimeout( function() {
  481. $targetSettingModal.addClass( isHidden );
  482. $postOption.eq( index - 1 ).focus();
  483. }, 350 );
  484. }
  485. } );
  486. }
  487. /**
  488. * Interactive behavior for the sidebar toggle, to show the options modals
  489. */
  490. function openSidebar() {
  491. sidebarIsOpen = true;
  492. $( '.options' ).removeClass( 'closed' ).addClass( 'open' );
  493. $( '.press-this-actions, #scanbar' ).addClass( isHidden );
  494. $( '.options-panel-back' ).removeClass( isHidden );
  495. $( '.options-panel' ).removeClass( offscreenHidden )
  496. .one( transitionEndEvent, function() {
  497. $( '.post-option:first' ).focus();
  498. } );
  499. }
  500. function closeSidebar() {
  501. sidebarIsOpen = false;
  502. $( '.options' ).removeClass( 'open' ).addClass( 'closed' );
  503. $( '.options-panel-back' ).addClass( isHidden );
  504. $( '.press-this-actions, #scanbar' ).removeClass( isHidden );
  505. $( '.options-panel' ).addClass( isOffScreen )
  506. .one( transitionEndEvent, function() {
  507. $( this ).addClass( isHidden );
  508. // Reset to options list
  509. $( '.post-options' ).removeClass( offscreenHidden );
  510. $( '.setting-modal').addClass( offscreenHidden );
  511. });
  512. }
  513. /**
  514. * Interactive behavior for the post title's field placeholder
  515. */
  516. function monitorPlaceholder() {
  517. var $titleField = $( '#title-container' ),
  518. $placeholder = $( '.post-title-placeholder' );
  519. $titleField.on( 'focus', function() {
  520. $placeholder.addClass( 'is-hidden' );
  521. }).on( 'blur', function() {
  522. if ( ! $titleField.text() && ! $titleField.html() ) {
  523. $placeholder.removeClass( 'is-hidden' );
  524. }
  525. }).on( 'keyup', function() {
  526. saveAlert = true;
  527. }).on( 'paste', function( event ) {
  528. var text, range,
  529. clipboard = event.originalEvent.clipboardData || window.clipboardData;
  530. if ( clipboard ) {
  531. try{
  532. text = clipboard.getData( 'Text' ) || clipboard.getData( 'text/plain' );
  533. if ( text ) {
  534. text = $.trim( text.replace( /\s+/g, ' ' ) );
  535. if ( window.getSelection ) {
  536. range = window.getSelection().getRangeAt(0);
  537. if ( range ) {
  538. if ( ! range.collapsed ) {
  539. range.deleteContents();
  540. }
  541. range.insertNode( document.createTextNode( text ) );
  542. }
  543. } else if ( document.selection ) {
  544. range = document.selection.createRange();
  545. if ( range ) {
  546. range.text = text;
  547. }
  548. }
  549. }
  550. } catch ( er ) {}
  551. event.preventDefault();
  552. }
  553. saveAlert = true;
  554. setTimeout( function() {
  555. $titleField.text( getTitleText() );
  556. }, 50 );
  557. });
  558. if ( $titleField.text() || $titleField.html() ) {
  559. $placeholder.addClass('is-hidden');
  560. }
  561. }
  562. function toggleCatItem( $element ) {
  563. if ( $element.hasClass( 'selected' ) ) {
  564. $element.removeClass( 'selected' ).attr( 'aria-checked', 'false' );
  565. } else {
  566. $element.addClass( 'selected' ).attr( 'aria-checked', 'true' );
  567. }
  568. }
  569. function monitorCatList() {
  570. $( '.categories-select' ).on( 'click.press-this keydown.press-this', function( event ) {
  571. var $element = $( event.target );
  572. if ( $element.is( 'div.category' ) ) {
  573. if ( event.type === 'keydown' && event.keyCode !== 32 ) {
  574. return;
  575. }
  576. toggleCatItem( $element );
  577. event.preventDefault();
  578. }
  579. });
  580. }
  581. function splitButtonClose() {
  582. $( '.split-button' ).removeClass( 'is-open' );
  583. $( '.split-button-toggle' ).attr( 'aria-expanded', 'false' );
  584. }
  585. /* ***************************************************************
  586. * PROCESSING FUNCTIONS
  587. *************************************************************** */
  588. /**
  589. * Calls all the rendring related functions to happen on page load
  590. */
  591. function render(){
  592. // We're on!
  593. renderToolsVisibility();
  594. renderDetectedMedia();
  595. renderStartupNotices();
  596. if ( window.tagBox ) {
  597. window.tagBox.init();
  598. }
  599. // iOS doesn't fire click events on "standard" elements without this...
  600. if ( iOS ) {
  601. $( document.body ).css( 'cursor', 'pointer' );
  602. }
  603. }
  604. /**
  605. * Set app events and other state monitoring related code.
  606. */
  607. function monitor() {
  608. var $splitButton = $( '.split-button' );
  609. $document.on( 'tinymce-editor-init', function( event, ed ) {
  610. editor = ed;
  611. editor.on( 'nodechange', function() {
  612. hasSetFocus = true;
  613. });
  614. editor.on( 'focus', function() {
  615. splitButtonClose();
  616. });
  617. editor.on( 'show', function() {
  618. setTimeout( function() {
  619. editor.execCommand( 'wpAutoResize' );
  620. }, 300 );
  621. });
  622. editor.on( 'hide', function() {
  623. setTimeout( function() {
  624. textEditorResize( 'reset' );
  625. }, 100 );
  626. });
  627. editor.on( 'keyup', mceKeyup );
  628. editor.on( 'undo redo', mceScroll );
  629. }).on( 'click.press-this keypress.press-this', '.suggested-media-thumbnail', function( event ) {
  630. if ( event.type === 'click' || event.keyCode === 13 ) {
  631. insertSelectedMedia( $( this ) );
  632. }
  633. }).on( 'click.press-this', function( event ) {
  634. if ( ! $( event.target ).closest( 'button' ).hasClass( 'split-button-toggle' ) ) {
  635. splitButtonClose();
  636. }
  637. });
  638. // Publish, Draft and Preview buttons
  639. $( '.post-actions' ).on( 'click.press-this', function( event ) {
  640. var location,
  641. $target = $( event.target ),
  642. $button = $target.closest( 'button' );
  643. if ( $button.length ) {
  644. if ( $button.hasClass( 'draft-button' ) ) {
  645. $( '.publish-button' ).addClass( 'is-saving' );
  646. submitPost( 'draft' );
  647. } else if ( $button.hasClass( 'publish-button' ) ) {
  648. $button.addClass( 'is-saving' );
  649. if ( window.history.replaceState ) {
  650. location = window.location.href;
  651. location += ( location.indexOf( '?' ) !== -1 ) ? '&' : '?';
  652. location += 'wp-press-this-reload=true';
  653. window.history.replaceState( null, null, location );
  654. }
  655. submitPost( 'publish' );
  656. } else if ( $button.hasClass( 'preview-button' ) ) {
  657. prepareFormData();
  658. window.opener && window.opener.focus();
  659. $( '#wp-preview' ).val( 'dopreview' );
  660. $( '#pressthis-form' ).attr( 'target', '_blank' ).submit().attr( 'target', '' );
  661. $( '#wp-preview' ).val( '' );
  662. } else if ( $button.hasClass( 'standard-editor-button' ) ) {
  663. $( '.publish-button' ).addClass( 'is-saving' );
  664. $( '#pt-force-redirect' ).val( 'true' );
  665. submitPost( 'draft' );
  666. } else if ( $button.hasClass( 'split-button-toggle' ) ) {
  667. if ( $splitButton.hasClass( 'is-open' ) ) {
  668. $splitButton.removeClass( 'is-open' );
  669. $button.attr( 'aria-expanded', 'false' );
  670. } else {
  671. $splitButton.addClass( 'is-open' );
  672. $button.attr( 'aria-expanded', 'true' );
  673. }
  674. }
  675. }
  676. });
  677. monitorOptionsModal();
  678. monitorPlaceholder();
  679. monitorCatList();
  680. $( '.options' ).on( 'click.press-this', function() {
  681. if ( $( this ).hasClass( 'open' ) ) {
  682. closeSidebar();
  683. } else {
  684. openSidebar();
  685. }
  686. });
  687. // Close the sidebar when focus moves outside of it.
  688. $( '.options-panel, .options-panel-back' ).on( 'focusout.press-this', function() {
  689. setTimeout( function() {
  690. var node = document.activeElement,
  691. $node = $( node );
  692. if ( sidebarIsOpen && node && ! $node.hasClass( 'options-panel-back' ) &&
  693. ( node.nodeName === 'BODY' ||
  694. ( ! $node.closest( '.options-panel' ).length &&
  695. ! $node.closest( '.options' ).length ) ) ) {
  696. closeSidebar();
  697. }
  698. }, 50 );
  699. });
  700. $( '#post-formats-select input' ).on( 'change', function() {
  701. var $this = $( this );
  702. if ( $this.is( ':checked' ) ) {
  703. $( '#post-option-post-format' ).text( $( 'label[for="' + $this.attr( 'id' ) + '"]' ).text() || '' );
  704. }
  705. } );
  706. $window.on( 'beforeunload.press-this', function() {
  707. if ( saveAlert || ( editor && editor.isDirty() ) ) {
  708. return __( 'saveAlert' );
  709. }
  710. } ).on( 'resize.press-this', function() {
  711. if ( ! editor || editor.isHidden() ) {
  712. textEditorResize( 'reset' );
  713. }
  714. });
  715. $( 'button.add-cat-toggle' ).on( 'click.press-this', function() {
  716. var $this = $( this );
  717. $this.toggleClass( 'is-toggled' );
  718. $this.attr( 'aria-expanded', 'false' === $this.attr( 'aria-expanded' ) ? 'true' : 'false' );
  719. $( '.setting-modal .add-category, .categories-search-wrapper' ).toggleClass( 'is-hidden' );
  720. } );
  721. $( 'button.add-cat-submit' ).on( 'click.press-this', saveNewCategory );
  722. $( '.categories-search' ).on( 'keyup.press-this', function() {
  723. var search = $( this ).val().toLowerCase() || '';
  724. // Don't search when less thasn 3 extended ASCII chars
  725. if ( /[\x20-\xFF]+/.test( search ) && search.length < 2 ) {
  726. return;
  727. }
  728. $.each( catsCache, function( i, cat ) {
  729. cat.node.removeClass( 'is-hidden searched-parent' );
  730. } );
  731. if ( search ) {
  732. $.each( catsCache, function( i, cat ) {
  733. if ( cat.text.indexOf( search ) === -1 ) {
  734. cat.node.addClass( 'is-hidden' );
  735. } else {
  736. cat.parents.addClass( 'searched-parent' );
  737. }
  738. } );
  739. }
  740. } );
  741. $textEditor.on( 'focus.press-this input.press-this propertychange.press-this', textEditorResize );
  742. return true;
  743. }
  744. function refreshCatsCache() {
  745. $( '.categories-select' ).find( 'li' ).each( function() {
  746. var $this = $( this );
  747. catsCache.push( {
  748. node: $this,
  749. parents: $this.parents( 'li' ),
  750. text: $this.children( '.category' ).text().toLowerCase()
  751. } );
  752. } );
  753. }
  754. // Let's go!
  755. $document.ready( function() {
  756. render();
  757. monitor();
  758. refreshCatsCache();
  759. });
  760. // Expose public methods?
  761. return {
  762. renderNotice: renderNotice,
  763. renderError: renderError
  764. };
  765. };
  766. window.wp = window.wp || {};
  767. window.wp.pressThis = new PressThis();
  768. }( jQuery, window ));