wp/wp-content/plugins/include-mastodon-feed/plugin.php
changeset 21 48c4eec2b7e6
child 23 417f20492bf7
equal deleted inserted replaced
20:7b1b88e27a20 21:48c4eec2b7e6
       
     1 <?php
       
     2 /*
       
     3   Plugin Name: Include Mastodon Feed
       
     4 	Plugin URI: https://wolfgang.lol/code/include-mastodon-feed-wordpress-plugin
       
     5 	Description: Plugin providing [include-mastodon-feed] shortcode
       
     6 	Version: 1.9.4
       
     7 	Author: wolfgang.lol
       
     8 	Author URI: https://wolfgang.lol
       
     9 */
       
    10 
       
    11 namespace IncludeMastodonFeedPlugin;
       
    12 
       
    13 // set defaults
       
    14 $constants = [
       
    15   [
       
    16     'key' => 'INCLUDE_MASTODON_FEED_DEBUG',
       
    17     'value' => false,
       
    18   ],
       
    19   [
       
    20     'key' => 'INCLUDE_MASTODON_FEED_DEFAULT_INSTANCE',
       
    21     'value' => false,
       
    22   ],
       
    23   [
       
    24     'key' => 'INCLUDE_MASTODON_FEED_LIMIT',
       
    25     'value' => 20,
       
    26   ],
       
    27   [
       
    28     'key' => 'INCLUDE_MASTODON_FEED_EXCLUDE_BOOSTS',
       
    29     'value' => false,
       
    30   ],
       
    31   [
       
    32     'key' => 'INCLUDE_MASTODON_FEED_EXCLUDE_REPLIES',
       
    33     'value' => false,
       
    34   ],
       
    35   [
       
    36     'key' => 'INCLUDE_MASTODON_FEED_EXCLUDE_CONVERSATIONSTARTERS',
       
    37     'value' => false,
       
    38   ],
       
    39   [
       
    40     'key' => 'INCLUDE_MASTODON_FEED_ONLY_PINNED',
       
    41     'value' => false,
       
    42   ],
       
    43   [
       
    44     'key' => 'INCLUDE_MASTODON_FEED_ONLY_MEDIA',
       
    45     'value' => false,
       
    46   ],
       
    47   [
       
    48     'key' => 'INCLUDE_MASTODON_FEED_PRESERVE_IMAGE_ASPECT_RATIO',
       
    49     'value' => false,
       
    50   ],
       
    51   [
       
    52     'key' => 'INCLUDE_MASTODON_FEED_TAGGED',
       
    53     'value' => false,
       
    54   ],
       
    55   [
       
    56     'key' => 'INCLUDE_MASTODON_FEED_LINKTARGET',
       
    57     'value' => '_self',
       
    58   ],
       
    59   [
       
    60     'key' => 'INCLUDE_MASTODON_FEED_SHOW_PREVIEWCARDS',
       
    61     'value' => true,
       
    62   ],
       
    63 
       
    64   // set styles
       
    65   [
       
    66     'key' => 'INCLUDE_MASTODON_FEED_DARKMODE',
       
    67     'value' => false,
       
    68   ],
       
    69   [
       
    70     'key' => 'INCLUDE_MASTODON_FEED_STYLE_BG_LIGHT_COLOR',
       
    71     'value' => 'rgba(100, 100, 100, 0.15)',
       
    72   ],
       
    73   [
       
    74     'key' => 'INCLUDE_MASTODON_FEED_STYLE_BG_DARK_COLOR',
       
    75     'value' => 'rgba(155, 155, 155, 0.15)',
       
    76   ],
       
    77   [
       
    78     'key' => 'INCLUDE_MASTODON_FEED_STYLE_ACCENT_COLOR',
       
    79     'value' => 'rgb(99, 100, 255)',
       
    80   ],
       
    81   [
       
    82     'key' => 'INCLUDE_MASTODON_FEED_STYLE_ACCENT_FONT_COLOR',
       
    83     'value' => 'rgb(255, 255, 255)',
       
    84   ],
       
    85   [
       
    86     'key' => 'INCLUDE_MASTODON_FEED_STYLE_BORDER_RADIUS',
       
    87     'value' => '0.25rem',
       
    88   ],
       
    89   [
       
    90     'key' => 'INCLUDE_MASTODON_FEED_HIDE_STATUS_META',
       
    91     'value' => false,
       
    92   ],
       
    93   [
       
    94     'key' => 'INCLUDE_MASTODON_FEED_HIDE_DATETIME',
       
    95     'value' => false,
       
    96   ],
       
    97   // set texts and localization
       
    98   [
       
    99     'key' => 'INCLUDE_MASTODON_FEED_TEXT_LOADING',
       
   100     'value' => 'Loading Mastodon feed...',
       
   101   ],
       
   102   [
       
   103     'key' => 'INCLUDE_MASTODON_FEED_TEXT_NO_STATUSES',
       
   104     'value' => 'No statuses availablae',
       
   105   ],
       
   106   [
       
   107     'key' => 'INCLUDE_MASTODON_FEED_TEXT_BOOSTED',
       
   108     'value' => 'boosted 🚀',
       
   109   ],
       
   110   [
       
   111     'key' => 'INCLUDE_MASTODON_FEED_TEXT_VIEW_ON_INSTANCE',
       
   112     'value' => 'view on instance',
       
   113   ],
       
   114   [
       
   115     'key' => 'INCLUDE_MASTODON_FEED_TEXT_SHOW_CONTENT',
       
   116     'value' => 'Show content',
       
   117   ],
       
   118   [
       
   119     'key' => 'INCLUDE_MASTODON_FEED_TEXT_PERMALINK_PRE',
       
   120     'value' => 'on',
       
   121   ],
       
   122   [
       
   123     'key' => 'INCLUDE_MASTODON_FEED_TEXT_PERMALINK_POST',
       
   124     'value' => '',
       
   125   ],
       
   126   [
       
   127     'key' => 'INCLUDE_MASTODON_FEED_TEXT_EDITED',
       
   128     'value' => '(edited)',
       
   129   ],
       
   130   [
       
   131     'key' => 'INCLUDE_MASTODON_FEED_DATE_LOCALE',
       
   132     'value' => 'en-US',
       
   133   ],
       
   134   [
       
   135     'key' => 'INCLUDE_MASTODON_FEED_DATE_OPTIONS',
       
   136     'value' => "{}",
       
   137   ],
       
   138 ];
       
   139 foreach($constants as $constant) {
       
   140   if(!defined($constant['key'])) {
       
   141       define($constant['key'], $constant['value']);
       
   142   }
       
   143 }
       
   144 unset($constants);
       
   145 
       
   146 function error($msg) {
       
   147   return '[include-mastodon-feed] ' . $msg;
       
   148 }
       
   149 
       
   150 
       
   151 function init_styles() {
       
   152   ob_start();
       
   153 ?>
       
   154   <style>
       
   155     :root {
       
   156       --include-mastodon-feed-bg-light: <?php echo filter_var( INCLUDE_MASTODON_FEED_STYLE_BG_LIGHT_COLOR, FILTER_UNSAFE_RAW ); ?>;
       
   157       --include-mastodon-feed-bg-dark: <?php echo filter_var( INCLUDE_MASTODON_FEED_STYLE_BG_DARK_COLOR, FILTER_UNSAFE_RAW ); ?>;
       
   158       --include-mastodon-feed-accent-color: <?php echo filter_var( INCLUDE_MASTODON_FEED_STYLE_ACCENT_COLOR, FILTER_UNSAFE_RAW ); ?>;
       
   159       --include-mastodon-feed-accent-font-color: <?php echo filter_var( INCLUDE_MASTODON_FEED_STYLE_ACCENT_FONT_COLOR, FILTER_UNSAFE_RAW ); ?>;
       
   160       --include-mastodon-feed-border-radius: <?php echo filter_var( INCLUDE_MASTODON_FEED_STYLE_BORDER_RADIUS, FILTER_UNSAFE_RAW ); ?>;
       
   161     }
       
   162 
       
   163     .include-mastodon-feed .status {
       
   164       margin: 0.5rem 0 1.5rem;
       
   165       border-radius: var(--include-mastodon-feed-border-radius);
       
   166       padding: 0.5rem;
       
   167       background: var(--include-mastodon-feed-bg-light);
       
   168     }
       
   169     .include-mastodon-feed .status a {
       
   170       color: var(--include-mastodon-feed-accent-color);
       
   171       text-decoration: none;
       
   172       word-wrap: break-word;
       
   173     }
       
   174     .include-mastodon-feed .status a:hover {
       
   175       text-decoration: underline;
       
   176     }
       
   177     .include-mastodon-feed .avatar {
       
   178       height: 1.25rem;
       
   179       border-radius: var(--include-mastodon-feed-border-radius);
       
   180       vertical-align: top;
       
   181     }
       
   182     .include-mastodon-feed .account {
       
   183       font-size: 0.8rem;
       
   184     }
       
   185     .include-mastodon-feed .account a {
       
   186       display: inline-block;
       
   187     }
       
   188     .include-mastodon-feed .account .booster {
       
   189       float: right;
       
   190       font-style: italic;
       
   191     }
       
   192     .include-mastodon-feed .boosted .account > a:first-child,
       
   193     .include-mastodon-feed .contentWarning a {
       
   194       border-radius: var(--include-mastodon-feed-border-radius);
       
   195       padding: 0.15rem 0.5rem;
       
   196       background: var(--include-mastodon-feed-accent-color);
       
   197       color: var(--include-mastodon-feed-accent-font-color);
       
   198     }
       
   199     .include-mastodon-feed .boosted .account > a:first-child:hover,
       
   200     .include-mastodon-feed .contentWarning a:hover {
       
   201       border-radius: var(--include-mastodon-feed-border-radius);
       
   202       padding: 0.15rem 0.5rem;
       
   203       background: var(--include-mastodon-feed-accent-font-color);
       
   204       color: var(--include-mastodon-feed-accent-color);
       
   205       text-decoration: none;
       
   206     }
       
   207     .include-mastodon-feed .contentWrapper.boosted {
       
   208       margin: 0.5rem 0;
       
   209       padding: 0.5rem;
       
   210       background: var(--include-mastodon-feed-bg-light);
       
   211     }
       
   212     .include-mastodon-feed .contentWarning {
       
   213       text-align: center;
       
   214       margin: 1rem;
       
   215       padding: 1rem;
       
   216     }
       
   217     .include-mastodon-feed .contentWarning .title {
       
   218       font-weight: bold;
       
   219     }
       
   220     .include-mastodon-feed img.emoji {
       
   221       height: 1rem;
       
   222     }
       
   223     .include-mastodon-feed .content .invisible {
       
   224       display: none;
       
   225     }
       
   226     .include-mastodon-feed .media {
       
   227       display: flex;
       
   228       justify-content: space-around;
       
   229       align-items: center;
       
   230       flex-wrap: wrap;
       
   231       gap: 0.5rem;
       
   232       margin: 1rem;
       
   233     }
       
   234     .include-mastodon-feed .media > div {
       
   235       flex-basis: calc(50% - 0.5rem);
       
   236       flex-grow: 1;
       
   237     }
       
   238     .include-mastodon-feed .media > .image {
       
   239       font-size: 0.8rem;
       
   240       font-weight: bold;
       
   241       text-align: center;
       
   242     }
       
   243     .include-mastodon-feed .media > .image a { 
       
   244       border-radius: var(--include-mastodon-feed-border-radius);
       
   245       display: block;
       
   246       aspect-ratio: 1.618;                                                      
       
   247       background-size: cover;
       
   248       background-position: center;
       
   249     }
       
   250         .include-mastodon-feed .media > .image a:hover {
       
   251       filter: contrast(110%) brightness(130%) saturate(130%);
       
   252     }
       
   253     .include-mastodon-feed .media > .image a img {
       
   254       width: 100%;
       
   255     }
       
   256     .include-mastodon-feed .media > .gifv video {
       
   257       max-width: 100%;
       
   258     }
       
   259 
       
   260     .include-mastodon-feed .card {
       
   261       border-radius: var(--include-mastodon-feed-border-radius);
       
   262       margin: 1rem 0.5rem;
       
   263     }
       
   264     .include-mastodon-feed .card iframe {
       
   265       border-radius: var(--include-mastodon-feed-border-radius);
       
   266       width: 100%;
       
   267       height: 100%;
       
   268       aspect-ratio: 2 / 1.25;
       
   269     }
       
   270     .include-mastodon-feed .card a {
       
   271       border-radius: var(--include-mastodon-feed-border-radius);
       
   272       display: block;
       
   273       text-decoration: none;
       
   274       color: #000;
       
   275     }
       
   276     .include-mastodon-feed.dark .card a {
       
   277       color: #fff;
       
   278     }
       
   279     .include-mastodon-feed .card a:hover {
       
   280       text-decoration: none;
       
   281       background: var(--include-mastodon-feed-accent-color);
       
   282       color: var(--include-mastodon-feed-accent-font-color);
       
   283     }
       
   284     .include-mastodon-feed .card .meta {
       
   285       background: var(--include-mastodon-feed-bg-light);
       
   286       font-size: 0.8rem;
       
   287       padding: 1rem;
       
   288     }
       
   289     .include-mastodon-feed .card .image {
       
   290       margin-bottom: 0.5rem;
       
   291       text-align: center;
       
   292     }
       
   293     .include-mastodon-feed .card .image img {
       
   294       max-width: 75%;
       
   295     }
       
   296     .include-mastodon-feed .card .title {
       
   297       font-weight: bold;
       
   298     }
       
   299     .include-mastodon-feed.dark .status,
       
   300     .include-mastodon-feed.dark .contentWrapper.boosted,
       
   301     .include-mastodon-feed.dark .card {
       
   302       background: var(--include-mastodon-feed-bg-dark);
       
   303     }
       
   304   </style>
       
   305 <?php
       
   306   echo ob_get_clean();
       
   307 }
       
   308 add_action('wp_head', __NAMESPACE__ . '\init_styles', 7);
       
   309 
       
   310 function init_scripts() {
       
   311   ob_start();
       
   312 ?>
       
   313   <script>
       
   314 
       
   315     const mastodonFeedCreateElement = function(type, className = null) {
       
   316       let element = document.createElement(type);
       
   317       if(null !== className) {
       
   318         element.className = className;
       
   319       }
       
   320       return element;
       
   321     }
       
   322 
       
   323     const mastodonFeedCreateElementAccountLink = function(account) {
       
   324       let accountLinkElem = mastodonFeedCreateElement('a');
       
   325       accountLinkElem.href = account.url;
       
   326 
       
   327       let accountImageElem = mastodonFeedCreateElement('img', 'avatar');
       
   328       accountImageElem.src = account.avatar_static;
       
   329 
       
   330       accountLinkElem.addEventListener('mouseover', (event) => {
       
   331         accountLinkElem.querySelector('.avatar').src = account.avatar;
       
   332       });
       
   333       accountLinkElem.addEventListener('mouseout', (event) => {
       
   334         accountLinkElem.querySelector('.avatar').src = account.avatar_static;
       
   335       });
       
   336 
       
   337       accountLinkElem.appendChild(accountImageElem);
       
   338       // inject emojis
       
   339       let displayName = account.display_name;
       
   340       if(account.emojis.length > 0) {
       
   341         account.emojis.forEach(function(emoji) {
       
   342           displayName = mastodonFeedInjectEmoji(displayName, emoji);
       
   343         });
       
   344       }
       
   345       accountLinkElem.innerHTML += ' ' + displayName;
       
   346       return accountLinkElem;
       
   347     }
       
   348 
       
   349     const mastodonFeedCreateElementPermalink = function(status, label) {
       
   350       let linkElem = mastodonFeedCreateElement('a');
       
   351       linkElem.href = status.url;
       
   352       linkElem.appendChild(document.createTextNode(label));
       
   353       return linkElem;
       
   354     }
       
   355 
       
   356     const mastodonFeedCreateElementMediaAttachments = function(status, options) {
       
   357       let attachments = status.media_attachments;
       
   358       let mediaWrapperElem = mastodonFeedCreateElement('div', 'media');
       
   359       for(let mediaIndex = 0; mediaIndex < attachments.length; mediaIndex++) {
       
   360         let media = attachments[mediaIndex];
       
   361         let mediaElem = mastodonFeedCreateElement('div', media.type);
       
   362         if('image' == media.type) {
       
   363           let mediaElemImgLink = mastodonFeedCreateElement('a');
       
   364           mediaElemImgLink.href = status.url;
       
   365           if(null === media.remote_url) {
       
   366             mediaElemImgLink.style.backgroundImage = 'url("' + media.preview_url + '")';
       
   367           }
       
   368           else {
       
   369             mediaElemImgLink.style.backgroundImage = 'url("' + media.remote_url + '")';
       
   370           }
       
   371           if(null !== media.description) {
       
   372             mediaElem.title = media.description;
       
   373           }
       
   374           if(options.preserveImageAspectRatio) {
       
   375             let mediaElemImgImage = mastodonFeedCreateElement('img');
       
   376             if(null === media.remote_url) {
       
   377               mediaElemImgImage.src = media.preview_url;
       
   378             }
       
   379             else {
       
   380               mediaElemImgImage.src = media.remote_url;
       
   381             }
       
   382             mediaElemImgLink.appendChild(mediaElemImgImage);
       
   383           }
       
   384           mediaElem.appendChild(mediaElemImgLink);
       
   385         }
       
   386         else if('gifv' == media.type) {
       
   387           let mediaElemGifvLink = mastodonFeedCreateElement('a');
       
   388           mediaElemGifvLink.href = status.url;
       
   389           let mediaElemGifv = mastodonFeedCreateElement('video', 'requiresInteraction');
       
   390           if(null === media.remote_url) {
       
   391             mediaElemGifv.src = media.url;
       
   392           }
       
   393           else {
       
   394             mediaElemGifv.src = media.remote_url;
       
   395           }
       
   396           mediaElemGifv.loop = true;
       
   397           mediaElemGifv.muted = 'muted';
       
   398           if(null !== media.description) {
       
   399             mediaElemGifv.title = media.description;
       
   400           }
       
   401           mediaElemGifvLink.appendChild(mediaElemGifv);
       
   402           mediaElem.appendChild(mediaElemGifvLink);
       
   403 
       
   404           mediaElemGifv.addEventListener('mouseover', (event) => {
       
   405             mediaElemGifv.play();
       
   406           });
       
   407           mediaElemGifv.addEventListener('mouseout', (event) => {
       
   408             mediaElemGifv.pause();
       
   409             mediaElemGifv.currentTime = 0;
       
   410           });
       
   411         }
       
   412         else {
       
   413           // TODO implement support for other media types
       
   414           //      currently only image and gifv support implemented
       
   415           mediaElem.innerHTML = 'Stripped ' + media.type + ' - only available on instance<br />';
       
   416           let permalinkElem = mastodonFeedCreateElement('span', 'permalink');
       
   417           permalinkElem.appendChild(mastodonFeedCreateElementPermalink(status, options.text.viewOnInstance));
       
   418           mediaElem.appendChild(permalinkElem);
       
   419         }
       
   420         mediaWrapperElem.appendChild(mediaElem);
       
   421       }
       
   422       return mediaWrapperElem;
       
   423     }
       
   424 
       
   425     const mastodonFeedCreateElementPreviewCard = function(card)  {
       
   426       let cardElem = mastodonFeedCreateElement('div', 'card');
       
   427           
       
   428       if(null === card.html || card.html.length < 1) {
       
   429         let cardElemMeta = mastodonFeedCreateElement('div', 'meta');
       
   430 
       
   431         if(null !== card.image) {
       
   432           let cardElemImageWrapper = mastodonFeedCreateElement('div', 'image');
       
   433           let cardElemImage = mastodonFeedCreateElement('img');
       
   434           cardElemImage.src = card.image;
       
   435           cardElemImageWrapper.appendChild(cardElemImage);
       
   436           cardElemMeta.appendChild(cardElemImageWrapper);
       
   437         }
       
   438 
       
   439         let cardElemTitle = mastodonFeedCreateElement('div', 'title');
       
   440         cardElemTitle.innerHTML = card.title;
       
   441         cardElemMeta.appendChild(cardElemTitle);
       
   442 
       
   443         let cardElemDescription = mastodonFeedCreateElement('div', 'description');
       
   444         cardElemDescription.innerHTML = card.description;
       
   445         cardElemMeta.appendChild(cardElemDescription);
       
   446         
       
   447         if(card.url === null) {
       
   448           cardElem.appendChild(cardElemMeta);
       
   449         }
       
   450         else {
       
   451           let cardElemLink = mastodonFeedCreateElement('a');
       
   452           cardElemLink.href = card.url;
       
   453           cardElemLink.appendChild(cardElemMeta);
       
   454           cardElem.appendChild(cardElemLink);
       
   455         }
       
   456       }
       
   457       else {
       
   458         cardElem.innerHTML = card.html;
       
   459       }
       
   460       return cardElem;
       
   461     }
       
   462 
       
   463     const mastodonFeedCreateElementTimeinfo = function(status, options, url = false) {
       
   464       let createdInfo = mastodonFeedCreateElement('span', 'permalink');
       
   465       createdInfo.innerHTML = ' ' + options.text.permalinkPre + ' ';
       
   466       if(false === url) {
       
   467         createdInfo.innerHTML += new Date(status.created_at).toLocaleString(options.localization.date.locale, options.localization.date.options);
       
   468       }
       
   469       else {
       
   470         createdInfo.appendChild(mastodonFeedCreateElementPermalink(status, new Date(status.created_at).toLocaleString(options.localization.date.locale, options.localization.date.options)));
       
   471       }
       
   472       createdInfo.innerHTML += ' ' + options.text.permalinkPost;
       
   473       return createdInfo;
       
   474     }
       
   475 
       
   476     const mastodonFeedInjectEmoji = function(string, emoji) {
       
   477       return string.replaceAll(':' + emoji.shortcode + ':', '<img class="emoji" src="' + emoji.url + '" title="' + emoji.shortcode + '" />');
       
   478     }
       
   479 
       
   480     const mastodonFeedRenderStatuses = function(statuses, rootElem, options) {
       
   481       if(statuses.length < 1) {
       
   482         console.log(options);
       
   483         rootElem.innerHTML = options.text.noStatuses;
       
   484       }
       
   485       else {
       
   486         for(let i = 0; i < statuses.length; i++) {
       
   487           let status = statuses[i];
       
   488           let isEdited = (null === status.edited_at ? true : false);
       
   489           let isReblog = (null === status.reblog ? false : true);
       
   490 
       
   491           let statusElem = mastodonFeedCreateElement('div', 'status');
       
   492 
       
   493           // add account meta info
       
   494           if(!options.content.hideStatusMeta) {
       
   495             let accountElem = mastodonFeedCreateElement('div', 'account');
       
   496             if(isReblog) {
       
   497               let boosterElem = mastodonFeedCreateElement('span', 'booster');
       
   498               boosterElem.appendChild(document.createTextNode( options.text.boosted ));
       
   499               accountElem.appendChild(boosterElem);
       
   500             }            
       
   501             accountElem.appendChild(mastodonFeedCreateElementAccountLink(status.account));
       
   502             if(!options.content.hideDateTime) {
       
   503               accountElem.appendChild(mastodonFeedCreateElementTimeinfo(status, options, (isReblog ? false : status.url)));
       
   504             }
       
   505             if(null !== status.edited_at) {
       
   506               accountElem.innerHTML += ' ' + options.text.edited;
       
   507             }
       
   508             statusElem.appendChild(accountElem);
       
   509           }
       
   510 
       
   511           // prepare content rendering
       
   512           let showStatus = status;
       
   513           if(isReblog) {
       
   514             showStatus = status.reblog;
       
   515           }
       
   516           let contentWrapperElem = mastodonFeedCreateElement('div', 'contentWrapper' + (isReblog ? ' boosted' : ''));
       
   517 
       
   518           // add boosted post meta info
       
   519           if(isReblog) {
       
   520             let boostElem = mastodonFeedCreateElement('div', 'account');
       
   521             let boostAccountLink = mastodonFeedCreateElementAccountLink(showStatus.account);
       
   522             boostElem.appendChild(boostAccountLink);
       
   523             boostElem.appendChild(mastodonFeedCreateElementTimeinfo(showStatus, options, showStatus.url));
       
   524 
       
   525             contentWrapperElem.appendChild(boostElem);
       
   526           }
       
   527 
       
   528           let contentElem = mastodonFeedCreateElement('div', 'content');
       
   529 
       
   530           // handle content warnings
       
   531           if(showStatus.sensitive || showStatus.spoiler_text.length > 0) {
       
   532             let cwElem = mastodonFeedCreateElement('div', 'contentWarning');
       
   533 
       
   534             if(showStatus.spoiler_text.length > 0) {
       
   535               let cwTitleElem = mastodonFeedCreateElement('div', 'title');
       
   536               cwTitleElem.innerHTML = showStatus.spoiler_text;
       
   537               cwElem.appendChild(cwTitleElem);
       
   538             }
       
   539 
       
   540             let cwLinkElem = mastodonFeedCreateElement('a');
       
   541             cwLinkElem.href = '#';
       
   542             cwLinkElem.onclick = function() {
       
   543               this.parentElement.style = 'display: none;';
       
   544               this.parentElement.nextSibling.style = 'display: block;';
       
   545               return false;
       
   546             }
       
   547             cwLinkElem.innerHTML = options.text.showContent;
       
   548             cwElem.appendChild(cwLinkElem);
       
   549 
       
   550             contentWrapperElem.appendChild(cwElem);
       
   551             contentElem.style = 'display: none;';
       
   552           }
       
   553 
       
   554           // add regular content
       
   555           let renderContent = showStatus.content;
       
   556           // inject emojis
       
   557           if(showStatus.emojis.length > 0) {
       
   558             showStatus.emojis.forEach(function(emoji) {
       
   559               renderContent = mastodonFeedInjectEmoji(renderContent, emoji);
       
   560             });
       
   561           }
       
   562           contentElem.innerHTML += renderContent;
       
   563 
       
   564           // handle media attachments
       
   565           if(showStatus.media_attachments.length > 0) {
       
   566             let mediaAttachmentsElem = mastodonFeedCreateElementMediaAttachments(showStatus, options);
       
   567             contentElem.appendChild(mediaAttachmentsElem);
       
   568           }
       
   569 
       
   570           // handle preview card
       
   571           if(options.showPreviewCards && showStatus.card != null) {
       
   572             let cardElem = mastodonFeedCreateElementPreviewCard(showStatus.card);
       
   573             contentElem.appendChild(cardElem);
       
   574           }
       
   575 
       
   576           contentWrapperElem.appendChild(contentElem);
       
   577           statusElem.appendChild(contentWrapperElem);
       
   578           rootElem.appendChild(statusElem);
       
   579         }
       
   580       }
       
   581       if('_self' != options.linkTarget) {
       
   582         rootElem.querySelectorAll('a').forEach(function(e) {
       
   583           e.target = options.linkTarget;
       
   584         });
       
   585       }
       
   586     }
       
   587 
       
   588     const mastodonFeedLoad = function(url, elementId, options) {
       
   589       const xhr = new XMLHttpRequest();
       
   590       xhr.open('GET', url, true);
       
   591       xhr.responseType = 'json';
       
   592       xhr.onload = function() {
       
   593         const statuses = xhr.response;
       
   594         const rootElem = document.getElementById(elementId);
       
   595         rootElem.innerHTML = '';
       
   596         <?php if(true === INCLUDE_MASTODON_FEED_DEBUG) : ?>
       
   597           console.log("<?php echo __NAMESPACE__; ?>", url);
       
   598         <?php endif; ?>
       
   599         if (xhr.status === 200) {
       
   600           <?php if(true === INCLUDE_MASTODON_FEED_DEBUG) : ?>
       
   601             console.log("<?php echo __NAMESPACE__; ?>", xhr.response);
       
   602           <?php endif; ?>
       
   603           if(options.excludeConversationStarters && statuses.length > 0) {
       
   604             const filteredStatuses = [];
       
   605             for(let i = 0; i < statuses.length; i++) {
       
   606               let includeStatus = true;
       
   607               if(statuses[i].mentions.length > 0) {
       
   608                 const statusContent = document.createElement('div');
       
   609                 statusContent.innerHTML = statuses[i].content;
       
   610                 const mentionUsername = statuses[i].mentions[0].acct.split('@')[0];
       
   611                 const plainTextContent = statusContent.textContent || statusContent.innerText;
       
   612                 if(plainTextContent.substring(1, ('@' + mentionUsername).length) == mentionUsername) {
       
   613                   includeStatus = false;
       
   614                 }
       
   615               }
       
   616               if(includeStatus) {
       
   617                 filteredStatuses.push(statuses[i]);
       
   618               }
       
   619             }
       
   620             mastodonFeedRenderStatuses(filteredStatuses, rootElem, options);
       
   621           }
       
   622           else  {
       
   623             mastodonFeedRenderStatuses(statuses, rootElem, options);
       
   624           }
       
   625         }
       
   626         else {
       
   627           <?php if(true === INCLUDE_MASTODON_FEED_DEBUG) : ?>
       
   628             console.log("<?php echo __NAMESPACE__; ?>", xhr);
       
   629           <?php endif; ?>
       
   630           rootElem.appendChild(document.createTextNode(xhr.response.error));
       
   631         }
       
   632       };
       
   633       xhr.send();
       
   634     }
       
   635   </script>
       
   636 <?php
       
   637   echo ob_get_clean();
       
   638 }
       
   639 add_action('wp_footer', __NAMESPACE__ . '\init_scripts');
       
   640 
       
   641 function display_feed($atts) {
       
   642   $atts = shortcode_atts(
       
   643       array(
       
   644           'instance' => ( INCLUDE_MASTODON_FEED_DEFAULT_INSTANCE === false ? false : filter_var( INCLUDE_MASTODON_FEED_DEFAULT_INSTANCE, FILTER_UNSAFE_RAW ) ),
       
   645 					'account' => false,
       
   646           'tag' => false,
       
   647           'limit' => INCLUDE_MASTODON_FEED_LIMIT,
       
   648           'excludeboosts' => filter_var(esc_html(INCLUDE_MASTODON_FEED_EXCLUDE_BOOSTS), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE),
       
   649           'excludereplies' => filter_var(esc_html(INCLUDE_MASTODON_FEED_EXCLUDE_REPLIES), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE),
       
   650           'excludeconversationstarters' => filter_var(esc_html(INCLUDE_MASTODON_FEED_EXCLUDE_CONVERSATIONSTARTERS), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE),
       
   651           'onlypinned' => filter_var(esc_html(INCLUDE_MASTODON_FEED_ONLY_PINNED), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE),
       
   652           'onlymedia' => filter_var(esc_html(INCLUDE_MASTODON_FEED_ONLY_MEDIA), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE),
       
   653           'preserveimageaspectratio' => filter_var(esc_html(INCLUDE_MASTODON_FEED_PRESERVE_IMAGE_ASPECT_RATIO), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE),
       
   654           'tagged' => INCLUDE_MASTODON_FEED_TAGGED,
       
   655           'linktarget' => INCLUDE_MASTODON_FEED_LINKTARGET,
       
   656           'showpreviewcards' => filter_var(esc_html(INCLUDE_MASTODON_FEED_SHOW_PREVIEWCARDS), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE),
       
   657           'hidestatusmeta' => filter_var(esc_html(INCLUDE_MASTODON_FEED_HIDE_STATUS_META), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE),
       
   658           'hidedatetime' => filter_var(esc_html(INCLUDE_MASTODON_FEED_HIDE_DATETIME), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE),
       
   659           'text-loading' => INCLUDE_MASTODON_FEED_TEXT_LOADING,
       
   660           'text-nostatuses' => INCLUDE_MASTODON_FEED_TEXT_NO_STATUSES,
       
   661           'text-boosted' => INCLUDE_MASTODON_FEED_TEXT_BOOSTED,
       
   662           'text-viewoninstance' => INCLUDE_MASTODON_FEED_TEXT_VIEW_ON_INSTANCE,
       
   663           'text-showcontent' => INCLUDE_MASTODON_FEED_TEXT_SHOW_CONTENT,
       
   664           'text-permalinkpre' => INCLUDE_MASTODON_FEED_TEXT_PERMALINK_PRE,
       
   665           'text-permalinkpost' => INCLUDE_MASTODON_FEED_TEXT_PERMALINK_POST,
       
   666           'text-edited' => INCLUDE_MASTODON_FEED_TEXT_EDITED,
       
   667           'date-locale' => INCLUDE_MASTODON_FEED_DATE_LOCALE,
       
   668           'date-options' => INCLUDE_MASTODON_FEED_DATE_OPTIONS,
       
   669           'darkmode' => filter_var(esc_html(INCLUDE_MASTODON_FEED_DARKMODE), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE),
       
   670       ), array_change_key_case($atts, CASE_LOWER)
       
   671   );
       
   672 
       
   673   if(false === filter_var($atts['instance'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) {
       
   674     return error('missing configuration: instance');
       
   675   }
       
   676   if(false === filter_var($atts['account'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) && false === filter_var($atts['tag'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) {
       
   677     return error('missing configuration: account id or tag');
       
   678   }
       
   679 
       
   680 
       
   681   if(false !== filter_var($atts['account'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) {
       
   682     $apiUrl = 'https://'.urlencode($atts['instance']).'/api/v1/accounts/'.$atts['account'].'/statuses';
       
   683   }
       
   684   if(false !== filter_var($atts['tag'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) {
       
   685     $apiUrl = 'https://'.urlencode($atts['instance']).'/api/v1/timelines/tag/'.urlencode($atts['tag']);
       
   686   }
       
   687 
       
   688   $getParams = [];
       
   689   if($atts['limit'] != 20 && $atts['limit'] > 0) {
       
   690     $getParams[] = 'limit=' . filter_var( $atts['limit'], FILTER_SANITIZE_NUMBER_INT );
       
   691   }
       
   692   if(false !== filter_var($atts['excludeboosts'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) {
       
   693     $getParams[] = 'exclude_reblogs=true';
       
   694   }
       
   695   if(false !== filter_var($atts['excludereplies'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) {
       
   696     $getParams[] = 'exclude_replies=true';
       
   697   }
       
   698   if(true === filter_var($atts['onlypinned'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) {
       
   699     $getParams[] = 'pinned=true';
       
   700   }
       
   701   if(true === filter_var($atts['onlymedia'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) {
       
   702     $getParams[] = 'only_media=true';
       
   703   }
       
   704   if(false !== filter_var($atts['tagged'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) {
       
   705     $getParams[] = 'tagged=' . filter_var( $atts['tagged'], FILTER_UNSAFE_RAW );
       
   706   }
       
   707   if(sizeof($getParams) > 0) {
       
   708     $apiUrl .= '?' . implode('&', $getParams);
       
   709   }
       
   710   $elemId = uniqid('include-mastodon-feed-');
       
   711   ob_start();
       
   712 ?>
       
   713   <script>
       
   714     window.addEventListener("load", () => {
       
   715       mastodonFeedLoad(
       
   716         "<?php echo sanitize_url( $apiUrl, ['https'] ); ?>",
       
   717         "<?php echo filter_var( $elemId, FILTER_UNSAFE_RAW ); ?>",
       
   718         {
       
   719           linkTarget: "<?php echo filter_var( $atts['linktarget'], FILTER_UNSAFE_RAW ); ?>",
       
   720           showPreviewCards: <?php echo (filter_var( $atts['showpreviewcards'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ? "true" : "false"); ?>,
       
   721           excludeConversationStarters: <?php echo (filter_var( $atts['excludeconversationstarters'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ? "true" : "false"); ?>,
       
   722           preserveImageAspectRatio: <?php echo (filter_var( $atts['preserveimageaspectratio'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ? "true" : "false"); ?>,
       
   723           content: {
       
   724             hideStatusMeta: <?php echo (filter_var( $atts['hidestatusmeta'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ? "true" : "false"); ?>,
       
   725             hideDateTime: <?php echo (filter_var( $atts['hidedatetime'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ? "true" : "false"); ?>
       
   726           },
       
   727           text: {
       
   728             boosted: "<?php echo esc_js( $atts['text-boosted'] ); ?>",
       
   729             noStatuses: "<?php echo esc_html( $atts['text-nostatuses'] ); ?>",
       
   730             viewOnInstance: "<?php echo esc_js( $atts['text-viewoninstance'] ); ?>",
       
   731             showContent: "<?php echo esc_js( $atts['text-showcontent'] ); ?>",
       
   732             permalinkPre: "<?php echo esc_js( $atts['text-permalinkpre'] ); ?>",
       
   733             permalinkPost: "<?php echo esc_js( $atts['text-permalinkpost'] ); ?>",
       
   734             edited: "<?php echo esc_js( $atts['text-edited'] ); ?>",
       
   735           },
       
   736           localization: {
       
   737             date: {
       
   738               locale: "<?php echo filter_var( $atts['date-locale'], FILTER_UNSAFE_RAW ); ?>",
       
   739               options: <?php echo filter_var( $atts['date-options'], FILTER_UNSAFE_RAW ); ?>,
       
   740             }
       
   741           }
       
   742         }
       
   743       );
       
   744     });
       
   745   </script>
       
   746   <div class="include-mastodon-feed<?php echo (true == $atts['darkmode'] ? ' dark' : ''); ?>" id="<?php echo esc_attr( $elemId ); ?>"><?php echo esc_html( $atts['text-loading'] ); ?></div>
       
   747 <?php
       
   748   return ob_get_clean();
       
   749 }
       
   750 add_shortcode('include-mastodon-feed', __NAMESPACE__ . '\display_feed');