/** * Transform match to clickable links or to embedded equivalent. * * URLs are typically matched against, which are then translated into a * clickable link or transformed into their equivalent embed, if supported. * There is a universal config to disable automatic embedding. * * @param array $Matches Captured and grouped matches against string. * @return string */ protected static function linksCallback($Matches) { static $Width, $Height, $InTag = 0, $InAnchor = false; if (!isset($Width)) { list($Width, $Height) = Gdn_Format::getEmbedSize(); } if ($Matches === null) { $InTag = 0; $InAnchor = false; return; } $InOut = $Matches[1]; $Tag = strtolower($Matches[2]); // $End = $Matches[3]; // $Url = $Matches[4]; if ($InOut == '<') { $InTag++; if ($Tag == 'a') { $InAnchor = true; } } elseif ($InOut == '</') { $InTag++; if ($Tag == 'a') { $InAnchor = false; } } elseif ($Matches[3]) { $InTag--; } if (!isset($Matches[4]) || $InTag || $InAnchor) { return $Matches[0]; } $Url = $Matches[4]; // Supported youtube embed urls: // // http://www.youtube.com/playlist?list=PL4CFF79651DB8159B // https://www.youtube.com/playlist?list=PL4CFF79651DB8159B // https://www.youtube.com/watch?v=sjm_gBpJ63k&list=PL4CFF79651DB8159B&index=1 // http://youtu.be/sjm_gBpJ63k // https://www.youtube.com/watch?v=sjm_gBpJ63k // http://YOUTU.BE/sjm_gBpJ63k?list=PL4CFF79651DB8159B // http://youtu.be/GUbyhoU81sQ?t=1m8s // https://m.youtube.com/watch?v=iAEKPcz9www // https://youtube.com/watch?v=iAEKPcz9www // https://www.youtube.com/watch?v=p5kcBxL7-qI // https://www.youtube.com/watch?v=bG6b3V2MNxQ#t=33 $YoutubeUrlMatch = '/https?:\\/\\/(?:(?:www.)|(?:m.))?(?:(?:youtube.com)|(?:youtu.be))\\/(?:(?:playlist?)|(?:(?:watch\\?v=)?(?P<videoId>[\\w-]*)))(?:\\?|\\&)?(?:list=(?P<listId>[\\w-]*))?(?:t=(?:(?P<minutes>\\d)*m)?(?P<seconds>\\d)*s)?(?:#t=(?P<start>\\d*))?/i'; $VimeoUrlMatch = 'https?://(www\\.)?vimeo\\.com/(?:channels/[a-z0-9]+/)?(\\d+)'; $TwitterUrlMatch = 'https?://(?:www\\.)?twitter\\.com/(?:#!/)?(?:[^/]+)/status(?:es)?/([\\d]+)'; $VineUrlMatch = 'https?://(?:www\\.)?vine.co/v/([\\w]+)'; $InstagramUrlMatch = 'https?://(?:www\\.)?instagr(?:\\.am|am\\.com)/p/([\\w-]+)'; $PintrestUrlMatch = 'https?://(?:www\\.)?pinterest.com/pin/([\\d]+)'; $GettyUrlMatch = 'http://embed.gettyimages.com/([\\w=?&;+-_]*)/([\\d]*)/([\\d]*)'; $TwitchUrlMatch = 'http://www.twitch.tv/([\\w]+)'; $HitboxUrlMatch = 'http://www.hitbox.tv/([\\w]+)'; $SoundcloudUrlMatch = 'https://soundcloud.com/([\\w=?&;+-_]*)/([\\w=?&;+-_]*)'; $ImgurGifvUrlMatch = 'https?\\://i\\.imgur\\.com/([a-z0-9]+)\\.gifv'; // YouTube if (preg_match($YoutubeUrlMatch, $Url, $Matches) && c('Garden.Format.YouTube', true) && !c('Garden.Format.DisableUrlEmbeds')) { $videoId = val('videoId', $Matches); $listId = val('listId', $Matches); if (!empty($listId)) { // Playlist. if (empty($videoId)) { // Playlist, no video. $Result = <<<EOT <iframe width="{$Width}" height="{$Height}" src="//www.youtube.com/embed/videoseries?list={$listId}" frameborder="0" allowfullscreen></iframe> EOT; } else { // Video in a playlist. $Result = <<<EOT <iframe width="{$Width}" height="{$Height}" src="https://www.youtube.com/embed/{$videoId}?list={$listId}" frameborder="0" allowfullscreen></iframe> EOT; } } else { // Regular ol' youtube video embed. $minutes = val('minutes', $Matches); $seconds = val('seconds', $Matches); $fullUrl = $videoId . '?autoplay=1'; if (!empty($minutes) || !empty($seconds)) { $time = $minutes * 60 + $seconds; $fullUrl .= '&start=' . $time; } // Jump to start time. if ($start = val('start', $Matches)) { $fullUrl .= '&start=' . $start; $start = '#t=' . $start; } $Result = '<span class="VideoWrap">'; $Result .= '<span class="Video YouTube" data-youtube="youtube-' . $fullUrl . '">'; $Result .= '<span class="VideoPreview"><a href="//youtube.com/watch?v=' . $videoId . $start . '">'; $Result .= '<img src="//img.youtube.com/vi/' . $videoId . '/0.jpg" width="' . $Width . '" height="' . $Height . '" border="0" /></a></span>'; $Result .= '<span class="VideoPlayer"></span>'; $Result .= '</span>'; } $Result .= '</span>'; // Vimeo } elseif (preg_match("`{$VimeoUrlMatch}`", $Url, $Matches) && C('Garden.Format.Vimeo', true) && !c('Garden.Format.DisableUrlEmbeds')) { $ID = $Matches[2]; $Result = <<<EOT <iframe src="//player.vimeo.com/video/{$ID}" width="{$Width}" height="{$Height}" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe> EOT; // Imgur GifV } elseif (preg_match("`{$ImgurGifvUrlMatch}`i", $Url, $Matches) && C('Garden.Format.Gifv', true) && !c('Garden.Format.DisableUrlEmbeds')) { $ID = $Matches[1]; $ModernBrowser = T('Your browser does not support HTML5 video!'); $Result = <<<EOT <div class="imgur-gifv VideoWrap"> <video poster="https://i.imgur.com/{$ID}h.jpg" preload="auto" autoplay="autoplay" muted="muted" loop="loop"> <source src="https://i.imgur.com/{$ID}.mp4" type="video/mp4"> <p>{$ModernBrowser} https://i.imgur.com/{$ID}.gifv</p> </video> </div> EOT; // Twitter } elseif (preg_match("`{$TwitterUrlMatch}`", $Url, $Matches) && C('Garden.Format.Twitter', true) && !c('Garden.Format.DisableUrlEmbeds')) { $Result = <<<EOT <div class="twitter-card" data-tweeturl="{$Matches[0]}" data-tweetid="{$Matches[1]}"><a href="{$Matches[0]}" class="tweet-url" rel="nofollow" target="_blank">{$Matches[0]}</a></div> EOT; // Vine } elseif (preg_match("`{$VineUrlMatch}`i", $Url, $Matches) && C('Garden.Format.Vine', true) && !c('Garden.Format.DisableUrlEmbeds')) { $Result = <<<EOT <div class="vine-video VideoWrap"> <iframe class="vine-embed" src="//vine.co/v/{$Matches[1]}/embed/simple" width="320" height="320" frameborder="0"></iframe><script async src="//platform.vine.co/static/scripts/embed.js" charset="utf-8"></script> </div> EOT; // Instagram } elseif (preg_match("`{$InstagramUrlMatch}`i", $Url, $Matches) && C('Garden.Format.Instagram', true) && !c('Garden.Format.DisableUrlEmbeds')) { $Result = <<<EOT <div class="instagram-video VideoWrap"> <iframe src="//instagram.com/p/{$Matches[1]}/embed/" width="412" height="510" frameborder="0" scrolling="no" allowtransparency="true"></iframe> </div> EOT; // Pintrest } elseif (preg_match("`({$PintrestUrlMatch})`", $Url, $Matches) && C('Garden.Format.Pintrest', true) && !c('Garden.Format.DisableUrlEmbeds')) { $Result = <<<EOT <a data-pin-do="embedPin" href="//pinterest.com/pin/{$Matches[2]}/" class="pintrest-pin" rel="nofollow" target="_blank"></a> EOT; // Getty } elseif (preg_match("`({$GettyUrlMatch})`i", $Url, $Matches) && C('Garden.Format.Getty', true) && !c('Garden.Format.DisableUrlEmbeds')) { $Result = <<<EOT <iframe src="//embed.gettyimages.com/embed/{$Matches[2]}" width="{$Matches[3]}" height="{$Matches[4]}" frameborder="0" scrolling="no"></iframe> EOT; // Twitch } elseif (preg_match("`({$TwitchUrlMatch})`i", $Url, $Matches) && C('Garden.Format.Twitch', true) && !c('Garden.Format.DisableUrlEmbeds')) { $Result = <<<EOT <object type="application/x-shockwave-flash" height="378" width="620" id="live_embed_player_flash" data="http://www.twitch.tv/widgets/live_embed_player.swf?channel={$Matches[2]}" bgcolor="#000000"><param name="allowFullScreen" value="true" /><param name="allowScriptAccess" value="always" /><param name="allowNetworking" value="all" /><param name="movie" value="http://www.twitch.tv/widgets/live_embed_player.swf" /><param name="flashvars" value="hostname=www.twitch.tv&channel={$Matches[2]}&auto_play=false&start_volume=25" /></object><a href="http://www.twitch.tv/{$Matches[2]}" style="padding:2px 0px 4px; display:block; width:345px; font-weight:normal; font-size:10px;text-decoration:underline; text-align:center;">Watch live video from {$Matches[2]} on www.twitch.tv</a> EOT; // Hitbox } elseif (preg_match("`({$HitboxUrlMatch})`i", $Url, $Matches) && C('Garden.Format.Hitbox', true) && !c('Garden.Format.DisableUrlEmbeds')) { $Result = <<<EOT \t <iframe width="640" height="360" src="http://hitbox.tv/#!/embed/{$Matches[2]}" frameborder="0" allowfullscreen></iframe> <a href="http://www.hitbox.tv/{$Matches[2]}" style="padding:2px 0px 4px; display:block; width:345px; font-weight:normal; font-size:10px;text-decoration:underline; text-align:center;">Watch live video from {$Matches[2]} on www.hitbox.tv</a> EOT; // Soundcloud } elseif (preg_match("`({$SoundcloudUrlMatch})`i", $Url, $Matches) && C('Garden.Format.Soundcloud', true) && !c('Garden.Format.DisableUrlEmbeds')) { $Result = <<<EOT <iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//soundcloud.com/{$Matches[2]}/{$Matches[3]}&color=ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false"></iframe> EOT; // Unformatted links } elseif (!self::$FormatLinks) { $Result = $Url; // Formatted links } else { // Strip punctuation off of the end of the url. $Punc = ''; if (preg_match('`^(.+)([.?,;:])$`', $Url, $Matches)) { $Url = $Matches[1]; $Punc = $Matches[2]; } // Get human-readable text from url. $Text = $Url; if (strpos($Text, '%') !== false) { $Text = rawurldecode($Text); $Text = htmlspecialchars($Text, ENT_QUOTES, c('Garden.Charset', 'UTF-8')); } $nofollow = self::$DisplayNoFollow ? ' rel="nofollow"' : ''; $Result = <<<EOT <a href="{$Url}" target="_blank"{$nofollow}>{$Text}</a>{$Punc} EOT; } return $Result; }
/** * Perform formatting against a string for the video tag. * * @param Nbbc $bbcode Instance of Nbbc doing the parsing. * @param int $action Value of one of NBBC's defined constants. Typically, this will be BBCODE_CHECK. * @param string $name Name of the tag. * @param string $default Value of the _default parameter, from the $params array. * @param array $params A standard set parameters related to the tag. * @param string $content Value between the open and close tags, if any. * @return string Formatted value. */ function doVideo($bbcode, $action, $name, $default, $params, $content) { list($width, $height) = Gdn_Format::getEmbedSize(); list($type, $code) = explode(';', $default); switch ($type) { case 'youtube': return "<div class=\"Video P\"><iframe width=\"{$width}\" height=\"{$height}\" src=\"https://www.youtube.com/embed/{$code}\" frameborder=\"0\" allowfullscreen></iframe></div>"; default: return $content; } }