This commit is contained in:
emmymayo
2025-02-05 23:15:46 +01:00
commit 7269c99357
16995 changed files with 3389680 additions and 0 deletions
@@ -0,0 +1,312 @@
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
use Automattic\Jetpack\Assets;
// Exit if file is accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit( 0 );
}
/**
* Class to include elements to modify Related Posts look in Customizer.
*
* @since 4.4.0
*/
class Jetpack_Related_Posts_Customize {
/**
* Key for panel, section and prefix for options. Same option name than in Options > Reading.
*
* @var string
*/
public $prefix = 'jetpack_relatedposts';
/**
* Control to focus when customizer loads
*
* @var string
*/
public $focus = '';
/**
* Class initialization.
*
* @since 4.4.0
*/
public function __construct() {
if ( ! wp_is_block_theme() ) {
add_action( 'customize_register', array( $this, 'customize_register' ) );
add_action( 'customize_controls_enqueue_scripts', array( $this, 'customize_controls_enqueue_scripts' ) );
}
}
/**
* Initialize Customizer controls.
*
* @since 4.4.0
*
* @param WP_Customize_Manager $wp_customize Customizer instance.
*/
public function customize_register( $wp_customize ) {
$wp_customize->add_section(
$this->prefix,
array(
'title' => esc_html__( 'Related Posts', 'jetpack' ),
'description' => '',
'capability' => 'edit_theme_options',
'priority' => 200,
)
);
$selective_options = array();
foreach ( $this->get_options( $wp_customize ) as $key => $field ) {
$control_id = "$this->prefix[$key]";
$selective_options[] = $control_id;
$wp_customize->add_setting(
$control_id,
array(
'default' => isset( $field['default'] ) ? $field['default'] : '',
'type' => isset( $field['setting_type'] ) ? $field['setting_type'] : 'option',
'capability' => isset( $field['capability'] ) ? $field['capability'] : 'edit_theme_options',
'transport' => isset( $field['transport'] ) ? $field['transport'] : 'postMessage',
)
);
$control_settings = array(
'label' => isset( $field['label'] ) ? $field['label'] : '',
'description' => isset( $field['description'] ) ? $field['description'] : '',
'settings' => $control_id,
'type' => isset( $field['control_type'] ) ? $field['control_type'] : 'text',
'section' => $this->prefix,
'priority' => 10,
'active_callback' => isset( $field['active_callback'] ) ? $field['active_callback'] : __CLASS__ . '::is_single',
);
switch ( $field['control_type'] ) {
case 'text':
case 'checkbox':
default:
$wp_customize->add_control( new WP_Customize_Control( $wp_customize, $control_id, $control_settings ) );
break;
case 'select':
if ( isset( $field['choices'] ) ) {
$control_settings['choices'] = $field['choices'];
$wp_customize->add_control( new WP_Customize_Control( $wp_customize, $control_id, $control_settings ) );
}
break;
case 'message':
$wp_customize->add_control( new Jetpack_Message_Control( $wp_customize, $control_id, $control_settings ) );
break;
}
}
// If selective refresh is available, implement it.
if ( isset( $wp_customize->selective_refresh ) ) {
$wp_customize->selective_refresh->add_partial(
"$this->prefix",
array(
'selector' => '.jp-relatedposts:not(.jp-relatedposts-block)',
'settings' => $selective_options,
'render_callback' => __CLASS__ . '::render_callback',
'container_inclusive' => false,
)
);
}
}
/**
* Callback that outputs the headline based on user choice.
*
* @since 4.4.0
*/
public static function render_callback() {
echo Jetpack_RelatedPosts::init()->get_headline(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- content escaped in get_headline method
}
/**
* Check whether the current post contains a Related Posts block.
*
* @since 6.9.0
*
* @return bool
*/
public static function contains_related_posts_block() {
if ( has_block( 'jetpack/related-posts' ) ) {
return true;
}
return false;
}
/**
* Check that we're in a single post view.
* Will return `false` if the current post contains a Related Posts block,
* because in that case we want to hide the Customizer controls.
*
* @since 4.4.0
*
* @return bool
*/
public static function is_single() {
if ( self::contains_related_posts_block() ) {
return false;
}
return is_single();
}
/**
* Check that we're not in a single post view.
* Will return `false` if the current post contains a Related Posts block,
* because in that case we want to hide the Customizer controls.
*
* @since 4.4.0
*
* @return bool
*/
public static function is_not_single() {
if ( self::contains_related_posts_block() ) {
return false;
}
return ! is_single();
}
/**
* Return list of options to modify.
*
* @since 4.4.0
*
* @param object $wp_customize Instance of WP Customizer.
*
* @return mixed|void
*/
public function get_options( $wp_customize ) {
$transport = isset( $wp_customize->selective_refresh ) ? 'postMessage' : 'refresh';
$switched_locale = switch_to_locale( get_user_locale() );
$headline = __( 'Related', 'jetpack' );
if ( $switched_locale ) {
restore_previous_locale();
}
/**
* The filter allows you to change the options used to display Related Posts in the Customizer.
*
* @module related-posts
*
* @since 4.4.0
*
* @param array $options Array of options used to display Related Posts in the Customizer.
*/
return apply_filters(
'jetpack_related_posts_customize_options',
array(
'enabled' => array(
'control_type' => 'hidden',
'default' => 1,
'setting_type' => 'option',
'transport' => $transport,
),
'show_headline' => array(
'label' => esc_html__( 'Show a headline', 'jetpack' ),
'description' => esc_html__( 'This helps to clearly separate the related posts from post content.', 'jetpack' ),
'control_type' => 'checkbox',
'default' => 1,
'setting_type' => 'option',
'transport' => $transport,
),
'headline' => array(
'label' => '',
'description' => esc_html__( 'Enter text to use as headline.', 'jetpack' ),
'control_type' => 'text',
'default' => esc_html( $headline ),
'setting_type' => 'option',
'transport' => $transport,
),
'show_thumbnails' => array(
'label' => esc_html__( 'Show thumbnails', 'jetpack' ),
'description' => esc_html__( 'Show a thumbnail image where available.', 'jetpack' ),
'control_type' => 'checkbox',
'default' => 1,
'setting_type' => 'option',
'transport' => $transport,
),
'show_date' => array(
'label' => esc_html__( 'Show date', 'jetpack' ),
'description' => esc_html__( 'Display date when entry was published.', 'jetpack' ),
'control_type' => 'checkbox',
'default' => 1,
'setting_type' => 'option',
'transport' => $transport,
),
'show_context' => array(
'label' => esc_html__( 'Show context', 'jetpack' ),
'description' => esc_html__( "Display entry's category or tag.", 'jetpack' ),
'control_type' => 'checkbox',
'default' => 1,
'setting_type' => 'option',
'transport' => $transport,
),
'layout' => array(
'label' => esc_html__( 'Layout', 'jetpack' ),
'description' => esc_html__( 'Arrange entries in different layouts.', 'jetpack' ),
'control_type' => 'select',
'choices' => array(
'grid' => esc_html__( 'Grid', 'jetpack' ),
'list' => esc_html__( 'List', 'jetpack' ),
),
'default' => 'grid',
'setting_type' => 'option',
'transport' => $transport,
),
'msg_go_to_single' => array(
'description' => esc_html__( 'Please visit a single post view to reveal the customization options.', 'jetpack' ),
'control_type' => 'message',
'active_callback' => __CLASS__ . '::is_not_single',
),
'msg_example' => array(
'description' => esc_html__( 'Please note that the related posts displayed now are only for previewing purposes.', 'jetpack' ),
'control_type' => 'message',
),
)
);
}
/**
* Enqueue assets for Customizer controls.
*
* @since 4.4.0
*/
public function customize_controls_enqueue_scripts() {
wp_enqueue_script(
'jetpack_related-posts-customizer',
Assets::get_file_url_for_environment(
'_inc/build/related-posts/related-posts-customizer.min.js',
'modules/related-posts/related-posts-customizer.js'
),
array( 'customize-controls' ),
JETPACK__VERSION,
false
);
}
} // class end
/**
* Control that displays a message in Customizer.
*
* @since 4.4.0
* @todo break this out into its own file.
*/
class Jetpack_Message_Control extends WP_Customize_Control { // phpcs:ignore
/**
* Render the message.
*
* @since 4.4.0
*/
public function render_content() {
echo '<p class="description">' . esc_html( $this->description ) . '</p>';
}
} // class end
// Initialize controls.
new Jetpack_Related_Posts_Customize();
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,36 @@
# Related Posts
The basic code flow for Related Posts (legacy version) is:
1. Generate an empty DIV to be added to the page.
2. Make an API call to get the working post IDs.
3. Call `get_related_post_data_for_post()` for each ID.
4. Use generated data to update the DIV once page load is complete.
For the block-based version:
1. Make an API call to get the working post IDs.
2. Call `get_related_post_data_for_post()` for each ID.
3. Use generated data to render the block when called.
## Prerequisites
If using a block theme, the site-wide Related Posts setting will be ignored. For other themes, the site-wide setting is honored as long as a Related Posts block or shortcode is not found on the page. The Related Posts code paths will only output once per page.
In both cases, the feature needs to be turned on in the Jetpack settings. This makes sense for the legacy widget but can be a bit confusing for the block. It will not be visible in the block "picker" until enabled.
## API Usage
The block depends on wpcom for the working post IDs. See `get_related_post_ids()` for details. The returned data should look like this:
```
[{"id":919},{"id":9},{"id":903}]
```
Note the data used in rendering is all generated locally once the post IDs have been provided.
## Block Source
The source for the Gutenberg block lives separately at:
`projects/plugins/jetpack/extensions/blocks/related-posts/`
@@ -0,0 +1,28 @@
/**
* Adds functionality for Related Posts controls in Customizer.
*/
( function ( api ) {
'use strict';
api( 'jetpack_relatedposts[show_headline]', function ( showHeadlineSetting ) {
var setupHeadlineControl = function ( headlineControl ) {
var setActiveState, isDisplayed;
isDisplayed = function () {
return showHeadlineSetting.findControls()[ 0 ].active.get() && showHeadlineSetting.get();
};
setActiveState = function () {
headlineControl.active.set( isDisplayed() );
};
headlineControl.active.validate = isDisplayed;
setActiveState();
showHeadlineSetting.bind( setActiveState );
};
api.control( 'jetpack_relatedposts[headline]', setupHeadlineControl );
} );
} )( wp.customize );
File diff suppressed because one or more lines are too long
@@ -0,0 +1,340 @@
/**
* Jetpack related posts
*/
/**
* The Gutenberg block
*/
.jp-related-posts-i2 {
margin-top: 1.5rem;
}
.jp-related-posts-i2__list {
--hgap: 1rem;
display: flex;
flex-wrap: wrap;
column-gap: var(--hgap);
row-gap: 2rem;
margin: 0;
padding: 0;
list-style-type: none;
}
.jp-related-posts-i2__post {
display: flex;
flex-direction: column;
/* Default: 2 items by row */
flex-basis: calc( ( 100% - var(--hgap) ) / 2 );
}
/* Quantity qeuries: see https://alistapart.com/article/quantity-queries-for-css/ */
.jp-related-posts-i2__post:nth-last-child(n+3):first-child,
.jp-related-posts-i2__post:nth-last-child(n+3):first-child ~ * {
/* From 3 total items on, 3 items by row */
flex-basis: calc( ( 100% - var(--hgap) * 2 ) / 3 );
}
.jp-related-posts-i2__post:nth-last-child(4):first-child,
.jp-related-posts-i2__post:nth-last-child(4):first-child ~ * {
/* Exception for 4 total items: 2 items by row */
flex-basis: calc( ( 100% - var(--hgap) ) / 2 );
}
.jp-related-posts-i2__post-link {
display: flex;
flex-direction: column;
row-gap: 0.5rem;
width: 100%;
margin-bottom: 1rem;
line-height: 1.2;
}
.jp-related-posts-i2__post-link:focus-visible {
outline-offset: 2px;
}
.jp-related-posts-i2__post-img {
order: -1;
max-width: 100%;
}
.jp-related-posts-i2__post-defs {
margin: 0;
list-style-type: unset;
}
/* Hide, except from screen readers */
.jp-related-posts-i2__post-defs dt {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px); white-space: nowrap;
}
.jp-related-posts-i2__post-defs dd {
margin: 0;
}
/* List view */
.jp-relatedposts-i2[data-layout="list"] .jp-related-posts-i2__list {
display: block;
}
.jp-relatedposts-i2[data-layout="list"] .jp-related-posts-i2__post {
margin-bottom: 2rem;
}
/* Breakpoints */
@media only screen and (max-width: 640px) {
.jp-related-posts-i2__list {
display: block;
}
.jp-related-posts-i2__post {
margin-bottom: 2rem;
}
}
/* Container */
#jp-relatedposts {
display: none;
padding-top: 1em;
margin: 1em 0;
position: relative;
clear: both;
}
.jp-relatedposts:after {
content: '';
display: block;
clear: both;
}
/* Headline above related posts section, labeled "Related" */
#jp-relatedposts h3.jp-relatedposts-headline {
margin: 0 0 1em 0;
display: inline-block;
float: left;
font-size: 9pt;
font-weight: bold;
font-family: inherit;
}
#jp-relatedposts h3.jp-relatedposts-headline em:before {
content: "";
display: block;
width: 100%;
min-width: 30px;
border-top: 1px solid #dcdcde;
border-top: 1px solid rgba(0,0,0,.2);
margin-bottom: 1em;
}
#jp-relatedposts h3.jp-relatedposts-headline em {
font-style: normal;
font-weight: bold;
}
/* Related posts items (wrapping items) */
#jp-relatedposts .jp-relatedposts-items {
clear: left;
}
#jp-relatedposts .jp-relatedposts-items-visual {
margin-right: -20px;
}
/* Related posts item */
#jp-relatedposts .jp-relatedposts-items .jp-relatedposts-post {
float: left;
width: 33%;
margin: 0 0 1em; /* Needs to be same as the main outer wrapper for Related Posts */
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
}
#jp-relatedposts .jp-relatedposts-items-visual .jp-relatedposts-post {
padding-right: 20px;
filter: alpha(opacity=80);
-moz-opacity: .8;
opacity: .8;
}
#jp-relatedposts .jp-relatedposts-items .jp-relatedposts-post:nth-child(3n+4),
#jp-relatedposts .jp-relatedposts-items-visual .jp-relatedposts-post:nth-child(3n+4) {
clear: both;
}
#jp-relatedposts .jp-relatedposts-items .jp-relatedposts-post:hover .jp-relatedposts-post-title a {
text-decoration: underline;
}
#jp-relatedposts .jp-relatedposts-items .jp-relatedposts-post:hover {
filter: alpha(opacity=100);
-moz-opacity: 1;
opacity: 1;
}
/* Related posts item content */
#jp-relatedposts .jp-relatedposts-items-visual h4.jp-relatedposts-post-title,
#jp-relatedposts .jp-relatedposts-items p,
#jp-relatedposts .jp-relatedposts-items time {
font-size: 14px;
line-height: 20px;
margin: 0;
}
#jp-relatedposts .jp-relatedposts-items-visual .jp-relatedposts-post-nothumbs {
position:relative;
}
#jp-relatedposts .jp-relatedposts-items-visual .jp-relatedposts-post-nothumbs a.jp-relatedposts-post-aoverlay {
position:absolute;
top:0;
bottom:0;
left:0;
right:0;
display:block;
border-bottom: 0;
}
#jp-relatedposts .jp-relatedposts-items p,
#jp-relatedposts .jp-relatedposts-items time {
margin-bottom: 0;
}
#jp-relatedposts .jp-relatedposts-items-visual h4.jp-relatedposts-post-title {
text-transform: none;
margin: 0;
font-family: inherit;
display: block;
max-width: 100%;
}
#jp-relatedposts .jp-relatedposts-items .jp-relatedposts-post .jp-relatedposts-post-title a {
font-size: inherit;
font-weight: normal;
text-decoration: none;
filter: alpha(opacity=100);
-moz-opacity: 1;
opacity: 1;
}
#jp-relatedposts .jp-relatedposts-items .jp-relatedposts-post .jp-relatedposts-post-title a:hover {
text-decoration: underline;
}
#jp-relatedposts .jp-relatedposts-items .jp-relatedposts-post img.jp-relatedposts-post-img,
#jp-relatedposts .jp-relatedposts-items .jp-relatedposts-post span {
display: block;
max-width: 90%;
overflow: hidden;
text-overflow: ellipsis;
}
#jp-relatedposts .jp-relatedposts-items-visual .jp-relatedposts-post img.jp-relatedposts-post-img,
#jp-relatedposts .jp-relatedposts-items-visual .jp-relatedposts-post span {
height: auto;
max-width: 100%;
}
#jp-relatedposts .jp-relatedposts-items .jp-relatedposts-post .jp-relatedposts-post-date,
#jp-relatedposts .jp-relatedposts-items .jp-relatedposts-post .jp-relatedposts-post-context {
opacity: .6;
}
/* Hide the date by default, but leave the element there if a theme wants to use css to make it visible. */
.jp-relatedposts-items .jp-relatedposts-post .jp-relatedposts-post-date {
display: none;
}
/* Behavior when there are thumbnails in visual mode */
#jp-relatedposts .jp-relatedposts-items-visual div.jp-relatedposts-post-thumbs p.jp-relatedposts-post-excerpt {
display: none;
}
/* Behavior when there are no thumbnails in visual mode */
#jp-relatedposts .jp-relatedposts-items-visual .jp-relatedposts-post-nothumbs p.jp-relatedposts-post-excerpt {
overflow: hidden;
}
#jp-relatedposts .jp-relatedposts-items-visual .jp-relatedposts-post-nothumbs span {
margin-bottom: 1em;
}
/* List Layout */
#jp-relatedposts .jp-relatedposts-list .jp-relatedposts-post {
clear: both;
width: 100%;
}
#jp-relatedposts .jp-relatedposts-list .jp-relatedposts-post img.jp-relatedposts-post-img {
float: left;
overflow: hidden;
max-width: 33%;
margin-right: 3%;
}
#jp-relatedposts .jp-relatedposts-list h4.jp-relatedposts-post-title {
display: inline-block;
max-width: 63%;
}
/*
* Responsive
*/
@media only screen and (max-width: 640px) {
#jp-relatedposts .jp-relatedposts-items .jp-relatedposts-post {
width: 50%;
}
#jp-relatedposts .jp-relatedposts-items .jp-relatedposts-post:nth-child(3n) {
clear: left;
}
#jp-relatedposts .jp-relatedposts-items-visual {
margin-right: 20px;
}
}
@media only screen and (max-width: 320px) {
#jp-relatedposts .jp-relatedposts-items .jp-relatedposts-post {
width: 100%;
clear: both;
margin: 0 0 1em;
}
#jp-relatedposts .jp-relatedposts-list .jp-relatedposts-post img.jp-relatedposts-post-img,
#jp-relatedposts .jp-relatedposts-list h4.jp-relatedposts-post-title {
float: none;
max-width: 100%;
margin-right: 0;
}
}
/*
* Hide the related post section in the print view of a post
*/
@media print {
.jp-relatedposts {
display:none !important;
}
}
@@ -0,0 +1,388 @@
/* globals related_posts_js_options */
/**
* Load related posts
*/
( function () {
'use strict';
var jprp = {
response: null,
/**
* Utility get related posts JSON endpoint from URLs
*
* @param {string} URL (optional)
* @return {string} Endpoint URL
*/
getEndpointURL: function ( URL ) {
var locationObject,
is_customizer =
'undefined' !== typeof wp &&
wp.customize &&
wp.customize.settings &&
wp.customize.settings.url &&
wp.customize.settings.url.self;
// If we're in Customizer, write the correct URL.
if ( is_customizer ) {
locationObject = document.createElement( 'a' );
locationObject.href = wp.customize.settings.url.self;
} else {
locationObject = document.location;
}
if ( 'string' === typeof URL && URL.match( /^https?:\/\// ) ) {
locationObject = document.createElement( 'a' );
locationObject.href = URL;
}
var args = 'relatedposts=1';
var relatedPosts = document.querySelector( '#jp-relatedposts' );
if ( ! relatedPosts ) {
return false;
}
if ( relatedPosts.hasAttribute( 'data-exclude' ) ) {
args += '&relatedposts_exclude=' + relatedPosts.getAttribute( 'data-exclude' );
}
if ( is_customizer ) {
args += '&jetpackrpcustomize=1';
}
var pathname = locationObject.pathname;
if ( '/' !== pathname[ 0 ] ) {
pathname = '/' + pathname;
}
if ( '' === locationObject.search ) {
return pathname + '?' + args;
}
return pathname + locationObject.search + '&' + args;
},
getAnchor: function ( post, classNames ) {
var anchorTitle = post.title;
var anchor = document.createElement( 'a' );
anchor.setAttribute( 'class', classNames );
anchor.setAttribute( 'href', post.url );
anchor.setAttribute( 'title', anchorTitle );
anchor.setAttribute( 'data-origin', post.url_meta.origin );
anchor.setAttribute( 'data-position', post.url_meta.position );
if ( '' !== post.rel ) {
anchor.setAttribute( 'rel', post.rel );
}
var div = document.createElement( 'div' );
div.appendChild( anchor );
var anchorHTML = div.innerHTML;
return [ anchorHTML.substring( 0, anchorHTML.length - 4 ), '</a>' ];
},
generateMinimalHtml: function ( posts, options ) {
var self = this;
var html = '';
posts.forEach( function ( post, index ) {
var anchor = self.getAnchor( post, 'jp-relatedposts-post-a' );
var classes = 'jp-relatedposts-post jp-relatedposts-post' + index;
if ( post.classes.length > 0 ) {
classes += ' ' + post.classes.join( ' ' );
}
html +=
'<p class="' +
classes +
'" data-post-id="' +
post.id +
'" data-post-format="' +
post.format +
'">';
html +=
'<span class="jp-relatedposts-post-title">' +
anchor[ 0 ] +
post.title +
anchor[ 1 ] +
'</span>';
if ( options.showDate ) {
html +=
'<time class="jp-relatedposts-post-date" datetime="' +
post.date +
'">' +
post.date +
'</time>';
}
if ( options.showContext ) {
html += '<span class="jp-relatedposts-post-context">' + post.context + '</span>';
}
html += '</p>';
} );
return (
'<div class="jp-relatedposts-items jp-relatedposts-items-minimal jp-relatedposts-' +
options.layout +
' ">' +
html +
'</div>'
);
},
generateVisualHtml: function ( posts, options ) {
var self = this;
var html = '';
posts.forEach( function ( post, index ) {
var anchor = self.getAnchor( post, 'jp-relatedposts-post-a' );
var classes = 'jp-relatedposts-post jp-relatedposts-post' + index;
if ( post.classes.length > 0 ) {
classes += ' ' + post.classes.join( ' ' );
}
if ( ! post.img.src ) {
classes += ' jp-relatedposts-post-nothumbs';
} else {
classes += ' jp-relatedposts-post-thumbs';
}
var dummyContainer = document.createElement( 'p' );
dummyContainer.innerHTML = post.excerpt;
var excerpt = dummyContainer.textContent;
html +=
'<div class="' +
classes +
'" data-post-id="' +
post.id +
'" data-post-format="' +
post.format +
'">';
if ( post.img.src ) {
html +=
anchor[ 0 ] +
'<img class="jp-relatedposts-post-img" loading="lazy" src="' +
post.img.src +
'" width="' +
post.img.width +
'" height="' +
post.img.height +
( post.img.srcset ? '" srcset="' + post.img.srcset : '' ) +
( post.img.sizes ? '" sizes="' + post.img.sizes : '' ) +
'" alt="' +
post.img.alt_text +
'" />' +
anchor[ 1 ];
} else {
var anchor_overlay = self.getAnchor(
post,
'jp-relatedposts-post-a jp-relatedposts-post-aoverlay'
);
html += anchor_overlay[ 0 ] + anchor_overlay[ 1 ];
}
html +=
'<' +
related_posts_js_options.post_heading +
' class="jp-relatedposts-post-title">' +
anchor[ 0 ] +
post.title +
anchor[ 1 ] +
'</' +
related_posts_js_options.post_heading +
'>';
html += '<p class="jp-relatedposts-post-excerpt">' + excerpt + '</p>';
if ( options.showDate ) {
html +=
'<time class="jp-relatedposts-post-date" datetime="' +
post.date +
'">' +
post.date +
'</time>';
}
if ( options.showContext ) {
html += '<p class="jp-relatedposts-post-context">' + post.context + '</p>';
}
html += '</div>';
} );
return (
'<div class="jp-relatedposts-items jp-relatedposts-items-visual jp-relatedposts-' +
options.layout +
' ">' +
html +
'</div>'
);
},
/**
* We want to set a max height on the excerpt however we want to set
* this according to the natual pacing of the page as we never want to
* cut off a line of text in the middle so we need to do some detective
* work.
*/
setVisualExcerptHeights: function () {
var elements = document.querySelectorAll(
'#jp-relatedposts .jp-relatedposts-post-nothumbs .jp-relatedposts-post-excerpt'
);
if ( ! elements.length ) {
return;
}
var firstElementStyles = getComputedStyle( elements[ 0 ] );
var fontSize = parseInt( firstElementStyles.fontSize, 10 );
var lineHeight = parseInt( firstElementStyles.lineHeight, 10 );
// Show 5 lines of text
for ( var i = 0; i < elements.length; i++ ) {
elements[ i ].style.maxHeight = ( 5 * lineHeight ) / fontSize + 'em';
}
},
getTrackedUrl: function ( anchor ) {
var args = 'relatedposts_hit=1';
args += '&relatedposts_origin=' + anchor.getAttribute( 'data-origin' );
args += '&relatedposts_position=' + anchor.getAttribute( 'data-position' );
var pathname = anchor.pathname;
if ( '/' !== pathname[ 0 ] ) {
pathname = '/' + pathname;
}
if ( '' === anchor.search ) {
return pathname + '?' + args;
}
return pathname + anchor.search + '&' + args;
},
cleanupTrackedUrl: function () {
if ( 'function' !== typeof history.replaceState ) {
return;
}
var cleaned_search = document.location.search.replace(
/\brelatedposts_[a-z]+=[0-9]*&?\b/gi,
''
);
if ( '?' === cleaned_search ) {
cleaned_search = '';
}
if ( document.location.search !== cleaned_search ) {
history.replaceState( {}, document.title, document.location.pathname + cleaned_search );
}
},
};
function afterPostsHaveLoaded() {
jprp.setVisualExcerptHeights();
var posts = document.querySelectorAll( '#jp-relatedposts a.jp-relatedposts-post-a' );
Array.prototype.forEach.call( posts, function ( post ) {
document.addEventListener( 'click', function () {
post.href = jprp.getTrackedUrl( post );
} );
} );
}
/**
* Initialize Related Posts.
*/
function startRelatedPosts() {
jprp.cleanupTrackedUrl();
var endpointURL = jprp.getEndpointURL();
if ( ! endpointURL ) {
return;
}
if ( document.querySelectorAll( '#jp-relatedposts .jp-relatedposts-post' ).length ) {
afterPostsHaveLoaded();
return;
}
var relatedPosts = document.querySelector( '#jp-relatedposts' );
var request = new XMLHttpRequest();
request.open( 'GET', endpointURL, true );
request.setRequestHeader( 'x-requested-with', 'XMLHttpRequest' );
request.onreadystatechange = function () {
if ( this.readyState === XMLHttpRequest.DONE && this.status === 200 ) {
try {
var response = JSON.parse( request.responseText );
if ( 0 === response.items.length || 0 === relatedPosts.length ) {
return;
}
jprp.response = response;
var html,
showThumbnails,
options = {};
if ( 'undefined' !== typeof wp && wp.customize ) {
showThumbnails = wp.customize.instance( 'jetpack_relatedposts[show_thumbnails]' ).get();
options.showDate = wp.customize.instance( 'jetpack_relatedposts[show_date]' ).get();
options.showContext = wp.customize
.instance( 'jetpack_relatedposts[show_context]' )
.get();
options.layout = wp.customize.instance( 'jetpack_relatedposts[layout]' ).get();
} else {
showThumbnails = response.show_thumbnails;
options.showDate = response.show_date;
options.showContext = response.show_context;
options.layout = response.layout;
}
html = ! showThumbnails
? jprp.generateMinimalHtml( response.items, options )
: jprp.generateVisualHtml( response.items, options );
var div = document.createElement( 'div' );
relatedPosts.appendChild( div );
div.outerHTML = html;
if ( options.showDate ) {
var dates = relatedPosts.querySelectorAll( '.jp-relatedposts-post-date' );
Array.prototype.forEach.call( dates, function ( date ) {
date.style.display = 'block';
} );
}
relatedPosts.style.display = 'block';
afterPostsHaveLoaded();
} catch {
// Do nothing
}
}
};
request.send();
}
function init() {
if ( 'undefined' !== typeof wp && wp.customize ) {
if ( wp.customize.selectiveRefresh ) {
wp.customize.selectiveRefresh.bind( 'partial-content-rendered', function ( placement ) {
if ( 'jetpack_relatedposts' === placement.partial.id ) {
startRelatedPosts();
}
} );
}
wp.customize.bind( 'preview-ready', startRelatedPosts );
} else {
startRelatedPosts();
}
}
if ( document.readyState !== 'loading' ) {
init();
} else {
document.addEventListener( 'DOMContentLoaded', init );
}
} )();
@@ -0,0 +1,190 @@
/* This file was automatically generated on Dec 01 2014 22:02:36 */
/**
* Styles for Jetpack related posts
*/
/* Container */
div#jp-relatedposts {
display: none;
padding-top: 1em;
margin: 1em 0;
position: relative;
}
div.jp-relatedposts:after {
content: '';
display: block;
clear: both;
}
/* Headline above related posts section, labeled "Related" */
div#jp-relatedposts h3.jp-relatedposts-headline {
margin: 0 0 1em 0;
display: inline-block;
float: right;
font-size: 9pt;
font-weight: bold;
font-family: inherit;
}
div#jp-relatedposts h3.jp-relatedposts-headline em:before {
content: "";
display: block;
width: 100%;
min-width: 30px;
border-top: 1px solid #ddd;
border-top: 1px solid rgba(0,0,0,.2);
margin-bottom: 1em;
}
div#jp-relatedposts h3.jp-relatedposts-headline em {
font-style: normal;
font-weight: bold;
}
/* Related posts items (wrapping items) */
div#jp-relatedposts div.jp-relatedposts-items {
clear: right;
}
div#jp-relatedposts div.jp-relatedposts-items-visual {
margin-left: -20px;
}
/* Related posts item */
div#jp-relatedposts div.jp-relatedposts-items .jp-relatedposts-post {
float: right;
width: 33%;
margin: 0 0 1em; /* Needs to be same as the main outer wrapper for Related Posts */
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
}
div#jp-relatedposts div.jp-relatedposts-items-visual .jp-relatedposts-post {
padding-left: 20px;
/*cursor: pointer;*/
filter: alpha(opacity=80);
-moz-opacity: .8;
opacity: .8;
}
div#jp-relatedposts div.jp-relatedposts-items div.jp-relatedposts-post:hover .jp-relatedposts-post-title a {
text-decoration: underline;
}
div#jp-relatedposts div.jp-relatedposts-items div.jp-relatedposts-post:hover {
filter: alpha(opacity=100);
-moz-opacity: 1;
opacity: 1;
}
/* Related posts item content */
div#jp-relatedposts div.jp-relatedposts-items-visual h4.jp-relatedposts-post-title,
div#jp-relatedposts div.jp-relatedposts-items p {
font-size: 14px;
line-height: 20px;
margin: 0;
}
div#jp-relatedposts div.jp-relatedposts-items-visual div.jp-relatedposts-post-nothumbs {
position:relative;
}
div#jp-relatedposts div.jp-relatedposts-items-visual div.jp-relatedposts-post-nothumbs a.jp-relatedposts-post-aoverlay {
position:absolute;
top:0;
bottom:0;
right:0;
left:0;
display:block;
}
div#jp-relatedposts div.jp-relatedposts-items p {
margin-bottom: 0;
}
div#jp-relatedposts div.jp-relatedposts-items-visual h4.jp-relatedposts-post-title {
text-transform: none;
margin: 0;
font-family: inherit;
display: block;
max-width: 100%;
}
div#jp-relatedposts div.jp-relatedposts-items .jp-relatedposts-post .jp-relatedposts-post-title a {
font-size: inherit;
font-weight: normal;
text-decoration: none;
filter: alpha(opacity=100);
-moz-opacity: 1;
opacity: 1;
}
div#jp-relatedposts div.jp-relatedposts-items .jp-relatedposts-post .jp-relatedposts-post-title a:hover {
text-decoration: underline;
}
div#jp-relatedposts div.jp-relatedposts-items .jp-relatedposts-post img.jp-relatedposts-post-img,
div#jp-relatedposts div.jp-relatedposts-items .jp-relatedposts-post span {
display: block;
max-width: 90%;
overflow: hidden;
text-overflow: ellipsis;
}
div#jp-relatedposts div.jp-relatedposts-items-visual .jp-relatedposts-post img.jp-relatedposts-post-img,
div#jp-relatedposts div.jp-relatedposts-items-visual .jp-relatedposts-post span {
max-width: 100%;
}
div#jp-relatedposts div.jp-relatedposts-items .jp-relatedposts-post .jp-relatedposts-post-context {
opacity: .6;
}
/* Behavior when there are thumbnails in visual mode */
div#jp-relatedposts div.jp-relatedposts-items-visual div.jp-relatedposts-post-thumbs p.jp-relatedposts-post-excerpt {
display: none;
}
/* Behavior when there are no thumbnails in visual mode */
div#jp-relatedposts div.jp-relatedposts-items-visual div.jp-relatedposts-post-nothumbs p.jp-relatedposts-post-excerpt {
overflow: hidden;
}
div#jp-relatedposts div.jp-relatedposts-items-visual div.jp-relatedposts-post-nothumbs span {
margin-bottom: 1em;
}
/**
* Responsive
*/
@media only screen and (max-width: 640px) {
div#jp-relatedposts div.jp-relatedposts-items .jp-relatedposts-post {
width: 50%;
}
div#jp-relatedposts div.jp-relatedposts-items .jp-relatedposts-post:nth-child(3n) {
clear: right;
}
div#jp-relatedposts div.jp-relatedposts-items-visual {
margin-left: 20px;
}
}
@media only screen and (max-width: 320px) {
div#jp-relatedposts div.jp-relatedposts-items .jp-relatedposts-post {
width: 100%;
clear: both;
margin: 0 0 1em;
}
}