PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.0-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.0-beta
16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / modules / related-posts / related-posts.js
jetpack / modules / related-posts Last commit date
abilities 1 week ago rtl 11 years ago class.related-posts-customize.php 1 month ago jetpack-related-posts.php 1 month ago related-posts-customizer.js 5 years ago related-posts-rtl.css 1 month ago related-posts.css 4 months ago related-posts.js 1 year ago
related-posts.js
389 lines
1 /* globals related_posts_js_options */
2
3 /**
4 * Load related posts
5 */
6 ( function () {
7 'use strict';
8
9 var jprp = {
10 response: null,
11
12 /**
13 * Utility get related posts JSON endpoint from URLs
14 *
15 * @param {string} URL (optional)
16 * @return {string} Endpoint URL
17 */
18 getEndpointURL: function ( URL ) {
19 var locationObject,
20 is_customizer =
21 'undefined' !== typeof wp &&
22 wp.customize &&
23 wp.customize.settings &&
24 wp.customize.settings.url &&
25 wp.customize.settings.url.self;
26
27 // If we're in Customizer, write the correct URL.
28 if ( is_customizer ) {
29 locationObject = document.createElement( 'a' );
30 locationObject.href = wp.customize.settings.url.self;
31 } else {
32 locationObject = document.location;
33 }
34
35 if ( 'string' === typeof URL && URL.match( /^https?:\/\// ) ) {
36 locationObject = document.createElement( 'a' );
37 locationObject.href = URL;
38 }
39
40 var args = 'relatedposts=1';
41 var relatedPosts = document.querySelector( '#jp-relatedposts' );
42
43 if ( ! relatedPosts ) {
44 return false;
45 }
46
47 if ( relatedPosts.hasAttribute( 'data-exclude' ) ) {
48 args += '&relatedposts_exclude=' + relatedPosts.getAttribute( 'data-exclude' );
49 }
50
51 if ( is_customizer ) {
52 args += '&jetpackrpcustomize=1';
53 }
54
55 var pathname = locationObject.pathname;
56 if ( '/' !== pathname[ 0 ] ) {
57 pathname = '/' + pathname;
58 }
59
60 if ( '' === locationObject.search ) {
61 return pathname + '?' + args;
62 }
63 return pathname + locationObject.search + '&' + args;
64 },
65
66 getAnchor: function ( post, classNames ) {
67 var anchorTitle = post.title;
68 var anchor = document.createElement( 'a' );
69 anchor.setAttribute( 'class', classNames );
70 anchor.setAttribute( 'href', post.url );
71 anchor.setAttribute( 'title', anchorTitle );
72 anchor.setAttribute( 'data-origin', post.url_meta.origin );
73 anchor.setAttribute( 'data-position', post.url_meta.position );
74
75 if ( '' !== post.rel ) {
76 anchor.setAttribute( 'rel', post.rel );
77 }
78
79 var div = document.createElement( 'div' );
80 div.appendChild( anchor );
81
82 var anchorHTML = div.innerHTML;
83 return [ anchorHTML.substring( 0, anchorHTML.length - 4 ), '</a>' ];
84 },
85
86 generateMinimalHtml: function ( posts, options ) {
87 var self = this;
88 var html = '';
89
90 posts.forEach( function ( post, index ) {
91 var anchor = self.getAnchor( post, 'jp-relatedposts-post-a' );
92 var classes = 'jp-relatedposts-post jp-relatedposts-post' + index;
93
94 if ( post.classes.length > 0 ) {
95 classes += ' ' + post.classes.join( ' ' );
96 }
97
98 html +=
99 '<p class="' +
100 classes +
101 '" data-post-id="' +
102 post.id +
103 '" data-post-format="' +
104 post.format +
105 '">';
106 html +=
107 '<span class="jp-relatedposts-post-title">' +
108 anchor[ 0 ] +
109 post.title +
110 anchor[ 1 ] +
111 '</span>';
112 if ( options.showDate ) {
113 html +=
114 '<time class="jp-relatedposts-post-date" datetime="' +
115 post.date +
116 '">' +
117 post.date +
118 '</time>';
119 }
120 if ( options.showContext ) {
121 html += '<span class="jp-relatedposts-post-context">' + post.context + '</span>';
122 }
123 html += '</p>';
124 } );
125 return (
126 '<div class="jp-relatedposts-items jp-relatedposts-items-minimal jp-relatedposts-' +
127 options.layout +
128 ' ">' +
129 html +
130 '</div>'
131 );
132 },
133
134 generateVisualHtml: function ( posts, options ) {
135 var self = this;
136 var html = '';
137
138 posts.forEach( function ( post, index ) {
139 var anchor = self.getAnchor( post, 'jp-relatedposts-post-a' );
140 var classes = 'jp-relatedposts-post jp-relatedposts-post' + index;
141
142 if ( post.classes.length > 0 ) {
143 classes += ' ' + post.classes.join( ' ' );
144 }
145
146 if ( ! post.img.src ) {
147 classes += ' jp-relatedposts-post-nothumbs';
148 } else {
149 classes += ' jp-relatedposts-post-thumbs';
150 }
151
152 var dummyContainer = document.createElement( 'p' );
153 dummyContainer.innerHTML = post.excerpt;
154 var excerpt = dummyContainer.textContent;
155
156 html +=
157 '<div class="' +
158 classes +
159 '" data-post-id="' +
160 post.id +
161 '" data-post-format="' +
162 post.format +
163 '">';
164 if ( post.img.src ) {
165 html +=
166 anchor[ 0 ] +
167 '<img class="jp-relatedposts-post-img" loading="lazy" src="' +
168 post.img.src +
169 '" width="' +
170 post.img.width +
171 '" height="' +
172 post.img.height +
173 ( post.img.srcset ? '" srcset="' + post.img.srcset : '' ) +
174 ( post.img.sizes ? '" sizes="' + post.img.sizes : '' ) +
175 '" alt="' +
176 post.img.alt_text +
177 '" />' +
178 anchor[ 1 ];
179 } else {
180 var anchor_overlay = self.getAnchor(
181 post,
182 'jp-relatedposts-post-a jp-relatedposts-post-aoverlay'
183 );
184 html += anchor_overlay[ 0 ] + anchor_overlay[ 1 ];
185 }
186 html +=
187 '<' +
188 related_posts_js_options.post_heading +
189 ' class="jp-relatedposts-post-title">' +
190 anchor[ 0 ] +
191 post.title +
192 anchor[ 1 ] +
193 '</' +
194 related_posts_js_options.post_heading +
195 '>';
196 html += '<p class="jp-relatedposts-post-excerpt">' + excerpt + '</p>';
197 if ( options.showDate ) {
198 html +=
199 '<time class="jp-relatedposts-post-date" datetime="' +
200 post.date +
201 '">' +
202 post.date +
203 '</time>';
204 }
205 if ( options.showContext ) {
206 html += '<p class="jp-relatedposts-post-context">' + post.context + '</p>';
207 }
208 html += '</div>';
209 } );
210 return (
211 '<div class="jp-relatedposts-items jp-relatedposts-items-visual jp-relatedposts-' +
212 options.layout +
213 ' ">' +
214 html +
215 '</div>'
216 );
217 },
218
219 /**
220 * We want to set a max height on the excerpt however we want to set
221 * this according to the natual pacing of the page as we never want to
222 * cut off a line of text in the middle so we need to do some detective
223 * work.
224 */
225 setVisualExcerptHeights: function () {
226 var elements = document.querySelectorAll(
227 '#jp-relatedposts .jp-relatedposts-post-nothumbs .jp-relatedposts-post-excerpt'
228 );
229
230 if ( ! elements.length ) {
231 return;
232 }
233
234 var firstElementStyles = getComputedStyle( elements[ 0 ] );
235
236 var fontSize = parseInt( firstElementStyles.fontSize, 10 );
237 var lineHeight = parseInt( firstElementStyles.lineHeight, 10 );
238
239 // Show 5 lines of text
240 for ( var i = 0; i < elements.length; i++ ) {
241 elements[ i ].style.maxHeight = ( 5 * lineHeight ) / fontSize + 'em';
242 }
243 },
244
245 getTrackedUrl: function ( anchor ) {
246 var args = 'relatedposts_hit=1';
247 args += '&relatedposts_origin=' + anchor.getAttribute( 'data-origin' );
248 args += '&relatedposts_position=' + anchor.getAttribute( 'data-position' );
249
250 var pathname = anchor.pathname;
251 if ( '/' !== pathname[ 0 ] ) {
252 pathname = '/' + pathname;
253 }
254
255 if ( '' === anchor.search ) {
256 return pathname + '?' + args;
257 }
258 return pathname + anchor.search + '&' + args;
259 },
260
261 cleanupTrackedUrl: function () {
262 if ( 'function' !== typeof history.replaceState ) {
263 return;
264 }
265
266 var cleaned_search = document.location.search.replace(
267 /\brelatedposts_[a-z]+=[0-9]*&?\b/gi,
268 ''
269 );
270 if ( '?' === cleaned_search ) {
271 cleaned_search = '';
272 }
273 if ( document.location.search !== cleaned_search ) {
274 history.replaceState( {}, document.title, document.location.pathname + cleaned_search );
275 }
276 },
277 };
278
279 function afterPostsHaveLoaded() {
280 jprp.setVisualExcerptHeights();
281 var posts = document.querySelectorAll( '#jp-relatedposts a.jp-relatedposts-post-a' );
282
283 Array.prototype.forEach.call( posts, function ( post ) {
284 document.addEventListener( 'click', function () {
285 post.href = jprp.getTrackedUrl( post );
286 } );
287 } );
288 }
289
290 /**
291 * Initialize Related Posts.
292 */
293 function startRelatedPosts() {
294 jprp.cleanupTrackedUrl();
295
296 var endpointURL = jprp.getEndpointURL();
297
298 if ( ! endpointURL ) {
299 return;
300 }
301
302 if ( document.querySelectorAll( '#jp-relatedposts .jp-relatedposts-post' ).length ) {
303 afterPostsHaveLoaded();
304 return;
305 }
306
307 var relatedPosts = document.querySelector( '#jp-relatedposts' );
308 var request = new XMLHttpRequest();
309 request.open( 'GET', endpointURL, true );
310 request.setRequestHeader( 'x-requested-with', 'XMLHttpRequest' );
311
312 request.onreadystatechange = function () {
313 if ( this.readyState === XMLHttpRequest.DONE && this.status === 200 ) {
314 try {
315 var response = JSON.parse( request.responseText );
316
317 if ( 0 === response.items.length || 0 === relatedPosts.length ) {
318 return;
319 }
320
321 jprp.response = response;
322
323 var html,
324 showThumbnails,
325 options = {};
326
327 if ( 'undefined' !== typeof wp && wp.customize ) {
328 showThumbnails = wp.customize.instance( 'jetpack_relatedposts[show_thumbnails]' ).get();
329 options.showDate = wp.customize.instance( 'jetpack_relatedposts[show_date]' ).get();
330 options.showContext = wp.customize
331 .instance( 'jetpack_relatedposts[show_context]' )
332 .get();
333 options.layout = wp.customize.instance( 'jetpack_relatedposts[layout]' ).get();
334 } else {
335 showThumbnails = response.show_thumbnails;
336 options.showDate = response.show_date;
337 options.showContext = response.show_context;
338 options.layout = response.layout;
339 }
340
341 html = ! showThumbnails
342 ? jprp.generateMinimalHtml( response.items, options )
343 : jprp.generateVisualHtml( response.items, options );
344
345 var div = document.createElement( 'div' );
346 relatedPosts.appendChild( div );
347 div.outerHTML = html;
348
349 if ( options.showDate ) {
350 var dates = relatedPosts.querySelectorAll( '.jp-relatedposts-post-date' );
351
352 Array.prototype.forEach.call( dates, function ( date ) {
353 date.style.display = 'block';
354 } );
355 }
356
357 relatedPosts.style.display = 'block';
358 afterPostsHaveLoaded();
359 } catch {
360 // Do nothing
361 }
362 }
363 };
364
365 request.send();
366 }
367
368 function init() {
369 if ( 'undefined' !== typeof wp && wp.customize ) {
370 if ( wp.customize.selectiveRefresh ) {
371 wp.customize.selectiveRefresh.bind( 'partial-content-rendered', function ( placement ) {
372 if ( 'jetpack_relatedposts' === placement.partial.id ) {
373 startRelatedPosts();
374 }
375 } );
376 }
377 wp.customize.bind( 'preview-ready', startRelatedPosts );
378 } else {
379 startRelatedPosts();
380 }
381 }
382
383 if ( document.readyState !== 'loading' ) {
384 init();
385 } else {
386 document.addEventListener( 'DOMContentLoaded', init );
387 }
388 } )();
389