Açıklama Yok

related-posts.js 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. /* globals related_posts_js_options */
  2. /**
  3. * Load related posts
  4. */
  5. ( function () {
  6. 'use strict';
  7. var jprp = {
  8. response: null,
  9. /**
  10. * Utility get related posts JSON endpoint from URLs
  11. *
  12. * @param {string} URL (optional)
  13. * @return {string} Endpoint URL
  14. */
  15. getEndpointURL: function ( URL ) {
  16. var locationObject,
  17. is_customizer =
  18. 'undefined' !== typeof wp &&
  19. wp.customize &&
  20. wp.customize.settings &&
  21. wp.customize.settings.url &&
  22. wp.customize.settings.url.self;
  23. // If we're in Customizer, write the correct URL.
  24. if ( is_customizer ) {
  25. locationObject = document.createElement( 'a' );
  26. locationObject.href = wp.customize.settings.url.self;
  27. } else {
  28. locationObject = document.location;
  29. }
  30. if ( 'string' === typeof URL && URL.match( /^https?:\/\// ) ) {
  31. locationObject = document.createElement( 'a' );
  32. locationObject.href = URL;
  33. }
  34. var args = 'relatedposts=1';
  35. var relatedPosts = document.querySelector( '#jp-relatedposts' );
  36. if ( ! relatedPosts ) {
  37. return false;
  38. }
  39. if ( relatedPosts.hasAttribute( 'data-exclude' ) ) {
  40. args += '&relatedposts_exclude=' + relatedPosts.getAttribute( 'data-exclude' );
  41. }
  42. if ( is_customizer ) {
  43. args += '&jetpackrpcustomize=1';
  44. }
  45. var pathname = locationObject.pathname;
  46. if ( '/' !== pathname[ 0 ] ) {
  47. pathname = '/' + pathname;
  48. }
  49. if ( '' === locationObject.search ) {
  50. return pathname + '?' + args;
  51. } else {
  52. return pathname + locationObject.search + '&' + args;
  53. }
  54. },
  55. getAnchor: function ( post, classNames ) {
  56. var anchorTitle = post.title;
  57. var anchor = document.createElement( 'a' );
  58. anchor.setAttribute( 'class', classNames );
  59. anchor.setAttribute( 'href', post.url );
  60. anchor.setAttribute( 'title', anchorTitle );
  61. anchor.setAttribute( 'data-origin', post.url_meta.origin );
  62. anchor.setAttribute( 'data-position', post.url_meta.position );
  63. if ( '' !== post.rel ) {
  64. anchor.setAttribute( 'rel', post.rel );
  65. }
  66. var div = document.createElement( 'div' );
  67. div.appendChild( anchor );
  68. var anchorHTML = div.innerHTML;
  69. return [ anchorHTML.substring( 0, anchorHTML.length - 4 ), '</a>' ];
  70. },
  71. generateMinimalHtml: function ( posts, options ) {
  72. var self = this;
  73. var html = '';
  74. posts.forEach( function ( post, index ) {
  75. var anchor = self.getAnchor( post, 'jp-relatedposts-post-a' );
  76. var classes = 'jp-relatedposts-post jp-relatedposts-post' + index;
  77. if ( post.classes.length > 0 ) {
  78. classes += ' ' + post.classes.join( ' ' );
  79. }
  80. html +=
  81. '<p class="' +
  82. classes +
  83. '" data-post-id="' +
  84. post.id +
  85. '" data-post-format="' +
  86. post.format +
  87. '">';
  88. html +=
  89. '<span class="jp-relatedposts-post-title">' +
  90. anchor[ 0 ] +
  91. post.title +
  92. anchor[ 1 ] +
  93. '</span>';
  94. if ( options.showDate ) {
  95. html +=
  96. '<time class="jp-relatedposts-post-date" datetime="' +
  97. post.date +
  98. '">' +
  99. post.date +
  100. '</time>';
  101. }
  102. if ( options.showContext ) {
  103. html += '<span class="jp-relatedposts-post-context">' + post.context + '</span>';
  104. }
  105. html += '</p>';
  106. } );
  107. return (
  108. '<div class="jp-relatedposts-items jp-relatedposts-items-minimal jp-relatedposts-' +
  109. options.layout +
  110. ' ">' +
  111. html +
  112. '</div>'
  113. );
  114. },
  115. generateVisualHtml: function ( posts, options ) {
  116. var self = this;
  117. var html = '';
  118. posts.forEach( function ( post, index ) {
  119. var anchor = self.getAnchor( post, 'jp-relatedposts-post-a' );
  120. var classes = 'jp-relatedposts-post jp-relatedposts-post' + index;
  121. if ( post.classes.length > 0 ) {
  122. classes += ' ' + post.classes.join( ' ' );
  123. }
  124. if ( ! post.img.src ) {
  125. classes += ' jp-relatedposts-post-nothumbs';
  126. } else {
  127. classes += ' jp-relatedposts-post-thumbs';
  128. }
  129. var dummyContainer = document.createElement( 'p' );
  130. dummyContainer.innerHTML = post.excerpt;
  131. var excerpt = dummyContainer.textContent;
  132. html +=
  133. '<div class="' +
  134. classes +
  135. '" data-post-id="' +
  136. post.id +
  137. '" data-post-format="' +
  138. post.format +
  139. '">';
  140. if ( post.img.src ) {
  141. html +=
  142. anchor[ 0 ] +
  143. '<img class="jp-relatedposts-post-img" loading="lazy" src="' +
  144. post.img.src +
  145. '" width="' +
  146. post.img.width +
  147. '" height="' +
  148. post.img.height +
  149. '" alt="' +
  150. post.img.alt_text +
  151. '" />' +
  152. anchor[ 1 ];
  153. } else {
  154. var anchor_overlay = self.getAnchor(
  155. post,
  156. 'jp-relatedposts-post-a jp-relatedposts-post-aoverlay'
  157. );
  158. html += anchor_overlay[ 0 ] + anchor_overlay[ 1 ];
  159. }
  160. html +=
  161. '<' +
  162. related_posts_js_options.post_heading +
  163. ' class="jp-relatedposts-post-title">' +
  164. anchor[ 0 ] +
  165. post.title +
  166. anchor[ 1 ] +
  167. '</' +
  168. related_posts_js_options.post_heading +
  169. '>';
  170. html += '<p class="jp-relatedposts-post-excerpt">' + excerpt + '</p>';
  171. if ( options.showDate ) {
  172. html +=
  173. '<time class="jp-relatedposts-post-date" datetime="' +
  174. post.date +
  175. '">' +
  176. post.date +
  177. '</time>';
  178. }
  179. if ( options.showContext ) {
  180. html += '<p class="jp-relatedposts-post-context">' + post.context + '</p>';
  181. }
  182. html += '</div>';
  183. } );
  184. return (
  185. '<div class="jp-relatedposts-items jp-relatedposts-items-visual jp-relatedposts-' +
  186. options.layout +
  187. ' ">' +
  188. html +
  189. '</div>'
  190. );
  191. },
  192. /**
  193. * We want to set a max height on the excerpt however we want to set
  194. * this according to the natual pacing of the page as we never want to
  195. * cut off a line of text in the middle so we need to do some detective
  196. * work.
  197. */
  198. setVisualExcerptHeights: function () {
  199. var elements = document.querySelectorAll(
  200. '#jp-relatedposts .jp-relatedposts-post-nothumbs .jp-relatedposts-post-excerpt'
  201. );
  202. if ( ! elements.length ) {
  203. return;
  204. }
  205. var firstElementStyles = getComputedStyle( elements[ 0 ] );
  206. var fontSize = parseInt( firstElementStyles.fontSize, 10 );
  207. var lineHeight = parseInt( firstElementStyles.lineHeight, 10 );
  208. // Show 5 lines of text
  209. for ( var i = 0; i < elements.length; i++ ) {
  210. elements[ i ].style.maxHeight = ( 5 * lineHeight ) / fontSize + 'em';
  211. }
  212. },
  213. getTrackedUrl: function ( anchor ) {
  214. var args = 'relatedposts_hit=1';
  215. args += '&relatedposts_origin=' + anchor.getAttribute( 'data-origin' );
  216. args += '&relatedposts_position=' + anchor.getAttribute( 'data-position' );
  217. var pathname = anchor.pathname;
  218. if ( '/' !== pathname[ 0 ] ) {
  219. pathname = '/' + pathname;
  220. }
  221. if ( '' === anchor.search ) {
  222. return pathname + '?' + args;
  223. } else {
  224. return pathname + anchor.search + '&' + args;
  225. }
  226. },
  227. cleanupTrackedUrl: function () {
  228. if ( 'function' !== typeof history.replaceState ) {
  229. return;
  230. }
  231. var cleaned_search = document.location.search.replace(
  232. /\brelatedposts_[a-z]+=[0-9]*&?\b/gi,
  233. ''
  234. );
  235. if ( '?' === cleaned_search ) {
  236. cleaned_search = '';
  237. }
  238. if ( document.location.search !== cleaned_search ) {
  239. history.replaceState( {}, document.title, document.location.pathname + cleaned_search );
  240. }
  241. },
  242. };
  243. function afterPostsHaveLoaded() {
  244. jprp.setVisualExcerptHeights();
  245. var posts = document.querySelectorAll( '#jp-relatedposts a.jp-relatedposts-post-a' );
  246. Array.prototype.forEach.call( posts, function ( post ) {
  247. document.addEventListener( 'click', function () {
  248. post.href = jprp.getTrackedUrl( post );
  249. } );
  250. } );
  251. }
  252. /**
  253. * Initialize Related Posts.
  254. */
  255. function startRelatedPosts() {
  256. jprp.cleanupTrackedUrl();
  257. var endpointURL = jprp.getEndpointURL();
  258. var relatedPosts = document.querySelector( '#jp-relatedposts' );
  259. if ( ! endpointURL ) {
  260. return;
  261. }
  262. if ( document.querySelectorAll( '#jp-relatedposts .jp-relatedposts-post' ).length ) {
  263. afterPostsHaveLoaded();
  264. return;
  265. }
  266. var request = new XMLHttpRequest();
  267. request.open( 'GET', endpointURL, true );
  268. request.setRequestHeader( 'x-requested-with', 'XMLHttpRequest' );
  269. request.onreadystatechange = function () {
  270. if ( this.readyState === XMLHttpRequest.DONE && this.status === 200 ) {
  271. try {
  272. var response = JSON.parse( request.responseText );
  273. if ( 0 === response.items.length || 0 === relatedPosts.length ) {
  274. return;
  275. }
  276. jprp.response = response;
  277. var html,
  278. showThumbnails,
  279. options = {};
  280. if ( 'undefined' !== typeof wp && wp.customize ) {
  281. showThumbnails = wp.customize.instance( 'jetpack_relatedposts[show_thumbnails]' ).get();
  282. options.showDate = wp.customize.instance( 'jetpack_relatedposts[show_date]' ).get();
  283. options.showContext = wp.customize
  284. .instance( 'jetpack_relatedposts[show_context]' )
  285. .get();
  286. options.layout = wp.customize.instance( 'jetpack_relatedposts[layout]' ).get();
  287. } else {
  288. showThumbnails = response.show_thumbnails;
  289. options.showDate = response.show_date;
  290. options.showContext = response.show_context;
  291. options.layout = response.layout;
  292. }
  293. html = ! showThumbnails
  294. ? jprp.generateMinimalHtml( response.items, options )
  295. : jprp.generateVisualHtml( response.items, options );
  296. var div = document.createElement( 'div' );
  297. relatedPosts.appendChild( div );
  298. div.outerHTML = html;
  299. if ( options.showDate ) {
  300. var dates = relatedPosts.querySelectorAll( '.jp-relatedposts-post-date' );
  301. Array.prototype.forEach.call( dates, function ( date ) {
  302. date.style.display = 'block';
  303. } );
  304. }
  305. relatedPosts.style.display = 'block';
  306. afterPostsHaveLoaded();
  307. } catch ( error ) {
  308. // Do nothing
  309. }
  310. }
  311. };
  312. request.send();
  313. }
  314. function init() {
  315. if ( 'undefined' !== typeof wp && wp.customize ) {
  316. if ( wp.customize.selectiveRefresh ) {
  317. wp.customize.selectiveRefresh.bind( 'partial-content-rendered', function ( placement ) {
  318. if ( 'jetpack_relatedposts' === placement.partial.id ) {
  319. startRelatedPosts();
  320. }
  321. } );
  322. }
  323. wp.customize.bind( 'preview-ready', startRelatedPosts );
  324. } else {
  325. startRelatedPosts();
  326. }
  327. }
  328. if ( document.readyState !== 'loading' ) {
  329. init();
  330. } else {
  331. document.addEventListener( 'DOMContentLoaded', init );
  332. }
  333. } )();