/** * PressThis App * */ ( function( $, window ) { var PressThis = function() { var editor, saveAlert = false, textarea = document.createElement( 'textarea' ), sidebarIsOpen = false, siteConfig = window.wpPressThisConfig || {}, data = window.wpPressThisData || {}, smallestWidth = 128, interestingImages = getInterestingImages( data ) || [], interestingEmbeds = getInterestingEmbeds( data ) || [], hasEmptyTitleStr = false, suggestedTitleStr = getSuggestedTitle( data ), suggestedContentStr = getSuggestedContent( data ), hasSetFocus = false, catsCache = [], isOffScreen = 'is-off-screen', isHidden = 'is-hidden', offscreenHidden = isOffScreen + ' ' + isHidden, transitionEndEvent = ( function() { var style = document.documentElement.style; if ( typeof style.transition !== 'undefined' ) { return 'transitionend'; } if ( typeof style.WebkitTransition !== 'undefined' ) { return 'webkitTransitionEnd'; } return false; }() ); /* *************************************************************** * HELPER FUNCTIONS *************************************************************** */ /** * Emulates our PHP __() gettext function, powered by the strings exported in pressThisL10n. * * @param key string Key of the string to be translated, as found in pressThisL10n. * @returns string Original or translated string, or empty string if no key. */ function __( key ) { if ( key && window.pressThisL10n ) { return window.pressThisL10n[key] || key; } return key || ''; } /** * Strips HTML tags * * @param string string Text to have the HTML tags striped out of. * @returns string Stripped text. */ function stripTags( string ) { string = string || ''; return string .replace( /|$)/g, '' ) .replace( /<(script|style)[^>]*>[\s\S]*?(<\/\1>|$)/ig, '' ) .replace( /<\/?[a-z][\s\S]*?(>|$)/ig, '' ); } /** * Strip HTML tags and convert HTML entities. * * @param text string Text. * @returns string Sanitized text. */ function sanitizeText( text ) { text = stripTags( text ); textarea.innerHTML = text; return stripTags( textarea.value ); } /** * Allow only HTTP or protocol relative URLs. * * @param url string The URL. * @returns string Processed URL. */ function checkUrl( url ) { url = $.trim( url || '' ); if ( /^(?:https?:)?\/\//.test( url ) ) { url = stripTags( url ); return url.replace( /["\\]+/g, '' ); } return ''; } /** * Gets the source page's canonical link, based on passed location and meta data. * * @returns string Discovered canonical URL, or empty */ function getCanonicalLink() { var link = ''; if ( data._links && data._links.canonical ) { link = data._links.canonical; } if ( ! link && data.u ) { link = data.u; } if ( ! link && data._meta ) { if ( data._meta['twitter:url'] ) { link = data._meta['twitter:url']; } else if ( data._meta['og:url'] ) { link = data._meta['og:url']; } } return checkUrl( decodeURI( link ) ); } /** * Gets the source page's site name, based on passed meta data. * * @returns string Discovered site name, or empty */ function getSourceSiteName() { var name = ''; if ( data._meta ) { if ( data._meta['og:site_name'] ) { name = data._meta['og:site_name']; } else if ( data._meta['application-name'] ) { name = data._meta['application-name']; } } return sanitizeText( name ); } /** * Gets the source page's title, based on passed title and meta data. * * @returns string Discovered page title, or empty */ function getSuggestedTitle() { var title = ''; if ( data.t ) { title = data.t; } if ( ! title && data._meta ) { if ( data._meta['twitter:title'] ) { title = data._meta['twitter:title']; } else if ( data._meta['og:title'] ) { title = data._meta['og:title']; } else if ( data._meta.title ) { title = data._meta.title; } } if ( ! title ) { title = __( 'newPost' ); hasEmptyTitleStr = true; } return sanitizeText( title ); } /** * Gets the source page's suggested content, based on passed data (description, selection, etc). * Features a blockquoted excerpt, as well as content attribution, if any. * * @returns string Discovered content, or empty */ function getSuggestedContent() { var content = '', text = '', title = getSuggestedTitle(), url = getCanonicalLink(), siteName = getSourceSiteName(); if ( data.s ) { text = data.s; } else if ( data._meta ) { if ( data._meta['twitter:description'] ) { text = data._meta['twitter:description']; } else if ( data._meta['og:description'] ) { text = data._meta['og:description']; } else if ( data._meta.description ) { text = data._meta.description; } } if ( text && siteConfig.html.quote ) { // Wrap suggested content in specified HTML. content = siteConfig.html.quote.replace( /%1\$s/g, sanitizeText( text ) ); } // Add a source attribution if there is one available. if ( url && siteConfig.html.link && ( ( title && __( 'newPost' ) !== title ) || siteName ) ) { content += siteConfig.html.link.replace( /%1\$s/g, encodeURI( url ) ).replace( /%2\$s/g, ( title || siteName ) ); } return content || ''; } /** * Get a list of valid embeds from what was passed via WpPressThis_App.data._embed on page load. * * @returns array */ function getInterestingEmbeds() { var embeds = data._embed || [], interestingEmbeds = [], alreadySelected = []; if ( embeds.length ) { $.each( embeds, function ( i, src ) { if ( ! src ) { // Skip: no src value return; } var schemelessSrc = src.replace( /^https?:/, '' ); if ( $.inArray( schemelessSrc, alreadySelected ) > -1 ) { // Skip: already shown return; } interestingEmbeds.push( src ); alreadySelected.push( schemelessSrc ); } ); } return interestingEmbeds; } /** * Get a list of valid images from what was passed via WpPressThis_App.data._img and WpPressThis_App.data._meta on page load. * * @returns array */ function getInterestingImages( data ) { var imgs = data._img || [], interestingImgs = [], alreadySelected = []; if ( imgs.length ) { $.each( imgs, function ( i, src ) { src = src.replace( /http:\/\/[\d]+\.gravatar\.com\//, 'https://secure.gravatar.com/' ); src = checkUrl( src ); if ( ! src ) { // Skip: no src value return; } var schemelessSrc = src.replace( /^https?:/, '' ); if ( Array.prototype.indexOf && alreadySelected.indexOf( schemelessSrc ) > -1 ) { // Skip: already shown return; } else if ( src.indexOf( 'avatar' ) > -1 && interestingImgs.length >= 15 ) { // Skip: some type of avatar and we've already gathered more than 23 diff images to show return; } interestingImgs.push( src ); alreadySelected.push( schemelessSrc ); } ); } return interestingImgs; } /** * Show UX spinner */ function showSpinner() { $( '#spinner' ).addClass( 'show' ); $( '.post-actions button' ).each( function() { $( this ).attr( 'disabled', 'disabled' ); } ); } /** * Hide UX spinner */ function hideSpinner() { $( '#spinner' ).removeClass( 'show' ); $( '.post-actions button' ).each( function() { $( this ).removeAttr( 'disabled' ); } ); } /** * Prepare the form data for saving. */ function prepareFormData() { editor && editor.save(); $( '#post_title' ).val( sanitizeText( $( '#title-container' ).text() ) ); // Make sure to flush out the tags with tagBox before saving if ( window.tagBox ) { $( 'div.tagsdiv' ).each( function() { window.tagBox.flushTags( this, false, 1 ); } ); } } /** * Submit the post form via AJAX, and redirect to the proper screen if published vs saved as a draft. * * @param action string publish|draft */ function submitPost( action ) { var data; saveAlert = false; showSpinner(); if ( 'publish' === action ) { $( '#post_status' ).val( 'publish' ); } prepareFormData(); data = $( '#pressthis-form' ).serialize(); $.ajax( { type: 'post', url: window.ajaxurl, data: data, success: function( response ) { if ( ! response.success ) { renderError( response.data.errorMessage ); hideSpinner(); } else if ( response.data.redirect ) { if ( window.opener && siteConfig.redirInParent ) { try { window.opener.location.href = response.data.redirect; } catch( er ) {} window.self.close(); } else { window.location.href = response.data.redirect; } } } } ); } /** * Inserts the media a user has selected from the presented list inside the editor, as an image or embed, based on type * * @param type string img|embed * @param src string Source URL * @param link string Optional destination link, for images (defaults to src) */ function insertSelectedMedia( type, src, link ) { var newContent = ''; if ( ! editor ) { return; } src = checkUrl( src ); link = checkUrl( link ); if ( 'img' === type ) { if ( ! link ) { link = src; } newContent = '\n'; } else { newContent = '[embed]' + src + '[/embed]\n'; } if ( ! hasSetFocus ) { editor.focus(); } editor.execCommand( 'mceInsertContent', false, newContent ); hasSetFocus = true; } /** * Save a new user-generated category via AJAX */ function saveNewCategory() { var data, name = $( '#new-category' ).val(); if ( ! name ) { return; } data = { action: 'press-this-add-category', post_id: $( '#post_ID' ).val() || 0, name: name, new_cat_nonce: $( '#_ajax_nonce-add-category' ).val() || '', parent: $( '#new-category-parent' ).val() || 0 }; $.post( window.ajaxurl, data, function( response ) { if ( ! response.success ) { renderError( response.data.errorMessage ); } else { // TODO: change if/when the html changes. var $parent, $ul, $wrap = $( 'ul.categories-select' ); $.each( response.data, function( i, newCat ) { var $node = $( '
  • ' ).attr( 'id', 'category-' + newCat.term_id ) .append( $( '