PageSourceSearch

https://robertocavada.com/wp-content/plugins/jetpack/modules/related-posts/related-posts.js?ver=20240116

js robertocavada.com collected 2026-09-24 22:51:52 UTC 10,428 bytes, 388 lines download raw bytes

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} )();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.