forked from mirror/pixelfed
200 lines
6.8 KiB
PHP
Executable File
200 lines
6.8 KiB
PHP
Executable File
<?php
|
|
|
|
/**
|
|
* @author Nick Pope <nick@nickpope.me.uk>
|
|
* @copyright Copyright © 2010, Nick Pope
|
|
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
|
*/
|
|
|
|
namespace App\Util\Lexer;
|
|
|
|
/**
|
|
* Twitter HitHighlighter Class.
|
|
*
|
|
* Performs "hit highlighting" on tweets that have been auto-linked already.
|
|
* Useful with the results returned from the search API.
|
|
*
|
|
* Originally written by {@link http://github.com/mikenz Mike Cochrane}, this
|
|
* is based on code by {@link http://github.com/mzsanford Matt Sanford} and
|
|
* heavily modified by {@link http://github.com/ngnpope Nick Pope}.
|
|
*
|
|
* @author Nick Pope <nick@nickpope.me.uk>
|
|
* @copyright Copyright © 2010, Nick Pope
|
|
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
|
*/
|
|
class HitHighlighter extends Regex
|
|
{
|
|
/**
|
|
* The tag to surround hits with.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $tag = 'em';
|
|
|
|
/**
|
|
* Provides fluent method chaining.
|
|
*
|
|
* @param string $tweet The tweet to be hit highlighted.
|
|
* @param bool $full_encode Whether to encode all special characters.
|
|
*
|
|
* @see __construct()
|
|
*
|
|
* @return HitHighlighter
|
|
*/
|
|
public static function create($tweet = null, $full_encode = false)
|
|
{
|
|
return new self($tweet, $full_encode);
|
|
}
|
|
|
|
/**
|
|
* Reads in a tweet to be parsed and hit highlighted.
|
|
*
|
|
* We take this opportunity to ensure that we escape user input.
|
|
*
|
|
* @see htmlspecialchars()
|
|
*
|
|
* @param string $tweet The tweet to be hit highlighted.
|
|
* @param bool $escape Whether to escape the tweet (default: true).
|
|
* @param bool $full_encode Whether to encode all special characters.
|
|
*/
|
|
public function __construct($tweet = null, $escape = true, $full_encode = false)
|
|
{
|
|
if (!empty($tweet) && $escape) {
|
|
if ($full_encode) {
|
|
parent::__construct(htmlentities($tweet, ENT_QUOTES, 'UTF-8', false));
|
|
} else {
|
|
parent::__construct(htmlspecialchars($tweet, ENT_QUOTES, 'UTF-8', false));
|
|
}
|
|
} else {
|
|
parent::__construct($tweet);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the highlighting tag to surround hits with. The default tag is 'em'.
|
|
*
|
|
* @return string The tag name.
|
|
*/
|
|
public function getTag()
|
|
{
|
|
return $this->tag;
|
|
}
|
|
|
|
/**
|
|
* Set the highlighting tag to surround hits with. The default tag is 'em'.
|
|
*
|
|
* @param string $v The tag name.
|
|
*
|
|
* @return HitHighlighter Fluid method chaining.
|
|
*/
|
|
public function setTag($v)
|
|
{
|
|
$this->tag = $v;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Hit highlights the tweet.
|
|
*
|
|
* @param string $tweet The tweet to be hit highlighted.
|
|
* @param array $hits An array containing the start and end index pairs
|
|
* for the highlighting.
|
|
* @param bool $escape Whether to escape the tweet (default: true).
|
|
* @param bool $full_encode Whether to encode all special characters.
|
|
*
|
|
* @return string The hit highlighted tweet.
|
|
*/
|
|
public function highlight($tweet = null, array $hits = null)
|
|
{
|
|
if (is_null($tweet)) {
|
|
$tweet = $this->tweet;
|
|
}
|
|
if (empty($hits)) {
|
|
return $tweet;
|
|
}
|
|
$highlightTweet = '';
|
|
$tags = ['<'.$this->tag.'>', '</'.$this->tag.'>'];
|
|
// Check whether we can simply replace or whether we need to chunk...
|
|
if (strpos($tweet, '<') === false) {
|
|
$ti = 0; // tag increment (for added tags)
|
|
$highlightTweet = $tweet;
|
|
foreach ($hits as $hit) {
|
|
$highlightTweet = StringUtils::substrReplace($highlightTweet, $tags[0], $hit[0] + $ti, 0);
|
|
$ti += StringUtils::strlen($tags[0]);
|
|
$highlightTweet = StringUtils::substrReplace($highlightTweet, $tags[1], $hit[1] + $ti, 0);
|
|
$ti += StringUtils::strlen($tags[1]);
|
|
}
|
|
} else {
|
|
$chunks = preg_split('/[<>]/iu', $tweet);
|
|
$chunk = $chunks[0];
|
|
$chunk_index = 0;
|
|
$chunk_cursor = 0;
|
|
$offset = 0;
|
|
$start_in_chunk = false;
|
|
// Flatten the multidimensional hits array:
|
|
$hits_flat = [];
|
|
foreach ($hits as $hit) {
|
|
$hits_flat = array_merge($hits_flat, $hit);
|
|
}
|
|
// Loop over the hit indices:
|
|
for ($index = 0; $index < count($hits_flat); $index++) {
|
|
$hit = $hits_flat[$index];
|
|
$tag = $tags[$index % 2];
|
|
$placed = false;
|
|
while ($chunk !== null && $hit >= ($i = $offset + StringUtils::strlen($chunk))) {
|
|
$highlightTweet .= StringUtils::substr($chunk, $chunk_cursor);
|
|
if ($start_in_chunk && $hit === $i) {
|
|
$highlightTweet .= $tag;
|
|
$placed = true;
|
|
}
|
|
if (isset($chunks[$chunk_index + 1])) {
|
|
$highlightTweet .= '<'.$chunks[$chunk_index + 1].'>';
|
|
}
|
|
$offset += StringUtils::strlen($chunk);
|
|
$chunk_cursor = 0;
|
|
$chunk_index += 2;
|
|
$chunk = (isset($chunks[$chunk_index]) ? $chunks[$chunk_index] : null);
|
|
$start_in_chunk = false;
|
|
}
|
|
if (!$placed && $chunk !== null) {
|
|
$hit_spot = $hit - $offset;
|
|
$highlightTweet .= StringUtils::substr($chunk, $chunk_cursor, $hit_spot - $chunk_cursor).$tag;
|
|
$chunk_cursor = $hit_spot;
|
|
$start_in_chunk = ($index % 2 === 0);
|
|
$placed = true;
|
|
}
|
|
// Ultimate fallback - hits that run off the end get a closing tag:
|
|
if (!$placed) {
|
|
$highlightTweet .= $tag;
|
|
}
|
|
}
|
|
if ($chunk !== null) {
|
|
if ($chunk_cursor < StringUtils::strlen($chunk)) {
|
|
$highlightTweet .= StringUtils::substr($chunk, $chunk_cursor);
|
|
}
|
|
for ($index = $chunk_index + 1; $index < count($chunks); $index++) {
|
|
$highlightTweet .= ($index % 2 === 0 ? $chunks[$index] : '<'.$chunks[$index].'>');
|
|
}
|
|
}
|
|
}
|
|
|
|
return $highlightTweet;
|
|
}
|
|
|
|
/**
|
|
* Hit highlights the tweet.
|
|
*
|
|
* @param array $hits An array containing the start and end index pairs
|
|
* for the highlighting.
|
|
*
|
|
* @return string The hit highlighted tweet.
|
|
*
|
|
* @deprecated since version 1.1.0
|
|
*/
|
|
public function addHitHighlighting(array $hits)
|
|
{
|
|
return $this->highlight($this->tweet, $hits);
|
|
}
|
|
}
|